diff options
author | stanislavh <stanislav.honcharov@sonarsource.com> | 2024-03-19 10:22:10 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-03-25 20:02:42 +0000 |
commit | 7565e9a2e350e1f6aec44c032f359c1c49fd97c0 (patch) | |
tree | 988b31efb4b21b7f9061c8455fed44432d3c1ba3 | |
parent | 494316d9f27f7a64b762e37d4f8bc34e4b4370d7 (diff) | |
download | sonarqube-7565e9a2e350e1f6aec44c032f359c1c49fd97c0.tar.gz sonarqube-7565e9a2e350e1f6aec44c032f359c1c49fd97c0.zip |
SONAR-21797 Show software qualites measure history in graph
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, ]; |