diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2019-04-02 12:35:38 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-04-08 20:21:06 +0200 |
commit | 497c52c32eec22b058284e3d51ff775f3be2cbcc (patch) | |
tree | 79ce2eec0550735d91b88e6bbfd27ca7cfd1b6a4 /server/sonar-web/src/main | |
parent | 99af758e84e5cc84f304e84e714ee0613fc32e59 (diff) | |
download | sonarqube-497c52c32eec22b058284e3d51ff775f3be2cbcc.tar.gz sonarqube-497c52c32eec22b058284e3d51ff775f3be2cbcc.zip |
SONAR-11734 Guard against missing language plugins
Diffstat (limited to 'server/sonar-web/src/main')
4 files changed, 350 insertions, 7 deletions
diff --git a/server/sonar-web/src/main/js/app/styles/components/page.css b/server/sonar-web/src/main/js/app/styles/components/page.css index 3f2e5960d57..633ad610da9 100644 --- a/server/sonar-web/src/main/js/app/styles/components/page.css +++ b/server/sonar-web/src/main/js/app/styles/components/page.css @@ -109,6 +109,7 @@ margin-bottom: 10px; margin-left: 10px; line-height: var(--controlHeight); + text-align: right; } .page-actions .badge { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx index 8258c8fdab5..10f8debfa0e 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx @@ -24,6 +24,7 @@ import RestoreProfileForm from './RestoreProfileForm'; import { Profile } from '../types'; import { getProfilePath } from '../utils'; import { Actions } from '../../../api/quality-profiles'; +import { Alert } from '../../../components/ui/Alert'; import { Button } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; import { withRouter, Router } from '../../../components/hoc/withRouter'; @@ -42,7 +43,7 @@ interface State { restoreFormOpen: boolean; } -class PageHeader extends React.PureComponent<Props, State> { +export class PageHeader extends React.PureComponent<Props, State> { state: State = { createFormOpen: false, restoreFormOpen: false @@ -76,13 +77,17 @@ class PageHeader extends React.PureComponent<Props, State> { }; render() { + const { actions, languages, organization, profiles } = this.props; return ( <header className="page-header"> <h1 className="page-title">{translate('quality_profiles.page')}</h1> - {this.props.actions.create && ( + {actions.create && ( <div className="page-actions"> - <Button id="quality-profiles-create" onClick={this.handleCreateClick}> + <Button + disabled={languages.length === 0} + id="quality-profiles-create" + onClick={this.handleCreateClick}> {translate('create')} </Button> <Button @@ -91,6 +96,11 @@ class PageHeader extends React.PureComponent<Props, State> { onClick={this.handleRestoreClick}> {translate('restore')} </Button> + {languages.length === 0 && ( + <Alert className="spacer-top" variant="warning"> + {translate('quality_profiles.no_languages_available')} + </Alert> + )} </div> )} @@ -112,17 +122,17 @@ class PageHeader extends React.PureComponent<Props, State> { <RestoreProfileForm onClose={this.closeRestoreForm} onRestore={this.props.updateProfiles} - organization={this.props.organization} + organization={organization} /> )} {this.state.createFormOpen && ( <CreateProfileForm - languages={this.props.languages} + languages={languages} onClose={this.closeCreateForm} onCreate={this.handleCreate} - organization={this.props.organization} - profiles={this.props.profiles} + organization={organization} + profiles={profiles} /> )} </header> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx new file mode 100644 index 00000000000..e271851d301 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { shallow } from 'enzyme'; +import { PageHeader } from '../PageHeader'; +import { mockLanguage, mockQualityProfile, mockRouter } from '../../../../helpers/testMocks'; +import { click } from '../../../../helpers/testUtils'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ actions: { create: true } })).toMatchSnapshot(); + expect(shallowRender({ actions: { create: true }, languages: [] })).toMatchSnapshot(); +}); + +it('should show a create form', () => { + const wrapper = shallowRender({ actions: { create: true } }); + click(wrapper.find('#quality-profiles-create')); + expect(wrapper).toMatchSnapshot(); +}); + +it('should show a restore form', () => { + const wrapper = shallowRender({ actions: { create: true } }); + click(wrapper.find('#quality-profiles-restore')); + expect(wrapper).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<PageHeader['props']> = {}) { + return shallow( + <PageHeader + actions={{ create: false }} + languages={[mockLanguage()]} + organization="foo" + profiles={[mockQualityProfile()]} + router={mockRouter()} + updateProfiles={jest.fn()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap new file mode 100644 index 00000000000..380a24788b4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap @@ -0,0 +1,276 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + quality_profiles.page + </h1> + <div + className="page-description markdown" + > + quality_profiles.intro1 + <br /> + quality_profiles.intro2 + <Link + className="spacer-left" + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to={ + Object { + "pathname": "/documentation/instance-administration/quality-profiles/", + } + } + > + learn_more + </Link> + </div> +</header> +`; + +exports[`should render correctly 2`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + quality_profiles.page + </h1> + <div + className="page-actions" + > + <Button + disabled={false} + id="quality-profiles-create" + onClick={[Function]} + > + create + </Button> + <Button + className="little-spacer-left" + id="quality-profiles-restore" + onClick={[Function]} + > + restore + </Button> + </div> + <div + className="page-description markdown" + > + quality_profiles.intro1 + <br /> + quality_profiles.intro2 + <Link + className="spacer-left" + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to={ + Object { + "pathname": "/documentation/instance-administration/quality-profiles/", + } + } + > + learn_more + </Link> + </div> +</header> +`; + +exports[`should render correctly 3`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + quality_profiles.page + </h1> + <div + className="page-actions" + > + <Button + disabled={true} + id="quality-profiles-create" + onClick={[Function]} + > + create + </Button> + <Button + className="little-spacer-left" + id="quality-profiles-restore" + onClick={[Function]} + > + restore + </Button> + <Alert + className="spacer-top" + variant="warning" + > + quality_profiles.no_languages_available + </Alert> + </div> + <div + className="page-description markdown" + > + quality_profiles.intro1 + <br /> + quality_profiles.intro2 + <Link + className="spacer-left" + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to={ + Object { + "pathname": "/documentation/instance-administration/quality-profiles/", + } + } + > + learn_more + </Link> + </div> +</header> +`; + +exports[`should show a create form 1`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + quality_profiles.page + </h1> + <div + className="page-actions" + > + <Button + disabled={false} + id="quality-profiles-create" + onClick={[Function]} + > + create + </Button> + <Button + className="little-spacer-left" + id="quality-profiles-restore" + onClick={[Function]} + > + restore + </Button> + </div> + <div + className="page-description markdown" + > + quality_profiles.intro1 + <br /> + quality_profiles.intro2 + <Link + className="spacer-left" + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to={ + Object { + "pathname": "/documentation/instance-administration/quality-profiles/", + } + } + > + learn_more + </Link> + </div> + <CreateProfileForm + languages={ + Array [ + Object { + "key": "css", + "name": "CSS", + }, + ] + } + onClose={[Function]} + onCreate={[Function]} + organization="foo" + profiles={ + Array [ + Object { + "activeDeprecatedRuleCount": 2, + "activeRuleCount": 10, + "childrenCount": 0, + "depth": 1, + "isBuiltIn": false, + "isDefault": false, + "isInherited": false, + "key": "key", + "language": "js", + "languageName": "JavaScript", + "name": "name", + "organization": "foo", + "projectCount": 3, + }, + ] + } + /> +</header> +`; + +exports[`should show a restore form 1`] = ` +<header + className="page-header" +> + <h1 + className="page-title" + > + quality_profiles.page + </h1> + <div + className="page-actions" + > + <Button + disabled={false} + id="quality-profiles-create" + onClick={[Function]} + > + create + </Button> + <Button + className="little-spacer-left" + id="quality-profiles-restore" + onClick={[Function]} + > + restore + </Button> + </div> + <div + className="page-description markdown" + > + quality_profiles.intro1 + <br /> + quality_profiles.intro2 + <Link + className="spacer-left" + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to={ + Object { + "pathname": "/documentation/instance-administration/quality-profiles/", + } + } + > + learn_more + </Link> + </div> + <RestoreProfileForm + onClose={[Function]} + onRestore={[MockFunction]} + organization="foo" + /> +</header> +`; |