Commit 7efe7715 authored by Samantha Ming's avatar Samantha Ming

Refactor vulnerability training to be reusable

This will make it possible to use this component for the
vulnerability modal in the pipeline.
parent 5ddd8885
......@@ -3,7 +3,10 @@ import { GlLink, GlSprintf, GlSafeHtmlDirective } from '@gitlab/ui';
import { bodyWithFallBack } from 'ee/vue_shared/security_reports/components/helpers';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import convertReportType from 'ee/vue_shared/security_reports/store/utils/convert_report_type';
import { SUPPORTING_MESSAGE_TYPES } from 'ee/vulnerabilities/constants';
import {
SUPPORTING_MESSAGE_TYPES,
VULNERABILITY_TRAINING_HEADING,
} from 'ee/vulnerabilities/constants';
import { s__, __ } from '~/locale';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import DetailItem from './detail_item.vue';
......@@ -183,6 +186,7 @@ export default {
'Vulnerability|Actual received response is the one received when this fault was detected',
),
},
VULNERABILITY_TRAINING_HEADING,
};
</script>
......@@ -376,6 +380,10 @@ export default {
</ul>
</template>
<vulnerability-training :identifiers="vulnerability.identifiers" />
<vulnerability-training :identifiers="vulnerability.identifiers">
<template #header>
<h3>{{ $options.VULNERABILITY_TRAINING_HEADING.title }}</h3>
</template>
</vulnerability-training>
</div>
</template>
......@@ -8,7 +8,6 @@ import axios from '~/lib/utils/axios_utils';
import { SUPPORTED_IDENTIFIER_TYPES } from '../constants';
export const i18n = {
trainingTitle: s__('Vulnerability|Training'),
trainingDescription: s__(
'Vulnerability|Learn more about this vulnerability and the best way to resolve it.',
),
......@@ -118,7 +117,7 @@ export default {
<template>
<div v-if="showVulnerabilityTraining">
<h3>{{ $options.i18n.trainingTitle }}</h3>
<slot name="header"></slot>
<p class="gl-text-gray-600!" data-testid="description">
{{ $options.i18n.trainingDescription }}
</p>
......
......@@ -89,3 +89,7 @@ export const SUPPORTING_MESSAGE_TYPES = {
export const SUPPORTED_IDENTIFIER_TYPES = {
cwe: 'cwe',
};
export const VULNERABILITY_TRAINING_HEADING = {
title: s__('Vulnerability|Training'),
};
......@@ -3,7 +3,10 @@ import { getAllByRole, getByTestId } from '@testing-library/dom';
import { mount, shallowMount } from '@vue/test-utils';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import VulnerabilityDetails from 'ee/vulnerabilities/components/vulnerability_details.vue';
import { SUPPORTING_MESSAGE_TYPES } from 'ee/vulnerabilities/constants';
import {
SUPPORTING_MESSAGE_TYPES,
VULNERABILITY_TRAINING_HEADING,
} from 'ee/vulnerabilities/constants';
import VulnerabilityTraining from 'ee/vulnerabilities/components/vulnerability_training.vue';
describe('Vulnerability Details', () => {
......@@ -18,7 +21,7 @@ describe('Vulnerability Details', () => {
identifiers: [],
};
const createWrapper = (vulnerabilityOverrides, { mountFn = mount } = {}) => {
const createWrapper = (vulnerabilityOverrides, { mountFn = mount, options = {} } = {}) => {
const propsData = {
vulnerability: { ...vulnerability, ...vulnerabilityOverrides },
};
......@@ -27,13 +30,16 @@ describe('Vulnerability Details', () => {
provide: {
projectFullPath: 'namespace/project',
},
...options,
});
};
const createShallowWrapper = (...args) => createWrapper(...args, { mountFn: shallowMount });
const createShallowWrapper = (vulnerabilityOverrides, options = {}) =>
createWrapper(vulnerabilityOverrides, { mountFn: shallowMount, options });
const getById = (id) => wrapper.find(`[data-testid="${id}"]`);
const getAllById = (id) => wrapper.findAll(`[data-testid="${id}"]`);
const getText = (id) => getById(id).text();
const findVulnerabilityTraining = () => wrapper.findComponent(VulnerabilityTraining);
afterEach(() => {
wrapper.destroy();
......@@ -197,11 +203,34 @@ describe('Vulnerability Details', () => {
assetsData.forEach(checkIdentifier);
});
it('renders the vulnerabilityTraining component', () => {
describe('VulnerabilityTraining', () => {
const identifiers = [{ externalType: 'cwe' }, { externalType: 'cve' }];
createShallowWrapper({ identifiers });
expect(wrapper.findComponent(VulnerabilityTraining).props()).toMatchObject({
it('renders component', () => {
createShallowWrapper({
identifiers,
});
expect(findVulnerabilityTraining().props()).toMatchObject({
identifiers,
});
});
it('renders title text', () => {
createShallowWrapper(
{
identifiers,
},
{
stubs: {
VulnerabilityTraining: {
template: '<div><slot name="header"></slot></div>',
},
},
},
);
expect(wrapper.text()).toContain(VULNERABILITY_TRAINING_HEADING.title);
});
});
......
......@@ -39,12 +39,13 @@ describe('VulnerabilityTraining component', () => {
]);
};
const createComponent = (props = {}, { secureVulnerabilityTraining = true } = {}) => {
const createComponent = (props = {}, { slots = {}, secureVulnerabilityTraining = true } = {}) => {
wrapper = shallowMountExtended(VulnerabilityTraining, {
propsData: {
...defaultProps,
...props,
},
slots,
apolloProvider,
provide: {
projectFullPath: 'namespace/project',
......@@ -71,7 +72,6 @@ describe('VulnerabilityTraining component', () => {
const mockTrainingSuccess = async () =>
mock.onGet(mockProvider.path).reply(httpStatus.OK, { url: mockSuccessTrainingUrl });
const waitForQueryToBeLoaded = () => waitForPromises();
const findTitle = () => wrapper.findByRole('heading', i18n.trainingTitle);
const findDescription = () => wrapper.findByTestId('description');
const findUnavailableMessage = () => wrapper.findByTestId('unavailable-message');
const findTrainingItemName = () => wrapper.findByText(mockProvider.name);
......@@ -84,12 +84,6 @@ describe('VulnerabilityTraining component', () => {
});
describe('basic structure', () => {
it('displays the title', async () => {
createComponent();
await waitForQueryToBeLoaded();
expect(findTitle().text()).toBe(i18n.trainingTitle);
});
it('displays the description', async () => {
createComponent();
await waitForQueryToBeLoaded();
......@@ -107,6 +101,15 @@ describe('VulnerabilityTraining component', () => {
});
});
describe('with title slot', () => {
it('renders slot content', async () => {
const mockSlotText = 'some title';
createComponent({}, { slots: { header: mockSlotText } });
await waitForQueryToBeLoaded();
expect(wrapper.text()).toContain(mockSlotText);
});
});
describe('training availability message', () => {
it('displays the message', async () => {
createComponent({
......
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