Quellcode durchsuchen

SONAR-10821 Display details about external rules

tags/7.5
Grégoire Aubert vor 6 Jahren
Ursprung
Commit
270239f18e

+ 6
- 5
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx Datei anzeigen

@@ -189,18 +189,19 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S

render() {
const { ruleDetails } = this.props;
const hasDescription = !ruleDetails.isExternal || ruleDetails.type !== 'UNKNOWN';

return (
<div className="js-rule-description">
{ruleDetails.isExternal ? (
<div className="coding-rules-detail-description rule-desc markdown">
{translateWithParameters('issue.external_issue_description', ruleDetails.name)}
</div>
) : (
{hasDescription ? (
<div
className="coding-rules-detail-description rule-desc markdown"
dangerouslySetInnerHTML={{ __html: ruleDetails.htmlDesc || '' }}
/>
) : (
<div className="coding-rules-detail-description rule-desc markdown">
{translateWithParameters('issue.external_issue_description', ruleDetails.name)}
</div>
)}

{!ruleDetails.templateKey && (

+ 40
- 8
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx Datei anzeigen

@@ -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<Props> {
renderType = () => {
const { ruleDetails } = this.props;
@@ -207,8 +209,29 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
);
};

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 (
<Tooltip overlay={translateWithParameters('coding_rules.external_rule.engine', engine)}>
<li className="coding-rules-detail-property">
<div className="outline-badge badge-tiny-height spacer-left vertical-text-top">
{engine}
</div>
</li>
</Tooltip>
);
};

render() {
const { ruleDetails } = this.props;
const hasTypeData = !ruleDetails.isExternal || ruleDetails.type !== 'UNKNOWN';
return (
<div className="js-rule-meta">
<header className="page-header">
@@ -230,18 +253,27 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
</h3>
</header>

{!ruleDetails.isExternal && (
{hasTypeData && (
<ul className="coding-rules-detail-properties">
{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()}
</ul>
)}
</div>

+ 90
- 0
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsDescription-test.tsx Datei anzeigen

@@ -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: '<p>Html Description</p>',
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: '<p>Html Description</p>',
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(
<RuleDetailsDescription
canWrite={false}
onChange={jest.fn()}
organization={undefined}
ruleDetails={RULE}
{...props}
/>
);
}

+ 63
- 21
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx Datei anzeigen

@@ -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(
<RuleDetailsMeta
canWrite={true}
onFilterChange={jest.fn()}
onTagsChange={onTagsChange}
organization={undefined}
referencedRepositories={{}}
ruleDetails={ruleDetails}
/>
);
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(
<RuleDetailsMeta
canWrite={true}
onFilterChange={jest.fn()}
onTagsChange={jest.fn()}
organization={undefined}
referencedRepositories={{}}
ruleDetails={RULE}
{...props}
/>
);
}

+ 64
- 0
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsDescription-test.tsx.snap Datei anzeigen

@@ -0,0 +1,64 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display correctly 1`] = `
<div
className="js-rule-description"
>
<div
className="coding-rules-detail-description rule-desc markdown"
dangerouslySetInnerHTML={
Object {
"__html": "<p>Html Description</p>",
}
}
/>
<div
className="coding-rules-detail-description coding-rules-detail-description-extra"
>
<div
id="coding-rules-detail-description-extra"
/>
</div>
</div>
`;

exports[`should display correctly 2`] = `
<div
className="js-rule-description"
>
<div
className="coding-rules-detail-description rule-desc markdown"
>
issue.external_issue_description.xoo:OneExternalIssuePerLine
</div>
<div
className="coding-rules-detail-description coding-rules-detail-description-extra"
>
<div
id="coding-rules-detail-description-extra"
/>
</div>
</div>
`;

exports[`should display correctly 3`] = `
<div
className="js-rule-description"
>
<div
className="coding-rules-detail-description rule-desc markdown"
dangerouslySetInnerHTML={
Object {
"__html": "<p>Html Description</p>",
}
}
/>
<div
className="coding-rules-detail-description coding-rules-detail-description-extra"
>
<div
id="coding-rules-detail-description-extra"
/>
</div>
</div>
`;

+ 283
- 0
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap Datei anzeigen

@@ -1,5 +1,288 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display right meta info 1`] = `
<div
className="js-rule-meta"
>
<header
className="page-header"
>
<div
className="pull-right"
>
<span
className="note text-middle"
>
squid:S1133
</span>
<Link
className="coding-rules-detail-permalink link-no-underline spacer-left text-middle"
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/coding_rules",
"query": Object {
"open": "squid:S1133",
"rule_key": "squid:S1133",
},
}
}
>
<LinkIcon />
</Link>
<SimilarRulesFilter
onFilterChange={[MockFunction]}
rule={
Object {
"createdAt": "2013-07-26T09:40:51+0200",
"key": "squid:S1133",
"lang": "java",
"langName": "Java",
"name": "Deprecated code should be removed",
"repo": "squid",
"scope": "MAIN",
"severity": "INFO",
"status": "READY",
"type": "CODE_SMELL",
}
}
/>
</div>
<h3
className="page-title coding-rules-detail-header"
>
<big>
Deprecated code should be removed
</big>
</h3>
</header>
<ul
className="coding-rules-detail-properties"
>
<Tooltip
overlay="coding_rules.type.tooltip.CODE_SMELL"
>
<li
className="coding-rules-detail-property"
data-meta="type"
>
<IssueTypeIcon
className="little-spacer-right"
query="CODE_SMELL"
/>
issue.type.CODE_SMELL
</li>
</Tooltip>
<Tooltip
overlay="default_severity"
>
<li
className="coding-rules-detail-property"
data-meta="severity"
>
<SeverityHelper
className="display-inline-flex-center"
severity="INFO"
/>
</li>
</Tooltip>
<React.Fragment>
<Tooltip
overlay="coding_rules.scope.title"
>
<li
className="coding-rules-detail-property"
>
<RuleScopeIcon
className="little-spacer-right"
/>
coding_rules.scope.MAIN
</li>
</Tooltip>
</React.Fragment>
<li
className="coding-rules-detail-property"
data-meta="tags"
>
<Dropdown
closeOnClick={false}
closeOnClickOutside={true}
overlay={
<RuleDetailsTagsPopup
organization={undefined}
setTags={[MockFunction]}
sysTags={Array []}
tags={Array []}
/>
}
overlayPlacement="bottom-left"
>
<Button
className="button-link"
>
<TagsList
allowUpdate={true}
tags={
Array [
"coding_rules.no_tags",
]
}
/>
</Button>
</Dropdown>
</li>
<li
className="coding-rules-detail-property"
data-meta="available-since"
>
<span
className="little-spacer-right"
>
coding_rules.available_since
</span>
<DateFormatter
date="2013-07-26T09:40:51+0200"
/>
</li>
<React.Fragment />
</ul>
</div>
`;

exports[`should display right meta info 2`] = `
<div
className="js-rule-meta"
>
<header
className="page-header"
>
<div
className="pull-right"
>
<span
className="note text-middle"
>
external_xoo:OneExternalIssuePerLine
</span>
</div>
<h3
className="page-title coding-rules-detail-header"
>
<big>
xoo:OneExternalIssuePerLine
</big>
</h3>
</header>
</div>
`;

exports[`should display right meta info 3`] = `
<div
className="js-rule-meta"
>
<header
className="page-header"
>
<div
className="pull-right"
>
<span
className="note text-middle"
>
external_xoo:OneExternalIssueWithDetailsPerLine
</span>
</div>
<h3
className="page-title coding-rules-detail-header"
>
<big>
One external issue per line
</big>
</h3>
</header>
<ul
className="coding-rules-detail-properties"
>
<Tooltip
overlay="coding_rules.type.tooltip.BUG"
>
<li
className="coding-rules-detail-property"
data-meta="type"
>
<IssueTypeIcon
className="little-spacer-right"
query="BUG"
/>
issue.type.BUG
</li>
</Tooltip>
<Tooltip
overlay="default_severity"
>
<li
className="coding-rules-detail-property"
data-meta="severity"
>
<SeverityHelper
className="display-inline-flex-center"
severity="MAJOR"
/>
</li>
</Tooltip>
<li
className="coding-rules-detail-property"
data-meta="tags"
>
<Dropdown
closeOnClick={false}
closeOnClickOutside={true}
overlay={
<RuleDetailsTagsPopup
organization={undefined}
setTags={[MockFunction]}
sysTags={Array []}
tags={
Array [
"tag",
]
}
/>
}
overlayPlacement="bottom-left"
>
<Button
className="button-link"
>
<TagsList
allowUpdate={true}
tags={
Array [
"tag",
]
}
/>
</Button>
</Dropdown>
</li>
<Tooltip
overlay="coding_rules.external_rule.engine.xoo"
>
<li
className="coding-rules-detail-property"
>
<div
className="outline-badge badge-tiny-height spacer-left vertical-text-top"
>
xoo
</div>
</li>
</Tooltip>
</ul>
</div>
`;

exports[`should edit tags 1`] = `
<li
className="coding-rules-detail-property"

+ 2
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Datei anzeigen

@@ -1334,6 +1334,8 @@ coding_rules.scope.MAIN=Main sources
coding_rules.scope.TEST=Test sources
coding_rules.scope.ALL=Main and Test sources

coding_rules.external_rule.engine=Rule provided by an external rule engine: {0}

#------------------------------------------------------------------------------
#
# EMAIL CONFIGURATION

Laden…
Abbrechen
Speichern