diff options
author | Viktor Vorona <viktor.vorona@sonarsource.com> | 2024-10-15 17:15:02 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-16 20:03:02 +0000 |
commit | 3bb32d2694602a7a8c63f307509b85ebb6a5bdcf (patch) | |
tree | 75d0c03e86e3f4a2266173fcf6fa262acf7c7d34 | |
parent | aff39a4a01010cd4e6425ecd6f17146dd3274f89 (diff) | |
download | sonarqube-3bb32d2694602a7a8c63f307509b85ebb6a5bdcf.tar.gz sonarqube-3bb32d2694602a7a8c63f307509b85ebb6a5bdcf.zip |
SONAR-23250 Show only changed impacts in quality profile comparison
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<string>; severity: string }; + left?: { impacts?: SoftwareImpact[]; params?: Dict<string>; severity?: string }; name: string; - right?: { params: Dict<string>; severity: string }; + right?: { impacts?: SoftwareImpact[]; params?: Dict<string>; severity?: string }; } export interface CompareResponse { inLeft: Array<RuleCompare>; inRight: Array<RuleCompare>; left: { name: string }; - modified: Array< - RuleCompare & { - left: { params: Dict<string>; severity: string }; - right: { params: Dict<string>; severity: string }; - } - >; + modified: Array<RuleCompare & Required<Pick<RuleCompare, 'left' | 'right'>>>; 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<Props>) { <TableRowInteractive key={`modified-${rule.key}`}> <ContentCell> <div> - <RuleCell rule={rule} severity={rule.left.severity} /> + <RuleCell rule={rule} severity={rule.left.severity} impacts={rule.left.impacts} /> <Parameters params={rule.left.params} /> </div> </ContentCell> <ContentCell className="sw-pl-4"> <div> - <RuleCell rule={rule} severity={rule.right.severity} /> + <RuleCell rule={rule} severity={rule.right.severity} impacts={rule.right.impacts} /> <Parameters params={rule.right.params} /> </div> </ContentCell> @@ -223,14 +224,24 @@ export default function ComparisonResults(props: Readonly<Props>) { ); } -function RuleCell({ rule, severity }: Readonly<{ rule: RuleCompare; severity?: string }>) { +type RuleCellProps = { + impacts?: SoftwareImpact[]; + rule: RuleCompare; + severity?: string; +}; + +function RuleCell({ rule, severity, impacts }: Readonly<RuleCellProps>) { 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 ( <div> @@ -238,7 +249,7 @@ function RuleCell({ rule, severity }: Readonly<{ rule: RuleCompare; severity?: s <LinkStandalone className="sw-ml-1" to={getRulesUrl({ rule_key: rule.key, open: rule.key })}> {rule.name} </LinkStandalone> - {!isStandardMode && (rule.cleanCodeAttributeCategory || rule.impacts.length > 0) && ( + {!isStandardMode && (rule.cleanCodeAttributeCategory || shouldRenderImpacts) && ( <ul className="sw-mt-3 sw-flex sw-items-center"> {rule.cleanCodeAttributeCategory && ( <li> @@ -247,9 +258,12 @@ function RuleCell({ rule, severity }: Readonly<{ rule: RuleCompare; severity?: s /> </li> )} - {rule.impacts.length > 0 && ( + {((impacts && impacts.length > 0) || rule.impacts) && ( <li> - <SoftwareImpactPillList className="sw-ml-2" softwareImpacts={rule.impacts} /> + <SoftwareImpactPillList + className="sw-ml-2" + softwareImpacts={impacts ?? rule.impacts ?? []} + /> </li> )} </ul> 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<CompareResponse> = {}): 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, |