aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2023-01-04 15:51:06 +0100
committersonartech <sonartech@sonarsource.com>2023-01-06 20:03:15 +0000
commit0e6e7e1e1e812f8481221afbd2f1a8ef79d24fae (patch)
tree81fd33a12f3026b06858841b4320d66aa9faf987 /server
parenta8dad1767c4665ebd601af8d933e338506f2bb25 (diff)
downloadsonarqube-0e6e7e1e1e812f8481221afbd2f1a8ef79d24fae.tar.gz
sonarqube-0e6e7e1e1e812f8481221afbd2f1a8ef79d24fae.zip
SONAR-17815 Highlight non-cayc Quality Gates
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts29
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CaycBadgeTooltip.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CaycStatusBadge.tsx50
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/utils.ts6
-rw-r--r--server/sonar-web/src/main/js/types/quality-gates.ts11
-rw-r--r--server/sonar-web/src/main/js/types/types.ts1
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;
}