Commit a7e9994f authored by Daniel Tian's avatar Daniel Tian Committed by Brandon Labuschagne

Use provide/inject for vulnerabilities export endpoint

Use provide/inject instead of props to pass the vulnerabilites export
endpoint URL to the CSV export button on the vulnerability report page
parent 4b6c31c7
<script> <script>
import { GlPopover, GlIcon, GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui'; import { GlPopover, GlIcon, GlLink, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import AccessorUtils from '~/lib/utils/accessor';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import download from '~/lib/utils/downloader'; import download from '~/lib/utils/downloader';
...@@ -19,12 +20,7 @@ export default { ...@@ -19,12 +20,7 @@ export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
props: { inject: ['vulnerabilitiesExportEndpoint'],
vulnerabilitiesExportEndpoint: {
type: String,
required: true,
},
},
data() { data() {
return { return {
isPreparingCsvExport: false, isPreparingCsvExport: false,
...@@ -45,10 +41,8 @@ export default { ...@@ -45,10 +41,8 @@ export default {
closePopover() { closePopover() {
this.showPopover = false; this.showPopover = false;
try { if (AccessorUtils.isLocalStorageAccessSafe()) {
localStorage.setItem(STORAGE_KEY, 'true'); localStorage.setItem(STORAGE_KEY, 'true');
} catch (e) {
// Ignore the error - this is just a safety measure.
} }
}, },
initiateCsvExport() { initiateCsvExport() {
...@@ -87,9 +81,10 @@ export default { ...@@ -87,9 +81,10 @@ export default {
> >
{{ __('Export') }} {{ __('Export') }}
<gl-popover <gl-popover
v-if="showPopover"
ref="popover" ref="popover"
:target="() => $refs.csvExportButton.$el" :target="() => $refs.csvExportButton.$el"
:show="showPopover" show
placement="left" placement="left"
triggers="manual" triggers="manual"
> >
......
...@@ -20,12 +20,6 @@ export default { ...@@ -20,12 +20,6 @@ export default {
VulnerabilitiesCountList, VulnerabilitiesCountList,
}, },
inject: ['groupFullPath'], inject: ['groupFullPath'],
props: {
vulnerabilitiesExportEndpoint: {
type: String,
required: true,
},
},
apollo: { apollo: {
projects: { projects: {
query: vulnerableProjectsQuery, query: vulnerableProjectsQuery,
...@@ -74,7 +68,7 @@ export default { ...@@ -74,7 +68,7 @@ export default {
<h2 class="gl-flex-grow-1 gl-my-0"> <h2 class="gl-flex-grow-1 gl-my-0">
{{ s__('SecurityReports|Vulnerability Report') }} {{ s__('SecurityReports|Vulnerability Report') }}
</h2> </h2>
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" /> <csv-export-button />
</header> </header>
<vulnerabilities-count-list <vulnerabilities-count-list
:scope="$options.vulnerabilitiesSeverityCountScopes.group" :scope="$options.vulnerabilitiesSeverityCountScopes.group"
......
...@@ -19,12 +19,6 @@ export default { ...@@ -19,12 +19,6 @@ export default {
DashboardNotConfigured, DashboardNotConfigured,
VulnerabilitiesCountList, VulnerabilitiesCountList,
}, },
props: {
vulnerabilitiesExportEndpoint: {
type: String,
required: true,
},
},
apollo: { apollo: {
projects: { projects: {
query: projectsQuery, query: projectsQuery,
...@@ -75,7 +69,7 @@ export default { ...@@ -75,7 +69,7 @@ export default {
<h2 class="gl-flex-grow-1 gl-my-0"> <h2 class="gl-flex-grow-1 gl-my-0">
{{ s__('SecurityReports|Vulnerability Report') }} {{ s__('SecurityReports|Vulnerability Report') }}
</h2> </h2>
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" /> <csv-export-button />
</header> </header>
<vulnerabilities-count-list <vulnerabilities-count-list
:scope="$options.vulnerabilitiesSeverityCountScopes.instance" :scope="$options.vulnerabilitiesSeverityCountScopes.instance"
......
...@@ -34,11 +34,6 @@ export default { ...@@ -34,11 +34,6 @@ export default {
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
vulnerabilitiesExportEndpoint: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
const shouldShowAutoFixUserCallout = const shouldShowAutoFixUserCallout =
...@@ -73,7 +68,7 @@ export default { ...@@ -73,7 +68,7 @@ export default {
<template #header> <template #header>
<div class="gl-mt-6 gl-display-flex"> <div class="gl-mt-6 gl-display-flex">
<h4 class="gl-flex-grow-1 gl-my-0">{{ __('Vulnerability Report') }}</h4> <h4 class="gl-flex-grow-1 gl-my-0">{{ __('Vulnerability Report') }}</h4>
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" /> <csv-export-button />
</div> </div>
<project-pipeline-status :pipeline="pipeline" /> <project-pipeline-status :pipeline="pipeline" />
<vulnerabilities-count-list <vulnerabilities-count-list
......
...@@ -65,6 +65,7 @@ export default (el, dashboardType) => { ...@@ -65,6 +65,7 @@ export default (el, dashboardType) => {
groupFullPath, groupFullPath,
securityConfigurationPath, securityConfigurationPath,
surveyRequestSvgPath, surveyRequestSvgPath,
vulnerabilitiesExportEndpoint,
hasVulnerabilities: parseBoolean(hasVulnerabilities), hasVulnerabilities: parseBoolean(hasVulnerabilities),
scanners: scanners ? JSON.parse(scanners) : [], scanners: scanners ? JSON.parse(scanners) : [],
hasJiraVulnerabilitiesIntegrationEnabled: parseBoolean( hasJiraVulnerabilitiesIntegrationEnabled: parseBoolean(
...@@ -76,7 +77,6 @@ export default (el, dashboardType) => { ...@@ -76,7 +77,6 @@ export default (el, dashboardType) => {
securityDashboardHelpPath, securityDashboardHelpPath,
projectAddEndpoint, projectAddEndpoint,
projectListEndpoint, projectListEndpoint,
vulnerabilitiesExportEndpoint,
}; };
let component; let component;
......
---
title: Use provide/inject for vulnerabilities export endpoint
merge_request: 57639
author:
type: other
...@@ -4,8 +4,10 @@ import MockAdapter from 'axios-mock-adapter'; ...@@ -4,8 +4,10 @@ import MockAdapter from 'axios-mock-adapter';
import CsvExportButton, { import CsvExportButton, {
STORAGE_KEY, STORAGE_KEY,
} from 'ee/security_dashboard/components/csv_export_button.vue'; } from 'ee/security_dashboard/components/csv_export_button.vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import AccessorUtils from '~/lib/utils/accessor';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import downloader from '~/lib/utils/downloader'; import downloader from '~/lib/utils/downloader';
...@@ -14,6 +16,8 @@ import statusCodes from '~/lib/utils/http_status'; ...@@ -14,6 +16,8 @@ import statusCodes from '~/lib/utils/http_status';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/lib/utils/downloader'); jest.mock('~/lib/utils/downloader');
useLocalStorageSpy();
const mockReportDate = formatDate(new Date(), 'isoDateTime'); const mockReportDate = formatDate(new Date(), 'isoDateTime');
const vulnerabilitiesExportEndpoint = `${TEST_HOST}/vulnerability_findings.csv`; const vulnerabilitiesExportEndpoint = `${TEST_HOST}/vulnerability_findings.csv`;
...@@ -29,7 +33,7 @@ describe('Csv Button Export', () => { ...@@ -29,7 +33,7 @@ describe('Csv Button Export', () => {
const createComponent = () => { const createComponent = () => {
return shallowMount(CsvExportButton, { return shallowMount(CsvExportButton, {
propsData: { provide: {
vulnerabilitiesExportEndpoint, vulnerabilitiesExportEndpoint,
}, },
stubs: { stubs: {
...@@ -49,7 +53,7 @@ describe('Csv Button Export', () => { ...@@ -49,7 +53,7 @@ describe('Csv Button Export', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
localStorage.removeItem(STORAGE_KEY); localStorage.clear();
}); });
describe('when the user sees the button for the first time', () => { describe('when the user sees the button for the first time', () => {
...@@ -117,16 +121,33 @@ describe('Csv Button Export', () => { ...@@ -117,16 +121,33 @@ describe('Csv Button Export', () => {
}); });
it('displays the popover by default', () => { it('displays the popover by default', () => {
expect(findPopover().attributes('show')).toBeTruthy(); expect(findPopover().exists()).toBe(true);
}); });
it('closes the popover when the button is clicked', async () => { describe('closing the popover', () => {
const button = findPopoverButton(); it('closes the popover when the button is clicked', async () => {
expect(button.text().trim()).toBe('Got it!'); expect(findPopoverButton().text()).toBe('Got it!');
button.vm.$emit('click'); findPopoverButton().vm.$emit('click');
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(findPopover().exists()).toBe(false);
});
it('sets localStorage', async () => {
jest.spyOn(AccessorUtils, 'isLocalStorageAccessSafe').mockImplementation(() => true);
findPopoverButton().vm.$emit('click');
await wrapper.vm.$nextTick();
expect(findPopover().attributes('show')).toBeUndefined(); expect(localStorage.setItem).toHaveBeenCalledTimes(1);
});
it(`does not set localStorage if it's not available`, async () => {
jest.spyOn(AccessorUtils, 'isLocalStorageAccessSafe').mockImplementation(() => false);
findPopoverButton().vm.$emit('click');
await wrapper.vm.$nextTick();
expect(localStorage.setItem).toHaveBeenCalledTimes(0);
});
}); });
}); });
...@@ -137,7 +158,7 @@ describe('Csv Button Export', () => { ...@@ -137,7 +158,7 @@ describe('Csv Button Export', () => {
}); });
it('does not display the popover anymore', () => { it('does not display the popover anymore', () => {
expect(findPopover().attributes('show')).toBeFalsy(); expect(findPopover().exists()).toBe(false);
}); });
}); });
}); });
...@@ -14,7 +14,6 @@ describe('First Class Group Dashboard Component', () => { ...@@ -14,7 +14,6 @@ describe('First Class Group Dashboard Component', () => {
const dashboardDocumentation = 'dashboard-documentation'; const dashboardDocumentation = 'dashboard-documentation';
const emptyStateSvgPath = 'empty-state-path'; const emptyStateSvgPath = 'empty-state-path';
const groupFullPath = 'group-full-path'; const groupFullPath = 'group-full-path';
const vulnerabilitiesExportEndpoint = '/vulnerabilities/exports';
const findDashboardLayout = () => wrapper.find(SecurityDashboardLayout); const findDashboardLayout = () => wrapper.find(SecurityDashboardLayout);
const findGroupVulnerabilities = () => wrapper.find(FirstClassGroupVulnerabilities); const findGroupVulnerabilities = () => wrapper.find(FirstClassGroupVulnerabilities);
...@@ -29,7 +28,6 @@ describe('First Class Group Dashboard Component', () => { ...@@ -29,7 +28,6 @@ describe('First Class Group Dashboard Component', () => {
propsData: { propsData: {
dashboardDocumentation, dashboardDocumentation,
emptyStateSvgPath, emptyStateSvgPath,
vulnerabilitiesExportEndpoint,
}, },
provide: { groupFullPath }, provide: { groupFullPath },
data, data,
...@@ -89,9 +87,7 @@ describe('First Class Group Dashboard Component', () => { ...@@ -89,9 +87,7 @@ describe('First Class Group Dashboard Component', () => {
}); });
it('displays the csv export button', () => { it('displays the csv export button', () => {
expect(findCsvExportButton().props('vulnerabilitiesExportEndpoint')).toBe( expect(findCsvExportButton().exists()).toBe(true);
vulnerabilitiesExportEndpoint,
);
}); });
it('loading button should not be rendered', () => { it('loading button should not be rendered', () => {
......
...@@ -14,8 +14,6 @@ describe('First Class Instance Dashboard Component', () => { ...@@ -14,8 +14,6 @@ describe('First Class Instance Dashboard Component', () => {
$apollo: { queries: { projects: { loading } } }, $apollo: { queries: { projects: { loading } } },
}); });
const vulnerabilitiesExportEndpoint = '/vulnerabilities/exports';
const findInstanceVulnerabilities = () => wrapper.find(FirstClassInstanceVulnerabilities); const findInstanceVulnerabilities = () => wrapper.find(FirstClassInstanceVulnerabilities);
const findCsvExportButton = () => wrapper.find(CsvExportButton); const findCsvExportButton = () => wrapper.find(CsvExportButton);
const findEmptyState = () => wrapper.find(DashboardNotConfigured); const findEmptyState = () => wrapper.find(DashboardNotConfigured);
...@@ -29,9 +27,6 @@ describe('First Class Instance Dashboard Component', () => { ...@@ -29,9 +27,6 @@ describe('First Class Instance Dashboard Component', () => {
return { ...data }; return { ...data };
}, },
mocks, mocks,
propsData: {
vulnerabilitiesExportEndpoint,
},
stubs: { stubs: {
...stubs, ...stubs,
SecurityDashboardLayout, SecurityDashboardLayout,
...@@ -77,9 +72,7 @@ describe('First Class Instance Dashboard Component', () => { ...@@ -77,9 +72,7 @@ describe('First Class Instance Dashboard Component', () => {
}); });
it('displays the csv export button', () => { it('displays the csv export button', () => {
expect(findCsvExportButton().props('vulnerabilitiesExportEndpoint')).toBe( expect(findCsvExportButton().exists()).toBe(true);
vulnerabilitiesExportEndpoint,
);
}); });
it('displays the vulnerability count list with the correct data', () => { it('displays the vulnerability count list with the correct data', () => {
......
...@@ -21,7 +21,6 @@ const props = { ...@@ -21,7 +21,6 @@ const props = {
path: '/mixed-vulnerabilities/dependency-list-test-01/-/pipelines/214', path: '/mixed-vulnerabilities/dependency-list-test-01/-/pipelines/214',
}, },
securityDashboardHelpPath: '/security/dashboard/help-path', securityDashboardHelpPath: '/security/dashboard/help-path',
vulnerabilitiesExportEndpoint: '/vulnerabilities/exports',
}; };
const provide = { const provide = {
......
...@@ -52,7 +52,6 @@ describe('ErrorTrackingList', () => { ...@@ -52,7 +52,6 @@ describe('ErrorTrackingList', () => {
beforeEach(() => { beforeEach(() => {
actions = { actions = {
getErrorList: () => {},
startPolling: jest.fn(), startPolling: jest.fn(),
restartPolling: jest.fn().mockName('restartPolling'), restartPolling: jest.fn().mockName('restartPolling'),
addRecentSearch: jest.fn(), addRecentSearch: jest.fn(),
......
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