From 06ee234f17c62f3296deb212e9b1ab20d812db42 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Thu, 29 Jun 2017 11:46:24 +0200 Subject: [PATCH] SONAR-9482 Display the number of missing Sonar Way rules on the QP page --- .../quality-profiles/details/ProfileRules.js | 266 +++++------------- .../details/ProfileRulesDeprecatedWarning.js | 44 +++ .../details/ProfileRulesRowOfType.js | 74 +++++ .../details/ProfileRulesRowTotal.js | 75 +++++ .../details/ProfileRulesSonarWayComparison.js | 47 ++++ .../details/__tests__/ProfileRules-test.js | 159 +++++++++++ .../ProfileRulesDeprecatedWarning-test.js} | 36 +-- .../__tests__/ProfileRulesRowOfType-test.js | 70 +++++ .../__tests__/ProfileRulesRowTotal-test.js | 44 +++ .../ProfileRulesSonarWayComparison-test.js | 36 +++ .../__snapshots__/ProfileRules-test.js.snap | 85 ++++++ ...ProfileRulesDeprecatedWarning-test.js.snap | 29 ++ .../ProfileRulesRowOfType-test.js.snap | 120 ++++++++ .../ProfileRulesRowTotal-test.js.snap | 112 ++++++++ ...rofileRulesSonarWayComparison-test.js.snap | 28 ++ .../main/js/apps/quality-profiles/styles.css | 9 +- .../resources/org/sonar/l10n/core.properties | 3 + 17 files changed, 1015 insertions(+), 222 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowOfType.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowTotal.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesSonarWayComparison.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.js rename server/sonar-web/src/main/js/apps/quality-profiles/details/{ProfileRulesRow.js => __tests__/ProfileRulesDeprecatedWarning-test.js} (60%) create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.js create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.js.snap create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.js.snap create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.js.snap create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.js.snap create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.js.snap diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js index a179e441d55..d9bb55dfefd 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js @@ -21,12 +21,14 @@ import React from 'react'; import { Link } from 'react-router'; import { keyBy } from 'lodash'; -import ProfileRulesRow from './ProfileRulesRow'; +import ProfileRulesRowOfType from './ProfileRulesRowOfType'; +import ProfileRulesRowTotal from './ProfileRulesRowTotal'; +import ProfileRulesDeprecatedWarning from './ProfileRulesDeprecatedWarning'; +import ProfileRulesSonarWayComparison from './ProfileRulesSonarWayComparison'; import { searchRules, takeFacet } from '../../../api/rules'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { formatMeasure } from '../../../helpers/measures'; -import { getRulesUrl, getDeprecatedActiveRulesUrl } from '../../../helpers/urls'; -import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; +import { getQualityProfiles } from '../../../api/quality-profiles'; +import { getRulesUrl } from '../../../helpers/urls'; +import { translate } from '../../../helpers/l10n'; import type { Profile } from '../propTypes'; const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL']; @@ -38,20 +40,24 @@ type Props = { }; type State = { - total: ?number, activatedTotal: ?number, + activatedByType?: { [string]: ?{ val: string, count: ?number } }, allByType?: { [string]: ?{ val: string, count: ?number } }, - activatedByType?: { [string]: ?{ val: string, count: ?number } } + compareToSonarWay: ?{ profile: string, profileName: string, missingRuleCount: number }, + loading: boolean, + total: ?number }; export default class ProfileRules extends React.PureComponent { mounted: boolean; props: Props; state: State = { - total: null, activatedTotal: null, + activatedByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val'), allByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val'), - activatedByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val') + compareToSonarWay: null, + loading: true, + total: null }; componentDidMount() { @@ -69,6 +75,16 @@ export default class ProfileRules extends React.PureComponent { this.mounted = false; } + loadProfile() { + if (this.props.profile.isBuiltIn) { + return Promise.resolve(null); + } + return getQualityProfiles({ + compareToSonarWay: true, + profile: this.props.profile.key + }); + } + loadAllRules() { return searchRules({ languages: this.props.profile.language, @@ -87,197 +103,44 @@ export default class ProfileRules extends React.PureComponent { } loadRules() { - Promise.all([this.loadAllRules(), this.loadActivatedRules()]).then(responses => { + this.setState({ loading: true }); + Promise.all([ + this.loadAllRules(), + this.loadActivatedRules(), + this.loadProfile() + ]).then(responses => { if (this.mounted) { - const [allRules, activatedRules] = responses; + const [allRules, activatedRules, showProfile] = responses; this.setState({ - total: allRules.total, activatedTotal: activatedRules.total, allByType: keyBy(takeFacet(allRules, 'types'), 'val'), - activatedByType: keyBy(takeFacet(activatedRules, 'types'), 'val') + activatedByType: keyBy(takeFacet(activatedRules, 'types'), 'val'), + compareToSonarWay: showProfile && showProfile.compareToSonarWay, + loading: false, + total: allRules.total }); } }); } - getTooltip(count: ?number, total: ?number) { - if (count == null || total == null) { - return ''; - } - - return translateWithParameters( - 'quality_profiles.x_activated_out_of_y', - formatMeasure(count, 'INT'), - formatMeasure(total, 'INT') - ); - } - - getTooltipForType(type: string) { - if ( - this.state.activatedByType && - this.state.activatedByType[type] && - this.state.allByType && - this.state.allByType[type] - ) { - const { count } = this.state.activatedByType[type]; - const total = this.state.allByType[type].count; - return this.getTooltip(count, total); - } - } - - renderActiveTitle() { - return ( - - {translate('total')} - - ); - } - - renderActiveCount() { - const rulesUrl = getRulesUrl( - { - qprofile: this.props.profile.key, - activation: 'true' - }, - this.props.organization - ); - - if (this.state.activatedTotal == null) { - return null; - } - - return ( - - - {formatMeasure(this.state.activatedTotal, 'SHORT_INT')} - - - ); - } - - renderActiveTotal() { - const rulesUrl = getRulesUrl( - { - qprofile: this.props.profile.key, - activation: 'false' - }, - this.props.organization - ); - - if (this.state.total == null || this.state.activatedTotal == null) { - return null; - } - - if (this.state.total === this.state.activatedTotal) { - return 0; - } - - return ( - - - {formatMeasure(this.state.total - this.state.activatedTotal, 'SHORT_INT')} - - - ); - } - - renderTitleForType(type: string) { - return ( - - - {translate('issue.type', type, 'plural')} - - ); - } - - renderCountForType(type: string) { - const rulesUrl = getRulesUrl( - { - qprofile: this.props.profile.key, - activation: 'true', - types: type - }, - this.props.organization - ); - - const count = this.state.activatedByType && this.state.activatedByType[type] + getRulesCountForType(type: string): ?number { + return this.state.activatedByType && this.state.activatedByType[type] ? this.state.activatedByType[type].count : null; - - if (count == null) { - return null; - } - - return ( - - {formatMeasure(count, 'SHORT_INT')} - - ); } - renderTotalForType(type: string) { - const rulesUrl = getRulesUrl( - { - qprofile: this.props.profile.key, - activation: 'false', - types: type - }, - this.props.organization - ); - - const count = this.state.activatedByType && this.state.activatedByType[type] - ? this.state.activatedByType[type].count - : null; - - const total = this.state.allByType && this.state.allByType[type] + getRulesTotalForType(type: string): ?number { + return this.state.allByType && this.state.allByType[type] ? this.state.allByType[type].count : null; - - if (count == null || total == null) { - return null; - } - - if (total === count) { - return 0; - } - - return ( - - {formatMeasure(total - count, 'SHORT_INT')} - - ); - } - - renderDeprecated() { - const { profile } = this.props; - - if (profile.activeDeprecatedRuleCount === 0) { - return null; - } - - const url = getDeprecatedActiveRulesUrl({ qprofile: profile.key }, this.props.organization); - - return ( -
-
- {translate('quality_profiles.deprecated_rules')} -
-
- - {profile.activeDeprecatedRuleCount} - -
-
- ); } render() { + const { organization, profile } = this.props; + const { compareToSonarWay } = this.state; const activateMoreUrl = getRulesUrl( - { - qprofile: this.props.profile.key, - activation: 'false' - }, - this.props.organization + { qprofile: profile.key, activation: 'false' }, + organization ); return ( @@ -289,38 +152,53 @@ export default class ProfileRules extends React.PureComponent {

{translate('rules')}

- Active - Inactive + {translate('active')} + {translate('inactive')} - {TYPES.map(type => ( - ))} {this.props.canAdmin && - !this.props.profile.isBuiltIn && + !profile.isBuiltIn &&
{translate('quality_profiles.activate_more')}
} - - {this.renderDeprecated()} + {profile.activeDeprecatedRuleCount > 0 && + } + {compareToSonarWay != null && + compareToSonarWay.missingRuleCount > 0 && + } ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.js new file mode 100644 index 00000000000..d50c81c227b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesDeprecatedWarning.js @@ -0,0 +1,44 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { Link } from 'react-router'; +import Tooltip from '../../../components/controls/Tooltip'; +import { getDeprecatedActiveRulesUrl } from '../../../helpers/urls'; +import { translate } from '../../../helpers/l10n'; + +type Props = { activeDeprecatedRules: number, organization: ?string, profile: string }; + +export default function ProfileRulesDeprecatedWarning(props: Props) { + const url = getDeprecatedActiveRulesUrl({ qprofile: props.profile }, props.organization); + return ( +
+ + {translate('quality_profiles.deprecated_rules')} + + + + + + {props.activeDeprecatedRules} + +
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowOfType.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowOfType.js new file mode 100644 index 00000000000..e6e04e59627 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowOfType.js @@ -0,0 +1,74 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { Link } from 'react-router'; +import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; +import { formatMeasure } from '../../../helpers/measures'; +import { getRulesUrl } from '../../../helpers/urls'; +import { translate } from '../../../helpers/l10n'; + +type Props = { + count: ?number, + organization: ?string, + qprofile: string, + total: ?number, + type: string +}; + +export default function ProfileRulesRowOfType(props: Props) { + const activeRulesUrl = getRulesUrl( + { qprofile: props.qprofile, activation: 'true', types: props.type }, + props.organization + ); + const inactiveRulesUrl = getRulesUrl( + { qprofile: props.qprofile, activation: 'false', types: props.type }, + props.organization + ); + let inactiveCount = null; + if (props.count != null && props.total != null) { + inactiveCount = props.total - props.count; + } + + return ( + + + + + {translate('issue.type', props.type, 'plural')} + + + + {props.count != null && + + {formatMeasure(props.count, 'SHORT_INT')} + } + + + {inactiveCount != null && + (inactiveCount > 0 + ? + {formatMeasure(inactiveCount, 'SHORT_INT')} + + : 0)} + + + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowTotal.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowTotal.js new file mode 100644 index 00000000000..f17bc2a8c86 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRowTotal.js @@ -0,0 +1,75 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { Link } from 'react-router'; +import { formatMeasure } from '../../../helpers/measures'; +import { getRulesUrl } from '../../../helpers/urls'; +import { translate } from '../../../helpers/l10n'; + +type Props = { + count: ?number, + organization: ?string, + qprofile: string, + total: ?number +}; + +export default function ProfileRulesRowTotal(props: Props) { + const activeRulesUrl = getRulesUrl( + { qprofile: props.qprofile, activation: 'true' }, + props.organization + ); + const inactiveRulesUrl = getRulesUrl( + { qprofile: props.qprofile, activation: 'false' }, + props.organization + ); + let inactiveCount = null; + if (props.count != null && props.total != null) { + inactiveCount = props.total - props.count; + } + + return ( + + + + {translate('total')} + + + + {props.count != null && + + + {formatMeasure(props.count, 'SHORT_INT')} + + } + + + {inactiveCount != null && + (inactiveCount > 0 + ? + + {formatMeasure(inactiveCount, 'SHORT_INT')} + + + : 0)} + + + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesSonarWayComparison.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesSonarWayComparison.js new file mode 100644 index 00000000000..97427ee1d34 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesSonarWayComparison.js @@ -0,0 +1,47 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { Link } from 'react-router'; +import Tooltip from '../../../components/controls/Tooltip'; +import { translate } from '../../../helpers/l10n'; + +type Props = { + organization: ?string, + profile: string, + sonarway: string, + sonarWayMissingRules: number +}; + +export default function ProfileRulesSonarWayComparison(props: Props) { + return ( +
+ + {translate('quality_profiles.sonarway_missing_rules')} + + + + + + {props.sonarWayMissingRules} + +
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.js new file mode 100644 index 00000000000..cd62b6a21d6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.js @@ -0,0 +1,159 @@ +/* + * 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 React from 'react'; +import { shallow } from 'enzyme'; +import ProfileRules from '../ProfileRules'; +import { doAsync } from '../../../../helpers/testUtils'; +import * as apiRules from '../../../../api/rules'; +import * as apiQP from '../../../../api/quality-profiles'; + +const PROFILE = { + key: 'foo', + name: 'Foo', + isBuiltIn: false, + isDefault: false, + isInherited: false, + language: 'java', + languageName: 'Java', + activeRuleCount: 68, + activeDeprecatedRuleCount: 0, + rulesUpdatedAt: '2017-06-28T12:58:44+0000', + depth: 0, + childrenCount: 0 +}; + +const apiResponseAll = { + total: 243, + facets: [ + { + property: 'types', + values: [ + { val: 'CODE_SMELL', count: 168 }, + { val: 'BUG', count: 68 }, + { val: 'VULNERABILITY', count: 7 } + ] + } + ] +}; + +const apiResponseActive = { + total: 68, + facets: [ + { + property: 'types', + values: [ + { val: 'BUG', count: 68 }, + { val: 'CODE_SMELL', count: 0 }, + { val: 'VULNERABILITY', count: 0 } + ] + } + ] +}; + +// Mock api some api functions +// eslint-disable-next-line +apiRules.searchRules = data => + Promise.resolve(data.activation === 'true' ? apiResponseActive : apiResponseAll); +// eslint-disable-next-line +apiQP.getQualityProfiles = () => + Promise.resolve({ + compareToSonarWay: { + profile: 'sonarway', + profileName: 'Sonar way', + missingRuleCount: 4 + } + }); + +it('should render the quality profiles rules with sonarway comparison', () => { + const wrapper = shallow(); + wrapper.instance().mounted = true; + wrapper.instance().loadRules(); + return doAsync(() => { + wrapper.update(); + expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(1); + expect(wrapper).toMatchSnapshot(); + }); +}); + +it('should show a button to activate more rules for admins', () => { + const wrapper = shallow(); + expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); +}); + +it('should show a deprecated rules warning message', () => { + const wrapper = shallow( + + ); + expect(wrapper.find('ProfileRulesDeprecatedWarning')).toMatchSnapshot(); +}); + +it('should not show a button to activate more rules on built in profiles', () => { + const wrapper = shallow( + + ); + expect(wrapper.find('.js-activate-rules')).toHaveLength(0); +}); + +it('should not show a button to activate more rules on built in profiles', () => { + const wrapper = shallow( + + ); + expect(wrapper.find('.js-activate-rules')).toHaveLength(0); +}); + +it('should not show sonarway comparison for built in profiles', () => { + // eslint-disable-next-line + apiQP.getQualityProfiles = jest.fn(() => Promise.resolve()); + const wrapper = shallow( + + ); + wrapper.instance().mounted = true; + wrapper.instance().loadRules(); + return doAsync(() => { + wrapper.update(); + expect(apiQP.getQualityProfiles).toHaveBeenCalledTimes(0); + expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0); + }); +}); + +it('should not show sonarway comparison if there is no missing rules', () => { + // eslint-disable-next-line + apiQP.getQualityProfiles = jest.fn(() => + Promise.resolve({ + compareToSonarWay: { + profile: 'sonarway', + profileName: 'Sonar way', + missingRuleCount: 0 + } + }) + ); + const wrapper = shallow(); + wrapper.instance().mounted = true; + wrapper.instance().loadRules(); + return doAsync(() => { + wrapper.update(); + expect(apiQP.getQualityProfiles).toHaveBeenCalledTimes(1); + expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.js similarity index 60% rename from server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js rename to server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.js index 9a7d140fa14..9a3387280e9 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.js @@ -19,31 +19,13 @@ */ // @flow import React from 'react'; +import { shallow } from 'enzyme'; +import ProfileRulesDeprecatedWarning from '../ProfileRulesDeprecatedWarning'; -type Props = { - renderCount: () => ?React.Element<*>, - renderTitle: () => React.Element<*>, - renderTotal: () => ?React.Element<*> -}; - -export default class ProfileRulesRow extends React.PureComponent { - props: Props; - - render() { - const { renderTitle, renderCount, renderTotal } = this.props; - - return ( - - - {renderTitle()} - - - {renderCount()} - - - {renderTotal()} - - - ); - } -} +it('should render correctly', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.js new file mode 100644 index 00000000000..dea445e01bf --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.js @@ -0,0 +1,70 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { shallow } from 'enzyme'; +import ProfileRulesRowOfType from '../ProfileRulesRowOfType'; + +it('should render correctly', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); + +it('should render correctly if there is 0 rules', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); + +it('should render correctly if there is missing data', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.js new file mode 100644 index 00000000000..20f2d697d5f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.js @@ -0,0 +1,44 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { shallow } from 'enzyme'; +import ProfileRulesRowTotal from '../ProfileRulesRowTotal'; + +it('should render correctly', () => { + expect( + shallow() + ).toMatchSnapshot(); +}); + +it('should render correctly if there is 0 rules', () => { + expect( + shallow() + ).toMatchSnapshot(); +}); + +it('should render correctly if there is missing data', () => { + expect( + shallow() + ).toMatchSnapshot(); + expect( + shallow() + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.js new file mode 100644 index 00000000000..35c0548ccb5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.js @@ -0,0 +1,36 @@ +/* + * 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. + */ +// @flow +import React from 'react'; +import { shallow } from 'enzyme'; +import ProfileRulesSonarWayComparison from '../ProfileRulesSonarWayComparison'; + +it('should render correctly', () => { + expect( + shallow( + + ) + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.js.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.js.snap new file mode 100644 index 00000000000..ea4e92251cb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.js.snap @@ -0,0 +1,85 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render the quality profiles rules with sonarway comparison 1`] = ` +
+
+ + + + + + + + + + + + + + +
+

+ rules +

+
+ active + + inactive +
+
+ +
+`; + +exports[`should show a button to activate more rules for admins 1`] = ` + + quality_profiles.activate_more + +`; + +exports[`should show a deprecated rules warning message 1`] = ` + +`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.js.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.js.snap new file mode 100644 index 00000000000..54fa91cc49d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +
+ + quality_profiles.deprecated_rules + + + + + + 18 + +
+`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.js.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.js.snap new file mode 100644 index 00000000000..62866095c17 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.js.snap @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` + + + + + issue.type.BUG.plural + + + + + 3 + + + + + 7 + + + +`; + +exports[`should render correctly if there is 0 rules 1`] = ` + + + + + issue.type.VULNERABILITY.plural + + + + + 0 + + + + + 0 + + + +`; + +exports[`should render correctly if there is missing data 1`] = ` + + + + + issue.type.VULNERABILITY.plural + + + + + 5 + + + + +`; + +exports[`should render correctly if there is missing data 2`] = ` + + + + + issue.type.VULNERABILITY.plural + + + + + +`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.js.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.js.snap new file mode 100644 index 00000000000..f8686659326 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.js.snap @@ -0,0 +1,112 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` + + + + total + + + + + + 3 + + + + + + + 7 + + + + +`; + +exports[`should render correctly if there is 0 rules 1`] = ` + + + + total + + + + + + 0 + + + + + + 0 + + + +`; + +exports[`should render correctly if there is missing data 1`] = ` + + + + total + + + + + + 5 + + + + + +`; + +exports[`should render correctly if there is missing data 2`] = ` + + + + total + + + + + +`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.js.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.js.snap new file mode 100644 index 00000000000..6864b28e71b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.js.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +
+ + quality_profiles.sonarway_missing_rules + + + + + + 158 + +
+`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/styles.css b/server/sonar-web/src/main/js/apps/quality-profiles/styles.css index 0498b5b1d48..2712ddf243e 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/styles.css +++ b/server/sonar-web/src/main/js/apps/quality-profiles/styles.css @@ -63,7 +63,9 @@ padding: 15px 20px 20px; } -.quality-profile-rules {} +.quality-profile-rules { + min-height: 182px; +} .quality-profile-rules > header { padding: 15px 20px; @@ -79,6 +81,11 @@ background-color: #f2dede; } +.quality-profile-rules-sonarway-missing { + padding: 15px 20px; + background-color: #fcf8e3; +} + .quality-profile-exporters { margin-top: 20px; } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index bc2d0f77ff4..f42eca5589d 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1514,7 +1514,10 @@ quality_profiles.latest_new_rules=Recently Added Rules quality_profiles.latest_new_rules.activated={0}, activated on {1} profile(s) quality_profiles.latest_new_rules.not_activated={0}, not yet activated quality_profiles.deprecated_rules=Deprecated Rules +quality_profiles.deprecated_rules_description=These deprecated rules will eventually disappear. You should proactively investigate replacing them. quality_profiles.deprecated_rules_are_still_activated=Deprecated rules are still activated on {0} quality profile(s): +quality_profiles.sonarway_missing_rules=Sonar way rules not included +quality_profiles.sonarway_missing_rules_description=Recommended rules are missing from your profile quality_profiles.stagnant_profiles=Stagnant Profiles quality_profiles.not_updated_more_than_year=The following profiles haven't been updated for more than 1 year: quality_profiles.exporters=Exporters -- 2.39.5