/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import { | |||||
associateProject, | |||||
dissociateProject, | |||||
getProfileProjects, | |||||
ProfileProject, | |||||
searchQualityProfiles, | |||||
} from '../../../api/quality-profiles'; | |||||
import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization'; | |||||
import { mockComponent } from '../../../helpers/mocks/component'; | |||||
import { waitAndUpdate } from '../../../helpers/testUtils'; | |||||
import { ProjectQualityProfilesApp } from '../ProjectQualityProfilesApp'; | |||||
jest.mock('../../../api/quality-profiles', () => { | |||||
const { mockQualityProfile } = jest.requireActual('../../../helpers/testMocks'); | |||||
return { | |||||
associateProject: jest.fn().mockResolvedValue({}), | |||||
dissociateProject: jest.fn().mockResolvedValue({}), | |||||
searchQualityProfiles: jest.fn().mockResolvedValue({ | |||||
profiles: [ | |||||
mockQualityProfile({ key: 'css', language: 'css' }), | |||||
mockQualityProfile({ key: 'css2', language: 'css' }), | |||||
mockQualityProfile({ key: 'css_default', language: 'css', isDefault: true }), | |||||
mockQualityProfile({ key: 'java', language: 'java' }), | |||||
mockQualityProfile({ key: 'java_default', language: 'java', isDefault: true }), | |||||
mockQualityProfile({ key: 'js', language: 'js' }), | |||||
mockQualityProfile({ key: 'js_default', language: 'js', isDefault: true }), | |||||
mockQualityProfile({ key: 'ts_default', language: 'ts', isDefault: true }), | |||||
mockQualityProfile({ key: 'html', language: 'html' }), | |||||
mockQualityProfile({ key: 'html_default', language: 'html', isDefault: true }), | |||||
], | |||||
}), | |||||
getProfileProjects: jest.fn(({ key }) => { | |||||
const results: ProfileProject[] = []; | |||||
if (key === 'js' || key === 'css' || key === 'html_default') { | |||||
results.push({ | |||||
key: 'foo', | |||||
name: 'Foo', | |||||
selected: true, | |||||
}); | |||||
} else if (key === 'html') { | |||||
results.push({ | |||||
key: 'foobar', | |||||
name: 'FooBar', | |||||
selected: true, | |||||
}); | |||||
} | |||||
return Promise.resolve({ results }); | |||||
}), | |||||
}; | |||||
}); | |||||
jest.mock('../../../helpers/globalMessages', () => ({ | |||||
addGlobalSuccessMessage: jest.fn(), | |||||
})); | |||||
jest.mock('../../../app/utils/handleRequiredAuthorization', () => jest.fn()); | |||||
beforeEach(jest.clearAllMocks); | |||||
it('renders correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot(); | |||||
}); | |||||
it('correctly checks permissions', () => { | |||||
const wrapper = shallowRender({ | |||||
component: mockComponent({ configuration: { showQualityProfiles: false } }), | |||||
}); | |||||
expect(wrapper.type()).toBeNull(); | |||||
expect(handleRequiredAuthorization).toHaveBeenCalled(); | |||||
}); | |||||
it('correctly fetches and treats profile data', async () => { | |||||
const wrapper = shallowRender(); | |||||
await waitAndUpdate(wrapper); | |||||
expect(searchQualityProfiles).toHaveBeenCalled(); | |||||
expect(getProfileProjects).toHaveBeenCalledTimes(10); | |||||
expect(wrapper.state().projectProfiles).toEqual([ | |||||
expect.objectContaining({ | |||||
profile: expect.objectContaining({ key: 'css' }), | |||||
selected: true, | |||||
}), | |||||
expect.objectContaining({ | |||||
profile: expect.objectContaining({ key: 'js' }), | |||||
selected: true, | |||||
}), | |||||
expect.objectContaining({ | |||||
profile: expect.objectContaining({ key: 'html_default' }), | |||||
selected: true, | |||||
}), | |||||
expect.objectContaining({ | |||||
profile: expect.objectContaining({ key: 'ts_default' }), | |||||
selected: false, | |||||
}), | |||||
]); | |||||
}); | |||||
it('correctly sets a profile', async () => { | |||||
const wrapper = shallowRender(); | |||||
const instance = wrapper.instance(); | |||||
await waitAndUpdate(wrapper); | |||||
// Dissociate a selected profile. | |||||
instance.handleSetProfile(undefined, 'css'); | |||||
expect(dissociateProject).toHaveBeenLastCalledWith( | |||||
expect.objectContaining({ key: 'css' }), | |||||
'foo' | |||||
); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper.state().projectProfiles).toEqual( | |||||
expect.arrayContaining([ | |||||
{ | |||||
profile: expect.objectContaining({ key: 'css_default' }), | |||||
// It's not explicitly selected, as we're inheriting the default. | |||||
selected: false, | |||||
}, | |||||
]) | |||||
); | |||||
// Associate a new profile. | |||||
instance.handleSetProfile('css2', 'css_default'); | |||||
expect(associateProject).toHaveBeenLastCalledWith( | |||||
expect.objectContaining({ key: 'css2' }), | |||||
'foo' | |||||
); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper.state().projectProfiles).toEqual( | |||||
expect.arrayContaining([ | |||||
{ | |||||
profile: expect.objectContaining({ key: 'css2' }), | |||||
// It's explicitly selected. | |||||
selected: true, | |||||
}, | |||||
]) | |||||
); | |||||
// Dissociate a default profile that was inherited. | |||||
(dissociateProject as jest.Mock).mockClear(); | |||||
instance.handleSetProfile(undefined, 'ts_default'); | |||||
// It won't call the WS. | |||||
expect(dissociateProject).not.toHaveBeenCalled(); | |||||
// Associate a default profile that was already inherited. | |||||
instance.handleSetProfile('ts_default', 'ts_default'); | |||||
expect(associateProject).toHaveBeenLastCalledWith( | |||||
expect.objectContaining({ key: 'ts_default' }), | |||||
'foo' | |||||
); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper.state().projectProfiles).toEqual( | |||||
expect.arrayContaining([ | |||||
{ | |||||
profile: expect.objectContaining({ key: 'ts_default' }), | |||||
// It's explicitly selected, even though it is the default profile. | |||||
selected: true, | |||||
}, | |||||
]) | |||||
); | |||||
}); | |||||
it('correctly adds a new language', async () => { | |||||
const wrapper = shallowRender(); | |||||
const instance = wrapper.instance(); | |||||
await waitAndUpdate(wrapper); | |||||
instance.handleAddLanguage('java'); | |||||
expect(associateProject).toHaveBeenLastCalledWith( | |||||
expect.objectContaining({ key: 'java' }), | |||||
'foo' | |||||
); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper.state().projectProfiles).toEqual( | |||||
expect.arrayContaining([ | |||||
{ | |||||
profile: expect.objectContaining({ key: 'java' }), | |||||
// It must be explicitly selected. Adding an unanalyzed language can | |||||
// only happen by explicitly choosing a profile. | |||||
selected: true, | |||||
}, | |||||
]) | |||||
); | |||||
}); | |||||
it('correctly handles WS errors', async () => { | |||||
(searchQualityProfiles as jest.Mock).mockRejectedValueOnce(null); | |||||
(getProfileProjects as jest.Mock).mockRejectedValueOnce(null); | |||||
const wrapper = shallowRender(); | |||||
await waitAndUpdate(wrapper); | |||||
expect(wrapper.state().allProfiles).toHaveLength(0); | |||||
expect(wrapper.state().projectProfiles).toHaveLength(0); | |||||
expect(wrapper.state().loading).toBe(false); | |||||
}); | |||||
function shallowRender(props: Partial<ProjectQualityProfilesApp['props']> = {}) { | |||||
return shallow<ProjectQualityProfilesApp>( | |||||
<ProjectQualityProfilesApp | |||||
component={mockComponent({ | |||||
key: 'foo', | |||||
configuration: { showQualityProfiles: true }, | |||||
qualityProfiles: [ | |||||
{ key: 'css2', name: 'CSS 2', language: 'css' }, | |||||
{ key: 'js', name: 'JS', language: 'js' }, | |||||
{ key: 'ts_default', name: 'TS (default)', language: 'ts' }, | |||||
{ key: 'html', name: 'HTML', language: 'html' }, | |||||
], | |||||
})} | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import { mockComponent } from '../../../helpers/mocks/component'; | |||||
import { mockQualityProfile } from '../../../helpers/testMocks'; | |||||
import ProjectQualityProfilesAppRenderer, { | |||||
ProjectQualityProfilesAppRendererProps, | |||||
} from '../ProjectQualityProfilesAppRenderer'; | |||||
it('should render correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot('default'); | |||||
expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); | |||||
expect( | |||||
shallowRender({ | |||||
showProjectProfileInModal: { | |||||
profile: mockQualityProfile({ key: 'foo', language: 'js' }), | |||||
selected: false, | |||||
}, | |||||
}) | |||||
).toMatchSnapshot('open profile'); | |||||
expect(shallowRender({ showAddLanguageModal: true })).toMatchSnapshot('add language'); | |||||
}); | |||||
function shallowRender(props: Partial<ProjectQualityProfilesAppRendererProps> = {}) { | |||||
return shallow<ProjectQualityProfilesAppRendererProps>( | |||||
<ProjectQualityProfilesAppRenderer | |||||
allProfiles={[ | |||||
mockQualityProfile({ key: 'foo', language: 'js' }), | |||||
mockQualityProfile({ key: 'bar', language: 'css' }), | |||||
mockQualityProfile({ key: 'baz', language: 'html' }), | |||||
]} | |||||
component={mockComponent()} | |||||
loading={false} | |||||
onAddLanguage={jest.fn()} | |||||
onCloseModal={jest.fn()} | |||||
onOpenAddLanguageModal={jest.fn()} | |||||
onOpenSetProfileModal={jest.fn()} | |||||
onSetProfile={jest.fn()} | |||||
projectProfiles={[ | |||||
{ | |||||
profile: mockQualityProfile({ | |||||
key: 'foo', | |||||
name: 'Foo', | |||||
isDefault: true, | |||||
language: 'js', | |||||
languageName: 'JS', | |||||
}), | |||||
selected: false, | |||||
}, | |||||
{ | |||||
profile: mockQualityProfile({ | |||||
key: 'bar', | |||||
name: 'Bar', | |||||
isDefault: true, | |||||
language: 'css', | |||||
languageName: 'CSS', | |||||
}), | |||||
selected: false, | |||||
}, | |||||
{ | |||||
profile: mockQualityProfile({ | |||||
key: 'baz', | |||||
name: 'Baz', | |||||
language: 'html', | |||||
languageName: 'HTML', | |||||
}), | |||||
selected: true, | |||||
}, | |||||
]} | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`renders correctly 1`] = ` | |||||
<ProjectQualityProfilesAppRenderer | |||||
component={ | |||||
{ | |||||
"breadcrumbs": [], | |||||
"configuration": { | |||||
"showQualityProfiles": true, | |||||
}, | |||||
"key": "foo", | |||||
"name": "MyProject", | |||||
"qualifier": "TRK", | |||||
"qualityGate": { | |||||
"isDefault": true, | |||||
"key": "30", | |||||
"name": "Sonar way", | |||||
}, | |||||
"qualityProfiles": [ | |||||
{ | |||||
"key": "css2", | |||||
"language": "css", | |||||
"name": "CSS 2", | |||||
}, | |||||
{ | |||||
"key": "js", | |||||
"language": "js", | |||||
"name": "JS", | |||||
}, | |||||
{ | |||||
"key": "ts_default", | |||||
"language": "ts", | |||||
"name": "TS (default)", | |||||
}, | |||||
{ | |||||
"key": "html", | |||||
"language": "html", | |||||
"name": "HTML", | |||||
}, | |||||
], | |||||
"tags": [], | |||||
} | |||||
} | |||||
loading={true} | |||||
onAddLanguage={[Function]} | |||||
onCloseModal={[Function]} | |||||
onOpenAddLanguageModal={[Function]} | |||||
onOpenSetProfileModal={[Function]} | |||||
onSetProfile={[Function]} | |||||
/> | |||||
`; |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: add language 1`] = ` | |||||
<div | |||||
className="page page-limited" | |||||
id="project-quality-profiles" | |||||
> | |||||
<Suggestions | |||||
suggestions="project_quality_profiles" | |||||
/> | |||||
<Helmet | |||||
defer={false} | |||||
encodeSpecialCharacters={true} | |||||
prioritizeSeoTags={false} | |||||
title="project_quality_profiles.page" | |||||
/> | |||||
<A11ySkipTarget | |||||
anchor="profiles_main" | |||||
/> | |||||
<header | |||||
className="page-header" | |||||
> | |||||
<div | |||||
className="page-title display-flex-center" | |||||
> | |||||
<h1> | |||||
project_quality_profiles.page | |||||
</h1> | |||||
<HelpTooltip | |||||
className="spacer-left" | |||||
overlay={ | |||||
<div | |||||
className="big-padded-top big-padded-bottom" | |||||
> | |||||
quality_profiles.list.projects.help | |||||
</div> | |||||
} | |||||
/> | |||||
</div> | |||||
</header> | |||||
<div | |||||
className="boxed-group" | |||||
> | |||||
<h2 | |||||
className="boxed-group-header" | |||||
> | |||||
project_quality_profile.subtitle | |||||
</h2> | |||||
<div | |||||
className="boxed-group-inner" | |||||
> | |||||
<p | |||||
className="big-spacer-bottom" | |||||
> | |||||
project_quality_profiles.page.description | |||||
</p> | |||||
<table | |||||
className="data zebra" | |||||
> | |||||
<thead> | |||||
<tr> | |||||
<th> | |||||
language | |||||
</th> | |||||
<th | |||||
className="thin nowrap" | |||||
> | |||||
project_quality_profile.current | |||||
</th> | |||||
<th | |||||
className="thin nowrap text-right" | |||||
> | |||||
coding_rules.filters.activation.active_rules | |||||
</th> | |||||
<th | |||||
aria-label="actions" | |||||
/> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr | |||||
key="css" | |||||
> | |||||
<td> | |||||
CSS | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
<em> | |||||
project_quality_profile.instance_default | |||||
</em> | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=bar", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
<tr | |||||
key="html" | |||||
> | |||||
<td> | |||||
HTML | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
Baz | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=baz", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
<tr | |||||
key="js" | |||||
> | |||||
<td> | |||||
JS | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
<em> | |||||
project_quality_profile.instance_default | |||||
</em> | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=foo", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
<div | |||||
className="big-spacer-top" | |||||
> | |||||
<h2> | |||||
project_quality_profile.add_language.title | |||||
</h2> | |||||
<p | |||||
className="spacer-top big-spacer-bottom" | |||||
> | |||||
project_quality_profile.add_language.description | |||||
</p> | |||||
<Button | |||||
disabled={false} | |||||
onClick={[MockFunction]} | |||||
> | |||||
<PlusCircleIcon | |||||
className="little-spacer-right" | |||||
/> | |||||
project_quality_profile.add_language.action | |||||
</Button> | |||||
</div> | |||||
<withLanguagesContext(AddLanguageModal) | |||||
onClose={[MockFunction]} | |||||
onSubmit={[MockFunction]} | |||||
profilesByLanguage={ | |||||
{ | |||||
"css": [ | |||||
{ | |||||
"activeDeprecatedRuleCount": 2, | |||||
"activeRuleCount": 10, | |||||
"childrenCount": 0, | |||||
"depth": 1, | |||||
"isBuiltIn": false, | |||||
"isDefault": false, | |||||
"isInherited": false, | |||||
"key": "bar", | |||||
"language": "css", | |||||
"languageName": "JavaScript", | |||||
"name": "name", | |||||
"projectCount": 3, | |||||
}, | |||||
], | |||||
"html": [ | |||||
{ | |||||
"activeDeprecatedRuleCount": 2, | |||||
"activeRuleCount": 10, | |||||
"childrenCount": 0, | |||||
"depth": 1, | |||||
"isBuiltIn": false, | |||||
"isDefault": false, | |||||
"isInherited": false, | |||||
"key": "baz", | |||||
"language": "html", | |||||
"languageName": "JavaScript", | |||||
"name": "name", | |||||
"projectCount": 3, | |||||
}, | |||||
], | |||||
"js": [ | |||||
{ | |||||
"activeDeprecatedRuleCount": 2, | |||||
"activeRuleCount": 10, | |||||
"childrenCount": 0, | |||||
"depth": 1, | |||||
"isBuiltIn": false, | |||||
"isDefault": false, | |||||
"isInherited": false, | |||||
"key": "foo", | |||||
"language": "js", | |||||
"languageName": "JavaScript", | |||||
"name": "name", | |||||
"projectCount": 3, | |||||
}, | |||||
], | |||||
} | |||||
} | |||||
unavailableLanguages={ | |||||
[ | |||||
"js", | |||||
"css", | |||||
"html", | |||||
] | |||||
} | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly: default 1`] = ` | |||||
<div | |||||
className="page page-limited" | |||||
id="project-quality-profiles" | |||||
> | |||||
<Suggestions | |||||
suggestions="project_quality_profiles" | |||||
/> | |||||
<Helmet | |||||
defer={false} | |||||
encodeSpecialCharacters={true} | |||||
prioritizeSeoTags={false} | |||||
title="project_quality_profiles.page" | |||||
/> | |||||
<A11ySkipTarget | |||||
anchor="profiles_main" | |||||
/> | |||||
<header | |||||
className="page-header" | |||||
> | |||||
<div | |||||
className="page-title display-flex-center" | |||||
> | |||||
<h1> | |||||
project_quality_profiles.page | |||||
</h1> | |||||
<HelpTooltip | |||||
className="spacer-left" | |||||
overlay={ | |||||
<div | |||||
className="big-padded-top big-padded-bottom" | |||||
> | |||||
quality_profiles.list.projects.help | |||||
</div> | |||||
} | |||||
/> | |||||
</div> | |||||
</header> | |||||
<div | |||||
className="boxed-group" | |||||
> | |||||
<h2 | |||||
className="boxed-group-header" | |||||
> | |||||
project_quality_profile.subtitle | |||||
</h2> | |||||
<div | |||||
className="boxed-group-inner" | |||||
> | |||||
<p | |||||
className="big-spacer-bottom" | |||||
> | |||||
project_quality_profiles.page.description | |||||
</p> | |||||
<table | |||||
className="data zebra" | |||||
> | |||||
<thead> | |||||
<tr> | |||||
<th> | |||||
language | |||||
</th> | |||||
<th | |||||
className="thin nowrap" | |||||
> | |||||
project_quality_profile.current | |||||
</th> | |||||
<th | |||||
className="thin nowrap text-right" | |||||
> | |||||
coding_rules.filters.activation.active_rules | |||||
</th> | |||||
<th | |||||
aria-label="actions" | |||||
/> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr | |||||
key="css" | |||||
> | |||||
<td> | |||||
CSS | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
<em> | |||||
project_quality_profile.instance_default | |||||
</em> | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=bar", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
<tr | |||||
key="html" | |||||
> | |||||
<td> | |||||
HTML | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
Baz | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=baz", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
<tr | |||||
key="js" | |||||
> | |||||
<td> | |||||
JS | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
<em> | |||||
project_quality_profile.instance_default | |||||
</em> | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=foo", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
<div | |||||
className="big-spacer-top" | |||||
> | |||||
<h2> | |||||
project_quality_profile.add_language.title | |||||
</h2> | |||||
<p | |||||
className="spacer-top big-spacer-bottom" | |||||
> | |||||
project_quality_profile.add_language.description | |||||
</p> | |||||
<Button | |||||
disabled={false} | |||||
onClick={[MockFunction]} | |||||
> | |||||
<PlusCircleIcon | |||||
className="little-spacer-right" | |||||
/> | |||||
project_quality_profile.add_language.action | |||||
</Button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly: loading 1`] = ` | |||||
<div | |||||
className="page page-limited" | |||||
id="project-quality-profiles" | |||||
> | |||||
<Suggestions | |||||
suggestions="project_quality_profiles" | |||||
/> | |||||
<Helmet | |||||
defer={false} | |||||
encodeSpecialCharacters={true} | |||||
prioritizeSeoTags={false} | |||||
title="project_quality_profiles.page" | |||||
/> | |||||
<A11ySkipTarget | |||||
anchor="profiles_main" | |||||
/> | |||||
<header | |||||
className="page-header" | |||||
> | |||||
<div | |||||
className="page-title display-flex-center" | |||||
> | |||||
<h1> | |||||
project_quality_profiles.page | |||||
</h1> | |||||
<HelpTooltip | |||||
className="spacer-left" | |||||
overlay={ | |||||
<div | |||||
className="big-padded-top big-padded-bottom" | |||||
> | |||||
quality_profiles.list.projects.help | |||||
</div> | |||||
} | |||||
/> | |||||
</div> | |||||
</header> | |||||
<div | |||||
className="boxed-group" | |||||
> | |||||
<h2 | |||||
className="boxed-group-header" | |||||
> | |||||
project_quality_profile.subtitle | |||||
</h2> | |||||
<div | |||||
className="boxed-group-inner" | |||||
> | |||||
<p | |||||
className="big-spacer-bottom" | |||||
> | |||||
project_quality_profiles.page.description | |||||
</p> | |||||
<i | |||||
className="spinner spacer-left" | |||||
/> | |||||
<div | |||||
className="big-spacer-top" | |||||
> | |||||
<h2> | |||||
project_quality_profile.add_language.title | |||||
</h2> | |||||
<p | |||||
className="spacer-top big-spacer-bottom" | |||||
> | |||||
project_quality_profile.add_language.description | |||||
</p> | |||||
<Button | |||||
disabled={true} | |||||
onClick={[MockFunction]} | |||||
> | |||||
<PlusCircleIcon | |||||
className="little-spacer-right" | |||||
/> | |||||
project_quality_profile.add_language.action | |||||
</Button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly: open profile 1`] = ` | |||||
<div | |||||
className="page page-limited" | |||||
id="project-quality-profiles" | |||||
> | |||||
<Suggestions | |||||
suggestions="project_quality_profiles" | |||||
/> | |||||
<Helmet | |||||
defer={false} | |||||
encodeSpecialCharacters={true} | |||||
prioritizeSeoTags={false} | |||||
title="project_quality_profiles.page" | |||||
/> | |||||
<A11ySkipTarget | |||||
anchor="profiles_main" | |||||
/> | |||||
<header | |||||
className="page-header" | |||||
> | |||||
<div | |||||
className="page-title display-flex-center" | |||||
> | |||||
<h1> | |||||
project_quality_profiles.page | |||||
</h1> | |||||
<HelpTooltip | |||||
className="spacer-left" | |||||
overlay={ | |||||
<div | |||||
className="big-padded-top big-padded-bottom" | |||||
> | |||||
quality_profiles.list.projects.help | |||||
</div> | |||||
} | |||||
/> | |||||
</div> | |||||
</header> | |||||
<div | |||||
className="boxed-group" | |||||
> | |||||
<h2 | |||||
className="boxed-group-header" | |||||
> | |||||
project_quality_profile.subtitle | |||||
</h2> | |||||
<div | |||||
className="boxed-group-inner" | |||||
> | |||||
<p | |||||
className="big-spacer-bottom" | |||||
> | |||||
project_quality_profiles.page.description | |||||
</p> | |||||
<table | |||||
className="data zebra" | |||||
> | |||||
<thead> | |||||
<tr> | |||||
<th> | |||||
language | |||||
</th> | |||||
<th | |||||
className="thin nowrap" | |||||
> | |||||
project_quality_profile.current | |||||
</th> | |||||
<th | |||||
className="thin nowrap text-right" | |||||
> | |||||
coding_rules.filters.activation.active_rules | |||||
</th> | |||||
<th | |||||
aria-label="actions" | |||||
/> | |||||
</tr> | |||||
</thead> | |||||
<tbody> | |||||
<tr | |||||
key="css" | |||||
> | |||||
<td> | |||||
CSS | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
<em> | |||||
project_quality_profile.instance_default | |||||
</em> | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=bar", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
<tr | |||||
key="html" | |||||
> | |||||
<td> | |||||
HTML | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
Baz | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=baz", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
<tr | |||||
key="js" | |||||
> | |||||
<td> | |||||
JS | |||||
</td> | |||||
<td | |||||
className="thin nowrap" | |||||
> | |||||
<span | |||||
className="display-inline-flex-center" | |||||
> | |||||
<em> | |||||
project_quality_profile.instance_default | |||||
</em> | |||||
</span> | |||||
</td> | |||||
<td | |||||
className="nowrap text-right" | |||||
> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/coding_rules", | |||||
"search": "?activation=true&qprofile=foo", | |||||
} | |||||
} | |||||
> | |||||
10 | |||||
</ForwardRef(Link)> | |||||
</td> | |||||
<td | |||||
className="text-right" | |||||
> | |||||
<Button | |||||
onClick={[Function]} | |||||
> | |||||
<EditIcon | |||||
className="spacer-right" | |||||
/> | |||||
project_quality_profile.change_profile | |||||
</Button> | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</table> | |||||
<div | |||||
className="big-spacer-top" | |||||
> | |||||
<h2> | |||||
project_quality_profile.add_language.title | |||||
</h2> | |||||
<p | |||||
className="spacer-top big-spacer-bottom" | |||||
> | |||||
project_quality_profile.add_language.description | |||||
</p> | |||||
<Button | |||||
disabled={false} | |||||
onClick={[MockFunction]} | |||||
> | |||||
<PlusCircleIcon | |||||
className="little-spacer-right" | |||||
/> | |||||
project_quality_profile.add_language.action | |||||
</Button> | |||||
</div> | |||||
<SetQualityProfileModal | |||||
availableProfiles={ | |||||
[ | |||||
{ | |||||
"activeDeprecatedRuleCount": 2, | |||||
"activeRuleCount": 10, | |||||
"childrenCount": 0, | |||||
"depth": 1, | |||||
"isBuiltIn": false, | |||||
"isDefault": false, | |||||
"isInherited": false, | |||||
"key": "foo", | |||||
"language": "js", | |||||
"languageName": "JavaScript", | |||||
"name": "name", | |||||
"projectCount": 3, | |||||
}, | |||||
] | |||||
} | |||||
component={ | |||||
{ | |||||
"breadcrumbs": [], | |||||
"key": "my-project", | |||||
"name": "MyProject", | |||||
"qualifier": "TRK", | |||||
"qualityGate": { | |||||
"isDefault": true, | |||||
"key": "30", | |||||
"name": "Sonar way", | |||||
}, | |||||
"qualityProfiles": [ | |||||
{ | |||||
"deleted": false, | |||||
"key": "my-qp", | |||||
"language": "ts", | |||||
"name": "Sonar way", | |||||
}, | |||||
], | |||||
"tags": [], | |||||
} | |||||
} | |||||
currentProfile={ | |||||
{ | |||||
"activeDeprecatedRuleCount": 2, | |||||
"activeRuleCount": 10, | |||||
"childrenCount": 0, | |||||
"depth": 1, | |||||
"isBuiltIn": false, | |||||
"isDefault": false, | |||||
"isInherited": false, | |||||
"key": "foo", | |||||
"language": "js", | |||||
"languageName": "JavaScript", | |||||
"name": "name", | |||||
"projectCount": 3, | |||||
} | |||||
} | |||||
onClose={[MockFunction]} | |||||
onSubmit={[MockFunction]} | |||||
usesDefault={true} | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
`; |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import userEvent from '@testing-library/user-event'; | |||||
import selectEvent from 'react-select-event'; | |||||
import { | |||||
ProfileProject, | |||||
associateProject, | |||||
getProfileProjects, | |||||
searchQualityProfiles, | |||||
} from '../../../api/quality-profiles'; | |||||
import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization'; | |||||
import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; | |||||
import { mockComponent } from '../../../helpers/mocks/component'; | |||||
import { | |||||
RenderContext, | |||||
renderAppWithComponentContext, | |||||
} from '../../../helpers/testReactTestingUtils'; | |||||
import { byLabelText, byRole, byText } from '../../../helpers/testSelector'; | |||||
import { Component } from '../../../types/types'; | |||||
import routes from '../routes'; | |||||
jest.mock('../../../api/quality-profiles', () => { | |||||
const { mockQualityProfile } = jest.requireActual('../../../helpers/testMocks'); | |||||
return { | |||||
associateProject: jest.fn().mockResolvedValue({}), | |||||
dissociateProject: jest.fn().mockResolvedValue({}), | |||||
searchQualityProfiles: jest.fn().mockResolvedValue({ | |||||
profiles: [ | |||||
mockQualityProfile({ | |||||
key: 'css', | |||||
language: 'css', | |||||
name: 'css profile', | |||||
languageName: 'CSS', | |||||
}), | |||||
mockQualityProfile({ | |||||
key: 'java', | |||||
language: 'java', | |||||
name: 'java profile', | |||||
languageName: 'Java', | |||||
}), | |||||
mockQualityProfile({ | |||||
key: 'js', | |||||
language: 'js', | |||||
name: 'js profile', | |||||
languageName: 'JavaScript', | |||||
}), | |||||
mockQualityProfile({ | |||||
key: 'ts', | |||||
language: 'ts', | |||||
isDefault: true, | |||||
name: 'ts profile', | |||||
languageName: 'Typescript', | |||||
}), | |||||
mockQualityProfile({ | |||||
key: 'html', | |||||
language: 'html', | |||||
name: 'html profile', | |||||
languageName: 'HTML', | |||||
}), | |||||
mockQualityProfile({ | |||||
key: 'html_default', | |||||
language: 'html', | |||||
isDefault: true, | |||||
isBuiltIn: true, | |||||
name: 'html default profile', | |||||
languageName: 'HTML', | |||||
}), | |||||
], | |||||
}), | |||||
getProfileProjects: jest.fn(({ key }) => { | |||||
const results: ProfileProject[] = []; | |||||
if (key === 'css' || key === 'java' || key === 'js' || key === 'ts' || key === 'java') { | |||||
results.push({ | |||||
key: 'my-project', | |||||
name: 'My project', | |||||
selected: true, | |||||
}); | |||||
} | |||||
return Promise.resolve({ results }); | |||||
}), | |||||
}; | |||||
}); | |||||
jest.mock('../../../helpers/globalMessages', () => { | |||||
const globalMessages = jest.requireActual('../../../helpers/globalMessages'); | |||||
return { | |||||
...globalMessages, | |||||
addGlobalSuccessMessage: jest.fn(), | |||||
}; | |||||
}); | |||||
jest.mock('../../../app/utils/handleRequiredAuthorization', () => jest.fn()); | |||||
beforeEach(jest.clearAllMocks); | |||||
const ui = { | |||||
pageTitle: byText('project_quality_profiles.page'), | |||||
pageSubTitle: byText('project_quality_profile.subtitle'), | |||||
pageDescription: byText('project_quality_profiles.page.description'), | |||||
helpTooltip: byLabelText('help'), | |||||
profileRows: byRole('row'), | |||||
addLanguageButton: byRole('button', { name: 'project_quality_profile.add_language.action' }), | |||||
modalAddLanguageTitle: byText('project_quality_profile.add_language_modal.title'), | |||||
selectLanguage: byRole('combobox', { | |||||
name: 'project_quality_profile.add_language_modal.choose_language', | |||||
}), | |||||
selectProfile: byRole('combobox', { | |||||
name: 'project_quality_profile.add_language_modal.choose_profile', | |||||
}), | |||||
selectUseSpecificProfile: byRole('combobox', { | |||||
name: 'project_quality_profile.always_use_specific', | |||||
}), | |||||
buttonSave: byRole('button', { name: 'save' }), | |||||
buttonChangeProfile: byRole('button', { name: 'project_quality_profile.change_profile' }), | |||||
htmlLanguage: byText('HTML'), | |||||
htmlProfile: byText('html profile'), | |||||
cssLanguage: byText('CSS'), | |||||
cssProfile: byText('css profile'), | |||||
htmlDefaultProfile: byText('html default profile'), | |||||
htmlActiveRuleslink: byRole('link', { name: '10' }), | |||||
radioButtonUseInstanceDefault: byRole('radio', { | |||||
name: /project_quality_profile.always_use_default/, | |||||
}), | |||||
radioButtonUseSpecific: byRole('radio', { name: /project_quality_profile.always_use_specific/ }), | |||||
newAnalysisWarningMessage: byText('project_quality_profile.requires_new_analysis'), | |||||
builtInTag: byText('quality_profiles.built_in'), | |||||
}; | |||||
it('should be able to add and change profile for languages', async () => { | |||||
const user = userEvent.setup(); | |||||
renderProjectQualityProfilesApp({ | |||||
languages: { | |||||
css: { key: 'css', name: 'CSS' }, | |||||
ts: { key: 'ts', name: 'TS' }, | |||||
js: { key: 'js', name: 'JS' }, | |||||
java: { key: 'java', name: 'JAVA' }, | |||||
html: { key: 'html', name: 'HTML' }, | |||||
}, | |||||
}); | |||||
expect(ui.pageTitle.get()).toBeInTheDocument(); | |||||
expect(ui.pageSubTitle.get()).toBeInTheDocument(); | |||||
expect(ui.pageDescription.get()).toBeInTheDocument(); | |||||
expect(ui.addLanguageButton.get()).toBeInTheDocument(); | |||||
await expect(ui.helpTooltip.get()).toHaveATooltipWithContent( | |||||
'quality_profiles.list.projects.help' | |||||
); | |||||
expect(ui.profileRows.getAll()).toHaveLength(5); | |||||
expect(ui.cssLanguage.get()).toBeInTheDocument(); | |||||
expect(ui.cssProfile.get()).toBeInTheDocument(); | |||||
await user.click(ui.addLanguageButton.get()); | |||||
// Opens the add language modal | |||||
expect(ui.modalAddLanguageTitle.get()).toBeInTheDocument(); | |||||
expect(ui.selectLanguage.get()).toBeEnabled(); | |||||
expect(ui.selectProfile.get()).toBeDisabled(); | |||||
expect(ui.buttonSave.get()).toBeInTheDocument(); | |||||
await selectEvent.select(ui.selectLanguage.get(), 'HTML'); | |||||
expect(ui.selectProfile.get()).toBeEnabled(); | |||||
await selectEvent.select(ui.selectProfile.get(), 'html profile'); | |||||
await user.click(ui.buttonSave.get()); | |||||
expect(associateProject).toHaveBeenLastCalledWith( | |||||
expect.objectContaining({ key: 'html', name: 'html profile' }), | |||||
'my-project' | |||||
); | |||||
expect(addGlobalSuccessMessage).toHaveBeenCalledWith( | |||||
'project_quality_profile.successfully_updated.HTML' | |||||
); | |||||
// Updates the page after API call | |||||
const htmlRow = byRole('row', { | |||||
name: 'HTML html profile 10 project_quality_profile.change_profile', | |||||
}); | |||||
expect(ui.htmlLanguage.get()).toBeInTheDocument(); | |||||
expect(ui.htmlProfile.get()).toBeInTheDocument(); | |||||
expect(ui.profileRows.getAll()).toHaveLength(6); | |||||
expect(htmlRow.get()).toBeInTheDocument(); | |||||
expect(htmlRow.byRole('link', { name: '10' }).get()).toHaveAttribute( | |||||
'href', | |||||
'/coding_rules?activation=true&qprofile=html' | |||||
); | |||||
expect(ui.builtInTag.query()).not.toBeInTheDocument(); | |||||
await user.click( | |||||
htmlRow.byRole('button', { name: 'project_quality_profile.change_profile' }).get() | |||||
); | |||||
//Opens modal to change profile | |||||
expect(ui.radioButtonUseInstanceDefault.get()).not.toBeChecked(); | |||||
expect(ui.radioButtonUseSpecific.get()).toBeChecked(); | |||||
expect(ui.newAnalysisWarningMessage.get()).toBeInTheDocument(); | |||||
expect(ui.selectUseSpecificProfile.get()).toBeInTheDocument(); | |||||
await selectEvent.select(ui.selectUseSpecificProfile.get(), 'html default profile'); | |||||
await user.click(ui.buttonSave.get()); | |||||
expect(addGlobalSuccessMessage).toHaveBeenCalledWith( | |||||
'project_quality_profile.successfully_updated.HTML' | |||||
); | |||||
// Updates the page after API call | |||||
expect(ui.htmlProfile.query()).not.toBeInTheDocument(); | |||||
expect(ui.htmlDefaultProfile.get()).toBeInTheDocument(); | |||||
expect(ui.builtInTag.get()).toBeInTheDocument(); | |||||
}); | |||||
it('should call authorization api when permissions is not proper', () => { | |||||
renderProjectQualityProfilesApp({}, { configuration: { showQualityProfiles: false } }); | |||||
expect(handleRequiredAuthorization).toHaveBeenCalled(); | |||||
}); | |||||
it('should still show page with add language button when api fails', () => { | |||||
jest.mocked(searchQualityProfiles).mockRejectedValueOnce(null); | |||||
jest.mocked(getProfileProjects).mockRejectedValueOnce(null); | |||||
renderProjectQualityProfilesApp(); | |||||
expect(ui.pageTitle.get()).toBeInTheDocument(); | |||||
expect(ui.pageSubTitle.get()).toBeInTheDocument(); | |||||
expect(ui.pageDescription.get()).toBeInTheDocument(); | |||||
expect(ui.addLanguageButton.get()).toBeInTheDocument(); | |||||
}); | |||||
function renderProjectQualityProfilesApp( | |||||
context?: RenderContext, | |||||
componentOverrides: Partial<Component> = { configuration: { showQualityProfiles: true } } | |||||
) { | |||||
return renderAppWithComponentContext('project/quality_profiles', routes, context, { | |||||
component: mockComponent(componentOverrides), | |||||
}); | |||||
} |
import * as React from 'react'; | import * as React from 'react'; | ||||
import { Profile } from '../../../api/quality-profiles'; | import { Profile } from '../../../api/quality-profiles'; | ||||
import withLanguagesContext from '../../../app/components/languages/withLanguagesContext'; | import withLanguagesContext from '../../../app/components/languages/withLanguagesContext'; | ||||
import { ButtonLink, SubmitButton } from '../../../components/controls/buttons'; | |||||
import Select, { LabelValueSelectOption } from '../../../components/controls/Select'; | import Select, { LabelValueSelectOption } from '../../../components/controls/Select'; | ||||
import SimpleModal from '../../../components/controls/SimpleModal'; | import SimpleModal from '../../../components/controls/SimpleModal'; | ||||
import { ButtonLink, SubmitButton } from '../../../components/controls/buttons'; | |||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { Languages } from '../../../types/languages'; | import { Languages } from '../../../types/languages'; | ||||
import { Dict } from '../../../types/types'; | import { Dict } from '../../../types/types'; | ||||
className="abs-width-300" | className="abs-width-300" | ||||
isDisabled={submitting} | isDisabled={submitting} | ||||
id="language" | id="language" | ||||
aria-label={translate( | |||||
'project_quality_profile.add_language_modal.choose_language' | |||||
)} | |||||
onChange={({ value }: LabelValueSelectOption) => { | onChange={({ value }: LabelValueSelectOption) => { | ||||
setSelected({ language: value, key: undefined }); | setSelected({ language: value, key: undefined }); | ||||
}} | }} | ||||
className="abs-width-300" | className="abs-width-300" | ||||
isDisabled={submitting || !language} | isDisabled={submitting || !language} | ||||
id="profiles" | id="profiles" | ||||
aria-label={translate( | |||||
'project_quality_profile.add_language_modal.choose_profile' | |||||
)} | |||||
onChange={({ value }: ProfileOption) => setSelected({ language, key: value })} | onChange={({ value }: ProfileOption) => setSelected({ language, key: value })} | ||||
options={profileOptions} | options={profileOptions} | ||||
components={{ | components={{ |
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { Profile } from '../../../api/quality-profiles'; | import { Profile } from '../../../api/quality-profiles'; | ||||
import { ButtonLink, SubmitButton } from '../../../components/controls/buttons'; | |||||
import Radio from '../../../components/controls/Radio'; | import Radio from '../../../components/controls/Radio'; | ||||
import Select from '../../../components/controls/Select'; | import Select from '../../../components/controls/Select'; | ||||
import SimpleModal from '../../../components/controls/SimpleModal'; | import SimpleModal from '../../../components/controls/SimpleModal'; | ||||
import { ButtonLink, SubmitButton } from '../../../components/controls/buttons'; | |||||
import { Alert } from '../../../components/ui/Alert'; | import { Alert } from '../../../components/ui/Alert'; | ||||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | import { translate, translateWithParameters } from '../../../helpers/l10n'; | ||||
import { Component } from '../../../types/types'; | import { Component } from '../../../types/types'; | ||||
<div className="display-flex-center"> | <div className="display-flex-center"> | ||||
<Select | <Select | ||||
className="abs-width-300" | className="abs-width-300" | ||||
aria-label={translate('project_quality_profile.always_use_specific')} | |||||
isDisabled={submitting || hasSelectedSysDefault} | isDisabled={submitting || hasSelectedSysDefault} | ||||
onChange={({ value }: ProfileOption) => setSelected(value)} | onChange={({ value }: ProfileOption) => setSelected(value)} | ||||
options={profileOptions} | options={profileOptions} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import { shallow, ShallowWrapper } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import Select from '../../../../components/controls/Select'; | |||||
import SimpleModal from '../../../../components/controls/SimpleModal'; | |||||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||||
import { AddLanguageModal, AddLanguageModalProps } from '../AddLanguageModal'; | |||||
it('should render correctly', () => { | |||||
expect(diveIntoSimpleModal(shallowRender())).toMatchSnapshot('default'); | |||||
}); | |||||
it('should correctly handle changes', () => { | |||||
const onSubmit = jest.fn(); | |||||
const wrapper = shallowRender({ onSubmit }); | |||||
const langSelect = getLanguageSelect(wrapper); | |||||
let profileSelect = getProfileSelect(wrapper); | |||||
// Language select should only have 2; JS is not available. Profile Select | |||||
// should have none, as no language is selected yet. | |||||
expect(langSelect.props().options).toHaveLength(2); | |||||
expect(profileSelect.props().options).toHaveLength(0); | |||||
// Choose CSS. | |||||
const langChange = langSelect.props().onChange; | |||||
expect(langChange).toBeDefined(); | |||||
langChange!({ value: 'css' }); | |||||
// Should now show 2 available profiles. | |||||
profileSelect = getProfileSelect(wrapper); | |||||
expect(profileSelect.props().options).toHaveLength(2); | |||||
expect(profileSelect.props().options).toEqual( | |||||
expect.arrayContaining([expect.objectContaining({ isDisabled: true })]) | |||||
); | |||||
// Choose 1 profile. | |||||
const profileChange = profileSelect.props().onChange; | |||||
expect(profileChange).toBeDefined(); | |||||
profileChange!({ value: 'css2' }); | |||||
submitSimpleModal(wrapper); | |||||
expect(onSubmit).toHaveBeenLastCalledWith('css2'); | |||||
}); | |||||
function diveIntoSimpleModal(wrapper: ShallowWrapper) { | |||||
return wrapper.find(SimpleModal).dive().children(); | |||||
} | |||||
function getLanguageSelect(wrapper: ShallowWrapper) { | |||||
return diveIntoSimpleModal(wrapper).find(Select).at(0); | |||||
} | |||||
function getProfileSelect(wrapper: ShallowWrapper) { | |||||
return diveIntoSimpleModal(wrapper).find(Select).at(1); | |||||
} | |||||
function submitSimpleModal(wrapper: ShallowWrapper) { | |||||
wrapper.find(SimpleModal).props().onSubmit(); | |||||
} | |||||
function shallowRender(props: Partial<AddLanguageModalProps> = {}) { | |||||
return shallow<AddLanguageModalProps>( | |||||
<AddLanguageModal | |||||
languages={{ | |||||
css: { key: 'css', name: 'CSS' }, | |||||
ts: { key: 'ts', name: 'TS' }, | |||||
js: { key: 'js', name: 'JS' }, | |||||
}} | |||||
onClose={jest.fn()} | |||||
onSubmit={jest.fn()} | |||||
profilesByLanguage={{ | |||||
css: [ | |||||
mockQualityProfile({ key: 'css', name: 'CSS', activeRuleCount: 0 }), | |||||
mockQualityProfile({ key: 'css2', name: 'CSS 2' }), | |||||
], | |||||
ts: [mockQualityProfile({ key: 'ts', name: 'TS' })], | |||||
js: [mockQualityProfile({ key: 'js', name: 'JS' })], | |||||
}} | |||||
unavailableLanguages={['js']} | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import DisableableSelectOption from '../../../../components/common/DisableableSelectOption'; | |||||
import { mockProfileOption } from '../../../../helpers/mocks/quality-profiles'; | |||||
import LanguageProfileSelectOption, { | |||||
LanguageProfileSelectOptionProps, | |||||
ProfileOption, | |||||
} from '../LanguageProfileSelectOption'; | |||||
it('should render correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot('default'); | |||||
}); | |||||
describe('tooltip', () => { | |||||
it('should render correctly', () => { | |||||
expect(renderTooltipOverly()).toMatchSnapshot('default'); | |||||
expect(renderTooltipOverly({ label: undefined })).toMatchSnapshot('no link'); | |||||
}); | |||||
}); | |||||
function renderTooltipOverly(overrides: Partial<ProfileOption> = {}) { | |||||
const wrapper = shallowRender(overrides); | |||||
const { disableTooltipOverlay } = wrapper.find(DisableableSelectOption).props(); | |||||
return disableTooltipOverlay(); | |||||
} | |||||
function shallowRender(overrides: Partial<ProfileOption> = {}) { | |||||
// satisfy the required props that the option actually gets from the select | |||||
const optionProps = {} as LanguageProfileSelectOptionProps; | |||||
return shallow( | |||||
<LanguageProfileSelectOption {...optionProps} data={{ ...mockProfileOption(), ...overrides }} /> | |||||
); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2023 SonarSource SA | |||||
* mailto:info AT sonarsource DOT com | |||||
* | |||||
* This program is free software; you can redistribute it and/or | |||||
* modify it under the terms of the GNU Lesser General Public | |||||
* License as published by the Free Software Foundation; either | |||||
* version 3 of the License, or (at your option) any later version. | |||||
* | |||||
* This program is distributed in the hope that it will be useful, | |||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
* Lesser General Public License for more details. | |||||
* | |||||
* You should have received a copy of the GNU Lesser General Public License | |||||
* along with this program; if not, write to the Free Software Foundation, | |||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
*/ | |||||
import { shallow, ShallowWrapper } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import Radio from '../../../../components/controls/Radio'; | |||||
import Select from '../../../../components/controls/Select'; | |||||
import SimpleModal from '../../../../components/controls/SimpleModal'; | |||||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||||
import SetQualityProfileModal, { SetQualityProfileModalProps } from '../SetQualityProfileModal'; | |||||
it('should render correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot('default'); | |||||
expect(shallowRender({ usesDefault: true })).toMatchSnapshot('inherits system default'); | |||||
expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('needs reanalysis'); | |||||
}); | |||||
it('should correctly handle changes', () => { | |||||
const onSubmit = jest.fn(); | |||||
const wrapper = shallowRender({ onSubmit }, false); | |||||
diveIntoSimpleModal(wrapper).find(Radio).at(0).props().onCheck(''); | |||||
submitSimpleModal(wrapper); | |||||
expect(onSubmit).toHaveBeenLastCalledWith(undefined, 'foo'); | |||||
diveIntoSimpleModal(wrapper).find(Radio).at(1).props().onCheck(''); | |||||
diveIntoSimpleModal(wrapper).find(Select).props().onChange({ value: 'bar' }); | |||||
submitSimpleModal(wrapper); | |||||
expect(onSubmit).toHaveBeenLastCalledWith('bar', 'foo'); | |||||
const change = diveIntoSimpleModal(wrapper).find(Select).props().onChange; | |||||
expect(change).toBeDefined(); | |||||
change!({ value: 'bar' }); | |||||
submitSimpleModal(wrapper); | |||||
expect(onSubmit).toHaveBeenLastCalledWith('bar', 'foo'); | |||||
}); | |||||
function diveIntoSimpleModal(wrapper: ShallowWrapper) { | |||||
return wrapper.find(SimpleModal).dive().children(); | |||||
} | |||||
function submitSimpleModal(wrapper: ShallowWrapper) { | |||||
wrapper.find(SimpleModal).props().onSubmit(); | |||||
} | |||||
function shallowRender(props: Partial<SetQualityProfileModalProps> = {}, dive = true) { | |||||
const wrapper = shallow<SetQualityProfileModalProps>( | |||||
<SetQualityProfileModal | |||||
availableProfiles={[ | |||||
mockQualityProfile({ key: 'foo', isDefault: true, language: 'js' }), | |||||
mockQualityProfile({ key: 'bar', language: 'js', activeRuleCount: 0 }), | |||||
]} | |||||
component={mockComponent({ qualityProfiles: [{ key: 'foo', name: 'Foo', language: 'js' }] })} | |||||
currentProfile={mockQualityProfile({ key: 'foo', language: 'js' })} | |||||
onClose={jest.fn()} | |||||
onSubmit={jest.fn()} | |||||
usesDefault={false} | |||||
{...props} | |||||
/> | |||||
); | |||||
return dive ? diveIntoSimpleModal(wrapper) : wrapper; | |||||
} |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: default 1`] = ` | |||||
[ | |||||
<div | |||||
className="modal-head" | |||||
> | |||||
<h2> | |||||
project_quality_profile.add_language_modal.title | |||||
</h2> | |||||
</div>, | |||||
<form | |||||
onSubmit={[Function]} | |||||
> | |||||
<div | |||||
className="modal-body" | |||||
> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
<label | |||||
className="text-bold" | |||||
htmlFor="language" | |||||
> | |||||
project_quality_profile.add_language_modal.choose_language | |||||
</label> | |||||
</div> | |||||
<Select | |||||
className="abs-width-300" | |||||
id="language" | |||||
isDisabled={false} | |||||
onChange={[Function]} | |||||
options={ | |||||
[ | |||||
{ | |||||
"label": "CSS", | |||||
"value": "css", | |||||
}, | |||||
{ | |||||
"label": "TS", | |||||
"value": "ts", | |||||
}, | |||||
] | |||||
} | |||||
/> | |||||
</div> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
<label | |||||
className="text-bold" | |||||
htmlFor="profiles" | |||||
> | |||||
project_quality_profile.add_language_modal.choose_profile | |||||
</label> | |||||
</div> | |||||
<Select | |||||
className="abs-width-300" | |||||
components={ | |||||
{ | |||||
"Option": [Function], | |||||
} | |||||
} | |||||
id="profiles" | |||||
isDisabled={true} | |||||
onChange={[Function]} | |||||
options={[]} | |||||
value={null} | |||||
/> | |||||
</div> | |||||
</div> | |||||
<div | |||||
className="modal-foot" | |||||
> | |||||
<SubmitButton | |||||
disabled={true} | |||||
> | |||||
save | |||||
</SubmitButton> | |||||
<ButtonLink | |||||
disabled={false} | |||||
onClick={[Function]} | |||||
> | |||||
cancel | |||||
</ButtonLink> | |||||
</div> | |||||
</form>, | |||||
] | |||||
`; |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: default 1`] = ` | |||||
<Option | |||||
data={ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "Profile 1", | |||||
"language": "Java", | |||||
"value": "profile-1", | |||||
} | |||||
} | |||||
> | |||||
<div> | |||||
<DisableableSelectOption | |||||
disableTooltipOverlay={[Function]} | |||||
disabledReason="project_quality_profile.add_language_modal.no_active_rules" | |||||
option={ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "Profile 1", | |||||
"language": "Java", | |||||
"value": "profile-1", | |||||
} | |||||
} | |||||
/> | |||||
</div> | |||||
</Option> | |||||
`; | |||||
exports[`tooltip should render correctly: default 1`] = ` | |||||
<React.Fragment> | |||||
<p> | |||||
project_quality_profile.add_language_modal.profile_unavailable_no_active_rules | |||||
</p> | |||||
<ForwardRef(Link) | |||||
to={ | |||||
{ | |||||
"pathname": "/profiles/show", | |||||
"search": "?name=Profile+1&language=Java", | |||||
} | |||||
} | |||||
> | |||||
project_quality_profile.add_language_modal.go_to_profile | |||||
</ForwardRef(Link)> | |||||
</React.Fragment> | |||||
`; | |||||
exports[`tooltip should render correctly: no link 1`] = ` | |||||
<React.Fragment> | |||||
<p> | |||||
project_quality_profile.add_language_modal.profile_unavailable_no_active_rules | |||||
</p> | |||||
</React.Fragment> | |||||
`; |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: default 1`] = ` | |||||
[ | |||||
<div | |||||
className="modal-head" | |||||
> | |||||
<h2> | |||||
project_quality_profile.change_lang_X_profile.JavaScript | |||||
</h2> | |||||
</div>, | |||||
<form | |||||
onSubmit={[Function]} | |||||
> | |||||
<div | |||||
className="modal-body" | |||||
> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<Radio | |||||
checked={false} | |||||
className="display-flex-start" | |||||
disabled={false} | |||||
onCheck={[Function]} | |||||
value="-1" | |||||
> | |||||
<div | |||||
className="spacer-left" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
project_quality_profile.always_use_default | |||||
</div> | |||||
<div | |||||
className="display-flex-center" | |||||
> | |||||
<span | |||||
className="text-muted spacer-right" | |||||
> | |||||
current_noun | |||||
: | |||||
</span> | |||||
name | |||||
</div> | |||||
</div> | |||||
</Radio> | |||||
</div> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<Radio | |||||
checked={true} | |||||
className="display-flex-start" | |||||
disabled={false} | |||||
onCheck={[Function]} | |||||
value="foo" | |||||
> | |||||
<div | |||||
className="spacer-left" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
project_quality_profile.always_use_specific | |||||
</div> | |||||
<div | |||||
className="display-flex-center" | |||||
> | |||||
<Select | |||||
className="abs-width-300" | |||||
components={ | |||||
{ | |||||
"Option": [Function], | |||||
} | |||||
} | |||||
isDisabled={false} | |||||
onChange={[Function]} | |||||
options={ | |||||
[ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "foo", | |||||
}, | |||||
{ | |||||
"isDisabled": true, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "bar", | |||||
}, | |||||
] | |||||
} | |||||
value={ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "foo", | |||||
} | |||||
} | |||||
/> | |||||
</div> | |||||
</div> | |||||
</Radio> | |||||
</div> | |||||
</div> | |||||
<div | |||||
className="modal-foot" | |||||
> | |||||
<SubmitButton | |||||
disabled={true} | |||||
> | |||||
save | |||||
</SubmitButton> | |||||
<ButtonLink | |||||
disabled={false} | |||||
onClick={[Function]} | |||||
> | |||||
cancel | |||||
</ButtonLink> | |||||
</div> | |||||
</form>, | |||||
] | |||||
`; | |||||
exports[`should render correctly: inherits system default 1`] = ` | |||||
[ | |||||
<div | |||||
className="modal-head" | |||||
> | |||||
<h2> | |||||
project_quality_profile.change_lang_X_profile.JavaScript | |||||
</h2> | |||||
</div>, | |||||
<form | |||||
onSubmit={[Function]} | |||||
> | |||||
<div | |||||
className="modal-body" | |||||
> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<Radio | |||||
checked={true} | |||||
className="display-flex-start" | |||||
disabled={false} | |||||
onCheck={[Function]} | |||||
value="-1" | |||||
> | |||||
<div | |||||
className="spacer-left" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
project_quality_profile.always_use_default | |||||
</div> | |||||
<div | |||||
className="display-flex-center" | |||||
> | |||||
<span | |||||
className="text-muted spacer-right" | |||||
> | |||||
current_noun | |||||
: | |||||
</span> | |||||
name | |||||
</div> | |||||
</div> | |||||
</Radio> | |||||
</div> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<Radio | |||||
checked={false} | |||||
className="display-flex-start" | |||||
disabled={false} | |||||
onCheck={[Function]} | |||||
value="foo" | |||||
> | |||||
<div | |||||
className="spacer-left" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
project_quality_profile.always_use_specific | |||||
</div> | |||||
<div | |||||
className="display-flex-center" | |||||
> | |||||
<Select | |||||
className="abs-width-300" | |||||
components={ | |||||
{ | |||||
"Option": [Function], | |||||
} | |||||
} | |||||
isDisabled={true} | |||||
onChange={[Function]} | |||||
options={ | |||||
[ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "foo", | |||||
}, | |||||
{ | |||||
"isDisabled": true, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "bar", | |||||
}, | |||||
] | |||||
} | |||||
value={ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "foo", | |||||
} | |||||
} | |||||
/> | |||||
</div> | |||||
</div> | |||||
</Radio> | |||||
</div> | |||||
</div> | |||||
<div | |||||
className="modal-foot" | |||||
> | |||||
<SubmitButton | |||||
disabled={true} | |||||
> | |||||
save | |||||
</SubmitButton> | |||||
<ButtonLink | |||||
disabled={false} | |||||
onClick={[Function]} | |||||
> | |||||
cancel | |||||
</ButtonLink> | |||||
</div> | |||||
</form>, | |||||
] | |||||
`; | |||||
exports[`should render correctly: needs reanalysis 1`] = ` | |||||
[ | |||||
<div | |||||
className="modal-head" | |||||
> | |||||
<h2> | |||||
project_quality_profile.change_lang_X_profile.JavaScript | |||||
</h2> | |||||
</div>, | |||||
<form | |||||
onSubmit={[Function]} | |||||
> | |||||
<div | |||||
className="modal-body" | |||||
> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<Radio | |||||
checked={false} | |||||
className="display-flex-start" | |||||
disabled={false} | |||||
onCheck={[Function]} | |||||
value="-1" | |||||
> | |||||
<div | |||||
className="spacer-left" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
project_quality_profile.always_use_default | |||||
</div> | |||||
<div | |||||
className="display-flex-center" | |||||
> | |||||
<span | |||||
className="text-muted spacer-right" | |||||
> | |||||
current_noun | |||||
: | |||||
</span> | |||||
name | |||||
</div> | |||||
</div> | |||||
</Radio> | |||||
</div> | |||||
<div | |||||
className="big-spacer-bottom" | |||||
> | |||||
<Radio | |||||
checked={true} | |||||
className="display-flex-start" | |||||
disabled={false} | |||||
onCheck={[Function]} | |||||
value="foo" | |||||
> | |||||
<div | |||||
className="spacer-left" | |||||
> | |||||
<div | |||||
className="little-spacer-bottom" | |||||
> | |||||
project_quality_profile.always_use_specific | |||||
</div> | |||||
<div | |||||
className="display-flex-center" | |||||
> | |||||
<Select | |||||
className="abs-width-300" | |||||
components={ | |||||
{ | |||||
"Option": [Function], | |||||
} | |||||
} | |||||
isDisabled={false} | |||||
onChange={[Function]} | |||||
options={ | |||||
[ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "foo", | |||||
}, | |||||
{ | |||||
"isDisabled": true, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "bar", | |||||
}, | |||||
] | |||||
} | |||||
value={ | |||||
{ | |||||
"isDisabled": false, | |||||
"label": "name", | |||||
"language": "js", | |||||
"value": "foo", | |||||
} | |||||
} | |||||
/> | |||||
</div> | |||||
</div> | |||||
</Radio> | |||||
</div> | |||||
<Alert | |||||
variant="warning" | |||||
> | |||||
project_quality_profile.requires_new_analysis | |||||
</Alert> | |||||
</div> | |||||
<div | |||||
className="modal-foot" | |||||
> | |||||
<SubmitButton | |||||
disabled={true} | |||||
> | |||||
save | |||||
</SubmitButton> | |||||
<ButtonLink | |||||
disabled={false} | |||||
onClick={[Function]} | |||||
> | |||||
cancel | |||||
</ButtonLink> | |||||
</div> | |||||
</form>, | |||||
] | |||||
`; |