From 653eee50443193b77a65759d405afb890c459597 Mon Sep 17 00:00:00 2001 From: stanislavh Date: Wed, 18 Oct 2023 12:17:41 +0200 Subject: [PATCH] SONAR-20742 Implement quality gate status --- server/sonar-web/__mocks__/react-intl.tsx | 15 +- .../src/components/__tests__/layouts-test.tsx | 2 +- .../design-system/src/helpers/constants.ts | 2 +- .../overview/components/BranchQualityGate.tsx | 87 ++++++++ .../BranchQualityGateConditions.tsx | 197 ++++++++++++++++++ .../components/QualityGateCondition.tsx | 8 +- .../__tests__/BranchQualityGate-it.tsx | 136 ++++++++++++ .../pullRequests/PullRequestOverview.tsx | 139 ++++-------- .../__tests__/PullRequestOverview-it.tsx | 22 +- .../src/main/js/apps/overview/utils.ts | 31 ++- .../src/main/js/helpers/testMocks.ts | 10 +- .../resources/org/sonar/l10n/core.properties | 6 + 12 files changed, 539 insertions(+), 116 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/overview/components/BranchQualityGate.tsx create mode 100644 server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx create mode 100644 server/sonar-web/src/main/js/apps/overview/components/__tests__/BranchQualityGate-it.tsx diff --git a/server/sonar-web/__mocks__/react-intl.tsx b/server/sonar-web/__mocks__/react-intl.tsx index aa20e55fbda..03edab2f5fd 100644 --- a/server/sonar-web/__mocks__/react-intl.tsx +++ b/server/sonar-web/__mocks__/react-intl.tsx @@ -17,12 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { isObject, some } from 'lodash'; import * as React from 'react'; module.exports = { ...jest.requireActual('react-intl'), useIntl: () => ({ - formatMessage: ({ id }, values = {}) => [id, ...Object.values(values)].join('.'), + formatMessage: ({ id }, values = {}) => { + if (some(values, isObject)) { + return ( + <> + {id} + {Object.entries(values).map(([key, value]) => ( + {value} + ))} + + ); + } + return [id, ...Object.values(values)].join('.'); + }, }), FormattedMessage: ({ id, values }: { id: string; values?: { [x: string]: React.ReactNode } }) => { return ( diff --git a/server/sonar-web/design-system/src/components/__tests__/layouts-test.tsx b/server/sonar-web/design-system/src/components/__tests__/layouts-test.tsx index 02e32e26edf..41ed6a7eecc 100644 --- a/server/sonar-web/design-system/src/components/__tests__/layouts-test.tsx +++ b/server/sonar-web/design-system/src/components/__tests__/layouts-test.tsx @@ -26,7 +26,7 @@ describe('CenteredLayout', () => { expect(screen.getByText('content')).toHaveStyle({ 'min-width': '1280px', - 'max-width': '1400px', + 'max-width': '1280px', }); }); }); diff --git a/server/sonar-web/design-system/src/helpers/constants.ts b/server/sonar-web/design-system/src/helpers/constants.ts index 368b81e9986..ce1c4080f0e 100644 --- a/server/sonar-web/design-system/src/helpers/constants.ts +++ b/server/sonar-web/design-system/src/helpers/constants.ts @@ -50,7 +50,7 @@ export const INPUT_SIZES = { }; export const LAYOUT_VIEWPORT_MIN_WIDTH = 1280; -export const LAYOUT_VIEWPORT_MAX_WIDTH = 1400; +export const LAYOUT_VIEWPORT_MAX_WIDTH = 1280; export const LAYOUT_VIEWPORT_MAX_WIDTH_LARGE = 1680; export const LAYOUT_MAIN_CONTENT_GUTTER = 60; export const LAYOUT_SIDEBAR_WIDTH = 240; diff --git a/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGate.tsx b/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGate.tsx new file mode 100644 index 00000000000..1b39965ed65 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGate.tsx @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { HelperHintIcon, LightPrimary, QualityGateIndicator, TextMuted } from 'design-system'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import HelpTooltip from '../../../components/controls/HelpTooltip'; +import { BranchLike } from '../../../types/branch-like'; +import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates'; +import { Component, Status } from '../../../types/types'; +import BranchQualityGateConditions from './BranchQualityGateConditions'; + +interface Props { + status: Status; + branchLike?: BranchLike; + component: Pick; + failedConditions: QualityGateStatusConditionEnhanced[]; +} + +export default function BranchQualityGate(props: Readonly) { + const { status, branchLike, component, failedConditions } = props; + + return ( + <> + + + + ); +} + +function BranchQGStatus({ status }: Readonly>) { + const intl = useIntl(); + + return ( +
+ +
+
+ + + + +
+
+ + {intl.formatMessage({ id: `metric.level.${status}` })} + +
+
+
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx b/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx new file mode 100644 index 00000000000..0f5a23cdf86 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/BranchQualityGateConditions.tsx @@ -0,0 +1,197 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { ChevronRightIcon, DangerButtonSecondary } from 'design-system'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { getBranchLikeQuery } from '../../../helpers/branch-like'; +import { getLocalizedMetricName } from '../../../helpers/l10n'; +import { formatMeasure, getShortType, isDiffMetric } from '../../../helpers/measures'; +import { + getComponentDrilldownUrl, + getComponentIssuesUrl, + getComponentSecurityHotspotsUrl, +} from '../../../helpers/urls'; +import { BranchLike } from '../../../types/branch-like'; +import { IssueType } from '../../../types/issues'; +import { MetricKey, MetricType } from '../../../types/metrics'; +import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates'; +import { Component } from '../../../types/types'; +import { + METRICS_REPORTED_IN_OVERVIEW_CARDS, + RATING_METRICS_MAPPING, + RATING_TO_SEVERITIES_MAPPING, +} from '../utils'; + +interface Props { + branchLike?: BranchLike; + component: Pick; + failedConditions: QualityGateStatusConditionEnhanced[]; +} + +export default function BranchQualityGateConditions(props: Readonly) { + const { branchLike, component, failedConditions } = props; + + const filteredFailedConditions = failedConditions.filter( + (condition) => !METRICS_REPORTED_IN_OVERVIEW_CARDS.includes(condition.metric as MetricKey), + ); + + return ( +
    + {filteredFailedConditions.map((condition) => ( +
  • + +
  • + ))} +
+ ); +} + +function FailedQGCondition( + props: Readonly< + Pick & { condition: QualityGateStatusConditionEnhanced } + >, +) { + const { branchLike, component, condition } = props; + const url = getQGConditionUrl(component.key, condition, branchLike); + + return ( + + + + + ); +} + +interface FailedMetricProps { + condition: QualityGateStatusConditionEnhanced; +} + +export function FailedMetric(props: Readonly) { + const { + condition: { + measure: { metric }, + }, + } = props; + + if (metric.type === MetricType.Rating) { + return ; + } + + return ; +} + +function FailedRatingMetric({ condition }: Readonly) { + const { + error, + measure: { + metric: { type, domain }, + }, + } = condition; + const intl = useIntl(); + + return ( + <> + {intl.formatMessage( + { id: 'overview.failed_condition.x_required' }, + { + metric: `${intl.formatMessage({ + id: `metric_domain.${domain}`, + })} ${intl.formatMessage({ id: 'metric.type.RATING' }).toLowerCase()}`, + threshold: ( + {formatMeasure(error, type)} + ), + }, + )} + + ); +} + +function FailedGeneralMetric({ condition }: Readonly) { + const { + error, + measure: { metric }, + } = condition; + const intl = useIntl(); + const measureFormattingOptions = { decimals: 2, omitExtraDecimalZeros: true }; + + return ( + <> + {intl.formatMessage( + { id: 'overview.failed_condition.x_required' }, + { + metric: ( + <> + + {formatMeasure( + condition.actual, + getShortType(metric.type), + measureFormattingOptions, + )} + + {getLocalizedMetricName(metric, true)} + + ), + threshold: ( + + {condition.op === 'GT' ? <>≤ : <>≥}{' '} + {formatMeasure(error, getShortType(metric.type), measureFormattingOptions)} + + ), + }, + )} + + ); +} + +function getQGConditionUrl( + componentKey: string, + condition: QualityGateStatusConditionEnhanced, + branchLike?: BranchLike, +) { + const { metric } = condition; + const sinceLeakPeriod = isDiffMetric(metric); + const issueType = RATING_METRICS_MAPPING[metric]; + + if (issueType) { + if (issueType === IssueType.SecurityHotspot) { + return getComponentSecurityHotspotsUrl(componentKey, { + ...getBranchLikeQuery(branchLike), + ...(sinceLeakPeriod ? { sinceLeakPeriod: 'true' } : {}), + }); + } + return getComponentIssuesUrl(componentKey, { + resolved: 'false', + types: issueType, + ...getBranchLikeQuery(branchLike), + ...(sinceLeakPeriod ? { sinceLeakPeriod: 'true' } : {}), + ...(issueType !== IssueType.CodeSmell + ? { severities: RATING_TO_SEVERITIES_MAPPING[Number(condition.error) - 1] } + : {}), + }); + } + + return getComponentDrilldownUrl({ + componentKey, + metric, + branchLike, + listView: true, + }); +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/QualityGateCondition.tsx b/server/sonar-web/src/main/js/apps/overview/components/QualityGateCondition.tsx index 1bb11ca1114..2104460da8a 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/QualityGateCondition.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/QualityGateCondition.tsx @@ -36,6 +36,7 @@ import { IssueType } from '../../../types/issues'; import { MetricKey, MetricType } from '../../../types/metrics'; import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates'; import { Component, Dict } from '../../../types/types'; +import { RATING_TO_SEVERITIES_MAPPING } from '../utils'; interface Props { branchLike?: BranchLike; @@ -71,13 +72,6 @@ export default class QualityGateCondition extends React.PureComponent { } getUrlForBugsOrVulnerabilities(type: string, inNewCodePeriod: boolean) { - const RATING_TO_SEVERITIES_MAPPING = [ - 'BLOCKER,CRITICAL,MAJOR,MINOR', - 'BLOCKER,CRITICAL,MAJOR', - 'BLOCKER,CRITICAL', - 'BLOCKER', - ]; - const { condition } = this.props; const threshold = condition.level === 'ERROR' ? condition.error : condition.warning; diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/BranchQualityGate-it.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/BranchQualityGate-it.tsx new file mode 100644 index 00000000000..714a6d7d76e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/BranchQualityGate-it.tsx @@ -0,0 +1,136 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; +import { mockComponent } from '../../../../helpers/mocks/component'; +import { mockQualityGateStatusConditionEnhanced } from '../../../../helpers/mocks/quality-gates'; +import { mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; +import { renderComponent } from '../../../../helpers/testReactTestingUtils'; +import { byLabelText, byRole } from '../../../../helpers/testSelector'; +import { MetricKey, MetricType } from '../../../../types/metrics'; +import { FCProps } from '../../../../types/misc'; +import { Status } from '../../utils'; +import BranchQualityGate from '../BranchQualityGate'; + +it('renders failed QG', () => { + renderBranchQualityGate(); + + // Maintainability rating condition + expect( + byRole('link', { + name: 'overview.failed_condition.x_requiredmetric_domain.Maintainability metric.type.rating A', + }).get(), + ).toBeInTheDocument(); + + // Security Hotspots rating condition + expect( + byRole('link', { + name: 'overview.failed_condition.x_requiredmetric_domain.Security Review metric.type.rating A', + }).get(), + ).toBeInTheDocument(); + + // New code smells + expect( + byRole('link', { + name: 'overview.failed_condition.x_required 5 Code Smells ≤ 1', + }).get(), + ).toBeInTheDocument(); + + // Conditions to cover + expect( + byRole('link', { + name: 'overview.failed_condition.x_required 5 Conditions to cover ≥ 10', + }).get(), + ).toBeInTheDocument(); + + expect(byLabelText('overview.quality_gate_x.overview.gate.ERROR').get()).toBeInTheDocument(); +}); + +it('renders passed QG', () => { + renderBranchQualityGate({ failedConditions: [], status: Status.OK }); + + expect(byLabelText('overview.quality_gate_x.overview.gate.OK').get()).toBeInTheDocument(); + expect(byRole('link').query()).not.toBeInTheDocument(); +}); + +function renderBranchQualityGate(props: Partial> = {}) { + return renderComponent( + , + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx index ae2114dac0f..d070c52d006 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx @@ -23,34 +23,25 @@ import { CenteredLayout, CoverageIndicator, DuplicationsIndicator, - HelperHintIcon, - Link, Spinner, - TextMuted, } from 'design-system'; import { uniq } from 'lodash'; import * as React from 'react'; import { useEffect, useState } from 'react'; -import { FormattedMessage } from 'react-intl'; import { getMeasuresWithMetrics } from '../../../api/measures'; -import HelpTooltip from '../../../components/controls/HelpTooltip'; import { duplicationRatingConverter } from '../../../components/measure/utils'; import { getBranchLikeQuery } from '../../../helpers/branch-like'; -import { translate } from '../../../helpers/l10n'; import { enhanceConditionWithMeasure, enhanceMeasuresWithMetrics } from '../../../helpers/measures'; import { isDefined } from '../../../helpers/types'; -import { getQualityGateUrl, getQualityGatesUrl } from '../../../helpers/urls'; import { useBranchStatusQuery } from '../../../queries/branch'; import { PullRequest } from '../../../types/branch-like'; import { IssueType } from '../../../types/issues'; import { Component, MeasureEnhanced } from '../../../types/types'; import MeasuresPanelIssueMeasure from '../branches/MeasuresPanelIssueMeasure'; import MeasuresPanelPercentMeasure from '../branches/MeasuresPanelPercentMeasure'; +import BranchQualityGate from '../components/BranchQualityGate'; import IgnoredConditionWarning from '../components/IgnoredConditionWarning'; import MetaTopBar from '../components/MetaTopBar'; -import QualityGateConditions from '../components/QualityGateConditions'; -import QualityGateStatusHeader from '../components/QualityGateStatusHeader'; -import QualityGateStatusPassedView from '../components/QualityGateStatusPassedView'; import SonarLintPromotion from '../components/SonarLintPromotion'; import '../styles.css'; import { MeasurementType, PR_METRICS, Status } from '../utils'; @@ -107,11 +98,6 @@ export default function PullRequestOverview(props: Props) { return null; } - const path = - component.qualityGate === undefined - ? getQualityGatesUrl() - : getQualityGateUrl(component.qualityGate.name); - const failedConditions = conditions .filter((condition) => condition.level === 'ERROR') .map((c) => enhanceConditionWithMeasure(c, measures)) @@ -119,93 +105,58 @@ export default function PullRequestOverview(props: Props) { return ( -
- - - - {ignoredConditions && } - -
- - {status && ( - - )} - -
- - {translate('overview.quality_gate')}, - }} - /> - } - > - - -
- - {status === Status.OK && failedConditions.length === 0 && ( - - )} - - {status !== Status.OK && } - - {failedConditions.length > 0 && ( -
- -
- )} -
- -
- -
-
- {[ - IssueType.Bug, - IssueType.CodeSmell, - IssueType.Vulnerability, - IssueType.SecurityHotspot, - ].map((type: IssueType) => ( - - - - ))} - - {[MeasurementType.Coverage, MeasurementType.Duplication].map( - (type: MeasurementType) => ( +
+
+ + + + {ignoredConditions && } + + {status && ( + + )} + +
+
+ {[ + IssueType.Bug, + IssueType.CodeSmell, + IssueType.Vulnerability, + IssueType.SecurityHotspot, + ].map((type: IssueType) => ( - - ), - )} + ))} + + {[MeasurementType.Coverage, MeasurementType.Duplication].map( + (type: MeasurementType) => ( + + + + ), + )} +
+
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx index 26af400499a..7fc29a23c58 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx @@ -26,6 +26,7 @@ import { mockComponent } from '../../../../helpers/mocks/component'; import { mockQualityGateProjectCondition } from '../../../../helpers/mocks/quality-gates'; import { mockLoggedInUser, mockMeasure, mockMetric } from '../../../../helpers/testMocks'; import { renderComponent } from '../../../../helpers/testReactTestingUtils'; +import { byLabelText, byRole } from '../../../../helpers/testSelector'; import { ComponentPropsType } from '../../../../helpers/testUtils'; import { ComponentQualifier } from '../../../../types/component'; import { MetricKey, MetricType } from '../../../../types/metrics'; @@ -121,6 +122,8 @@ it('should render correctly for a passed QG', async () => { renderPullRequestOverview(); await waitFor(async () => expect(await screen.findByText('metric.level.OK')).toBeInTheDocument()); + expect(screen.getByLabelText('overview.quality_gate_x.overview.gate.OK')).toBeInTheDocument(); + expect(screen.getByText('metric.new_lines.name')).toBeInTheDocument(); expect(screen.getByText(/overview.last_analysis_x/)).toBeInTheDocument(); }); @@ -165,18 +168,21 @@ it('should render correctly for a failed QG', async () => { renderPullRequestOverview(); await waitFor(async () => - expect(await screen.findByText('metric.level.ERROR')).toBeInTheDocument(), + expect( + await byLabelText('overview.quality_gate_x.overview.gate.ERROR').find(), + ).toBeInTheDocument(), ); - expect(await screen.findByText('1.0% metric.new_coverage.name')).toBeInTheDocument(); - expect(await screen.findByText('quality_gates.operator.GT 2.0%')).toBeInTheDocument(); - expect( - await screen.findByText('1.0% metric.duplicated_lines.name quality_gates.conditions.new_code'), + byRole('link', { + name: 'overview.failed_condition.x_required 10.0% duplicated_lines ≤ 1.0%', + }).get(), + ).toBeInTheDocument(); + expect( + byRole('link', { + name: 'overview.failed_condition.x_required 10 new_bugs ≤ 3', + }).get(), ).toBeInTheDocument(); - expect(await screen.findByText('quality_gates.operator.GT 1.0%')).toBeInTheDocument(); - - expect(screen.getByText('quality_gates.operator.GT 3')).toBeInTheDocument(); }); function renderPullRequestOverview( diff --git a/server/sonar-web/src/main/js/apps/overview/utils.ts b/server/sonar-web/src/main/js/apps/overview/utils.ts index abca784fab8..aeb0251f16b 100644 --- a/server/sonar-web/src/main/js/apps/overview/utils.ts +++ b/server/sonar-web/src/main/js/apps/overview/utils.ts @@ -26,7 +26,7 @@ import { parseAsString } from '../../helpers/query'; import { IssueType } from '../../types/issues'; import { MetricKey } from '../../types/metrics'; import { AnalysisMeasuresVariations, MeasureHistory } from '../../types/project-activity'; -import { RawQuery } from '../../types/types'; +import { Dict, RawQuery } from '../../types/types'; export const METRICS: string[] = [ // quality gate @@ -152,6 +152,35 @@ const MEASUREMENTS_MAP = { }, }; +export const RATING_TO_SEVERITIES_MAPPING = [ + 'BLOCKER,CRITICAL,MAJOR,MINOR', + 'BLOCKER,CRITICAL,MAJOR', + 'BLOCKER,CRITICAL', + 'BLOCKER', +]; + +export const RATING_METRICS_MAPPING: Dict = { + [MetricKey.reliability_rating]: IssueType.Bug, + [MetricKey.new_reliability_rating]: IssueType.Bug, + [MetricKey.security_rating]: IssueType.Vulnerability, + [MetricKey.new_security_rating]: IssueType.Vulnerability, + [MetricKey.sqale_rating]: IssueType.CodeSmell, + [MetricKey.new_maintainability_rating]: IssueType.CodeSmell, + [MetricKey.security_review_rating]: IssueType.SecurityHotspot, + [MetricKey.new_security_review_rating]: IssueType.SecurityHotspot, +}; + +export const METRICS_REPORTED_IN_OVERVIEW_CARDS = [ + MetricKey.new_violations, + MetricKey.violations, + MetricKey.new_coverage, + MetricKey.coverage, + MetricKey.new_security_hotspots_reviewed, + MetricKey.security_hotspots_reviewed, + MetricKey.new_duplicated_lines_density, + MetricKey.duplicated_lines_density, +]; + export function getIssueRatingName(type: IssueType) { return translate('metric_domain', ISSUETYPE_METRIC_KEYS_MAP[type].ratingName); } diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index b353b7baea1..b0fece2070a 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -34,6 +34,7 @@ import { RuleRepository } from '../types/coding-rules'; import { EditionKey } from '../types/editions'; import { IssueScope, IssueSeverity, IssueStatus, IssueType, RawIssue } from '../types/issues'; import { Language } from '../types/languages'; +import { MetricKey, MetricType } from '../types/metrics'; import { Notification } from '../types/notifications'; import { DumpStatus, DumpTask } from '../types/project-dump'; import { TaskStatuses } from '../types/tasks'; @@ -395,11 +396,14 @@ export function mockLocation(overrides: Partial = {}): Location { }; } -export function mockMetric(overrides: Partial> = {}): Metric { - const key = overrides.key || 'coverage'; +export function mockMetric( + overrides: Partial> = {}, +): Metric { + const key = overrides.key || MetricKey.coverage; const name = overrides.name || key; - const type = overrides.type || 'PERCENT'; + const type = overrides.type || MetricType.Percent; return { + ...overrides, id: key, key, name, diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index fc35a833d05..869b49dc597 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2897,6 +2897,7 @@ metric.ncloc_language_distribution.description=Non Commenting Lines of Code Dist metric.ncloc_language_distribution.name=Lines of Code Per Language metric.new_blocker_violations.description=New Blocker issues metric.new_blocker_violations.name=New Blocker Issues +metric.new_blocker_violations.short_name=Blocker Issues metric.new_branch_coverage.description=Condition coverage of new/changed code metric.new_branch_coverage.name=Condition Coverage on New Code metric.new_branch_coverage.extra_short_name=Condition Coverage @@ -2915,6 +2916,7 @@ metric.new_coverage.name=Coverage on New Code metric.new_coverage.short_name=Coverage metric.new_critical_violations.description=New Critical issues metric.new_critical_violations.name=New Critical Issues +metric.new_critical_violations.short_name=Critical Issues metric.new_development_cost.description=Development cost on new code metric.new_development_cost.name=Development Cost on New Code metric.new_duplicated_blocks.name=Duplicated Blocks on New Code @@ -2930,6 +2932,7 @@ metric.new_duplicated_lines_density.short_name=Duplications metric.new_duplicated_lines_density.extra_short_name=Density metric.new_info_violations.description=New Info issues metric.new_info_violations.name=New Info Issues +metric.new_info_violations.short_name=Info Issues metric.new_it_branch_coverage.description=Integration tests condition coverage of new/changed code metric.new_it_branch_coverage.name=Condition Coverage by IT on New Code metric.new_it_conditions_to_cover.description=New conditions to cover by integration tests @@ -2955,8 +2958,10 @@ metric.new_maintainability_rating.name=Maintainability Rating on New Code metric.new_maintainability_rating.extra_short_name=Rating metric.new_major_violations.description=New Major issues metric.new_major_violations.name=New Major Issues +metric.new_major_violations.short_name=Major Issues metric.new_minor_violations.description=New Minor issues metric.new_minor_violations.name=New Minor Issues +metric.new_minor_violations.short_name=Minor Issues metric.new_lines.name=New Lines metric.new_lines.description=New lines metric.new_lines.short_name=Lines @@ -3740,6 +3745,7 @@ system.version_is_availble={version} is available #------------------------------------------------------------------------------ overview.1_condition_failed=1 failed condition overview.X_conditions_failed={0} failed conditions +overview.failed_condition.x_required={metric} required {threshold} overview.fix_failed_conditions_with_sonarlint=Fix issues before they fail your Quality Gate with {link} in your IDE. Power up with connected mode! overview.quality_gate.status=Quality Gate Status overview.quality_gate=Quality Gate -- 2.39.5