From 3bb32d2694602a7a8c63f307509b85ebb6a5bdcf Mon Sep 17 00:00:00 2001 From: Viktor Vorona Date: Tue, 15 Oct 2024 17:15:02 +0200 Subject: [PATCH] SONAR-23250 Show only changed impacts in quality profile comparison --- .../src/main/js/api/quality-profiles.ts | 13 ++--- .../__tests__/QualityProfilesApp-it.tsx | 48 ++++++++++++++++++- .../compare/ComparisonResults.tsx | 36 +++++++++----- .../src/main/js/helpers/testMocks.ts | 22 ++++++++- 4 files changed, 96 insertions(+), 23 deletions(-) diff --git a/server/sonar-web/src/main/js/api/quality-profiles.ts b/server/sonar-web/src/main/js/api/quality-profiles.ts index f8ed8782c79..21a61af4e4f 100644 --- a/server/sonar-web/src/main/js/api/quality-profiles.ts +++ b/server/sonar-web/src/main/js/api/quality-profiles.ts @@ -199,23 +199,18 @@ export function getProfileChangelog( export interface RuleCompare { cleanCodeAttributeCategory?: CleanCodeAttributeCategory; - impacts: SoftwareImpact[]; + impacts?: SoftwareImpact[]; key: string; - left?: { params: Dict; severity: string }; + left?: { impacts?: SoftwareImpact[]; params?: Dict; severity?: string }; name: string; - right?: { params: Dict; severity: string }; + right?: { impacts?: SoftwareImpact[]; params?: Dict; severity?: string }; } export interface CompareResponse { inLeft: Array; inRight: Array; left: { name: string }; - modified: Array< - RuleCompare & { - left: { params: Dict; severity: string }; - right: { params: Dict; severity: string }; - } - >; + modified: Array>>; right: { name: string }; } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx index bf0737f757b..9d0dd72c7a8 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx @@ -22,7 +22,7 @@ import userEvent from '@testing-library/user-event'; import { byLabelText, byRole, byText } from '~sonar-aligned/helpers/testSelector'; import QualityProfilesServiceMock from '../../../api/mocks/QualityProfilesServiceMock'; import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock'; -import { mockPaging, mockRule } from '../../../helpers/testMocks'; +import { mockCompareResult, mockPaging, mockRule } from '../../../helpers/testMocks'; import { renderAppRoutes } from '../../../helpers/testReactTestingUtils'; import routes from '../routes'; @@ -329,11 +329,57 @@ it('should be able to compare profiles', async () => { expect(ui.comparisonDiffTableHeading(1, 'java quality profile #2').get()).toBeInTheDocument(); expect(ui.comparisonModifiedTableHeading(1).get()).toBeInTheDocument(); + expect( + ui.comparisonModifiedTableHeading(1).byLabelText('severity_impact.BLOCKER').get(), + ).toBeInTheDocument(); + expect( + ui.comparisonModifiedTableHeading(1).byLabelText('severity_impact.LOW').get(), + ).toBeInTheDocument(); + // java quality profile is not editable expect(ui.activeRuleButton('java quality profile').query()).not.toBeInTheDocument(); expect(ui.deactivateRuleButton('java quality profile').query()).not.toBeInTheDocument(); }); +it('should be able to compare profiles without impacts', async () => { + // From the list page + const user = userEvent.setup(); + serviceMock.comparisonResult = mockCompareResult({ + modified: [ + { + impacts: [], + key: 'java:S1698', + name: '== and != should not be used when equals is overridden', + left: { + params: {}, + severity: 'MINOR', + }, + right: { + params: {}, + severity: 'CRITICAL', + }, + }, + ], + }); + serviceMock.setAdmin(); + renderQualityProfiles(); + + await user.click(await ui.listProfileActions('java quality profile', 'Java').find()); + expect(ui.compareButton.get()).toBeInTheDocument(); + await user.click(ui.compareButton.get()); + await user.click(ui.compareDropdown.get()); + await user.click(byRole('option', { name: 'java quality profile #2' }).get()); + + expect(await ui.comparisonModifiedTableHeading(1).find()).toBeInTheDocument(); + + expect( + ui + .comparisonModifiedTableHeading(1) + .byLabelText(/severity_impact/) + .query(), + ).not.toBeInTheDocument(); +}); + it('should be able to activate or deactivate rules in comparison page', async () => { // From the list page const user = userEvent.setup(); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx index 9af91ac5e32..1f66dbd9dfb 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx @@ -28,6 +28,7 @@ import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttr import SoftwareImpactPillList from '../../../components/shared/SoftwareImpactPillList'; import { getRulesUrl } from '../../../helpers/urls'; import { useStandardExperienceMode } from '../../../queries/settings'; +import { SoftwareImpact } from '../../../types/clean-code-taxonomy'; import { IssueSeverity } from '../../../types/issues'; import { Dict } from '../../../types/types'; import ComparisonResultActivation from './ComparisonResultActivation'; @@ -186,13 +187,13 @@ export default function ComparisonResults(props: Readonly) {
- +
- +
@@ -223,14 +224,24 @@ export default function ComparisonResults(props: Readonly) { ); } -function RuleCell({ rule, severity }: Readonly<{ rule: RuleCompare; severity?: string }>) { +type RuleCellProps = { + impacts?: SoftwareImpact[]; + rule: RuleCompare; + severity?: string; +}; + +function RuleCell({ rule, severity, impacts }: Readonly) { const { data: isStandardMode } = useStandardExperienceMode(); const shouldRenderSeverity = - isStandardMode && - Boolean(severity) && - rule.left && - rule.right && - isEqual(rule.left.params, rule.right.params); + isStandardMode &&Boolean(severity) && + rule.left?.severity && + rule.right?.severity && + !isEqual(rule.left.severity, + rule.right.severity); + const shouldRenderImpacts = + rule.impacts || + (rule.left?.impacts && rule.right?.impacts && + !isEqual(rule.left.impacts, rule.right.impacts)); return (
@@ -238,7 +249,7 @@ function RuleCell({ rule, severity }: Readonly<{ rule: RuleCompare; severity?: s {rule.name} - {!isStandardMode && (rule.cleanCodeAttributeCategory || rule.impacts.length > 0) && ( + {!isStandardMode && (rule.cleanCodeAttributeCategory || shouldRenderImpacts) && (
    {rule.cleanCodeAttributeCategory && (
  • @@ -247,9 +258,12 @@ function RuleCell({ rule, severity }: Readonly<{ rule: RuleCompare; severity?: s />
  • )} - {rule.impacts.length > 0 && ( + {((impacts && impacts.length > 0) || rule.impacts) && (
  • - +
  • )}
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index 2408be0427c..3abec815549 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -551,8 +551,26 @@ export function mockCompareResult(overrides: Partial = {}): Com impacts: [], key: 'java:S1698', name: '== and != should not be used when equals is overridden', - left: { params: {}, severity: 'MINOR' }, - right: { params: {}, severity: 'CRITICAL' }, + left: { + params: {}, + severity: 'MINOR', + impacts: [ + { + softwareQuality: SoftwareQuality.Security, + severity: SoftwareImpactSeverity.Blocker, + }, + ], + }, + right: { + params: {}, + severity: 'CRITICAL', + impacts: [ + { + softwareQuality: SoftwareQuality.Security, + severity: SoftwareImpactSeverity.Low, + }, + ], + }, }, ], ...overrides, -- 2.39.5