describe('serializeQuery', () => { | describe('serializeQuery', () => { | ||||
it('should serialize query for api request', () => { | it('should serialize query for api request', () => { | ||||
expect(utils.serializeQuery(QUERY)).toEqual({ | expect(utils.serializeQuery(QUERY)).toEqual({ | ||||
from: '2017-04-27T08:21:32+0000', | |||||
project: 'foo', | project: 'foo', | ||||
}); | }); | ||||
expect(utils.serializeQuery({ ...QUERY, graph: GraphType.coverage, category: 'test' })).toEqual( | expect(utils.serializeQuery({ ...QUERY, graph: GraphType.coverage, category: 'test' })).toEqual( | ||||
{ | { | ||||
from: '2017-04-27T08:21:32+0000', | |||||
project: 'foo', | project: 'foo', | ||||
category: 'test', | category: 'test', | ||||
}, | }, |
export function serializeQuery(query: Query): RawQuery { | export function serializeQuery(query: Query): RawQuery { | ||||
return cleanQuery({ | return cleanQuery({ | ||||
category: serializeString(query.category), | category: serializeString(query.category), | ||||
from: serializeDate(query.from), | |||||
project: serializeString(query.project), | project: serializeString(query.project), | ||||
to: serializeDate(query.to), | |||||
}); | }); | ||||
} | } | ||||
import { ButtonSecondary, ChevronDownIcon, Dropdown, TextMuted } from 'design-system'; | import { ButtonSecondary, ChevronDownIcon, Dropdown, TextMuted } from 'design-system'; | ||||
import { sortBy } from 'lodash'; | import { sortBy } from 'lodash'; | ||||
import * as React from 'react'; | 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 { getLocalizedMetricName, translate } from '../../helpers/l10n'; | ||||
import { isDiffMetric } from '../../helpers/measures'; | import { isDiffMetric } from '../../helpers/measures'; | ||||
import { MetricKey, MetricType } from '../../types/metrics'; | import { MetricKey, MetricType } from '../../types/metrics'; | ||||
if (isDiffMetric(metric.key)) { | if (isDiffMetric(metric.key)) { | ||||
return false; | 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; | return false; | ||||
} | } | ||||
if (HIDDEN_METRICS.includes(metric.key as MetricKey)) { | if (HIDDEN_METRICS.includes(metric.key as MetricKey)) { |
import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
import { Theme, themeColor } from 'design-system'; | import { Theme, themeColor } from 'design-system'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { LINE_CHART_DASHES } from './utils'; | |||||
interface Props { | interface Props { | ||||
className?: string; | className?: string; | ||||
clipRule="evenodd" | clipRule="evenodd" | ||||
fillRule="evenodd" | fillRule="evenodd" | ||||
height={16} | height={16} | ||||
strokeLinejoin="round" | |||||
strokeMiterlimit={1.41421} | strokeMiterlimit={1.41421} | ||||
viewBox="0 0 16 16" | viewBox="0 0 16 16" | ||||
width={16} | width={16} | ||||
xmlnsXlink="http://www.w3.org/1999/xlink" | xmlnsXlink="http://www.w3.org/1999/xlink" | ||||
> | > | ||||
<path | <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={{ | style={{ | ||||
fill: themeColor(`graphLineColor.${index}` as Parameters<typeof themeColor>[0])({ | |||||
stroke: themeColor(`graphLineColor.${index}` as Parameters<typeof themeColor>[0])({ | |||||
theme, | theme, | ||||
}), | }), | ||||
strokeDasharray: LINE_CHART_DASHES[index], | |||||
}} | }} | ||||
/> | /> | ||||
</svg> | </svg> |
import userEvent from '@testing-library/user-event'; | import userEvent from '@testing-library/user-event'; | ||||
import { times } from 'lodash'; | import { times } from 'lodash'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { CCT_SOFTWARE_QUALITY_METRICS } from '../../../helpers/constants'; | |||||
import { parseDate } from '../../../helpers/dates'; | import { parseDate } from '../../../helpers/dates'; | ||||
import { mockHistoryItem, mockMeasureHistory } from '../../../helpers/mocks/project-activity'; | import { mockHistoryItem, mockMeasureHistory } from '../../../helpers/mocks/project-activity'; | ||||
import { mockMetric } from '../../../helpers/testMocks'; | import { mockMetric } from '../../../helpers/testMocks'; | ||||
await ui.clickOnMetric(MetricKey.code_smells); | await ui.clickOnMetric(MetricKey.code_smells); | ||||
await ui.clickOnMetric(MetricKey.coverage); | 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(); | 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.duplicated_lines_density); | ||||
await ui.removeMetric(MetricKey.test_success_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. | // Should show message that there's no data to be rendered. | ||||
expect(ui.noDataText.get()).toBeInTheDocument(); | expect(ui.noDataText.get()).toBeInTheDocument(); | ||||
}); | }); | ||||
// Add/remove metrics. | // Add/remove metrics. | ||||
addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }), | addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }), | ||||
deprecatedBadge: byText('deprecated'), | deprecatedBadge: byText('deprecated'), | ||||
bugsCheckbox: byRole('checkbox', { name: MetricKey.bugs }), | |||||
maintainabilityIssuesCheckbox: byRole('checkbox', { name: MetricKey.maintainability_issues }), | |||||
newBugsCheckbox: byRole('checkbox', { name: MetricKey.new_bugs }), | newBugsCheckbox: byRole('checkbox', { name: MetricKey.new_bugs }), | ||||
burnedBudgetCheckbox: byRole('checkbox', { name: MetricKey.burned_budget }), | burnedBudgetCheckbox: byRole('checkbox', { name: MetricKey.burned_budget }), | ||||
vulnerabilityCheckbox: byRole('checkbox', { name: MetricKey.vulnerabilities }), | vulnerabilityCheckbox: byRole('checkbox', { name: MetricKey.vulnerabilities }), | ||||
MetricKey.bugs, | MetricKey.bugs, | ||||
MetricKey.code_smells, | MetricKey.code_smells, | ||||
MetricKey.confirmed_issues, | MetricKey.confirmed_issues, | ||||
MetricKey.maintainability_issues, | |||||
MetricKey.vulnerabilities, | MetricKey.vulnerabilities, | ||||
MetricKey.blocker_violations, | MetricKey.blocker_violations, | ||||
MetricKey.lines_to_cover, | MetricKey.lines_to_cover, | ||||
return mockHistoryItem({ date, value: i.toString() }); | return mockHistoryItem({ date, value: i.toString() }); | ||||
}); | }); | ||||
history.push( | 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') }), | mockHistoryItem({ date: parseDate('2020-10-27T16:33:50+0200') }), | ||||
); | ); | ||||
measuresHistory.push(mockMeasureHistory({ metric, history })); | measuresHistory.push(mockMeasureHistory({ metric, history })); |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { chunk, flatMap, groupBy, sortBy } from 'lodash'; | import { chunk, flatMap, groupBy, sortBy } from 'lodash'; | ||||
import { CCT_SOFTWARE_QUALITY_METRICS } from '../../helpers/constants'; | |||||
import { getLocalizedMetricName, translate } from '../../helpers/l10n'; | import { getLocalizedMetricName, translate } from '../../helpers/l10n'; | ||||
import { localizeMetric } from '../../helpers/measures'; | import { localizeMetric } from '../../helpers/measures'; | ||||
import { get, save } from '../../helpers/storage'; | import { get, save } from '../../helpers/storage'; | ||||
], | ], | ||||
}; | }; | ||||
export const LINE_CHART_DASHES = [0, 3, 7]; | |||||
export function isCustomGraph(graph: GraphType) { | export function isCustomGraph(graph: GraphType) { | ||||
return graph === GraphType.custom; | return graph === GraphType.custom; | ||||
} | } | ||||
return generateCoveredLinesMetric(measure, measuresHistory); | return generateCoveredLinesMetric(measure, measuresHistory); | ||||
} | } | ||||
const metric = findMetric(measure.metric, metrics); | const metric = findMetric(measure.metric, metrics); | ||||
const isSoftwareQualityMetric = CCT_SOFTWARE_QUALITY_METRICS.includes( | |||||
metric?.key as MetricKey, | |||||
); | |||||
return { | 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, | name: measure.metric, | ||||
translatedName: metric ? getLocalizedMetricName(metric) : localizeMetric(measure.metric), | translatedName: metric ? getLocalizedMetricName(metric) : localizeMetric(measure.metric), | ||||
type: metric ? metric.type : MetricType.Integer, | |||||
type: !metric || isSoftwareQualityMetric ? MetricType.Integer : metric.type, | |||||
}; | }; | ||||
}), | }), | ||||
(serie) => | (serie) => |
import { isDefined } from '../../helpers/types'; | import { isDefined } from '../../helpers/types'; | ||||
import { MetricType } from '../../types/metrics'; | import { MetricType } from '../../types/metrics'; | ||||
import { Chart } from '../../types/types'; | import { Chart } from '../../types/types'; | ||||
import { LINE_CHART_DASHES } from '../activity-graph/utils'; | |||||
import './AdvancedTimeline.css'; | import './AdvancedTimeline.css'; | ||||
import './LineChart.css'; | import './LineChart.css'; | ||||
stroke={themeColor(`graphLineColor.${idx}` as Parameters<typeof themeColor>[0])({ | stroke={themeColor(`graphLineColor.${idx}` as Parameters<typeof themeColor>[0])({ | ||||
theme, | theme, | ||||
})} | })} | ||||
strokeDasharray={LINE_CHART_DASHES[idx]} | |||||
/> | /> | ||||
))} | ))} | ||||
</g> | </g> |
import Draggable, { DraggableBounds, DraggableCore, DraggableData } from 'react-draggable'; | import Draggable, { DraggableBounds, DraggableCore, DraggableData } from 'react-draggable'; | ||||
import { MetricType } from '../../types/metrics'; | import { MetricType } from '../../types/metrics'; | ||||
import { Chart } from '../../types/types'; | import { Chart } from '../../types/types'; | ||||
import { LINE_CHART_DASHES } from '../activity-graph/utils'; | |||||
export interface Props { | export interface Props { | ||||
basisCurve?: boolean; | basisCurve?: boolean; | ||||
clip-path: url(#chart-clip); | clip-path: url(#chart-clip); | ||||
fill: none; | fill: none; | ||||
stroke: ${({ index }) => themeColor(`graphLineColor.${index}` as CSSColor)}; | stroke: ${({ index }) => themeColor(`graphLineColor.${index}` as CSSColor)}; | ||||
stroke-dasharray: ${({ index }) => LINE_CHART_DASHES[index]}; | |||||
stroke-width: 2px; | stroke-width: 2px; | ||||
`; | `; | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,NaNL20,NaN" | d="M0,NaNL20,NaN" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,NaNZ" | d="M40,NaNZ" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,0L20,6" | d="M0,0L20,6" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,12Z" | d="M40,12Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> | ||||
class="line-chart-path line-chart-path-0" | class="line-chart-path line-chart-path-0" | ||||
d="M0,16L20,8" | d="M0,16L20,8" | ||||
stroke="rgb(85,170,223)" | stroke="rgb(85,170,223)" | ||||
stroke-dasharray="0" | |||||
/> | /> | ||||
<path | <path | ||||
class="line-chart-path line-chart-path-1" | class="line-chart-path line-chart-path-1" | ||||
d="M40,0Z" | d="M40,0Z" | ||||
stroke="rgb(58,127,173)" | stroke="rgb(58,127,173)" | ||||
stroke-dasharray="3" | |||||
/> | /> | ||||
</g> | </g> | ||||
<g> | <g> |
]; | ]; | ||||
export const DEPRECATED_ACTIVITY_METRICS = [ | export const DEPRECATED_ACTIVITY_METRICS = [ | ||||
...OLD_TAXONOMY_METRICS, | |||||
MetricKey.blocker_violations, | MetricKey.blocker_violations, | ||||
MetricKey.critical_violations, | MetricKey.critical_violations, | ||||
MetricKey.major_violations, | MetricKey.major_violations, | ||||
MetricKey.minor_violations, | MetricKey.minor_violations, | ||||
MetricKey.info_violations, | MetricKey.info_violations, | ||||
MetricKey.code_smells, | |||||
MetricKey.bugs, | |||||
MetricKey.vulnerabilities, | |||||
MetricKey.confirmed_issues, | MetricKey.confirmed_issues, | ||||
]; | ]; | ||||