Commit 2a67c3ff authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '14006-vulnerability-details-prop-ee' into 'master'

Refactor vulnerability-details component for better reusability

See merge request gitlab-org/gitlab!25830
parents b3cc472c 10e20f3a
import Vue from 'vue'; import Vue from 'vue';
import getFileLocation from 'ee/vue_shared/security_reports/store/utils/get_file_location';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import * as types from './mutation_types'; import * as types from './mutation_types';
...@@ -75,57 +74,12 @@ export default { ...@@ -75,57 +74,12 @@ export default {
}, },
[types.SET_MODAL_DATA](state, payload) { [types.SET_MODAL_DATA](state, payload) {
const { vulnerability } = payload; const { vulnerability } = payload;
const { location } = vulnerability;
Vue.set(state.modal, 'title', vulnerability.name); Vue.set(state.modal, 'title', vulnerability.name);
Vue.set(state.modal.data.description, 'value', vulnerability.description);
Vue.set(
state.modal.data.project,
'value',
vulnerability.project && vulnerability.project.full_name,
);
Vue.set(
state.modal.data.project,
'url',
vulnerability.project && vulnerability.project.full_path,
);
Vue.set(
state.modal.data.identifiers,
'value',
vulnerability.identifiers.length && vulnerability.identifiers,
);
if (location) {
const {
file,
start_line: startLine,
end_line: endLine,
image,
operating_system: namespace,
class: className,
} = location;
const fileLocation = getFileLocation(location);
let lineSuffix = ''; Vue.set(state.modal.project, 'value', vulnerability.project?.full_name);
Vue.set(state.modal.project, 'url', vulnerability.project?.full_path);
if (startLine) {
lineSuffix += `:${startLine}`;
if (endLine && startLine !== endLine) {
lineSuffix += `-${endLine}`;
}
}
Vue.set(state.modal.data.className, 'value', className);
Vue.set(state.modal.data.file, 'value', file ? `${file}${lineSuffix}` : null);
Vue.set(state.modal.data.image, 'value', image);
Vue.set(state.modal.data.namespace, 'value', namespace);
Vue.set(state.modal.data.url, 'value', fileLocation);
Vue.set(state.modal.data.url, 'url', fileLocation);
}
Vue.set(state.modal.data.severity, 'value', vulnerability.severity);
Vue.set(state.modal.data.reportType, 'value', vulnerability.report_type);
Vue.set(state.modal, 'vulnerability', vulnerability); Vue.set(state.modal, 'vulnerability', vulnerability);
Vue.set( Vue.set(
state.modal.vulnerability, state.modal.vulnerability,
...@@ -143,18 +97,6 @@ export default { ...@@ -143,18 +97,6 @@ export default {
Vue.set(state.modal.vulnerability, 'isDismissed', Boolean(vulnerability.dismissal_feedback)); Vue.set(state.modal.vulnerability, 'isDismissed', Boolean(vulnerability.dismissal_feedback));
Vue.set(state.modal, 'error', null); Vue.set(state.modal, 'error', null);
Vue.set(state.modal, 'isCommentingOnDismissal', false); Vue.set(state.modal, 'isCommentingOnDismissal', false);
if (vulnerability.instances && vulnerability.instances.length) {
Vue.set(state.modal.data.instances, 'value', vulnerability.instances);
} else {
Vue.set(state.modal.data.instances, 'value', null);
}
if (vulnerability.links && vulnerability.links.length) {
Vue.set(state.modal.data.links, 'value', vulnerability.links);
} else {
Vue.set(state.modal.data.links, 'value', null);
}
}, },
[types.REQUEST_CREATE_ISSUE](state) { [types.REQUEST_CREATE_ISSUE](state) {
state.isCreatingIssue = true; state.isCreatingIssue = true;
......
import { __, s__ } from '~/locale';
export default () => ({ export default () => ({
isLoadingVulnerabilities: true, isLoadingVulnerabilities: true,
errorLoadingVulnerabilities: false, errorLoadingVulnerabilities: false,
...@@ -21,24 +19,8 @@ export default () => ({ ...@@ -21,24 +19,8 @@ export default () => ({
activeVulnerability: null, activeVulnerability: null,
sourceBranch: null, sourceBranch: null,
modal: { modal: {
data: {
description: { text: s__('Vulnerability|Description') },
project: {
text: s__('Vulnerability|Project'),
isLink: true,
},
url: { text: __('URL'), isLink: true },
file: { text: s__('Vulnerability|File') },
identifiers: { text: s__('Vulnerability|Identifiers') },
severity: { text: s__('Vulnerability|Severity') },
reportType: { text: s__('Vulnerability|Report Type') },
className: { text: s__('Vulnerability|Class') },
image: { text: s__('Vulnerability|Image') },
namespace: { text: s__('Vulnerability|Namespace') },
links: { text: s__('Vulnerability|Links') },
instances: { text: s__('Vulnerability|Instances') },
},
vulnerability: {}, vulnerability: {},
project: {},
isCreatingNewIssue: false, isCreatingNewIssue: false,
isCreatingMergeRequest: false, isCreatingMergeRequest: false,
isDismissingVulnerability: false, isDismissingVulnerability: false,
......
...@@ -86,7 +86,7 @@ export default { ...@@ -86,7 +86,7 @@ export default {
); );
}, },
project() { project() {
return this.modal.data.project; return this.modal.project;
}, },
solution() { solution() {
return this.vulnerability && this.vulnerability.solution; return this.vulnerability && this.vulnerability.solution;
...@@ -122,22 +122,6 @@ export default { ...@@ -122,22 +122,6 @@ export default {
isEditingExistingFeedback() { isEditingExistingFeedback() {
return this.dismissalFeedback && this.modal.isCommentingOnDismissal; return this.dismissalFeedback && this.modal.isCommentingOnDismissal;
}, },
valuedFields() {
const { data } = this.modal;
const result = {};
Object.keys(data).forEach(key => {
if (data[key].value && data[key].value.length) {
result[key] = data[key];
if (key === 'file' && this.vulnerability.blob_path) {
result[key].isLink = true;
result[key].url = this.vulnerability.blob_path;
}
}
});
return result;
},
dismissalFeedbackObject() { dismissalFeedbackObject() {
if (this.dismissalFeedback) { if (this.dismissalFeedback) {
return this.dismissalFeedback; return this.dismissalFeedback;
...@@ -153,7 +137,6 @@ export default { ...@@ -153,7 +137,6 @@ export default {
} = gon; } = gon;
return { return {
project_id: this.project ? this.project.id : null,
author: { author: {
id: current_user_id, id: current_user_id,
name: current_user_fullname, name: current_user_fullname,
...@@ -203,7 +186,7 @@ export default { ...@@ -203,7 +186,7 @@ export default {
class="modal-security-report-dast" class="modal-security-report-dast"
> >
<slot> <slot>
<vulnerability-details :details="valuedFields" class="js-vulnerability-details" /> <vulnerability-details :vulnerability="vulnerability" class="js-vulnerability-details" />
<solution-card <solution-card
:solution="solution" :solution="solution"
......
<script>
export default {
name: 'VulnerabilityDetail',
props: {
label: {
type: String,
required: true,
},
},
};
</script>
<template functional>
<div class="d-sm-flex my-sm-2 my-4">
<label class="col-sm-2 text-sm-right font-weight-bold pl-0">{{ props.label }}:</label>
<div class="col-sm-10 pl-0 text-secondary">
<slot></slot>
</div>
</div>
</template>
...@@ -2,40 +2,78 @@ ...@@ -2,40 +2,78 @@
import { GlFriendlyWrap } from '@gitlab/ui'; import { GlFriendlyWrap } from '@gitlab/ui';
import SafeLink from 'ee/vue_shared/components/safe_link.vue'; import SafeLink from 'ee/vue_shared/components/safe_link.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import ExpandButton from '~/vue_shared/components/expand_button.vue';
import SeverityBadge from './severity_badge.vue'; import SeverityBadge from './severity_badge.vue';
import getFileLocation from '../store/utils/get_file_location';
import VulnerabilityDetail from './vulnerability_detail.vue';
export default { export default {
name: 'VulnerabilityDetails', name: 'VulnerabilityDetails',
components: { components: {
ExpandButton,
GlFriendlyWrap, GlFriendlyWrap,
Icon, Icon,
SafeLink, SafeLink,
SeverityBadge, SeverityBadge,
VulnerabilityDetail,
}, },
props: { props: {
details: { vulnerability: {
type: Object, type: Object,
required: true, required: true,
}, },
}, },
methods: { computed: {
hasMoreValues(index, values) { url() {
return index < values.length - 1; return getFileLocation(this.vulnerability.location);
},
file() {
const file = this.vulnerability?.location?.file;
if (!file) {
return null;
}
let lineSuffix = '';
const { start_line: startLine, end_line: endLine } = this.vulnerability.location;
if (startLine) {
lineSuffix += `:${startLine}`;
if (endLine && startLine !== endLine) {
lineSuffix += `-${endLine}`;
}
}
return `${file}${lineSuffix}`;
},
identifiers() {
return this.asNonEmptyListOrNull(this.vulnerability.identifiers);
},
className() {
return this.vulnerability.location?.class;
},
methodName() {
return this.vulnerability.location?.method;
}, },
hasValue(field) { image() {
return field.value && field.value.length > 0; return this.vulnerability.location?.image;
}, },
hasInstances(key) { namespace() {
return key === 'instances'; return this.vulnerability.location?.operating_system;
}, },
hasIdentifiers(key) { links() {
return key === 'identifiers'; return this.asNonEmptyListOrNull(this.vulnerability.links);
}, },
hasLinks(key) { instances() {
return key === 'links'; return this.asNonEmptyListOrNull(this.vulnerability.instances);
},
},
methods: {
hasMoreValues(index, values) {
return index < values.length - 1;
}, },
hasSeverity(key) { asNonEmptyListOrNull(list) {
return key === 'severity'; return list?.length > 0 ? list : null;
}, },
}, },
}; };
...@@ -43,86 +81,122 @@ export default { ...@@ -43,86 +81,122 @@ export default {
<template> <template>
<div class="border-white mb-0 px-3"> <div class="border-white mb-0 px-3">
<div v-for="(field, key, index) in details" :key="index" class="d-sm-flex my-sm-2 my-4"> <vulnerability-detail
<label class="col-sm-2 text-sm-right font-weight-bold pl-0">{{ field.text }}:</label> v-if="vulnerability.description"
<div class="col-sm-10 pl-0 text-secondary"> :label="s__('Vulnerability|Description')"
<template v-if="hasValue(field)"> >
<div v-if="hasInstances(key)" class="info-well"> <gl-friendly-wrap :text="vulnerability.description" />
<ul class="report-block-list"> </vulnerability-detail>
<li v-for="(instance, i) in field.value" :key="i" class="report-block-list-issue">
<div class="report-block-list-icon append-right-5 failed"> <vulnerability-detail v-if="vulnerability.project" :label="s__('Vulnerability|Project')">
<icon :size="32" name="status_failed_borderless" /> <safe-link ref="projectLink" :href="vulnerability.project.full_path" target="_blank">
</div> <gl-friendly-wrap :text="vulnerability.project.full_name" />
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5"> </safe-link>
<div class="report-block-list-issue-description-text"> </vulnerability-detail>
{{ instance.method }}
</div> <vulnerability-detail v-if="url" :label="__('URL')">
<div class="report-block-list-issue-description-link"> <safe-link ref="urlLink" :href="url" target="_blank">
<safe-link <gl-friendly-wrap :text="url" />
:href="instance.uri" </safe-link>
target="_blank" </vulnerability-detail>
rel="noopener noreferrer nofollow"
class="break-link" <vulnerability-detail v-if="file" :label="s__('Vulnerability|File')">
> <safe-link
{{ instance.uri }} v-if="vulnerability.blob_path"
</safe-link> ref="fileLink"
</div> :href="vulnerability.blob_path"
<expand-button v-if="instance.evidence"> target="_blank"
<pre >
slot="expanded" <gl-friendly-wrap :text="file" />
class="block report-block-dast-code prepend-top-10 report-block-issue-code" </safe-link>
> <gl-friendly-wrap v-else :text="file" />
{{ instance.evidence }}</pre </vulnerability-detail>
>
</expand-button> <vulnerability-detail v-if="identifiers" :label="s__('Vulnerability|Identifiers')">
</div> <span v-for="(identifier, i) in identifiers" :key="i">
</li> <safe-link
</ul> v-if="identifier.url"
</div> ref="identifiersLink"
<template v-else-if="hasIdentifiers(key)"> :href="identifier.url"
<span v-for="(identifier, i) in field.value" :key="i"> target="_blank"
<safe-link rel="noopener noreferrer"
v-if="identifier.url" >
:class="`js-link-${key}`" {{ identifier.name }}
:href="identifier.url" </safe-link>
target="_blank" <span v-else> {{ identifier.name }} </span>
rel="noopener noreferrer" <span v-if="hasMoreValues(i, identifiers)">,&nbsp;</span>
> </span>
{{ identifier.name }} </vulnerability-detail>
</safe-link>
<span v-else> {{ identifier.name }} </span> <vulnerability-detail v-if="vulnerability.severity" :label="s__('Vulnerability|Severity')">
<span v-if="hasMoreValues(i, field.value)">,&nbsp;</span> <severity-badge :severity="vulnerability.severity" class="d-inline" />
</span> </vulnerability-detail>
</template>
<template v-else-if="hasLinks(key)"> <vulnerability-detail
<span v-for="(link, i) in field.value" :key="i"> v-if="vulnerability.report_type"
<safe-link :label="s__('Vulnerability|Report Type')"
:class="`js-link-${key}`" >
:href="link.url" <gl-friendly-wrap :text="vulnerability.report_type" />
target="_blank" </vulnerability-detail>
rel="noopener noreferrer"
> <vulnerability-detail v-if="className" :label="s__('Vulnerability|Class')">
{{ link.value || link.url }} <gl-friendly-wrap :text="className" />
</safe-link> </vulnerability-detail>
<span v-if="hasMoreValues(i, field.value)">,&nbsp;</span>
</span> <vulnerability-detail v-if="methodName" :label="s__('Vulnerability|Method')">
</template> <gl-friendly-wrap :text="methodName" />
<template v-else-if="hasSeverity(key)"> </vulnerability-detail>
<severity-badge :severity="field.value" class="d-inline" />
</template> <vulnerability-detail v-if="image" :label="s__('Vulnerability|Image')">
<template v-else> <gl-friendly-wrap :text="image" />
<safe-link </vulnerability-detail>
v-if="field.isLink"
:class="`js-link-${key}`" <vulnerability-detail v-if="namespace" :label="s__('Vulnerability|Namespace')">
:href="field.url" <gl-friendly-wrap :text="namespace" />
target="_blank" </vulnerability-detail>
>
<gl-friendly-wrap :text="field.value" /> <vulnerability-detail v-if="links" :label="s__('Vulnerability|Links')">
</safe-link> <span v-for="(link, i) in links" :key="i">
<gl-friendly-wrap v-else :text="field.value" /> <safe-link ref="linksLink" :href="link.url" target="_blank" rel="noopener noreferrer">
</template> {{ link.value || link.url }}
</template> </safe-link>
<span v-if="hasMoreValues(i, links)">,&nbsp;</span>
</span>
</vulnerability-detail>
<vulnerability-detail v-if="instances" :label="s__('Vulnerability|Instances')">
<div class="info-well">
<ul class="report-block-list">
<li v-for="(instance, i) in instances" :key="i" class="report-block-list-issue">
<div class="report-block-list-icon append-right-5 failed">
<icon :size="32" name="status_failed_borderless" />
</div>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text">
{{ instance.method }}
</div>
<div class="report-block-list-issue-description-link">
<safe-link
:href="instance.uri"
target="_blank"
rel="noopener noreferrer nofollow"
class="break-link"
>
{{ instance.uri }}
</safe-link>
</div>
<expand-button v-if="instance.evidence">
<template #expanded>
<pre
class="block report-block-dast-code prepend-top-10 report-block-issue-code"
v-text="instance.evidence"
></pre>
</template>
</expand-button>
</div>
</li>
</ul>
</div> </div>
</div> </vulnerability-detail>
</div> </div>
</template> </template>
import Vue from 'vue'; import Vue from 'vue';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { findIssueIndex, parseDiff } from './utils'; import { findIssueIndex, parseDiff } from './utils';
import getFileLocation from './utils/get_file_location';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
export default { export default {
...@@ -134,36 +133,8 @@ export default { ...@@ -134,36 +133,8 @@ export default {
[types.SET_ISSUE_MODAL_DATA](state, payload) { [types.SET_ISSUE_MODAL_DATA](state, payload) {
const { issue, status } = payload; const { issue, status } = payload;
const fileLocation = getFileLocation(issue.location);
Vue.set(state.modal, 'title', issue.title); Vue.set(state.modal, 'title', issue.title);
Vue.set(state.modal.data.description, 'value', issue.description);
Vue.set(state.modal.data.file, 'value', issue.location && issue.location.file);
Vue.set(state.modal.data.file, 'url', issue.urlPath);
Vue.set(state.modal.data.className, 'value', issue.location && issue.location.class);
Vue.set(state.modal.data.methodName, 'value', issue.location && issue.location.method);
Vue.set(state.modal.data.image, 'value', issue.location && issue.location.image);
Vue.set(state.modal.data.namespace, 'value', issue.location && issue.location.operating_system);
Vue.set(state.modal.data.url, 'value', fileLocation);
Vue.set(state.modal.data.url, 'url', fileLocation);
if (issue.identifiers && issue.identifiers.length > 0) {
Vue.set(state.modal.data.identifiers, 'value', issue.identifiers);
} else {
// Force a null value for identifiers to avoid showing an empty array
Vue.set(state.modal.data.identifiers, 'value', null);
}
Vue.set(state.modal.data.severity, 'value', issue.severity);
if (issue.links && issue.links.length > 0) {
Vue.set(state.modal.data.links, 'value', issue.links);
} else {
// Force a null value for links to avoid showing an empty array
Vue.set(state.modal.data.links, 'value', null);
}
Vue.set(state.modal.data.instances, 'value', issue.instances);
Vue.set(state.modal, 'vulnerability', issue); Vue.set(state.modal, 'vulnerability', issue);
Vue.set(state.modal, 'isResolved', status === 'success'); Vue.set(state.modal, 'isResolved', status === 'success');
......
import { __, s__ } from '~/locale';
export default () => ({ export default () => ({
blobPath: { blobPath: {
head: null, head: null,
...@@ -67,66 +65,6 @@ export default () => ({ ...@@ -67,66 +65,6 @@ export default () => ({
modal: { modal: {
title: null, title: null,
// Dynamic data rendered for each issue
data: {
description: {
value: null,
text: s__('ciReport|Description'),
isLink: false,
},
url: {
value: null,
url: null,
text: __('URL'),
isLink: true,
},
file: {
value: null,
url: null,
text: s__('ciReport|File'),
isLink: true,
},
identifiers: {
value: [],
text: s__('ciReport|Identifiers'),
isLink: false,
},
severity: {
value: null,
text: s__('ciReport|Severity'),
isLink: false,
},
className: {
value: null,
text: s__('ciReport|Class'),
isLink: false,
},
methodName: {
value: null,
text: s__('ciReport|Method'),
isLink: false,
},
image: {
value: null,
text: s__('ciReport|Image'),
isLink: false,
},
namespace: {
value: null,
text: s__('ciReport|Namespace'),
isLink: false,
},
links: {
value: [],
text: s__('ciReport|Links'),
isLink: false,
},
instances: {
value: [],
text: s__('ciReport|Instances'),
isLink: false,
},
},
learnMoreUrl: null, learnMoreUrl: null,
vulnerability: { vulnerability: {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VulnerabilityDetail component renders the label prop and default slot 1`] = `
<div
class="d-sm-flex my-sm-2 my-4"
>
<label
class="col-sm-2 text-sm-right font-weight-bold pl-0"
>
foo:
</label>
<div
class="col-sm-10 pl-0 text-secondary"
>
<p>
bar
</p>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VulnerabilityDetails component pin test renders correctly 1`] = `
<div
class="border-white mb-0 px-3"
>
<vulnerability-detail-stub
label="Description"
>
<gl-friendly-wrap-stub
symbols="/"
text="The serialize-javascript npm package is vulnerable to Cross-site Scripting (XSS). It does not properly mitigate against unsafe characters in serialized regular expressions. If serialized data of regular expression objects are used in an environment other than Node.js, it is affected by this vulnerability."
/>
</vulnerability-detail-stub>
<vulnerability-detail-stub
label="Project"
>
<safe-link-stub
href="/gitlab-org/gitlab-ui"
target="_blank"
>
<gl-friendly-wrap-stub
symbols="/"
text="GitLab.org / gitlab-ui"
/>
</safe-link-stub>
</vulnerability-detail-stub>
<!---->
<vulnerability-detail-stub
label="File"
>
<safe-link-stub
href="/gitlab-org/gitlab-ui/blob/ad137f0a8ac59af961afe47d04e5cc062c6864a9/yarn.lock"
target="_blank"
>
<gl-friendly-wrap-stub
symbols="/"
text="yarn.lock"
/>
</safe-link-stub>
</vulnerability-detail-stub>
<vulnerability-detail-stub
label="Identifiers"
>
<span>
<safe-link-stub
href="https://deps.sec.gitlab.com/packages/npm/serialize-javascript/versions/1.7.0/advisories"
rel="noopener noreferrer"
target="_blank"
>
Gemnasium-58caa017-9a9a-46d6-bab2-ec930f46833c
</safe-link-stub>
<span>
</span>
</span>
<span>
<safe-link-stub
href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16769"
rel="noopener noreferrer"
target="_blank"
>
CVE-2019-16769
</safe-link-stub>
<!---->
</span>
</vulnerability-detail-stub>
<vulnerability-detail-stub
label="Severity"
>
<severity-badge-stub
class="d-inline"
severity="unknown"
/>
</vulnerability-detail-stub>
<vulnerability-detail-stub
label="Report Type"
>
<gl-friendly-wrap-stub
symbols="/"
text="dependency_scanning"
/>
</vulnerability-detail-stub>
<!---->
<!---->
<!---->
<!---->
<vulnerability-detail-stub
label="Links"
>
<span>
<safe-link-stub
href="https://nvd.nist.gov/vuln/detail/CVE-2019-16769"
rel="noopener noreferrer"
target="_blank"
>
https://nvd.nist.gov/vuln/detail/CVE-2019-16769
</safe-link-stub>
<!---->
</span>
</vulnerability-detail-stub>
<vulnerability-detail-stub
label="Instances"
>
<div
class="info-well"
>
<ul
class="report-block-list"
>
<li
class="report-block-list-issue"
>
<div
class="report-block-list-icon append-right-5 failed"
>
<icon-stub
name="status_failed_borderless"
size="32"
/>
</div>
<div
class="report-block-list-issue-description prepend-top-5 append-bottom-5"
>
<div
class="report-block-list-issue-description-text"
>
POST
</div>
<div
class="report-block-list-issue-description-link"
>
<safe-link-stub
class="break-link"
href="/bar"
rel="noopener noreferrer nofollow"
target="_blank"
>
/bar
</safe-link-stub>
</div>
<expand-button-stub />
</div>
</li>
</ul>
</div>
</vulnerability-detail-stub>
</div>
`;
...@@ -286,8 +286,10 @@ describe('Security Reports modal', () => { ...@@ -286,8 +286,10 @@ describe('Security Reports modal', () => {
modal: createState().modal, modal: createState().modal,
}; };
propsData.modal.vulnerability.blob_path = blobPath; propsData.modal.vulnerability.blob_path = blobPath;
propsData.modal.data.namespace.value = namespaceValue; propsData.modal.vulnerability.location = {
propsData.modal.data.file.value = fileValue; file: fileValue,
operating_system: namespaceValue,
};
mountComponent({ propsData }, mount); mountComponent({ propsData }, mount);
}); });
...@@ -295,23 +297,8 @@ describe('Security Reports modal', () => { ...@@ -295,23 +297,8 @@ describe('Security Reports modal', () => {
const vulnerabilityDetails = wrapper.find('.js-vulnerability-details'); const vulnerabilityDetails = wrapper.find('.js-vulnerability-details');
expect(vulnerabilityDetails.exists()).toBe(true); expect(vulnerabilityDetails.exists()).toBe(true);
expect(vulnerabilityDetails.text()).toContain('foobar'); expect(vulnerabilityDetails.text()).toContain(namespaceValue);
}); expect(vulnerabilityDetails.text()).toContain(fileValue);
it('computes valued fields properly', () => {
expect(wrapper.vm.valuedFields).toMatchObject({
file: {
value: fileValue,
url: blobPath,
isLink: true,
text: 'File',
},
namespace: {
value: namespaceValue,
text: 'Namespace',
isLink: false,
},
});
}); });
}); });
......
import { shallowMount } from '@vue/test-utils';
import VulnerabilityDetail from 'ee/vue_shared/security_reports/components/vulnerability_detail.vue';
describe('VulnerabilityDetail component', () => {
let wrapper;
const factory = ({ propsData, defaultSlot }) => {
wrapper = shallowMount(VulnerabilityDetail, {
propsData,
slots: {
default: defaultSlot,
},
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders the label prop and default slot', () => {
factory({ propsData: { label: 'foo' }, defaultSlot: '<p>bar</p>' });
expect(wrapper.element).toMatchSnapshot();
});
});
import { mount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import createState from 'ee/vue_shared/security_reports/store/state';
import VulnerabilityDetails from 'ee/vue_shared/security_reports/components/vulnerability_details.vue'; import VulnerabilityDetails from 'ee/vue_shared/security_reports/components/vulnerability_details.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';
import SafeLink from 'ee/vue_shared/components/safe_link.vue'; import SafeLink from 'ee/vue_shared/components/safe_link.vue';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { cloneDeep } from 'lodash';
import { mockFindings } from '../mock_data';
function makeVulnerability(changes = {}) {
return Object.assign(cloneDeep(mockFindings[0]), changes);
}
describe('VulnerabilityDetails component', () => { describe('VulnerabilityDetails component', () => {
let wrapper; let wrapper;
const componentFactory = (options = {}) => { const componentFactory = vulnerability => {
wrapper = mount(VulnerabilityDetails, { wrapper = mount(VulnerabilityDetails, {
...options, propsData: { vulnerability },
}); });
}; };
...@@ -20,57 +25,56 @@ describe('VulnerabilityDetails component', () => { ...@@ -20,57 +25,56 @@ describe('VulnerabilityDetails component', () => {
expect(link.text()).toBe(text); expect(link.text()).toBe(text);
}; };
const findLink = name => wrapper.find({ ref: `${name}Link` });
afterEach(() => {
wrapper.destroy();
});
it('renders severity with a badge', () => { it('renders severity with a badge', () => {
const value = 'critical'; const vulnerability = makeVulnerability({ severity: 'critical' });
componentFactory({ propsData: { details: { severity: { value } } } }); componentFactory(vulnerability);
const badge = wrapper.find(SeverityBadge); const badge = wrapper.find(SeverityBadge);
expect(badge.props('severity')).toBe(value); expect(badge.props('severity')).toBe(vulnerability.severity);
}); });
it('renders link fields with link', () => { it('renders link fields with link', () => {
const details = { const vulnerability = makeVulnerability();
somefield: { componentFactory(vulnerability);
value: 'foo',
url: `${TEST_HOST}/bar`,
isLink: true,
},
};
componentFactory({ propsData: { details } }); expectSafeLink(findLink('project'), {
href: vulnerability.project.full_path,
expectSafeLink(wrapper.find('.js-link-somefield'), { text: vulnerability.project.full_name,
href: `${TEST_HOST}/bar`,
text: 'foo',
}); });
}); });
it('renders wrapped file paths', () => { it('renders wrapped file paths', () => {
const value = '/some/file/path'; const vulnerability = makeVulnerability({
const valueWrapped = '/<wbr>some/<wbr>file/<wbr>path'; blob_path: `${TEST_HOST}/bar`,
const details = { location: {
file: { file: '/some/file/path',
value,
url: `${TEST_HOST}/bar`,
isLink: true,
}, },
}; });
componentFactory({ propsData: { details } });
expect(wrapper.find(SafeLink).html()).toMatch(valueWrapped); componentFactory(vulnerability);
expect(findLink('file').html()).toMatch('/<wbr>some/<wbr>file/<wbr>path');
}); });
it('escapes wrapped file paths', () => { it('escapes wrapped file paths', () => {
const value = '/unsafe/path<script></script>'; const vulnerability = makeVulnerability({
const valueWrapped = '/<wbr>unsafe/<wbr>path&lt;script&gt;&lt;/<wbr>script&gt;'; blob_path: `${TEST_HOST}/bar`,
const details = { location: {
file: { file: '/unsafe/path<script></script>',
value,
url: `${TEST_HOST}/bar`,
isLink: true,
}, },
}; });
componentFactory({ propsData: { details } });
expect(wrapper.find(SafeLink).html()).toMatch(valueWrapped); componentFactory(vulnerability);
expect(findLink('file').html()).toMatch(
'/<wbr>unsafe/<wbr>path&lt;script&gt;&lt;/<wbr>script&gt;',
);
}); });
describe('does not render XSS links', () => { describe('does not render XSS links', () => {
...@@ -78,49 +82,35 @@ describe('VulnerabilityDetails component', () => { ...@@ -78,49 +82,35 @@ describe('VulnerabilityDetails component', () => {
const badUrl = 'javascript:alert("")'; const badUrl = 'javascript:alert("")';
beforeEach(() => { beforeEach(() => {
const details = createState().modal.data; const vulnerability = makeVulnerability({
blob_path: badUrl,
details.file.value = 'badFile.lock'; location: {
details.file.url = badUrl; file: 'badFile.lock',
details.links.value = [
{
url: badUrl,
}, },
]; links: [{ url: badUrl }],
details.identifiers.value = [ identifiers: [{ name: 'BAD_URL', url: badUrl }],
{ instances: [{ method: 'GET', uri: badUrl }],
type: 'CVE', });
name: 'BAD_URL',
url: badUrl,
},
];
details.instances.value = [
{
param: 'X-Content-Type-Options',
method: 'GET',
uri: badUrl,
},
];
componentFactory({ propsData: { details } }); componentFactory(vulnerability);
}); });
it('for the link field', () => { it('for the link field', () => {
expectSafeLink(wrapper.find('.js-link-links'), { expectSafeLink(findLink('links'), {
href: badUrl, href: badUrl,
text: badUrl, text: badUrl,
}); });
}); });
it('for the identifiers field', () => { it('for the identifiers field', () => {
expectSafeLink(wrapper.find('.js-link-identifiers'), { expectSafeLink(findLink('identifiers'), {
href: badUrl, href: badUrl,
text: 'BAD_URL', text: 'BAD_URL',
}); });
}); });
it('for the file field', () => { it('for the file field', () => {
expectSafeLink(wrapper.find('.js-link-file'), { expectSafeLink(findLink('file'), {
href: badUrl, href: badUrl,
text: 'badFile.lock', text: 'badFile.lock',
}); });
...@@ -136,16 +126,14 @@ describe('VulnerabilityDetails component', () => { ...@@ -136,16 +126,14 @@ describe('VulnerabilityDetails component', () => {
describe('with instances', () => { describe('with instances', () => {
beforeEach(() => { beforeEach(() => {
const details = { const vulnerability = makeVulnerability({
instances: { instances: [
value: [ { uri: 'http://192.168.32.236:3001/explore?sort=latest_activity_desc' },
{ uri: 'http://192.168.32.236:3001/explore?sort=latest_activity_desc' }, { uri: 'http://192.168.32.236:3001/help/user/group/subgroups/index.md' },
{ uri: 'http://192.168.32.236:3001/help/user/group/subgroups/index.md' }, ],
], });
},
};
componentFactory({ propsData: { details } }); componentFactory(vulnerability);
}); });
it('renders instances list', () => { it('renders instances list', () => {
...@@ -160,4 +148,30 @@ describe('VulnerabilityDetails component', () => { ...@@ -160,4 +148,30 @@ describe('VulnerabilityDetails component', () => {
); );
}); });
}); });
describe('pin test', () => {
const factory = vulnFinding => {
wrapper = shallowMount(VulnerabilityDetails, {
propsData: {
vulnerability: vulnFinding,
},
});
};
it('renders correctly', () => {
factory(
makeVulnerability({
instances: [
{
method: 'POST',
evidence: 'foo',
uri: '/bar',
},
],
}),
);
expect(wrapper.element).toMatchSnapshot();
});
});
}); });
...@@ -97,51 +97,12 @@ describe('security reports mutations', () => { ...@@ -97,51 +97,12 @@ describe('security reports mutations', () => {
describe('SET_ISSUE_MODAL_DATA', () => { describe('SET_ISSUE_MODAL_DATA', () => {
it('has default data', () => { it('has default data', () => {
expect(stateCopy.modal.data.description.value).toEqual(null);
expect(stateCopy.modal.data.description.text).toEqual('Description');
expect(stateCopy.modal.data.description.isLink).toEqual(false);
expect(stateCopy.modal.data.identifiers.value).toEqual([]);
expect(stateCopy.modal.data.identifiers.text).toEqual('Identifiers');
expect(stateCopy.modal.data.identifiers.isLink).toEqual(false);
expect(stateCopy.modal.data.file.value).toEqual(null);
expect(stateCopy.modal.data.file.text).toEqual('File');
expect(stateCopy.modal.data.file.isLink).toEqual(true);
expect(stateCopy.modal.data.className.value).toEqual(null);
expect(stateCopy.modal.data.className.text).toEqual('Class');
expect(stateCopy.modal.data.className.isLink).toEqual(false);
expect(stateCopy.modal.data.methodName.value).toEqual(null);
expect(stateCopy.modal.data.methodName.text).toEqual('Method');
expect(stateCopy.modal.data.methodName.isLink).toEqual(false);
expect(stateCopy.modal.data.namespace.value).toEqual(null);
expect(stateCopy.modal.data.namespace.text).toEqual('Namespace');
expect(stateCopy.modal.data.namespace.isLink).toEqual(false);
expect(stateCopy.modal.data.severity.value).toEqual(null);
expect(stateCopy.modal.data.severity.text).toEqual('Severity');
expect(stateCopy.modal.data.severity.isLink).toEqual(false);
expect(stateCopy.modal.data.links.value).toEqual([]);
expect(stateCopy.modal.data.links.text).toEqual('Links');
expect(stateCopy.modal.data.links.isLink).toEqual(false);
expect(stateCopy.modal.data.instances.value).toEqual([]);
expect(stateCopy.modal.data.instances.text).toEqual('Instances');
expect(stateCopy.modal.data.instances.isLink).toEqual(false);
expect(stateCopy.modal.vulnerability.isDismissed).toEqual(false); expect(stateCopy.modal.vulnerability.isDismissed).toEqual(false);
expect(stateCopy.modal.vulnerability.hasIssue).toEqual(false); expect(stateCopy.modal.vulnerability.hasIssue).toEqual(false);
expect(stateCopy.modal.isCreatingNewIssue).toEqual(false); expect(stateCopy.modal.isCreatingNewIssue).toEqual(false);
expect(stateCopy.modal.isDismissingVulnerability).toEqual(false); expect(stateCopy.modal.isDismissingVulnerability).toEqual(false);
expect(stateCopy.modal.data.url.value).toEqual(null);
expect(stateCopy.modal.data.url.url).toEqual(null);
expect(stateCopy.modal.title).toEqual(null); expect(stateCopy.modal.title).toEqual(null);
expect(stateCopy.modal.learnMoreUrl).toEqual(null); expect(stateCopy.modal.learnMoreUrl).toEqual(null);
expect(stateCopy.modal.error).toEqual(null); expect(stateCopy.modal.error).toEqual(null);
...@@ -194,23 +155,6 @@ describe('security reports mutations', () => { ...@@ -194,23 +155,6 @@ describe('security reports mutations', () => {
mutations[types.SET_ISSUE_MODAL_DATA](stateCopy, { issue, status }); mutations[types.SET_ISSUE_MODAL_DATA](stateCopy, { issue, status });
expect(stateCopy.modal.title).toEqual(issue.title); expect(stateCopy.modal.title).toEqual(issue.title);
expect(stateCopy.modal.data.description.value).toEqual(issue.description);
expect(stateCopy.modal.data.file.value).toEqual(issue.location.file);
expect(stateCopy.modal.data.file.url).toEqual(issue.urlPath);
expect(stateCopy.modal.data.className.value).toEqual(issue.location.class);
expect(stateCopy.modal.data.methodName.value).toEqual(issue.location.method);
expect(stateCopy.modal.data.namespace.value).toEqual(issue.location.operating_system);
expect(stateCopy.modal.data.image.value).toEqual(issue.location.image);
expect(stateCopy.modal.data.identifiers.value).toEqual(issue.identifiers);
expect(stateCopy.modal.data.severity.value).toEqual(issue.severity);
expect(stateCopy.modal.data.links.value).toEqual(issue.links);
expect(stateCopy.modal.data.instances.value).toEqual(issue.instances);
expect(stateCopy.modal.data.url.value).toEqual(
`${issue.location.hostname}${issue.location.path}`,
);
expect(stateCopy.modal.data.url.url).toEqual(
`${issue.location.hostname}${issue.location.path}`,
);
expect(stateCopy.modal.vulnerability).toEqual(issue); expect(stateCopy.modal.vulnerability).toEqual(issue);
expect(stateCopy.modal.isResolved).toEqual(true); expect(stateCopy.modal.isResolved).toEqual(true);
}); });
......
...@@ -37,7 +37,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -37,7 +37,7 @@ describe('vulnerabilities module mutations', () => {
mutations[types.SET_VULNERABILITIES_ENDPOINT](state, endpoint); mutations[types.SET_VULNERABILITIES_ENDPOINT](state, endpoint);
expect(state.vulnerabilitiesEndpoint).toEqual(endpoint); expect(state.vulnerabilitiesEndpoint).toBe(endpoint);
}); });
}); });
...@@ -46,7 +46,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -46,7 +46,7 @@ describe('vulnerabilities module mutations', () => {
it(`should set pageInfo.page to ${page}`, () => { it(`should set pageInfo.page to ${page}`, () => {
mutations[types.SET_VULNERABILITIES_PAGE](state, page); mutations[types.SET_VULNERABILITIES_PAGE](state, page);
expect(state.pageInfo.page).toEqual(page); expect(state.pageInfo.page).toBe(page);
}); });
}); });
...@@ -116,7 +116,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -116,7 +116,7 @@ describe('vulnerabilities module mutations', () => {
mutations[types.SET_VULNERABILITIES_COUNT_ENDPOINT](state, endpoint); mutations[types.SET_VULNERABILITIES_COUNT_ENDPOINT](state, endpoint);
expect(state.vulnerabilitiesCountEndpoint).toEqual(endpoint); expect(state.vulnerabilitiesCountEndpoint).toBe(endpoint);
}); });
}); });
...@@ -166,7 +166,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -166,7 +166,7 @@ describe('vulnerabilities module mutations', () => {
mutations[types.SET_VULNERABILITIES_HISTORY_ENDPOINT](state, endpoint); mutations[types.SET_VULNERABILITIES_HISTORY_ENDPOINT](state, endpoint);
expect(state.vulnerabilitiesHistoryEndpoint).toEqual(endpoint); expect(state.vulnerabilitiesHistoryEndpoint).toBe(endpoint);
}); });
}); });
...@@ -214,19 +214,19 @@ describe('vulnerabilities module mutations', () => { ...@@ -214,19 +214,19 @@ describe('vulnerabilities module mutations', () => {
it('should set the vulnerabilitiesHistoryDayRange to number of days', () => { it('should set the vulnerabilitiesHistoryDayRange to number of days', () => {
mutations[types.SET_VULNERABILITIES_HISTORY_DAY_RANGE](state, DAYS.THIRTY); mutations[types.SET_VULNERABILITIES_HISTORY_DAY_RANGE](state, DAYS.THIRTY);
expect(state.vulnerabilitiesHistoryDayRange).toEqual(DAYS.THIRTY); expect(state.vulnerabilitiesHistoryDayRange).toBe(DAYS.THIRTY);
}); });
it('should set the vulnerabilitiesHistoryMaxDayInterval to 7 if days are 60 and under', () => { it('should set the vulnerabilitiesHistoryMaxDayInterval to 7 if days are 60 and under', () => {
mutations[types.SET_VULNERABILITIES_HISTORY_DAY_RANGE](state, DAYS.THIRTY); mutations[types.SET_VULNERABILITIES_HISTORY_DAY_RANGE](state, DAYS.THIRTY);
expect(state.vulnerabilitiesHistoryMaxDayInterval).toEqual(7); expect(state.vulnerabilitiesHistoryMaxDayInterval).toBe(7);
}); });
it('should set the vulnerabilitiesHistoryMaxDayInterval to 14 if over 60', () => { it('should set the vulnerabilitiesHistoryMaxDayInterval to 14 if over 60', () => {
mutations[types.SET_VULNERABILITIES_HISTORY_DAY_RANGE](state, DAYS.NINETY); mutations[types.SET_VULNERABILITIES_HISTORY_DAY_RANGE](state, DAYS.NINETY);
expect(state.vulnerabilitiesHistoryMaxDayInterval).toEqual(14); expect(state.vulnerabilitiesHistoryMaxDayInterval).toBe(14);
}); });
}); });
...@@ -241,68 +241,17 @@ describe('vulnerabilities module mutations', () => { ...@@ -241,68 +241,17 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the modal title', () => { it('should set the modal title', () => {
expect(state.modal.title).toEqual(vulnerability.name); expect(state.modal.title).toBe(vulnerability.name);
});
it('should set the modal description', () => {
expect(state.modal.data.description.value).toEqual(vulnerability.description);
}); });
it('should set the modal project', () => { it('should set the modal project', () => {
expect(state.modal.data.project.value).toEqual(vulnerability.project.full_name); expect(state.modal.project.value).toBe(vulnerability.project.full_name);
expect(state.modal.data.project.url).toEqual(vulnerability.project.full_path); expect(state.modal.project.url).toBe(vulnerability.project.full_path);
});
it('should set the modal file', () => {
expect(state.modal.data.file.value).toEqual(
`${vulnerability.location.file}:${vulnerability.location.start_line}`,
);
});
it('should set the modal className', () => {
expect(state.modal.data.className.value).toEqual(vulnerability.location.class);
});
it('should set the modal image', () => {
expect(state.modal.data.image.value).toEqual(vulnerability.location.image);
});
it('should set the modal namespace', () => {
expect(state.modal.data.namespace.value).toEqual(vulnerability.location.operating_system);
});
it('should set the modal identifiers', () => {
expect(state.modal.data.identifiers.value).toEqual(vulnerability.identifiers);
});
it('should set the modal severity', () => {
expect(state.modal.data.severity.value).toEqual(vulnerability.severity);
});
it('should set the modal class', () => {
expect(state.modal.data.className.value).toEqual(vulnerability.location.class);
});
it('should set the modal links', () => {
expect(state.modal.data.links.value).toEqual(vulnerability.links);
});
it('should set the modal instances', () => {
expect(state.modal.data.instances.value).toEqual(vulnerability.instances);
}); });
it('should set the modal vulnerability', () => { it('should set the modal vulnerability', () => {
expect(state.modal.vulnerability).toEqual(vulnerability); expect(state.modal.vulnerability).toEqual(vulnerability);
}); });
it('should set the modal URL', () => {
const { url } = state.modal.data;
const { hostname, path } = vulnerability.location;
const expected = `${hostname}${path}`;
expect(url.value).toEqual(expected);
expect(url.url).toEqual(expected);
});
}); });
describe('with irregular data', () => { describe('with irregular data', () => {
...@@ -313,7 +262,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -313,7 +262,7 @@ describe('vulnerabilities module mutations', () => {
}; };
mutations[types.SET_MODAL_DATA](state, payload); mutations[types.SET_MODAL_DATA](state, payload);
expect(state.modal.vulnerability.isDismissed).toEqual(true); expect(state.modal.vulnerability.isDismissed).toBe(true);
}); });
it('should set hasIssue when the vulnerabilitiy has a related issue', () => { it('should set hasIssue when the vulnerabilitiy has a related issue', () => {
...@@ -327,7 +276,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -327,7 +276,7 @@ describe('vulnerabilities module mutations', () => {
}; };
mutations[types.SET_MODAL_DATA](state, payload); mutations[types.SET_MODAL_DATA](state, payload);
expect(state.modal.vulnerability.hasIssue).toEqual(true); expect(state.modal.vulnerability.hasIssue).toBe(true);
}); });
it('should not set hasIssue when the issue_iid in null', () => { it('should not set hasIssue when the issue_iid in null', () => {
...@@ -341,28 +290,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -341,28 +290,7 @@ describe('vulnerabilities module mutations', () => {
}; };
mutations[types.SET_MODAL_DATA](state, payload); mutations[types.SET_MODAL_DATA](state, payload);
expect(state.modal.vulnerability.hasIssue).toEqual(false); expect(state.modal.vulnerability.hasIssue).toBe(false);
});
it('should nullify the modal links', () => {
const payload = { vulnerability: { ...vulnerability, links: [] } };
mutations[types.SET_MODAL_DATA](state, payload);
expect(state.modal.data.links.value).toEqual(null);
});
it('should nullify the instances', () => {
const payload = { vulnerability: { ...vulnerability, instances: [] } };
mutations[types.SET_MODAL_DATA](state, payload);
expect(state.modal.data.instances.value).toEqual(null);
});
it('should nullify the file value', () => {
const payload = { vulnerability: { ...vulnerability, location: {} } };
mutations[types.SET_MODAL_DATA](state, payload);
expect(state.modal.data.file.value).toEqual(null);
}); });
}); });
}); });
...@@ -409,7 +337,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -409,7 +337,7 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the error state on the modal', () => { it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error creating the issue'); expect(state.modal.error).toBe('There was an error creating the issue');
}); });
}); });
...@@ -455,7 +383,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -455,7 +383,7 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the error state on the modal', () => { it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error creating the merge request'); expect(state.modal.error).toBe('There was an error creating the merge request');
}); });
}); });
...@@ -521,7 +449,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -521,7 +449,7 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the error state on the modal', () => { it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error dismissing the vulnerability.'); expect(state.modal.error).toBe('There was an error dismissing the vulnerability.');
}); });
}); });
...@@ -587,7 +515,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -587,7 +515,7 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the error state on the modal', () => { it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error deleting the comment.'); expect(state.modal.error).toBe('There was an error deleting the comment.');
}); });
}); });
...@@ -673,7 +601,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -673,7 +601,7 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the error state on the modal', () => { it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error adding the comment.'); expect(state.modal.error).toBe('There was an error adding the comment.');
}); });
}); });
...@@ -737,7 +665,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -737,7 +665,7 @@ describe('vulnerabilities module mutations', () => {
}); });
it('should set the error state on the modal', () => { it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error reverting the dismissal.'); expect(state.modal.error).toBe('There was an error reverting the dismissal.');
}); });
}); });
......
...@@ -21817,6 +21817,9 @@ msgstr "" ...@@ -21817,6 +21817,9 @@ msgstr ""
msgid "Vulnerability|Links" msgid "Vulnerability|Links"
msgstr "" msgstr ""
msgid "Vulnerability|Method"
msgstr ""
msgid "Vulnerability|Namespace" msgid "Vulnerability|Namespace"
msgstr "" msgstr ""
...@@ -22935,9 +22938,6 @@ msgstr "" ...@@ -22935,9 +22938,6 @@ msgstr ""
msgid "ciReport|Base pipeline codequality artifact not found" msgid "ciReport|Base pipeline codequality artifact not found"
msgstr "" msgstr ""
msgid "ciReport|Class"
msgstr ""
msgid "ciReport|Code quality" msgid "ciReport|Code quality"
msgstr "" msgstr ""
...@@ -22968,9 +22968,6 @@ msgstr "" ...@@ -22968,9 +22968,6 @@ msgstr ""
msgid "ciReport|Dependency scanning" msgid "ciReport|Dependency scanning"
msgstr "" msgstr ""
msgid "ciReport|Description"
msgstr ""
msgid "ciReport|Download patch to resolve" msgid "ciReport|Download patch to resolve"
msgstr "" msgstr ""
...@@ -22983,42 +22980,21 @@ msgstr "" ...@@ -22983,42 +22980,21 @@ msgstr ""
msgid "ciReport|Failed to load %{reportName} report" msgid "ciReport|Failed to load %{reportName} report"
msgstr "" msgstr ""
msgid "ciReport|File"
msgstr ""
msgid "ciReport|Fixed:" msgid "ciReport|Fixed:"
msgstr "" msgstr ""
msgid "ciReport|Identifiers"
msgstr ""
msgid "ciReport|Image"
msgstr ""
msgid "ciReport|Instances"
msgstr ""
msgid "ciReport|Investigate this vulnerability by creating an issue" msgid "ciReport|Investigate this vulnerability by creating an issue"
msgstr "" msgstr ""
msgid "ciReport|Learn more about interacting with security reports" msgid "ciReport|Learn more about interacting with security reports"
msgstr "" msgstr ""
msgid "ciReport|Links"
msgstr ""
msgid "ciReport|Loading %{reportName} report" msgid "ciReport|Loading %{reportName} report"
msgstr "" msgstr ""
msgid "ciReport|Manage licenses" msgid "ciReport|Manage licenses"
msgstr "" msgstr ""
msgid "ciReport|Method"
msgstr ""
msgid "ciReport|Namespace"
msgstr ""
msgid "ciReport|No changes to code quality" msgid "ciReport|No changes to code quality"
msgstr "" msgstr ""
...@@ -23040,9 +23016,6 @@ msgstr "" ...@@ -23040,9 +23016,6 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results" msgid "ciReport|Security scanning failed loading any results"
msgstr "" msgstr ""
msgid "ciReport|Severity"
msgstr ""
msgid "ciReport|Solution" msgid "ciReport|Solution"
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