From: Wouter Admiraal Date: Thu, 19 Dec 2019 09:45:39 +0000 (+0100) Subject: SONAR-12632 SONAR-12648 Update Pull Request dashboard X-Git-Tag: 8.2.0.32929~108 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=84fef2186cb4fe42737f71f557a36506c8a2639f;p=sonarqube.git SONAR-12632 SONAR-12648 Update Pull Request dashboard - Rename component to PullRequestOverview for better consistency - Update all measure and condition related logic to cater for the quality gate condition components - Move sub-components to the shared folder, to make them re-usable. - SONAR-12648 Change Metrics title to Measures --- diff --git a/server/sonar-web/src/main/js/app/styles/init/type.css b/server/sonar-web/src/main/js/app/styles/init/type.css index 320be2e34c4..b04036497ff 100644 --- a/server/sonar-web/src/main/js/app/styles/init/type.css +++ b/server/sonar-web/src/main/js/app/styles/init/type.css @@ -253,10 +253,18 @@ small, text-transform: none; } +.text-light { + font-weight: 300 !important; +} + .text-normal { font-weight: normal !important; } +.text-bold { + font-weight: bold !important; +} + .text-muted { color: var(--secondFontColor); } diff --git a/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx new file mode 100644 index 00000000000..1460880bd9e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/IssueLabel.tsx @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { Link } from 'react-router'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { formatMeasure, localizeMetric } from 'sonar-ui-common/helpers/measures'; +import DocTooltip from '../../../components/docs/DocTooltip'; +import { getLeakValue } from '../../../components/measure/utils'; +import { getBranchLikeQuery } from '../../../helpers/branch-like'; +import { findMeasure } from '../../../helpers/measures'; +import { getComponentIssuesUrl } from '../../../helpers/urls'; +import { BranchLike } from '../../../types/branch-like'; +import { getIssueIconClass, getIssueMetricKey, IssueType } from '../utils'; + +export interface IssueLabelProps { + branchLike?: BranchLike; + component: T.Component; + docTooltip?: Promise<{ default: string }>; + measures: T.MeasureEnhanced[]; + type: IssueType; + useDiffMetric?: boolean; +} + +export function IssueLabel(props: IssueLabelProps) { + const { branchLike, component, docTooltip, measures, type, useDiffMetric = false } = props; + const metric = getIssueMetricKey(type, useDiffMetric); + const measure = findMeasure(measures, metric); + const iconClass = getIssueIconClass(type); + + let value; + if (measure) { + value = useDiffMetric ? getLeakValue(measure) : measure.value; + } + + const params = { + ...getBranchLikeQuery(branchLike), + resolved: 'false', + types: type, + sinceLeakPeriod: useDiffMetric ? 'true' : 'false' + }; + + return ( + <> + {value === undefined ? ( + + ) : ( + + {formatMeasure(value, 'SHORT_INT')} + + )} + {React.createElement(iconClass, { className: 'big-spacer-left little-spacer-right' })} + {localizeMetric(metric)} + {docTooltip && } + + ); +} + +export default React.memo(IssueLabel); diff --git a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx new file mode 100644 index 00000000000..9379a4078e3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 Tooltip from 'sonar-ui-common/components/controls/Tooltip'; +import Rating from 'sonar-ui-common/components/ui/Rating'; +import { getLeakValue, getRatingTooltip } from '../../../components/measure/utils'; +import DrilldownLink from '../../../components/shared/DrilldownLink'; +import { findMeasure } from '../../../helpers/measures'; +import { BranchLike } from '../../../types/branch-like'; +import { getIssueRatingMetricKey, getIssueRatingName, IssueType } from '../utils'; + +export interface IssueRatingProps { + branchLike?: BranchLike; + component: T.Component; + measures: T.MeasureEnhanced[]; + type: IssueType; + useDiffMetric?: boolean; +} + +export function IssueRating(props: IssueRatingProps) { + const { branchLike, component, measures, type, useDiffMetric = false } = props; + const rating = getIssueRatingMetricKey(type, useDiffMetric); + const measure = findMeasure(measures, rating); + + if (!rating || !measure) { + return null; + } + + const value = useDiffMetric ? getLeakValue(measure) : measure.value; + const tooltip = value && getRatingTooltip(rating, Number(value)); + + return ( + <> + {getIssueRatingName(type)} + + + + + + + + + ); +} + +export default React.memo(IssueRating); diff --git a/server/sonar-web/src/main/js/apps/overview/components/MeasurementLabel.tsx b/server/sonar-web/src/main/js/apps/overview/components/MeasurementLabel.tsx new file mode 100644 index 00000000000..3de85a144c9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/MeasurementLabel.tsx @@ -0,0 +1,136 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { FormattedMessage } from 'react-intl'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { + formatMeasure, + getMinDecimalsCountToBeDistinctFromThreshold +} from 'sonar-ui-common/helpers/measures'; +import { getLeakValue } from '../../../components/measure/utils'; +import DrilldownLink from '../../../components/shared/DrilldownLink'; +import { findMeasure } from '../../../helpers/measures'; +import { BranchLike } from '../../../types/branch-like'; +import { + getMeasurementIconClass, + getMeasurementLabelKeys, + getMeasurementLinesMetricKey, + getMeasurementMetricKey, + getThreshold, + MeasurementType +} from '../utils'; + +interface Props { + branchLike?: BranchLike; + centered?: boolean; + component: T.Component; + measures: T.MeasureEnhanced[]; + type: MeasurementType; + useDiffMetric?: boolean; +} + +export default class MeasurementLabel extends React.Component { + getLabelText = () => { + const { branchLike, component, measures, type, useDiffMetric = false } = this.props; + const { expandedLabelKey, labelKey } = getMeasurementLabelKeys(type, useDiffMetric); + const linesMetric = getMeasurementLinesMetricKey(type, useDiffMetric); + const measure = findMeasure(measures, linesMetric); + + if (!measure) { + return translate(labelKey); + } else { + const value = useDiffMetric ? getLeakValue(measure) : measure.value; + + return ( + + {formatMeasure(value, 'SHORT_INT')} + + ) + }} + /> + ); + } + }; + + render() { + const { branchLike, centered, component, measures, type, useDiffMetric = false } = this.props; + const iconClass = getMeasurementIconClass(type); + const metric = getMeasurementMetricKey(type, useDiffMetric); + const measure = findMeasure(measures, metric); + + let value; + if (measure) { + value = useDiffMetric ? getLeakValue(measure) : measure.value; + } + + if (value === undefined) { + return ( +
+ + {this.getLabelText()} +
+ ); + } + + const icon = React.createElement(iconClass, { size: 'big', value: Number(value) }); + const link = ( + + {formatMeasure(value, 'PERCENT', { + decimals: getMinDecimalsCountToBeDistinctFromThreshold( + parseFloat(value), + getThreshold(measures, metric) + ) + })} + + ); + const label = this.getLabelText(); + + return centered ? ( +
+
+ {icon} + {link} +
+
{label}
+
+ ) : ( +
+ {icon} +
+ {link} + {label} +
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueLabel-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueLabel-test.tsx new file mode 100644 index 00000000000..17b9f42fcb7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueLabel-test.tsx @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; +import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; +import { MetricKey } from '../../../../types/metrics'; +import { IssueType } from '../../utils'; +import { IssueLabel, IssueLabelProps } from '../IssueLabel'; + +it('should render correctly for bugs', () => { + const measures = [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.bugs }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_bugs }) }) + ]; + expect(shallowRender({ measures })).toMatchSnapshot(); + expect(shallowRender({ measures, useDiffMetric: true })).toMatchSnapshot(); +}); + +it('should render correctly for code smells', () => { + const type = IssueType.CodeSmell; + const measures = [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.code_smells }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_code_smells }) }) + ]; + expect(shallowRender({ measures, type })).toMatchSnapshot(); + expect(shallowRender({ measures, type, useDiffMetric: true })).toMatchSnapshot(); +}); + +it('should render correctly for vulnerabilities', () => { + const type = IssueType.Vulnerability; + const measures = [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.vulnerabilities }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_vulnerabilities }) }) + ]; + expect(shallowRender({ measures, type })).toMatchSnapshot(); + expect(shallowRender({ measures, type, useDiffMetric: true })).toMatchSnapshot(); +}); + +it('should render correctly for hotspots', () => { + const docTooltip = Promise.resolve({ default: 'tooltip text' }); + const type = IssueType.SecurityHotspot; + const measures = [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.security_hotspots }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_security_hotspots }) }) + ]; + expect( + shallowRender({ + docTooltip, + measures, + type + }) + ).toMatchSnapshot(); + expect( + shallowRender({ + docTooltip, + measures, + type, + useDiffMetric: true + }) + ).toMatchSnapshot(); +}); + +it('should render correctly if no values are present', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx new file mode 100644 index 00000000000..a4647657af6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/IssueRating-test.tsx @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; +import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; +import { MetricKey } from '../../../../types/metrics'; +import { IssueType } from '../../utils'; +import { IssueRating, IssueRatingProps } from '../IssueRating'; + +it('should render correctly for bugs', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ useDiffMetric: true })).toMatchSnapshot(); +}); + +it('should render correctly for code smells', () => { + expect(shallowRender({ type: IssueType.CodeSmell })).toMatchSnapshot(); + expect(shallowRender({ type: IssueType.CodeSmell, useDiffMetric: true })).toMatchSnapshot(); +}); + +it('should render correctly for vulnerabilities', () => { + expect(shallowRender({ type: IssueType.Vulnerability })).toMatchSnapshot(); + expect(shallowRender({ type: IssueType.Vulnerability, useDiffMetric: true })).toMatchSnapshot(); +}); + +it('should render correctly if no values are present', () => { + expect( + shallowRender({ + measures: [mockMeasureEnhanced({ metric: mockMetric({ key: 'NONE' }) })] + }).type() + ).toBeNull(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/MeasurementLabel-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/MeasurementLabel-test.tsx new file mode 100644 index 00000000000..944e27835f6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/MeasurementLabel-test.tsx @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; +import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; +import { MetricKey } from '../../../../types/metrics'; +import { MeasurementType } from '../../utils'; +import MeasurementLabel from '../MeasurementLabel'; + +it('should render correctly for coverage', () => { + expect(shallowRender()).toMatchSnapshot(); + expect( + shallowRender({ + measures: [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.coverage }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.lines_to_cover }) }) + ] + }) + ).toMatchSnapshot(); + expect( + shallowRender({ + measures: [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_coverage }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_lines_to_cover }) }) + ], + useDiffMetric: true + }) + ).toMatchSnapshot(); +}); + +it('should render correctly for duplications', () => { + expect( + shallowRender({ + measures: [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.duplicated_lines_density }) }) + ], + type: MeasurementType.Duplication + }) + ).toMatchSnapshot(); + expect( + shallowRender({ + measures: [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.duplicated_lines_density }) }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.ncloc }) }) + ], + type: MeasurementType.Duplication + }) + ).toMatchSnapshot(); + expect( + shallowRender({ + measures: [ + mockMeasureEnhanced({ + metric: mockMetric({ key: MetricKey.new_duplicated_lines_density }) + }), + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_lines }) }) + ], + type: MeasurementType.Duplication, + useDiffMetric: true + }) + ).toMatchSnapshot(); +}); + +it('should render correctly with no value', () => { + expect(shallowRender({ measures: [] })).toMatchSnapshot(); +}); + +it('should render correctly when centered', () => { + expect(shallowRender({ centered: true })).toMatchSnapshot(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap new file mode 100644 index 00000000000..04a4cf74349 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueLabel-test.tsx.snap @@ -0,0 +1,246 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for bugs 1`] = ` + + + 1 + + + metric.bugs.name + +`; + +exports[`should render correctly for bugs 2`] = ` + + + 1 + + + metric.new_bugs.name + +`; + +exports[`should render correctly for code smells 1`] = ` + + + 1 + + + metric.code_smells.name + +`; + +exports[`should render correctly for code smells 2`] = ` + + + 1 + + + metric.new_code_smells.name + +`; + +exports[`should render correctly for hotspots 1`] = ` + + + 1 + + + metric.security_hotspots.name + + +`; + +exports[`should render correctly for hotspots 2`] = ` + + + 1 + + + metric.new_security_hotspots.name + + +`; + +exports[`should render correctly for vulnerabilities 1`] = ` + + + 1 + + + metric.vulnerabilities.name + +`; + +exports[`should render correctly for vulnerabilities 2`] = ` + + + 1 + + + metric.new_vulnerabilities.name + +`; + +exports[`should render correctly if no values are present 1`] = ` + + + + metric.bugs.name + +`; diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap new file mode 100644 index 00000000000..45be5c5c55a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap @@ -0,0 +1,211 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for bugs 1`] = ` + + + metric_domain.Reliability + + + + + + + + + +`; + +exports[`should render correctly for bugs 2`] = ` + + + metric_domain.Reliability + + + + + + + + + +`; + +exports[`should render correctly for code smells 1`] = ` + + + metric_domain.Maintainability + + + + + + + + + +`; + +exports[`should render correctly for code smells 2`] = ` + + + metric_domain.Maintainability + + + + + + + + + +`; + +exports[`should render correctly for vulnerabilities 1`] = ` + + + metric_domain.Security + + + + + + + + + +`; + +exports[`should render correctly for vulnerabilities 2`] = ` + + + metric_domain.Security + + + + + + + + + +`; diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/MeasurementLabel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/MeasurementLabel-test.tsx.snap new file mode 100644 index 00000000000..732fe6a4674 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/MeasurementLabel-test.tsx.snap @@ -0,0 +1,412 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for coverage 1`] = ` +
+ + + +
+ + + 1.0% + + + + metric.coverage.name + +
+
+`; + +exports[`should render correctly for coverage 2`] = ` +
+ + + +
+ + + 1.0% + + + + + 1 + , + } + } + /> + +
+
+`; + +exports[`should render correctly for coverage 3`] = ` +
+ + + +
+ + + 1.0% + + + + + 1 + , + } + } + /> + +
+
+`; + +exports[`should render correctly for duplications 1`] = ` +
+ + + +
+ + + 1.0% + + + + metric.duplicated_lines_density.short_name + +
+
+`; + +exports[`should render correctly for duplications 2`] = ` +
+ + + +
+ + + 1.0% + + + + + 1 + , + } + } + /> + +
+
+`; + +exports[`should render correctly for duplications 3`] = ` +
+ + + +
+ + + 1.0% + + + + + 1 + , + } + } + /> + +
+
+`; + +exports[`should render correctly when centered 1`] = ` +
+
+ + + + + 1.0% + +
+
+ metric.coverage.name +
+
+`; + +exports[`should render correctly with no value 1`] = ` +
+ + + metric.coverage.name + +
+`; diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/AfterMergeEstimate.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/AfterMergeEstimate.tsx index fdf15e9f47b..ce19d29dc4a 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/AfterMergeEstimate.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/AfterMergeEstimate.tsx @@ -22,16 +22,16 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { formatMeasure } from 'sonar-ui-common/helpers/measures'; import { findMeasure } from '../../../helpers/measures'; -import { MEASUREMENTS_MAP, MeasurementType } from '../utils'; +import { getMeasurementAfterMergeMetricKey, MeasurementType } from '../utils'; -interface Props { +export interface AfterMergeEstimateProps { className?: string; - measures: T.Measure[]; + measures: T.MeasureEnhanced[]; type: MeasurementType; } -export default function AfterMergeEstimate({ className, measures, type }: Props) { - const { afterMergeMetric } = MEASUREMENTS_MAP[type]; +export function AfterMergeEstimate({ className, measures, type }: AfterMergeEstimateProps) { + const afterMergeMetric = getMeasurementAfterMergeMetricKey(type); const measure = findMeasure(measures, afterMergeMetric); @@ -42,9 +42,11 @@ export default function AfterMergeEstimate({ className, measures, type }: Props) return (
{formatMeasure(measure.value, 'PERCENT')} - + {translate('component_measures.facet_category.overall_category.estimated')}
); } + +export default React.memo(AfterMergeEstimate); diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueLabel.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueLabel.tsx deleted file mode 100644 index 6753b0e75dd..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueLabel.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 classNames from 'classnames'; -import * as React from 'react'; -import { Link } from 'react-router'; -import { formatMeasure } from 'sonar-ui-common/helpers/measures'; -import DocTooltip from '../../../components/docs/DocTooltip'; -import { getLeakValue } from '../../../components/measure/utils'; -import { getBranchLikeQuery } from '../../../helpers/branch-like'; -import { findMeasure } from '../../../helpers/measures'; -import { getComponentIssuesUrl } from '../../../helpers/urls'; -import { BranchLike } from '../../../types/branch-like'; -import { getMetricName, IssueType, ISSUETYPE_MAP } from '../utils'; - -export interface Props { - branchLike?: BranchLike; - className?: string; - component: T.Component; - docTooltip?: Promise<{ default: string }>; - measures: T.Measure[]; - type: IssueType; -} - -export default function IssueLabel({ - branchLike, - className, - component, - docTooltip, - measures, - type -}: Props) { - const { metric, iconClass } = ISSUETYPE_MAP[type]; - const measure = findMeasure(measures, metric); - - let value; - if (measure) { - value = getLeakValue(measure); - } - - const params = { - ...getBranchLikeQuery(branchLike), - resolved: 'false', - types: type - }; - - return ( - <> - {value === undefined ? ( - — - ) : ( - - {formatMeasure(value, 'SHORT_INT')} - - )} - {React.createElement(iconClass, { className: 'big-spacer-left little-spacer-right' })} - {getMetricName(metric)} - {docTooltip && } - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueRating.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueRating.tsx deleted file mode 100644 index 221e05f5a07..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/IssueRating.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 Tooltip from 'sonar-ui-common/components/controls/Tooltip'; -import Rating from 'sonar-ui-common/components/ui/Rating'; -import { getLeakValue, getRatingTooltip } from '../../../components/measure/utils'; -import DrilldownLink from '../../../components/shared/DrilldownLink'; -import { findMeasure } from '../../../helpers/measures'; -import { BranchLike } from '../../../types/branch-like'; -import { getRatingName, IssueType, ISSUETYPE_MAP } from '../utils'; - -interface Props { - branchLike?: BranchLike; - component: T.Component; - measures: T.Measure[]; - type: IssueType; -} - -export default function IssueRating({ branchLike, component, measures, type }: Props) { - const { rating } = ISSUETYPE_MAP[type]; - const measure = findMeasure(measures, rating); - - if (!rating || !measure) { - return null; - } - - const value = getLeakValue(measure); - const tooltip = value && getRatingTooltip(rating, Number(value)); - - return ( - <> - {getRatingName(type)} - - - - - - - - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/LargeQualityGateBadge.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/LargeQualityGateBadge.tsx index cb5e106c61f..eb7c69748bc 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/LargeQualityGateBadge.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/LargeQualityGateBadge.tsx @@ -25,7 +25,6 @@ import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; import HelpIcon from 'sonar-ui-common/components/icons/HelpIcon'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { colors } from '../../../app/theme'; -import { isSonarCloud } from '../../../helpers/system'; import { getQualityGatesUrl, getQualityGateUrl } from '../../../helpers/urls'; interface Props { @@ -33,25 +32,17 @@ interface Props { level?: T.Status; } -export default function LargeQualityGateBadge({ component, level }: Props) { +export function LargeQualityGateBadge({ component, level }: Props) { const success = level === 'OK'; - let path; - if (isSonarCloud()) { - path = - component.qualityGate === undefined - ? getQualityGatesUrl(component.organization) - : getQualityGateUrl(component.qualityGate.key, component.organization); - } else { - path = - component.qualityGate === undefined - ? getQualityGatesUrl() - : getQualityGateUrl(component.qualityGate.key); - } + const path = + component.qualityGate === undefined + ? getQualityGatesUrl() + : getQualityGateUrl(component.qualityGate.key); return (
@@ -73,8 +64,10 @@ export default function LargeQualityGateBadge({ component, level }: Props) {
{level !== undefined && ( -

{translate('metric.level', level)}

+

{translate('metric.level', level)}

)} ); } + +export default React.memo(LargeQualityGateBadge); diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasurementLabel.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasurementLabel.tsx deleted file mode 100644 index b940161df0a..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/MeasurementLabel.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 classNames from 'classnames'; -import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { formatMeasure } from 'sonar-ui-common/helpers/measures'; -import { getLeakValue } from '../../../components/measure/utils'; -import DrilldownLink from '../../../components/shared/DrilldownLink'; -import { findMeasure } from '../../../helpers/measures'; -import { BranchLike } from '../../../types/branch-like'; -import { MEASUREMENTS_MAP, MeasurementType } from '../utils'; - -interface Props { - branchLike?: BranchLike; - className?: string; - component: T.Component; - measures: T.Measure[]; - type: MeasurementType; -} - -export default class MeasurementLabel extends React.Component { - getLabelText = () => { - const { branchLike, component, measures, type } = this.props; - const { expandedLabelKey, linesMetric, labelKey } = MEASUREMENTS_MAP[type]; - - const measure = findMeasure(measures, linesMetric); - if (!measure) { - return translate(labelKey); - } else { - return ( - - {formatMeasure(getLeakValue(measure), 'SHORT_INT')} - - ) - }} - /> - ); - } - }; - - render() { - const { branchLike, className, component, measures, type } = this.props; - const { iconClass, metric } = MEASUREMENTS_MAP[type]; - - const measure = findMeasure(measures, metric); - - let value; - if (measure) { - value = getLeakValue(measure); - } - - return ( - <> - {value === undefined ? ( - — - ) : ( - <> - - {React.createElement(iconClass, { size: 'big', value: Number(value) })} - - - {formatMeasure(value, 'PERCENT')} - - - )} - {this.getLabelText()} - - ); - } -} 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 new file mode 100644 index 00000000000..38c6b0b3fee --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx @@ -0,0 +1,258 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 classNames from 'classnames'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { isDefined } from 'sonar-ui-common/helpers/types'; +import { getMeasuresAndMeta } from '../../../api/measures'; +import DocTooltip from '../../../components/docs/DocTooltip'; +import { getBranchLikeQuery } from '../../../helpers/branch-like'; +import { enhanceConditionWithMeasure, enhanceMeasuresWithMetrics } from '../../../helpers/measures'; +import { fetchBranchStatus } from '../../../store/rootActions'; +import { getBranchStatusByBranchLike, Store } from '../../../store/rootReducer'; +import { BranchLike, PullRequest } from '../../../types/branch-like'; +import { QualityGateStatusCondition } from '../../../types/quality-gates'; +import IssueLabel from '../components/IssueLabel'; +import IssueRating from '../components/IssueRating'; +import MeasurementLabel from '../components/MeasurementLabel'; +import QualityGateConditions from '../components/QualityGateConditions'; +import '../styles.css'; +import { IssueType, MeasurementType, PR_METRICS } from '../utils'; +import AfterMergeEstimate from './AfterMergeEstimate'; +import LargeQualityGateBadge from './LargeQualityGateBadge'; + +interface Props { + branchLike: PullRequest; + component: T.Component; + conditions?: QualityGateStatusCondition[]; + fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise; + ignoredConditions?: boolean; + status?: T.Status; +} + +interface State { + loading: boolean; + measures: T.MeasureEnhanced[]; +} + +export class PullRequestOverview extends React.PureComponent { + mounted = false; + + state: State = { + loading: false, + measures: [] + }; + + componentDidMount() { + this.mounted = true; + this.fetchBranchData(); + } + + componentWillUnmount() { + this.mounted = false; + } + + fetchBranchData = () => { + const { + branchLike, + component: { key } + } = this.props; + + this.setState({ loading: true }); + + Promise.all([ + getMeasuresAndMeta(key, PR_METRICS, { + additionalFields: 'metrics', + ...getBranchLikeQuery(branchLike) + }), + this.props.fetchBranchStatus(branchLike, key) + ]).then( + ([{ component, metrics }]) => { + if (this.mounted && component.measures) { + this.setState({ + loading: false, + measures: enhanceMeasuresWithMetrics(component.measures || [], metrics || []) + }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + }; + + render() { + const { branchLike, component, conditions, ignoredConditions, status } = this.props; + const { loading, measures } = this.state; + + if (loading) { + return ( +
+ +
+ ); + } + + if (conditions === undefined) { + return null; + } + + const failedConditions = conditions + .filter(condition => condition.level === 'ERROR') + .map(c => enhanceConditionWithMeasure(c, measures)) + .filter(isDefined); + + return ( +
+
0 + })}> + {ignoredConditions && ( + + + {translate('overview.quality_gate.ignored_conditions')} + + + + )} +
+
+

+ {translate('overview.quality_gate')} + +

+ +
+ + {failedConditions.length > 0 && ( +
+

+ {translate('overview.failed_conditions')} +

+ +
+ )} + +
+

+ {translate('overview.measures')} +

+ +
+ {[IssueType.Bug, IssueType.Vulnerability, IssueType.CodeSmell].map( + (type: IssueType) => ( +
+
+ +
+ {type === 'VULNERABILITY' && ( +
+ +
+ )} +
+ +
+
+ ) + )} + + {[MeasurementType.Coverage, MeasurementType.Duplication].map( + (type: MeasurementType) => ( +
+
+ +
+ + +
+ ) + )} +
+
+
+
+
+ ); + } +} + +const mapStateToProps = (state: Store, { branchLike, component }: Props) => { + const { conditions, ignoredConditions, status } = getBranchStatusByBranchLike( + state, + component.key, + branchLike + ); + return { conditions, ignoredConditions, status }; +}; + +const mapDispatchToProps = { fetchBranchStatus: fetchBranchStatus as any }; + +export default connect(mapStateToProps, mapDispatchToProps)(PullRequestOverview); diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/ReviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/ReviewApp.tsx deleted file mode 100644 index 21e5fc5324a..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/ReviewApp.tsx +++ /dev/null @@ -1,243 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 classNames from 'classnames'; -import * as React from 'react'; -import { connect } from 'react-redux'; -import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; -import { Alert } from 'sonar-ui-common/components/ui/Alert'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getMeasures } from '../../../api/measures'; -import DocTooltip from '../../../components/docs/DocTooltip'; -import { getBranchLikeQuery } from '../../../helpers/branch-like'; -import { fetchBranchStatus } from '../../../store/rootActions'; -import { getBranchStatusByBranchLike, Store } from '../../../store/rootReducer'; -import { BranchLike } from '../../../types/branch-like'; -import QualityGateConditions from '../qualityGate/QualityGateConditions'; -import '../styles.css'; -import { IssueType, MeasurementType, PR_METRICS } from '../utils'; -import AfterMergeEstimate from './AfterMergeEstimate'; -import IssueLabel from './IssueLabel'; -import IssueRating from './IssueRating'; -import LargeQualityGateBadge from './LargeQualityGateBadge'; -import MeasurementLabel from './MeasurementLabel'; - -interface OwnProps { - branchLike: BranchLike; - component: T.Component; -} - -interface StateProps { - conditions?: T.QualityGateStatusCondition[]; - ignoredConditions?: boolean; - status?: T.Status; -} - -interface DispatchProps { - fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise; -} - -type Props = OwnProps & StateProps & DispatchProps; - -interface State { - loading: boolean; - measures: T.Measure[]; -} - -export class ReviewApp extends React.PureComponent { - mounted = false; - - state: State = { - loading: false, - measures: [] - }; - - componentDidMount() { - this.mounted = true; - this.fetchBranchData(); - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchBranchData = () => { - const { branchLike, component } = this.props; - - this.setState({ loading: true }); - - Promise.all([ - getMeasures({ - component: component.key, - metricKeys: PR_METRICS.join(), - ...getBranchLikeQuery(branchLike) - }), - this.props.fetchBranchStatus(branchLike, component.key) - ]).then( - ([measures]) => { - if (this.mounted && measures) { - this.setState({ - loading: false, - measures - }); - } - }, - () => { - if (this.mounted) { - this.setState({ loading: false }); - } - } - ); - }; - - render() { - const { branchLike, component, conditions, ignoredConditions, status } = this.props; - const { loading, measures } = this.state; - - if (loading || !conditions) { - return ( -
- -
- ); - } - - const erroredConditions = conditions.filter(condition => condition.level === 'ERROR'); - - return ( -
-
0 - })}> - {ignoredConditions && ( - - - {translate('overview.quality_gate.ignored_conditions')} - - - - )} -
-
-

- {translate('overview.quality_gate')} - -

- -
- - {erroredConditions.length > 0 && ( -
-

{translate('overview.failed_conditions')}

- -
- )} - -
-

{translate('overview.metrics')}

- - {['BUG', 'VULNERABILITY', 'CODE_SMELL'].map((type: IssueType) => ( -
-
- -
- {type === 'VULNERABILITY' && ( -
- -
- )} -
- -
-
- ))} - - {['COVERAGE', 'DUPLICATION'].map((type: MeasurementType) => ( -
-
- -
- - -
- ))} -
-
-
-
- ); - } -} - -const mapStateToProps = (state: Store, { branchLike, component }: OwnProps) => { - const { conditions, ignoredConditions, status } = getBranchStatusByBranchLike( - state, - component.key, - branchLike - ); - return { conditions, ignoredConditions, status }; -}; - -const mapDispatchToProps = { fetchBranchStatus: fetchBranchStatus as any }; - -export default connect(mapStateToProps, mapDispatchToProps)(ReviewApp); diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/AfterMergeEstimate-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/AfterMergeEstimate-test.tsx index 656a26f2075..6e99380f466 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/AfterMergeEstimate-test.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/AfterMergeEstimate-test.tsx @@ -19,26 +19,36 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockMeasure } from '../../../../helpers/testMocks'; -import AfterMergeEstimate from '../AfterMergeEstimate'; +import { mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks'; +import { MetricKey } from '../../../../types/metrics'; +import { MeasurementType } from '../../utils'; +import { AfterMergeEstimate, AfterMergeEstimateProps } from '../AfterMergeEstimate'; it('should render correctly for coverage', () => { - expect(shallowRender({ measures: [mockMeasure({ metric: 'coverage' })] })).toMatchSnapshot(); + expect(shallowRender()).toMatchSnapshot(); }); it('should render correctly for duplications', () => { expect( shallowRender({ - measures: [mockMeasure({ metric: 'duplicated_lines_density' })], - type: 'DUPLICATION' + measures: [ + mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.duplicated_lines_density }) }) + ], + type: MeasurementType.Duplication }) ).toMatchSnapshot(); }); it('should render correctly with no value', () => { - expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ measures: [] })).toMatchSnapshot(); }); -function shallowRender(props = {}) { - return shallow(); +function shallowRender(props: Partial = {}) { + return shallow( + + ); } diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/IssueLabel-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/IssueLabel-test.tsx deleted file mode 100644 index 271b63c48b9..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/IssueLabel-test.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; -import { mockComponent, mockMeasure } from '../../../../helpers/testMocks'; -import IssueLabel, { Props } from '../IssueLabel'; - -it('should render correctly for bugs', () => { - expect( - shallowRender({ - measures: [mockMeasure({ metric: 'new_bugs' })] - }) - ).toMatchSnapshot(); -}); - -it('should render correctly for code smells', () => { - expect( - shallowRender({ - measures: [mockMeasure({ metric: 'new_code_smells' })], - type: 'CODE_SMELL' - }) - ).toMatchSnapshot(); -}); - -it('should render correctly for vulnerabilities', () => { - expect( - shallowRender({ - measures: [mockMeasure({ metric: 'new_vulnerabilities' })], - type: 'VULNERABILITY' - }) - ).toMatchSnapshot(); -}); - -it('should render correctly for hotspots', () => { - expect( - shallowRender({ - docTooltip: Promise.resolve({ default: 'tooltip text' }), - measures: [mockMeasure({ metric: 'new_security_hotspots' })], - type: 'SECURITY_HOTSPOT' - }) - ).toMatchSnapshot(); -}); - -it('should render correctly if no values are present', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/IssueRating-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/IssueRating-test.tsx deleted file mode 100644 index 0142aeb5050..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/IssueRating-test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; -import { mockComponent, mockMeasure } from '../../../../helpers/testMocks'; -import IssueRating from '../IssueRating'; - -it('should render correctly for bugs', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should render correctly for code smells', () => { - expect(shallowRender({ type: 'CODE_SMELL' })).toMatchSnapshot(); -}); - -it('should render correctly for vulnerabilities', () => { - expect(shallowRender({ type: 'VULNERABILITY' })).toMatchSnapshot(); -}); - -it('should render correctly if no values are present', () => { - expect(shallowRender({ measures: [mockMeasure({ metric: 'NONE' })] })).toMatchSnapshot(); -}); - -function shallowRender(props = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/LargeQualityGateBadge-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/LargeQualityGateBadge-test.tsx index 1a119cf9b23..0cf7f78e0d6 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/LargeQualityGateBadge-test.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/LargeQualityGateBadge-test.tsx @@ -19,26 +19,14 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { isSonarCloud } from '../../../../helpers/system'; import { mockComponent } from '../../../../helpers/testMocks'; -import LargeQualityGateBadge from '../LargeQualityGateBadge'; - -jest.mock('../../../../helpers/system', () => ({ - isSonarCloud: jest.fn() -})); +import { LargeQualityGateBadge } from '../LargeQualityGateBadge'; it('should render correctly for SQ', () => { - (isSonarCloud as jest.Mock).mockReturnValue(false); - expect(shallowRender()).toMatchSnapshot(); expect(shallowRender({ level: 'OK' })).toMatchSnapshot(); }); -it('should render the link correctly for SC', () => { - (isSonarCloud as jest.Mock).mockReturnValue(true); - expect(shallowRender()).toMatchSnapshot(); -}); - function shallowRender(props = {}) { return shallow(); } diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/MeasurementLabel-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/MeasurementLabel-test.tsx deleted file mode 100644 index 7e81f62d0f3..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/MeasurementLabel-test.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockBranch } from '../../../../helpers/mocks/branch-like'; -import { mockComponent, mockMeasure } from '../../../../helpers/testMocks'; -import MeasurementLabel from '../MeasurementLabel'; - -it('should render correctly for coverage', () => { - expect(shallowRender()).toMatchSnapshot(); - expect( - shallowRender({ - measures: [ - mockMeasure({ metric: 'new_coverage' }), - mockMeasure({ metric: 'new_lines_to_cover' }) - ] - }) - ).toMatchSnapshot(); -}); - -it('should render correctly for duplications', () => { - expect( - shallowRender({ - measures: [mockMeasure({ metric: 'new_duplicated_lines_density' })], - type: 'DUPLICATION' - }) - ).toMatchSnapshot(); -}); - -it('should render correctly with no value', () => { - expect(shallowRender({ measures: [mockMeasure({ metric: 'NONE' })] })).toMatchSnapshot(); -}); - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-test.tsx new file mode 100644 index 00000000000..1c2c1cb34d5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-test.tsx @@ -0,0 +1,124 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; +import { getMeasuresAndMeta } from '../../../../api/measures'; +import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; +import { mockQualityGateStatusCondition } from '../../../../helpers/mocks/quality-gates'; +import { mockComponent } from '../../../../helpers/testMocks'; +import { PullRequestOverview } from '../PullRequestOverview'; + +jest.mock('../../../../api/measures', () => { + const { mockMeasure, mockMetric } = require.requireActual('../../../../helpers/testMocks'); + return { + getMeasuresAndMeta: jest.fn().mockResolvedValue({ + component: { + measures: [ + mockMeasure({ metric: 'new_bugs' }), + mockMeasure({ metric: 'new_vulnerabilities' }), + mockMeasure({ metric: 'new_code_smells' }), + mockMeasure({ metric: 'new_security_hotspots' }) + ] + }, + metrics: [ + mockMetric({ key: 'new_bugs', name: 'new_bugs', id: 'new_bugs' }), + mockMetric({ + key: 'new_vulnerabilities', + name: 'new_vulnerabilities', + id: 'new_vulnerabilities' + }), + mockMetric({ key: 'new_code_smells', name: 'new_code_smells', id: 'new_code_smells' }), + mockMetric({ + key: 'new_security_hotspots', + name: 'new_security_hotspots', + id: 'new_security_hotspots' + }) + ] + }) + }; +}); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +it('should render correctly for a passed QG', async () => { + const fetchBranchStatus = jest.fn(); + + const wrapper = shallowRender({ fetchBranchStatus, status: 'OK' }); + + wrapper.setProps({ conditions: [] }); + + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + + expect(wrapper.find('QualityGateConditions').exists()).toBe(false); + + expect(getMeasuresAndMeta).toBeCalled(); + expect(fetchBranchStatus).toBeCalled(); +}); + +it('should render correctly if conditions are ignored', async () => { + const wrapper = shallowRender({ conditions: [], ignoredConditions: true }); + await waitAndUpdate(wrapper); + expect(wrapper.find('Alert').exists()).toBe(true); +}); + +it('should render correctly for a failed QG', async () => { + const wrapper = shallowRender({ + status: 'ERROR', + conditions: [ + mockQualityGateStatusCondition({ + error: '1.0', + level: 'OK', + metric: 'new_bugs', + period: 1 + }), + mockQualityGateStatusCondition({ + error: '1.0', + metric: 'new_code_smells', + period: 1 + }) + ] + }); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); +}); + +it('should correctly handle a WS failure', async () => { + (getMeasuresAndMeta as jest.Mock).mockRejectedValueOnce({}); + const fetchBranchStatus = jest.fn().mockRejectedValue({}); + const wrapper = shallowRender({ fetchBranchStatus }); + + await waitAndUpdate(wrapper); + expect(wrapper.type()).toBeNull(); +}); + +function shallowRender(props: Partial = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/ReviewApp-test.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/ReviewApp-test.tsx deleted file mode 100644 index 18d06897fa2..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/ReviewApp-test.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { getMeasures } from '../../../../api/measures'; -import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; -import { mockComponent, mockQualityGateStatusCondition } from '../../../../helpers/testMocks'; -import { ReviewApp } from '../ReviewApp'; - -jest.mock('../../../../api/measures', () => { - const { mockMeasure } = require.requireActual('../../../../helpers/testMocks'); - return { - getMeasures: jest - .fn() - .mockResolvedValue([ - mockMeasure({ metric: 'new_bugs ' }), - mockMeasure({ metric: 'new_vulnerabilities' }), - mockMeasure({ metric: 'new_code_smells' }), - mockMeasure({ metric: 'new_security_hotspots' }) - ]) - }; -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('should render correctly for a passed QG', async () => { - const fetchBranchStatus = jest.fn(); - - const wrapper = shallowRender({ fetchBranchStatus, status: 'OK' }); - - wrapper.setProps({ conditions: [] }); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - - expect(wrapper.find('QualityGateConditions').exists()).toBe(false); - - expect(getMeasures).toBeCalled(); - expect(fetchBranchStatus).toBeCalled(); -}); - -it('should render correctly if conditions are ignored', async () => { - const wrapper = shallowRender({ conditions: [], ignoredConditions: true }); - await waitAndUpdate(wrapper); - expect(wrapper.find('Alert').exists()).toBe(true); -}); - -it('should render correctly for a failed QG', async () => { - const wrapper = shallowRender({ - status: 'ERROR', - conditions: [ - mockQualityGateStatusCondition({ - error: '1.0', - level: 'OK', - metric: 'new_bugs', - period: 1 - }), - mockQualityGateStatusCondition({ - error: '1.0', - metric: 'new_code_smells', - period: 1 - }) - ] - }); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - - expect(wrapper.find('QualityGateConditions').exists()).toBe(true); -}); - -it('should correctly handle a WS failure', async () => { - (getMeasures as jest.Mock).mockRejectedValue({}); - const fetchBranchStatus = jest.fn().mockRejectedValue({}); - const wrapper = shallowRender({ fetchBranchStatus }); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/AfterMergeEstimate-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/AfterMergeEstimate-test.tsx.snap index 274d006d8cd..fd304d0e754 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/AfterMergeEstimate-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/AfterMergeEstimate-test.tsx.snap @@ -10,7 +10,7 @@ exports[`should render correctly for coverage 1`] = ` 1.0%
component_measures.facet_category.overall_category.estimated @@ -27,7 +27,7 @@ exports[`should render correctly for duplications 1`] = ` 1.0% component_measures.facet_category.overall_category.estimated diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/IssueLabel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/IssueLabel-test.tsx.snap deleted file mode 100644 index f2ec9952a75..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/IssueLabel-test.tsx.snap +++ /dev/null @@ -1,123 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly for bugs 1`] = ` - - - 1 - - - overview.metric.new_bugs - -`; - -exports[`should render correctly for code smells 1`] = ` - - - 1 - - - overview.metric.new_code_smells - -`; - -exports[`should render correctly for hotspots 1`] = ` - - - 1 - - - overview.metric.new_security_hotspots - - -`; - -exports[`should render correctly for vulnerabilities 1`] = ` - - - 1 - - - overview.metric.new_vulnerabilities - -`; - -exports[`should render correctly if no values are present 1`] = ` - - - — - - - overview.metric.new_bugs - -`; diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/IssueRating-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/IssueRating-test.tsx.snap deleted file mode 100644 index 7025894710c..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/IssueRating-test.tsx.snap +++ /dev/null @@ -1,108 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly for bugs 1`] = ` - - - metric_domain.Reliability - - - - - - - - - -`; - -exports[`should render correctly for code smells 1`] = ` - - - metric_domain.Maintainability - - - - - - - - - -`; - -exports[`should render correctly for vulnerabilities 1`] = ` - - - metric_domain.Security - - - - - - - - - -`; - -exports[`should render correctly if no values are present 1`] = `""`; diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/LargeQualityGateBadge-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/LargeQualityGateBadge-test.tsx.snap index 6fdbae1eb7d..517bbf540be 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/LargeQualityGateBadge-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/LargeQualityGateBadge-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should render correctly for SQ 1`] = `
-

metric.level.ERROR -

+
`; exports[`should render correctly for SQ 2`] = `
-

metric.level.OK -

-
-`; - -exports[`should render the link correctly for SC 1`] = ` -
-
- - overview.on_new_code_long - - - overview.quality_gate - , - } - } - /> - } - > - - -
-

- metric.level.ERROR -

+
`; diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/MeasurementLabel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/MeasurementLabel-test.tsx.snap deleted file mode 100644 index 6c1396c176d..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/MeasurementLabel-test.tsx.snap +++ /dev/null @@ -1,133 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly for coverage 1`] = ` - - - - - - 1.0% - - - overview.metric.coverage - - -`; - -exports[`should render correctly for coverage 2`] = ` - - - - - - 1.0% - - - - 1 - , - } - } - /> - - -`; - -exports[`should render correctly for duplications 1`] = ` - - - - - - 1.0% - - - overview.metric.duplications - - -`; - -exports[`should render correctly with no value 1`] = ` - - - — - - - overview.metric.coverage - - -`; diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap new file mode 100644 index 00000000000..3d778f26731 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/PullRequestOverview-test.tsx.snap @@ -0,0 +1,2599 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for a failed QG 1`] = ` +
+
+
+
+

+ overview.quality_gate + +

+ +
+
+

+ overview.failed_conditions +

+ +
+
+

+ overview.measures +

+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+`; + +exports[`should render correctly for a passed QG 1`] = ` +
+
+
+
+

+ overview.quality_gate + +

+ +
+
+

+ overview.measures +

+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+`; diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/ReviewApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/ReviewApp-test.tsx.snap deleted file mode 100644 index 66376718be8..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/__snapshots__/ReviewApp-test.tsx.snap +++ /dev/null @@ -1,2138 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should correctly handle a WS failure 1`] = ` -
- -
-`; - -exports[`should render correctly for a failed QG 1`] = ` -
-
-
-
-

