From 270239f18e866cecaa3bda198f24515b48c05e3b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Thu, 31 May 2018 13:25:16 +0200 Subject: [PATCH] SONAR-10821 Display details about external rules --- .../components/RuleDetailsDescription.tsx | 11 +- .../components/RuleDetailsMeta.tsx | 48 ++- .../__tests__/RuleDetailsDescription-test.tsx | 90 ++++++ .../__tests__/RuleDetailsMeta-test.tsx | 84 ++++-- .../RuleDetailsDescription-test.tsx.snap | 64 ++++ .../RuleDetailsMeta-test.tsx.snap | 283 ++++++++++++++++++ .../resources/org/sonar/l10n/core.properties | 2 + 7 files changed, 548 insertions(+), 34 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsDescription-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsDescription-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx index b6e5b8455ef..7e8889b80ad 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx @@ -189,18 +189,19 @@ export default class RuleDetailsDescription extends React.PureComponent - {ruleDetails.isExternal ? ( -
- {translateWithParameters('issue.external_issue_description', ruleDetails.name)} -
- ) : ( + {hasDescription ? (
+ ) : ( +
+ {translateWithParameters('issue.external_issue_description', ruleDetails.name)} +
)} {!ruleDetails.templateKey && ( diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx index 52417451371..c74e8ad3df6 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx @@ -28,7 +28,7 @@ import LinkIcon from '../../../components/icons-components/LinkIcon'; import RuleScopeIcon from '../../../components/icons-components/RuleScopeIcon'; import Tooltip from '../../../components/controls/Tooltip'; import DocTooltip from '../../../components/docs/DocTooltip'; -import { translate } from '../../../helpers/l10n'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; import SeverityHelper from '../../../components/shared/SeverityHelper'; import Dropdown from '../../../components/controls/Dropdown'; @@ -47,6 +47,8 @@ interface Props { ruleDetails: RuleDetails; } +const EXTERNAL_RULE_REPO_PREFIX = 'external_'; + export default class RuleDetailsMeta extends React.PureComponent { renderType = () => { const { ruleDetails } = this.props; @@ -207,8 +209,29 @@ export default class RuleDetailsMeta extends React.PureComponent { ); }; + renderExternalBadge = () => { + const { ruleDetails } = this.props; + if (!ruleDetails.repo) { + return null; + } + const engine = ruleDetails.repo.replace(new RegExp(`^${EXTERNAL_RULE_REPO_PREFIX}`), ''); + if (!engine) { + return null; + } + return ( + +
  • +
    + {engine} +
    +
  • +
    + ); + }; + render() { const { ruleDetails } = this.props; + const hasTypeData = !ruleDetails.isExternal || ruleDetails.type !== 'UNKNOWN'; return (
    @@ -230,18 +253,27 @@ export default class RuleDetailsMeta extends React.PureComponent {
    - {!ruleDetails.isExternal && ( + {hasTypeData && (
      {this.renderType()} {this.renderSeverity()} - {this.renderStatus()} - {this.renderScope()} + {!ruleDetails.isExternal && ( + <> + {this.renderStatus()} + {this.renderScope()} + + )} {this.renderTags()} - {this.renderCreationDate()} + {!ruleDetails.isExternal && this.renderCreationDate()} {this.renderRepository()} - {this.renderTemplate()} - {this.renderParentTemplate()} - {this.renderRemediation()} + {!ruleDetails.isExternal && ( + <> + {this.renderTemplate()} + {this.renderParentTemplate()} + {this.renderRemediation()} + + )} + {ruleDetails.isExternal && this.renderExternalBadge()}
    )}
    diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsDescription-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsDescription-test.tsx new file mode 100644 index 00000000000..10179121078 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsDescription-test.tsx @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 RuleDetailsDescription from '../RuleDetailsDescription'; +import { click, change, waitAndUpdate } from '../../../../helpers/testUtils'; + +jest.mock('../../../../api/rules', () => ({ + updateRule: jest.fn().mockResolvedValue('updatedrule') +})); + +const RULE = { + key: 'squid:S1133', + repo: 'squid', + name: 'Deprecated code should be removed', + createdAt: '2013-07-26T09:40:51+0200', + htmlDesc: '

    Html Description

    ', + mdNote: 'Md Note', + severity: 'INFO', + status: 'READY', + lang: 'java', + langName: 'Java', + type: 'CODE_SMELL' +}; + +const EXTERNAL_RULE = { + key: 'external_xoo:OneExternalIssuePerLine', + repo: 'external_xoo', + name: 'xoo:OneExternalIssuePerLine', + status: 'READY', + isExternal: true, + type: 'UNKNOWN' +}; + +const EXTERNAL_RULE_WITH_DATA = { + key: 'external_xoo:OneExternalIssueWithDetailsPerLine', + repo: 'external_xoo', + name: 'One external issue per line', + createdAt: '2018-05-31T11:19:51+0200', + htmlDesc: '

    Html Description

    ', + status: 'READY', + isExternal: true, + type: 'BUG' +}; + +it('should display correctly', () => { + expect(getWrapper()).toMatchSnapshot(); + expect(getWrapper({ ruleDetails: EXTERNAL_RULE })).toMatchSnapshot(); + expect(getWrapper({ ruleDetails: EXTERNAL_RULE_WITH_DATA })).toMatchSnapshot(); +}); + +it('should add extra description', async () => { + const onChange = jest.fn(); + const wrapper = getWrapper({ canWrite: true, onChange }); + click(wrapper.find('#coding-rules-detail-extend-description')); + expect(wrapper.find('textarea').exists()).toBeTruthy(); + change(wrapper.find('textarea'), 'new description'); + click(wrapper.find('#coding-rules-detail-extend-description-submit')); + await waitAndUpdate(wrapper); + expect(onChange).toBeCalledWith('updatedrule'); +}); + +function getWrapper(props = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx index 0a725bc6d02..1380e6c0bfc 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx @@ -20,33 +20,61 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import RuleDetailsMeta from '../RuleDetailsMeta'; -import { RuleDetails } from '../../../../app/types'; +import { RuleScope } from '../../../../app/types'; import RuleDetailsTagsPopup from '../RuleDetailsTagsPopup'; -const ruleDetails: RuleDetails = { - createdAt: '', - repo: '', - key: 'key', - lang: '', - langName: '', - name: '', - severity: '', - status: '', - type: '' +const RULE = { + key: 'squid:S1133', + repo: 'squid', + name: 'Deprecated code should be removed', + createdAt: '2013-07-26T09:40:51+0200', + severity: 'INFO', + status: 'READY', + lang: 'java', + langName: 'Java', + scope: RuleScope.Main, + type: 'CODE_SMELL' }; +const EXTERNAL_RULE = { + key: 'external_xoo:OneExternalIssuePerLine', + repo: 'external_xoo', + name: 'xoo:OneExternalIssuePerLine', + createdAt: '2018-05-31T11:22:13+0200', + status: 'READY', + scope: RuleScope.All, + isExternal: true, + type: 'UNKNOWN' +}; + +const EXTERNAL_RULE_WITH_DATA = { + key: 'external_xoo:OneExternalIssueWithDetailsPerLine', + repo: 'external_xoo', + name: 'One external issue per line', + createdAt: '2018-05-31T11:19:51+0200', + severity: 'MAJOR', + status: 'READY', + tags: ['tag'], + lang: 'xoo', + langName: 'Xoo', + scope: RuleScope.All, + isExternal: true, + type: 'BUG' +}; + +it('should display right meta info', () => { + expect(getWrapper()).toMatchSnapshot(); + expect( + getWrapper({ hideSimilarRulesFilter: true, ruleDetails: EXTERNAL_RULE }) + ).toMatchSnapshot(); + expect( + getWrapper({ hideSimilarRulesFilter: true, ruleDetails: EXTERNAL_RULE_WITH_DATA }) + ).toMatchSnapshot(); +}); + it('should edit tags', () => { const onTagsChange = jest.fn(); - const wrapper = shallow( - - ); + const wrapper = getWrapper({ onTagsChange }); expect(wrapper.find('[data-meta="tags"]')).toMatchSnapshot(); const overlay = wrapper .find('[data-meta="tags"]') @@ -56,3 +84,17 @@ it('should edit tags', () => { overlay.props.setTags(['foo', 'bar']); expect(onTagsChange).toBeCalledWith(['foo', 'bar']); }); + +function getWrapper(props = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsDescription-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsDescription-test.tsx.snap new file mode 100644 index 00000000000..2e50c52359b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsDescription-test.tsx.snap @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display correctly 1`] = ` +
    +
    Html Description

    ", + } + } + /> +
    +
    +
    +
    +`; + +exports[`should display correctly 2`] = ` +
    +
    + issue.external_issue_description.xoo:OneExternalIssuePerLine +
    +
    +
    +
    +
    +`; + +exports[`should display correctly 3`] = ` +
    +
    Html Description

    ", + } + } + /> +
    +
    +
    +
    +`; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap index 8e501772a23..2f6ac55b176 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap @@ -1,5 +1,288 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`should display right meta info 1`] = ` +
    +
    +
    + + squid:S1133 + + + + + +
    +

    + + Deprecated code should be removed + +

    +
    +
      + +
    • + + issue.type.CODE_SMELL +
    • +
      + +
    • + +
    • +
      + + +
    • + + coding_rules.scope.MAIN +
    • +
      +
      +
    • + + } + overlayPlacement="bottom-left" + > + + +
    • +
    • + + coding_rules.available_since + + +
    • + +
    +
    +`; + +exports[`should display right meta info 2`] = ` +
    +
    +
    + + external_xoo:OneExternalIssuePerLine + +
    +

    + + xoo:OneExternalIssuePerLine + +

    +
    +
    +`; + +exports[`should display right meta info 3`] = ` +
    +
    +
    + + external_xoo:OneExternalIssueWithDetailsPerLine + +
    +

    + + One external issue per line + +

    +
    +
      + +
    • + + issue.type.BUG +
    • +
      + +
    • + +
    • +
      +
    • + + } + overlayPlacement="bottom-left" + > + + +
    • + +
    • +
      + xoo +
      +
    • +
      +
    +
    +`; + exports[`should edit tags 1`] = `