aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstanislavh <stanislav.honcharov@sonarsource.com>2024-03-19 10:22:10 +0100
committersonartech <sonartech@sonarsource.com>2024-03-25 20:02:42 +0000
commit7565e9a2e350e1f6aec44c032f359c1c49fd97c0 (patch)
tree988b31efb4b21b7f9061c8455fed44432d3c1ba3
parent494316d9f27f7a64b762e37d4f8bc34e4b4370d7 (diff)
downloadsonarqube-7565e9a2e350e1f6aec44c032f359c1c49fd97c0.tar.gz
sonarqube-7565e9a2e350e1f6aec44c032f359c1c49fd97c0.zip
SONAR-21797 Show software qualites measure history in graph
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/utils.ts2
-rw-r--r--server/sonar-web/src/main/js/components/activity-graph/AddGraphMetric.tsx7
-rw-r--r--server/sonar-web/src/main/js/components/activity-graph/ChartLegend.tsx9
-rw-r--r--server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx27
-rw-r--r--server/sonar-web/src/main/js/components/activity-graph/utils.ts23
-rw-r--r--server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/charts/ZoomTimeLine.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap20
-rw-r--r--server/sonar-web/src/main/js/helpers/constants.ts4
10 files changed, 73 insertions, 25 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.ts
index 6e4d19e7379..2d940fa9691 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.ts
+++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.ts
@@ -162,12 +162,10 @@ describe('parseQuery', () => {
describe('serializeQuery', () => {
it('should serialize query for api request', () => {
expect(utils.serializeQuery(QUERY)).toEqual({
- from: '2017-04-27T08:21:32+0000',
project: 'foo',
});
expect(utils.serializeQuery({ ...QUERY, graph: GraphType.coverage, category: 'test' })).toEqual(
{
- from: '2017-04-27T08:21:32+0000',
project: 'foo',
category: 'test',
},
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.ts b/server/sonar-web/src/main/js/apps/projectActivity/utils.ts
index cc0a319a41a..171b70d1efe 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/utils.ts
+++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.ts
@@ -128,9 +128,7 @@ export function parseQuery(urlQuery: RawQuery): Query {
export function serializeQuery(query: Query): RawQuery {
return cleanQuery({
category: serializeString(query.category),
- from: serializeDate(query.from),
project: serializeString(query.project),
- to: serializeDate(query.to),
});
}
diff --git a/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetric.tsx b/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetric.tsx
index 7a224c9d3f7..6f26ed42735 100644
--- a/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetric.tsx
+++ b/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetric.tsx
@@ -20,7 +20,7 @@
import { ButtonSecondary, ChevronDownIcon, Dropdown, TextMuted } from 'design-system';
import { sortBy } from 'lodash';
import * as React from 'react';
-import { HIDDEN_METRICS } from '../../helpers/constants';
+import { CCT_SOFTWARE_QUALITY_METRICS, HIDDEN_METRICS } from '../../helpers/constants';
import { getLocalizedMetricName, translate } from '../../helpers/l10n';
import { isDiffMetric } from '../../helpers/measures';
import { MetricKey, MetricType } from '../../types/metrics';
@@ -66,7 +66,10 @@ export default class AddGraphMetric extends React.PureComponent<Props, State> {
if (isDiffMetric(metric.key)) {
return false;
}
- if ([MetricType.Data, MetricType.Distribution].includes(metric.type as MetricType)) {
+ if (
+ [MetricType.Data, MetricType.Distribution].includes(metric.type as MetricType) &&
+ !CCT_SOFTWARE_QUALITY_METRICS.includes(metric.key as MetricKey)
+ ) {
return false;
}
if (HIDDEN_METRICS.includes(metric.key as MetricKey)) {
diff --git a/server/sonar-web/src/main/js/components/activity-graph/ChartLegend.tsx b/server/sonar-web/src/main/js/components/activity-graph/ChartLegend.tsx
index b07e5c0d84d..b4ac04e5b24 100644
--- a/server/sonar-web/src/main/js/components/activity-graph/ChartLegend.tsx
+++ b/server/sonar-web/src/main/js/components/activity-graph/ChartLegend.tsx
@@ -21,6 +21,7 @@ import { useTheme } from '@emotion/react';
import classNames from 'classnames';
import { Theme, themeColor } from 'design-system';
import * as React from 'react';
+import { LINE_CHART_DASHES } from './utils';
interface Props {
className?: string;
@@ -36,7 +37,6 @@ export function ChartLegend({ index, className }: Readonly<Props>) {
clipRule="evenodd"
fillRule="evenodd"
height={16}
- strokeLinejoin="round"
strokeMiterlimit={1.41421}
viewBox="0 0 16 16"
width={16}
@@ -44,12 +44,13 @@ export function ChartLegend({ index, className }: Readonly<Props>) {
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<path
- className={classNames('line-chart-path line-chart-path-legend', `line-chart-path-${index}`)}
- d="M14.325 7.143v1.714q0 0.357-0.25 0.607t-0.607 0.25h-10.857q-0.357 0-0.607-0.25t-0.25-0.607v-1.714q0-0.357 0.25-0.607t0.607-0.25h10.857q0.357 0 0.607 0.25t0.25 0.607z"
+ className={classNames('line-chart-path', `line-chart-path-${index}`)}
+ d="M0 8 L 16 8"
style={{
- fill: themeColor(`graphLineColor.${index}` as Parameters<typeof themeColor>[0])({
+ stroke: themeColor(`graphLineColor.${index}` as Parameters<typeof themeColor>[0])({
theme,
}),
+ strokeDasharray: LINE_CHART_DASHES[index],
}}
/>
</svg>
diff --git a/server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx b/server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx
index 88131755a68..06eb0fa41af 100644
--- a/server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx
+++ b/server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx
@@ -21,6 +21,7 @@ import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { times } from 'lodash';
import * as React from 'react';
+import { CCT_SOFTWARE_QUALITY_METRICS } from '../../../helpers/constants';
import { parseDate } from '../../../helpers/dates';
import { mockHistoryItem, mockMeasureHistory } from '../../../helpers/mocks/project-activity';
import { mockMetric } from '../../../helpers/testMocks';
@@ -153,16 +154,22 @@ it('should correctly handle adding/removing custom metrics', async () => {
await ui.clickOnMetric(MetricKey.code_smells);
await ui.clickOnMetric(MetricKey.coverage);
- // Search for option.
- await ui.searchForMetric('bug');
- expect(ui.bugsCheckbox.get()).toBeInTheDocument();
+ // Search for option and select it
+ await ui.searchForMetric('maintainability');
expect(ui.vulnerabilityCheckbox.query()).not.toBeInTheDocument();
+ await ui.clickOnMetric(MetricKey.maintainability_issues);
- // Disable final metrics by clicking on the legend items.
- await ui.removeMetric(MetricKey.confirmed_issues);
+ // Disable percentage metrics by clicking on the legend items.
await ui.removeMetric(MetricKey.duplicated_lines_density);
await ui.removeMetric(MetricKey.test_success_density);
+ // We should see 1 graph
+ expect(ui.graphs.getAll()).toHaveLength(1);
+
+ // Disable final number metrics
+ await ui.removeMetric(MetricKey.confirmed_issues);
+ await ui.removeMetric(MetricKey.maintainability_issues);
+
// Should show message that there's no data to be rendered.
expect(ui.noDataText.get()).toBeInTheDocument();
});
@@ -176,7 +183,7 @@ function getPageObject() {
// Add/remove metrics.
addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }),
deprecatedBadge: byText('deprecated'),
- bugsCheckbox: byRole('checkbox', { name: MetricKey.bugs }),
+ maintainabilityIssuesCheckbox: byRole('checkbox', { name: MetricKey.maintainability_issues }),
newBugsCheckbox: byRole('checkbox', { name: MetricKey.new_bugs }),
burnedBudgetCheckbox: byRole('checkbox', { name: MetricKey.burned_budget }),
vulnerabilityCheckbox: byRole('checkbox', { name: MetricKey.vulnerabilities }),
@@ -255,6 +262,7 @@ function renderActivityGraph(
MetricKey.bugs,
MetricKey.code_smells,
MetricKey.confirmed_issues,
+ MetricKey.maintainability_issues,
MetricKey.vulnerabilities,
MetricKey.blocker_violations,
MetricKey.lines_to_cover,
@@ -269,7 +277,12 @@ function renderActivityGraph(
return mockHistoryItem({ date, value: i.toString() });
});
history.push(
- mockHistoryItem({ date: parseDate('2018-10-27T12:21:15+0200') }),
+ mockHistoryItem({
+ date: parseDate('2018-10-27T12:21:15+0200'),
+ value: CCT_SOFTWARE_QUALITY_METRICS.includes(metric)
+ ? JSON.stringify({ total: 2286 })
+ : '2286',
+ }),
mockHistoryItem({ date: parseDate('2020-10-27T16:33:50+0200') }),
);
measuresHistory.push(mockMeasureHistory({ metric, history }));
diff --git a/server/sonar-web/src/main/js/components/activity-graph/utils.ts b/server/sonar-web/src/main/js/components/activity-graph/utils.ts
index 74de8cc4116..a4bc18da96d 100644
--- a/server/sonar-web/src/main/js/components/activity-graph/utils.ts
+++ b/server/sonar-web/src/main/js/components/activity-graph/utils.ts
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { chunk, flatMap, groupBy, sortBy } from 'lodash';
+import { CCT_SOFTWARE_QUALITY_METRICS } from '../../helpers/constants';
import { getLocalizedMetricName, translate } from '../../helpers/l10n';
import { localizeMetric } from '../../helpers/measures';
import { get, save } from '../../helpers/storage';
@@ -46,6 +47,8 @@ const GRAPHS_METRICS: Dict<string[]> = {
],
};
+export const LINE_CHART_DASHES = [0, 3, 7];
+
export function isCustomGraph(graph: GraphType) {
return graph === GraphType.custom;
}
@@ -126,14 +129,24 @@ export function generateSeries(
return generateCoveredLinesMetric(measure, measuresHistory);
}
const metric = findMetric(measure.metric, metrics);
+ const isSoftwareQualityMetric = CCT_SOFTWARE_QUALITY_METRICS.includes(
+ metric?.key as MetricKey,
+ );
return {
- data: measure.history.map((analysis) => ({
- x: analysis.date,
- y: metric && metric.type === MetricType.Level ? analysis.value : Number(analysis.value),
- })),
+ data: measure.history.map((analysis) => {
+ let { value } = analysis;
+
+ if (value !== undefined && isSoftwareQualityMetric) {
+ value = JSON.parse(value).total;
+ }
+ return {
+ x: analysis.date,
+ y: metric?.type === MetricType.Level ? value : Number(value),
+ };
+ }),
name: measure.metric,
translatedName: metric ? getLocalizedMetricName(metric) : localizeMetric(measure.metric),
- type: metric ? metric.type : MetricType.Integer,
+ type: !metric || isSoftwareQualityMetric ? MetricType.Integer : metric.type,
};
}),
(serie) =>
diff --git a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx
index 79cd2a7bb29..6f60e1fc794 100644
--- a/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx
+++ b/server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx
@@ -36,6 +36,7 @@ import * as React from 'react';
import { isDefined } from '../../helpers/types';
import { MetricType } from '../../types/metrics';
import { Chart } from '../../types/types';
+import { LINE_CHART_DASHES } from '../activity-graph/utils';
import './AdvancedTimeline.css';
import './LineChart.css';
@@ -450,6 +451,7 @@ export class AdvancedTimelineClass extends React.PureComponent<Props, State> {
stroke={themeColor(`graphLineColor.${idx}` as Parameters<typeof themeColor>[0])({
theme,
})}
+ strokeDasharray={LINE_CHART_DASHES[idx]}
/>
))}
</g>
diff --git a/server/sonar-web/src/main/js/components/charts/ZoomTimeLine.tsx b/server/sonar-web/src/main/js/components/charts/ZoomTimeLine.tsx
index 0fac846503a..5e63ae9a97a 100644
--- a/server/sonar-web/src/main/js/components/charts/ZoomTimeLine.tsx
+++ b/server/sonar-web/src/main/js/components/charts/ZoomTimeLine.tsx
@@ -27,6 +27,7 @@ import * as React from 'react';
import Draggable, { DraggableBounds, DraggableCore, DraggableData } from 'react-draggable';
import { MetricType } from '../../types/metrics';
import { Chart } from '../../types/types';
+import { LINE_CHART_DASHES } from '../activity-graph/utils';
export interface Props {
basisCurve?: boolean;
@@ -412,6 +413,7 @@ const StyledPath = styled.path<{ index: number }>`
clip-path: url(#chart-clip);
fill: none;
stroke: ${({ index }) => themeColor(`graphLineColor.${index}` as CSSColor)};
+ stroke-dasharray: ${({ index }) => LINE_CHART_DASHES[index]};
stroke-width: 2px;
`;
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap
index 78d53f22355..65b69b04b6c 100644
--- a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap
@@ -247,11 +247,13 @@ exports[`should render correctly: Zoom enabled 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -509,11 +511,13 @@ exports[`should render correctly: basisCurve 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -771,11 +775,13 @@ exports[`should render correctly: default 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -1193,11 +1199,13 @@ exports[`should render correctly: format y tick 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -1463,11 +1471,13 @@ exports[`should render correctly: leakPeriodDate 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -1608,11 +1618,13 @@ exports[`should render correctly: level metric 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,NaNL20,NaN"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,NaNZ"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -1869,11 +1881,13 @@ exports[`should render correctly: no areas 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -2036,11 +2050,13 @@ exports[`should render correctly: rating metric 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,0L20,6"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,12Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -2298,11 +2314,13 @@ exports[`should render correctly: selected date 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
@@ -2585,11 +2603,13 @@ exports[`should render correctly: zoomSpeed 1`] = `
class="line-chart-path line-chart-path-0"
d="M0,16L20,8"
stroke="rgb(85,170,223)"
+ stroke-dasharray="0"
/>
<path
class="line-chart-path line-chart-path-1"
d="M40,0Z"
stroke="rgb(58,127,173)"
+ stroke-dasharray="3"
/>
</g>
<g>
diff --git a/server/sonar-web/src/main/js/helpers/constants.ts b/server/sonar-web/src/main/js/helpers/constants.ts
index f5b42b3adce..809bc472756 100644
--- a/server/sonar-web/src/main/js/helpers/constants.ts
+++ b/server/sonar-web/src/main/js/helpers/constants.ts
@@ -144,14 +144,12 @@ export const HIDDEN_METRICS = [
];
export const DEPRECATED_ACTIVITY_METRICS = [
+ ...OLD_TAXONOMY_METRICS,
MetricKey.blocker_violations,
MetricKey.critical_violations,
MetricKey.major_violations,
MetricKey.minor_violations,
MetricKey.info_violations,
- MetricKey.code_smells,
- MetricKey.bugs,
- MetricKey.vulnerabilities,
MetricKey.confirmed_issues,
];