Commit 85e35a36 authored by Scott Hampton's avatar Scott Hampton Committed by Miguel Rincon

Code quality badges

Adding badges to the code quality for severity
parent de151f2a
...@@ -3,15 +3,21 @@ ...@@ -3,15 +3,21 @@
* Renders Code quality body text * Renders Code quality body text
* Fixed: [name] in [link]:[line] * Fixed: [name] in [link]:[line]
*/ */
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import ReportLink from '~/reports/components/report_link.vue'; import ReportLink from '~/reports/components/report_link.vue';
import { STATUS_SUCCESS } from '~/reports/constants'; import { STATUS_SUCCESS } from '~/reports/constants';
import { s__ } from '~/locale';
import { SEVERITY_CLASSES, SEVERITY_ICONS } from '../constants';
export default { export default {
name: 'CodequalityIssueBody', name: 'CodequalityIssueBody',
components: { components: {
GlIcon,
ReportLink, ReportLink,
}, },
directives: {
tooltip: GlTooltipDirective,
},
props: { props: {
status: { status: {
type: String, type: String,
...@@ -23,20 +29,44 @@ export default { ...@@ -23,20 +29,44 @@ export default {
}, },
}, },
computed: { computed: {
issueName() {
return `${this.severityLabel} - ${this.issue.name}`;
},
isStatusSuccess() { isStatusSuccess() {
return this.status === STATUS_SUCCESS; return this.status === STATUS_SUCCESS;
}, },
severityClass() {
return SEVERITY_CLASSES[this.issue.severity] || SEVERITY_CLASSES.unknown;
},
severityIcon() {
return SEVERITY_ICONS[this.issue.severity] || SEVERITY_ICONS.unknown;
},
severityLabel() {
return this.$options.severityText[this.issue.severity] || this.$options.severityText.unknown;
},
},
severityText: {
info: s__('severity|Info'),
minor: s__('severity|Minor'),
major: s__('severity|Major'),
critical: s__('severity|Critical'),
blocker: s__('severity|Blocker'),
unknown: s__('severity|Unknown'),
}, },
}; };
</script> </script>
<template> <template>
<div class="report-block-list-issue-description gl-mt-2 gl-mb-2"> <div class="gl-display-flex gl-mt-2 gl-mb-2 gl-w-full">
<div class="report-block-list-issue-description-text"> <span :class="severityClass" class="gl-mr-5" data-testid="codequality-severity-icon">
<template v-if="isStatusSuccess">{{ s__('ciReport|Fixed:') }}</template> <gl-icon v-tooltip="severityLabel" :name="severityIcon" :size="12" />
</span>
<div class="gl-flex-fill-1">
<div>
<strong v-if="isStatusSuccess">{{ s__('ciReport|Fixed:') }}</strong>
{{ issueName }}
</div>
{{ issue.name }} <report-link v-if="issue.path" :issue="issue" />
</div> </div>
<report-link v-if="issue.path" :issue="issue" />
</div> </div>
</template> </template>
export const SEVERITY_CLASSES = {
info: 'text-primary-400',
minor: 'text-warning-200',
major: 'text-warning-400',
critical: 'text-danger-600',
blocker: 'text-danger-800',
unknown: 'text-secondary-400',
};
export const SEVERITY_ICONS = {
info: 'severity-info',
minor: 'severity-low',
major: 'severity-medium',
critical: 'severity-high',
blocker: 'severity-critical',
unknown: 'severity-unknown',
};
...@@ -78,6 +78,7 @@ export default { ...@@ -78,6 +78,7 @@ export default {
:has-issues="hasCodequalityIssues" :has-issues="hasCodequalityIssues"
:component="$options.componentNames.CodequalityIssueBody" :component="$options.componentNames.CodequalityIssueBody"
:popover-options="codequalityPopover" :popover-options="codequalityPopover"
:show-report-section-status-icon="false"
class="js-codequality-widget mr-widget-border-top mr-report" class="js-codequality-widget mr-widget-border-top mr-report"
/> />
</template> </template>
---
title: Show code quality severity rating in the merge request details page
merge_request: 46829
author:
type: changed
...@@ -123,6 +123,7 @@ export const codequalityParsedIssues = [ ...@@ -123,6 +123,7 @@ export const codequalityParsedIssues = [
path: 'Gemfile.lock', path: 'Gemfile.lock',
line: 12, line: 12,
urlPath: 'foo/Gemfile.lock', urlPath: 'foo/Gemfile.lock',
severity: 'minor',
}, },
]; ];
......
...@@ -37,7 +37,7 @@ describe('Report issues', () => { ...@@ -37,7 +37,7 @@ describe('Report issues', () => {
it('should render "Fixed" keyword', () => { it('should render "Fixed" keyword', () => {
expect(vm.$el.textContent).toContain('Fixed'); expect(vm.$el.textContent).toContain('Fixed');
expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual( expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual(
'Fixed: Insecure Dependency in Gemfile.lock:12', 'Fixed: Minor - Insecure Dependency in Gemfile.lock:12',
); );
}); });
}); });
......
...@@ -37,7 +37,7 @@ describe('Report issue', () => { ...@@ -37,7 +37,7 @@ describe('Report issue', () => {
it('should render "Fixed" keyword', () => { it('should render "Fixed" keyword', () => {
expect(vm.$el.textContent).toContain('Fixed'); expect(vm.$el.textContent).toContain('Fixed');
expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual( expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual(
'Fixed: Insecure Dependency in Gemfile.lock:12', 'Fixed: Minor - Insecure Dependency in Gemfile.lock:12',
); );
}); });
}); });
......
...@@ -32636,6 +32636,9 @@ msgstr "" ...@@ -32636,6 +32636,9 @@ msgstr ""
msgid "security Reports|There was an error creating the merge request" msgid "security Reports|There was an error creating the merge request"
msgstr "" msgstr ""
msgid "severity|Blocker"
msgstr ""
msgid "severity|Critical" msgid "severity|Critical"
msgstr "" msgstr ""
...@@ -32648,9 +32651,15 @@ msgstr "" ...@@ -32648,9 +32651,15 @@ msgstr ""
msgid "severity|Low" msgid "severity|Low"
msgstr "" msgstr ""
msgid "severity|Major"
msgstr ""
msgid "severity|Medium" msgid "severity|Medium"
msgstr "" msgstr ""
msgid "severity|Minor"
msgstr ""
msgid "severity|None" msgid "severity|None"
msgstr "" msgstr ""
......
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import component from '~/reports/codequality_report/components/codequality_issue_body.vue'; import component from '~/reports/codequality_report/components/codequality_issue_body.vue';
import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants'; import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
describe('code quality issue body issue body', () => { describe('code quality issue body issue body', () => {
let wrapper; let wrapper;
const findSeverityIcon = () => wrapper.findByTestId('codequality-severity-icon');
const findGlIcon = () => wrapper.find(GlIcon);
const codequalityIssue = { const codequalityIssue = {
name: name:
'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses', 'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses',
...@@ -14,13 +19,15 @@ describe('code quality issue body issue body', () => { ...@@ -14,13 +19,15 @@ describe('code quality issue body issue body', () => {
urlPath: '/Gemfile.lock#L22', urlPath: '/Gemfile.lock#L22',
}; };
const mountWithStatus = initialStatus => { const createComponent = (initialStatus, issue = codequalityIssue) => {
wrapper = shallowMount(component, { wrapper = extendedWrapper(
propsData: { shallowMount(component, {
issue: codequalityIssue, propsData: {
status: initialStatus, issue,
}, status: initialStatus,
}); },
}),
);
}; };
afterEach(() => { afterEach(() => {
...@@ -28,17 +35,43 @@ describe('code quality issue body issue body', () => { ...@@ -28,17 +35,43 @@ describe('code quality issue body issue body', () => {
wrapper = null; wrapper = null;
}); });
describe('severity rating', () => {
it.each`
severity | iconClass | iconName
${'info'} | ${'text-primary-400'} | ${'severity-info'}
${'minor'} | ${'text-warning-200'} | ${'severity-low'}
${'major'} | ${'text-warning-400'} | ${'severity-medium'}
${'critical'} | ${'text-danger-600'} | ${'severity-high'}
${'blocker'} | ${'text-danger-800'} | ${'severity-critical'}
${'unknown'} | ${'text-secondary-400'} | ${'severity-unknown'}
${'invalid'} | ${'text-secondary-400'} | ${'severity-unknown'}
`(
'renders correct icon for "$severity" severity rating',
({ severity, iconClass, iconName }) => {
createComponent(STATUS_FAILED, {
...codequalityIssue,
severity,
});
const icon = findGlIcon();
expect(findSeverityIcon().classes()).toContain(iconClass);
expect(icon.exists()).toBe(true);
expect(icon.props('name')).toBe(iconName);
},
);
});
describe('with success', () => { describe('with success', () => {
it('renders fixed label', () => { it('renders fixed label', () => {
mountWithStatus(STATUS_SUCCESS); createComponent(STATUS_SUCCESS);
expect(wrapper.text()).toContain('Fixed'); expect(wrapper.text()).toContain('Fixed');
}); });
}); });
describe('without success', () => { describe('without success', () => {
it('renders fixed label', () => { it('does not render fixed label', () => {
mountWithStatus(STATUS_FAILED); createComponent(STATUS_FAILED);
expect(wrapper.text()).not.toContain('Fixed'); expect(wrapper.text()).not.toContain('Fixed');
}); });
...@@ -46,7 +79,7 @@ describe('code quality issue body issue body', () => { ...@@ -46,7 +79,7 @@ describe('code quality issue body issue body', () => {
describe('name', () => { describe('name', () => {
it('renders name', () => { it('renders name', () => {
mountWithStatus(STATUS_NEUTRAL); createComponent(STATUS_NEUTRAL);
expect(wrapper.text()).toContain(codequalityIssue.name); expect(wrapper.text()).toContain(codequalityIssue.name);
}); });
...@@ -54,7 +87,7 @@ describe('code quality issue body issue body', () => { ...@@ -54,7 +87,7 @@ describe('code quality issue body issue body', () => {
describe('path', () => { describe('path', () => {
it('renders the report-link path using the correct code quality issue', () => { it('renders the report-link path using the correct code quality issue', () => {
mountWithStatus(STATUS_NEUTRAL); createComponent(STATUS_NEUTRAL);
expect(wrapper.find('report-link-stub').props('issue')).toBe(codequalityIssue); expect(wrapper.find('report-link-stub').props('issue')).toBe(codequalityIssue);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment