Commit 4c872937 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch '214255-fix-dismissal-not-being-updated' into 'master'

Fix state not being updated

See merge request gitlab-org/gitlab!30503
parents 67dd65a2 0fad9ea1
......@@ -67,13 +67,15 @@ export default {
.then(() => {
toast(this.dismissalSuccessMessage());
this.$emit('deselect-all-vulnerabilities');
this.$emit('refetch-vulnerabilities');
})
.catch(() => {
createFlash(
s__('SecurityReports|There was an error dismissing the vulnerabilities.'),
'alert',
);
})
.finally(() => {
this.$emit('refetch-vulnerabilities');
});
},
},
......
......@@ -97,6 +97,15 @@ export default {
filters() {
this.selectedVulnerabilities = {};
},
vulnerabilities(vulnerabilities) {
const ids = new Set(vulnerabilities.map(v => v.id));
Object.keys(this.selectedVulnerabilities).forEach(vulnerabilityId => {
if (!ids.has(vulnerabilityId)) {
this.$delete(this.selectedVulnerabilities, vulnerabilityId);
}
});
},
},
methods: {
deselectAllVulnerabilities() {
......
---
title: Fix dismissal state not being updated
merge_request: 30503
author:
type: fixed
......@@ -3,6 +3,7 @@ import SelectionSummary from 'ee/security_dashboard/components/selection_summary
import { GlFormSelect, GlButton } from '@gitlab/ui';
import createFlash from '~/flash';
import toast from '~/vue_shared/plugins/global_toast';
import waitForPromises from 'helpers/wait_for_promises';
jest.mock('~/flash');
jest.mock('~/vue_shared/plugins/global_toast');
......@@ -26,6 +27,10 @@ describe('Selection Summary component', () => {
const formSelect = () => wrapper.find(GlFormSelect);
const createComponent = ({ props = {}, data = defaultData, mocks = defaultMocks }) => {
if (wrapper) {
throw new Error('Please avoid recreating components in the same spec');
}
spyMutate = mocks.$apollo.mutate;
wrapper = mount(SelectionSummary, {
mocks: {
......@@ -45,33 +50,21 @@ describe('Selection Summary component', () => {
wrapper = null;
});
describe('when vulnerabilities are selected', () => {
describe('it renders correctly', () => {
beforeEach(() => {
createComponent({ props: { selectedVulnerabilities: [{ id: 'id_0' }] } });
});
it('returns the right message for one selected vulnerabilities', () => {
expect(dismissMessage().text()).toBe('Dismiss 1 selected vulnerability as');
});
describe('with 1 vulnerability selected', () => {
beforeEach(() => {
createComponent({ props: { selectedVulnerabilities: [{ id: 'id_0' }] } });
});
it('returns the right message for greater than one selected vulnerabilities', () => {
createComponent({ props: { selectedVulnerabilities: [{ id: 'id_0' }, { id: 'id_1' }] } });
expect(dismissMessage().text()).toBe('Dismiss 2 selected vulnerabilities as');
});
it('renders correctly', () => {
expect(dismissMessage().text()).toBe('Dismiss 1 selected vulnerability as');
});
describe('dismiss button', () => {
beforeEach(() => {
createComponent({ props: { selectedVulnerabilities: [{ id: 'id_0' }] } });
});
it('should have the button disabled if an option is not selected', () => {
expect(dismissButton().attributes('disabled')).toBe('disabled');
});
it('should have the button enabled if a vulnerability is selected and an option is selected', () => {
createComponent({ props: { selectedVulnerabilities: [{ id: 'id_0' }] } });
expect(wrapper.vm.dismissalReason).toBe(null);
expect(wrapper.findAll('option')).toHaveLength(4);
formSelect()
......@@ -85,40 +78,69 @@ describe('Selection Summary component', () => {
});
});
});
});
describe('clicking the dismiss vulnerability button', () => {
beforeEach(() => {
createComponent({
props: { selectedVulnerabilities: [{ id: 'id_0' }, { id: 'id_1' }] },
data: { dismissalReason: 'Will Not Fix' },
});
describe('with 1 vulnerabilities selected', () => {
beforeEach(() => {
createComponent({ props: { selectedVulnerabilities: [{ id: 'id_0' }, { id: 'id_1' }] } });
});
it('renders correctly', () => {
expect(dismissMessage().text()).toBe('Dismiss 2 selected vulnerabilities as');
});
});
describe('clicking the dismiss vulnerability button', () => {
let mutateMock;
beforeEach(() => {
mutateMock = jest.fn().mockResolvedValue();
createComponent({
props: { selectedVulnerabilities: [{ id: 'id_0' }, { id: 'id_1' }] },
data: { dismissalReason: 'Will Not Fix' },
mocks: { $apollo: { mutate: mutateMock } },
});
});
it('should make an API request for each vulnerability', () => {
dismissButton().trigger('submit');
expect(spyMutate).toHaveBeenCalledTimes(2);
});
it('should show toast with the right message if all calls were successful', () => {
dismissButton().trigger('submit');
return waitForPromises().then(() => {
expect(toast).toHaveBeenCalledWith('2 vulnerabilities dismissed');
});
});
it('should make an API request for each vulnerability', () => {
dismissButton().trigger('submit');
expect(spyMutate).toHaveBeenCalledTimes(2);
it('should show flash with the right message if some calls failed', () => {
mutateMock.mockRejectedValue();
dismissButton().trigger('submit');
return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error dismissing the vulnerabilities.',
'alert',
);
});
});
it('should show toast with the right message if all calls were successful', () => {
dismissButton().trigger('submit');
setImmediate(() => {
expect(toast).toHaveBeenCalledWith('2 vulnerabilities dismissed');
});
it('should emit an event to refetch the vulnerabilities when the request is successful', () => {
dismissButton().trigger('submit');
return waitForPromises().then(() => {
expect(wrapper.emittedByOrder()).toEqual([
{ name: 'deselect-all-vulnerabilities', args: [] },
{ name: 'refetch-vulnerabilities', args: [] },
]);
});
});
it('should show flash with the right message if some calls failed', () => {
createComponent({
props: { selectedVulnerabilities: [{ id: 'id_0' }, { id: 'id_1' }] },
data: { dismissalReason: 'Will Not Fix' },
mocks: { $apollo: { mutate: jest.fn().mockRejectedValue() } },
});
dismissButton().trigger('submit');
setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith(
'There was an error dismissing the vulnerabilities.',
'alert',
);
});
it('should still emit an event to refetch the vulnerabilities when the request fails', () => {
mutateMock.mockRejectedValue();
dismissButton().trigger('submit');
return waitForPromises().then(() => {
expect(wrapper.emittedByOrder()).toEqual([{ name: 'refetch-vulnerabilities', args: [] }]);
});
});
});
......@@ -127,8 +149,9 @@ describe('Selection Summary component', () => {
beforeEach(() => {
createComponent({});
});
it('should have the button disabled', () => {
expect(dismissButton().attributes().disabled).toBe('disabled');
expect(dismissButton().attributes('disabled')).toBe('disabled');
});
});
});
......@@ -77,25 +77,37 @@ describe('Vulnerability list component', () => {
expect(findCheckAllCheckboxCell().classes()).toContain('d-none');
expect(findFirstCheckboxCell().classes()).toContain('d-none');
});
});
it('should show the selection summary when a checkbox is selected', () => {
describe('when vulnerability selection is enabled', () => {
beforeEach(() => {
wrapper = createWrapper({
props: { vulnerabilities, shouldShowSelection: true },
});
const checkbox = findFirstCheckboxCell().find('input');
checkbox.setChecked();
findFirstCheckboxCell()
.find('input')
.setChecked(true);
});
it('should show the selection summary when a checkbox is selected', () => {
return wrapper.vm.$nextTick().then(() => {
expect(findSelectionSummary().exists()).toBe(true);
});
});
it('should show the checkboxes if shouldShowSelection is passed in', () => {
wrapper = createWrapper({
props: { vulnerabilities, shouldShowSelection: true },
});
expect(findCheckAllCheckboxCell().classes()).not.toContain('d-none');
expect(findFirstCheckboxCell().classes()).not.toContain('d-none');
});
it('should sync selected vulnerabilities when the vulnerability list is updated', () => {
expect(findSelectionSummary().props('selectedVulnerabilities')).toHaveLength(1);
wrapper.setProps({ vulnerabilities: [] });
return wrapper.vm.$nextTick().then(() => {
expect(findSelectionSummary().exists()).toBe(false);
});
});
});
describe('when displayed on instance or group level dashboard', () => {
......
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