diff options
author | Viktor Vorona <viktor.vorona@sonarsource.com> | 2024-10-16 13:18:21 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-16 20:03:02 +0000 |
commit | e811e3183bedf03b78096012c3654da1b2ef414e (patch) | |
tree | 46b89f232f41e006903e449a607dcdae53b7a91e | |
parent | 3bb32d2694602a7a8c63f307509b85ebb6a5bdcf (diff) | |
download | sonarqube-e811e3183bedf03b78096012c3654da1b2ef414e.tar.gz sonarqube-e811e3183bedf03b78096012c3654da1b2ef414e.zip |
SONAR-23250 Ignore the order of impacts on showing the customization info
4 files changed, 126 insertions, 95 deletions
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts index a5010470c86..e9b4b398af5 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts @@ -18,14 +18,15 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { fireEvent, screen } from '@testing-library/react'; +import { byRole } from '~sonar-aligned/helpers/testSelector'; import CodingRulesServiceMock, { RULE_TAGS_MOCK } from '../../../api/mocks/CodingRulesServiceMock'; import SettingsServiceMock from '../../../api/mocks/SettingsServiceMock'; -import { RULE_1 } from '../../../api/mocks/data/ids'; -import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; -import { byRole } from '../../../sonar-aligned/helpers/testSelector'; +import { QP_2, RULE_1, RULE_10 } from '../../../api/mocks/data/ids'; +import { mockCurrentUser, mockLoggedInUser, mockRuleActivation } from '../../../helpers/testMocks'; import { CleanCodeAttribute, CleanCodeAttributeCategory, + SoftwareImpactSeverity, SoftwareQuality, } from '../../../types/clean-code-taxonomy'; import { Feature } from '../../../types/features'; @@ -35,8 +36,8 @@ import { getPageObjects, renderCodingRulesApp } from '../utils-tests'; const rulesHandler = new CodingRulesServiceMock(); const settingsHandler = new SettingsServiceMock(); afterEach(() => { - rulesHandler.reset(); settingsHandler.reset(); + rulesHandler.reset(); }); describe('rendering', () => { @@ -169,6 +170,33 @@ describe('rendering', () => { expect(ui.caycNotificationButton.query()).not.toBeInTheDocument(); }); + + it('should not show customized severity if the order of impacts is different', async () => { + const { ui } = getPageObjects(); + rulesHandler.rulesActivations = { + [RULE_10]: [ + mockRuleActivation({ + qProfile: QP_2, + severity: 'MINOR', + impacts: [ + { + softwareQuality: SoftwareQuality.Reliability, + severity: SoftwareImpactSeverity.High, + }, + { + softwareQuality: SoftwareQuality.Maintainability, + severity: SoftwareImpactSeverity.Low, + }, + ], + }), + ], + }; + renderCodingRulesApp(mockCurrentUser(), 'coding_rules?open=rule10'); + + await ui.detailsloaded(); + + expect(ui.newSeverityCustomizedCell.query()).not.toBeInTheDocument(); + }); }); it('can activate/change/deactivate rule in quality profile', async () => { @@ -240,7 +268,7 @@ it('can activate/change/deactivate rule in quality profile', async () => { it('can activate/change/deactivate rule in quality profile for legacy mode', async () => { const { ui, user } = getPageObjects(); - settingsHandler.set(SettingsKey.MQRMode, 'true'); + settingsHandler.set(SettingsKey.MQRMode, 'false'); rulesHandler.setIsAdmin(); renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule1', [Feature.PrioritizedRules]); await ui.detailsloaded(); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx index 52a91e8babc..29b0f06e319 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx @@ -44,7 +44,8 @@ import { useActivateRuleMutation, useDeactivateRuleMutation, } from '../../../queries/quality-profiles'; -import { useIsLegacyCCTMode } from '../../../queries/settings'; +import { useStandardExperienceMode } from '../../../queries/settings'; +import { SoftwareImpact } from '../../../types/clean-code-taxonomy'; import { Dict, RuleActivation, RuleDetails } from '../../../types/types'; import BuiltInQualityProfileBadge from '../../quality-profiles/components/BuiltInQualityProfileBadge'; import ActivatedRuleActions from './ActivatedRuleActions'; @@ -71,7 +72,7 @@ export default function RuleDetailsProfiles(props: Readonly<Props>) { const { activations = [], referencedProfiles, ruleDetails, canDeactivateInherited } = props; const { mutate: activateRule } = useActivateRuleMutation(props.onActivate); const { mutate: deactivateRule } = useDeactivateRuleMutation(props.onDeactivate); - const { data: isLegacy } = useIsLegacyCCTMode(); + const { data: isStandardMode } = useStandardExperienceMode(); const canActivate = Object.values(referencedProfiles).some((profile) => Boolean(profile.actions?.edit && profile.language === ruleDetails.lang), @@ -144,6 +145,12 @@ export default function RuleDetailsProfiles(props: Readonly<Props>) { ) : null; + const sortImpacts = (a: SoftwareImpact, b: SoftwareImpact) => { + const indexA = softwareQualityOrderMap.get(a.softwareQuality) ?? -1; + const indexB = softwareQualityOrderMap.get(b.softwareQuality) ?? -1; + return indexA - indexB; + }; + return ( <TableRowInteractive key={profile.key}> <ContentCell className="sw-flex sw-flex-col sw-gap-2"> @@ -163,73 +170,65 @@ export default function RuleDetailsProfiles(props: Readonly<Props>) { <Text isSubdued>{translate('coding_rules.prioritized_rule.title')}</Text> </> )} - {!isLegacy && + {!isStandardMode && activation.impacts && - !isEqual(activation.impacts, ruleDetails.impacts) && ( + !isEqual( + [...activation.impacts].sort(sortImpacts), + [...ruleDetails.impacts].sort(sortImpacts), + ) && ( <> <SeparatorCircleIcon /> <Tooltip content={ <> - {[...activation.impacts] - .sort((a, b) => { - const indexA = softwareQualityOrderMap.get(a.softwareQuality) ?? -1; - const indexB = softwareQualityOrderMap.get(b.softwareQuality) ?? -1; - return indexA - indexB; - }) - .map((impact) => { - const ruleImpact = ruleDetails.impacts.find( - (i) => i.softwareQuality === impact.softwareQuality, - ); - if (!ruleImpact || ruleImpact.severity === impact.severity) { - return null; - } - return ( - <Text - as="div" - colorOverride="echoes-color-text-on-color" - key={impact.softwareQuality} - > - <FormattedMessage - id="coding_rules.impact_customized.detail" - values={{ - softwareQuality: ( - <Text - isHighlighted - colorOverride="echoes-color-text-on-color" - > - <FormattedMessage - id={`software_quality.${impact.softwareQuality}`} - /> - </Text> - ), - recommended: ( - <Text - isHighlighted - colorOverride="echoes-color-text-on-color" - className="sw-lowercase" - > - <FormattedMessage - id={`severity_impact.${ruleImpact?.severity}`} - /> - </Text> - ), - customized: ( - <Text - isHighlighted - colorOverride="echoes-color-text-on-color" - className="sw-lowercase" - > - <FormattedMessage - id={`severity_impact.${impact.severity}`} - /> - </Text> - ), - }} - /> - </Text> - ); - })} + {[...activation.impacts].sort(sortImpacts).map((impact) => { + const ruleImpact = ruleDetails.impacts.find( + (i) => i.softwareQuality === impact.softwareQuality, + ); + if (!ruleImpact || ruleImpact.severity === impact.severity) { + return null; + } + return ( + <Text + as="div" + colorOverride="echoes-color-text-on-color" + key={impact.softwareQuality} + > + <FormattedMessage + id="coding_rules.impact_customized.detail" + values={{ + softwareQuality: ( + <Text isHighlighted colorOverride="echoes-color-text-on-color"> + <FormattedMessage + id={`software_quality.${impact.softwareQuality}`} + /> + </Text> + ), + recommended: ( + <Text + isHighlighted + colorOverride="echoes-color-text-on-color" + className="sw-lowercase" + > + <FormattedMessage + id={`severity_impact.${ruleImpact?.severity}`} + /> + </Text> + ), + customized: ( + <Text + isHighlighted + colorOverride="echoes-color-text-on-color" + className="sw-lowercase" + > + <FormattedMessage id={`severity_impact.${impact.severity}`} /> + </Text> + ), + }} + /> + </Text> + ); + })} </> } > @@ -238,28 +237,30 @@ export default function RuleDetailsProfiles(props: Readonly<Props>) { </> )} - {isLegacy && activation.severity && activation.severity !== ruleDetails.severity && ( - <> - <SeparatorCircleIcon /> - <Text isSubdued> - <FormattedMessage - id="coding_rules.severity_customized.message" - values={{ - recommended: ( - <Text isHighlighted className="sw-lowercase"> - <FormattedMessage id={`severity.${ruleDetails.severity}`} /> - </Text> - ), - customized: ( - <Text isHighlighted className="sw-lowercase"> - <FormattedMessage id={`severity.${activation.severity}`} /> - </Text> - ), - }} - /> - </Text> - </> - )} + {isStandardMode && + activation.severity && + activation.severity !== ruleDetails.severity && ( + <> + <SeparatorCircleIcon /> + <Text isSubdued> + <FormattedMessage + id="coding_rules.severity_customized.message" + values={{ + recommended: ( + <Text isHighlighted className="sw-lowercase"> + <FormattedMessage id={`severity.${ruleDetails.severity}`} /> + </Text> + ), + customized: ( + <Text isHighlighted className="sw-lowercase"> + <FormattedMessage id={`severity.${activation.severity}`} /> + </Text> + ), + }} + /> + </Text> + </> + )} {profile.isBuiltIn && <BuiltInQualityProfileBadge />} </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx index b1d4c2b24ff..66b303fb58f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx @@ -21,6 +21,7 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { byRole, byText } from '~sonar-aligned/helpers/testSelector'; import QualityProfilesServiceMock from '../../../../api/mocks/QualityProfilesServiceMock'; +import SettingsServiceMock from '../../../../api/mocks/SettingsServiceMock'; import { mockQualityProfileChangelogEvent } from '../../../../helpers/testMocks'; import { renderAppRoutes } from '../../../../helpers/testReactTestingUtils'; import routes from '../../routes'; @@ -28,6 +29,7 @@ import routes from '../../routes'; jest.mock('../../../../api/quality-profiles'); const serviceMock = new QualityProfilesServiceMock(); +const settingsMock = new SettingsServiceMock(); const ui = { row: byRole('row'), cell: byRole('cell'), @@ -75,6 +77,7 @@ afterEach(() => { jest.useRealTimers(); serviceMock.reset(); + settingsMock.reset(); }); it('should see the changelog', async () => { 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 1f66dbd9dfb..da85a1fd73c 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 @@ -233,15 +233,14 @@ type RuleCellProps = { function RuleCell({ rule, severity, impacts }: Readonly<RuleCellProps>) { const { data: isStandardMode } = useStandardExperienceMode(); const shouldRenderSeverity = - isStandardMode &&Boolean(severity) && + isStandardMode && + Boolean(severity) && rule.left?.severity && rule.right?.severity && - !isEqual(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)); + (rule.left?.impacts && rule.right?.impacts && !isEqual(rule.left.impacts, rule.right.impacts)); return ( <div> |