@@ -17,6 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { ComponentQualifier } from '../../../types/component'; | |||
import { MetricKey } from '../../../types/metrics'; | |||
import * as utils from '../utils'; | |||
@@ -151,3 +152,75 @@ describe('serializeQuery', () => { | |||
expect(utils.serializeQuery(query)).toBe(utils.serializeQuery(query)); | |||
}); | |||
}); | |||
describe('extract measure', () => { | |||
const componentBuilder = (qual: ComponentQualifier): T.ComponentMeasure => { | |||
return { | |||
qualifier: qual, | |||
key: '1', | |||
name: 'TEST', | |||
measures: [ | |||
{ | |||
metric: 'alert_status', | |||
value: '3.2', | |||
periods: [{ index: 1, value: '0.0' }] | |||
}, | |||
{ | |||
metric: 'releasability_rating', | |||
value: '3.2', | |||
periods: [{ index: 1, value: '0.0' }] | |||
}, | |||
{ | |||
metric: 'releasability_effort', | |||
value: '3.2', | |||
periods: [{ index: 1, value: '0.0' }] | |||
} | |||
] | |||
}; | |||
}; | |||
it('should ban quality gate for app', () => { | |||
const measure = utils.banQualityGateMeasure(componentBuilder(ComponentQualifier.Application)); | |||
expect(measure).toHaveLength(0); | |||
}); | |||
it('should ban quality gate for portfolio', () => { | |||
const measure = utils.banQualityGateMeasure(componentBuilder(ComponentQualifier.Portfolio)); | |||
expect(measure).toHaveLength(3); | |||
}); | |||
it('should ban quality gate for file', () => { | |||
const measure = utils.banQualityGateMeasure(componentBuilder(ComponentQualifier.File)); | |||
expect(measure).toHaveLength(2); | |||
measure.forEach(({ metric }) => { | |||
expect(['releasability_rating', 'releasability_effort']).toContain(metric); | |||
}); | |||
}); | |||
}); | |||
describe('Component classification', () => { | |||
const componentBuilder = (qual: ComponentQualifier): T.ComponentMeasure => { | |||
return { | |||
qualifier: qual, | |||
key: '1', | |||
name: 'TEST' | |||
}; | |||
}; | |||
it('should be file type', () => { | |||
[ComponentQualifier.File, ComponentQualifier.TestFile].forEach(qual => { | |||
const component = componentBuilder(qual); | |||
expect(utils.isFileType(component)).toBeTruthy(); | |||
}); | |||
}); | |||
it('should be view type', () => { | |||
[ | |||
ComponentQualifier.Portfolio, | |||
ComponentQualifier.SubPortfolio, | |||
ComponentQualifier.Application | |||
].forEach(qual => { | |||
const component = componentBuilder(qual); | |||
expect(utils.isViewType(component)).toBeTruthy(); | |||
}); | |||
}); | |||
}); |
@@ -47,6 +47,7 @@ import { BranchLike } from '../../../types/branch-like'; | |||
import Sidebar from '../sidebar/Sidebar'; | |||
import '../style.css'; | |||
import { | |||
banQualityGateMeasure, | |||
getMeasuresPageMetricKeys, | |||
groupByDomains, | |||
hasBubbleChart, | |||
@@ -136,24 +137,13 @@ export class App extends React.PureComponent<Props, State> { | |||
const filteredKeys = getMeasuresPageMetricKeys(metrics, branchLike); | |||
const banQualityGate = ({ measures = [], qualifier }: T.ComponentMeasure) => { | |||
const bannedMetrics: string[] = []; | |||
if (!['VW', 'SVW'].includes(qualifier)) { | |||
bannedMetrics.push('alert_status', 'security_review_rating'); | |||
} | |||
if (qualifier === 'APP') { | |||
bannedMetrics.push('releasability_rating', 'releasability_effort'); | |||
} | |||
return measures.filter(measure => !bannedMetrics.includes(measure.metric)); | |||
}; | |||
getMeasuresAndMeta(componentKey, filteredKeys, { | |||
additionalFields: 'periods', | |||
...getBranchLikeQuery(branchLike) | |||
}).then( | |||
({ component, periods }) => { | |||
if (this.mounted) { | |||
const measures = banQualityGate(component).map(measure => | |||
const measures = banQualityGateMeasure(component).map(measure => | |||
enhanceMeasure(measure, metrics) | |||
); | |||
@@ -46,13 +46,26 @@ export const domains: Domains = { | |||
MetricKey.new_vulnerabilities, | |||
MetricKey.new_security_rating, | |||
MetricKey.new_security_remediation_effort, | |||
MetricKey.new_security_hotspots, | |||
'overall_category', | |||
MetricKey.vulnerabilities, | |||
MetricKey.security_rating, | |||
MetricKey.security_remediation_effort, | |||
MetricKey.security_hotspots | |||
MetricKey.security_remediation_effort | |||
] | |||
}, | |||
SecurityReview: { | |||
categories: ['new_code_category', 'overall_category'], | |||
order: [ | |||
'new_code_category', | |||
MetricKey.new_security_hotspots, | |||
MetricKey.new_security_review_rating, | |||
MetricKey.new_security_hotspots_reviewed, | |||
'overall_category', | |||
MetricKey.security_hotspots, | |||
MetricKey.security_review_rating, | |||
MetricKey.security_hotspots_reviewed | |||
] | |||
}, | |||
@@ -24,6 +24,7 @@ import { enhanceMeasure } from '../../components/measure/utils'; | |||
import { isBranch, isPullRequest } from '../../helpers/branch-like'; | |||
import { getDisplayMetrics, isDiffMetric } from '../../helpers/measures'; | |||
import { BranchLike } from '../../types/branch-like'; | |||
import { ComponentQualifier } from '../../types/component'; | |||
import { bubbles } from './config/bubbles'; | |||
import { domains } from './config/domains'; | |||
@@ -36,6 +37,7 @@ export const KNOWN_DOMAINS = [ | |||
'Releasability', | |||
'Reliability', | |||
'Security', | |||
'SecurityReview', | |||
'Maintainability', | |||
'Coverage', | |||
'Duplications', | |||
@@ -103,11 +105,31 @@ export function enhanceComponent( | |||
} | |||
export function isFileType(component: T.ComponentMeasure): boolean { | |||
return ['FIL', 'UTS'].includes(component.qualifier); | |||
return [ComponentQualifier.File, ComponentQualifier.TestFile].includes( | |||
component.qualifier as ComponentQualifier | |||
); | |||
} | |||
export function isViewType(component: T.ComponentMeasure): boolean { | |||
return ['VW', 'SVW', 'APP'].includes(component.qualifier); | |||
return [ | |||
ComponentQualifier.Portfolio, | |||
ComponentQualifier.SubPortfolio, | |||
ComponentQualifier.Application | |||
].includes(component.qualifier as ComponentQualifier); | |||
} | |||
export function banQualityGateMeasure({ | |||
measures = [], | |||
qualifier | |||
}: T.ComponentMeasure): T.Measure[] { | |||
const bannedMetrics: string[] = []; | |||
if (ComponentQualifier.Portfolio !== qualifier && ComponentQualifier.SubPortfolio !== qualifier) { | |||
bannedMetrics.push('alert_status'); | |||
} | |||
if (qualifier === ComponentQualifier.Application) { | |||
bannedMetrics.push('releasability_rating', 'releasability_effort'); | |||
} | |||
return measures.filter(measure => !bannedMetrics.includes(measure.metric)); | |||
} | |||
export const groupByDomains = memoize((measures: T.MeasureEnhanced[]) => { |
@@ -329,7 +329,7 @@ export default class MeasuresOverlay extends React.PureComponent<Props, State> { | |||
<div className="measures-viewer-card" key={domain}> | |||
<div className="measures"> | |||
<div className="measure measure-big"> | |||
<span className="measure-name">{domain}</span> | |||
<span className="measure-name">{translate('metric_domain', domain)}</span> | |||
</div> | |||
{sortBy( | |||
measures.filter(measure => measure.value !== undefined), |
@@ -1146,7 +1146,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Complexity | |||
metric_domain.Complexity | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1190,7 +1190,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Size | |||
metric_domain.Size | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1312,7 +1312,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Coverage | |||
metric_domain.Coverage | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1382,7 +1382,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Reliability | |||
metric_domain.Reliability | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1430,7 +1430,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Security | |||
metric_domain.Security | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1474,7 +1474,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Tests | |||
metric_domain.Tests | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1544,7 +1544,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Issues | |||
metric_domain.Issues | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1601,7 +1601,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Duplications | |||
metric_domain.Duplications | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure | |||
@@ -1671,7 +1671,7 @@ exports[`should render source file 2`] = ` | |||
<span | |||
className="measure-name" | |||
> | |||
Maintainability | |||
metric_domain.Maintainability | |||
</span> | |||
</div> | |||
<MeasuresOverlayMeasure |
@@ -96,8 +96,10 @@ export enum MetricKey { | |||
new_reliability_rating = 'new_reliability_rating', | |||
new_reliability_remediation_effort = 'new_reliability_remediation_effort', | |||
new_security_hotspots = 'new_security_hotspots', | |||
new_security_hotspots_reviewed = 'new_security_hotspots_reviewed', | |||
new_security_rating = 'new_security_rating', | |||
new_security_remediation_effort = 'new_security_remediation_effort', | |||
new_security_review_rating = 'new_security_review_rating', | |||
new_sqale_debt_ratio = 'new_sqale_debt_ratio', | |||
new_technical_debt = 'new_technical_debt', | |||
new_uncovered_conditions = 'new_uncovered_conditions', | |||
@@ -118,6 +120,7 @@ export enum MetricKey { | |||
reliability_remediation_effort = 'reliability_remediation_effort', | |||
reopened_issues = 'reopened_issues', | |||
security_hotspots = 'security_hotspots', | |||
security_hotspots_reviewed = 'security_hotspots_reviewed', | |||
security_rating = 'security_rating', | |||
security_rating_effort = 'security_rating_effort', | |||
security_remediation_effort = 'security_remediation_effort', |
@@ -2113,14 +2113,18 @@ metric.new_security_hotspots.name=New Security Hotspots | |||
metric.new_security_hotspots.short_name=Security Hotspots | |||
metric.new_security_hotspots_reviewed.description=Security Hotspots Reviewed on New Code | |||
metric.new_security_hotspots_reviewed.name=Security Hotspots Reviewed on New Code | |||
metric.new_security_hotspots_reviewed.short_name=Security Hotspots Reviewed | |||
metric.new_security_rating.description=Security rating on new code | |||
metric.new_security_rating.name=Security Rating on New Code | |||
metric.new_security_rating.extra_short_name=Rating | |||
metric.new_security_review_rating.description=Security Review rating on new code | |||
metric.new_security_review_rating.name=Security Review Rating on New Code | |||
metric.new_security_remediation_effort.description=Security remediation effort on new code | |||
metric.new_security_remediation_effort.name=Security Remediation Effort on New Code | |||
metric.new_security_remediation_effort.extra_short_name=Remediation Effort | |||
metric.new_security_review_rating.description=Security Review Rating on New Code | |||
metric.new_security_review_rating.name=Security Review Rating on New Code | |||
metric.new_security_review_rating.extra_short_name=Rating | |||
metric.new_sqale_debt_ratio.description=Technical Debt Ratio of new/changed code. | |||
metric.new_sqale_debt_ratio.name=Technical Debt Ratio on New Code | |||
metric.new_sqale_debt_ratio.short_name=Debt Ratio on new code | |||
@@ -2217,6 +2221,9 @@ metric.rfc_distribution.description=Class distribution /RFC | |||
metric.rfc_distribution.name=Class Distribution / RFC | |||
metric.security_hotspots.description=Security Hotspots | |||
metric.security_hotspots.name=Security Hotspots | |||
metric.security_hotspots_reviewed.description=Security Hotspots Reviewed | |||
metric.security_hotspots_reviewed.name=Security Hotspots Reviewed | |||
metric.security_hotspots_reviewed.extra_short_name=Hotspots Reviewed | |||
metric.security_rating.description=Security rating | |||
metric.security_rating.name=Security Rating | |||
metric.security_rating.extra_short_name=Rating | |||
@@ -2230,10 +2237,7 @@ metric.security_remediation_effort.name=Security Remediation Effort | |||
metric.security_remediation_effort.extra_short_name=Remediation Effort | |||
metric.security_review_rating.description=Security Review Rating | |||
metric.security_review_rating.name=Security Review Rating | |||
metric.security_review_rating.extra_short_name=Review Rating | |||
metric.security_hotspots_reviewed.description=Security Hotspots Reviewed | |||
metric.security_hotspots_reviewed.name=Security Hotspots Reviewed | |||
metric.security_hotspots_reviewed.extra_short_name=Hotspots Reviewed | |||
metric.security_review_rating.extra_short_name=Rating | |||
metric.skipped_tests.description=Number of skipped unit tests | |||
metric.skipped_tests.name=Skipped Unit Tests | |||
metric.skipped_tests.short_name=Skipped | |||
@@ -2861,6 +2865,7 @@ component_measures.overview.Duplications.description=See duplications' long-term | |||
component_measures.domain_facets.Reliability.help=Issues in this domain mark code where you will get behavior other than what was expected. | |||
component_measures.domain_facets.Maintainability.help=Issues in this domain mark code that will be more difficult to update competently than it should. | |||
component_measures.domain_facets.Security.help=Issues in this domain mark potential weaknesses to hackers. | |||
component_measures.domain_facets.SecurityReview.help=This domain represents potential security risks in the form of hotspots and their review status. | |||
component_measures.domain_facets.Complexity.help=How simple or complicated the control flow of the application is. Cyclomatic Complexity measures the minimum number of test cases required for full test coverage. Cognitive Complexity is a measure of how difficult the application is to understand | |||
component_measures.facet_category.new_code_category=On new code |