Commit 50d2a85b authored by Savas Vedova's avatar Savas Vedova

Merge branch 'make-vulnerability-training-reusable' into 'master'

Refactor vulnerability training to be reusable

See merge request gitlab-org/gitlab!80308
parents 5b46af3b 7efe7715
......@@ -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({
identifiers,
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