Commit d6e90f61 authored by Scott Hampton's avatar Scott Hampton

Merge branch 'dpisek-make-it-easier-to-add-generic-report-type' into 'master'

Simplify adding a type-component for generic vulnerability reports

See merge request gitlab-org/gitlab!59943
parents c293c5e4 23ad7e55
<script>
import List from './types/list.vue';
import Url from './types/url.vue';
import { REPORT_COMPONENTS, getComponentNameForType } from './types/constants';
export default {
components: {
List,
Url,
...REPORT_COMPONENTS,
},
props: {
item: {
......@@ -13,8 +11,13 @@ export default {
required: true,
},
},
computed: {
componentName() {
return getComponentNameForType(this.item.type);
},
},
};
</script>
<template>
<component :is="item.type" v-bind="item" data-testid="reportComponent" />
<component :is="componentName" v-bind="item" data-testid="reportComponent" />
</template>
export const REPORT_TYPE_LIST = 'list';
export const REPORT_TYPE_URL = 'url';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
export const REPORT_TYPES = [REPORT_TYPE_LIST, REPORT_TYPE_URL];
export const REPORT_TYPES = {
list: 'list',
url: 'url',
};
const REPORT_TYPE_TO_COMPONENT_MAP = {
[REPORT_TYPES.list]: () => import('./list.vue'),
[REPORT_TYPES.url]: () => import('./url.vue'),
};
export const getComponentNameForType = (reportType) =>
`ReportType${capitalizeFirstCharacter(reportType)}`;
export const REPORT_COMPONENTS = Object.fromEntries(
Object.entries(REPORT_TYPE_TO_COMPONENT_MAP).map(([reportType, component]) => [
getComponentNameForType(reportType),
component,
]),
);
......@@ -27,7 +27,7 @@ export default {
:key="item.name"
:class="{ 'gl-list-style-none!': $options.isListType(item) }"
>
<report-item :item="item" />
<report-item :item="item" data-testid="reportItem" />
</li>
</ul>
</template>
import { overEvery } from 'lodash';
import { REPORT_TYPES, REPORT_TYPE_LIST } from './constants';
import { REPORT_TYPES } from './constants';
/**
* Check if the given report is of a type that can be rendered (i.e, is mapped to a component and can be rendered)
......@@ -7,7 +7,7 @@ import { REPORT_TYPES, REPORT_TYPE_LIST } from './constants';
* @param {{ type: string }} reportItem
* @returns boolean
*/
const isSupportedType = ({ type }) => REPORT_TYPES.includes(type);
const isSupportedType = ({ type }) => Object.values(REPORT_TYPES).includes(type);
/**
* Check if the given report is of type list
......@@ -15,7 +15,7 @@ const isSupportedType = ({ type }) => REPORT_TYPES.includes(type);
* @param {{ type: string } } reportItem
* @returns boolean
*/
export const isListType = ({ type }) => type === REPORT_TYPE_LIST;
export const isListType = ({ type }) => type === REPORT_TYPES.list;
/**
* Check if the current report item is of that list and is not nested deeper than the maximum depth
......
......@@ -2,17 +2,16 @@ import { shallowMount } from '@vue/test-utils';
import ReportItem from 'ee/vulnerabilities/components/generic_report/report_item.vue';
import {
REPORT_TYPES,
REPORT_TYPE_URL,
REPORT_TYPE_LIST,
REPORT_COMPONENTS,
} from 'ee/vulnerabilities/components/generic_report/types/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
const TEST_DATA = {
[REPORT_TYPE_URL]: {
[REPORT_TYPES.url]: {
href: 'http://foo.com',
},
[REPORT_TYPE_LIST]: {
items: [{ type: 'foo' }],
[REPORT_TYPES.list]: {
items: [],
},
};
......@@ -26,12 +25,18 @@ describe('ee/vulnerabilities/components/generic_report/report_item.vue', () => {
item: {},
...props,
},
// manual stubbing is needed because the components are dynamically imported
stubs: Object.keys(REPORT_COMPONENTS),
}),
);
const findReportComponent = () => wrapper.findByTestId('reportComponent');
describe.each(REPORT_TYPES)('with report type "%s"', (reportType) => {
afterEach(() => {
wrapper.destroy();
});
describe.each(Object.values(REPORT_TYPES))('with report type "%s"', (reportType) => {
const reportItem = { type: reportType, ...TEST_DATA[reportType] };
beforeEach(() => {
......
......@@ -2,19 +2,19 @@ import { within, fireEvent } from '@testing-library/dom';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import ReportSection from 'ee/vulnerabilities/components/generic_report/report_section.vue';
import { REPORT_TYPE_URL } from 'ee/vulnerabilities/components/generic_report/types/constants';
import { REPORT_TYPES } from 'ee/vulnerabilities/components/generic_report/types/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
const TEST_DATA = {
supportedTypes: {
one: {
name: 'one',
type: REPORT_TYPE_URL,
type: REPORT_TYPES.url,
href: 'http://foo.com',
},
two: {
name: 'two',
type: REPORT_TYPE_URL,
type: REPORT_TYPES.url,
href: 'http://bar.com',
},
},
......
import { screen } from '@testing-library/dom';
import { shallowMount } from '@vue/test-utils';
import ReportItem from 'ee/vulnerabilities/components/generic_report/report_item.vue';
import List from 'ee/vulnerabilities/components/generic_report/types/list.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
const TEST_DATA = {
items: [
......@@ -13,21 +13,30 @@ const TEST_DATA = {
describe('ee/vulnerabilities/components/generic_report/types/list.vue', () => {
let wrapper;
const createWrapper = () => {
return shallowMount(List, {
propsData: {
items: TEST_DATA.items,
},
attachTo: document.body,
});
};
const createWrapper = () =>
extendedWrapper(
shallowMount(List, {
propsData: {
items: TEST_DATA.items,
},
attachTo: document.body,
// manual stubbing is needed because the component is dynamically imported
stubs: {
ReportItem: true,
},
}),
);
const findReportItems = () => wrapper.findAllComponents(ReportItem);
const findReportItems = () => wrapper.findAllByTestId('reportItem');
beforeEach(() => {
wrapper = createWrapper();
});
afterEach(() => {
wrapper.destroy();
});
it('renders a list', () => {
expect(screen.getByRole('list')).toBeInstanceOf(HTMLElement);
});
......
......@@ -23,6 +23,10 @@ describe('ee/vulnerabilities/components/generic_report/types/url.vue', () => {
wrapper = createWrapper();
});
afterEach(() => {
wrapper.destroy();
});
it('renders a link', () => {
expect(findLink().exists()).toBe(true);
});
......
import {
REPORT_TYPE_LIST,
REPORT_TYPE_URL,
} from 'ee/vulnerabilities/components/generic_report/types/constants';
import { REPORT_TYPES } from 'ee/vulnerabilities/components/generic_report/types/constants';
import { filterTypesAndLimitListDepth } from 'ee/vulnerabilities/components/generic_report/types/utils';
const MOCK_REPORT_TYPE_UNSUPPORTED = 'MOCK_REPORT_TYPE_UNSUPPORTED';
const TEST_DATA = {
url: {
type: REPORT_TYPE_URL,
type: REPORT_TYPES.url,
name: 'url1',
},
list: {
type: REPORT_TYPE_LIST,
type: REPORT_TYPES.list,
name: 'rootList',
items: [
{ type: REPORT_TYPE_URL, name: 'url2' },
{ type: REPORT_TYPES.url, name: 'url2' },
{
type: REPORT_TYPE_LIST,
type: REPORT_TYPES.list,
name: 'listDepthOne',
items: [
{ type: REPORT_TYPE_URL, name: 'url3' },
{ type: REPORT_TYPES.url, name: 'url3' },
{
type: REPORT_TYPE_LIST,
type: REPORT_TYPES.list,
name: 'listDepthTwo',
items: [
{ type: REPORT_TYPE_URL, name: 'url4' },
{ type: REPORT_TYPES.url, name: 'url4' },
{
type: REPORT_TYPE_LIST,
type: REPORT_TYPES.list,
name: 'listDepthThree',
items: [
{ type: REPORT_TYPE_URL, name: 'url5' },
{ type: REPORT_TYPES.url, name: 'url5' },
{ type: MOCK_REPORT_TYPE_UNSUPPORTED },
],
},
......@@ -52,7 +49,7 @@ describe('ee/vulnerabilities/components/generic_report/types/utils', () => {
const getListWithDepthTwo = (reportsData) => reportsData.list.items[1].items[1];
const includesType = (type) => (items) =>
items.find(({ type: currentType }) => currentType === type) !== undefined;
const includesListItem = includesType(REPORT_TYPE_LIST);
const includesListItem = includesType(REPORT_TYPES.list);
const includesUnsupportedType = includesType(MOCK_REPORT_TYPE_UNSUPPORTED);
describe.each`
......
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