diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-09-01 16:33:48 +0200 |
---|---|---|
committer | Janos Gyerik <janos.gyerik@sonarsource.com> | 2017-09-12 11:34:58 +0200 |
commit | c2954e35714c703c84b0e4c2af4e0249751516b6 (patch) | |
tree | 2b41a712c1f161815fc936eb520ee04da49b2a07 /server/sonar-web/src/main/js/apps/projectQualityProfiles | |
parent | 081f3bce1dfa04d2445178045885bd8a50494f1f (diff) | |
download | sonarqube-c2954e35714c703c84b0e4c2af4e0249751516b6.tar.gz sonarqube-c2954e35714c703c84b0e4c2af4e0249751516b6.zip |
SONAR-9736 fix access to project admin pages
Diffstat (limited to 'server/sonar-web/src/main/js/apps/projectQualityProfiles')
12 files changed, 850 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/App.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/App.tsx new file mode 100644 index 00000000000..ccc85320a62 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/App.tsx @@ -0,0 +1,139 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 * as React from 'react'; +import Helmet from 'react-helmet'; +import Header from './Header'; +import Table from './Table'; +import { + associateProject, + dissociateProject, + searchQualityProfiles, + Profile +} from '../../api/quality-profiles'; +import { Component } from '../../app/types'; +import addGlobalSuccessMessage from '../../app/utils/addGlobalSuccessMessage'; +import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization'; +import { translate, translateWithParameters } from '../../helpers/l10n'; + +interface Props { + component: Component; + customOrganizations: boolean; +} + +interface State { + allProfiles?: Profile[]; + loading: boolean; + profiles?: Profile[]; +} + +export default class QualityProfiles extends React.PureComponent<Props, State> { + mounted: boolean; + state: State = { loading: true }; + + componentDidMount() { + this.mounted = true; + if (this.checkPermissions()) { + this.fetchProfiles(); + } else { + handleRequiredAuthorization(); + } + } + + componentWillUnmount() { + this.mounted = false; + } + + checkPermissions() { + const { configuration } = this.props.component; + const hasPermission = configuration && configuration.showQualityProfiles; + return !!hasPermission; + } + + fetchProfiles() { + const { component } = this.props; + const organization = this.props.customOrganizations ? component.organization : undefined; + Promise.all([ + searchQualityProfiles({ organization }), + searchQualityProfiles({ organization, projectKey: component.key }) + ]).then( + ([allProfiles, profiles]) => { + if (this.mounted) { + this.setState({ loading: false, allProfiles, profiles }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + } + + handleChangeProfile = (oldKey: string, newKey: string) => { + const { component } = this.props; + const { allProfiles, profiles } = this.state; + const newProfile = allProfiles && allProfiles.find(profile => profile.key === newKey); + const request = + newProfile && newProfile.isDefault + ? dissociateProject(oldKey, component.key) + : associateProject(newKey, component.key); + + return request.then(() => { + if (this.mounted && profiles && newProfile) { + // remove old profile, add new one + const nextProfiles = [...profiles.filter(profile => profile.key !== oldKey), newProfile]; + this.setState({ profiles: nextProfiles }); + + addGlobalSuccessMessage( + translateWithParameters( + 'project_quality_profile.successfully_updated', + newProfile.languageName + ) + ); + } + }); + }; + + render() { + if (!this.checkPermissions()) { + return null; + } + + const { allProfiles, loading, profiles } = this.state; + + return ( + <div className="page page-limited"> + <Helmet title={translate('project_quality_profiles.page')} /> + + <Header /> + + {loading + ? <i className="spinner" /> + : allProfiles && + profiles && + <Table + allProfiles={allProfiles} + profiles={profiles} + onChangeProfile={this.handleChangeProfile} + />} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/Header.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/Header.tsx new file mode 100644 index 00000000000..a758189099d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/Header.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 * as React from 'react'; +import { translate } from '../../helpers/l10n'; + +export default function Header() { + return ( + <header className="page-header"> + <h1 className="page-title"> + {translate('project_quality_profiles.page')} + </h1> + <div className="page-description"> + {translate('project_quality_profiles.page.description')} + </div> + </header> + ); +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProfileRow.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProfileRow.tsx new file mode 100644 index 00000000000..0679b4463b4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProfileRow.tsx @@ -0,0 +1,122 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 * as React from 'react'; +import * as Select from 'react-select'; +import { Profile } from '../../api/quality-profiles'; +import { translate } from '../../helpers/l10n'; + +interface Props { + onChangeProfile: (oldProfile: string, newProfile: string) => Promise<void>; + possibleProfiles: Profile[]; + profile: Profile; +} + +interface State { + loading: boolean; +} + +export default class ProfileRow extends React.PureComponent<Props, State> { + mounted: boolean; + state: State = { loading: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + stopLoading = () => { + if (this.mounted) { + this.setState({ loading: false }); + } + }; + + handleChange = (option: { value: string }) => { + if (this.props.profile.key !== option.value) { + this.setState({ loading: true }); + this.props + .onChangeProfile(this.props.profile.key, option.value) + .then(this.stopLoading, this.stopLoading); + } + }; + + renderProfileName = (profileOption: { isDefault: boolean; label: string }) => { + if (profileOption.isDefault) { + return ( + <span> + <strong> + {translate('default')} + </strong> + {': '} + {profileOption.label} + </span> + ); + } + + return ( + <span> + {profileOption.label} + </span> + ); + }; + + renderProfileSelect() { + const { profile, possibleProfiles } = this.props; + + const options = possibleProfiles.map(profile => ({ + value: profile.key, + label: profile.name, + isDefault: profile.isDefault + })); + + return ( + <Select + clearable={false} + disabled={this.state.loading} + onChange={this.handleChange} + optionRenderer={this.renderProfileName} + options={options} + style={{ width: 300 }} + valueRenderer={this.renderProfileName} + value={profile.key} + /> + ); + } + + render() { + const { profile } = this.props; + + return ( + <tr data-key={profile.language}> + <td className="thin nowrap"> + {profile.languageName} + </td> + <td className="thin nowrap"> + {this.renderProfileSelect()} + </td> + <td> + {this.state.loading && <i className="spinner" />} + </td> + </tr> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx new file mode 100644 index 00000000000..43fca05f7ae --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/Table.tsx @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 * as React from 'react'; +import { groupBy, orderBy } from 'lodash'; +import ProfileRow from './ProfileRow'; +import { Profile } from '../../api/quality-profiles'; +import { translate } from '../../helpers/l10n'; + +interface Props { + allProfiles: Profile[]; + profiles: Profile[]; + onChangeProfile: (oldProfile: string, newProfile: string) => Promise<void>; +} + +export default function Table(props: Props) { + const profilesByLanguage = groupBy(props.allProfiles, 'language'); + const orderedProfiles = orderBy(props.profiles, 'languageName'); + + // set key to language to avoid destroying of component + const profileRows = orderedProfiles.map(profile => + <ProfileRow + key={profile.language} + profile={profile} + possibleProfiles={profilesByLanguage[profile.language]} + onChangeProfile={props.onChangeProfile} + /> + ); + + return ( + <table className="data zebra"> + <thead> + <tr> + <th className="thin nowrap"> + {translate('language')} + </th> + <th className="thin nowrap"> + {translate('quality_profile')} + </th> + {/* keep one empty cell for the spinner */} + <th> </th> + </tr> + </thead> + <tbody> + {profileRows} + </tbody> + </table> + ); +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/App-test.tsx new file mode 100644 index 00000000000..ab1f2ec7071 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/App-test.tsx @@ -0,0 +1,122 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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. + */ +jest.mock('../../../api/quality-profiles', () => ({ + associateProject: jest.fn(() => Promise.resolve()), + dissociateProject: jest.fn(() => Promise.resolve()), + searchQualityProfiles: jest.fn() +})); + +jest.mock('../../../app/utils/addGlobalSuccessMessage', () => ({ + default: jest.fn() +})); + +jest.mock('../../../app/utils/handleRequiredAuthorization', () => ({ + default: jest.fn() +})); + +import * as React from 'react'; +import { mount } from 'enzyme'; +import App from '../App'; + +const associateProject = require('../../../api/quality-profiles').associateProject as jest.Mock< + any +>; + +const dissociateProject = require('../../../api/quality-profiles') + .dissociateProject as jest.Mock<any>; + +const searchQualityProfiles = require('../../../api/quality-profiles') + .searchQualityProfiles as jest.Mock<any>; + +const addGlobalSuccessMessage = require('../../../app/utils/addGlobalSuccessMessage') + .default as jest.Mock<any>; + +const handleRequiredAuthorization = require('../../../app/utils/handleRequiredAuthorization') + .default as jest.Mock<any>; + +const component = { + analysisDate: '', + breadcrumbs: [], + configuration: { showQualityProfiles: true }, + key: 'foo', + name: 'foo', + organization: 'org', + qualifier: 'TRK', + version: '0.0.1' +}; + +it('checks permissions', () => { + handleRequiredAuthorization.mockClear(); + mount(<App component={{ ...component, configuration: undefined }} customOrganizations={false} />); + expect(handleRequiredAuthorization).toBeCalled(); +}); + +it('fetches profiles', () => { + searchQualityProfiles.mockClear(); + mount(<App component={component} customOrganizations={false} />); + expect(searchQualityProfiles.mock.calls).toHaveLength(2); + expect(searchQualityProfiles).toBeCalledWith({ organization: undefined }); + expect(searchQualityProfiles).toBeCalledWith({ organization: undefined, projectKey: 'foo' }); +}); + +it('fetches profiles with organization', () => { + searchQualityProfiles.mockClear(); + mount(<App component={component} customOrganizations={true} />); + expect(searchQualityProfiles.mock.calls).toHaveLength(2); + expect(searchQualityProfiles).toBeCalledWith({ organization: 'org' }); + expect(searchQualityProfiles).toBeCalledWith({ organization: 'org', projectKey: 'foo' }); +}); + +it('changes profile', () => { + associateProject.mockClear(); + dissociateProject.mockClear(); + addGlobalSuccessMessage.mockClear(); + const wrapper = mount(<App component={component} customOrganizations={false} />); + + const fooJava = randomProfile('foo-java', 'java'); + const fooJs = randomProfile('foo-js', 'js'); + const allProfiles = [ + fooJava, + randomProfile('bar-java', 'java'), + randomProfile('baz-java', 'java', true), + fooJs + ]; + const profiles = [fooJava, fooJs]; + wrapper.setState({ allProfiles, loading: false, profiles }); + + wrapper.find('Table').prop<Function>('onChangeProfile')('foo-java', 'bar-java'); + expect(associateProject).toBeCalledWith('bar-java', 'foo'); + + wrapper.find('Table').prop<Function>('onChangeProfile')('foo-java', 'baz-java'); + expect(dissociateProject).toBeCalledWith('foo-java', 'foo'); +}); + +function randomProfile(key: string, language: string, isDefault = false) { + return { + activeRuleCount: 17, + activeDeprecatedRuleCount: 0, + isDefault, + key, + name: key, + language: language, + languageName: language, + organization: 'org' + }; +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/Header-test.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/Header-test.tsx new file mode 100644 index 00000000000..adeee211e56 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/Header-test.tsx @@ -0,0 +1,26 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 * as React from 'react'; +import { shallow } from 'enzyme'; +import Header from '../Header'; + +it('renders', () => { + expect(shallow(<Header />)).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProfileRow-test.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProfileRow-test.tsx new file mode 100644 index 00000000000..cec351e32db --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/ProfileRow-test.tsx @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 * as React from 'react'; +import { shallow } from 'enzyme'; +import ProfileRow from '../ProfileRow'; +import { doAsync } from '../../../helpers/testUtils'; + +it('renders', () => { + expect( + shallow( + <ProfileRow + onChangeProfile={jest.fn()} + possibleProfiles={[randomProfile('bar'), randomProfile('baz')]} + profile={randomProfile('foo')} + /> + ) + ).toMatchSnapshot(); +}); + +it('changes profile', () => { + const onChangeProfile = jest.fn(() => Promise.resolve()); + const wrapper = shallow( + <ProfileRow + onChangeProfile={onChangeProfile} + possibleProfiles={[randomProfile('bar'), randomProfile('baz')]} + profile={randomProfile('foo')} + /> + ); + (wrapper.instance() as ProfileRow).mounted = true; + wrapper.find('Select').prop<Function>('onChange')({ value: 'baz' }); + expect(onChangeProfile).toBeCalledWith('foo', 'baz'); + expect(wrapper.state().loading).toBeTruthy(); + return doAsync().then(() => { + expect(wrapper.state().loading).toBeFalsy(); + }); +}); + +function randomProfile(key: string) { + return { + activeRuleCount: 17, + activeDeprecatedRuleCount: 0, + key, + name: key, + language: 'xoo', + languageName: 'xoo', + organization: 'org' + }; +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/Table-test.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/Table-test.tsx new file mode 100644 index 00000000000..84e70359fab --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/Table-test.tsx @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 * as React from 'react'; +import { shallow } from 'enzyme'; +import Table from '../Table'; + +it('renders', () => { + const fooJava = randomProfile('foo-java', 'java'); + const fooJs = randomProfile('foo-js', 'js'); + const allProfiles = [ + fooJava, + randomProfile('bar-java', 'java'), + randomProfile('baz-java', 'java'), + fooJs + ]; + const profiles = [fooJava, fooJs]; + expect( + shallow(<Table allProfiles={allProfiles} onChangeProfile={jest.fn()} profiles={profiles} />) + ).toMatchSnapshot(); +}); + +function randomProfile(key: string, language: string) { + return { + activeRuleCount: 17, + activeDeprecatedRuleCount: 0, + key, + name: key, + language: language, + languageName: language, + organization: 'org' + }; +} diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Header-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Header-test.tsx.snap new file mode 100644 index 00000000000..4f0e35f4a2f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Header-test.tsx.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders 1`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + project_quality_profiles.page + </h1> + <div + className="page-description" + > + project_quality_profiles.page.description + </div> +</header> +`; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/ProfileRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/ProfileRow-test.tsx.snap new file mode 100644 index 00000000000..541a117c889 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/ProfileRow-test.tsx.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders 1`] = ` +<tr + data-key="xoo" +> + <td + className="thin nowrap" + > + xoo + </td> + <td + className="thin nowrap" + > + <Select + addLabelText="Add \\"{label}\\"?" + arrowRenderer={[Function]} + autosize={true} + backspaceRemoves={true} + backspaceToRemoveMessage="Press backspace to remove {label}" + clearAllText="Clear all" + clearRenderer={[Function]} + clearValueText="Clear value" + clearable={false} + deleteRemoves={true} + delimiter="," + disabled={false} + escapeClearsValue={true} + filterOptions={[Function]} + ignoreAccents={true} + ignoreCase={true} + inputProps={Object {}} + isLoading={false} + joinValues={false} + labelKey="label" + matchPos="any" + matchProp="any" + menuBuffer={0} + menuRenderer={[Function]} + multi={false} + noResultsText="No results found" + onBlurResetsInput={true} + onChange={[Function]} + onCloseResetsInput={true} + optionComponent={[Function]} + optionRenderer={[Function]} + options={ + Array [ + Object { + "isDefault": undefined, + "label": "bar", + "value": "bar", + }, + Object { + "isDefault": undefined, + "label": "baz", + "value": "baz", + }, + ] + } + pageSize={5} + placeholder="Select..." + required={false} + scrollMenuIntoView={true} + searchable={true} + simpleValue={false} + style={ + Object { + "width": 300, + } + } + tabSelectsValue={true} + value="foo" + valueComponent={[Function]} + valueKey="value" + valueRenderer={[Function]} + /> + </td> + <td /> +</tr> +`; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap new file mode 100644 index 00000000000..b4b991157eb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Table-test.tsx.snap @@ -0,0 +1,99 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders 1`] = ` +<table + className="data zebra" +> + <thead> + <tr> + <th + className="thin nowrap" + > + language + </th> + <th + className="thin nowrap" + > + quality_profile + </th> + <th> + + </th> + </tr> + </thead> + <tbody> + <ProfileRow + onChangeProfile={[Function]} + possibleProfiles={ + Array [ + Object { + "activeDeprecatedRuleCount": 0, + "activeRuleCount": 17, + "key": "foo-java", + "language": "java", + "languageName": "java", + "name": "foo-java", + "organization": "org", + }, + Object { + "activeDeprecatedRuleCount": 0, + "activeRuleCount": 17, + "key": "bar-java", + "language": "java", + "languageName": "java", + "name": "bar-java", + "organization": "org", + }, + Object { + "activeDeprecatedRuleCount": 0, + "activeRuleCount": 17, + "key": "baz-java", + "language": "java", + "languageName": "java", + "name": "baz-java", + "organization": "org", + }, + ] + } + profile={ + Object { + "activeDeprecatedRuleCount": 0, + "activeRuleCount": 17, + "key": "foo-java", + "language": "java", + "languageName": "java", + "name": "foo-java", + "organization": "org", + } + } + /> + <ProfileRow + onChangeProfile={[Function]} + possibleProfiles={ + Array [ + Object { + "activeDeprecatedRuleCount": 0, + "activeRuleCount": 17, + "key": "foo-js", + "language": "js", + "languageName": "js", + "name": "foo-js", + "organization": "org", + }, + ] + } + profile={ + Object { + "activeDeprecatedRuleCount": 0, + "activeRuleCount": 17, + "key": "foo-js", + "language": "js", + "languageName": "js", + "name": "foo-js", + "organization": "org", + } + } + /> + </tbody> +</table> +`; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts b/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts new file mode 100644 index 00000000000..e342e0f4070 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/routes.ts @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { RouterState, IndexRouteProps } from 'react-router'; + +const routes = [ + { + getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) { + import('./App').then(i => callback(null, { component: i.default })); + } + } +]; + +export default routes; |