From 1c5b183ed53ebe484b245880b56f888150f7b44c Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Fri, 18 Aug 2023 12:52:22 +0200 Subject: [PATCH] SONAR-20197 Add CCT to rule list view --- .../coding-rules/__tests__/CodingRules-it.ts | 9 +++ .../coding-rules/components/RuleListItem.tsx | 28 +++++++--- .../main/js/apps/coding-rules/utils-tests.tsx | 10 ++++ .../apps/issues/__tests__/IssueHeader-it.tsx | 2 +- .../shared/CleanCodeAttributePill.tsx | 37 ++++++++++--- .../components/shared/SoftwareImpactPill.tsx | 7 ++- .../resources/org/sonar/l10n/core.properties | 55 +++++++++++++++++-- 7 files changed, 122 insertions(+), 26 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts index db7d2336942..1ec9fd4567d 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts @@ -24,6 +24,7 @@ import { RULE_TYPES } from '../../../helpers/constants'; import { parseDate } from '../../../helpers/dates'; import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; import { dateInputEvent, renderAppRoutes } from '../../../helpers/testReactTestingUtils'; +import { CleanCodeAttributeCategory, SoftwareQuality } from '../../../types/clean-code-taxonomy'; import { CurrentUser } from '../../../types/users'; import routes from '../routes'; import { getPageObjects } from '../utils-tests'; @@ -44,6 +45,14 @@ describe('Rules app list', () => { .allRulesName() .forEach((name) => expect(ui.ruleListItemLink(name).get()).toBeInTheDocument()); + // Render clean code attributes. + expect( + ui.ruleCleanCodeAttributeCategory(CleanCodeAttributeCategory.Adaptable).getAll().length + ).toBeGreaterThan(1); + expect(ui.ruleSoftwareQuality(SoftwareQuality.Maintainability).getAll().length).toBeGreaterThan( + 1 + ); + // Renders type facets RULE_TYPES.map((type) => `issue.type.${type}`).forEach((name) => expect(ui.facetItem(name).get()).toBeInTheDocument() diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx index 3975a9cf1c2..1d29eddbcb0 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx @@ -24,8 +24,9 @@ import Link from '../../../components/common/Link'; import { Button } from '../../../components/controls/buttons'; import ConfirmButton from '../../../components/controls/ConfirmButton'; import Tooltip from '../../../components/controls/Tooltip'; -import IssueTypeIcon from '../../../components/icons/IssueTypeIcon'; import SeverityIcon from '../../../components/icons/SeverityIcon'; +import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttributePill'; +import SoftwareImpactPill from '../../../components/shared/SoftwareImpactPill'; import TagsList from '../../../components/tags/TagsList'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getRuleUrl } from '../../../helpers/urls'; @@ -231,6 +232,13 @@ export default class RuleListItem extends React.PureComponent {
+ {rule.cleanCodeAttributeCategory !== undefined && ( + + )} {rule.status !== 'READY' && ( {translate('rules.status', rule.status)} @@ -239,14 +247,16 @@ export default class RuleListItem extends React.PureComponent { {rule.langName} - - - - - {translate('issue.type', rule.type)} - - - + {rule.impacts !== undefined && + rule.impacts.map(({ severity, softwareQuality }) => ( + + ))} {allTags.length > 0 && ( + byText(`rule.clean_code_attribute_category.${category}.title_short`), + ruleCleanCodeAttribute: (attribute: CleanCodeAttribute) => + byText(new RegExp(`rule\\.clean_code_attribute\\.${attribute}$`)), + ruleSoftwareQuality: (quality: SoftwareQuality) => byText(`issue.software_quality.${quality}`), // Rule tags tagsDropdown: byRole('button', { name: /tags_list_x/ }), diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx index fce07fd1743..8804df16a5e 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx @@ -52,7 +52,7 @@ it('renders correctly', async () => { // CCT attribute const cctBadge = byText( - `issue.clean_code_attribute_category.${issue.cleanCodeAttributeCategory}.issue` + `issue.clean_code_attribute_category.${issue.cleanCodeAttributeCategory}.title_short` ).get(); expect(cctBadge).toBeInTheDocument(); await expect(cctBadge).toHaveATooltipWithContent( diff --git a/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx b/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx index 0657d5a5c79..43bcfcf23ea 100644 --- a/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx +++ b/server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx @@ -26,23 +26,37 @@ import DocumentationTooltip from '../common/DocumentationTooltip'; export interface Props { className?: string; + type?: 'issue' | 'rule'; cleanCodeAttributeCategory: CleanCodeAttributeCategory; cleanCodeAttribute?: CleanCodeAttribute; } export function CleanCodeAttributePill(props: Props) { - const { className, cleanCodeAttributeCategory, cleanCodeAttribute } = props; - - const translationKey = cleanCodeAttribute - ? `issue.clean_code_attribute.${cleanCodeAttribute}` - : `issue.clean_code_attribute_category.${cleanCodeAttributeCategory}`; + const { className, cleanCodeAttributeCategory, cleanCodeAttribute, type = 'issue' } = props; + const showAdvice = type === 'issue'; return ( -

{translate(translationKey, 'title')}

-

{translate(translationKey, 'advice')}

+

+ {translate( + type, + cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category', + cleanCodeAttribute ?? cleanCodeAttributeCategory, + 'title' + )} +

+ {showAdvice && ( +

+ {translate( + type, + cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category', + cleanCodeAttribute ?? cleanCodeAttributeCategory, + 'advice' + )} +

+ )} } links={[ @@ -54,10 +68,15 @@ export function CleanCodeAttributePill(props: Props) { > - {translate('issue.clean_code_attribute_category', cleanCodeAttributeCategory, 'issue')} + {translate( + type, + 'clean_code_attribute_category', + cleanCodeAttributeCategory, + 'title_short' + )} {cleanCodeAttribute && ( - | {translate('issue.clean_code_attribute', cleanCodeAttribute)} + | {translate(type, 'clean_code_attribute', cleanCodeAttribute)} )}
diff --git a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx index cdacee48e61..ff753d0a42f 100644 --- a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx +++ b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx @@ -29,11 +29,12 @@ import SoftwareImpactSeverityIcon from '../icons/SoftwareImpactSeverityIcon'; export interface Props { className?: string; severity: SoftwareImpactSeverity; + type?: 'issue' | 'rule'; quality: SoftwareQuality; } export default function SoftwareImpactPill(props: Props) { - const { className, severity, quality } = props; + const { className, severity, quality, type = 'issue' } = props; const variant = { [SoftwareImpactSeverity.High]: 'danger', @@ -45,8 +46,8 @@ export default function SoftwareImpactPill(props: Props) {