Commit fc3c2872 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'refactor-training-vulner-det-page' into 'master'

Refactor vulnerability training component

See merge request gitlab-org/gitlab!78225
parents 09083bab 9f21f67f
...@@ -6,7 +6,6 @@ import convertReportType from 'ee/vue_shared/security_reports/store/utils/conver ...@@ -6,7 +6,6 @@ import convertReportType from 'ee/vue_shared/security_reports/store/utils/conver
import { SUPPORTING_MESSAGE_TYPES } from 'ee/vulnerabilities/constants'; import { SUPPORTING_MESSAGE_TYPES } from 'ee/vulnerabilities/constants';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import CodeBlock from '~/vue_shared/components/code_block.vue'; import CodeBlock from '~/vue_shared/components/code_block.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DetailItem from './detail_item.vue'; import DetailItem from './detail_item.vue';
import VulnerabilityDetailSection from './vulnerability_detail_section.vue'; import VulnerabilityDetailSection from './vulnerability_detail_section.vue';
import VulnerabilityTraining from './vulnerability_training.vue'; import VulnerabilityTraining from './vulnerability_training.vue';
...@@ -25,7 +24,6 @@ export default { ...@@ -25,7 +24,6 @@ export default {
directives: { directives: {
SafeHtml: GlSafeHtmlDirective, SafeHtml: GlSafeHtmlDirective,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
vulnerability: { vulnerability: {
type: Object, type: Object,
...@@ -152,9 +150,6 @@ export default { ...@@ -152,9 +150,6 @@ export default {
hasResponses() { hasResponses() {
return Boolean(this.hasResponse || this.hasRecordedResponse); return Boolean(this.hasResponse || this.hasRecordedResponse);
}, },
hasTraining() {
return this.glFeatures.secureVulnerabilityTraining && this.vulnerability.identifiers?.length;
},
}, },
methods: { methods: {
getHeadersAsCodeBlockLines(headers) { getHeadersAsCodeBlockLines(headers) {
...@@ -381,6 +376,6 @@ export default { ...@@ -381,6 +376,6 @@ export default {
</ul> </ul>
</template> </template>
<vulnerability-training v-if="hasTraining" :identifiers="vulnerability.identifiers" /> <vulnerability-training :identifiers="vulnerability.identifiers" />
</div> </div>
</template> </template>
<script> <script>
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { SUPPORTED_REFERENCE_SCHEMA } from '../constants'; import { SUPPORTED_REFERENCE_SCHEMA } from '../constants';
export const i18n = { export const i18n = {
...@@ -12,6 +13,7 @@ export const i18n = { ...@@ -12,6 +13,7 @@ export const i18n = {
export default { export default {
i18n, i18n,
mixins: [glFeatureFlagsMixin()],
props: { props: {
identifiers: { identifiers: {
type: Array, type: Array,
...@@ -19,20 +21,23 @@ export default { ...@@ -19,20 +21,23 @@ export default {
}, },
}, },
computed: { computed: {
hasTraining() {
return this.glFeatures.secureVulnerabilityTraining && this.identifiers?.length;
},
isSupportedReferenceSchema() { isSupportedReferenceSchema() {
return this.referenceSchemas.some( return this.referenceSchemas?.some(
(referenceSchema) => referenceSchema?.toLowerCase() === SUPPORTED_REFERENCE_SCHEMA.cwe, (referenceSchema) => referenceSchema?.toLowerCase() === SUPPORTED_REFERENCE_SCHEMA.cwe,
); );
}, },
referenceSchemas() { referenceSchemas() {
return this.identifiers.map((identifier) => identifier?.externalType); return this.identifiers?.map((identifier) => identifier?.externalType);
}, },
}, },
}; };
</script> </script>
<template> <template>
<div> <div v-if="hasTraining">
<h3>{{ $options.i18n.trainingTitle }}</h3> <h3>{{ $options.i18n.trainingTitle }}</h3>
<p class="gl-text-gray-600!" data-testid="description"> <p class="gl-text-gray-600!" data-testid="description">
{{ $options.i18n.trainingDescription }} {{ $options.i18n.trainingDescription }}
......
...@@ -15,26 +15,21 @@ describe('Vulnerability Details', () => { ...@@ -15,26 +15,21 @@ describe('Vulnerability Details', () => {
reportType: 'Some report type', reportType: 'Some report type',
description: 'vulnerability description', description: 'vulnerability description',
descriptionHtml: 'vulnerability description <code>sample</code>', descriptionHtml: 'vulnerability description <code>sample</code>',
identifiers: [],
}; };
const createWrapper = (vulnerabilityOverrides, { secureVulnerabilityTraining = true } = {}) => { const createWrapper = (vulnerabilityOverrides) => {
const propsData = { const propsData = {
vulnerability: { ...vulnerability, ...vulnerabilityOverrides }, vulnerability: { ...vulnerability, ...vulnerabilityOverrides },
}; };
wrapper = mount(VulnerabilityDetails, { wrapper = mount(VulnerabilityDetails, {
propsData, propsData,
provide: {
glFeatures: {
secureVulnerabilityTraining,
},
},
}); });
}; };
const getById = (id) => wrapper.find(`[data-testid="${id}"]`); const getById = (id) => wrapper.find(`[data-testid="${id}"]`);
const getAllById = (id) => wrapper.findAll(`[data-testid="${id}"]`); const getAllById = (id) => wrapper.findAll(`[data-testid="${id}"]`);
const getText = (id) => getById(id).text(); const getText = (id) => getById(id).text();
const findVulnerabilityTraining = () => wrapper.findComponent(VulnerabilityTraining);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -198,6 +193,14 @@ describe('Vulnerability Details', () => { ...@@ -198,6 +193,14 @@ describe('Vulnerability Details', () => {
assetsData.forEach(checkIdentifier); assetsData.forEach(checkIdentifier);
}); });
it('renders the vulnerabilityTraining component', () => {
const identifiers = [{ externalType: 'cwe' }, { externalType: 'cve' }];
createWrapper({ identifiers });
expect(wrapper.findComponent(VulnerabilityTraining).props()).toMatchObject({
identifiers,
});
});
describe('file link', () => { describe('file link', () => {
const file = () => getById('file').find(GlLink); const file = () => getById('file').find(GlLink);
...@@ -406,26 +409,4 @@ describe('Vulnerability Details', () => { ...@@ -406,26 +409,4 @@ describe('Vulnerability Details', () => {
expect(getSectionData('recorded-response')).toEqual(expectedData); expect(getSectionData('recorded-response')).toEqual(expectedData);
}); });
}); });
describe('vulnerability training', () => {
it('renders the component', () => {
const identifiers = [{ externalType: 'cwe' }, { externalType: 'cve' }];
createWrapper({ identifiers });
expect(wrapper.findComponent(VulnerabilityTraining).props()).toMatchObject({
identifiers,
});
});
it('does not render the component when there are no identifiers', () => {
createWrapper();
expect(findVulnerabilityTraining().exists()).toBe(false);
});
});
describe('when secureVulnerabilityTraining feature flag is disabled', () => {
it('does not render the VulnerabilityTraining component', () => {
createWrapper(undefined, { secureVulnerabilityTraining: false });
expect(findVulnerabilityTraining().exists()).toBe(false);
});
});
}); });
...@@ -11,12 +11,17 @@ const defaultProps = { ...@@ -11,12 +11,17 @@ const defaultProps = {
describe('VulnerabilityTraining component', () => { describe('VulnerabilityTraining component', () => {
let wrapper; let wrapper;
const createComponent = (props = {}) => { const createComponent = (props = {}, { secureVulnerabilityTraining = true } = {}) => {
wrapper = shallowMountExtended(VulnerabilityTraining, { wrapper = shallowMountExtended(VulnerabilityTraining, {
propsData: { propsData: {
...defaultProps, ...defaultProps,
...props, ...props,
}, },
provide: {
glFeatures: {
secureVulnerabilityTraining,
},
},
}); });
}; };
...@@ -40,6 +45,11 @@ describe('VulnerabilityTraining component', () => { ...@@ -40,6 +45,11 @@ describe('VulnerabilityTraining component', () => {
it('displays the description', () => { it('displays the description', () => {
expect(findDescription().text()).toBe(i18n.trainingDescription); expect(findDescription().text()).toBe(i18n.trainingDescription);
}); });
it('does not render component when there are no identifiers', () => {
createComponent({ identifiers: [] });
expect(wrapper.html()).toBeFalsy();
});
}); });
describe('training availability message', () => { describe('training availability message', () => {
...@@ -58,4 +68,11 @@ describe('VulnerabilityTraining component', () => { ...@@ -58,4 +68,11 @@ describe('VulnerabilityTraining component', () => {
expect(findUnavailableMessage().exists()).toBe(exists); expect(findUnavailableMessage().exists()).toBe(exists);
}); });
}); });
describe('when secureVulnerabilityTraining feature flag is disabled', () => {
it('does not render the VulnerabilityTraining component', () => {
createComponent({}, { secureVulnerabilityTraining: false });
expect(wrapper.html()).toBeFalsy();
});
});
}); });
...@@ -13,4 +13,5 @@ export const mockVulnerability = { ...@@ -13,4 +13,5 @@ export const mockVulnerability = {
project: { project: {
full_path: '/project_full_path', full_path: '/project_full_path',
}, },
identifiers: [],
}; };
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