aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/public/images/source-code.svg1
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/tables.css2
-rw-r--r--server/sonar-web/src/main/js/app/theme.js4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx141
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/Analysis.tsx (renamed from server/sonar-web/src/main/js/apps/overview/events/Analysis.tsx)6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/ApplicationLeakPeriodInfo.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx410
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx115
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/DebtValue.tsx66
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/DrilldownMeasureValue.tsx64
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/Event.tsx (renamed from server/sonar-web/src/main/js/apps/overview/events/Event.tsx)4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx265
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/NoCodeWarning.tsx90
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx125
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx79
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-test.tsx83
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/Analysis-test.tsx (renamed from server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx)36
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-test.tsx349
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverviewRenderer-test.tsx46
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/DebtValue-test.tsx (renamed from server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx)42
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/DrilldownMeasureValue-test.tsx42
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/Event-test.tsx (renamed from server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx)16
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/MeasuresPanel-test.tsx82
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/NoCodeWarning-test.tsx68
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanel-test.tsx83
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx58
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ActivityPanel-test.tsx.snap349
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Analysis-test.tsx.snap81
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ApplicationLeakPeriodInfo-test.tsx.snap19
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverview-test.tsx.snap1702
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverviewRenderer-test.tsx.snap386
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DebtValue-test.tsx.snap65
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DrilldownMeasureValue-test.tsx.snap47
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Event-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/LeakPeriodInfo-test.tsx.snap25
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap5943
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ProjectLeakPeriodInfo-test.tsx.snap115
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanel-test.tsx.snap625
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanelSection-test.tsx.snap436
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/App.tsx29
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx122
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx341
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx69
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx85
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx179
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/Timeline-test.tsx72
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap337
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap790
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.tsx.snap110
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx146
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Analysis-test.tsx.snap41
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx135
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx136
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx193
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx194
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx128
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx80
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx109
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx88
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx115
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap229
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap295
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap251
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap227
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap307
-rw-r--r--server/sonar-web/src/main/js/apps/overview/main/enhance.tsx227
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap81
-rw-r--r--server/sonar-web/src/main/js/apps/overview/styles.css529
-rw-r--r--server/sonar-web/src/main/js/components/intl/__mocks__/DateFromNow.tsx (renamed from server/sonar-web/src/main/js/apps/overview/events/__tests__/Analysis-test.tsx)21
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties42
74 files changed, 12372 insertions, 5477 deletions
diff --git a/server/sonar-web/public/images/source-code.svg b/server/sonar-web/public/images/source-code.svg
new file mode 100644
index 00000000000..dd597d4f7ee
--- /dev/null
+++ b/server/sonar-web/public/images/source-code.svg
@@ -0,0 +1 @@
+<svg width="57" height="51" xmlns="http://www.w3.org/2000/svg"><g fill="#236A97" fill-rule="nonzero"><path d="M47.002 50.629H2.42a2.203 2.203 0 01-2.19-2.21V9.932c0-1.218.982-2.21 2.19-2.21h6.158c.506 0 .916.415.916.925s-.41.925-.916.925H2.42a.359.359 0 00-.357.36v38.487c0 .199.16.36.357.36h44.582a.359.359 0 00.357-.36v-6.206c0-.51.41-.925.917-.925.506 0 .917.414.917.925v6.206c0 1.218-.983 2.21-2.191 2.21z"/><path d="M8.387 18.15h-6.39a.921.921 0 01-.917-.926c0-.51.41-.924.917-.924h6.39c.505 0 .916.414.916.924a.92.92 0 01-.916.925z"/><g><path d="M54.433 43.137H9.851a2.202 2.202 0 01-2.19-2.209V2.44c0-1.218.983-2.209 2.19-2.209h44.582c1.208 0 2.191.991 2.191 2.21v38.487c0 1.218-.983 2.21-2.19 2.21zM9.851 2.081a.358.358 0 00-.356.36v38.487c0 .199.16.36.356.36h44.582a.359.359 0 00.357-.36V2.44c0-.198-.16-.36-.357-.36H9.851z"/><path d="M55.707 10.658H9.428a.921.921 0 01-.917-.925c0-.51.41-.925.917-.925h46.279c.506 0 .917.415.917.925s-.41.925-.917.925zM17.231 6.186h-3.403a.921.921 0 01-.917-.924c0-.51.411-.925.917-.925h3.403c.506 0 .917.414.917.925a.92.92 0 01-.917.924zM24.98 6.186h-3.403a.921.921 0 01-.917-.924c0-.51.41-.925.917-.925h3.403c.506 0 .917.414.917.925 0 .51-.411.924-.918.924z"/><g stroke="#236A97"><path d="M19.685 25.593l7.37 3.048-.54 1.607-8.968-3.82v-1.67l8.968-3.82.54 1.607zM29.63 35.697h-1.973l7.039-20.208h1.93zM37.23 22.546l.539-1.608 8.968 3.82v1.67l-8.968 3.82-.54-1.607 7.37-3.047z"/></g></g></g></svg> \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/app/styles/init/tables.css b/server/sonar-web/src/main/js/app/styles/init/tables.css
index dd34a6cc3a8..75431350415 100644
--- a/server/sonar-web/src/main/js/app/styles/init/tables.css
+++ b/server/sonar-web/src/main/js/app/styles/init/tables.css
@@ -269,7 +269,7 @@ table.data.boxed-padding > thead + tbody > tr:first-child > td {
}
table.data.zebra-hover > tbody > tr:hover {
- background-color: #ecf6fe !important;
+ background-color: var(--rowHoverHighlight) !important;
}
table.data.zebra > tbody > tr.selected {
diff --git a/server/sonar-web/src/main/js/app/theme.js b/server/sonar-web/src/main/js/app/theme.js
index 48b05a12810..7f302abda3b 100644
--- a/server/sonar-web/src/main/js/app/theme.js
+++ b/server/sonar-web/src/main/js/app/theme.js
@@ -55,6 +55,9 @@ module.exports = {
globalNavBarBg: '#262626',
+ // table
+ rowHoverHighlight: '#ecf6fe',
+
// fonts
baseFontColor: '#444',
secondFontColor: '#777',
@@ -145,6 +148,7 @@ module.exports = {
mediumFontSize: '14px',
bigFontSize: '16px',
hugeFontSize: '24px',
+ giganticFontSize: '36px',
hugeControlHeight: `${5 * grid}px`,
largeControlHeight: `${4 * grid}px`,
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx
new file mode 100644
index 00000000000..6118f97707d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/ActivityPanel.tsx
@@ -0,0 +1,141 @@
+/*
+ * 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 DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
+import { parseDate } from 'sonar-ui-common/helpers/dates';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import GraphsHeader from '../../../components/activity-graph/GraphsHeader';
+import GraphsHistory from '../../../components/activity-graph/GraphsHistory';
+import {
+ DEFAULT_GRAPH,
+ generateSeries,
+ getDisplayedHistoryMetrics,
+ splitSeriesInGraphs
+} from '../../../components/activity-graph/utils';
+import ActivityLink from '../../../components/common/ActivityLink';
+import { BranchLike } from '../../../types/branch-like';
+import { GraphType, MeasureHistory } from '../../../types/project-activity';
+import Analysis from './Analysis';
+
+export interface ActivityPanelProps {
+ analyses?: T.Analysis[];
+ branchLike?: BranchLike;
+ component: Pick<T.Component, 'key' | 'qualifier'>;
+ graph?: GraphType;
+ leakPeriodDate?: Date;
+ loading?: boolean;
+ measuresHistory: MeasureHistory[];
+ metrics: T.Metric[];
+ onGraphChange: (graph: GraphType) => void;
+}
+
+const MAX_ANALYSES_NB = 5;
+const MAX_GRAPH_NB = 2;
+const MAX_SERIES_PER_GRAPH = 3;
+
+export function ActivityPanel(props: ActivityPanelProps) {
+ const {
+ analyses = [],
+ branchLike,
+ component,
+ graph = DEFAULT_GRAPH,
+ leakPeriodDate,
+ loading,
+ measuresHistory,
+ metrics
+ } = props;
+
+ const series = generateSeries(
+ measuresHistory,
+ graph,
+ metrics,
+ getDisplayedHistoryMetrics(graph, [])
+ );
+ const graphs = splitSeriesInGraphs(series, MAX_GRAPH_NB, MAX_SERIES_PER_GRAPH);
+ const parsedAnalyses = analyses.map(a => ({ ...a, date: parseDate(a.date) }));
+ let shownLeakPeriodDate;
+ if (leakPeriodDate !== undefined) {
+ const startDate = measuresHistory.reduce((oldest: Date, { history }) => {
+ if (history.length > 0) {
+ const date = parseDate(history[0].date);
+ return oldest.getTime() > date.getTime() ? date : oldest;
+ } else {
+ return oldest;
+ }
+ }, new Date());
+ shownLeakPeriodDate =
+ startDate.getTime() > leakPeriodDate.getTime() ? startDate : leakPeriodDate;
+ }
+
+ const filteredAnalyses = analyses.filter(a => a.events.length > 0).slice(0, MAX_ANALYSES_NB);
+
+ return (
+ <div className="overview-panel big-spacer-top" data-test="overview__activity-panel">
+ <h2 className="overview-panel-title">{translate('overview.activity')}</h2>
+
+ <div className="overview-panel-content">
+ <div className="display-flex-row">
+ <div className="display-flex-column flex-1">
+ <div aria-hidden={true} className="overview-panel-padded display-flex-column flex-1">
+ <GraphsHeader graph={graph} metrics={metrics} updateGraph={props.onGraphChange} />
+ <GraphsHistory
+ analyses={parsedAnalyses}
+ graph={graph}
+ graphs={graphs}
+ leakPeriodDate={shownLeakPeriodDate}
+ loading={Boolean(loading)}
+ measuresHistory={measuresHistory}
+ series={series}
+ />
+ </div>
+
+ <div className="overview-panel-padded bordered-top text-right">
+ <ActivityLink branchLike={branchLike} component={component.key} graph={graph} />
+ </div>
+ </div>
+
+ <div className="overview-panel-padded bordered-left width-30">
+ <div data-test="overview__activity-analyses">
+ <DeferredSpinner
+ className="spacer-top spacer-left"
+ loading={analyses.length === 0 && loading}>
+ {analyses.length === 0 ? (
+ <p className="spacer-top spacer-left note">{translate('no_results')}</p>
+ ) : (
+ <ul className="spacer-top spacer-left">
+ {filteredAnalyses.map(analysis => (
+ <Analysis
+ analysis={analysis}
+ key={analysis.key}
+ qualifier={component.qualifier}
+ />
+ ))}
+ </ul>
+ )}
+ </DeferredSpinner>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+
+export default React.memo(ActivityPanel);
diff --git a/server/sonar-web/src/main/js/apps/overview/events/Analysis.tsx b/server/sonar-web/src/main/js/apps/overview/branches/Analysis.tsx
index 78534752de0..0860bfa80c4 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/Analysis.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/Analysis.tsx
@@ -23,12 +23,12 @@ import { translate } from 'sonar-ui-common/helpers/l10n';
import DateTooltipFormatter from '../../../components/intl/DateTooltipFormatter';
import Event from './Event';
-interface Props {
+export interface AnalysisProps {
analysis: T.Analysis;
qualifier: string;
}
-export default function Analysis({ analysis, ...props }: Props) {
+export function Analysis({ analysis, ...props }: AnalysisProps) {
const sortedEvents = sortBy(
analysis.events,
// versions first
@@ -60,3 +60,5 @@ export default function Analysis({ analysis, ...props }: Props) {
</li>
);
}
+
+export default React.memo(Analysis);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/ApplicationLeakPeriodInfo.tsx b/server/sonar-web/src/main/js/apps/overview/branches/ApplicationLeakPeriodInfo.tsx
index 1d27c582616..3505f3d96a8 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/ApplicationLeakPeriodInfo.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/ApplicationLeakPeriodInfo.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import DateFromNow from '../../../components/intl/DateFromNow';
import { ApplicationPeriod } from '../../../types/application';
@@ -28,18 +29,18 @@ export interface ApplicationLeakPeriodInfoProps {
export function ApplicationLeakPeriodInfo({ leakPeriod }: ApplicationLeakPeriodInfoProps) {
return (
- <>
- <div className="note spacer-top">
- {translateWithParameters('overview.max_new_code_period_from_x', leakPeriod.projectName)}
- </div>
+ <div className="note spacer-top display-inline-flex-center">
<DateFromNow date={leakPeriod.date}>
- {fromNow => (
- <div className="note little-spacer-top">
- {translateWithParameters('overview.started_x', fromNow)}
- </div>
- )}
+ {fromNow => translateWithParameters('overview.started_x', fromNow)}
</DateFromNow>
- </>
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay={translateWithParameters(
+ 'overview.max_new_code_period_from_x',
+ leakPeriod.projectName
+ )}
+ />
+ </div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
new file mode 100644
index 00000000000..6ad66b9295d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverview.tsx
@@ -0,0 +1,410 @@
+/*
+ * 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 { sortBy, uniq } from 'lodash';
+import * as React from 'react';
+import { parseDate, toNotSoISOString } from 'sonar-ui-common/helpers/dates';
+import { isDefined } from 'sonar-ui-common/helpers/types';
+import { getApplicationLeak } from '../../../api/application';
+import { getMeasuresAndMeta } from '../../../api/measures';
+import { getProjectActivity } from '../../../api/projectActivity';
+import { getApplicationQualityGate, getQualityGateProjectStatus } from '../../../api/quality-gates';
+import { getTimeMachineData } from '../../../api/time-machine';
+import {
+ getActivityGraph,
+ getHistoryMetrics,
+ saveActivityGraph
+} from '../../../components/activity-graph/utils';
+import {
+ getBranchLikeDisplayName,
+ getBranchLikeQuery,
+ isSameBranchLike
+} from '../../../helpers/branch-like';
+import { enhanceConditionWithMeasure, enhanceMeasuresWithMetrics } from '../../../helpers/measures';
+import { getLeakPeriod } from '../../../helpers/periods';
+import {
+ extractStatusConditionsFromApplicationStatusChildProject,
+ extractStatusConditionsFromProjectStatus
+} from '../../../helpers/qualityGates';
+import { ApplicationPeriod } from '../../../types/application';
+import { BranchLike } from '../../../types/branch-like';
+import { MetricKey } from '../../../types/metrics';
+import { GraphType, MeasureHistory } from '../../../types/project-activity';
+import { QualityGateStatus, QualityGateStatusCondition } from '../../../types/quality-gates';
+import '../styles.css';
+import { HISTORY_METRICS_LIST, METRICS } from '../utils';
+import BranchOverviewRenderer from './BranchOverviewRenderer';
+
+interface Props {
+ branchLike?: BranchLike;
+ component: T.Component;
+}
+
+interface State {
+ analyses?: T.Analysis[];
+ appLeak?: ApplicationPeriod;
+ graph: GraphType;
+ loadingHistory?: boolean;
+ loadingStatus?: boolean;
+ measures?: T.MeasureEnhanced[];
+ measuresHistory?: MeasureHistory[];
+ metrics?: T.Metric[];
+ periods?: T.Period[];
+ qgStatuses?: QualityGateStatus[];
+}
+
+export const BRANCH_OVERVIEW_ACTIVITY_GRAPH = 'sonar_branch_overview.graph';
+
+// Get all history data over the past year.
+const FROM_DATE = toNotSoISOString(new Date().setFullYear(new Date().getFullYear() - 1));
+
+export default class BranchOverview extends React.PureComponent<Props, State> {
+ mounted = false;
+ state: State;
+
+ constructor(props: Props) {
+ super(props);
+
+ const { graph } = getActivityGraph(BRANCH_OVERVIEW_ACTIVITY_GRAPH, props.component.key);
+ this.state = { graph };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ this.loadStatus();
+ this.loadHistory();
+ }
+
+ componentDidUpdate(prevProps: Props) {
+ if (
+ this.props.component.key !== prevProps.component.key ||
+ !isSameBranchLike(this.props.branchLike, prevProps.branchLike)
+ ) {
+ this.loadStatus();
+ this.loadHistory();
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ loadStatus = () => {
+ if (this.props.component.qualifier === 'APP') {
+ this.loadApplicationStatus();
+ } else {
+ this.loadProjectStatus();
+ }
+ };
+
+ loadApplicationStatus = async () => {
+ const { branchLike, component } = this.props;
+ this.setState({ loadingStatus: true });
+
+ // Start by loading the application quality gate info, as well as the meta
+ // data for the application as a whole.
+ const appStatus = await getApplicationQualityGate({
+ application: component.key,
+ ...getBranchLikeQuery(branchLike)
+ });
+ const { measures: appMeasures, metrics, periods } = await this.loadMeasuresAndMeta(
+ component.key
+ );
+
+ // We also need to load the application leak periods separately.
+ getApplicationLeak(component.key, branchLike && getBranchLikeDisplayName(branchLike)).then(
+ leaks => {
+ if (this.mounted && leaks && leaks.length) {
+ const sortedLeaks = sortBy(leaks, leak => {
+ return new Date(leak.date);
+ });
+ this.setState({
+ appLeak: sortedLeaks[0]
+ });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ appLeak: undefined });
+ }
+ }
+ );
+
+ // We need to load the measures for each project in an application
+ // individually, in order to display all QG conditions correctly. Loading
+ // them at the parent application level will not get all the necessary
+ // information, unfortunately, as they are aggregated.
+ Promise.all(
+ appStatus.projects.map(project => {
+ return this.loadMeasuresAndMeta(
+ project.key,
+ // Only load metrics that apply to failing QG conditions; we don't
+ // need the others anyway.
+ project.conditions.filter(c => c.status !== 'OK').map(c => c.metric)
+ ).then(({ measures }) => ({
+ measures,
+ project
+ }));
+ })
+ ).then(
+ results => {
+ if (this.mounted) {
+ const qgStatuses = results.map(({ measures = [], project }) => {
+ const { key, name, status } = project;
+ const conditions = extractStatusConditionsFromApplicationStatusChildProject(project);
+ const failedConditions = this.getFailedConditions(conditions, measures);
+
+ return {
+ failedConditions,
+ key,
+ name,
+ status
+ };
+ });
+
+ this.setState({
+ loadingStatus: false,
+ measures: appMeasures,
+ metrics,
+ periods,
+ qgStatuses
+ });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loadingStatus: false, qgStatuses: undefined });
+ }
+ }
+ );
+ };
+
+ loadProjectStatus = async () => {
+ const {
+ branchLike,
+ component: { key, name }
+ } = this.props;
+ this.setState({ loadingStatus: true });
+
+ const projectStatus = await getQualityGateProjectStatus({
+ projectKey: key,
+ ...getBranchLikeQuery(branchLike)
+ });
+
+ // Get failing condition metric keys. We need measures for them as well to
+ // render them.
+ const metricKeys =
+ projectStatus.conditions !== undefined
+ ? uniq([...METRICS, ...projectStatus.conditions.map(c => c.metricKey)])
+ : METRICS;
+
+ this.loadMeasuresAndMeta(key, metricKeys).then(
+ ({ measures, metrics, periods }) => {
+ if (this.mounted && measures) {
+ const { ignoredConditions, status } = projectStatus;
+ const conditions = extractStatusConditionsFromProjectStatus(projectStatus);
+ const failedConditions = this.getFailedConditions(conditions, measures);
+
+ const qgStatus = {
+ ignoredConditions,
+ failedConditions,
+ key,
+ name,
+ status
+ };
+
+ this.setState({
+ loadingStatus: false,
+ measures,
+ metrics,
+ periods,
+ qgStatuses: [qgStatus]
+ });
+ } else if (this.mounted) {
+ this.setState({ loadingStatus: false, qgStatuses: undefined });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loadingStatus: false, qgStatuses: undefined });
+ }
+ }
+ );
+ };
+
+ loadMeasuresAndMeta = (componentKey: string, metricKeys: string[] = []) => {
+ const { branchLike } = this.props;
+
+ return getMeasuresAndMeta(componentKey, metricKeys.length > 0 ? metricKeys : METRICS, {
+ additionalFields: 'metrics,periods',
+ ...getBranchLikeQuery(branchLike)
+ }).then(({ component: { measures }, metrics, periods }) => {
+ return {
+ measures: enhanceMeasuresWithMetrics(measures || [], metrics || []),
+ metrics,
+ periods
+ };
+ });
+ };
+
+ loadHistory = () => {
+ this.setState({ loadingHistory: true });
+
+ return Promise.all([this.loadHistoryMeasures(), this.loadAnalyses()]).then(
+ this.doneLoadingHistory,
+ this.doneLoadingHistory
+ );
+ };
+
+ loadHistoryMeasures = () => {
+ const { branchLike, component } = this.props;
+ const { graph } = this.state;
+
+ const graphMetrics = getHistoryMetrics(graph, []);
+ const metrics = uniq([...HISTORY_METRICS_LIST, ...graphMetrics]);
+
+ return getTimeMachineData({
+ ...getBranchLikeQuery(branchLike),
+ from: FROM_DATE,
+ component: component.key,
+ metrics: metrics.join()
+ }).then(
+ ({ measures }) => {
+ if (this.mounted) {
+ this.setState({
+ measuresHistory: measures.map(measure => ({
+ metric: measure.metric,
+ history: measure.history.map(analysis => ({
+ date: parseDate(analysis.date),
+ value: analysis.value
+ }))
+ }))
+ });
+ }
+ },
+ () => {}
+ );
+ };
+
+ loadAnalyses = () => {
+ const { branchLike } = this.props;
+
+ return getProjectActivity({
+ ...getBranchLikeQuery(branchLike),
+ project: this.getTopLevelComponent(),
+ from: FROM_DATE
+ }).then(
+ ({ analyses }) => {
+ if (this.mounted) {
+ this.setState({
+ analyses
+ });
+ }
+ },
+ () => {}
+ );
+ };
+
+ getFailedConditions = (
+ conditions: QualityGateStatusCondition[],
+ measures: T.MeasureEnhanced[]
+ ) => {
+ return (
+ conditions
+ .filter(c => c.level !== 'OK')
+ // Enhance them with Metric information, which will be needed
+ // to render the conditions properly.
+ .map(c => enhanceConditionWithMeasure(c, measures))
+ // The enhancement will return undefined if it cannot find the
+ // appropriate measure. Make sure we filter them out.
+ .filter(isDefined)
+ );
+ };
+
+ getTopLevelComponent = () => {
+ const { component } = this.props;
+ let current = component.breadcrumbs.length - 1;
+ while (
+ current > 0 &&
+ !['TRK', 'VW', 'APP'].includes(component.breadcrumbs[current].qualifier)
+ ) {
+ current--;
+ }
+ return component.breadcrumbs[current].key;
+ };
+
+ doneLoadingHistory = () => {
+ if (this.mounted) {
+ this.setState({
+ loadingHistory: false
+ });
+ }
+ };
+
+ handleGraphChange = (graph: GraphType) => {
+ const { component } = this.props;
+ saveActivityGraph(BRANCH_OVERVIEW_ACTIVITY_GRAPH, component.key, graph);
+ this.setState({ graph, loadingHistory: true }, () => {
+ this.loadHistoryMeasures().then(this.doneLoadingHistory, this.doneLoadingHistory);
+ });
+ };
+
+ render() {
+ const { branchLike, component } = this.props;
+ const {
+ analyses,
+ appLeak,
+ graph,
+ loadingStatus,
+ loadingHistory,
+ measures,
+ measuresHistory,
+ metrics,
+ periods,
+ qgStatuses
+ } = this.state;
+
+ const leakPeriod = component.qualifier === 'APP' ? appLeak : getLeakPeriod(periods);
+
+ const projectIsEmpty =
+ loadingStatus === false &&
+ (measures === undefined ||
+ measures.find(measure =>
+ ([MetricKey.lines, MetricKey.new_lines] as string[]).includes(measure.metric.key)
+ ) === undefined);
+
+ return (
+ <BranchOverviewRenderer
+ analyses={analyses}
+ branchLike={branchLike}
+ component={component}
+ graph={graph}
+ leakPeriod={leakPeriod}
+ loadingHistory={loadingHistory}
+ loadingStatus={loadingStatus}
+ measures={measures}
+ measuresHistory={measuresHistory}
+ metrics={metrics}
+ onGraphChange={this.handleGraphChange}
+ projectIsEmpty={projectIsEmpty}
+ qgStatuses={qgStatuses}
+ />
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx
new file mode 100644
index 00000000000..7db0fe4158e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx
@@ -0,0 +1,115 @@
+/*
+ * 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 { parseDate } from 'sonar-ui-common/helpers/dates';
+import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
+import { ApplicationPeriod } from '../../../types/application';
+import { BranchLike } from '../../../types/branch-like';
+import { GraphType, MeasureHistory } from '../../../types/project-activity';
+import { QualityGateStatus } from '../../../types/quality-gates';
+import ActivityPanel from './ActivityPanel';
+import { MeasuresPanel } from './MeasuresPanel';
+import NoCodeWarning from './NoCodeWarning';
+import QualityGatePanel from './QualityGatePanel';
+
+export interface BranchOverviewRendererProps {
+ analyses?: T.Analysis[];
+ branchLike?: BranchLike;
+ component: T.Component;
+ graph?: GraphType;
+ leakPeriod?: T.Period | ApplicationPeriod;
+ loadingHistory?: boolean;
+ loadingStatus?: boolean;
+ measures?: T.MeasureEnhanced[];
+ measuresHistory?: MeasureHistory[];
+ metrics?: T.Metric[];
+ onGraphChange: (graph: GraphType) => void;
+ projectIsEmpty?: boolean;
+ qgStatuses?: QualityGateStatus[];
+}
+
+export function BranchOverviewRenderer(props: BranchOverviewRendererProps) {
+ const {
+ analyses,
+ branchLike,
+ component,
+ graph,
+ leakPeriod,
+ loadingHistory,
+ loadingStatus,
+ measures,
+ measuresHistory = [],
+ metrics = [],
+ onGraphChange,
+ projectIsEmpty,
+ qgStatuses
+ } = props;
+
+ return (
+ <div className="page page-limited">
+ <div className="overview">
+ <A11ySkipTarget anchor="overview_main" />
+
+ {projectIsEmpty ? (
+ <NoCodeWarning branchLike={branchLike} component={component} measures={measures} />
+ ) : (
+ <>
+ <div className="display-flex-row">
+ <div className="width-25 big-spacer-right">
+ <QualityGatePanel
+ branchLike={branchLike}
+ component={component}
+ loading={loadingStatus}
+ qgStatuses={qgStatuses}
+ />
+ </div>
+
+ <div className="flex-1">
+ <div className="display-flex-column">
+ <MeasuresPanel
+ branchLike={branchLike}
+ component={component}
+ leakPeriod={leakPeriod}
+ loading={loadingStatus}
+ measures={measures}
+ />
+
+ <ActivityPanel
+ analyses={analyses}
+ branchLike={branchLike}
+ component={component}
+ graph={graph}
+ leakPeriodDate={leakPeriod && parseDate(leakPeriod.date)}
+ loading={loadingHistory}
+ measuresHistory={measuresHistory}
+ metrics={metrics}
+ onGraphChange={onGraphChange}
+ />
+ </div>
+ </div>
+ </div>
+ </>
+ )}
+ </div>
+ </div>
+ );
+}
+
+export default React.memo(BranchOverviewRenderer);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/DebtValue.tsx b/server/sonar-web/src/main/js/apps/overview/branches/DebtValue.tsx
new file mode 100644
index 00000000000..00c01a5057f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/DebtValue.tsx
@@ -0,0 +1,66 @@
+/*
+ * 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 { getLocalizedMetricName, 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, localizeMetric } from '../../../helpers/measures';
+import { BranchLike } from '../../../types/branch-like';
+import { MetricKey } from '../../../types/metrics';
+
+export interface DebtValueProps {
+ branchLike?: BranchLike;
+ component: T.Component;
+ measures: T.MeasureEnhanced[];
+ useDiffMetric?: boolean;
+}
+
+export function DebtValue(props: DebtValueProps) {
+ const { branchLike, component, measures, useDiffMetric = false } = props;
+ const metric = useDiffMetric ? MetricKey.new_technical_debt : MetricKey.sqale_index;
+ const measure = findMeasure(measures, metric);
+
+ let value;
+ if (measure) {
+ value = useDiffMetric ? getLeakValue(measure) : measure.value;
+ }
+
+ return (
+ <>
+ {value === undefined ? (
+ <span aria-label={translate('no_data')} className="overview-measures-empty-value" />
+ ) : (
+ <DrilldownLink
+ branchLike={branchLike}
+ className="overview-measures-value text-light"
+ component={component.key}
+ metric={metric}>
+ {formatMeasure(value, 'WORK_DUR')}
+ </DrilldownLink>
+ )}
+ <span className="big-spacer-left">
+ {measure ? getLocalizedMetricName(measure.metric, true) : localizeMetric(metric)}
+ </span>
+ </>
+ );
+}
+
+export default React.memo(DebtValue);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/DrilldownMeasureValue.tsx b/server/sonar-web/src/main/js/apps/overview/branches/DrilldownMeasureValue.tsx
new file mode 100644
index 00000000000..4db04aef6c0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/DrilldownMeasureValue.tsx
@@ -0,0 +1,64 @@
+/*
+ * 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 { getLocalizedMetricName } from 'sonar-ui-common/helpers/l10n';
+import { formatMeasure } from 'sonar-ui-common/helpers/measures';
+import DrilldownLink from '../../../components/shared/DrilldownLink';
+import { findMeasure } from '../../../helpers/measures';
+import { BranchLike } from '../../../types/branch-like';
+import { MetricKey } from '../../../types/metrics';
+
+export interface DrilldownMeasureValueProps {
+ branchLike?: BranchLike;
+ component: T.Component;
+ measures: T.MeasureEnhanced[];
+ metric: MetricKey;
+}
+
+export function DrilldownMeasureValue(props: DrilldownMeasureValueProps) {
+ const { branchLike, component, measures, metric } = props;
+ const measure = findMeasure(measures, metric);
+
+ let content;
+ if (!measure) {
+ content = <span className="overview-measures-value text-light">-</span>;
+ } else {
+ content = (
+ <span>
+ <DrilldownLink
+ branchLike={branchLike}
+ className="overview-measures-value text-light"
+ component={component.key}
+ metric={metric}>
+ {formatMeasure(measure.value, 'SHORT_INT')}
+ </DrilldownLink>
+ </span>
+ );
+ }
+
+ return (
+ <div className="display-flex-column display-flex-center">
+ {content}
+ <span className="spacer-top">{getLocalizedMetricName({ key: metric })}</span>
+ </div>
+ );
+}
+
+export default React.memo(DrilldownMeasureValue);
diff --git a/server/sonar-web/src/main/js/apps/overview/events/Event.tsx b/server/sonar-web/src/main/js/apps/overview/branches/Event.tsx
index bdd90941df2..375380d086b 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/Event.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/Event.tsx
@@ -28,7 +28,7 @@ interface Props {
event: T.AnalysisEvent;
}
-export default function Event({ event }: Props) {
+export function Event({ event }: Props) {
if (event.category === 'VERSION') {
return (
<span
@@ -76,3 +76,5 @@ export default function Event({ event }: Props) {
</div>
);
}
+
+export default React.memo(Event);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx
new file mode 100644
index 00000000000..dfd6f067690
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx
@@ -0,0 +1,265 @@
+/*
+ * 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 { Link } from 'react-router';
+import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs';
+import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { isDiffMetric } from 'sonar-ui-common/helpers/measures';
+import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
+import { rawSizes } from '../../../app/theme';
+import { findMeasure } from '../../../helpers/measures';
+import { ApplicationPeriod } from '../../../types/application';
+import { BranchLike } from '../../../types/branch-like';
+import { ComponentQualifier } from '../../../types/component';
+import { MetricKey } from '../../../types/metrics';
+import IssueLabel from '../components/IssueLabel';
+import IssueRating from '../components/IssueRating';
+import MeasurementLabel from '../components/MeasurementLabel';
+import { IssueType, MeasurementType } from '../utils';
+import DebtValue from './DebtValue';
+import { DrilldownMeasureValue } from './DrilldownMeasureValue';
+import { LeakPeriodInfo } from './LeakPeriodInfo';
+
+export interface MeasuresPanelProps {
+ branchLike?: BranchLike;
+ component: T.Component;
+ leakPeriod?: T.Period | ApplicationPeriod;
+ loading?: boolean;
+ measures?: T.MeasureEnhanced[];
+}
+
+export enum MeasuresPanelTabs {
+ New,
+ Overall
+}
+
+export function MeasuresPanel(props: MeasuresPanelProps) {
+ const { branchLike, component, loading, leakPeriod, measures = [] } = props;
+
+ const hasDiffMeasures = measures.some(m => isDiffMetric(m.metric.key));
+ const isApp = component.qualifier === ComponentQualifier.Application;
+
+ const [tab, selectTab] = React.useState(MeasuresPanelTabs.New);
+
+ React.useEffect(() => {
+ // Open Overall tab by default if there are no new measures.
+ if (loading === false && !hasDiffMeasures && tab === MeasuresPanelTabs.New) {
+ selectTab(MeasuresPanelTabs.Overall);
+ }
+ // In this case, we explicitly do NOT want to mark tab as a dependency, as
+ // it would prevent the user from selecting it, even if it's empty.
+ /* eslint-disable-next-line react-hooks/exhaustive-deps */
+ }, [loading, hasDiffMeasures]);
+
+ const tabs = [
+ {
+ key: MeasuresPanelTabs.New,
+ label: (
+ <div className="text-left overview-measures-tab">
+ <span className="text-bold">{translate('overview.new_code')}</span>
+ {leakPeriod && <LeakPeriodInfo leakPeriod={leakPeriod} />}
+ </div>
+ )
+ },
+ {
+ key: MeasuresPanelTabs.Overall,
+ label: (
+ <div className="text-left overview-measures-tab">
+ <span className="text-bold" style={{ position: 'absolute', top: 2 * rawSizes.grid }}>
+ {translate('overview.overall_code')}
+ </span>
+ </div>
+ )
+ }
+ ];
+
+ return (
+ <div className="overview-panel" data-test="overview__measures-panel">
+ <h2 className="overview-panel-title">{translate('overview.measures')}</h2>
+
+ {loading ? (
+ <div className="overview-panel-content overview-panel-big-padded">
+ <DeferredSpinner loading={loading} />
+ </div>
+ ) : (
+ <>
+ <BoxedTabs onSelect={selectTab} selected={tab} tabs={tabs} />
+
+ <div className="overview-panel-content flex-1 bordered">
+ {!hasDiffMeasures && tab === MeasuresPanelTabs.New ? (
+ <div
+ className="display-flex-center display-flex-justify-center"
+ style={{ height: 500 }}>
+ <img
+ alt="" /* Make screen readers ignore this image; it's purely eye candy. */
+ className="spacer-right"
+ height={52}
+ src={`${getBaseUrl()}/images/source-code.svg`}
+ />
+ <div className="big-spacer-left text-muted" style={{ maxWidth: 500 }}>
+ <p className="spacer-bottom big-spacer-top big">
+ {translate('overview.measures.empty_explanation')}
+ </p>
+ <p>
+ <FormattedMessage
+ defaultMessage={translate('overview.measures.empty_link')}
+ id="overview.measures.empty_link"
+ values={{
+ learn_more_link: (
+ <Link to="/documentation/user-guide/clean-as-you-code/">
+ {translate('learn_more')}
+ </Link>
+ )
+ }}
+ />
+ </p>
+ </div>
+ </div>
+ ) : (
+ <>
+ {[IssueType.Bug, IssueType.Vulnerability, IssueType.CodeSmell].map(
+ (type: IssueType) => (
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test={`overview__measures-${type.toString().toLowerCase()}`}
+ key={type}>
+ {type === IssueType.CodeSmell ? (
+ <>
+ <div className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left">
+ <DebtValue
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+ </div>
+ <div className="flex-1 small display-flex-center">
+ <IssueLabel
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ type={type}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+ </div>
+ </>
+ ) : (
+ <>
+ <div className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left">
+ <IssueLabel
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ type={type}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+ </div>
+ {type === 'VULNERABILITY' && (
+ <div className="flex-1 small display-flex-center">
+ <IssueLabel
+ branchLike={branchLike}
+ component={component}
+ docTooltip={import(
+ /* webpackMode: "eager" */ 'Docs/tooltips/metrics/security-hotspots.md'
+ )}
+ measures={measures}
+ type={IssueType.SecurityHotspot}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+ </div>
+ )}
+ </>
+ )}
+ {(!isApp || tab === MeasuresPanelTabs.Overall) && (
+ <div className="overview-panel-big-padded overview-measures-aside display-flex-center">
+ <IssueRating
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ type={type}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+ </div>
+ )}
+ </div>
+ )
+ )}
+
+ <div className="display-flex-row overview-measures-row">
+ {(findMeasure(measures, MetricKey.coverage) ||
+ findMeasure(measures, MetricKey.new_coverage)) && (
+ <div
+ className="overview-panel-huge-padded flex-1 bordered-right display-flex-center"
+ data-test="overview__measures-coverage">
+ <MeasurementLabel
+ branchLike={branchLike}
+ centered={tab === MeasuresPanelTabs.New}
+ component={component}
+ measures={measures}
+ type={MeasurementType.Coverage}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+
+ {tab === MeasuresPanelTabs.Overall && (
+ <div className="huge-spacer-left">
+ <DrilldownMeasureValue
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ metric={MetricKey.tests}
+ />
+ </div>
+ )}
+ </div>
+ )}
+ <div className="overview-panel-huge-padded flex-1 display-flex-center">
+ <MeasurementLabel
+ branchLike={branchLike}
+ centered={tab === MeasuresPanelTabs.New}
+ component={component}
+ measures={measures}
+ type={MeasurementType.Duplication}
+ useDiffMetric={tab === MeasuresPanelTabs.New}
+ />
+
+ {tab === MeasuresPanelTabs.Overall && (
+ <div className="huge-spacer-left">
+ <DrilldownMeasureValue
+ branchLike={branchLike}
+ component={component}
+ measures={measures}
+ metric={MetricKey.duplicated_blocks}
+ />
+ </div>
+ )}
+ </div>
+ </div>
+ </>
+ )}
+ </div>
+ </>
+ )}
+ </div>
+ );
+}
+
+export default React.memo(MeasuresPanel);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/NoCodeWarning.tsx b/server/sonar-web/src/main/js/apps/overview/branches/NoCodeWarning.tsx
new file mode 100644
index 00000000000..1a81132e2a1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/NoCodeWarning.tsx
@@ -0,0 +1,90 @@
+/*
+ * 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 { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import { getBranchLikeDisplayName, isMainBranch } from '../../../helpers/branch-like';
+import { BranchLike } from '../../../types/branch-like';
+
+interface Props {
+ branchLike?: BranchLike;
+ component: T.Component;
+ measures?: T.MeasureEnhanced[];
+}
+
+export function NoCodeWarning({ branchLike, component, measures }: Props) {
+ const isApp = component.qualifier === 'APP';
+
+ /* eslint-disable no-lonely-if */
+ // - Is App
+ // - No measures, OR measures, but no projects => empty
+ // - Else => no lines of code
+ // - Else
+ // - No measures => empty
+ // - Main branch?
+ // - LLB?
+ // - No branch info?
+ // - Measures, but no ncloc (checked in isEmpty()) => no lines of code
+ // - Main branch?
+ // - LLB?
+ // - No branch info?
+ let title = translate('overview.project.no_lines_of_code');
+ if (isApp) {
+ if (
+ measures === undefined ||
+ measures.find(measure => measure.metric.key === 'projects') === undefined
+ ) {
+ title = translate('portfolio.app.empty');
+ } else {
+ title = translate('portfolio.app.no_lines_of_code');
+ }
+ } else {
+ if (measures === undefined || measures.length === 0) {
+ if (isMainBranch(branchLike)) {
+ title = translate('overview.project.main_branch_empty');
+ } else if (branchLike !== undefined) {
+ title = translateWithParameters(
+ 'overview.project.branch_X_empty',
+ getBranchLikeDisplayName(branchLike)
+ );
+ } else {
+ title = translate('overview.project.empty');
+ }
+ } else {
+ if (isMainBranch(branchLike)) {
+ title = translate('overview.project.main_branch_no_lines_of_code');
+ } else if (branchLike !== undefined) {
+ title = translateWithParameters(
+ 'overview.project.branch_X_no_lines_of_code',
+ getBranchLikeDisplayName(branchLike)
+ );
+ }
+ }
+ }
+ /* eslint-enable no-lonely-if */
+
+ return (
+ <Alert display="banner" variant="warning">
+ {title}
+ </Alert>
+ );
+}
+
+export default React.memo(NoCodeWarning);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
new file mode 100644
index 00000000000..fbea9c5716d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx
@@ -0,0 +1,125 @@
+/*
+ * 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 HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import DocTooltip from '../../../components/docs/DocTooltip';
+import { BranchLike } from '../../../types/branch-like';
+import { QualityGateStatus } from '../../../types/quality-gates';
+import QualityGatePanelSection from './QualityGatePanelSection';
+
+export interface QualityGatePanelProps {
+ branchLike?: BranchLike;
+ component: Pick<T.Component, 'key' | 'qualifier'>;
+ loading?: boolean;
+ qgStatuses?: QualityGateStatus[];
+}
+
+export function QualityGatePanel(props: QualityGatePanelProps) {
+ const { branchLike, component, loading, qgStatuses = [] } = props;
+
+ if (qgStatuses === undefined) {
+ return null;
+ }
+
+ const overallLevel = qgStatuses.map(s => s.status).includes('ERROR') ? 'ERROR' : 'OK';
+ const success = overallLevel === 'OK';
+
+ const overallFailedConditionsCount = qgStatuses.reduce(
+ (acc, qgStatus) => acc + qgStatus.failedConditions.length,
+ 0
+ );
+
+ const showIgnoredConditionWarning =
+ component.qualifier === 'TRK' &&
+ qgStatuses !== undefined &&
+ qgStatuses.some(p => Boolean(p.ignoredConditions));
+
+ return (
+ <div className="overview-panel" data-test="overview__quality-gate-panel">
+ <h2 className="overview-panel-title display-inline-flex-center">
+ {translate('overview.quality_gate')}{' '}
+ <DocTooltip
+ className="little-spacer-left"
+ doc={import(
+ /* webpackMode: "eager" */ 'Docs/tooltips/quality-gates/project-homepage-quality-gate.md'
+ )}
+ />
+ </h2>
+
+ {showIgnoredConditionWarning && (
+ <Alert className="big-spacer-bottom" display="inline" variant="info">
+ <span className="text-middle">
+ {translate('overview.quality_gate.ignored_conditions')}
+ </span>
+ <HelpTooltip
+ className="spacer-left"
+ overlay={translate('overview.quality_gate.ignored_conditions.tooltip')}
+ />
+ </Alert>
+ )}
+
+ <div className="overview-panel-content">
+ {loading ? (
+ <div className="overview-panel-big-padded">
+ <DeferredSpinner loading={loading} />
+ </div>
+ ) : (
+ <>
+ <div
+ className={classNames('overview-quality-gate-badge-large', {
+ failed: !success,
+ success
+ })}>
+ <h3 className="big-spacer-bottom huge">{translate('metric.level', overallLevel)}</h3>
+
+ <span className="small">
+ {overallFailedConditionsCount > 0
+ ? translateWithParameters(
+ 'overview.X_conditions_failed',
+ overallFailedConditionsCount
+ )
+ : translate('overview.quality_gate_all_conditions_passed')}
+ </span>
+ </div>
+
+ {overallFailedConditionsCount > 0 && (
+ <div data-test="overview__quality-gate-conditions">
+ {qgStatuses.map(qgStatus => (
+ <QualityGatePanelSection
+ branchLike={branchLike}
+ component={component}
+ key={qgStatus.key}
+ qgStatus={qgStatus}
+ />
+ ))}
+ </div>
+ )}
+ </>
+ )}
+ </div>
+ </div>
+ );
+}
+
+export default React.memo(QualityGatePanel);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
new file mode 100644
index 00000000000..a304c5dccd9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanelSection.tsx
@@ -0,0 +1,79 @@
+/*
+ * 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 { translate } from 'sonar-ui-common/helpers/l10n';
+import { isDiffMetric } from '../../../helpers/measures';
+import { BranchLike } from '../../../types/branch-like';
+import { QualityGateStatus } from '../../../types/quality-gates';
+import { QualityGateConditions } from '../components/QualityGateConditions';
+
+export interface QualityGatePanelSectionProps {
+ branchLike?: BranchLike;
+ component: Pick<T.Component, 'key' | 'qualifier'>;
+ qgStatus: QualityGateStatus;
+}
+
+export function QualityGatePanelSection(props: QualityGatePanelSectionProps) {
+ const { branchLike, component, qgStatus } = props;
+ const newCodeFailedConditions = qgStatus.failedConditions.filter(c => isDiffMetric(c.metric));
+ const overallFailedConditions = qgStatus.failedConditions.filter(c => !isDiffMetric(c.metric));
+
+ if (newCodeFailedConditions.length === 0 && overallFailedConditions.length === 0) {
+ return null;
+ }
+
+ const showName = component.qualifier === 'APP';
+
+ return (
+ <div className="overview-quality-gate-conditions">
+ {showName && (
+ <h3 className="overview-quality-gate-conditions-project-name">{qgStatus.name}</h3>
+ )}
+
+ {newCodeFailedConditions.length > 0 && (
+ <>
+ <h4 className="overview-quality-gate-conditions-section-title">
+ {translate('quality_gates.conditions.new_code')}
+ </h4>
+ <QualityGateConditions
+ branchLike={branchLike}
+ component={qgStatus}
+ failedConditions={newCodeFailedConditions}
+ />
+ </>
+ )}
+
+ {overallFailedConditions.length > 0 && (
+ <>
+ <h4 className="overview-quality-gate-conditions-section-title">
+ {translate('quality_gates.conditions.overall_code')}
+ </h4>
+ <QualityGateConditions
+ branchLike={branchLike}
+ component={qgStatus}
+ failedConditions={overallFailedConditions}
+ />
+ </>
+ )}
+ </div>
+ );
+}
+
+export default React.memo(QualityGatePanelSection);
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-test.tsx
new file mode 100644
index 00000000000..21856507dd6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/ActivityPanel-test.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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 { parseDate } from 'sonar-ui-common/helpers/dates';
+import GraphsHistory from '../../../../components/activity-graph/GraphsHistory';
+import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import {
+ mockAnalysis,
+ mockAnalysisEvent,
+ mockComponent,
+ mockMeasure,
+ mockMetric
+} from '../../../../helpers/testMocks';
+import { GraphType } from '../../../../types/project-activity';
+import { ActivityPanel, ActivityPanelProps } from '../ActivityPanel';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ loading: true, analyses: undefined })).toMatchSnapshot();
+});
+
+it('should correctly pass the leak period start date', () => {
+ // Leak period start is more recent than the oldest historic measure.
+ let { leakPeriodDate } = shallowRender({
+ leakPeriodDate: parseDate('2017-08-27T16:33:50+0200')
+ })
+ .find(GraphsHistory)
+ .props();
+
+ if (leakPeriodDate) {
+ expect(leakPeriodDate.getTime()).toBe(1503844430000); /* 2017-08-27T16:33:50+0200 */
+ } else {
+ fail('leakPeriodDate should have been set');
+ }
+
+ // Leak period start is older than the oldest historic measure.
+ ({ leakPeriodDate } = shallowRender({ leakPeriodDate: parseDate('2015-08-27T16:33:50+0200') })
+ .find(GraphsHistory)
+ .props());
+
+ if (leakPeriodDate) {
+ expect(leakPeriodDate.getTime()).toBe(1477578830000); /* 2016-10-27T16:33:50+0200 */
+ } else {
+ fail('leakPeriodDate should have been set');
+ }
+});
+
+function shallowRender(props: Partial<ActivityPanelProps> = {}) {
+ return shallow(
+ <ActivityPanel
+ analyses={[mockAnalysis({ events: [mockAnalysisEvent()] }), mockAnalysis()]}
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ graph={GraphType.issues}
+ loading={false}
+ measuresHistory={[mockMeasure()].map(m => ({
+ ...m,
+ history: [{ date: parseDate('2016-10-27T16:33:50+0200'), value: '20' }]
+ }))}
+ metrics={[mockMetric({ key: 'bugs' })]}
+ onGraphChange={jest.fn()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/Analysis-test.tsx
index 52ce01f3aa4..d530f2fff8d 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/AnalysesList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/Analysis-test.tsx
@@ -19,21 +19,25 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
-import AnalysesList from '../AnalysesList';
+import { mockAnalysis } from '../../../../helpers/testMocks';
+import { Analysis, AnalysisProps } from '../Analysis';
-it('should render show more link', () => {
- const branchLike = mockMainBranch();
- const component = {
- breadcrumbs: [{ key: 'foo', name: 'foo', qualifier: 'TRK' }],
- key: 'foo',
- name: 'foo',
- organization: 'org',
- qualifier: 'TRK'
- };
- const wrapper = shallow(
- <AnalysesList branchLike={branchLike} component={component} metrics={{}} qualifier="TRK" />
- );
- wrapper.setState({ loading: false });
- expect(wrapper.find('Link')).toMatchSnapshot();
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ qualifier: 'APP' })).toMatchSnapshot();
});
+
+function shallowRender(props: Partial<AnalysisProps> = {}) {
+ return shallow(
+ <Analysis
+ analysis={mockAnalysis({
+ events: [
+ { key: '1', category: 'OTHER', name: 'test' },
+ { key: '2', category: 'VERSION', name: '6.5-SNAPSHOT' }
+ ]
+ })}
+ qualifier="TRK"
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-test.tsx
new file mode 100644
index 00000000000..99244d2f41c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-test.tsx
@@ -0,0 +1,349 @@
+/*
+ * 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.
+ */
+/* eslint-disable sonarjs/no-duplicate-string */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import { isDiffMetric } from 'sonar-ui-common/helpers/measures';
+import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import { getApplicationLeak } from '../../../../api/application';
+import { getMeasuresAndMeta } from '../../../../api/measures';
+import { getProjectActivity } from '../../../../api/projectActivity';
+import {
+ getApplicationQualityGate,
+ getQualityGateProjectStatus
+} from '../../../../api/quality-gates';
+import { getTimeMachineData } from '../../../../api/time-machine';
+import { getActivityGraph, saveActivityGraph } from '../../../../components/activity-graph/utils';
+import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import { mockComponent } from '../../../../helpers/testMocks';
+import { MetricKey } from '../../../../types/metrics';
+import { GraphType } from '../../../../types/project-activity';
+import BranchOverview, { BRANCH_OVERVIEW_ACTIVITY_GRAPH } from '../BranchOverview';
+import BranchOverviewRenderer from '../BranchOverviewRenderer';
+
+jest.mock('sonar-ui-common/helpers/dates', () => ({
+ parseDate: jest.fn(date => `PARSED:${date}`),
+ toNotSoISOString: jest.fn(date => date)
+}));
+
+jest.mock('../../../../api/measures', () => {
+ const { mockMeasure, mockMetric } = require.requireActual('../../../../helpers/testMocks');
+ return {
+ getMeasuresAndMeta: jest.fn((_, metricKeys: string[]) => {
+ const metrics: T.Metric[] = [];
+ const measures: T.Measure[] = [];
+ metricKeys.forEach(key => {
+ if (key === 'unknown_metric') {
+ return;
+ }
+
+ let type;
+ if (/(coverage|duplication)$/.test(key)) {
+ type = 'PERCENT';
+ } else if (/_rating$/.test(key)) {
+ type = 'RATING';
+ } else {
+ type = 'INT';
+ }
+ metrics.push(mockMetric({ key, id: key, name: key, type }));
+ measures.push(
+ mockMeasure({
+ metric: key,
+ ...(isDiffMetric(key) ? { leak: '1' } : { periods: undefined })
+ })
+ );
+ });
+ return Promise.resolve({
+ component: {
+ measures,
+ name: 'foo'
+ },
+ metrics
+ });
+ })
+ };
+});
+
+jest.mock('../../../../api/quality-gates', () => {
+ const { mockQualityGateProjectStatus, mockQualityGateApplicationStatus } = require.requireActual(
+ '../../../../helpers/mocks/quality-gates'
+ );
+ const { MetricKey } = require.requireActual('../../../../types/metrics');
+ return {
+ getQualityGateProjectStatus: jest.fn().mockResolvedValue(
+ mockQualityGateProjectStatus({
+ status: 'ERROR',
+ conditions: [
+ {
+ actualValue: '2',
+ comparator: 'GT',
+ errorThreshold: '1.0',
+ metricKey: MetricKey.new_bugs,
+ periodIndex: 1,
+ status: 'ERROR'
+ },
+ {
+ actualValue: '5',
+ comparator: 'GT',
+ errorThreshold: '2.0',
+ metricKey: MetricKey.bugs,
+ periodIndex: 0,
+ status: 'ERROR'
+ },
+ {
+ actualValue: '2',
+ comparator: 'GT',
+ errorThreshold: '1.0',
+ metricKey: 'unknown_metric',
+ periodIndex: 0,
+ status: 'ERROR'
+ }
+ ]
+ })
+ ),
+ getApplicationQualityGate: jest.fn().mockResolvedValue(mockQualityGateApplicationStatus())
+ };
+});
+
+jest.mock('../../../../api/time-machine', () => {
+ const { MetricKey } = require.requireActual('../../../../types/metrics');
+ return {
+ getTimeMachineData: jest.fn().mockResolvedValue({
+ measures: [
+ { metric: MetricKey.bugs, history: [{ date: '2019-01-05', value: '2.0' }] },
+ { metric: MetricKey.vulnerabilities, history: [{ date: '2019-01-05', value: '0' }] },
+ { metric: MetricKey.sqale_index, history: [{ date: '2019-01-01', value: '1.0' }] },
+ {
+ metric: MetricKey.duplicated_lines_density,
+ history: [{ date: '2019-01-02', value: '1.0' }]
+ },
+ { metric: MetricKey.ncloc, history: [{ date: '2019-01-03', value: '10000' }] },
+ { metric: MetricKey.coverage, history: [{ date: '2019-01-04', value: '95.5' }] }
+ ]
+ })
+ };
+});
+
+jest.mock('../../../../api/projectActivity', () => {
+ const { mockAnalysis } = require.requireActual('../../../../helpers/testMocks');
+ return {
+ getProjectActivity: jest.fn().mockResolvedValue({
+ analyses: [mockAnalysis(), mockAnalysis(), mockAnalysis(), mockAnalysis(), mockAnalysis()]
+ })
+ };
+});
+
+jest.mock('../../../../api/application', () => ({
+ getApplicationLeak: jest.fn().mockResolvedValue([
+ {
+ date: '2017-01-05',
+ project: 'foo',
+ projectName: 'Foo'
+ }
+ ])
+}));
+
+jest.mock('../../../../components/activity-graph/utils', () => {
+ const { MetricKey } = require.requireActual('../../../../types/metrics');
+ const { GraphType } = require.requireActual('../../../../types/project-activity');
+ return {
+ getActivityGraph: jest.fn(() => ({ graph: GraphType.coverage })),
+ saveActivityGraph: jest.fn(),
+ getHistoryMetrics: jest.fn(() => [MetricKey.lines_to_cover, MetricKey.uncovered_lines])
+ };
+});
+
+beforeEach(jest.clearAllMocks);
+
+describe('project overview', () => {
+ it('should render correctly', async () => {
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("should correctly load a project's status", async () => {
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(getQualityGateProjectStatus).toBeCalled();
+ expect(getMeasuresAndMeta).toBeCalled();
+
+ // Check the conditions got correctly enhanced with measure meta data.
+ const { qgStatuses } = wrapper.state();
+ expect(qgStatuses).toHaveLength(1);
+ const [qgStatus] = qgStatuses!;
+
+ expect(qgStatus).toEqual(
+ expect.objectContaining({
+ name: 'Foo',
+ key: 'foo',
+ status: 'ERROR'
+ })
+ );
+
+ const { failedConditions } = qgStatus;
+ expect(failedConditions).toHaveLength(2);
+ expect(failedConditions[0]).toMatchObject({
+ actual: '2',
+ level: 'ERROR',
+ metric: MetricKey.new_bugs,
+ measure: expect.objectContaining({
+ metric: expect.objectContaining({ key: MetricKey.new_bugs })
+ })
+ });
+ expect(failedConditions[1]).toMatchObject({
+ actual: '5',
+ level: 'ERROR',
+ metric: MetricKey.bugs,
+ measure: expect.objectContaining({
+ metric: expect.objectContaining({ key: MetricKey.bugs })
+ })
+ });
+ });
+
+ it('should correctly flag a project as empty', async () => {
+ (getMeasuresAndMeta as jest.Mock).mockResolvedValueOnce({ component: {} });
+
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(wrapper.find(BranchOverviewRenderer).props().projectIsEmpty).toBe(true);
+ });
+});
+
+describe('application overview', () => {
+ const component = mockComponent({
+ breadcrumbs: [mockComponent({ key: 'foo', qualifier: 'APP' })],
+ qualifier: 'APP'
+ });
+
+ it('should render correctly', async () => {
+ const wrapper = shallowRender({ component });
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("should correctly load an application's status", async () => {
+ const wrapper = shallowRender({ component });
+ await waitAndUpdate(wrapper);
+ expect(getApplicationQualityGate).toBeCalled();
+ expect(getApplicationLeak).toBeCalled();
+ expect(getMeasuresAndMeta).toBeCalled();
+
+ // Check the conditions got correctly enhanced with measure meta data.
+ const { qgStatuses } = wrapper.state();
+ expect(qgStatuses).toHaveLength(2);
+ const [qgStatus1, qgStatus2] = qgStatuses!;
+
+ expect(qgStatus1).toEqual(
+ expect.objectContaining({
+ name: 'Foo',
+ key: 'foo',
+ status: 'ERROR'
+ })
+ );
+
+ const { failedConditions: failedConditions1 } = qgStatus1;
+ expect(failedConditions1).toHaveLength(2);
+ expect(failedConditions1[0]).toMatchObject({
+ actual: '10',
+ level: 'ERROR',
+ metric: MetricKey.coverage,
+ measure: expect.objectContaining({
+ metric: expect.objectContaining({ key: MetricKey.coverage })
+ })
+ });
+ expect(failedConditions1[1]).toMatchObject({
+ actual: '5',
+ level: 'ERROR',
+ metric: MetricKey.new_bugs,
+ measure: expect.objectContaining({
+ metric: expect.objectContaining({ key: MetricKey.new_bugs })
+ })
+ });
+
+ expect(qgStatus1).toEqual(
+ expect.objectContaining({
+ name: 'Foo',
+ key: 'foo',
+ status: 'ERROR'
+ })
+ );
+
+ const { failedConditions: failedConditions2 } = qgStatus2;
+ expect(failedConditions2).toHaveLength(1);
+ expect(failedConditions2[0]).toMatchObject({
+ actual: '15',
+ level: 'ERROR',
+ metric: MetricKey.new_bugs,
+ measure: expect.objectContaining({
+ metric: expect.objectContaining({ key: MetricKey.new_bugs })
+ })
+ });
+ });
+
+ it('should correctly flag an application as empty', async () => {
+ (getMeasuresAndMeta as jest.Mock).mockResolvedValueOnce({ component: {} });
+
+ const wrapper = shallowRender({ component });
+ await waitAndUpdate(wrapper);
+
+ expect(wrapper.find(BranchOverviewRenderer).props().projectIsEmpty).toBe(true);
+ });
+});
+
+it("should correctly load a component's history", async () => {
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(getProjectActivity).toBeCalled();
+ expect(getTimeMachineData).toBeCalled();
+
+ const { measuresHistory } = wrapper.state();
+ expect(measuresHistory).toHaveLength(6);
+ expect(measuresHistory![0]).toEqual(
+ expect.objectContaining({
+ metric: MetricKey.bugs,
+ history: [{ date: 'PARSED:2019-01-05', value: '2.0' }]
+ })
+ );
+});
+
+it('should correctly handle graph type storage', () => {
+ const wrapper = shallowRender();
+ expect(getActivityGraph).toBeCalledWith(BRANCH_OVERVIEW_ACTIVITY_GRAPH, 'foo');
+ expect(wrapper.state().graph).toBe(GraphType.coverage);
+
+ wrapper.instance().handleGraphChange(GraphType.issues);
+ expect(saveActivityGraph).toBeCalledWith(BRANCH_OVERVIEW_ACTIVITY_GRAPH, 'foo', GraphType.issues);
+ expect(wrapper.state().graph).toBe(GraphType.issues);
+});
+
+function shallowRender(props: Partial<BranchOverview['props']> = {}) {
+ return shallow<BranchOverview>(
+ <BranchOverview
+ branchLike={mockMainBranch()}
+ component={mockComponent({
+ breadcrumbs: [mockComponent({ key: 'foo' })],
+ key: 'foo',
+ name: 'Foo'
+ })}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverviewRenderer-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverviewRenderer-test.tsx
new file mode 100644
index 00000000000..024b32cf1a6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverviewRenderer-test.tsx
@@ -0,0 +1,46 @@
+/*
+ * 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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import { mockComponent, mockMeasureEnhanced } from '../../../../helpers/testMocks';
+import { GraphType } from '../../../../types/project-activity';
+import { BranchOverviewRenderer, BranchOverviewRendererProps } from '../BranchOverviewRenderer';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ projectIsEmpty: true })).toMatchSnapshot();
+ expect(shallowRender({ loadingHistory: true, loadingStatus: true })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<BranchOverviewRendererProps> = {}) {
+ return shallow(
+ <BranchOverviewRenderer
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ graph={GraphType.issues}
+ loadingHistory={false}
+ loadingStatus={false}
+ measures={[mockMeasureEnhanced()]}
+ onGraphChange={jest.fn()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/DebtValue-test.tsx
index 13534354d84..49f1d272fd1 100644
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Coverage-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/DebtValue-test.tsx
@@ -21,49 +21,25 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
-import Coverage from '../Coverage';
-import { ComposedProps } from '../enhance';
+import { MetricKey } from '../../../../types/metrics';
+import { DebtValue, DebtValueProps } from '../DebtValue';
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ useDiffMetric: true })).toMatchSnapshot();
+ expect(shallowRender({ measures: [] })).toMatchSnapshot();
});
-function shallowRender(props: Partial<ComposedProps> = {}) {
+function shallowRender(props: Partial<DebtValueProps> = {}) {
return shallow(
- <Coverage
+ <DebtValue
branchLike={mockMainBranch()}
component={mockComponent()}
- leakPeriod={{ index: 1 } as T.Period}
measures={[
- mockMeasureEnhanced(),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_lines_to_cover',
- key: 'new_lines_to_cover',
- name: 'New Lines to Cover',
- type: 'INT'
- }),
- value: '52'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_coverage',
- key: 'new_coverage',
- name: 'New Coverage'
- }),
- value: '85.0'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'tests',
- key: 'tests',
- name: 'Unit Tests',
- type: 'INT'
- }),
- value: '15'
- })
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.sqale_index }) }),
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_technical_debt }) })
]}
{...props}
/>
- ).dive();
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/DrilldownMeasureValue-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/DrilldownMeasureValue-test.tsx
new file mode 100644
index 00000000000..73ab71e8bef
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/DrilldownMeasureValue-test.tsx
@@ -0,0 +1,42 @@
+/*
+ * 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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
+import { MetricKey } from '../../../../types/metrics';
+import { DrilldownMeasureValue, DrilldownMeasureValueProps } from '../DrilldownMeasureValue';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ metric: MetricKey.bugs })).toMatchSnapshot('measure not found');
+});
+
+function shallowRender(props: Partial<DrilldownMeasureValueProps> = {}) {
+ return shallow(
+ <DrilldownMeasureValue
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ measures={[mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.tests }) })]}
+ metric={MetricKey.tests}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/Event-test.tsx
index 70179114ae4..036705ea099 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/Event-test.tsx
@@ -19,22 +19,22 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { RichQualityGateEvent } from '../../../projectActivity/components/RichQualityGateEventInner';
-import Event from '../Event';
-
-const EVENT = { key: '1', category: 'OTHER', name: 'test' };
-const VERSION = { key: '2', category: 'VERSION', name: '6.5-SNAPSHOT' };
+import { Event } from '../Event';
it('should render an event correctly', () => {
- expect(shallow(<Event event={EVENT} />)).toMatchSnapshot();
+ expect(
+ shallow(<Event event={{ key: '1', category: 'OTHER', name: 'test' }} />)
+ ).toMatchSnapshot();
});
it('should render a version correctly', () => {
- expect(shallow(<Event event={VERSION} />)).toMatchSnapshot();
+ expect(
+ shallow(<Event event={{ key: '2', category: 'VERSION', name: '6.5-SNAPSHOT' }} />)
+ ).toMatchSnapshot();
});
it('should render rich quality gate event', () => {
- const event: RichQualityGateEvent = {
+ const event: T.AnalysisEvent = {
category: 'QUALITY_GATE',
key: 'foo1234',
name: '',
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/MeasuresPanel-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/MeasuresPanel-test.tsx
new file mode 100644
index 00000000000..4f0094bc1b4
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/MeasuresPanel-test.tsx
@@ -0,0 +1,82 @@
+/*
+ * 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 BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs';
+import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
+import { MetricKey } from '../../../../types/metrics';
+import { MeasuresPanel, MeasuresPanelProps, MeasuresPanelTabs } from '../MeasuresPanel';
+
+it('should render correctly for projects', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+ wrapper.find(BoxedTabs).prop<Function>('onSelect')(MeasuresPanelTabs.Overall);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render correctly for applications', () => {
+ const wrapper = shallowRender({ component: mockComponent({ qualifier: 'APP' }) });
+ expect(wrapper).toMatchSnapshot();
+ wrapper.find(BoxedTabs).prop<Function>('onSelect')(MeasuresPanelTabs.Overall);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render correctly if there is no new code measures', () => {
+ const wrapper = shallowRender({
+ measures: [
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.coverage }) }),
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.bugs }) })
+ ]
+ });
+ wrapper.find(BoxedTabs).prop<Function>('onSelect')(MeasuresPanelTabs.New);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render correctly if there is no coverage', () => {
+ expect(
+ shallowRender({
+ measures: [
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.bugs }) }),
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_bugs }) })
+ ]
+ })
+ ).toMatchSnapshot();
+});
+
+it('should render correctly if the data is still loading', () => {
+ expect(shallowRender({ loading: true })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<MeasuresPanelProps> = {}) {
+ return shallow(
+ <MeasuresPanel
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ measures={[
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.coverage }) }),
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_coverage }) }),
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.bugs }) }),
+ mockMeasureEnhanced({ metric: mockMetric({ key: MetricKey.new_bugs }) })
+ ]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/NoCodeWarning-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/NoCodeWarning-test.tsx
new file mode 100644
index 00000000000..cfec0025b07
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/NoCodeWarning-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 { mockBranch, mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
+import { NoCodeWarning } from '../NoCodeWarning';
+
+it.only('should render correctly if the project has no lines of code', () => {
+ const wrapper = shallowRender();
+ expect(wrapper.children().text()).toBe('overview.project.main_branch_no_lines_of_code');
+
+ wrapper.setProps({ branchLike: mockBranch({ name: 'branch-foo' }) });
+ expect(wrapper.children().text()).toBe('overview.project.branch_X_no_lines_of_code.branch-foo');
+
+ wrapper.setProps({ branchLike: undefined });
+ expect(wrapper.children().text()).toBe('overview.project.no_lines_of_code');
+});
+
+it('should correctly if the project is empty', () => {
+ const wrapper = shallowRender({ measures: [] });
+ expect(wrapper.children().text()).toBe('overview.project.main_branch_empty');
+
+ wrapper.setProps({ branchLike: mockBranch({ name: 'branch-foo' }) });
+ expect(wrapper.children().text()).toBe('overview.project.branch_X_empty.branch-foo');
+
+ wrapper.setProps({ branchLike: undefined });
+ expect(wrapper.children().text()).toBe('overview.project.empty');
+});
+
+it('should render correctly if the application is empty or has no lines of code', () => {
+ const wrapper = shallowRender({
+ component: mockComponent({ qualifier: 'APP' }),
+ measures: [mockMeasureEnhanced({ metric: mockMetric({ key: 'projects' }) })]
+ });
+ expect(wrapper.children().text()).toBe('portfolio.app.no_lines_of_code');
+
+ wrapper.setProps({ measures: [] });
+ expect(wrapper.children().text()).toBe('portfolio.app.empty');
+});
+
+function shallowRender(props = {}) {
+ return shallow(
+ <NoCodeWarning
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ measures={[mockMeasureEnhanced({ metric: mockMetric({ key: 'bugs' }) })]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanel-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanel-test.tsx
new file mode 100644
index 00000000000..8769973e216
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanel-test.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import {
+ mockQualityGateStatus,
+ mockQualityGateStatusConditionEnhanced
+} from '../../../../helpers/mocks/quality-gates';
+import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
+import { QualityGatePanel, QualityGatePanelProps } from '../QualityGatePanel';
+
+it('should render correctly for projects', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(
+ shallowRender({ qgStatuses: [mockQualityGateStatus({ status: 'OK', failedConditions: [] })] })
+ ).toMatchSnapshot();
+
+ const wrapper = shallowRender({
+ qgStatuses: [mockQualityGateStatus({ ignoredConditions: true })]
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render correctly for applications', () => {
+ expect(
+ shallowRender({
+ component: mockComponent({ qualifier: 'APP' }),
+ qgStatuses: [
+ mockQualityGateStatus(),
+ mockQualityGateStatus({
+ failedConditions: [
+ mockQualityGateStatusConditionEnhanced(),
+ mockQualityGateStatusConditionEnhanced({
+ measure: mockMeasureEnhanced({ metric: mockMetric({ key: 'new_code_smells' }) }),
+ metric: 'new_code_smells'
+ })
+ ]
+ })
+ ]
+ })
+ ).toMatchSnapshot();
+
+ const wrapper = shallowRender({
+ component: mockComponent({ qualifier: 'APP' }),
+ qgStatuses: [
+ mockQualityGateStatus(),
+ mockQualityGateStatus({
+ status: 'OK',
+ failedConditions: []
+ })
+ ]
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<QualityGatePanelProps> = {}) {
+ return shallow(
+ <QualityGatePanel
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ qgStatuses={[mockQualityGateStatus()]}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx
new file mode 100644
index 00000000000..50169bf8223
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/QualityGatePanelSection-test.tsx
@@ -0,0 +1,58 @@
+/*
+ * 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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
+import {
+ mockQualityGateStatus,
+ mockQualityGateStatusConditionEnhanced
+} from '../../../../helpers/mocks/quality-gates';
+import { mockComponent } from '../../../../helpers/testMocks';
+import { QualityGatePanelSection, QualityGatePanelSectionProps } from '../QualityGatePanelSection';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+ expect(
+ shallowRender({
+ qgStatus: mockQualityGateStatus({
+ failedConditions: [],
+ status: 'OK'
+ })
+ }).type()
+ ).toBeNull();
+ expect(shallowRender({ component: mockComponent({ qualifier: 'APP' }) })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<QualityGatePanelSectionProps> = {}) {
+ return shallow(
+ <QualityGatePanelSection
+ branchLike={mockMainBranch()}
+ component={mockComponent()}
+ qgStatus={mockQualityGateStatus({
+ failedConditions: [
+ mockQualityGateStatusConditionEnhanced({ metric: 'bugs' }),
+ mockQualityGateStatusConditionEnhanced({ metric: 'new_bugs' })
+ ],
+ status: 'ERROR'
+ })}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ActivityPanel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ActivityPanel-test.tsx.snap
new file mode 100644
index 00000000000..90b14ae0e81
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ActivityPanel-test.tsx.snap
@@ -0,0 +1,349 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="overview-panel big-spacer-top"
+ data-test="overview__activity-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.activity
+ </h2>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="display-flex-row"
+ >
+ <div
+ className="display-flex-column flex-1"
+ >
+ <div
+ aria-hidden={true}
+ className="overview-panel-padded display-flex-column flex-1"
+ >
+ <GraphsHeader
+ graph="issues"
+ metrics={
+ Array [
+ Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ updateGraph={[MockFunction]}
+ />
+ <GraphsHistory
+ analyses={
+ Array [
+ Object {
+ "date": 2017-03-01T08:36:01.000Z,
+ "events": Array [
+ Object {
+ "category": "QUALITY_GATE",
+ "description": "Lorem ipsum dolor sit amet",
+ "key": "E11",
+ "name": "Lorem ipsum",
+ "qualityGate": Object {
+ "failing": Array [
+ Object {
+ "branch": "master",
+ "key": "foo",
+ "name": "Foo",
+ },
+ Object {
+ "branch": "feature/bar",
+ "key": "bar",
+ "name": "Bar",
+ },
+ ],
+ "status": "ERROR",
+ "stillFailing": true,
+ },
+ },
+ ],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": 2017-03-01T08:36:01.000Z,
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ ]
+ }
+ graph="issues"
+ graphs={
+ Array [
+ Array [
+ Object {
+ "data": Array [
+ Object {
+ "x": 2016-10-27T14:33:50.000Z,
+ "y": 20,
+ },
+ ],
+ "name": "bugs",
+ "translatedName": "Bugs",
+ "type": "PERCENT",
+ },
+ ],
+ ]
+ }
+ loading={false}
+ measuresHistory={
+ Array [
+ Object {
+ "bestValue": true,
+ "history": Array [
+ Object {
+ "date": 2016-10-27T14:33:50.000Z,
+ "value": "20",
+ },
+ ],
+ "metric": "bugs",
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ series={
+ Array [
+ Object {
+ "data": Array [
+ Object {
+ "x": 2016-10-27T14:33:50.000Z,
+ "y": 20,
+ },
+ ],
+ "name": "bugs",
+ "translatedName": "Bugs",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ />
+ </div>
+ <div
+ className="overview-panel-padded bordered-top text-right"
+ >
+ <ActivityLink
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component="my-project"
+ graph="issues"
+ />
+ </div>
+ </div>
+ <div
+ className="overview-panel-padded bordered-left width-30"
+ >
+ <div
+ data-test="overview__activity-analyses"
+ >
+ <DeferredSpinner
+ className="spacer-top spacer-left"
+ loading={false}
+ timeout={100}
+ >
+ <ul
+ className="spacer-top spacer-left"
+ >
+ <Memo(Analysis)
+ analysis={
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [
+ Object {
+ "category": "QUALITY_GATE",
+ "description": "Lorem ipsum dolor sit amet",
+ "key": "E11",
+ "name": "Lorem ipsum",
+ "qualityGate": Object {
+ "failing": Array [
+ Object {
+ "branch": "master",
+ "key": "foo",
+ "name": "Foo",
+ },
+ Object {
+ "branch": "feature/bar",
+ "key": "bar",
+ "name": "Bar",
+ },
+ ],
+ "status": "ERROR",
+ "stillFailing": true,
+ },
+ },
+ ],
+ "key": "foo",
+ "projectVersion": "1.0",
+ }
+ }
+ key="foo"
+ qualifier="TRK"
+ />
+ </ul>
+ </DeferredSpinner>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly 2`] = `
+<div
+ className="overview-panel big-spacer-top"
+ data-test="overview__activity-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.activity
+ </h2>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="display-flex-row"
+ >
+ <div
+ className="display-flex-column flex-1"
+ >
+ <div
+ aria-hidden={true}
+ className="overview-panel-padded display-flex-column flex-1"
+ >
+ <GraphsHeader
+ graph="issues"
+ metrics={
+ Array [
+ Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ updateGraph={[MockFunction]}
+ />
+ <GraphsHistory
+ analyses={Array []}
+ graph="issues"
+ graphs={
+ Array [
+ Array [
+ Object {
+ "data": Array [
+ Object {
+ "x": 2016-10-27T14:33:50.000Z,
+ "y": 20,
+ },
+ ],
+ "name": "bugs",
+ "translatedName": "Bugs",
+ "type": "PERCENT",
+ },
+ ],
+ ]
+ }
+ loading={true}
+ measuresHistory={
+ Array [
+ Object {
+ "bestValue": true,
+ "history": Array [
+ Object {
+ "date": 2016-10-27T14:33:50.000Z,
+ "value": "20",
+ },
+ ],
+ "metric": "bugs",
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ series={
+ Array [
+ Object {
+ "data": Array [
+ Object {
+ "x": 2016-10-27T14:33:50.000Z,
+ "y": 20,
+ },
+ ],
+ "name": "bugs",
+ "translatedName": "Bugs",
+ "type": "PERCENT",
+ },
+ ]
+ }
+ />
+ </div>
+ <div
+ className="overview-panel-padded bordered-top text-right"
+ >
+ <ActivityLink
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component="my-project"
+ graph="issues"
+ />
+ </div>
+ </div>
+ <div
+ className="overview-panel-padded bordered-left width-30"
+ >
+ <div
+ data-test="overview__activity-analyses"
+ >
+ <DeferredSpinner
+ className="spacer-top spacer-left"
+ loading={true}
+ timeout={100}
+ >
+ <p
+ className="spacer-top spacer-left note"
+ >
+ no_results
+ </p>
+ </DeferredSpinner>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Analysis-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Analysis-test.tsx.snap
new file mode 100644
index 00000000000..e8a36493871
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Analysis-test.tsx.snap
@@ -0,0 +1,81 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<li
+ className="overview-analysis"
+>
+ <div
+ className="small little-spacer-bottom"
+ >
+ <strong>
+ <DateTooltipFormatter
+ date="2017-03-01T09:36:01+0100"
+ />
+ </strong>
+ </div>
+ <div
+ className="overview-activity-events"
+ >
+ <Memo(Event)
+ event={
+ Object {
+ "category": "VERSION",
+ "key": "2",
+ "name": "6.5-SNAPSHOT",
+ }
+ }
+ key="2"
+ />
+ <Memo(Event)
+ event={
+ Object {
+ "category": "OTHER",
+ "key": "1",
+ "name": "test",
+ }
+ }
+ key="1"
+ />
+ </div>
+</li>
+`;
+
+exports[`should render correctly 2`] = `
+<li
+ className="overview-analysis"
+>
+ <div
+ className="small little-spacer-bottom"
+ >
+ <strong>
+ <DateTooltipFormatter
+ date="2017-03-01T09:36:01+0100"
+ />
+ </strong>
+ </div>
+ <div
+ className="overview-activity-events"
+ >
+ <Memo(Event)
+ event={
+ Object {
+ "category": "VERSION",
+ "key": "2",
+ "name": "6.5-SNAPSHOT",
+ }
+ }
+ key="2"
+ />
+ <Memo(Event)
+ event={
+ Object {
+ "category": "OTHER",
+ "key": "1",
+ "name": "test",
+ }
+ }
+ key="1"
+ />
+ </div>
+</li>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ApplicationLeakPeriodInfo-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ApplicationLeakPeriodInfo-test.tsx.snap
new file mode 100644
index 00000000000..8a0e03c64df
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ApplicationLeakPeriodInfo-test.tsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div
+ className="note spacer-top display-inline-flex-center"
+>
+ <DateFromNow
+ date="2017-10-01"
+ >
+ <Component />
+ </DateFromNow>
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay="overview.max_new_code_period_from_x.Foo"
+ />
+</div>
+`;
+
+exports[`renders correctly 2`] = `"overview.started_x.2017-10-01"`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverview-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverview-test.tsx.snap
new file mode 100644
index 00000000000..a7e7dc16f27
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverview-test.tsx.snap
@@ -0,0 +1,1702 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`application overview should render correctly 1`] = `
+<Memo(BranchOverviewRenderer)
+ analyses={
+ Array [
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ ]
+ }
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [
+ Object {
+ "breadcrumbs": Array [],
+ "key": "foo",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ },
+ ],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ graph="coverage"
+ leakPeriod={
+ Object {
+ "date": "2017-01-05",
+ "project": "foo",
+ "projectName": "Foo",
+ }
+ }
+ loadingHistory={false}
+ loadingStatus={false}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "alert_status",
+ "key": "alert_status",
+ "name": "alert_status",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "quality_gate_details",
+ "key": "quality_gate_details",
+ "name": "quality_gate_details",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "bugs",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "reliability_rating",
+ "key": "reliability_rating",
+ "name": "reliability_rating",
+ "type": "RATING",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_reliability_rating",
+ "key": "new_reliability_rating",
+ "name": "new_reliability_rating",
+ "type": "RATING",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "vulnerabilities",
+ "key": "vulnerabilities",
+ "name": "vulnerabilities",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_vulnerabilities",
+ "key": "new_vulnerabilities",
+ "name": "new_vulnerabilities",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "security_rating",
+ "key": "security_rating",
+ "name": "security_rating",
+ "type": "RATING",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_security_rating",
+ "key": "new_security_rating",
+ "name": "new_security_rating",
+ "type": "RATING",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "security_hotspots",
+ "key": "security_hotspots",
+ "name": "security_hotspots",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_security_hotspots",
+ "key": "new_security_hotspots",
+ "name": "new_security_hotspots",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "code_smells",
+ "key": "code_smells",
+ "name": "code_smells",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "new_code_smells",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "sqale_rating",
+ "key": "sqale_rating",
+ "name": "sqale_rating",
+ "type": "RATING",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_maintainability_rating",
+ "key": "new_maintainability_rating",
+ "name": "new_maintainability_rating",
+ "type": "RATING",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "sqale_index",
+ "key": "sqale_index",
+ "name": "sqale_index",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_technical_debt",
+ "key": "new_technical_debt",
+ "name": "new_technical_debt",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "coverage",
+ "type": "PERCENT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "new_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "lines_to_cover",
+ "key": "lines_to_cover",
+ "name": "lines_to_cover",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_lines_to_cover",
+ "key": "new_lines_to_cover",
+ "name": "new_lines_to_cover",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "tests",
+ "key": "tests",
+ "name": "tests",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "duplicated_lines_density",
+ "key": "duplicated_lines_density",
+ "name": "duplicated_lines_density",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_duplicated_lines_density",
+ "key": "new_duplicated_lines_density",
+ "name": "new_duplicated_lines_density",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "duplicated_blocks",
+ "key": "duplicated_blocks",
+ "name": "duplicated_blocks",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "ncloc",
+ "key": "ncloc",
+ "name": "ncloc",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "ncloc_language_distribution",
+ "key": "ncloc_language_distribution",
+ "name": "ncloc_language_distribution",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "projects",
+ "key": "projects",
+ "name": "projects",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "lines",
+ "key": "lines",
+ "name": "lines",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_lines",
+ "key": "new_lines",
+ "name": "new_lines",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ measuresHistory={
+ Array [
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-05",
+ "value": "2.0",
+ },
+ ],
+ "metric": "bugs",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-05",
+ "value": "0",
+ },
+ ],
+ "metric": "vulnerabilities",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-01",
+ "value": "1.0",
+ },
+ ],
+ "metric": "sqale_index",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-02",
+ "value": "1.0",
+ },
+ ],
+ "metric": "duplicated_lines_density",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-03",
+ "value": "10000",
+ },
+ ],
+ "metric": "ncloc",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-04",
+ "value": "95.5",
+ },
+ ],
+ "metric": "coverage",
+ },
+ ]
+ }
+ metrics={
+ Array [
+ Object {
+ "id": "alert_status",
+ "key": "alert_status",
+ "name": "alert_status",
+ "type": "INT",
+ },
+ Object {
+ "id": "quality_gate_details",
+ "key": "quality_gate_details",
+ "name": "quality_gate_details",
+ "type": "INT",
+ },
+ Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "bugs",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ Object {
+ "id": "reliability_rating",
+ "key": "reliability_rating",
+ "name": "reliability_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "new_reliability_rating",
+ "key": "new_reliability_rating",
+ "name": "new_reliability_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "vulnerabilities",
+ "key": "vulnerabilities",
+ "name": "vulnerabilities",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_vulnerabilities",
+ "key": "new_vulnerabilities",
+ "name": "new_vulnerabilities",
+ "type": "INT",
+ },
+ Object {
+ "id": "security_rating",
+ "key": "security_rating",
+ "name": "security_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "new_security_rating",
+ "key": "new_security_rating",
+ "name": "new_security_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "security_hotspots",
+ "key": "security_hotspots",
+ "name": "security_hotspots",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_security_hotspots",
+ "key": "new_security_hotspots",
+ "name": "new_security_hotspots",
+ "type": "INT",
+ },
+ Object {
+ "id": "code_smells",
+ "key": "code_smells",
+ "name": "code_smells",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "new_code_smells",
+ "type": "INT",
+ },
+ Object {
+ "id": "sqale_rating",
+ "key": "sqale_rating",
+ "name": "sqale_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "new_maintainability_rating",
+ "key": "new_maintainability_rating",
+ "name": "new_maintainability_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "sqale_index",
+ "key": "sqale_index",
+ "name": "sqale_index",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_technical_debt",
+ "key": "new_technical_debt",
+ "name": "new_technical_debt",
+ "type": "INT",
+ },
+ Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "coverage",
+ "type": "PERCENT",
+ },
+ Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "new_coverage",
+ "type": "PERCENT",
+ },
+ Object {
+ "id": "lines_to_cover",
+ "key": "lines_to_cover",
+ "name": "lines_to_cover",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_lines_to_cover",
+ "key": "new_lines_to_cover",
+ "name": "new_lines_to_cover",
+ "type": "INT",
+ },
+ Object {
+ "id": "tests",
+ "key": "tests",
+ "name": "tests",
+ "type": "INT",
+ },
+ Object {
+ "id": "duplicated_lines_density",
+ "key": "duplicated_lines_density",
+ "name": "duplicated_lines_density",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_duplicated_lines_density",
+ "key": "new_duplicated_lines_density",
+ "name": "new_duplicated_lines_density",
+ "type": "INT",
+ },
+ Object {
+ "id": "duplicated_blocks",
+ "key": "duplicated_blocks",
+ "name": "duplicated_blocks",
+ "type": "INT",
+ },
+ Object {
+ "id": "ncloc",
+ "key": "ncloc",
+ "name": "ncloc",
+ "type": "INT",
+ },
+ Object {
+ "id": "ncloc_language_distribution",
+ "key": "ncloc_language_distribution",
+ "name": "ncloc_language_distribution",
+ "type": "INT",
+ },
+ Object {
+ "id": "projects",
+ "key": "projects",
+ "name": "projects",
+ "type": "INT",
+ },
+ Object {
+ "id": "lines",
+ "key": "lines",
+ "name": "lines",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_lines",
+ "key": "new_lines",
+ "name": "new_lines",
+ "type": "INT",
+ },
+ ]
+ }
+ onGraphChange={[Function]}
+ projectIsEmpty={false}
+ qgStatuses={
+ Array [
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "1.0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "coverage",
+ "type": "PERCENT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ "metric": "coverage",
+ "op": "GT",
+ "period": undefined,
+ },
+ Object {
+ "actual": "5",
+ "error": "1.0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ "period": 1,
+ },
+ ],
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ },
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "15",
+ "error": "5.0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ "period": 1,
+ },
+ ],
+ "key": "bar",
+ "name": "Bar",
+ "status": "ERROR",
+ },
+ ]
+ }
+/>
+`;
+
+exports[`project overview should render correctly 1`] = `
+<Memo(BranchOverviewRenderer)
+ analyses={
+ Array [
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ Object {
+ "date": "2017-03-01T09:36:01+0100",
+ "events": Array [],
+ "key": "foo",
+ "projectVersion": "1.0",
+ },
+ ]
+ }
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [
+ Object {
+ "breadcrumbs": Array [],
+ "key": "foo",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ },
+ ],
+ "key": "foo",
+ "name": "Foo",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ graph="coverage"
+ loadingHistory={false}
+ loadingStatus={false}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "alert_status",
+ "key": "alert_status",
+ "name": "alert_status",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "quality_gate_details",
+ "key": "quality_gate_details",
+ "name": "quality_gate_details",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "bugs",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "reliability_rating",
+ "key": "reliability_rating",
+ "name": "reliability_rating",
+ "type": "RATING",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_reliability_rating",
+ "key": "new_reliability_rating",
+ "name": "new_reliability_rating",
+ "type": "RATING",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "vulnerabilities",
+ "key": "vulnerabilities",
+ "name": "vulnerabilities",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_vulnerabilities",
+ "key": "new_vulnerabilities",
+ "name": "new_vulnerabilities",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "security_rating",
+ "key": "security_rating",
+ "name": "security_rating",
+ "type": "RATING",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_security_rating",
+ "key": "new_security_rating",
+ "name": "new_security_rating",
+ "type": "RATING",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "security_hotspots",
+ "key": "security_hotspots",
+ "name": "security_hotspots",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_security_hotspots",
+ "key": "new_security_hotspots",
+ "name": "new_security_hotspots",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "code_smells",
+ "key": "code_smells",
+ "name": "code_smells",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "new_code_smells",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "sqale_rating",
+ "key": "sqale_rating",
+ "name": "sqale_rating",
+ "type": "RATING",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_maintainability_rating",
+ "key": "new_maintainability_rating",
+ "name": "new_maintainability_rating",
+ "type": "RATING",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "sqale_index",
+ "key": "sqale_index",
+ "name": "sqale_index",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_technical_debt",
+ "key": "new_technical_debt",
+ "name": "new_technical_debt",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "coverage",
+ "type": "PERCENT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "new_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "lines_to_cover",
+ "key": "lines_to_cover",
+ "name": "lines_to_cover",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_lines_to_cover",
+ "key": "new_lines_to_cover",
+ "name": "new_lines_to_cover",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "tests",
+ "key": "tests",
+ "name": "tests",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "duplicated_lines_density",
+ "key": "duplicated_lines_density",
+ "name": "duplicated_lines_density",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_duplicated_lines_density",
+ "key": "new_duplicated_lines_density",
+ "name": "new_duplicated_lines_density",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "duplicated_blocks",
+ "key": "duplicated_blocks",
+ "name": "duplicated_blocks",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "ncloc",
+ "key": "ncloc",
+ "name": "ncloc",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "ncloc_language_distribution",
+ "key": "ncloc_language_distribution",
+ "name": "ncloc_language_distribution",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "projects",
+ "key": "projects",
+ "name": "projects",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "lines",
+ "key": "lines",
+ "name": "lines",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_lines",
+ "key": "new_lines",
+ "name": "new_lines",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ measuresHistory={
+ Array [
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-05",
+ "value": "2.0",
+ },
+ ],
+ "metric": "bugs",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-05",
+ "value": "0",
+ },
+ ],
+ "metric": "vulnerabilities",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-01",
+ "value": "1.0",
+ },
+ ],
+ "metric": "sqale_index",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-02",
+ "value": "1.0",
+ },
+ ],
+ "metric": "duplicated_lines_density",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-03",
+ "value": "10000",
+ },
+ ],
+ "metric": "ncloc",
+ },
+ Object {
+ "history": Array [
+ Object {
+ "date": "PARSED:2019-01-04",
+ "value": "95.5",
+ },
+ ],
+ "metric": "coverage",
+ },
+ ]
+ }
+ metrics={
+ Array [
+ Object {
+ "id": "alert_status",
+ "key": "alert_status",
+ "name": "alert_status",
+ "type": "INT",
+ },
+ Object {
+ "id": "quality_gate_details",
+ "key": "quality_gate_details",
+ "name": "quality_gate_details",
+ "type": "INT",
+ },
+ Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "bugs",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ Object {
+ "id": "reliability_rating",
+ "key": "reliability_rating",
+ "name": "reliability_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "new_reliability_rating",
+ "key": "new_reliability_rating",
+ "name": "new_reliability_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "vulnerabilities",
+ "key": "vulnerabilities",
+ "name": "vulnerabilities",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_vulnerabilities",
+ "key": "new_vulnerabilities",
+ "name": "new_vulnerabilities",
+ "type": "INT",
+ },
+ Object {
+ "id": "security_rating",
+ "key": "security_rating",
+ "name": "security_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "new_security_rating",
+ "key": "new_security_rating",
+ "name": "new_security_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "security_hotspots",
+ "key": "security_hotspots",
+ "name": "security_hotspots",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_security_hotspots",
+ "key": "new_security_hotspots",
+ "name": "new_security_hotspots",
+ "type": "INT",
+ },
+ Object {
+ "id": "code_smells",
+ "key": "code_smells",
+ "name": "code_smells",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "new_code_smells",
+ "type": "INT",
+ },
+ Object {
+ "id": "sqale_rating",
+ "key": "sqale_rating",
+ "name": "sqale_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "new_maintainability_rating",
+ "key": "new_maintainability_rating",
+ "name": "new_maintainability_rating",
+ "type": "RATING",
+ },
+ Object {
+ "id": "sqale_index",
+ "key": "sqale_index",
+ "name": "sqale_index",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_technical_debt",
+ "key": "new_technical_debt",
+ "name": "new_technical_debt",
+ "type": "INT",
+ },
+ Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "coverage",
+ "type": "PERCENT",
+ },
+ Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "new_coverage",
+ "type": "PERCENT",
+ },
+ Object {
+ "id": "lines_to_cover",
+ "key": "lines_to_cover",
+ "name": "lines_to_cover",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_lines_to_cover",
+ "key": "new_lines_to_cover",
+ "name": "new_lines_to_cover",
+ "type": "INT",
+ },
+ Object {
+ "id": "tests",
+ "key": "tests",
+ "name": "tests",
+ "type": "INT",
+ },
+ Object {
+ "id": "duplicated_lines_density",
+ "key": "duplicated_lines_density",
+ "name": "duplicated_lines_density",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_duplicated_lines_density",
+ "key": "new_duplicated_lines_density",
+ "name": "new_duplicated_lines_density",
+ "type": "INT",
+ },
+ Object {
+ "id": "duplicated_blocks",
+ "key": "duplicated_blocks",
+ "name": "duplicated_blocks",
+ "type": "INT",
+ },
+ Object {
+ "id": "ncloc",
+ "key": "ncloc",
+ "name": "ncloc",
+ "type": "INT",
+ },
+ Object {
+ "id": "ncloc_language_distribution",
+ "key": "ncloc_language_distribution",
+ "name": "ncloc_language_distribution",
+ "type": "INT",
+ },
+ Object {
+ "id": "projects",
+ "key": "projects",
+ "name": "projects",
+ "type": "INT",
+ },
+ Object {
+ "id": "lines",
+ "key": "lines",
+ "name": "lines",
+ "type": "INT",
+ },
+ Object {
+ "id": "new_lines",
+ "key": "new_lines",
+ "name": "new_lines",
+ "type": "INT",
+ },
+ ]
+ }
+ onGraphChange={[Function]}
+ projectIsEmpty={false}
+ qgStatuses={
+ Array [
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "2",
+ "error": "1.0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "new_bugs",
+ "type": "INT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ "period": 1,
+ },
+ Object {
+ "actual": "5",
+ "error": "2.0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "bugs",
+ "type": "INT",
+ },
+ "periods": undefined,
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ "period": 0,
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ },
+ ]
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverviewRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverviewRenderer-test.tsx.snap
new file mode 100644
index 00000000000..26d005be471
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/BranchOverviewRenderer-test.tsx.snap
@@ -0,0 +1,386 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="page page-limited"
+>
+ <div
+ className="overview"
+ >
+ <A11ySkipTarget
+ anchor="overview_main"
+ />
+ <div
+ className="display-flex-row"
+ >
+ <div
+ className="width-25 big-spacer-right"
+ >
+ <Memo(QualityGatePanel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ loading={false}
+ />
+ </div>
+ <div
+ className="flex-1"
+ >
+ <div
+ className="display-flex-column"
+ >
+ <MeasuresPanel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ loading={false}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ />
+ <Memo(ActivityPanel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ graph="issues"
+ loading={false}
+ measuresHistory={Array []}
+ metrics={Array []}
+ onGraphChange={[MockFunction]}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly 2`] = `
+<div
+ className="page page-limited"
+>
+ <div
+ className="overview"
+ >
+ <A11ySkipTarget
+ anchor="overview_main"
+ />
+ <Memo(NoCodeWarning)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ />
+ </div>
+</div>
+`;
+
+exports[`should render correctly 3`] = `
+<div
+ className="page page-limited"
+>
+ <div
+ className="overview"
+ >
+ <A11ySkipTarget
+ anchor="overview_main"
+ />
+ <div
+ className="display-flex-row"
+ >
+ <div
+ className="width-25 big-spacer-right"
+ >
+ <Memo(QualityGatePanel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ loading={true}
+ />
+ </div>
+ <div
+ className="flex-1"
+ >
+ <div
+ className="display-flex-column"
+ >
+ <MeasuresPanel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ loading={true}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ />
+ <Memo(ActivityPanel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ graph="issues"
+ loading={true}
+ measuresHistory={Array []}
+ metrics={Array []}
+ onGraphChange={[MockFunction]}
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DebtValue-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DebtValue-test.tsx.snap
new file mode 100644
index 00000000000..a96ac567ff1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DebtValue-test.tsx.snap
@@ -0,0 +1,65 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <DrilldownLink
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ className="overview-measures-value text-light"
+ component="my-project"
+ metric="sqale_index"
+ >
+ work_duration.x_minutes.1
+ </DrilldownLink>
+ <span
+ className="big-spacer-left"
+ >
+ Sqale_index
+ </span>
+</Fragment>
+`;
+
+exports[`should render correctly 2`] = `
+<Fragment>
+ <DrilldownLink
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ className="overview-measures-value text-light"
+ component="my-project"
+ metric="new_technical_debt"
+ >
+ work_duration.x_minutes.1
+ </DrilldownLink>
+ <span
+ className="big-spacer-left"
+ >
+ New_technical_debt
+ </span>
+</Fragment>
+`;
+
+exports[`should render correctly 3`] = `
+<Fragment>
+ <span
+ aria-label="no_data"
+ className="overview-measures-empty-value"
+ />
+ <span
+ className="big-spacer-left"
+ >
+ metric.sqale_index.name
+ </span>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DrilldownMeasureValue-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DrilldownMeasureValue-test.tsx.snap
new file mode 100644
index 00000000000..210bb6293e2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/DrilldownMeasureValue-test.tsx.snap
@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<div
+ className="display-flex-column display-flex-center"
+>
+ <span>
+ <DrilldownLink
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ className="overview-measures-value text-light"
+ component="my-project"
+ metric="tests"
+ >
+ 1
+ </DrilldownLink>
+ </span>
+ <span
+ className="spacer-top"
+ >
+ tests
+ </span>
+</div>
+`;
+
+exports[`should render correctly: measure not found 1`] = `
+<div
+ className="display-flex-column display-flex-center"
+>
+ <span
+ className="overview-measures-value text-light"
+ >
+ -
+ </span>
+ <span
+ className="spacer-top"
+ >
+ bugs
+ </span>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Event-test.tsx.snap
index 56508dbaede..56508dbaede 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/Event-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/LeakPeriodInfo-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/LeakPeriodInfo-test.tsx.snap
new file mode 100644
index 00000000000..a6a8d910dfa
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/LeakPeriodInfo-test.tsx.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly for applications 1`] = `
+<Memo(ApplicationLeakPeriodInfo)
+ leakPeriod={
+ Object {
+ "date": "2017-10-01",
+ "project": "foo",
+ "projectName": "Foo",
+ }
+ }
+/>
+`;
+
+exports[`renders correctly for projects 1`] = `
+<Memo(InjectIntl(ProjectLeakPeriodInfo))
+ leakPeriod={
+ Object {
+ "date": "2019-04-23T02:12:32+0100",
+ "index": 0,
+ "mode": "previous_version",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap
new file mode 100644
index 00000000000..299370e948f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap
@@ -0,0 +1,5943 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly for applications 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <BoxedTabs
+ onSelect={[Function]}
+ selected={0}
+ tabs={
+ Array [
+ Object {
+ "key": 0,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ >
+ overview.new_code
+ </span>
+ </div>,
+ },
+ Object {
+ "key": 1,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ style={
+ Object {
+ "position": "absolute",
+ "top": 16,
+ }
+ }
+ >
+ overview.overall_code
+ </span>
+ </div>,
+ },
+ ]
+ }
+ />
+ <div
+ className="overview-panel-content flex-1 bordered"
+ >
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-bug"
+ key="BUG"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-vulnerability"
+ key="VULNERABILITY"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ docTooltip={Promise {}}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="SECURITY_HOTSPOT"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-code_smell"
+ key="CODE_SMELL"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(DebtValue)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ >
+ <div
+ className="overview-panel-huge-padded flex-1 bordered-right display-flex-center"
+ data-test="overview__measures-coverage"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={true}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="COVERAGE"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-huge-padded flex-1 display-flex-center"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={true}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="DUPLICATION"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for applications 2`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <BoxedTabs
+ onSelect={[Function]}
+ selected={1}
+ tabs={
+ Array [
+ Object {
+ "key": 0,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ >
+ overview.new_code
+ </span>
+ </div>,
+ },
+ Object {
+ "key": 1,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ style={
+ Object {
+ "position": "absolute",
+ "top": 16,
+ }
+ }
+ >
+ overview.overall_code
+ </span>
+ </div>,
+ },
+ ]
+ }
+ />
+ <div
+ className="overview-panel-content flex-1 bordered"
+ >
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-bug"
+ key="BUG"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={false}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-vulnerability"
+ key="VULNERABILITY"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ docTooltip={Promise {}}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="SECURITY_HOTSPOT"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={false}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-code_smell"
+ key="CODE_SMELL"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(DebtValue)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={false}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ >
+ <div
+ className="overview-panel-huge-padded flex-1 bordered-right display-flex-center"
+ data-test="overview__measures-coverage"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={false}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="COVERAGE"
+ useDiffMetric={false}
+ />
+ <div
+ className="huge-spacer-left"
+ >
+ <DrilldownMeasureValue
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ metric="tests"
+ />
+ </div>
+ </div>
+ <div
+ className="overview-panel-huge-padded flex-1 display-flex-center"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={false}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="DUPLICATION"
+ useDiffMetric={false}
+ />
+ <div
+ className="huge-spacer-left"
+ >
+ <DrilldownMeasureValue
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ metric="duplicated_blocks"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for projects 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <BoxedTabs
+ onSelect={[Function]}
+ selected={0}
+ tabs={
+ Array [
+ Object {
+ "key": 0,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ >
+ overview.new_code
+ </span>
+ </div>,
+ },
+ Object {
+ "key": 1,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ style={
+ Object {
+ "position": "absolute",
+ "top": 16,
+ }
+ }
+ >
+ overview.overall_code
+ </span>
+ </div>,
+ },
+ ]
+ }
+ />
+ <div
+ className="overview-panel-content flex-1 bordered"
+ >
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-bug"
+ key="BUG"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-vulnerability"
+ key="VULNERABILITY"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ docTooltip={Promise {}}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="SECURITY_HOTSPOT"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-code_smell"
+ key="CODE_SMELL"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(DebtValue)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ >
+ <div
+ className="overview-panel-huge-padded flex-1 bordered-right display-flex-center"
+ data-test="overview__measures-coverage"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={true}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="COVERAGE"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-huge-padded flex-1 display-flex-center"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={true}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="DUPLICATION"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for projects 2`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <BoxedTabs
+ onSelect={[Function]}
+ selected={1}
+ tabs={
+ Array [
+ Object {
+ "key": 0,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ >
+ overview.new_code
+ </span>
+ </div>,
+ },
+ Object {
+ "key": 1,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ style={
+ Object {
+ "position": "absolute",
+ "top": 16,
+ }
+ }
+ >
+ overview.overall_code
+ </span>
+ </div>,
+ },
+ ]
+ }
+ />
+ <div
+ className="overview-panel-content flex-1 bordered"
+ >
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-bug"
+ key="BUG"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={false}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-vulnerability"
+ key="VULNERABILITY"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ docTooltip={Promise {}}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="SECURITY_HOTSPOT"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={false}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-code_smell"
+ key="CODE_SMELL"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(DebtValue)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={false}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={false}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ >
+ <div
+ className="overview-panel-huge-padded flex-1 bordered-right display-flex-center"
+ data-test="overview__measures-coverage"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={false}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="COVERAGE"
+ useDiffMetric={false}
+ />
+ <div
+ className="huge-spacer-left"
+ >
+ <DrilldownMeasureValue
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ metric="tests"
+ />
+ </div>
+ </div>
+ <div
+ className="overview-panel-huge-padded flex-1 display-flex-center"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={false}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="DUPLICATION"
+ useDiffMetric={false}
+ />
+ <div
+ className="huge-spacer-left"
+ >
+ <DrilldownMeasureValue
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_coverage",
+ "key": "new_coverage",
+ "name": "New_coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ metric="duplicated_blocks"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly if the data is still loading 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <div
+ className="overview-panel-content overview-panel-big-padded"
+ >
+ <DeferredSpinner
+ loading={true}
+ timeout={100}
+ />
+ </div>
+</div>
+`;
+
+exports[`should render correctly if there is no coverage 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <BoxedTabs
+ onSelect={[Function]}
+ selected={0}
+ tabs={
+ Array [
+ Object {
+ "key": 0,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ >
+ overview.new_code
+ </span>
+ </div>,
+ },
+ Object {
+ "key": 1,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ style={
+ Object {
+ "position": "absolute",
+ "top": 16,
+ }
+ }
+ >
+ overview.overall_code
+ </span>
+ </div>,
+ },
+ ]
+ }
+ />
+ <div
+ className="overview-panel-content flex-1 bordered"
+ >
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-bug"
+ key="BUG"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="BUG"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-vulnerability"
+ key="VULNERABILITY"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ docTooltip={Promise {}}
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="SECURITY_HOTSPOT"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="VULNERABILITY"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ data-test="overview__measures-code_smell"
+ key="CODE_SMELL"
+ >
+ <div
+ className="overview-panel-big-padded flex-1 small display-flex-center big-spacer-left"
+ >
+ <Memo(DebtValue)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="flex-1 small display-flex-center"
+ >
+ <Memo(IssueLabel)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={true}
+ />
+ </div>
+ <div
+ className="overview-panel-big-padded overview-measures-aside display-flex-center"
+ >
+ <Memo(IssueRating)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="CODE_SMELL"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-row overview-measures-row"
+ >
+ <div
+ className="overview-panel-huge-padded flex-1 display-flex-center"
+ >
+ <MeasurementLabel
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ centered={true}
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={
+ Array [
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "bugs",
+ "key": "bugs",
+ "name": "Bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_bugs",
+ "key": "new_bugs",
+ "name": "New_bugs",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ ]
+ }
+ type="DUPLICATION"
+ useDiffMetric={true}
+ />
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly if there is no new code measures 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__measures-panel"
+>
+ <h2
+ className="overview-panel-title"
+ >
+ overview.measures
+ </h2>
+ <BoxedTabs
+ onSelect={[Function]}
+ selected={0}
+ tabs={
+ Array [
+ Object {
+ "key": 0,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ >
+ overview.new_code
+ </span>
+ </div>,
+ },
+ Object {
+ "key": 1,
+ "label": <div
+ className="text-left overview-measures-tab"
+ >
+ <span
+ className="text-bold"
+ style={
+ Object {
+ "position": "absolute",
+ "top": 16,
+ }
+ }
+ >
+ overview.overall_code
+ </span>
+ </div>,
+ },
+ ]
+ }
+ />
+ <div
+ className="overview-panel-content flex-1 bordered"
+ >
+ <div
+ className="display-flex-center display-flex-justify-center"
+ style={
+ Object {
+ "height": 500,
+ }
+ }
+ >
+ <img
+ alt=""
+ className="spacer-right"
+ height={52}
+ src="/images/source-code.svg"
+ />
+ <div
+ className="big-spacer-left text-muted"
+ style={
+ Object {
+ "maxWidth": 500,
+ }
+ }
+ >
+ <p
+ className="spacer-bottom big-spacer-top big"
+ >
+ overview.measures.empty_explanation
+ </p>
+ <p>
+ <FormattedMessage
+ defaultMessage="overview.measures.empty_link"
+ id="overview.measures.empty_link"
+ values={
+ Object {
+ "learn_more_link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/documentation/user-guide/clean-as-you-code/"
+ >
+ learn_more
+ </Link>,
+ }
+ }
+ />
+ </p>
+ </div>
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ProjectLeakPeriodInfo-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ProjectLeakPeriodInfo-test.tsx.snap
new file mode 100644
index 00000000000..dacc2a60a94
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/ProjectLeakPeriodInfo-test.tsx.snap
@@ -0,0 +1,115 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render a more precise date 1`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.previous_version_only_date
+ </div>
+ <DateFromNow
+ date={2018-08-16T22:00:00.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
+
+exports[`should render correctly for "manual_baseline" 1`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.manual_baseline.formattedTime.2019-04-23T02:12:32+0100
+ </div>
+ <DateFromNow
+ date={2019-04-23T01:12:32.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
+
+exports[`should render correctly for "manual_baseline" 2`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.manual_baseline.1.1.2
+ </div>
+ <DateFromNow
+ date={2019-04-23T01:12:32.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
+
+exports[`should render correctly for "previous_analysis" 1`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.previous_analysis.
+ </div>
+ <DateFromNow
+ date={2019-04-23T01:12:32.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
+
+exports[`should render correctly for "previous_version" 1`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.previous_version_only_date
+ </div>
+ <DateFromNow
+ date={2019-04-23T01:12:32.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
+
+exports[`should render correctly for 10 days 1`] = `
+<div
+ className="note spacer-top"
+>
+ overview.period.days.10
+
+</div>
+`;
+
+exports[`should render correctly for a specific date 1`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.date.formatted.2013-01-01
+ </div>
+ <DateFromNow
+ date={2019-04-23T01:12:32.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
+
+exports[`should render correctly for a specific version 1`] = `
+<Fragment>
+ <div
+ className="note spacer-top"
+ >
+ overview.period.version.0.1
+ </div>
+ <DateFromNow
+ date={2019-04-23T01:12:32.000Z}
+ >
+ <Component />
+ </DateFromNow>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanel-test.tsx.snap
new file mode 100644
index 00000000000..3940bb8d720
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanel-test.tsx.snap
@@ -0,0 +1,625 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly for applications 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__quality-gate-panel"
+>
+ <h2
+ className="overview-panel-title display-inline-flex-center"
+ >
+ overview.quality_gate
+
+ <DocTooltip
+ className="little-spacer-left"
+ doc={Promise {}}
+ />
+ </h2>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="overview-quality-gate-badge-large failed"
+ >
+ <h3
+ className="big-spacer-bottom huge"
+ >
+ metric.level.ERROR
+ </h3>
+ <span
+ className="small"
+ >
+ overview.X_conditions_failed.3
+ </span>
+ </div>
+ <div
+ data-test="overview__quality-gate-conditions"
+ >
+ <Memo(QualityGatePanelSection)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ key="foo"
+ qgStatus={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "foo",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ />
+ <Memo(QualityGatePanelSection)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ key="foo"
+ qgStatus={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "foo",
+ "op": "GT",
+ },
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "new_code_smells",
+ "key": "new_code_smells",
+ "name": "New_code_smells",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_code_smells",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ />
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for applications 2`] = `
+<div
+ className="overview-panel"
+ data-test="overview__quality-gate-panel"
+>
+ <h2
+ className="overview-panel-title display-inline-flex-center"
+ >
+ overview.quality_gate
+
+ <DocTooltip
+ className="little-spacer-left"
+ doc={Promise {}}
+ />
+ </h2>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="overview-quality-gate-badge-large failed"
+ >
+ <h3
+ className="big-spacer-bottom huge"
+ >
+ metric.level.ERROR
+ </h3>
+ <span
+ className="small"
+ >
+ overview.X_conditions_failed.1
+ </span>
+ </div>
+ <div
+ data-test="overview__quality-gate-conditions"
+ >
+ <Memo(QualityGatePanelSection)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ key="foo"
+ qgStatus={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "foo",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ />
+ <Memo(QualityGatePanelSection)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ key="foo"
+ qgStatus={
+ Object {
+ "failedConditions": Array [],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "OK",
+ }
+ }
+ />
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for projects 1`] = `
+<div
+ className="overview-panel"
+ data-test="overview__quality-gate-panel"
+>
+ <h2
+ className="overview-panel-title display-inline-flex-center"
+ >
+ overview.quality_gate
+
+ <DocTooltip
+ className="little-spacer-left"
+ doc={Promise {}}
+ />
+ </h2>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="overview-quality-gate-badge-large failed"
+ >
+ <h3
+ className="big-spacer-bottom huge"
+ >
+ metric.level.ERROR
+ </h3>
+ <span
+ className="small"
+ >
+ overview.X_conditions_failed.1
+ </span>
+ </div>
+ <div
+ data-test="overview__quality-gate-conditions"
+ >
+ <Memo(QualityGatePanelSection)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ key="foo"
+ qgStatus={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "foo",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ />
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for projects 2`] = `
+<div
+ className="overview-panel"
+ data-test="overview__quality-gate-panel"
+>
+ <h2
+ className="overview-panel-title display-inline-flex-center"
+ >
+ overview.quality_gate
+
+ <DocTooltip
+ className="little-spacer-left"
+ doc={Promise {}}
+ />
+ </h2>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="overview-quality-gate-badge-large success"
+ >
+ <h3
+ className="big-spacer-bottom huge"
+ >
+ metric.level.OK
+ </h3>
+ <span
+ className="small"
+ >
+ overview.quality_gate_all_conditions_passed
+ </span>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly for projects 3`] = `
+<div
+ className="overview-panel"
+ data-test="overview__quality-gate-panel"
+>
+ <h2
+ className="overview-panel-title display-inline-flex-center"
+ >
+ overview.quality_gate
+
+ <DocTooltip
+ className="little-spacer-left"
+ doc={Promise {}}
+ />
+ </h2>
+ <Alert
+ className="big-spacer-bottom"
+ display="inline"
+ variant="info"
+ >
+ <span
+ className="text-middle"
+ >
+ overview.quality_gate.ignored_conditions
+ </span>
+ <HelpTooltip
+ className="spacer-left"
+ overlay="overview.quality_gate.ignored_conditions.tooltip"
+ />
+ </Alert>
+ <div
+ className="overview-panel-content"
+ >
+ <div
+ className="overview-quality-gate-badge-large failed"
+ >
+ <h3
+ className="big-spacer-bottom huge"
+ >
+ metric.level.ERROR
+ </h3>
+ <span
+ className="small"
+ >
+ overview.X_conditions_failed.1
+ </span>
+ </div>
+ <div
+ data-test="overview__quality-gate-conditions"
+ >
+ <Memo(QualityGatePanelSection)
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ key="foo"
+ qgStatus={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "foo",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": true,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ />
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanelSection-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanelSection-test.tsx.snap
new file mode 100644
index 00000000000..23dfdaf13b7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/QualityGatePanelSection-test.tsx.snap
@@ -0,0 +1,436 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="overview-quality-gate-conditions"
+>
+ <h4
+ className="overview-quality-gate-conditions-section-title"
+ >
+ quality_gates.conditions.new_code
+ </h4>
+ <QualityGateConditions
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ },
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ failedConditions={
+ Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ },
+ ]
+ }
+ />
+ <h4
+ className="overview-quality-gate-conditions-section-title"
+ >
+ quality_gates.conditions.overall_code
+ </h4>
+ <QualityGateConditions
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ },
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ failedConditions={
+ Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ },
+ ]
+ }
+ />
+</div>
+`;
+
+exports[`should render correctly 2`] = `
+<div
+ className="overview-quality-gate-conditions"
+>
+ <h3
+ className="overview-quality-gate-conditions-project-name"
+ >
+ Foo
+ </h3>
+ <h4
+ className="overview-quality-gate-conditions-section-title"
+ >
+ quality_gates.conditions.new_code
+ </h4>
+ <QualityGateConditions
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ },
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ failedConditions={
+ Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ },
+ ]
+ }
+ />
+ <h4
+ className="overview-quality-gate-conditions-section-title"
+ >
+ quality_gates.conditions.overall_code
+ </h4>
+ <QualityGateConditions
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ }
+ }
+ component={
+ Object {
+ "failedConditions": Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ },
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "new_bugs",
+ "op": "GT",
+ },
+ ],
+ "ignoredConditions": false,
+ "key": "foo",
+ "name": "Foo",
+ "status": "ERROR",
+ }
+ }
+ failedConditions={
+ Array [
+ Object {
+ "actual": "10",
+ "error": "0",
+ "level": "ERROR",
+ "measure": Object {
+ "bestValue": true,
+ "leak": "1",
+ "metric": Object {
+ "id": "coverage",
+ "key": "coverage",
+ "name": "Coverage",
+ "type": "PERCENT",
+ },
+ "periods": Array [
+ Object {
+ "bestValue": true,
+ "index": 1,
+ "value": "1.0",
+ },
+ ],
+ "value": "1.0",
+ },
+ "metric": "bugs",
+ "op": "GT",
+ },
+ ]
+ }
+ />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
index 3a25fa68865..7790272f2e4 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx
@@ -18,19 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Helmet } from 'react-helmet-async';
import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
-import { getBaseUrl, getPathUrlAsString } from 'sonar-ui-common/helpers/urls';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import { isPullRequest } from '../../../helpers/branch-like';
-import { isSonarCloud } from '../../../helpers/system';
-import { getProjectUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
-import OverviewApp from './OverviewApp';
+import BranchOverview from '../branches/BranchOverview';
const EmptyOverview = lazyLoad(() => import('./EmptyOverview'));
-const ReviewApp = lazyLoad(() => import('../pullRequests/ReviewApp'));
+const PullRequestOverview = lazyLoad(() => import('../pullRequests/PullRequestOverview'));
interface Props {
branchLike?: BranchLike;
@@ -38,7 +34,6 @@ interface Props {
component: T.Component;
isInProgress?: boolean;
isPending?: boolean;
- onComponentChange: (changes: Partial<T.Component>) => void;
router: Pick<Router, 'replace'>;
}
@@ -67,19 +62,10 @@ export class App extends React.PureComponent<Props> {
return (
<>
- {isSonarCloud() && (
- <Helmet>
- <link
- href={getBaseUrl() + getPathUrlAsString(getProjectUrl(component.key))}
- rel="canonical"
- />
- </Helmet>
- )}
-
{isPullRequest(branchLike) ? (
<>
<Suggestions suggestions="pull_requests" />
- <ReviewApp branchLike={branchLike} component={component} />
+ <PullRequestOverview branchLike={branchLike} component={component} />
</>
) : (
<>
@@ -91,16 +77,9 @@ export class App extends React.PureComponent<Props> {
branchLikes={branchLikes}
component={component}
hasAnalyses={this.props.isPending || this.props.isInProgress}
- onComponentChange={this.props.onComponentChange}
/>
) : (
- <OverviewApp
- branchLike={branchLike}
- component={component}
- isInProgress={this.props.isInProgress}
- isPending={this.props.isPending}
- onComponentChange={this.props.onComponentChange}
- />
+ <BranchOverview branchLike={branchLike} component={component} />
)}
</>
)}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
index eb78fc506ca..970ee8560a6 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
@@ -22,109 +22,71 @@ import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { isBranch, isMainBranch } from '../../../helpers/branch-like';
-import { isSonarCloud } from '../../../helpers/system';
+import { getBranchLikeDisplayName, isBranch, isMainBranch } from '../../../helpers/branch-like';
import { isLoggedIn } from '../../../helpers/users';
import { getCurrentUser, Store } from '../../../store/rootReducer';
import { BranchLike } from '../../../types/branch-like';
import AnalyzeTutorial from '../../tutorials/analyzeProject/AnalyzeTutorial';
-import AnalyzeTutorialSonarCloud from '../../tutorials/analyzeProject/AnalyzeTutorialSonarCloud';
-import MetaContainer from '../meta/MetaContainer';
-interface OwnProps {
+interface Props {
branchLike?: BranchLike;
branchLikes: BranchLike[];
component: T.Component;
+ currentUser: T.CurrentUser;
hasAnalyses?: boolean;
- onComponentChange: (changes: {}) => void;
}
-interface StateProps {
- currentUser: T.CurrentUser;
-}
+export function EmptyOverview(props: Props) {
+ const { branchLike, branchLikes, component, currentUser, hasAnalyses } = props;
-type Props = OwnProps & StateProps;
+ if (!isBranch(branchLike)) {
+ return null;
+ }
-export function EmptyOverview({
- branchLike,
- branchLikes,
- component,
- currentUser,
- hasAnalyses,
- onComponentChange
-}: Props) {
const hasBranches = branchLikes.length > 1;
const hasBadBranchConfig =
branchLikes.length > 2 ||
- (branchLikes.length === 2 &&
- branchLikes.some(branch => isBranch(branch) && !isMainBranch(branchLike)));
- return (
- <div className="page page-limited">
- <div className="overview page-with-sidebar">
- <div className="overview-main page-main">
- {isLoggedIn(currentUser) && isMainBranch(branchLike) ? (
- <>
- {hasBranches && (
- <WarningMessage
- branchLike={branchLike}
- message={
- hasBadBranchConfig
- ? translate('provisioning.no_analysis_on_main_branch.bad_configuration')
- : translate('provisioning.no_analysis_on_main_branch')
- }
- />
- )}
- {!hasBranches &&
- !hasAnalyses &&
- (isSonarCloud() ? (
- <AnalyzeTutorialSonarCloud component={component} currentUser={currentUser} />
- ) : (
- <AnalyzeTutorial component={component} currentUser={currentUser} />
- ))}
- </>
- ) : (
- <WarningMessage
- branchLike={branchLike}
- message={translate('provisioning.no_analysis_on_main_branch')}
- />
- )}
- </div>
+ (branchLikes.length === 2 && branchLikes.some(branch => isBranch(branch)));
- {!isSonarCloud() && (
- <div className="overview-sidebar page-sidebar-fixed">
- <MetaContainer
- branchLike={branchLike}
- component={component}
- onComponentChange={onComponentChange}
- />
- </div>
- )}
- </div>
- </div>
- );
-}
+ const showWarning = isMainBranch(branchLike) && hasBranches;
+ const showTutorial =
+ isMainBranch(branchLike) && !hasBranches && !hasAnalyses && component.qualifier !== 'APP';
-export function WarningMessage({
- branchLike,
- message
-}: {
- branchLike?: BranchLike;
- message: string;
-}) {
- if (!isBranch(branchLike)) {
- return null;
- }
- return (
- <Alert variant="warning">
+ let warning;
+ if (isLoggedIn(currentUser) && showWarning && hasBadBranchConfig) {
+ warning = (
<FormattedMessage
- defaultMessage={message}
- id={message}
+ defaultMessage={translate('provisioning.no_analysis_on_main_branch.bad_configuration')}
+ id="provisioning.no_analysis_on_main_branch.bad_configuration"
values={{
- branchName: branchLike.name,
+ branchName: getBranchLikeDisplayName(branchLike),
branchType: translate('branches.main_branch')
}}
/>
- </Alert>
+ );
+ } else {
+ warning = (
+ <FormattedMessage
+ defaultMessage={translate('provisioning.no_analysis_on_main_branch')}
+ id="provisioning.no_analysis_on_main_branch"
+ values={{
+ branchName: getBranchLikeDisplayName(branchLike)
+ }}
+ />
+ );
+ }
+
+ return (
+ <div className="page page-limited">
+ {isLoggedIn(currentUser) ? (
+ <>
+ {showWarning && <Alert variant="warning">{warning}</Alert>}
+ {showTutorial && <AnalyzeTutorial component={component} currentUser={currentUser} />}
+ </>
+ ) : (
+ <Alert variant="warning">{warning}</Alert>
+ )}
+ </div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
deleted file mode 100644
index 4a9ae70d613..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
+++ /dev/null
@@ -1,341 +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 { uniq } from 'lodash';
-import * as React from 'react';
-import { connect } from 'react-redux';
-import { Alert } from 'sonar-ui-common/components/ui/Alert';
-import { parseDate } from 'sonar-ui-common/helpers/dates';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { isDiffMetric } from 'sonar-ui-common/helpers/measures';
-import { getMeasuresAndMeta } from '../../../api/measures';
-import { getAllTimeMachineData } from '../../../api/time-machine';
-import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
-import {
- getBranchLikeDisplayName,
- getBranchLikeQuery,
- isBranch,
- isMainBranch,
- isSameBranchLike
-} from '../../../helpers/branch-like';
-import { enhanceMeasuresWithMetrics } from '../../../helpers/measures';
-import { getLeakPeriod } from '../../../helpers/periods';
-import { fetchMetrics } from '../../../store/rootActions';
-import { getMetrics, Store } from '../../../store/rootReducer';
-import { BranchLike } from '../../../types/branch-like';
-import { ComponentQualifier } from '../../../types/component';
-import {
- DEFAULT_GRAPH,
- getDisplayedHistoryMetrics,
- getProjectActivityGraph
-} from '../../projectActivity/utils';
-import Bugs from '../main/Bugs';
-import CodeSmells from '../main/CodeSmells';
-import Coverage from '../main/Coverage';
-import Duplications from '../main/Duplications';
-import VulnerabilitiesAndHotspots from '../main/VulnerabilitiesAndHotspots';
-import MetaContainer from '../meta/MetaContainer';
-import ApplicationQualityGate from '../qualityGate/ApplicationQualityGate';
-import QualityGate from '../qualityGate/QualityGate';
-import '../styles.css';
-import { HISTORY_METRICS_LIST, METRICS } from '../utils';
-
-interface Props {
- branchLike?: BranchLike;
- component: T.Component;
- isInProgress?: boolean;
- isPending?: boolean;
- fetchMetrics: () => void;
- onComponentChange: (changes: {}) => void;
- metrics: T.Dict<T.Metric>;
-}
-
-interface State {
- history?: {
- [metric: string]: Array<{ date: Date; value?: string }>;
- };
- historyStartDate?: Date;
- loading: boolean;
- measures: T.MeasureEnhanced[];
- periods?: T.Period[];
-}
-
-export class OverviewApp extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: true, measures: [] };
-
- componentDidMount() {
- this.mounted = true;
- this.props.fetchMetrics();
- this.loadMeasures().then(this.loadHistory, () => {});
- }
-
- componentDidUpdate(prevProps: Props) {
- if (
- this.props.component.key !== prevProps.component.key ||
- !isSameBranchLike(this.props.branchLike, prevProps.branchLike)
- ) {
- this.loadMeasures().then(this.loadHistory, () => {});
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- getApplicationLeakPeriod = () => {
- return this.state.measures.find(measure => measure.metric.key === 'new_bugs')
- ? ({ index: 1 } as T.Period)
- : undefined;
- };
-
- isEmpty = () => {
- const { measures } = this.state;
- return (
- measures === undefined ||
- measures.find(measure => ['lines', 'new_lines'].includes(measure.metric.key)) === undefined
- );
- };
-
- loadHistory = () => {
- const { branchLike, component } = this.props;
-
- const { graph, customGraphs } = getProjectActivityGraph(component.key);
- let graphMetrics = getDisplayedHistoryMetrics(graph, customGraphs);
- if (!graphMetrics || graphMetrics.length <= 0) {
- graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []);
- }
-
- const metrics = uniq(HISTORY_METRICS_LIST.concat(graphMetrics));
- return getAllTimeMachineData({
- ...getBranchLikeQuery(branchLike),
- component: component.key,
- metrics: metrics.join()
- }).then(r => {
- if (this.mounted) {
- const history: T.Dict<Array<{ date: Date; value?: string }>> = {};
- r.measures.forEach(measure => {
- const measureHistory = measure.history.map(analysis => ({
- date: parseDate(analysis.date),
- value: analysis.value
- }));
- history[measure.metric] = measureHistory;
- });
- const historyStartDate = history[HISTORY_METRICS_LIST[0]][0].date;
- this.setState({ history, historyStartDate });
- }
- });
- };
-
- loadMeasures = () => {
- const { branchLike, component } = this.props;
- this.setState({ loading: true });
-
- return getMeasuresAndMeta(component.key, METRICS, {
- additionalFields: 'metrics,periods',
- ...getBranchLikeQuery(branchLike)
- }).then(
- ({ component, metrics, periods }) => {
- if (this.mounted && metrics && component.measures) {
- this.setState({
- loading: false,
- measures: enhanceMeasuresWithMetrics(component.measures, metrics),
- periods
- });
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- };
-
- renderEmpty = () => {
- const { branchLike, component } = this.props;
- const isApp = component.qualifier === ComponentQualifier.Application;
-
- /* eslint-disable no-lonely-if */
- // - Is App
- // - No measures, OR measures, but no projects => empty
- // - Else => no lines of code
- // - Else
- // - No measures => empty
- // - Main branch?
- // - LLB?
- // - No branch info?
- // - Measures, but no ncloc (checked in isEmpty()) => no lines of code
- // - Main branch?
- // - LLB?
- // - No branch info?
- let title;
- if (isApp) {
- if (
- this.state.measures === undefined ||
- this.state.measures.find(measure => measure.metric.key === 'projects') === undefined
- ) {
- title = translate('portfolio.app.empty');
- } else {
- title = translate('portfolio.app.no_lines_of_code');
- }
- } else {
- if (this.state.measures === undefined || this.state.measures.length === 0) {
- if (isMainBranch(branchLike)) {
- title = translate('overview.project.main_branch_empty');
- } else if (branchLike !== undefined) {
- title = translateWithParameters(
- 'overview.project.branch_X_empty',
- getBranchLikeDisplayName(branchLike)
- );
- } else {
- title = translate('overview.project.empty');
- }
- } else {
- if (isMainBranch(branchLike)) {
- title = translate('overview.project.main_branch_no_lines_of_code');
- } else if (branchLike !== undefined) {
- title = translateWithParameters(
- 'overview.project.branch_X_no_lines_of_code',
- getBranchLikeDisplayName(branchLike)
- );
- } else {
- title = translate('overview.project.no_lines_of_code');
- }
- }
- }
- /* eslint-enable no-lonely-if */
- return (
- <div className="overview-main page-main">
- {this.renderNewAnalysisRequired()}
- <h3>{title}</h3>
- </div>
- );
- };
-
- renderNewAnalysisRequired = () => {
- const { component, isInProgress, isPending } = this.props;
- const { measures, periods } = this.state;
- const leakPeriod = getLeakPeriod(periods);
-
- if (
- !isInProgress &&
- !isPending &&
- component.qualifier !== ComponentQualifier.Application &&
- measures.some(m => isDiffMetric(m.metric.key)) &&
- leakPeriod === undefined
- ) {
- return (
- <Alert className="big-spacer-bottom" display="inline" variant="warning">
- {translate('overview.project.branch_needs_new_analysis')}
- </Alert>
- );
- } else {
- return null;
- }
- };
-
- renderLoading = () => {
- return (
- <div className="text-center">
- <i className="spinner spacer" />
- </div>
- );
- };
-
- renderMain = () => {
- const { branchLike, component } = this.props;
- const { periods, measures, history, historyStartDate } = this.state;
- const leakPeriod =
- component.qualifier === ComponentQualifier.Application
- ? this.getApplicationLeakPeriod()
- : getLeakPeriod(periods);
- const domainProps = {
- branchLike,
- component,
- measures,
- leakPeriod,
- history,
- historyStartDate
- };
-
- if (this.isEmpty()) {
- return this.renderEmpty();
- }
-
- return (
- <div className="overview-main page-main">
- {this.renderNewAnalysisRequired()}
-
- {component.qualifier === ComponentQualifier.Application ? (
- <ApplicationQualityGate
- branch={isBranch(branchLike) && !isMainBranch(branchLike) ? branchLike : undefined}
- component={component}
- />
- ) : (
- <QualityGate branchLike={branchLike} component={component} measures={measures} />
- )}
-
- <div className="overview-domains-list">
- <Bugs {...domainProps} />
- <VulnerabilitiesAndHotspots {...domainProps} />
- <CodeSmells {...domainProps} />
- <Coverage {...domainProps} />
- <Duplications {...domainProps} />
- </div>
- </div>
- );
- };
-
- render() {
- const { branchLike, component } = this.props;
- const { loading, measures, history } = this.state;
-
- if (loading) {
- return this.renderLoading();
- }
-
- return (
- <div className="page page-limited">
- <div className="overview page-with-sidebar">
- <A11ySkipTarget anchor="overview_main" />
-
- {this.renderMain()}
-
- <div className="overview-sidebar page-sidebar-fixed">
- <MetaContainer
- branchLike={branchLike}
- component={component}
- history={history}
- measures={measures}
- metrics={this.props.metrics}
- onComponentChange={this.props.onComponentChange}
- />
- </div>
- </div>
- </div>
- );
- }
-}
-
-const mapDispatchToProps = { fetchMetrics };
-
-const mapStateToProps = (state: Store) => ({ metrics: getMetrics(state) });
-
-export default connect(mapStateToProps, mapDispatchToProps)(OverviewApp);
diff --git a/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx b/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx
deleted file mode 100644
index e6cf42920f9..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx
+++ /dev/null
@@ -1,69 +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 { max } from 'd3-array';
-import * as React from 'react';
-import LineChart from 'sonar-ui-common/components/charts/LineChart';
-
-const HEIGHT = 80;
-
-interface Props {
- history: Array<{ date: Date; value?: string }>;
- before?: Date;
- after?: Date;
-}
-
-export default class Timeline extends React.PureComponent<Props> {
- filterSnapshots() {
- const { history, before, after } = this.props;
-
- return history.filter(s => {
- const matchBefore = !before || s.date <= before;
- const matchAfter = !after || s.date >= after;
- return matchBefore && matchAfter;
- });
- }
-
- render() {
- const snapshots = this.filterSnapshots();
-
- if (snapshots.length < 2) {
- return null;
- }
-
- const data = snapshots.map((snapshot, index) => {
- return { x: index, y: snapshot.value !== undefined ? Number(snapshot.value) : undefined };
- });
- const domain = [
- 0,
- max(this.props.history, d => (d.value !== undefined ? parseFloat(d.value) : 0))
- ] as [number, number];
- return (
- <LineChart
- data={data}
- displayBackdrop={true}
- displayPoints={false}
- displayVerticalGrid={false}
- domain={domain}
- height={HEIGHT}
- padding={[0, 0, 0, 0]}
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx
index 49f38f54c7a..3d2050e8bb8 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/App-test.tsx
@@ -20,6 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { isSonarCloud } from '../../../../helpers/system';
+import BranchOverview from '../../branches/BranchOverview';
import { App } from '../App';
jest.mock('../../../../helpers/system', () => ({ isSonarCloud: jest.fn() }));
@@ -39,22 +40,16 @@ beforeEach(() => {
(isSonarCloud as jest.Mock<any>).mockReturnValue(false);
});
-it('should render OverviewApp', () => {
+it('should render BranchOverview', () => {
expect(
getWrapper()
- .find('Connect(OverviewApp)')
+ .find(BranchOverview)
.exists()
).toBeTruthy();
});
function getWrapper(props = {}) {
return shallow(
- <App
- branchLikes={[]}
- component={component}
- onComponentChange={jest.fn()}
- router={{ replace: jest.fn() }}
- {...props}
- />
+ <App branchLikes={[]} component={component} router={{ replace: jest.fn() }} {...props} />
);
}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx
index 385df2e3933..655ff347aee 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx
@@ -19,76 +19,41 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockMainBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
-import { EmptyOverview, WarningMessage } from '../EmptyOverview';
-
-const branch = mockMainBranch();
-const component = mockComponent({ version: '0.0.1' });
-const LoggedInUser = mockLoggedInUser();
+import { mockBranch, mockMainBranch, mockPullRequest } from '../../../../helpers/mocks/branch-like';
+import { mockComponent, mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
+import { EmptyOverview } from '../EmptyOverview';
it('renders correctly', () => {
- expect(
- shallow(
- <EmptyOverview
- branchLike={branch}
- branchLikes={[branch]}
- component={component}
- currentUser={LoggedInUser}
- onComponentChange={jest.fn()}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
+ expect(shallowRender({ hasAnalyses: true })).toMatchSnapshot();
+ expect(shallowRender({ currentUser: mockCurrentUser() })).toMatchSnapshot();
});
it('should render another message when there are branches', () => {
+ expect(shallowRender({ branchLikes: [mockMainBranch(), mockBranch()] })).toMatchSnapshot();
expect(
- shallow(
- <EmptyOverview
- branchLike={branch}
- branchLikes={[branch, branch]}
- component={component}
- currentUser={LoggedInUser}
- onComponentChange={jest.fn()}
- />
- )
- ).toMatchSnapshot();
- expect(
- shallow(
- <EmptyOverview
- branchLike={branch}
- branchLikes={[branch, branch, branch]}
- component={component}
- currentUser={LoggedInUser}
- onComponentChange={jest.fn()}
- />
- )
+ shallowRender({
+ branchLikes: [mockMainBranch(), mockBranch(), mockBranch({ name: 'branch-7.8' })]
+ })
).toMatchSnapshot();
});
-it('should not render the tutorial', () => {
- expect(
- shallow(
- <EmptyOverview
- branchLike={branch}
- branchLikes={[branch]}
- component={component}
- currentUser={LoggedInUser}
- hasAnalyses={true}
- onComponentChange={jest.fn()}
- />
- )
- ).toMatchSnapshot();
+it('should not render warning message for pull requests', () => {
+ expect(shallowRender({ branchLike: mockPullRequest() }).type()).toBeNull();
});
-it('should render warning message', () => {
- expect(shallow(<WarningMessage branchLike={branch} message="foo" />)).toMatchSnapshot();
+it('should not render the tutorial for applications', () => {
+ expect(shallowRender({ component: mockComponent({ qualifier: 'APP' }) })).toMatchSnapshot();
});
-it('should not render warning message', () => {
- expect(
- shallow(<WarningMessage branchLike={mockPullRequest()} message="foo" />)
- .find('FormattedMessage')
- .exists()
- ).toBeFalsy();
-});
+function shallowRender(props = {}) {
+ return shallow(
+ <EmptyOverview
+ branchLike={mockMainBranch()}
+ branchLikes={[mockMainBranch()]}
+ component={mockComponent({ version: '0.0.1' })}
+ currentUser={mockLoggedInUser()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx
deleted file mode 100644
index e12b636ad18..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/OverviewApp-test.tsx
+++ /dev/null
@@ -1,179 +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 { getMeasuresAndMeta } from '../../../../api/measures';
-import { getAllTimeMachineData } from '../../../../api/time-machine';
-import { mockBranch, mockMainBranch } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockMeasure, mockMetric } from '../../../../helpers/testMocks';
-import { OverviewApp } from '../OverviewApp';
-
-jest.mock('../../../../api/measures', () => {
- const { mockMeasure, mockMetric } = getMockHelpers();
- return {
- getMeasuresAndMeta: jest.fn().mockResolvedValue({
- component: {
- measures: [mockMeasure({ metric: 'lines' }), mockMeasure({ metric: 'coverage' })],
- name: 'foo'
- },
- metrics: [mockMetric({ key: 'lines' }), mockMetric()]
- })
- };
-});
-
-jest.mock('sonar-ui-common/helpers/dates', () => ({
- parseDate: jest.fn(date => date)
-}));
-
-jest.mock('../../../../api/time-machine', () => ({
- getAllTimeMachineData: jest.fn().mockResolvedValue({
- measures: [
- { metric: 'bugs', history: [{ date: '2019-01-05', value: '2.0' }] },
- { metric: 'vulnerabilities', history: [{ date: '2019-01-05', value: '0' }] },
- { metric: 'sqale_index', history: [{ date: '2019-01-01', value: '1.0' }] },
- { metric: 'duplicated_lines_density', history: [{ date: '2019-01-02', value: '1.0' }] },
- { metric: 'lines', history: [{ date: '2019-01-03', value: '10000' }] },
- { metric: 'coverage', history: [{ date: '2019-01-04', value: '95.5' }] }
- ]
- })
-}));
-
-it('should render correctly', async () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
-
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
- expect(getMeasuresAndMeta).toBeCalled();
- expect(getAllTimeMachineData).toBeCalled();
-});
-
-it('should show the correct message if the application is empty or has no lines of code', async () => {
- (getMeasuresAndMeta as jest.Mock).mockResolvedValue({
- component: {
- measures: [mockMeasure({ metric: 'projects' })],
- name: 'foo'
- },
- metrics: [mockMetric({ key: 'projects' })]
- });
-
- const wrapper = shallowRender({
- component: mockComponent({ key: 'foo', name: 'foo', qualifier: 'APP' })
- });
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('portfolio.app.no_lines_of_code');
-
- (getMeasuresAndMeta as jest.Mock).mockResolvedValue({
- component: {
- measures: [],
- name: 'bar'
- },
- metrics: []
- });
- wrapper.setProps({ component: mockComponent({ key: 'bar', name: 'bar', qualifier: 'APP' }) });
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('portfolio.app.empty');
-});
-
-it('should show the correct message if the project is empty', async () => {
- (getMeasuresAndMeta as jest.Mock).mockResolvedValue({
- component: {
- measures: [],
- name: 'foo'
- },
- metrics: []
- });
- const wrapper = shallowRender({ branchLike: mockMainBranch() });
-
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('overview.project.main_branch_empty');
-
- wrapper.setProps({ branchLike: mockBranch({ name: 'branch-foo' }) });
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('overview.project.branch_X_empty.branch-foo');
-
- wrapper.setProps({ branchLike: undefined });
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('overview.project.empty');
-});
-
-it('should show the correct message if the project has no lines of code', async () => {
- (getMeasuresAndMeta as jest.Mock).mockResolvedValue({
- component: {
- measures: [mockMeasure({ metric: 'bugs' })],
- name: 'foo'
- },
- metrics: [mockMetric({ key: 'bugs' })]
- });
- const wrapper = shallowRender({ branchLike: mockMainBranch() });
-
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('overview.project.main_branch_no_lines_of_code');
-
- wrapper.setProps({ branchLike: mockBranch({ name: 'branch-foo' }) });
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('overview.project.branch_X_no_lines_of_code.branch-foo');
-
- wrapper.setProps({ branchLike: undefined });
- await waitAndUpdate(wrapper);
- expect(wrapper.find('h3').text()).toBe('overview.project.no_lines_of_code');
-});
-
-it('should show a warning if the project has new measures, but no period info', async () => {
- (getMeasuresAndMeta as jest.Mock).mockResolvedValue({
- component: {
- measures: [mockMeasure({ metric: 'bugs' }), mockMeasure({ metric: 'new_bugs' })],
- name: 'foo'
- },
- metrics: [mockMetric({ key: 'bugs' }), mockMetric({ key: 'new_bugs' })]
- });
-
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(
- wrapper
- .find('Alert')
- .dive()
- .text()
- ).toContain('overview.project.branch_needs_new_analysis');
-});
-
-function getMockHelpers() {
- // We use this little "force-requiring" instead of an import statement in
- // order to prevent a hoisting race condition while mocking. If we want to use
- // a mock helper in a Jest mock, we have to require it like this. Otherwise,
- // we get errors like:
- // ReferenceError: testMocks_1 is not defined
- return require.requireActual('../../../../helpers/testMocks');
-}
-
-function shallowRender(props: Partial<OverviewApp['props']> = {}) {
- return shallow(
- <OverviewApp
- branchLike={mockMainBranch()}
- component={mockComponent({ name: 'foo' })}
- fetchMetrics={jest.fn()}
- metrics={{ coverage: mockMetric() }}
- onComponentChange={jest.fn()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/Timeline-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/Timeline-test.tsx
deleted file mode 100644
index eadfbdc155d..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/Timeline-test.tsx
+++ /dev/null
@@ -1,72 +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 { parseDate } from 'sonar-ui-common/helpers/dates';
-import Timeline from '../Timeline';
-
-const range = parseDate('2017-05-01T00:00:00.000Z');
-const history = [
- { date: parseDate('2017-04-08T00:00:00.000Z'), value: '29.6' },
- { date: parseDate('2017-04-09T00:00:00.000Z'), value: '170.8' },
- { date: parseDate('2017-05-08T00:00:00.000Z'), value: '360' },
- { date: parseDate('2017-05-09T00:00:00.000Z'), value: '39' }
-];
-
-it('should render correctly with an "after" range', () => {
- expect(shallow(<Timeline after={range} history={history} />)).toMatchSnapshot();
-});
-
-it('should render correctly with a "before" range', () => {
- expect(shallow(<Timeline before={range} history={history} />)).toMatchSnapshot();
-});
-
-it('should have a correct domain with strings or numbers', () => {
- const date = parseDate('2017-05-08T00:00:00.000Z');
- const wrapper = shallow(<Timeline after={range} history={history} />);
- expect(wrapper.find('LineChart').prop('domain')).toEqual([0, 360]);
-
- wrapper.setProps({
- history: [
- { date, value: '360.33' },
- { date, value: '39.54' }
- ]
- });
- expect(wrapper.find('LineChart').prop('domain')).toEqual([0, 360.33]);
-
- wrapper.setProps({
- history: [
- { date, value: 360 },
- { date, value: 39 }
- ]
- });
- expect(wrapper.find('LineChart').prop('domain')).toEqual([0, 360]);
-});
-
-it('should not fail when a value is missing', () => {
- expect(
- shallow(
- <Timeline
- before={range}
- history={[{ date: parseDate('2017-04-07T00:00:00.000Z') }, ...history]}
- />
- )
- ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap
index 99f1d1577a4..204c5941596 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap
@@ -4,206 +4,94 @@ exports[`renders correctly 1`] = `
<div
className="page page-limited"
>
- <div
- className="overview page-with-sidebar"
- >
- <div
- className="overview-main page-main"
- >
- <AnalyzeTutorial
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- "version": "0.0.1",
- }
- }
- currentUser={
- Object {
- "groups": Array [],
- "isLoggedIn": true,
- "login": "luke",
- "name": "Skywalker",
- "scmAccounts": Array [],
- }
- }
- />
- </div>
- <div
- className="overview-sidebar page-sidebar-fixed"
- >
- <Connect(Meta)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
+ <AnalyzeTutorial
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- "version": "0.0.1",
- }
- }
- onComponentChange={[MockFunction]}
- />
- </div>
- </div>
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ "version": "0.0.1",
+ }
+ }
+ currentUser={
+ Object {
+ "groups": Array [],
+ "isLoggedIn": true,
+ "login": "luke",
+ "name": "Skywalker",
+ "scmAccounts": Array [],
+ }
+ }
+ />
</div>
`;
-exports[`should not render the tutorial 1`] = `
+exports[`renders correctly 2`] = `
+<div
+ className="page page-limited"
+/>
+`;
+
+exports[`renders correctly 3`] = `
<div
className="page page-limited"
>
- <div
- className="overview page-with-sidebar"
+ <Alert
+ variant="warning"
>
- <div
- className="overview-main page-main"
- />
- <div
- className="overview-sidebar page-sidebar-fixed"
- >
- <Connect(Meta)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- "version": "0.0.1",
- }
+ <FormattedMessage
+ defaultMessage="provisioning.no_analysis_on_main_branch"
+ id="provisioning.no_analysis_on_main_branch"
+ values={
+ Object {
+ "branchName": "master",
}
- onComponentChange={[MockFunction]}
- />
- </div>
- </div>
+ }
+ />
+ </Alert>
</div>
`;
+exports[`should not render the tutorial for applications 1`] = `
+<div
+ className="page page-limited"
+/>
+`;
+
exports[`should render another message when there are branches 1`] = `
<div
className="page page-limited"
>
- <div
- className="overview page-with-sidebar"
+ <Alert
+ variant="warning"
>
- <div
- className="overview-main page-main"
- >
- <WarningMessage
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- message="provisioning.no_analysis_on_main_branch"
- />
- </div>
- <div
- className="overview-sidebar page-sidebar-fixed"
- >
- <Connect(Meta)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
+ <FormattedMessage
+ defaultMessage="provisioning.no_analysis_on_main_branch.bad_configuration"
+ id="provisioning.no_analysis_on_main_branch.bad_configuration"
+ values={
+ Object {
+ "branchName": "master",
+ "branchType": "branches.main_branch",
}
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- "version": "0.0.1",
- }
- }
- onComponentChange={[MockFunction]}
- />
- </div>
- </div>
+ }
+ />
+ </Alert>
</div>
`;
@@ -211,80 +99,19 @@ exports[`should render another message when there are branches 2`] = `
<div
className="page page-limited"
>
- <div
- className="overview page-with-sidebar"
+ <Alert
+ variant="warning"
>
- <div
- className="overview-main page-main"
- >
- <WarningMessage
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
+ <FormattedMessage
+ defaultMessage="provisioning.no_analysis_on_main_branch.bad_configuration"
+ id="provisioning.no_analysis_on_main_branch.bad_configuration"
+ values={
+ Object {
+ "branchName": "master",
+ "branchType": "branches.main_branch",
}
- message="provisioning.no_analysis_on_main_branch.bad_configuration"
- />
- </div>
- <div
- className="overview-sidebar page-sidebar-fixed"
- >
- <Connect(Meta)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- "version": "0.0.1",
- }
- }
- onComponentChange={[MockFunction]}
- />
- </div>
- </div>
-</div>
-`;
-
-exports[`should render warning message 1`] = `
-<Alert
- variant="warning"
->
- <FormattedMessage
- defaultMessage="foo"
- id="foo"
- values={
- Object {
- "branchName": "master",
- "branchType": "branches.main_branch",
}
- }
- />
-</Alert>
+ />
+ </Alert>
+</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap
deleted file mode 100644
index 17432f3a5e6..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/OverviewApp-test.tsx.snap
+++ /dev/null
@@ -1,790 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="text-center"
->
- <i
- className="spinner spacer"
- />
-</div>
-`;
-
-exports[`should render correctly 2`] = `
-<div
- className="page page-limited"
->
- <div
- className="overview page-with-sidebar"
- >
- <A11ySkipTarget
- anchor="overview_main"
- />
- <div
- className="overview-main page-main"
- >
- <QualityGate
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- />
- <div
- className="overview-domains-list"
- >
- <enhance(Bugs)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- history={
- Object {
- "bugs": Array [
- Object {
- "date": "2019-01-05",
- "value": "2.0",
- },
- ],
- "coverage": Array [
- Object {
- "date": "2019-01-04",
- "value": "95.5",
- },
- ],
- "duplicated_lines_density": Array [
- Object {
- "date": "2019-01-02",
- "value": "1.0",
- },
- ],
- "lines": Array [
- Object {
- "date": "2019-01-03",
- "value": "10000",
- },
- ],
- "sqale_index": Array [
- Object {
- "date": "2019-01-01",
- "value": "1.0",
- },
- ],
- "vulnerabilities": Array [
- Object {
- "date": "2019-01-05",
- "value": "0",
- },
- ],
- }
- }
- historyStartDate="2019-01-05"
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- />
- <enhance(VulnerabiltiesAndHotspots)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- history={
- Object {
- "bugs": Array [
- Object {
- "date": "2019-01-05",
- "value": "2.0",
- },
- ],
- "coverage": Array [
- Object {
- "date": "2019-01-04",
- "value": "95.5",
- },
- ],
- "duplicated_lines_density": Array [
- Object {
- "date": "2019-01-02",
- "value": "1.0",
- },
- ],
- "lines": Array [
- Object {
- "date": "2019-01-03",
- "value": "10000",
- },
- ],
- "sqale_index": Array [
- Object {
- "date": "2019-01-01",
- "value": "1.0",
- },
- ],
- "vulnerabilities": Array [
- Object {
- "date": "2019-01-05",
- "value": "0",
- },
- ],
- }
- }
- historyStartDate="2019-01-05"
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- />
- <enhance(CodeSmells)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- history={
- Object {
- "bugs": Array [
- Object {
- "date": "2019-01-05",
- "value": "2.0",
- },
- ],
- "coverage": Array [
- Object {
- "date": "2019-01-04",
- "value": "95.5",
- },
- ],
- "duplicated_lines_density": Array [
- Object {
- "date": "2019-01-02",
- "value": "1.0",
- },
- ],
- "lines": Array [
- Object {
- "date": "2019-01-03",
- "value": "10000",
- },
- ],
- "sqale_index": Array [
- Object {
- "date": "2019-01-01",
- "value": "1.0",
- },
- ],
- "vulnerabilities": Array [
- Object {
- "date": "2019-01-05",
- "value": "0",
- },
- ],
- }
- }
- historyStartDate="2019-01-05"
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- />
- <enhance(Coverage)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- history={
- Object {
- "bugs": Array [
- Object {
- "date": "2019-01-05",
- "value": "2.0",
- },
- ],
- "coverage": Array [
- Object {
- "date": "2019-01-04",
- "value": "95.5",
- },
- ],
- "duplicated_lines_density": Array [
- Object {
- "date": "2019-01-02",
- "value": "1.0",
- },
- ],
- "lines": Array [
- Object {
- "date": "2019-01-03",
- "value": "10000",
- },
- ],
- "sqale_index": Array [
- Object {
- "date": "2019-01-01",
- "value": "1.0",
- },
- ],
- "vulnerabilities": Array [
- Object {
- "date": "2019-01-05",
- "value": "0",
- },
- ],
- }
- }
- historyStartDate="2019-01-05"
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- />
- <enhance(Duplications)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- history={
- Object {
- "bugs": Array [
- Object {
- "date": "2019-01-05",
- "value": "2.0",
- },
- ],
- "coverage": Array [
- Object {
- "date": "2019-01-04",
- "value": "95.5",
- },
- ],
- "duplicated_lines_density": Array [
- Object {
- "date": "2019-01-02",
- "value": "1.0",
- },
- ],
- "lines": Array [
- Object {
- "date": "2019-01-03",
- "value": "10000",
- },
- ],
- "sqale_index": Array [
- Object {
- "date": "2019-01-01",
- "value": "1.0",
- },
- ],
- "vulnerabilities": Array [
- Object {
- "date": "2019-01-05",
- "value": "0",
- },
- ],
- }
- }
- historyStartDate="2019-01-05"
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- />
- </div>
- </div>
- <div
- className="overview-sidebar page-sidebar-fixed"
- >
- <Connect(Meta)
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "foo",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- history={
- Object {
- "bugs": Array [
- Object {
- "date": "2019-01-05",
- "value": "2.0",
- },
- ],
- "coverage": Array [
- Object {
- "date": "2019-01-04",
- "value": "95.5",
- },
- ],
- "duplicated_lines_density": Array [
- Object {
- "date": "2019-01-02",
- "value": "1.0",
- },
- ],
- "lines": Array [
- Object {
- "date": "2019-01-03",
- "value": "10000",
- },
- ],
- "sqale_index": Array [
- Object {
- "date": "2019-01-01",
- "value": "1.0",
- },
- ],
- "vulnerabilities": Array [
- Object {
- "date": "2019-01-05",
- "value": "0",
- },
- ],
- }
- }
- measures={
- Array [
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "lines",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- Object {
- "bestValue": true,
- "metric": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- "periods": Array [
- Object {
- "bestValue": true,
- "index": 1,
- "value": "1.0",
- },
- ],
- "value": "1.0",
- },
- ]
- }
- metrics={
- Object {
- "coverage": Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- }
- }
- onComponentChange={[MockFunction]}
- />
- </div>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.tsx.snap
deleted file mode 100644
index 710cbbf695a..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.tsx.snap
+++ /dev/null
@@ -1,110 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should not fail when a value is missing 1`] = `
-<LineChart
- data={
- Array [
- Object {
- "x": 0,
- "y": undefined,
- },
- Object {
- "x": 1,
- "y": 29.6,
- },
- Object {
- "x": 2,
- "y": 170.8,
- },
- ]
- }
- displayBackdrop={true}
- displayPoints={false}
- displayVerticalGrid={false}
- domain={
- Array [
- 0,
- 360,
- ]
- }
- height={80}
- padding={
- Array [
- 0,
- 0,
- 0,
- 0,
- ]
- }
-/>
-`;
-
-exports[`should render correctly with a "before" range 1`] = `
-<LineChart
- data={
- Array [
- Object {
- "x": 0,
- "y": 29.6,
- },
- Object {
- "x": 1,
- "y": 170.8,
- },
- ]
- }
- displayBackdrop={true}
- displayPoints={false}
- displayVerticalGrid={false}
- domain={
- Array [
- 0,
- 360,
- ]
- }
- height={80}
- padding={
- Array [
- 0,
- 0,
- 0,
- 0,
- ]
- }
-/>
-`;
-
-exports[`should render correctly with an "after" range 1`] = `
-<LineChart
- data={
- Array [
- Object {
- "x": 0,
- "y": 360,
- },
- Object {
- "x": 1,
- "y": 39,
- },
- ]
- }
- displayBackdrop={true}
- displayPoints={false}
- displayVerticalGrid={false}
- domain={
- Array [
- 0,
- 360,
- ]
- }
- height={80}
- padding={
- Array [
- 0,
- 0,
- 0,
- 0,
- ]
- }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx
deleted file mode 100644
index a66a705cda1..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.tsx
+++ /dev/null
@@ -1,146 +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 { Link } from 'react-router';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { getProjectActivity } from '../../../api/projectActivity';
-import PreviewGraph from '../../../components/preview-graph/PreviewGraph';
-import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like';
-import { getActivityUrl } from '../../../helpers/urls';
-import { BranchLike } from '../../../types/branch-like';
-import Analysis from './Analysis';
-
-interface Props {
- branchLike?: BranchLike;
- component: T.Component;
- history?: {
- [metric: string]: Array<{ date: Date; value?: string }>;
- };
- metrics: T.Dict<T.Metric>;
- qualifier: string;
-}
-
-interface State {
- analyses: T.Analysis[];
- loading: boolean;
-}
-
-const PAGE_SIZE = 3;
-
-export default class AnalysesList extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { analyses: [], loading: true };
-
- componentDidMount() {
- this.mounted = true;
- this.fetchData();
- }
-
- componentDidUpdate(prevProps: Props) {
- if (
- prevProps.component.key !== this.props.component.key ||
- !isSameBranchLike(prevProps.branchLike, this.props.branchLike)
- ) {
- this.fetchData();
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- getTopLevelComponent = () => {
- const { component } = this.props;
- let current = component.breadcrumbs.length - 1;
- while (
- current > 0 &&
- !['TRK', 'VW', 'APP'].includes(component.breadcrumbs[current].qualifier)
- ) {
- current--;
- }
- return component.breadcrumbs[current].key;
- };
-
- fetchData = () => {
- this.setState({ loading: true });
-
- getProjectActivity({
- ...getBranchLikeQuery(this.props.branchLike),
- project: this.getTopLevelComponent(),
- ps: PAGE_SIZE
- }).then(
- ({ analyses }) => {
- if (this.mounted) {
- this.setState({ analyses, loading: false });
- }
- },
- () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- }
- );
- };
-
- renderList(analyses: T.Analysis[]) {
- if (!analyses.length) {
- return <p className="spacer-top note">{translate('no_results')}</p>;
- }
-
- return (
- <ul className="spacer-top">
- {analyses.map(analysis => (
- <Analysis analysis={analysis} key={analysis.key} qualifier={this.props.qualifier} />
- ))}
- </ul>
- );
- }
-
- render() {
- const { analyses, loading } = this.state;
-
- if (loading) {
- return null;
- }
-
- return (
- <div className="overview-meta-card">
- <h4 className="overview-meta-header">
- {translate('overview.project_activity', this.props.component.qualifier)}
- </h4>
-
- <PreviewGraph
- branchLike={this.props.branchLike}
- history={this.props.history}
- metrics={this.props.metrics}
- project={this.props.component.key}
- />
-
- {this.renderList(analyses)}
-
- <div className="spacer-top small">
- <Link to={getActivityUrl(this.props.component.key, this.props.branchLike)}>
- {translate('show_more')}
- </Link>
- </div>
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap
deleted file mode 100644
index 2320d0d0c4f..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/AnalysesList-test.tsx.snap
+++ /dev/null
@@ -1,18 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render show more link 1`] = `
-<Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "id": "foo",
- },
- }
- }
->
- show_more
-</Link>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Analysis-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Analysis-test.tsx.snap
deleted file mode 100644
index f61e89ebd8a..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Analysis-test.tsx.snap
+++ /dev/null
@@ -1,41 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should sort the events with version first 1`] = `
-<li
- className="overview-analysis"
->
- <div
- className="small little-spacer-bottom"
- >
- <strong>
- <DateTooltipFormatter
- date="2017-06-10T16:10:59+0200"
- />
- </strong>
- </div>
- <div
- className="overview-activity-events"
- >
- <Event
- event={
- Object {
- "category": "VERSION",
- "key": "2",
- "name": "6.5-SNAPSHOT",
- }
- }
- key="2"
- />
- <Event
- event={
- Object {
- "category": "OTHER",
- "key": "1",
- "name": "test",
- }
- }
- key="1"
- />
- </div>
-</li>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx b/server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx
deleted file mode 100644
index 1ef59518983..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/Bugs.tsx
+++ /dev/null
@@ -1,135 +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 BugIcon from 'sonar-ui-common/components/icons/BugIcon';
-import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import DocTooltip from '../../../components/docs/DocTooltip';
-import DateFromNow from '../../../components/intl/DateFromNow';
-import { isBranch, isMainBranch } from '../../../helpers/branch-like';
-import ApplicationLeakPeriodLegend from '../components/ApplicationLeakPeriodLegend';
-import LeakPeriodLegend from '../components/LeakPeriodLegend';
-import { getMetricName } from '../utils';
-import enhance, { ComposedProps } from './enhance';
-
-export class Bugs extends React.PureComponent<ComposedProps> {
- renderHeader() {
- return this.props.renderHeader('Reliability');
- }
-
- renderTimelineStartDate(historyStartDate?: Date) {
- if (!historyStartDate) {
- return undefined;
- }
- return (
- <DateFromNow date={historyStartDate}>
- {fromNow => (
- <span className="overview-domain-timeline-date">
- {translateWithParameters('overview.started_x', fromNow)}
- </span>
- )}
- </DateFromNow>
- );
- }
-
- renderTimeline(range: string, historyStartDate?: Date) {
- return this.props.renderTimeline('bugs', range, this.renderTimelineStartDate(historyStartDate));
- }
-
- renderLeak() {
- const { branchLike, component, leakPeriod } = this.props;
- if (!this.props.hasDiffMetrics()) {
- return null;
- }
-
- return (
- <div className="overview-domain-leak">
- {component.qualifier === 'APP' && (
- <ApplicationLeakPeriodLegend
- branch={isBranch(branchLike) && !isMainBranch(branchLike) ? branchLike : undefined}
- component={component}
- />
- )}
-
- {component.qualifier !== 'APP' && leakPeriod !== undefined && (
- <LeakPeriodLegend period={leakPeriod} />
- )}
-
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span className="offset-left">{this.props.renderIssues('new_bugs', 'BUG')}</span>
- {this.props.renderRating('new_reliability_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <BugIcon className="little-spacer-right" />
- {getMetricName('new_bugs')}
- </div>
- </div>
- </div>
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span className="offset-left">{this.props.renderIssues('bugs', 'BUG')}</span>
- {this.props.renderRating('reliability_rating')}
- </div>
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- <BugIcon className="little-spacer-right " />
- {getMetricName('bugs')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/bugs.md')}
- />
- </div>
- {this.props.renderHistoryLink('bugs')}
- </div>
- </div>
- {this.renderTimeline('before', this.props.historyStartDate)}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const bugsMeasure = measures.find(measure => measure.metric.key === 'bugs');
- if (!bugsMeasure) {
- return null;
- }
- return (
- <div className="overview-card">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(Bugs);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
deleted file mode 100644
index 28b8b868883..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx
+++ /dev/null
@@ -1,136 +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 CodeSmellIcon from 'sonar-ui-common/components/icons/CodeSmellIcon';
-import { formatMeasure } from 'sonar-ui-common/helpers/measures';
-import DocTooltip from '../../../components/docs/DocTooltip';
-import DrilldownLink from '../../../components/shared/DrilldownLink';
-import { getMetricName } from '../utils';
-import enhance, { ComposedProps } from './enhance';
-
-export class CodeSmells extends React.PureComponent<ComposedProps> {
- renderHeader() {
- return this.props.renderHeader('Maintainability');
- }
-
- renderDebt(metric: string) {
- const { branchLike, measures, component } = this.props;
- const measure = measures.find(measure => measure.metric.key === metric);
- const value = measure ? this.props.getValue(measure) : undefined;
-
- return (
- <DrilldownLink branchLike={branchLike} component={component.key} metric={metric}>
- {formatMeasure(value, 'SHORT_WORK_DUR')}
- </DrilldownLink>
- );
- }
-
- renderTimeline(range: string) {
- return this.props.renderTimeline('sqale_index', range);
- }
-
- renderLeak() {
- if (!this.props.hasDiffMetrics()) {
- return null;
- }
-
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span className="offset-left">{this.renderDebt('new_technical_debt')}</span>
- {this.props.renderRating('new_maintainability_rating')}
- </div>
- <div className="overview-domain-measure-label">{getMetricName('new_effort')}</div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('new_code_smells', 'CODE_SMELL')}
- </div>
- <div className="overview-domain-measure-label">
- <CodeSmellIcon className="little-spacer-right" />
- {getMetricName('new_code_smells')}
- </div>
- </div>
- </div>
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span className="offset-left">{this.renderDebt('sqale_index')}</span>
- {this.props.renderRating('sqale_rating')}
- </div>
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- {getMetricName('effort')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/debt.md')}
- />
- </div>
- {this.props.renderHistoryLink('sqale_index')}
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('code_smells', 'CODE_SMELL')}
- </div>
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- <CodeSmellIcon className="little-spacer-right " />
- {getMetricName('code_smells')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/code-smells.md')}
- />
- </div>
- {this.props.renderHistoryLink('code_smells')}
- </div>
- </div>
- {this.renderTimeline('before')}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const codeSmellsMeasure = measures.find(measure => measure.metric.key === 'code_smells');
- if (!codeSmellsMeasure) {
- return null;
- }
- return (
- <div className="overview-card" id="overview-code-smells">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(CodeSmells);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
deleted file mode 100644
index fe24f269adc..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx
+++ /dev/null
@@ -1,193 +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 { translate } from 'sonar-ui-common/helpers/l10n';
-import {
- formatMeasure,
- getMinDecimalsCountToBeDistinctFromThreshold
-} from 'sonar-ui-common/helpers/measures';
-import DocTooltip from '../../../components/docs/DocTooltip';
-import DrilldownLink from '../../../components/shared/DrilldownLink';
-import CoverageRating from '../../../components/ui/CoverageRating';
-import { getMetricName, getThreshold } from '../utils';
-import enhance, { ComposedProps } from './enhance';
-
-export class Coverage extends React.PureComponent<ComposedProps> {
- getCoverage() {
- const measure = this.props.measures.find(measure => measure.metric.key === 'coverage');
- return Number(measure ? measure.value : undefined);
- }
-
- renderHeader() {
- return this.props.renderHeader('Coverage', translate('metric.coverage.name'));
- }
-
- renderTimeline(range: string) {
- return this.props.renderTimeline('coverage', range);
- }
-
- renderTests() {
- return this.props.renderMeasure(
- 'tests',
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/unit-tests.md')}
- />
- );
- }
-
- renderCoverage() {
- const { branchLike, component } = this.props;
- const metric = 'coverage';
- const coverage = this.getCoverage();
-
- return (
- <div className="overview-domain-measure">
- <div className="display-inline-block text-middle big-spacer-right neg-offset-left">
- <CoverageRating size="big" value={coverage} />
- </div>
-
- <div className="display-inline-block text-middle">
- <div className="overview-domain-measure-value">
- <DrilldownLink branchLike={branchLike} component={component.key} metric={metric}>
- <span className="js-overview-main-coverage">
- {formatMeasure(coverage, 'PERCENT')}
- </span>
- </DrilldownLink>
- </div>
-
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- {getMetricName('coverage')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/coverage.md')}
- />
- </div>
- {this.props.renderHistoryLink('coverage')}
- </div>
- {this.props.renderHistoryLink('coverage')}
- </div>
- );
- }
-
- renderNewCoverage() {
- const { branchLike, component, measures } = this.props;
-
- const newCoverageMeasure = measures.find(measure => measure.metric.key === 'new_coverage');
- const newCoverageValue = newCoverageMeasure && this.props.getValue(newCoverageMeasure);
- const formattedValue =
- newCoverageMeasure && newCoverageValue !== undefined ? (
- <div>
- <DrilldownLink
- branchLike={branchLike}
- component={component.key}
- metric={newCoverageMeasure.metric.key}>
- <span className="js-overview-main-new-coverage">
- {formatMeasure(newCoverageValue, 'PERCENT', {
- decimals: getMinDecimalsCountToBeDistinctFromThreshold(
- parseFloat(newCoverageValue),
- getThreshold(measures, 'new_coverage')
- )
- })}
- </span>
- </DrilldownLink>
- </div>
- ) : (
- <span className="big">—</span>
- );
-
- const newLinesToCover = measures.find(measure => measure.metric.key === 'new_lines_to_cover');
- const newLinesToCoverValue = newLinesToCover && this.props.getValue(newLinesToCover);
- const label =
- newLinesToCover && newLinesToCoverValue !== undefined && Number(newLinesToCoverValue) > 0 ? (
- <div className="overview-domain-measure-label">
- {translate('overview.coverage_on')}
- <br />
- <DrilldownLink
- branchLike={branchLike}
- className="spacer-right overview-domain-secondary-measure-value"
- component={component.key}
- metric={newLinesToCover.metric.key}>
- <span className="js-overview-main-new-coverage">
- {formatMeasure(newLinesToCoverValue, 'SHORT_INT')}
- </span>
- </DrilldownLink>
- {getMetricName('new_lines_to_cover')}
- </div>
- ) : (
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- {getMetricName('new_coverage')}
- </div>
- );
-
- return (
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">{formattedValue}</div>
- {label}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures overview-domain-measures-big">
- {this.renderCoverage()}
- {this.renderTests()}
- </div>
-
- {this.renderTimeline('before')}
- </div>
- );
- }
-
- renderLeak() {
- if (!this.props.hasDiffMetrics()) {
- return null;
- }
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">{this.renderNewCoverage()}</div>
-
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const coverageMeasure = measures.find(measure => measure.metric.key === 'coverage');
- if (!coverageMeasure) {
- return null;
- }
- return (
- <div className="overview-card">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(Coverage);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
deleted file mode 100644
index a22c2f09cbd..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx
+++ /dev/null
@@ -1,194 +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 DuplicationsRating from 'sonar-ui-common/components/ui/DuplicationsRating';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import {
- formatMeasure,
- getMinDecimalsCountToBeDistinctFromThreshold
-} from 'sonar-ui-common/helpers/measures';
-import DocTooltip from '../../../components/docs/DocTooltip';
-import DrilldownLink from '../../../components/shared/DrilldownLink';
-import { getMetricName, getThreshold } from '../utils';
-import enhance, { ComposedProps } from './enhance';
-
-export class Duplications extends React.PureComponent<ComposedProps> {
- renderHeader() {
- return this.props.renderHeader('Duplications', translate('overview.domain.duplications'));
- }
-
- renderTimeline(range: string) {
- return this.props.renderTimeline('duplicated_lines_density', range);
- }
-
- renderDuplicatedBlocks() {
- return this.props.renderMeasure(
- 'duplicated_blocks',
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/duplicated-blocks.md')}
- />
- );
- }
-
- renderDuplications() {
- const { branchLike, component, measures } = this.props;
- const measure = measures.find(measure => measure.metric.key === 'duplicated_lines_density');
- if (!measure) {
- return null;
- }
-
- const duplications = Number(measure.value);
-
- return (
- <div className="overview-domain-measure">
- <div className="display-inline-block text-middle big-spacer-right neg-offset-left">
- <DuplicationsRating size="big" value={duplications} />
- </div>
-
- <div className="display-inline-block text-middle">
- <div className="overview-domain-measure-value">
- <DrilldownLink
- branchLike={branchLike}
- component={component.key}
- metric="duplicated_lines_density">
- {formatMeasure(duplications, 'PERCENT')}
- </DrilldownLink>
- </div>
-
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- {getMetricName('duplications')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/duplications.md')}
- />
- </div>
- {this.props.renderHistoryLink('duplicated_lines_density')}
- </div>
- </div>
- );
- }
-
- renderNewDuplications() {
- const { branchLike, component, measures } = this.props;
- const newDuplicationsMeasure = measures.find(
- measure => measure.metric.key === 'new_duplicated_lines_density'
- );
- const newDuplicationsValue =
- newDuplicationsMeasure && this.props.getValue(newDuplicationsMeasure);
- const formattedValue =
- newDuplicationsMeasure && newDuplicationsValue ? (
- <div>
- <DrilldownLink
- branchLike={branchLike}
- component={component.key}
- metric={newDuplicationsMeasure.metric.key}>
- <span className="js-overview-main-new-duplications">
- {formatMeasure(newDuplicationsValue, 'PERCENT', {
- decimals: getMinDecimalsCountToBeDistinctFromThreshold(
- parseFloat(newDuplicationsValue),
- getThreshold(measures, 'new_duplicated_lines_density')
- )
- })}
- </span>
- </DrilldownLink>
- </div>
- ) : (
- <span className="big">—</span>
- );
-
- const newLinesMeasure = measures.find(measure => measure.metric.key === 'new_lines');
- const newLinesValue = newLinesMeasure && this.props.getValue(newLinesMeasure);
- const label =
- newLinesMeasure && newLinesValue !== undefined && Number(newLinesValue) > 0 ? (
- <div className="overview-domain-measure-label">
- {translate('overview.duplications_on')}
- <br />
- <DrilldownLink
- branchLike={branchLike}
- className="spacer-right overview-domain-secondary-measure-value"
- component={component.key}
- metric={newLinesMeasure.metric.key}>
- <span className="js-overview-main-new-lines">
- {formatMeasure(newLinesValue, 'SHORT_INT')}
- </span>
- </DrilldownLink>
- {getMetricName('new_lines')}
- </div>
- ) : (
- <div className="overview-domain-measure-label">{getMetricName('new_duplications')}</div>
- );
-
- return (
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">{formattedValue}</div>
- {label}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures overview-domain-measures-big">
- {this.renderDuplications()}
- {this.renderDuplicatedBlocks()}
- </div>
-
- {this.renderTimeline('before')}
- </div>
- );
- }
-
- renderLeak() {
- if (!this.props.hasDiffMetrics()) {
- return null;
- }
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">{this.renderNewDuplications()}</div>
-
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- render() {
- const { measures } = this.props;
- const duplications = measures.find(
- measure => measure.metric.key === 'duplicated_lines_density'
- );
- if (duplications == null) {
- return null;
- }
- return (
- <div className="overview-card">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(Duplications);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx b/server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx
deleted file mode 100644
index 513879fed34..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/VulnerabilitiesAndHotspots.tsx
+++ /dev/null
@@ -1,128 +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 SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
-import VulnerabilityIcon from 'sonar-ui-common/components/icons/VulnerabilityIcon';
-import DocTooltip from '../../../components/docs/DocTooltip';
-import { getMetricName } from '../utils';
-import enhance, { ComposedProps } from './enhance';
-
-export class VulnerabiltiesAndHotspots extends React.PureComponent<ComposedProps> {
- renderHeader() {
- return this.props.renderHeader('Security');
- }
-
- renderTimeline(range: string) {
- return this.props.renderTimeline('vulnerabilities', range);
- }
-
- renderLeak() {
- if (!this.props.hasDiffMetrics()) {
- return null;
- }
-
- return (
- <div className="overview-domain-leak">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span className="offset-left">
- {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')}
- </span>
- {this.props.renderRating('new_security_rating')}
- </div>
- <div className="overview-domain-measure-label">
- <VulnerabilityIcon className="little-spacer-right" />
- {getMetricName('new_vulnerabilities')}
- </div>
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('new_security_hotspots', 'SECURITY_HOTSPOT')}
- </div>
- <div className="overview-domain-measure-label">
- <SecurityHotspotIcon className="little-spacer-right" />
- {getMetricName('new_security_hotspots')}
- </div>
- </div>
- </div>
- {this.renderTimeline('after')}
- </div>
- );
- }
-
- renderNutshell() {
- return (
- <div className="overview-domain-nutshell">
- <div className="overview-domain-measures">
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <span className="offset-left">
- {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')}
- </span>
- {this.props.renderRating('security_rating')}
- </div>
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- <VulnerabilityIcon className="little-spacer-right" />
- {getMetricName('vulnerabilities')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(/* webpackMode: "eager" */ 'Docs/tooltips/metrics/vulnerabilities.md')}
- />
- </div>
- {this.props.renderHistoryLink('vulnerabilities')}
- </div>
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- {this.props.renderIssues('security_hotspots', 'SECURITY_HOTSPOT')}
- </div>
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- <SecurityHotspotIcon className="little-spacer-right" />
- {getMetricName('security_hotspots')}
- <DocTooltip
- className="little-spacer-left"
- doc={import(
- /* webpackMode: "eager" */ 'Docs/tooltips/metrics/security-hotspots.md'
- )}
- />
- </div>
- {this.props.renderHistoryLink('security_hotspots')}
- </div>
- </div>
- {this.renderTimeline('before')}
- </div>
- );
- }
-
- render() {
- return (
- <div className="overview-card">
- {this.renderHeader()}
-
- <div className="overview-domain-panel">
- {this.renderNutshell()}
- {this.renderLeak()}
- </div>
- </div>
- );
- }
-}
-
-export default enhance(VulnerabiltiesAndHotspots);
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx
deleted file mode 100644
index ae0fdfcc297..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Bugs-test.tsx
+++ /dev/null
@@ -1,80 +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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
-import Bugs from '../Bugs';
-import { ComposedProps } from '../enhance';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ComposedProps> = {}) {
- return shallow(
- <Bugs
- branchLike={mockMainBranch()}
- component={mockComponent()}
- history={{ bugs: [] }}
- historyStartDate={new Date('2019-01-14T15:44:51.000Z')}
- leakPeriod={{ index: 1, mode: 'days' } as T.Period}
- measures={[
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'bugs',
- key: 'bugs',
- name: 'Bugs',
- type: 'INT'
- }),
- value: '5'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_bugs',
- key: 'new_bugs',
- name: 'New Bugs',
- type: 'INT'
- }),
- value: '2'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'reliability_rating',
- key: 'reliability_rating',
- name: 'Reliability',
- type: 'RATING'
- }),
- value: '1.0'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_reliability_rating',
- key: 'new_reliability_rating',
- name: 'New Reliability',
- type: 'RATING'
- }),
- value: '2.0'
- })
- ]}
- {...props}
- />
- ).dive();
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-test.tsx
deleted file mode 100644
index d87e979a344..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/CodeSmells-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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
-import CodeSmells from '../CodeSmells';
-import { ComposedProps } from '../enhance';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ComposedProps> = {}) {
- return shallow(
- <CodeSmells
- branchLike={mockMainBranch()}
- component={mockComponent()}
- history={{ sqale_index: [] }}
- leakPeriod={{ index: 1 } as T.Period}
- measures={[
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'code_smells',
- key: 'code_smells',
- name: 'Code Smells',
- type: 'INT'
- }),
- value: '15'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'sqale_index',
- key: 'sqale_index',
- name: 'Debt',
- type: 'INT'
- }),
- value: '1052'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'sqale_rating',
- key: 'sqale_rating',
- name: 'Maintainability',
- type: 'RATING'
- }),
- value: '2.0'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_code_smells',
- key: 'new_code_smells',
- name: 'New Code Smells',
- type: 'INT'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '52'
- }
- ]
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_technical_debt',
- key: 'new_technical_debt',
- name: 'New Debt',
- type: 'INT'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '85'
- }
- ]
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_maintainability_rating',
- key: 'new_maintainability_rating',
- name: 'New Maintainability',
- type: 'RATING'
- }),
- value: '3.0'
- })
- ]}
- {...props}
- />
- ).dive();
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx
deleted file mode 100644
index 63d915c677d..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/Duplications-test.tsx
+++ /dev/null
@@ -1,88 +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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
-import Duplications from '../Duplications';
-import { ComposedProps } from '../enhance';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ComposedProps> = {}) {
- return shallow(
- <Duplications
- branchLike={mockMainBranch()}
- component={mockComponent()}
- leakPeriod={{ index: 1 } as T.Period}
- measures={[
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'duplicated_lines_density',
- key: 'duplicated_lines_density',
- name: 'Duplicated Lines'
- }),
- value: '0.5'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_lines',
- key: 'new_lines',
- name: 'New Lines',
- type: 'INT'
- }),
- value: '52'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_duplicated_lines_density',
- key: 'new_duplicated_lines_density',
- name: 'New Duplicated Lines'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '1.5'
- }
- ]
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'duplicated_blocks',
- key: 'duplicated_blocks',
- name: 'Duplicated Blocks',
- type: 'INT'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '2'
- }
- ]
- })
- ]}
- {...props}
- />
- ).dive();
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx b/server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx
deleted file mode 100644
index d25f902c2cf..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/VulnerabilitiesAndHotspots-test.tsx
+++ /dev/null
@@ -1,115 +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 { mockMainBranch } from '../../../../helpers/mocks/branch-like';
-import { mockComponent, mockMeasureEnhanced, mockMetric } from '../../../../helpers/testMocks';
-import { ComposedProps } from '../enhance';
-import VulnerabilitiesAndHotspots from '../VulnerabilitiesAndHotspots';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ComposedProps> = {}) {
- return shallow(
- <VulnerabilitiesAndHotspots
- branchLike={mockMainBranch()}
- component={mockComponent()}
- history={{ vulnerabilities: [] }}
- leakPeriod={{ index: 1 } as T.Period}
- measures={[
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'vulnerabilities',
- key: 'vulnerabilities',
- name: 'Vulnerabilities',
- type: 'INT'
- }),
- value: '0'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'security_hotspots',
- key: 'security_hotspots',
- name: 'Security Hotspots',
- type: 'INT'
- }),
- value: '0'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'security_rating',
- key: 'security_rating',
- name: 'Security',
- type: 'RATING'
- }),
- value: '1.0'
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_vulnerabilities',
- key: 'new_vulnerabilities',
- name: 'New Vulnerabilities',
- type: 'INT'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '1'
- }
- ]
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_security_hotspots',
- key: 'new_security_hotspots',
- name: 'New Security Hotspots',
- type: 'INT'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '10'
- }
- ]
- }),
- mockMeasureEnhanced({
- metric: mockMetric({
- id: 'new_security_rating',
- key: 'new_security_rating',
- name: 'New Security Rating',
- type: 'RATING'
- }),
- periods: [
- {
- bestValue: true,
- index: 1,
- value: '5.0'
- }
- ]
- })
- ]}
- {...props}
- />
- ).dive();
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap
deleted file mode 100644
index 83f8ef8442a..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Bugs-test.tsx.snap
+++ /dev/null
@@ -1,229 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="overview-card"
->
- <div
- className="overview-card-header"
- >
- <div
- className="overview-title"
- >
- <span>
- metric_domain.Reliability
- </span>
- <Link
- className="spacer-left small"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/component_measures",
- "query": Object {
- "id": "my-project",
- "metric": "Reliability",
- },
- }
- }
- >
- layout.measures
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-panel"
- >
- <div
- className="overview-domain-nutshell"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <span
- className="offset-left"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "my-project",
- "resolved": "false",
- "types": "BUG",
- },
- }
- }
- >
- 5
- </Link>
- </span>
- <Tooltip
- overlay="metric.reliability_rating.tooltip.A"
- >
- <div
- className="overview-domain-measure-sup"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="link-no-underline"
- component="my-project"
- metric="reliability_rating"
- >
- <Rating
- value="1.0"
- />
- </DrilldownLink>
- </div>
- </Tooltip>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- <BugIcon
- className="little-spacer-right "
- />
- overview.metric.bugs
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "bugs",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-timeline"
- >
- <Timeline
- history={Array []}
- />
- <DateFromNow
- date={2019-01-14T15:44:51.000Z}
- >
- <Component />
- </DateFromNow>
- </div>
- </div>
- <div
- className="overview-domain-leak"
- >
- <InjectIntl(LeakPeriodLegend)
- period={
- Object {
- "index": 1,
- "mode": "days",
- }
- }
- />
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <span
- className="offset-left"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "my-project",
- "resolved": "false",
- "sinceLeakPeriod": "true",
- "types": "BUG",
- },
- }
- }
- >
- 1
- </Link>
- </span>
- <Tooltip
- overlay="metric.reliability_rating.tooltip.A"
- >
- <div
- className="overview-domain-measure-sup"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="link-no-underline"
- component="my-project"
- metric="new_reliability_rating"
- >
- <Rating
- value="1.0"
- />
- </DrilldownLink>
- </div>
- </Tooltip>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- <BugIcon
- className="little-spacer-right"
- />
- overview.metric.new_bugs
- </div>
- </div>
- </div>
- <div
- className="overview-domain-timeline"
- >
- <Timeline
- history={Array []}
- />
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap
deleted file mode 100644
index 88d0a6bac8b..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/CodeSmells-test.tsx.snap
+++ /dev/null
@@ -1,295 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="overview-card"
- id="overview-code-smells"
->
- <div
- className="overview-card-header"
- >
- <div
- className="overview-title"
- >
- <span>
- metric_domain.Maintainability
- </span>
- <Link
- className="spacer-left small"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/component_measures",
- "query": Object {
- "id": "my-project",
- "metric": "Maintainability",
- },
- }
- }
- >
- layout.measures
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-panel"
- >
- <div
- className="overview-domain-nutshell"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <span
- className="offset-left"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="sqale_index"
- >
- work_duration.x_days.2
- </DrilldownLink>
- </span>
- <Tooltip
- overlay="metric.sqale_rating.tooltip.B.0.0%"
- >
- <div
- className="overview-domain-measure-sup"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="link-no-underline"
- component="my-project"
- metric="sqale_rating"
- >
- <Rating
- value="2.0"
- />
- </DrilldownLink>
- </div>
- </Tooltip>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- overview.metric.effort
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "sqale_index",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "my-project",
- "resolved": "false",
- "types": "CODE_SMELL",
- },
- }
- }
- >
- 15
- </Link>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- <CodeSmellIcon
- className="little-spacer-right "
- />
- overview.metric.code_smells
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "code_smells",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-timeline"
- >
- <Timeline
- history={Array []}
- />
- </div>
- </div>
- <div
- className="overview-domain-leak"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <span
- className="offset-left"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="new_technical_debt"
- >
- work_duration.x_hours.1
- </DrilldownLink>
- </span>
- <Tooltip
- overlay="metric.sqale_rating.tooltip.A.0.0%"
- >
- <div
- className="overview-domain-measure-sup"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="link-no-underline"
- component="my-project"
- metric="new_maintainability_rating"
- >
- <Rating
- value="1.0"
- />
- </DrilldownLink>
- </div>
- </Tooltip>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- overview.metric.new_effort
- </div>
- </div>
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "my-project",
- "resolved": "false",
- "sinceLeakPeriod": "true",
- "types": "CODE_SMELL",
- },
- }
- }
- >
- 52
- </Link>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- <CodeSmellIcon
- className="little-spacer-right"
- />
- overview.metric.new_code_smells
- </div>
- </div>
- </div>
- <div
- className="overview-domain-timeline"
- >
- <Timeline
- history={Array []}
- />
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap
deleted file mode 100644
index 53140c34d90..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Coverage-test.tsx.snap
+++ /dev/null
@@ -1,251 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="overview-card"
->
- <div
- className="overview-card-header"
- >
- <div
- className="overview-title"
- >
- <span>
- metric.coverage.name
- </span>
- <Link
- className="spacer-left small"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/component_measures",
- "query": Object {
- "id": "my-project",
- "metric": "Coverage",
- },
- }
- }
- >
- layout.measures
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-panel"
- >
- <div
- className="overview-domain-nutshell"
- >
- <div
- className="overview-domain-measures overview-domain-measures-big"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="display-inline-block text-middle big-spacer-right neg-offset-left"
- >
- <CoverageRating
- size="big"
- value={1}
- />
- </div>
- <div
- className="display-inline-block text-middle"
- >
- <div
- className="overview-domain-measure-value"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="coverage"
- >
- <span
- className="js-overview-main-coverage"
- >
- 1.0%
- </span>
- </DrilldownLink>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- overview.metric.coverage
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "coverage",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "coverage",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="tests"
- >
- <span
- className="js-overview-main-tests"
- >
- 15
- </span>
- </DrilldownLink>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- Unit Tests
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "tests",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- </div>
- </div>
- </div>
- <div
- className="overview-domain-leak"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <div>
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="new_coverage"
- >
- <span
- className="js-overview-main-new-coverage"
- >
- 1.0%
- </span>
- </DrilldownLink>
- </div>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- overview.coverage_on
- <br />
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="spacer-right overview-domain-secondary-measure-value"
- component="my-project"
- metric="new_lines_to_cover"
- >
- <span
- className="js-overview-main-new-coverage"
- >
- 1
- </span>
- </DrilldownLink>
- overview.metric.new_lines_to_cover
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap
deleted file mode 100644
index af8b3b669d3..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/Duplications-test.tsx.snap
+++ /dev/null
@@ -1,227 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="overview-card"
->
- <div
- className="overview-card-header"
- >
- <div
- className="overview-title"
- >
- <span>
- overview.domain.duplications
- </span>
- <Link
- className="spacer-left small"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/component_measures",
- "query": Object {
- "id": "my-project",
- "metric": "Duplications",
- },
- }
- }
- >
- layout.measures
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-panel"
- >
- <div
- className="overview-domain-nutshell"
- >
- <div
- className="overview-domain-measures overview-domain-measures-big"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="display-inline-block text-middle big-spacer-right neg-offset-left"
- >
- <DuplicationsRating
- size="big"
- value={0.5}
- />
- </div>
- <div
- className="display-inline-block text-middle"
- >
- <div
- className="overview-domain-measure-value"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="duplicated_lines_density"
- >
- 0.5%
- </DrilldownLink>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- overview.metric.duplications
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "duplicated_lines_density",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="duplicated_blocks"
- >
- <span
- className="js-overview-main-tests"
- >
- 1
- </span>
- </DrilldownLink>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- Duplicated Blocks
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "duplicated_blocks",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- </div>
- </div>
- </div>
- <div
- className="overview-domain-leak"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <div>
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- component="my-project"
- metric="new_duplicated_lines_density"
- >
- <span
- className="js-overview-main-new-duplications"
- >
- 1.5%
- </span>
- </DrilldownLink>
- </div>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- overview.duplications_on
- <br />
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="spacer-right overview-domain-secondary-measure-value"
- component="my-project"
- metric="new_lines"
- >
- <span
- className="js-overview-main-new-lines"
- >
- 1
- </span>
- </DrilldownLink>
- overview.metric.new_lines
- </div>
- </div>
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap
deleted file mode 100644
index 7aed1c83259..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/__tests__/__snapshots__/VulnerabilitiesAndHotspots-test.tsx.snap
+++ /dev/null
@@ -1,307 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="overview-card"
->
- <div
- className="overview-card-header"
- >
- <div
- className="overview-title"
- >
- <span>
- metric_domain.Security
- </span>
- <Link
- className="spacer-left small"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/component_measures",
- "query": Object {
- "id": "my-project",
- "metric": "Security",
- },
- }
- }
- >
- layout.measures
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-panel"
- >
- <div
- className="overview-domain-nutshell"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <span
- className="offset-left"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "my-project",
- "resolved": "false",
- "types": "VULNERABILITY",
- },
- }
- }
- >
- 0
- </Link>
- </span>
- <Tooltip
- overlay="metric.security_rating.tooltip.A"
- >
- <div
- className="overview-domain-measure-sup"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="link-no-underline"
- component="my-project"
- metric="security_rating"
- >
- <Rating
- value="1.0"
- />
- </DrilldownLink>
- </div>
- </Tooltip>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- <VulnerabilityIcon
- className="little-spacer-right"
- />
- overview.metric.vulnerabilities
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "vulnerabilities",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/security_hotspots",
- "query": Object {
- "assignedToMe": "false",
- "id": "my-project",
- "resolved": "false",
- "types": "SECURITY_HOTSPOT",
- },
- }
- }
- >
- 0
- </Link>
- </div>
- <div
- className="overview-domain-measure-label display-flex-center display-flex-justify-center"
- >
- <SecurityHotspotIcon
- className="little-spacer-right"
- />
- overview.metric.security_hotspots
- <DocTooltip
- className="little-spacer-left"
- doc={Promise {}}
- />
- </div>
- <Link
- className="overview-domain-measure-history-link"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/activity",
- "query": Object {
- "custom_metrics": "security_hotspots",
- "graph": "custom",
- "id": "my-project",
- },
- }
- }
- >
- <HistoryIcon />
- <span>
- project_activity.page
- </span>
- </Link>
- </div>
- </div>
- <div
- className="overview-domain-timeline"
- >
- <Timeline
- history={Array []}
- />
- </div>
- </div>
- <div
- className="overview-domain-leak"
- >
- <div
- className="overview-domain-measures"
- >
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <span
- className="offset-left"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/project/issues",
- "query": Object {
- "id": "my-project",
- "resolved": "false",
- "sinceLeakPeriod": "true",
- "types": "VULNERABILITY",
- },
- }
- }
- >
- 1
- </Link>
- </span>
- <Tooltip
- overlay="metric.security_rating.tooltip.E"
- >
- <div
- className="overview-domain-measure-sup"
- >
- <DrilldownLink
- branchLike={
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- }
- }
- className="link-no-underline"
- component="my-project"
- metric="new_security_rating"
- >
- <Rating
- value="5.0"
- />
- </DrilldownLink>
- </div>
- </Tooltip>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- <VulnerabilityIcon
- className="little-spacer-right"
- />
- overview.metric.new_vulnerabilities
- </div>
- </div>
- <div
- className="overview-domain-measure"
- >
- <div
- className="overview-domain-measure-value"
- >
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/security_hotspots",
- "query": Object {
- "assignedToMe": "false",
- "id": "my-project",
- "resolved": "false",
- "sinceLeakPeriod": "true",
- "types": "SECURITY_HOTSPOT",
- },
- }
- }
- >
- 10
- </Link>
- </div>
- <div
- className="overview-domain-measure-label"
- >
- <SecurityHotspotIcon
- className="little-spacer-right"
- />
- overview.metric.new_security_hotspots
- </div>
- </div>
- </div>
- <div
- className="overview-domain-timeline"
- >
- <Timeline
- history={Array []}
- />
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
deleted file mode 100644
index 4caf589f566..00000000000
--- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
+++ /dev/null
@@ -1,227 +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 { Link } from 'react-router';
-import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
-import HistoryIcon from 'sonar-ui-common/components/icons/HistoryIcon';
-import Rating from 'sonar-ui-common/components/ui/Rating';
-import { getLocalizedMetricName, translate } from 'sonar-ui-common/helpers/l10n';
-import { formatMeasure } from 'sonar-ui-common/helpers/measures';
-import { getWrappedDisplayName } from '../../../components/hoc/utils';
-import { getLeakValue } from '../../../components/measure/utils';
-import DrilldownLink from '../../../components/shared/DrilldownLink';
-import { getBranchLikeQuery } from '../../../helpers/branch-like';
-import { getRatingTooltip, getShortType, isDiffMetric } from '../../../helpers/measures';
-import { getPeriodDate } from '../../../helpers/periods';
-import {
- getComponentDrilldownUrl,
- getComponentIssuesUrl,
- getMeasureHistoryUrl
-} from '../../../helpers/urls';
-import { BranchLike } from '../../../types/branch-like';
-import Timeline from '../components/Timeline';
-
-export interface EnhanceProps {
- branchLike?: BranchLike;
- component: T.Component;
- measures: T.MeasureEnhanced[];
- leakPeriod?: T.Period;
- history?: {
- [metric: string]: Array<{ date: Date; value?: string }>;
- };
- historyStartDate?: Date;
-}
-
-export interface ComposedProps extends EnhanceProps {
- getValue: (measure: T.MeasureEnhanced) => string | undefined;
- hasDiffMetrics: () => boolean;
- renderHeader: (domain: string, label?: string) => React.ReactNode;
- renderMeasure: (metricKey: string, tooltip?: React.ReactNode) => React.ReactNode;
- renderRating: (metricKey: string) => React.ReactNode;
- renderIssues: (metric: string, type: T.IssueType) => React.ReactNode;
- renderHistoryLink: (metricKey: string) => React.ReactNode;
- renderTimeline: (metricKey: string, range: string, children?: React.ReactNode) => React.ReactNode;
-}
-
-export default function enhance(ComposedComponent: React.ComponentType<ComposedProps>) {
- return class extends React.PureComponent<EnhanceProps> {
- static displayName = getWrappedDisplayName(ComposedComponent, 'enhance');
-
- getValue = (measure: T.MeasureEnhanced) => {
- if (!measure) {
- return '0';
- }
- return isDiffMetric(measure.metric.key) ? getLeakValue(measure) : measure.value;
- };
-
- hasDiffMetrics = () => {
- const { measures } = this.props;
- return measures.some(m => isDiffMetric(m.metric.key));
- };
-
- renderHeader = (domain: string, label?: string) => {
- const { branchLike, component } = this.props;
- label = label !== undefined ? label : translate('metric_domain', domain);
- return (
- <div className="overview-card-header">
- <div className="overview-title">
- <span>{label}</span>
- <Link
- className="spacer-left small"
- to={getComponentDrilldownUrl({
- componentKey: component.key,
- metric: domain,
- branchLike
- })}>
- {translate('layout.measures')}
- </Link>
- </div>
- </div>
- );
- };
-
- renderMeasure = (metricKey: string, tooltip?: React.ReactNode) => {
- const { branchLike, measures, component } = this.props;
- const measure = measures.find(measure => measure.metric.key === metricKey);
- if (!measure) {
- return null;
- }
-
- return (
- <div className="overview-domain-measure">
- <div className="overview-domain-measure-value">
- <DrilldownLink branchLike={branchLike} component={component.key} metric={metricKey}>
- <span className="js-overview-main-tests">
- {formatMeasure(measure.value, getShortType(measure.metric.type))}
- </span>
- </DrilldownLink>
- </div>
-
- <div className="overview-domain-measure-label display-flex-center display-flex-justify-center">
- {getLocalizedMetricName(measure.metric)}
- {tooltip}
- {this.renderHistoryLink(measure.metric.key)}
- </div>
- </div>
- );
- };
-
- renderRating = (metricKey: string) => {
- const { branchLike, component, measures } = this.props;
- const measure = measures.find(measure => measure.metric.key === metricKey);
- if (!measure) {
- return null;
- }
-
- const value = this.getValue(measure);
- const title = value && getRatingTooltip(metricKey, value);
- return (
- <Tooltip overlay={title}>
- <div className="overview-domain-measure-sup">
- <DrilldownLink
- branchLike={branchLike}
- className="link-no-underline"
- component={component.key}
- metric={metricKey}>
- <Rating value={value} />
- </DrilldownLink>
- </div>
- </Tooltip>
- );
- };
-
- renderIssues = (metric: string, type: string) => {
- const { branchLike, measures, component } = this.props;
- const measure = measures.find(measure => measure.metric.key === metric);
- if (!measure) {
- return <span className="big">—</span>;
- }
-
- const value = this.getValue(measure);
- const params = { ...getBranchLikeQuery(branchLike), resolved: 'false', types: type };
- if (isDiffMetric(metric)) {
- Object.assign(params, { sinceLeakPeriod: 'true' });
- }
-
- if (metric.endsWith('security_hotspots')) {
- return (
- <Link
- to={{
- pathname: '/security_hotspots',
- query: { ...params, id: component.key, assignedToMe: 'false' }
- }}>
- {formatMeasure(value, 'SHORT_INT')}
- </Link>
- );
- }
-
- return (
- <Link to={getComponentIssuesUrl(component.key, params)}>
- {formatMeasure(value, 'SHORT_INT')}
- </Link>
- );
- };
-
- renderHistoryLink = (metricKey: string) => {
- const linkClass = 'overview-domain-measure-history-link';
- return (
- <Link
- className={linkClass}
- to={getMeasureHistoryUrl(this.props.component.key, metricKey, this.props.branchLike)}>
- <HistoryIcon />
- <span>{translate('project_activity.page')}</span>
- </Link>
- );
- };
-
- renderTimeline = (metricKey: string, range: 'before' | 'after', children?: React.ReactNode) => {
- if (!this.props.history) {
- return null;
- }
- const history = this.props.history[metricKey];
- if (!history) {
- return null;
- }
- const props = { history, [range]: getPeriodDate(this.props.leakPeriod) };
- return (
- <div className="overview-domain-timeline">
- <Timeline {...props} />
- {children}
- </div>
- );
- };
-
- render() {
- return (
- <ComposedComponent
- {...this.props}
- getValue={this.getValue}
- hasDiffMetrics={this.hasDiffMetrics}
- renderHeader={this.renderHeader}
- renderHistoryLink={this.renderHistoryLink}
- renderIssues={this.renderIssues}
- renderMeasure={this.renderMeasure}
- renderRating={this.renderRating}
- renderTimeline={this.renderTimeline}
- />
- );
- }
- };
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
index 2161ceba139..bd9fbfaca1a 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
@@ -32,13 +32,11 @@ import {
Store
} from '../../../store/rootReducer';
import { BranchLike } from '../../../types/branch-like';
-import AnalysesList from '../events/AnalysesList';
import MetaKey from './MetaKey';
import MetaLinks from './MetaLinks';
import MetaOrganizationKey from './MetaOrganizationKey';
import MetaQualityGate from './MetaQualityGate';
import MetaQualityProfiles from './MetaQualityProfiles';
-import MetaSize from './MetaSize';
import MetaTags from './MetaTags';
const ProjectBadges = lazyLoadComponent(() => import('../badges/ProjectBadges'), 'ProjectBadges');
@@ -103,7 +101,7 @@ export class Meta extends React.PureComponent<Props> {
render() {
const { organizationsEnabled } = this.props.appState;
- const { branchLike, component, currentUser, measures, metrics, organization } = this.props;
+ const { branchLike, component, currentUser, metrics, organization } = this.props;
const { qualifier, description, visibility } = component;
const isProject = qualifier === 'TRK';
@@ -131,21 +129,8 @@ export class Meta extends React.PureComponent<Props> {
{isProject && (
<MetaTags component={component} onComponentChange={this.props.onComponentChange} />
)}
- {measures && (
- <MetaSize branchLike={branchLike} component={component} measures={measures} />
- )}
</div>
- {metrics && (
- <AnalysesList
- branchLike={branchLike}
- component={component}
- history={this.props.history}
- metrics={metrics}
- qualifier={component.qualifier}
- />
- )}
-
{this.renderQualityInfos()}
{isProject && <MetaLinks component={component} />}
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
index 3a8b2281593..353ccfed13b 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
@@ -25,7 +25,6 @@ import { formatMeasure } from 'sonar-ui-common/helpers/measures';
import LanguageDistributionContainer from '../../../components/charts/LanguageDistributionContainer';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import { BranchLike } from '../../../types/branch-like';
-import { getMetricName } from '../utils';
interface Props {
branchLike?: BranchLike;
@@ -55,7 +54,6 @@ export default class MetaSize extends React.PureComponent<Props> {
) : (
<span>0</span>
)}
- <div className="spacer-top text-muted">{getMetricName('ncloc')}</div>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap
index e7b7c3c489a..0ca8d471138 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaContainer-test.tsx.snap
@@ -39,33 +39,6 @@ exports[`should hide QG and QP links if the organization has a paid plan, and th
onComponentChange={[MockFunction]}
/>
</div>
- <AnalysesList
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- metrics={Object {}}
- qualifier="TRK"
- />
<MetaLinks
component={
Object {
@@ -179,33 +152,6 @@ exports[`should render correctly 1`] = `
onComponentChange={[MockFunction]}
/>
</div>
- <AnalysesList
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- metrics={Object {}}
- qualifier="TRK"
- />
<div
className="overview-meta-card"
id="overview-meta-quality-gate"
@@ -348,33 +294,6 @@ exports[`should show QG and QP links if the organization has a paid plan, and th
onComponentChange={[MockFunction]}
/>
</div>
- <AnalysesList
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "organization": "foo",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
- Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
- "name": "Sonar way",
- },
- ],
- "tags": Array [],
- }
- }
- metrics={Object {}}
- qualifier="TRK"
- />
<div
className="overview-meta-card"
id="overview-meta-quality-gate"
diff --git a/server/sonar-web/src/main/js/apps/overview/styles.css b/server/sonar-web/src/main/js/apps/overview/styles.css
index 10deade8fd1..6074716b4b5 100644
--- a/server/sonar-web/src/main/js/apps/overview/styles.css
+++ b/server/sonar-web/src/main/js/apps/overview/styles.css
@@ -21,286 +21,156 @@
animation: fadeIn 0.5s forwards;
}
-.overview-main {
- background-color: var(--barBackgroundColor);
- transition: transform 0.5s ease, opacity 0.5s ease;
- width: calc(100% - 300px);
-}
-
-.overview-sidebar {
- margin-top: calc(-1 * var(--pagePadding));
- margin-bottom: calc(-1 * var(--pagePadding));
- margin-left: var(--pagePadding);
- padding-left: calc(var(--pagePadding) - 1px);
- padding-top: var(--pagePadding);
- padding-bottom: var(--pagePadding);
- border-left: 1px solid var(--barBorderColor);
-}
-
-/*
- * Title
- */
-
-.overview-title {
- font-size: var(--bigFontSize);
- font-weight: 400;
- line-height: 1.3;
-}
-
-/*
- * Quality Gate
- */
-
-.overview-quality-gate {
- padding-bottom: 15px;
- border-bottom: 1px solid var(--barBorderColor);
- background-color: var(--barBackgroundColor);
+.overview-panel {
+ min-height: 100%;
}
-.overview-quality-gate-conditions-list {
- display: flex;
- flex-wrap: wrap;
- align-items: flex-start;
-}
-
-.overview-quality-gate-conditions-list-collapse {
- margin: calc(2 * var(--gridSize)) 0;
+.overview-panel-content {
+ background: white;
+ border: 1px solid var(--barBorderColor);
}
-.overview-quality-gate-condition {
- display: block;
- margin-top: 15px;
- margin-right: 30px;
- border: none;
- border-left: 5px solid;
- border-top-right-radius: 2px;
- border-bottom-right-radius: 2px;
- background-color: #fff;
- color: var(--baseFontColor);
- transition: none;
+.overview-panel-title {
+ text-transform: uppercase;
+ font-weight: 600;
+ font-size: var(--smallFontSize);
+ margin-bottom: var(--gridSize);
}
-.overview-quality-gate-condition:hover,
-.overview-quality-gate-condition:focus {
- color: var(--baseFontColor);
+.overview-panel-padded {
+ padding: calc(2 * var(--gridSize));
}
-.overview-quality-gate-condition:hover .overview-quality-gate-condition-container,
-.overview-quality-gate-condition:focus .overview-quality-gate-condition-container {
- border-color: inherit;
+.overview-panel-big-padded {
+ padding: calc(3 * var(--gridSize));
}
-.overview-quality-gate-condition-leak {
- background-color: var(--leakPrimaryColor);
+.overview-panel-huge-padded {
+ padding: calc(5 * var(--gridSize));
}
-.overview-quality-gate-condition-metric,
-.overview-quality-gate-condition-period {
- max-width: 125px;
- line-height: 16px;
- font-size: var(--smallFontSize);
-}
+/*
+ * Measures
+ */
-.overview-quality-gate-condition-period {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
+.overview-measures-row {
+ min-height: 105px;
+ box-sizing: border-box;
}
-.overview-quality-gate-condition-container {
- display: flex;
- align-items: center;
- min-width: 150px;
- /* three lines by 16px and 4px margin */
- min-height: 52px;
- padding: 10px;
+.overview-measures-row + .overview-measures-row {
border-top: 1px solid var(--barBorderColor);
- border-bottom: 1px solid var(--barBorderColor);
- border-right: 1px solid var(--barBorderColor);
- transition: border-color 0.3s ease;
}
-.overview-quality-gate-condition-value {
+.overview-measures-value {
line-height: 1;
- margin-right: 10px;
- font-size: 24px;
- font-weight: normal;
-
- /* for consistency with ratings */
- min-width: 24px;
- text-align: center;
+ font-size: var(--giganticFontSize);
+ white-space: nowrap;
}
-.overview-quality-gate-condition-value span {
- display: inline-block;
- vertical-align: top;
+.overview-measures-empty-value {
+ height: 1px;
+ width: var(--bigFontSize);
+ background: var(--baseFontColor);
}
-.overview-quality-gate-threshold {
- margin-top: 4px;
- font-size: var(--smallFontSize);
- color: var(--secondFontColor);
-}
-
-.overview-quality-gate-warning {
- margin: 15px 0 0;
+.overview-measures-aside {
+ flex-basis: 180px;
+ box-sizing: border-box;
}
-.overview-quality-gate-condition-error {
- border-color: var(--red);
+.overview-measures-tab {
+ width: calc(160px - calc(4 * var(--gridSize)));
}
-.overview-quality-gate-condition-warn {
- border-color: var(--orange);
+.overview-measures-emphasis {
+ background: var(--veryLightGreen);
}
/*
- * Domain
+ * Quality Gate
*/
-.overview-domains-list {
- animation: fadeIn 0.5s forwards;
-}
-
-.overview-card {
- margin: calc(2 * var(--gridSize)) 0;
- padding-top: 3px;
-}
-
-.overview-card .offset-left {
- margin-left: 30px;
-}
-
-.overview-card .neg-offset-left {
- margin-left: -30px;
-}
-
-.overview-card-header {
- display: flex;
- align-items: baseline;
- justify-content: space-between;
- margin-bottom: 10px;
- line-height: var(--controlHeight);
+.overview-quality-gate-badge-large {
+ padding: calc(2 * var(--gridSize));
+ color: white;
+ box-sizing: border-box;
}
-.overview-domain-panel {
- display: flex;
- margin-top: 10px;
- border: 1px solid var(--barBorderColor);
- background-color: #fff;
+.overview-quality-gate-badge-large.failed {
+ background: var(--red);
}
-.overview-domain-nutshell,
-.overview-domain-leak {
- position: relative;
- display: flex;
+.overview-quality-gate-badge-large.success {
+ background: var(--green);
+ height: 160px;
}
-.overview-domain-nutshell {
- flex: 3;
+.overview-quality-gate-badge-large h3 {
+ color: white;
}
-.overview-domain-nutshell .line-chart-backdrop {
- fill: #e5f1f9;
+.overview-quality-gate-conditions {
+ padding-bottom: calc(2 * var(--gridSize));
}
-.overview-domain-leak {
- flex: 2;
- background-color: var(--leakPrimaryColor);
+.overview-quality-gate-conditions-project-name {
+ padding: var(--gridSize) 0 var(--gridSize) calc(2 * var(--gridSize));
+ font-size: var(--bigFontSize);
+ background: var(--barBorderColor);
}
-.overview-domain-leak .overview-domain-measures {
- padding: 0;
+.overview-quality-gate-conditions-section-title {
+ padding: calc(2 * var(--gridSize)) calc(2 * var(--gridSize)) var(--gridSize)
+ calc(2 * var(--gridSize));
+ border-bottom: 1px solid var(--barBorderColor);
+ margin: 0;
+ font-size: var(--baseFontSize);
}
-.overview-domain-leak .line-chart-backdrop {
- fill: var(--leakSecondaryColor);
+.overview-quality-gate-conditions-list {
+ margin-bottom: calc(2 * var(--gridSize));
}
-.overview-domain-measures {
- position: relative;
- z-index: var(--normalZIndex);
- display: flex;
- flex: 1;
- padding: 0;
+.overview-quality-gate-conditions-list-collapse {
+ margin: calc(2 * var(--gridSize)) 0;
}
-.overview-domain-measure {
- flex: 1;
- text-align: center;
- padding: 15px 10px;
- position: relative;
+.overview-quality-gate-condition,
+.overview-quality-gate-condition:hover {
+ display: block;
+ color: var(--baseFontColor);
+ border: none;
+ transition: background-color 0.3s ease;
}
-.overview-domain-measures-big .overview-domain-measure {
- padding-top: 24px;
+.overview-quality-gate-condition:hover {
+ background-color: var(--rowHoverHighlight);
}
-.overview-domain-measure-value {
- line-height: 1;
- font-size: 36px;
- font-weight: 300;
- white-space: nowrap;
+.overview-quality-gate-condition-container {
+ padding: calc(1.5 * var(--gridSize)) var(--gridSize) calc(1.5 * var(--gridSize))
+ calc(4 * var(--gridSize));
+ border-bottom: 1px solid var(--barBorderColor);
}
-.overview-domain-secondary-measure-value {
+.overview-quality-gate-condition-value {
line-height: 1;
- font-size: 20px;
- font-weight: 300;
-}
-
-.overview-domain-measure-label {
- margin-top: 10px;
-}
-
-.overview-domain-measure-history-link {
- position: absolute;
- top: var(--gridSize);
- right: var(--gridSize);
- border-bottom: 0;
- visibility: hidden;
-}
-
-.overview-domain-measure-history-link span {
- border-bottom: 1px solid var(--lightBlue);
-}
+ margin-right: 10px;
+ font-size: var(--bigFontSize);
-.overview-domain-measure:hover .overview-domain-measure-history-link {
- visibility: visible;
+ /* for consistency with ratings */
+ min-width: 24px;
}
-.overview-domain-measure-sup {
+.overview-quality-gate-condition-value span {
display: inline-block;
vertical-align: top;
- margin-top: -4px;
- margin-left: 6px;
- font-size: var(--bigFontSize);
-}
-
-.overview-domain-timeline {
- position: absolute;
- z-index: var(--belowNormalZIndex);
- bottom: 0;
- left: 0;
- right: 0;
- animation: fadeIn 0.5s forwards;
-}
-
-.overview-domain-timeline .line-chart-path {
- fill: none;
- stroke: none;
-}
-
-.overview-domain-timeline-date {
- position: absolute;
- bottom: 2px;
- left: 5px;
- color: rgba(119, 119, 119, 0.6);
- font-size: 11px;
}
/*
* Meta
+ TODO REMOVE ME!!
*/
.overview-meta {
@@ -342,25 +212,6 @@
position: relative;
}
-.overview-meta-size-ncloc {
- display: inline-block;
- vertical-align: middle;
- width: 100px;
- text-align: center;
-}
-
-.overview-meta-size-ncloc.is-half-width {
- width: 50%;
- box-sizing: border-box;
-}
-
-.overview-meta-size-ncloc a,
-.overview-meta-size-ncloc span {
- line-height: var(--controlHeight);
- font-size: 18px;
- font-weight: 300;
-}
-
.overview-meta-size-lang-dist {
display: inline-block;
vertical-align: middle;
@@ -370,96 +221,6 @@
box-sizing: border-box;
}
-.overview-analysis {
- color: var(--secondFontColor);
-}
-
-.overview-analysis + .overview-analysis {
- margin-top: calc(2 * var(--gridSize));
-}
-
-.overview-analysis-graph {
- display: block;
- cursor: pointer;
- outline: none;
- border: none;
-}
-
-.overview-analysis-graph-popup {
- opacity: 0.8;
- padding: 0;
-}
-
-.overview-analysis-graph-tooltip {
- padding: 4px;
- pointer-events: none;
- font-size: var(--smallFontSize);
- overflow: hidden;
-}
-
-.overview-analysis-graph-tooltip-line {
- padding-bottom: 2px;
-}
-
-.overview-analysis-graph-tooltip-title {
- font-weight: bold;
- margin-bottom: 4px;
-}
-
-.overview-analysis-graph-tooltip-value {
- font-weight: bold;
-}
-
-.overview-analysis-graph-tooltip-description {
- max-width: 80px;
-}
-
-.overview-activity-events {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
-}
-
-.overview-analysis-event {
- display: inline-block;
-}
-
-.overview-analysis-event.badge {
- /* 260px to match the sidebar width on project dashboard */
- max-width: 260px;
- border-radius: 2px;
- font-weight: bold;
- font-size: var(--smallFontSize);
- letter-spacing: 0;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.overview-analysis-event + .overview-analysis-event {
- margin-top: 4px;
-}
-
-/*
- * Other
- */
-
-.overview-legend {
- position: absolute;
- bottom: 100%;
- left: 0;
- right: -1px;
- padding: 5px 0 2px;
- border: 1px solid var(--barBorderColor);
- background-color: var(--leakPrimaryColor);
- font-size: var(--mediumFontSize);
- text-align: center;
- transform: translateY(-4px);
-}
-
-.overview-legend-spaced-line {
- padding: 14px 0 10px;
-}
-
.overview-key {
width: 100%;
background-color: transparent !important;
@@ -491,6 +252,7 @@
/*
* PRs and SLBs
*/
+
.pr-overview {
max-width: 1020px;
margin: 0 auto;
@@ -500,10 +262,6 @@
max-width: 1260px;
}
-.pr-overview h3 {
- text-transform: uppercase;
-}
-
.pr-overview-failed-conditions {
flex: 0 0 240px;
}
@@ -513,78 +271,109 @@
}
.pr-overview .overview-quality-gate-condition {
- margin-right: 0;
margin-top: 12px;
- box-sizing: border-box;
- width: 100%;
+ background-color: #fff;
+ border-left: 5px solid;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
}
-.pr-overview-measurements-row {
- min-height: 85px;
- box-sizing: border-box;
- background: white;
- border: 1px solid var(--barBorderColor);
+.pr-overview .overview-quality-gate-condition-error {
+ border-color: var(--red);
}
-.pr-overview-measurements-row + .pr-overview-measurements-row {
- border-top: 0;
+.pr-overview .overview-quality-gate-condition-warn {
+ border-color: var(--orange);
}
-.pr-overview-measurements-value,
-.pr-overview-measurements-rating,
-.pr-overview-measurements-estimate {
- padding: calc(3 * var(--gridSize));
+.pr-overview .overview-quality-gate-condition:hover .overview-quality-gate-condition-container,
+.pr-overview .overview-quality-gate-condition:focus .overview-quality-gate-condition-container {
+ border-color: inherit;
}
-.pr-overview-measurements-value + .pr-overview-measurements-value {
- padding-right: 0;
- padding-left: 0;
+.pr-overview .overview-quality-gate-condition-metric,
+.pr-overview .overview-quality-gate-condition-period {
+ display: block;
+ max-width: 125px;
+ line-height: 16px;
+ font-size: var(--smallFontSize);
}
-.pr-overview-measurements-value .measure-empty {
- margin-top: -4px;
- font-size: var(--bigFontSize);
+.pr-overview .overview-quality-gate-condition-container {
+ min-width: 150px;
+ /* three lines by 16px and 4px margin */
+ min-height: 52px;
+ padding: var(--gridSize);
+ border-top: 1px solid var(--barBorderColor);
+ border-right: 1px solid var(--barBorderColor);
+ transition: border-color 0.3s ease;
+}
+
+.pr-overview .overview-quality-gate-condition-value {
+ font-size: var(--hugeFontSize);
}
-.pr-overview-measurements-rating,
-.pr-overview-measurements-estimate {
+.pr-overview .overview-quality-gate-badge-large {
+ width: 240px;
+ min-height: 160px;
+ color: var(--transparentWhite);
+}
+
+.pr-pverview .overview-measures-row {
+ min-height: 85px;
+}
+
+.pr-overview .overview-measures-aside {
flex-basis: 270px;
- text-align: right;
- box-sizing: border-box;
}
@media (max-width: 1200px) {
- .pr-overview-measurements-rating,
- .pr-overview-measurements-estimate {
+ .pr-overview .overview-measures-aside {
flex-basis: 220px;
}
}
-.pr-overview-measurements-estimate {
- background: var(--veryLightGreen);
-}
+/*
+ * ACTIVITY
+ */
-.pr-overview-measurements-estimate .label {
- margin-left: var(--gridSize);
+.overview-panel .activity-graph-legends {
text-align: right;
+ margin-top: -30px;
}
-.quality-gate-badge-large {
- width: 240px;
- min-height: 160px;
- padding: calc(2 * var(--gridSize));
- color: var(--transparentWhite);
- box-sizing: border-box;
+.overview-analysis {
+ color: var(--secondFontColor);
}
-.quality-gate-badge-large.failed {
- background: var(--red);
+.overview-analysis + .overview-analysis {
+ margin-top: calc(2 * var(--gridSize));
}
-.quality-gate-badge-large.success {
- background: var(--green);
+.overview-activity-events {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
}
-.quality-gate-badge-large h4 {
- color: white;
+.overview-analysis-event {
+ display: inline-block;
+}
+
+.overview-analysis-event.badge {
+ border-radius: 2px;
+ font-weight: bold;
+ font-size: var(--smallFontSize);
+ letter-spacing: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.overview-analysis-event + .overview-analysis-event {
+ margin-top: 4px;
+}
+
+.overview-panel .activity-graph-container {
+ min-height: 200px;
+ padding-bottom: 0;
}
diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/Analysis-test.tsx b/server/sonar-web/src/main/js/components/intl/__mocks__/DateFromNow.tsx
index cfeae8a7a8c..762fcb26a48 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/Analysis-test.tsx
+++ b/server/sonar-web/src/main/js/components/intl/__mocks__/DateFromNow.tsx
@@ -17,19 +17,14 @@
* 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 Analysis from '../Analysis';
+import { DateSource } from 'react-intl';
-const ANALYSIS = {
- key: '1',
- date: '2017-06-10T16:10:59+0200',
- events: [
- { key: '1', category: 'OTHER', name: 'test' },
- { key: '2', category: 'VERSION', name: '6.5-SNAPSHOT' }
- ]
-};
+interface Props {
+ children?: (formattedDate: string) => React.ReactNode;
+ date: DateSource;
+}
-it('should sort the events with version first', () => {
- expect(shallow(<Analysis analysis={ANALYSIS} qualifier="TRK" />)).toMatchSnapshot();
-});
+export default function DateFromNow({ children, date }: Props) {
+ return children && children(date.toString());
+}
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 534c4320f2d..67bb752c54f 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2643,17 +2643,22 @@ system.version_is_availble={version} is available
#------------------------------------------------------------------------------
overview.failed_conditions=Failed conditions
overview.X_more_failed_conditions={0} more failed conditions
-overview.metrics=Metrics
-overview.quality_gate=Quality Gate
+overview.X_conditions_failed={0} conditions failed
+overview.quality_gate=Quality Gate Status
overview.quality_gate_x=Quality Gate: {0}
overview.quality_gate_failed_with_x=with {0} errors
+overview.quality_gate_code_clean=Your code is clean!
+overview.quality_gate_all_conditions_passed=All conditions passed.
overview.you_should_define_quality_gate=You should define a quality gate on this project.
overview.quality_gate.ignored_conditions=Some Quality Gate conditions on New Code were ignored because of the small number of New Lines
overview.quality_gate.ignored_conditions.tooltip=At the start of a new code period, if very few lines have been added or modified, it might be difficult to reach the desired level of code coverage or duplications. To prevent Quality Gate failure when there's little that can be done about it, Quality Gate conditions about duplications in new code and coverage on new code are ignored until the number of new lines is at least 20.
overview.quality_gate.conditions_on_new_code=Only conditions on new code that are defined in the Quality Gate are checked. See the {link} associated to the project for details.
overview.quality_profiles=Quality Profiles
-overview.new_code_period_x=New code: {0}
-overview.started_x=started {0}
+overview.new_code_period_x=New Code: {0}
+overview.max_new_code_period_from_x=Max New Code from: {0}
+overview.started_x=Started {0}
+overview.new_code=New Code
+overview.overall_code=Overall Code
overview.previous_analysis_x=Previous analysis was {0}
overview.started_on_x=Started on {0}
overview.previous_analysis_on_x=Previous analysis on {0}
@@ -2668,6 +2673,11 @@ overview.project_activity.click_to_see=Click to see project activity
overview.external_links=External Links
overview.project_key.APP=Application Key
overview.project_key.TRK=Project Key
+overview.activity=Activity
+overview.recent_activity=Recent Activity
+overview.measures=Measures
+overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch.
+overview.measures.empty_link={learn_more_link} about the Clean as You Code approach.
overview.project.no_lines_of_code=This project has no lines of code.
overview.project.empty=This project is empty.
@@ -2677,23 +2687,25 @@ overview.project.main_branch_no_lines_of_code=The main branch has no lines of co
overview.project.main_branch_empty=The main branch of this project is empty.
overview.project.branch_needs_new_analysis=The branch data is incomplete. Run a new analysis to update it.
overview.coverage_on=Coverage on
-overview.coverage_on_X_lines=Coverage on {count} New Lines to cover
+overview.coverage_on_X_lines=Coverage on {count} Lines to cover
+overview.coverage_on_X_new_lines=Coverage on {count} New Lines to cover
overview.duplications_on=Duplications on
-overview.duplications_on_X=Duplications on {count} New Lines
+overview.duplications_on_X_lines=Duplications on {count} Lines
+overview.duplications_on_X_new_lines=Duplications on {count} New Lines
-overview.period.previous_version=since {0}
+overview.period.previous_version=Since {0}
# Old periods, necessary to display project that haven't been analyzed with new setting (MMF-1579)
-overview.period.previous_version_only_date=since previous version
-overview.period.previous_analysis=since previous analysis
-overview.period.days=last {0} days
-overview.period.version=since {0}
-overview.period.date=after {0}
-overview.period.manual_baseline=since {0}
+overview.period.previous_version_only_date=Since previous version
+overview.period.previous_analysis=Since previous analysis
+overview.period.days=Last {0} days
+overview.period.version=Since {0}
+overview.period.date=After {0}
+overview.period.manual_baseline=Since {0}
# New periods (MMF-1579)
-overview.period.number_of_days=from last {0} days
-overview.period.specific_analysis=since {0}
+overview.period.number_of_days=From last {0} days
+overview.period.specific_analysis=Since {0}
overview.gate.ERROR=Failed
overview.gate.WARN=Warning