Commit d59eb9c9 authored by Sam Beckham's avatar Sam Beckham Committed by Mark Florian

Adds a remediation badge on vuln list

Adds, translates, and tests a remediation badge on the vulnerability
list component.
parent 2abade0e
<script>
import { GlBadge, GlPopover } from '@gitlab/ui';
export default {
name: 'RemediatedBadge',
components: {
GlBadge,
GlPopover,
},
};
</script>
<template>
<div class="d-inline-block">
<gl-badge ref="badge" variant="info">{{ __('Remediated: needs review') }}</gl-badge>
<gl-popover
ref="popover"
:content="
__(
'The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status.',
)
"
:target="() => $refs.badge.$el"
:title="__('Vulnerability remediated. Review before resolving.')"
placement="top"
triggers="hover focus"
/>
</div>
</template>
<script> <script>
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import { GlEmptyState, GlLink, GlSkeletonLoading, GlTable } from '@gitlab/ui'; import { GlEmptyState, GlLink, GlSkeletonLoading, GlTable } from '@gitlab/ui';
import RemediatedBadge from './remediated_badge.vue';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue'; import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
export default { export default {
...@@ -10,6 +11,7 @@ export default { ...@@ -10,6 +11,7 @@ export default {
GlLink, GlLink,
GlSkeletonLoading, GlSkeletonLoading,
GlTable, GlTable,
RemediatedBadge,
SeverityBadge, SeverityBadge,
}, },
props: { props: {
...@@ -77,9 +79,10 @@ export default { ...@@ -77,9 +79,10 @@ export default {
</template> </template>
<template #cell(title)="{ item }"> <template #cell(title)="{ item }">
<gl-link class="text-body js-description" :href="vulnerabilityPath(item)">{{ <gl-link class="text-body js-description" :href="vulnerabilityPath(item)">
item.title {{ item.title }}
}}</gl-link> </gl-link>
<remediated-badge v-if="item.resolved_on_default_branch" class="ml-2" />
</template> </template>
<template #table-busy> <template #table-busy>
......
// eslint-disable-next-line import/prefer-default-export export const generateVulnerabilities = () => [
export const vulnerabilities = [
{ {
id: 'id_0', id: 'id_0',
title: 'Vuln1', title: 'Vulnerability 1',
severity: 'critical', severity: 'critical',
state: 'dismissed', state: 'dismissed',
}, },
{ {
id: 'id_1', id: 'id_1',
title: 'Vuln2', title: 'Vulnerability 2',
severity: 'high', severity: 'high',
state: 'opened', state: 'opened',
}, },
]; ];
export const vulnerabilities = generateVulnerabilities();
import { shallowMount } from '@vue/test-utils';
import { GlBadge, GlPopover } from '@gitlab/ui';
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue';
const POPOVER_TITLE = 'Vulnerability remediated. Review before resolving.';
const POPOVER_CONTENT =
'The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status.';
describe('Remediated badge component', () => {
let wrapper;
const createWrapper = () => {
return shallowMount(RemediatedBadge);
};
beforeEach(() => {
wrapper = createWrapper();
});
afterEach(() => wrapper.destroy());
it('should link the badge and the popover', () => {
const badge = wrapper.find(GlBadge);
const { popover } = wrapper.vm.$refs;
expect(popover.$attrs.target()).toEqual(badge.element);
});
it('should pass down the data to the popover', () => {
const popoverAttributes = wrapper.find(GlPopover).attributes();
expect(popoverAttributes.title).toEqual(POPOVER_TITLE);
expect(popoverAttributes.content).toEqual(POPOVER_CONTENT);
});
});
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlEmptyState, GlSkeletonLoading } from '@gitlab/ui'; import { GlEmptyState, GlSkeletonLoading } from '@gitlab/ui';
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue';
import VulnerabilityList from 'ee/vulnerabilities/components/vulnerability_list.vue'; import VulnerabilityList from 'ee/vulnerabilities/components/vulnerability_list.vue';
import { vulnerabilities } from './mock_data'; import { generateVulnerabilities } from './mock_data';
describe('Vulnerability list component', () => { describe('Vulnerability list component', () => {
let wrapper; let wrapper;
...@@ -14,15 +15,23 @@ describe('Vulnerability list component', () => { ...@@ -14,15 +15,23 @@ describe('Vulnerability list component', () => {
vulnerabilities: [], vulnerabilities: [],
...props, ...props,
}, },
stubs: {
GlPopover: true,
},
attachToDocument: true,
}); });
}; };
const findCell = label => wrapper.find(`.js-${label}`); const findCell = label => wrapper.find(`.js-${label}`);
const findRow = (index = 0) => wrapper.findAll('tbody tr').at(index);
afterEach(() => wrapper.destroy()); afterEach(() => wrapper.destroy());
describe('with vulnerabilities', () => { describe('with vulnerabilities', () => {
let vulnerabilities;
beforeEach(() => { beforeEach(() => {
vulnerabilities = generateVulnerabilities();
wrapper = createWrapper({ vulnerabilities }); wrapper = createWrapper({ vulnerabilities });
}); });
...@@ -48,6 +57,30 @@ describe('Vulnerability list component', () => { ...@@ -48,6 +57,30 @@ describe('Vulnerability list component', () => {
}); });
}); });
describe('when a vulnerability is resolved on the default branch', () => {
let vulnerabilities;
beforeEach(() => {
vulnerabilities = generateVulnerabilities();
vulnerabilities[0].resolved_on_default_branch = true;
wrapper = createWrapper({ vulnerabilities });
});
it('should render the remediated info badge on the first vulnerability', () => {
const row = findRow(0);
const badge = row.find(RemediatedBadge);
expect(badge.exists()).toEqual(true);
});
it('should not render the remediated info badge on the second vulnerability', () => {
const row = findRow(1);
const badge = row.find(RemediatedBadge);
expect(badge.exists()).toEqual(false);
});
});
describe('when loading', () => { describe('when loading', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createWrapper({ isLoading: true }); wrapper = createWrapper({ isLoading: true });
......
...@@ -16433,6 +16433,9 @@ msgstr "" ...@@ -16433,6 +16433,9 @@ msgstr ""
msgid "Release|Something went wrong while saving the release details" msgid "Release|Something went wrong while saving the release details"
msgstr "" msgstr ""
msgid "Remediated: needs review"
msgstr ""
msgid "Remember me" msgid "Remember me"
msgstr "" msgstr ""
...@@ -19961,6 +19964,9 @@ msgstr "" ...@@ -19961,6 +19964,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6." msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "" msgstr ""
msgid "The vulnerability is no longer detected. Verify the vulnerability has been fixed or removed before changing its status."
msgstr ""
msgid "There are no GPG keys associated with this account." msgid "There are no GPG keys associated with this account."
msgstr "" msgstr ""
...@@ -22364,6 +22370,9 @@ msgstr "" ...@@ -22364,6 +22370,9 @@ msgstr ""
msgid "Vulnerability List" msgid "Vulnerability List"
msgstr "" msgstr ""
msgid "Vulnerability remediated. Review before resolving."
msgstr ""
msgid "Vulnerability-Check" msgid "Vulnerability-Check"
msgstr "" msgstr ""
......
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