- overview.quality_gate - -

- -
-
-

- overview.failed_conditions -

- -
-
-

- overview.metrics -

-
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-`; - -exports[`should render correctly for a passed QG 1`] = ` -
-
-
-
-

- overview.quality_gate - -

- -
-
-

- overview.metrics -

-
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-`; 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 d7cc776291a..fa7c0341f9c 100644 --- a/server/sonar-web/src/main/js/apps/overview/utils.ts +++ b/server/sonar-web/src/main/js/apps/overview/utils.ts @@ -24,145 +24,208 @@ import VulnerabilityIcon from 'sonar-ui-common/components/icons/VulnerabilityIco import DuplicationsRating from 'sonar-ui-common/components/ui/DuplicationsRating'; import { translate } from 'sonar-ui-common/helpers/l10n'; import CoverageRating from '../../components/ui/CoverageRating'; +import { MetricKey } from '../../types/metrics'; +import { QualityGateStatusConditionEnhanced } from '../../types/quality-gates'; -export const METRICS = [ +export const METRICS: string[] = [ // quality gate - 'alert_status', - 'quality_gate_details', + MetricKey.alert_status, + MetricKey.quality_gate_details, // TODO: still relevant? // bugs - 'bugs', - 'new_bugs', - 'reliability_rating', - 'new_reliability_rating', + MetricKey.bugs, + MetricKey.new_bugs, + MetricKey.reliability_rating, + MetricKey.new_reliability_rating, // vulnerabilities - 'vulnerabilities', - 'new_vulnerabilities', - 'security_rating', - 'new_security_rating', - 'security_hotspots', - 'new_security_hotspots', + MetricKey.vulnerabilities, + MetricKey.new_vulnerabilities, + MetricKey.security_rating, + MetricKey.new_security_rating, + MetricKey.security_hotspots, + MetricKey.new_security_hotspots, // code smells - 'code_smells', - 'new_code_smells', - 'sqale_rating', - 'new_maintainability_rating', - 'sqale_index', - 'new_technical_debt', + MetricKey.code_smells, + MetricKey.new_code_smells, + MetricKey.sqale_rating, + MetricKey.new_maintainability_rating, + MetricKey.sqale_index, + MetricKey.new_technical_debt, // coverage - 'coverage', - 'new_coverage', - 'new_lines_to_cover', - 'tests', + MetricKey.coverage, + MetricKey.new_coverage, + MetricKey.lines_to_cover, + MetricKey.new_lines_to_cover, + MetricKey.tests, // duplications - 'duplicated_lines_density', - 'new_duplicated_lines_density', - 'duplicated_blocks', + MetricKey.duplicated_lines_density, + MetricKey.new_duplicated_lines_density, + MetricKey.duplicated_blocks, // size - 'ncloc', - 'lines', - 'ncloc_language_distribution', - 'projects', - 'new_lines' + MetricKey.ncloc, + MetricKey.ncloc_language_distribution, + MetricKey.projects, + MetricKey.lines, + MetricKey.new_lines ]; -export const PR_METRICS = [ - 'coverage', - 'new_coverage', - 'new_lines_to_cover', - - 'duplicated_lines_density', - 'new_duplicated_lines_density', - 'new_lines', - 'new_code_smells', - 'new_maintainability_rating', - 'new_bugs', - 'new_reliability_rating', - 'new_vulnerabilities', - 'new_security_hotspots', - 'new_security_rating' +export const PR_METRICS: string[] = [ + MetricKey.coverage, + MetricKey.new_coverage, + MetricKey.new_lines_to_cover, + + MetricKey.duplicated_lines_density, + MetricKey.new_duplicated_lines_density, + MetricKey.new_lines, + MetricKey.new_code_smells, + MetricKey.new_maintainability_rating, + MetricKey.new_bugs, + MetricKey.new_reliability_rating, + MetricKey.new_vulnerabilities, + MetricKey.new_security_hotspots, + MetricKey.new_security_rating ]; -export const HISTORY_METRICS_LIST = [ - 'bugs', - 'vulnerabilities', - 'sqale_index', - 'duplicated_lines_density', - 'ncloc', - 'coverage' +export const HISTORY_METRICS_LIST: string[] = [ + MetricKey.bugs, + MetricKey.vulnerabilities, + MetricKey.sqale_index, + MetricKey.duplicated_lines_density, + MetricKey.ncloc, + MetricKey.coverage ]; -export type MeasurementType = 'COVERAGE' | 'DUPLICATION'; +export enum MeasurementType { + Coverage = 'COVERAGE', + Duplication = 'DUPLICATION' +} -export const MEASUREMENTS_MAP = { - COVERAGE: { - metric: 'new_coverage', - linesMetric: 'new_lines_to_cover', - afterMergeMetric: 'coverage', - labelKey: 'overview.metric.coverage', +const MEASUREMENTS_MAP = { + [MeasurementType.Coverage]: { + metric: MetricKey.coverage, + newMetric: MetricKey.new_coverage, + linesMetric: MetricKey.lines_to_cover, + newLinesMetric: MetricKey.new_lines_to_cover, + afterMergeMetric: MetricKey.coverage, + labelKey: 'metric.coverage.name', expandedLabelKey: 'overview.coverage_on_X_lines', + newLinesExpandedLabelKey: 'overview.coverage_on_X_new_lines', iconClass: CoverageRating }, - DUPLICATION: { - metric: 'new_duplicated_lines_density', - linesMetric: 'new_lines', - afterMergeMetric: 'duplicated_lines_density', - labelKey: 'overview.metric.duplications', - expandedLabelKey: 'overview.duplications_on_X', + [MeasurementType.Duplication]: { + metric: MetricKey.duplicated_lines_density, + newMetric: MetricKey.new_duplicated_lines_density, + linesMetric: MetricKey.ncloc, + newLinesMetric: MetricKey.new_lines, + afterMergeMetric: MetricKey.duplicated_lines_density, + labelKey: 'metric.duplicated_lines_density.short_name', + expandedLabelKey: 'overview.duplications_on_X_lines', + newLinesExpandedLabelKey: 'overview.duplications_on_X_new_lines', iconClass: DuplicationsRating } }; -export type IssueType = 'CODE_SMELL' | 'VULNERABILITY' | 'BUG' | 'SECURITY_HOTSPOT'; +export enum IssueType { + CodeSmell = 'CODE_SMELL', + Vulnerability = 'VULNERABILITY', + Bug = 'BUG', + SecurityHotspot = 'SECURITY_HOTSPOT' +} -export const ISSUETYPE_MAP = { - CODE_SMELL: { - metric: 'new_code_smells', - rating: 'new_maintainability_rating', +const ISSUETYPE_MAP = { + [IssueType.CodeSmell]: { + metric: MetricKey.code_smells, + newMetric: MetricKey.new_code_smells, + rating: MetricKey.sqale_rating, + newRating: MetricKey.new_maintainability_rating, ratingName: 'Maintainability', iconClass: CodeSmellIcon }, - VULNERABILITY: { - metric: 'new_vulnerabilities', - rating: 'new_security_rating', + [IssueType.Vulnerability]: { + metric: MetricKey.vulnerabilities, + newMetric: MetricKey.new_vulnerabilities, + rating: MetricKey.security_rating, + newRating: MetricKey.new_security_rating, ratingName: 'Security', iconClass: VulnerabilityIcon }, - BUG: { - metric: 'new_bugs', - rating: 'new_reliability_rating', + [IssueType.Bug]: { + metric: MetricKey.bugs, + newMetric: MetricKey.new_bugs, + rating: MetricKey.reliability_rating, + newRating: MetricKey.new_reliability_rating, ratingName: 'Reliability', iconClass: BugIcon }, - SECURITY_HOTSPOT: { - metric: 'new_security_hotspots', + [IssueType.SecurityHotspot]: { + metric: MetricKey.security_hotspots, + newMetric: MetricKey.new_security_hotspots, rating: '', + newRating: '', ratingName: '', iconClass: SecurityHotspotIcon } }; -export function getMetricName(metricKey: string) { - return translate('overview.metric', metricKey); +export function getIssueRatingName(type: IssueType) { + return translate('metric_domain', ISSUETYPE_MAP[type].ratingName); } -export function getRatingName(type: IssueType) { - return translate('metric_domain', ISSUETYPE_MAP[type].ratingName); +export function getIssueIconClass(type: IssueType) { + return ISSUETYPE_MAP[type].iconClass; +} + +export function getIssueMetricKey(type: IssueType, useDiffMetric: boolean) { + return useDiffMetric ? ISSUETYPE_MAP[type].newMetric : ISSUETYPE_MAP[type].metric; +} + +export function getIssueRatingMetricKey(type: IssueType, useDiffMetric: boolean) { + return useDiffMetric ? ISSUETYPE_MAP[type].newRating : ISSUETYPE_MAP[type].rating; +} + +export function getMeasurementIconClass(type: MeasurementType) { + return MEASUREMENTS_MAP[type].iconClass; +} + +export function getMeasurementMetricKey(type: MeasurementType, useDiffMetric: boolean) { + return useDiffMetric ? MEASUREMENTS_MAP[type].newMetric : MEASUREMENTS_MAP[type].metric; +} + +export function getMeasurementAfterMergeMetricKey(type: MeasurementType) { + return MEASUREMENTS_MAP[type].afterMergeMetric; +} + +export function getMeasurementLinesMetricKey(type: MeasurementType, useDiffMetric: boolean) { + return useDiffMetric ? MEASUREMENTS_MAP[type].newLinesMetric : MEASUREMENTS_MAP[type].linesMetric; +} + +export function getMeasurementLabelKeys(type: MeasurementType, useDiffMetric: boolean) { + return { + expandedLabelKey: useDiffMetric + ? MEASUREMENTS_MAP[type].newLinesExpandedLabelKey + : MEASUREMENTS_MAP[type].expandedLabelKey, + labelKey: MEASUREMENTS_MAP[type].labelKey + }; } /* * Extract a specific metric's threshold from the quality gate details */ -export function getThreshold(measures: T.MeasureEnhanced[], metricKey: string): number | undefined { - const detailsMeasure = measures.find(measure => measure.metric.key === 'quality_gate_details'); +export function getThreshold( + measures: T.MeasureEnhanced[], + metricKey: MetricKey | string +): number | undefined { + const detailsMeasure = measures.find( + measure => measure.metric.key === MetricKey.quality_gate_details + ); if (detailsMeasure && detailsMeasure.value) { const details = safeParse(detailsMeasure.value); - const conditions: T.QualityGateStatusConditionEnhanced[] = details.conditions || []; + const conditions: QualityGateStatusConditionEnhanced[] = details.conditions || []; const condition = conditions.find(c => c.metric === metricKey); if (condition) {