diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2023-01-04 15:51:06 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-01-06 20:03:15 +0000 |
commit | 0e6e7e1e1e812f8481221afbd2f1a8ef79d24fae (patch) | |
tree | 81fd33a12f3026b06858841b4320d66aa9faf987 /server | |
parent | a8dad1767c4665ebd601af8d933e338506f2bb25 (diff) | |
download | sonarqube-0e6e7e1e1e812f8481221afbd2f1a8ef79d24fae.tar.gz sonarqube-0e6e7e1e1e812f8481221afbd2f1a8ef79d24fae.zip |
SONAR-17815 Highlight non-cayc Quality Gates
Diffstat (limited to 'server')
10 files changed, 104 insertions, 50 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts index ffed501eb8f..1e7ef5d75fc 100644 --- a/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts @@ -21,7 +21,8 @@ import { cloneDeep, flatten, omit, remove } from 'lodash'; import { Project } from '../../apps/quality-gates/components/Projects'; import { mockQualityGate } from '../../helpers/mocks/quality-gates'; import { mockUserBase } from '../../helpers/mocks/users'; -import { mockGroup } from '../../helpers/testMocks'; +import { mockCondition, mockGroup } from '../../helpers/testMocks'; +import { MetricKey } from '../../types/metrics'; import { Condition, QualityGate } from '../../types/types'; import { addGroup, @@ -81,6 +82,7 @@ export class QualityGatesServiceMock { ], isDefault: true, isBuiltIn: false, + isCaycCompliant: true, }), mockQualityGate({ id: 'AXGYZrDqC-YjVCvvbRDY', @@ -121,6 +123,7 @@ export class QualityGatesServiceMock { ], isDefault: false, isBuiltIn: true, + isCaycCompliant: true, }), ]; @@ -214,9 +217,31 @@ export class QualityGatesServiceMock { mockQualityGate({ id: newId, name, - conditions: [], + conditions: [ + mockCondition({ + id: `${MetricKey.new_reliability_rating}1`, + metric: MetricKey.new_reliability_rating, + error: '1', + }), + mockCondition({ + id: `${MetricKey.new_maintainability_rating}1`, + metric: MetricKey.new_maintainability_rating, + error: '1', + }), + mockCondition({ + id: `${MetricKey.new_security_rating}1`, + metric: MetricKey.new_security_rating, + error: '1', + }), + mockCondition({ + id: `${MetricKey.new_security_hotspots_reviewed}1`, + metric: MetricKey.new_security_hotspots_reviewed, + error: '100', + }), + ], isDefault: false, isBuiltIn: false, + isCaycCompliant: true, }) ); return this.reply({ diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx index 7c983379e3c..156ce62c0c8 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx @@ -20,16 +20,18 @@ import * as React from 'react'; import DocLink from '../../../components/common/DocLink'; import { translate } from '../../../helpers/l10n'; +import { BadgeTarget, QGBadgeType } from '../../../types/quality-gates'; interface Props { - badgeType: 'missing' | 'weak' | 'ok'; + badgeType: QGBadgeType; + target: BadgeTarget; } -export default function CaycBadgeTooltip({ badgeType }: Props) { +export default function CaycBadgeTooltip({ badgeType, target }: Props) { return ( <div> <p className="spacer-bottom padded-bottom bordered-bottom-cayc"> - {translate('quality_gates.cayc.tooltip', badgeType)} + {translate('quality_gates.cayc.tooltip', badgeType, target)} </p> <DocLink to="/user-guide/clean-as-you-code/"> {translate('quality_gates.cayc.badge.tooltip.learn_more')} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CaycStatusBadge.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CaycStatusBadge.tsx index 31931453b38..c1c9ea4e857 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/CaycStatusBadge.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CaycStatusBadge.tsx @@ -24,47 +24,35 @@ import AlertErrorIcon from '../../../components/icons/AlertErrorIcon'; import AlertWarnIcon from '../../../components/icons/AlertWarnIcon'; import CheckIcon from '../../../components/icons/CheckIcon'; import { translate } from '../../../helpers/l10n'; -import { Condition } from '../../../types/types'; -import { isCaycWeakCondition } from '../utils'; +import { BadgeTarget, QGBadgeType } from '../../../types/quality-gates'; import CaycBadgeTooltip from './CaycBadgeTooltip'; interface Props { className?: string; - isMissingCondition?: boolean; - condition: Condition; - isCaycModal?: boolean; + target?: BadgeTarget; + type: QGBadgeType; } +const iconForType = { + [QGBadgeType.Ok]: CheckIcon, + [QGBadgeType.Missing]: AlertErrorIcon, + [QGBadgeType.Weak]: AlertWarnIcon, +}; + +const getIcon = (type: QGBadgeType) => iconForType[type]; + export default function CaycStatusBadge({ className, - isMissingCondition, - condition, - isCaycModal, + target = BadgeTarget.Condition, + type, }: Props) { - if (isMissingCondition && !isCaycModal) { - return ( - <Tooltip overlay={<CaycBadgeTooltip badgeType="missing" />}> - <div className={classNames('badge qg-cayc-missing-badge display-flex-center', className)}> - <AlertErrorIcon className="spacer-right" /> - <span>{translate('quality_gates.cayc_condition.missing')}</span> - </div> - </Tooltip> - ); - } else if (isCaycWeakCondition(condition) && !isCaycModal) { - return ( - <Tooltip overlay={<CaycBadgeTooltip badgeType="weak" />}> - <div className={classNames('badge qg-cayc-weak-badge display-flex-center', className)}> - <AlertWarnIcon className="spacer-right" /> - <span>{translate('quality_gates.cayc_condition.weak')}</span> - </div> - </Tooltip> - ); - } + const Icon = getIcon(type); + return ( - <Tooltip overlay={<CaycBadgeTooltip badgeType="ok" />}> - <div className={classNames('badge qg-cayc-ok-badge display-flex-center', className)}> - <CheckIcon className="spacer-right" /> - <span>{translate('quality_gates.cayc_condition.ok')}</span> + <Tooltip overlay={<CaycBadgeTooltip badgeType={type} target={target} />}> + <div className={classNames(`badge qg-cayc-${type}-badge display-flex-center`, className)}> + <Icon className="spacer-right" /> + <span>{translate('quality_gates.cayc_condition', type)}</span> </div> </Tooltip> ); diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx index 3458febd88c..ff8106788fe 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx @@ -25,8 +25,9 @@ import { DeleteButton, EditButton } from '../../../components/controls/buttons'; import ConfirmModal from '../../../components/controls/ConfirmModal'; import { Alert } from '../../../components/ui/Alert'; import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n'; +import { QGBadgeType } from '../../../types/quality-gates'; import { Condition as ConditionType, Dict, Metric, QualityGate } from '../../../types/types'; -import { getLocalizedMetricNameNoDiffMetric, isCaycCondition } from '../utils'; +import { getLocalizedMetricNameNoDiffMetric, isCaycCondition, isCaycWeakCondition } from '../utils'; import CaycStatusBadge from './CaycStatusBadge'; import ConditionModal from './ConditionModal'; import ConditionValue from './ConditionValue'; @@ -86,6 +87,20 @@ export class ConditionComponent extends React.PureComponent<Props, State> { ); }; + getBadgeType = () => { + const { condition, isCaycModal, isMissingCondition } = this.props; + + if (!isCaycModal) { + if (isMissingCondition) { + return QGBadgeType.Missing; + } else if (isCaycWeakCondition(condition)) { + return QGBadgeType.Weak; + } + } + + return QGBadgeType.Ok; + }; + renderOperator() { // TODO can operator be missing? const { op = 'GT' } = this.props.condition; @@ -138,15 +153,9 @@ export class ConditionComponent extends React.PureComponent<Props, State> { > <ConditionValue metric={metric} isCaycModal={isCaycModal} condition={condition} /> </td> + <td className="text-middle nowrap display-flex-justify-end"> - {isCaycCondition(condition) && ( - <CaycStatusBadge - isMissingCondition={isMissingCondition} - condition={condition} - isCaycModal={isCaycModal} - className="spacer-right" - /> - )} + {isCaycCondition(condition) && <CaycStatusBadge type={this.getBadgeType()} />} {canEdit && showEdit && !isMissingCondition && ( <> <EditButton diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx index f561dbe4c46..d5749a08e40 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx @@ -23,8 +23,10 @@ import { Button } from '../../../components/controls/buttons'; import ModalButton from '../../../components/controls/ModalButton'; import Tooltip from '../../../components/controls/Tooltip'; import { translate } from '../../../helpers/l10n'; +import { BadgeTarget, QGBadgeType } from '../../../types/quality-gates'; import { QualityGate } from '../../../types/types'; import BuiltInQualityGateBadge from './BuiltInQualityGateBadge'; +import CaycStatusBadge from './CaycStatusBadge'; import CopyQualityGateForm from './CopyQualityGateForm'; import DeleteQualityGateForm from './DeleteQualityGateForm'; import RenameQualityGateForm from './RenameQualityGateForm'; @@ -69,6 +71,13 @@ export default class DetailsHeader extends React.PureComponent<Props> { <div className="pull-left display-flex-center"> <h2>{qualityGate.name}</h2> {qualityGate.isBuiltIn && <BuiltInQualityGateBadge className="spacer-left" />} + {!qualityGate.isCaycCompliant && ( + <CaycStatusBadge + className="spacer-left" + target={BadgeTarget.QualityGate} + type={QGBadgeType.Weak} + /> + )} </div> <div className="pull-right"> diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx index 82a8b06001b..15062dded7a 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx @@ -21,8 +21,10 @@ import * as React from 'react'; import { NavLink } from 'react-router-dom'; import { translate } from '../../../helpers/l10n'; import { getQualityGateUrl } from '../../../helpers/urls'; +import { BadgeTarget, QGBadgeType } from '../../../types/quality-gates'; import { QualityGate } from '../../../types/types'; import BuiltInQualityGateBadge from './BuiltInQualityGateBadge'; +import CaycStatusBadge from './CaycStatusBadge'; interface Props { qualityGates: QualityGate[]; @@ -46,6 +48,13 @@ export default function List({ qualityGates }: Props) { <span className="badge little-spacer-left">{translate('default')}</span> )} {qualityGate.isBuiltIn && <BuiltInQualityGateBadge className="little-spacer-left" />} + {!qualityGate.isCaycCompliant && ( + <CaycStatusBadge + className="little-spacer-left" + target={BadgeTarget.QualityGate} + type={QGBadgeType.Weak} + /> + )} </NavLink> ))} </div> diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx index 786f53c5f9d..964dcc5bcd8 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx @@ -86,6 +86,7 @@ it('should be able to create a quality gate then delete it', async () => { await user.click(saveButton); const newQG = await screen.findByRole('menuitem', { name: 'testtwo' }); + expect(newQG).toBeInTheDocument(); // Delete the quality gate @@ -220,7 +221,8 @@ it('should be able to handle duplicate or deprecated condition', async () => { handler.setIsAdmin(true); renderQualityGateApp(); await user.click( - await screen.findByRole('menuitem', { name: handler.getCorruptedQualityGateName() }) + // make it a regexp to ignore badges: + await screen.findByRole('menuitem', { name: new RegExp(handler.getCorruptedQualityGateName()) }) ); expect(await screen.findByText('quality_gates.duplicated_conditions')).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/quality-gates/utils.ts b/server/sonar-web/src/main/js/apps/quality-gates/utils.ts index 1548766e61e..c532e9e5ed3 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/utils.ts +++ b/server/sonar-web/src/main/js/apps/quality-gates/utils.ts @@ -53,7 +53,7 @@ export function getCaycConditions(conditions: Condition[]) { } export function isCaycCondition(condition: Condition) { - return Object.keys(CAYC_CONDITIONS_WITH_EXPECTED_VALUE).includes(condition.metric); + return condition.metric in CAYC_CONDITIONS_WITH_EXPECTED_VALUE; } export function isCaycWeakCondition(condition: Condition) { @@ -83,9 +83,7 @@ export function getWeakAndMissingConditions(conditions: Condition[]) { } export function getOthersConditions(conditions: Condition[]) { - return conditions.filter( - (condition) => !Object.keys(CAYC_CONDITIONS_WITH_EXPECTED_VALUE).includes(condition.metric) - ); + return conditions.filter((condition) => !isCaycCondition(condition)); } export function getCorrectCaycCondition(condition: Condition) { diff --git a/server/sonar-web/src/main/js/types/quality-gates.ts b/server/sonar-web/src/main/js/types/quality-gates.ts index c7dd3c45c36..ed27b6713d5 100644 --- a/server/sonar-web/src/main/js/types/quality-gates.ts +++ b/server/sonar-web/src/main/js/types/quality-gates.ts @@ -109,3 +109,14 @@ export interface Group { export function isUser(item: UserBase | Group): item is UserBase { return item && (item as UserBase).login !== undefined; } + +export enum QGBadgeType { + 'Missing' = 'missing', + 'Weak' = 'weak', + 'Ok' = 'ok', +} + +export enum BadgeTarget { + QualityGate = 'quality_gate', + Condition = 'condition', +} diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts index 07e7367f97e..31ac5a7c905 100644 --- a/server/sonar-web/src/main/js/types/types.ts +++ b/server/sonar-web/src/main/js/types/types.ts @@ -517,6 +517,7 @@ export interface QualityGate { conditions?: Condition[]; id: string; isBuiltIn?: boolean; + isCaycCompliant?: boolean; isDefault?: boolean; name: string; } |