Commit d1ec236d authored by Fernando Arias's avatar Fernando Arias Committed by Paul Slaughter

Add toast messages for vuln dismissals

* Add toast message for add/edit/delete dismissal reasons
parent f24ff741
import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
Vue.use(GlToast);
export default function showGlobalToast(...args) {
return Vue.toasted.show(...args);
}
import $ from 'jquery'; import $ from 'jquery';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import downloadPatchHelper from 'ee/vue_shared/security_reports/store/utils/download_patch_helper'; import downloadPatchHelper from 'ee/vue_shared/security_reports/store/utils/download_patch_helper';
import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { s__ } from '~/locale'; import { s__, sprintf } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import toast from '~/vue_shared/plugins/global_toast';
import * as types from './mutation_types';
/** /**
* A lot of this file has duplicate actions in * A lot of this file has duplicate actions in
...@@ -153,6 +154,10 @@ export const dismissVulnerability = ( ...@@ -153,6 +154,10 @@ export const dismissVulnerability = (
) => { ) => {
dispatch('requestDismissVulnerability'); dispatch('requestDismissVulnerability');
const toastMsg = sprintf(s__("Security Reports|Dismissed '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
});
axios axios
.post(vulnerability.create_vulnerability_feedback_dismissal_path, { .post(vulnerability.create_vulnerability_feedback_dismissal_path, {
vulnerability_feedback: { vulnerability_feedback: {
...@@ -170,6 +175,7 @@ export const dismissVulnerability = ( ...@@ -170,6 +175,7 @@ export const dismissVulnerability = (
.then(({ data }) => { .then(({ data }) => {
dispatch('closeDismissalCommentBox'); dispatch('closeDismissalCommentBox');
dispatch('receiveDismissVulnerabilitySuccess', { vulnerability, data }); dispatch('receiveDismissVulnerabilitySuccess', { vulnerability, data });
toast(toastMsg);
}) })
.catch(() => { .catch(() => {
dispatch('receiveDismissVulnerabilityError', { flashError }); dispatch('receiveDismissVulnerabilityError', { flashError });
...@@ -198,10 +204,20 @@ export const receiveDismissVulnerabilityError = ({ commit }, { flashError }) => ...@@ -198,10 +204,20 @@ export const receiveDismissVulnerabilityError = ({ commit }, { flashError }) =>
export const addDismissalComment = ({ dispatch }, { vulnerability, comment }) => { export const addDismissalComment = ({ dispatch }, { vulnerability, comment }) => {
dispatch('requestAddDismissalComment'); dispatch('requestAddDismissalComment');
const { dismissal_feedback } = vulnerability; const { dismissal_feedback } = vulnerability;
const url = `${vulnerability.create_vulnerability_feedback_dismissal_path}/${dismissal_feedback.id}`; const url = `${vulnerability.create_vulnerability_feedback_dismissal_path}/${dismissal_feedback.id}`;
const editingDismissalContent =
dismissal_feedback.comment_details && dismissal_feedback.comment_details.comment;
const toastMsg = editingDismissalContent
? sprintf(s__("Security Reports|Comment edited on '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
})
: sprintf(s__("Security Reports|Comment added to '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
});
axios axios
.patch(url, { .patch(url, {
project_id: dismissal_feedback.project_id, project_id: dismissal_feedback.project_id,
...@@ -211,6 +227,7 @@ export const addDismissalComment = ({ dispatch }, { vulnerability, comment }) => ...@@ -211,6 +227,7 @@ export const addDismissalComment = ({ dispatch }, { vulnerability, comment }) =>
.then(({ data }) => { .then(({ data }) => {
dispatch('closeDismissalCommentBox'); dispatch('closeDismissalCommentBox');
dispatch('receiveAddDismissalCommentSuccess', { vulnerability, data }); dispatch('receiveAddDismissalCommentSuccess', { vulnerability, data });
toast(toastMsg);
}) })
.catch(() => { .catch(() => {
dispatch('receiveAddDismissalCommentError'); dispatch('receiveAddDismissalCommentError');
...@@ -222,6 +239,9 @@ export const deleteDismissalComment = ({ dispatch }, { vulnerability }) => { ...@@ -222,6 +239,9 @@ export const deleteDismissalComment = ({ dispatch }, { vulnerability }) => {
const { dismissal_feedback } = vulnerability; const { dismissal_feedback } = vulnerability;
const url = `${vulnerability.create_vulnerability_feedback_dismissal_path}/${dismissal_feedback.id}`; const url = `${vulnerability.create_vulnerability_feedback_dismissal_path}/${dismissal_feedback.id}`;
const toastMsg = sprintf(s__("Security Reports|Comment deleted on '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
});
axios axios
.patch(url, { .patch(url, {
...@@ -232,6 +252,7 @@ export const deleteDismissalComment = ({ dispatch }, { vulnerability }) => { ...@@ -232,6 +252,7 @@ export const deleteDismissalComment = ({ dispatch }, { vulnerability }) => {
const { id } = vulnerability; const { id } = vulnerability;
dispatch('closeDismissalCommentBox'); dispatch('closeDismissalCommentBox');
dispatch('receiveDeleteDismissalCommentSuccess', { id, data }); dispatch('receiveDeleteDismissalCommentSuccess', { id, data });
toast(toastMsg);
}) })
.catch(() => { .catch(() => {
dispatch('receiveDeleteDismissalCommentError'); dispatch('receiveDeleteDismissalCommentError');
......
import $ from 'jquery'; import $ from 'jquery';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale'; import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import toast from '~/vue_shared/plugins/global_toast';
import * as types from './mutation_types'; import * as types from './mutation_types';
import downloadPatchHelper from './utils/download_patch_helper'; import downloadPatchHelper from './utils/download_patch_helper';
import { pollUntilComplete } from './utils'; import { pollUntilComplete } from './utils';
...@@ -290,6 +291,10 @@ export const receiveDismissVulnerabilityError = ({ commit }, error) => ...@@ -290,6 +291,10 @@ export const receiveDismissVulnerabilityError = ({ commit }, error) =>
export const dismissVulnerability = ({ state, dispatch }, comment) => { export const dismissVulnerability = ({ state, dispatch }, comment) => {
dispatch('requestDismissVulnerability'); dispatch('requestDismissVulnerability');
const toastMsg = sprintf(s__("Security Reports|Dismissed '%{vulnerabilityName}'"), {
vulnerabilityName: state.modal.vulnerability.name,
});
axios axios
.post(state.createVulnerabilityFeedbackDismissalPath, { .post(state.createVulnerabilityFeedbackDismissalPath, {
vulnerability_feedback: { vulnerability_feedback: {
...@@ -310,8 +315,8 @@ export const dismissVulnerability = ({ state, dispatch }, comment) => { ...@@ -310,8 +315,8 @@ export const dismissVulnerability = ({ state, dispatch }, comment) => {
dispatch('closeDismissalCommentBox'); dispatch('closeDismissalCommentBox');
dispatch('receiveDismissVulnerability', updatedIssue); dispatch('receiveDismissVulnerability', updatedIssue);
hideModal(); hideModal();
toast(toastMsg);
}) })
.catch(() => { .catch(() => {
dispatch( dispatch(
...@@ -328,6 +333,17 @@ export const addDismissalComment = ({ state, dispatch }, { comment }) => { ...@@ -328,6 +333,17 @@ export const addDismissalComment = ({ state, dispatch }, { comment }) => {
const { dismissalFeedback } = vulnerability; const { dismissalFeedback } = vulnerability;
const url = `${state.createVulnerabilityFeedbackDismissalPath}/${dismissalFeedback.id}`; const url = `${state.createVulnerabilityFeedbackDismissalPath}/${dismissalFeedback.id}`;
const editingDismissalContent =
dismissalFeedback.comment_details && dismissalFeedback.comment_details.comment;
const toastMsg = editingDismissalContent
? sprintf(s__("Security Reports|Comment edited on '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
})
: sprintf(s__("Security Reports|Comment added to '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
});
axios axios
.patch(url, { .patch(url, {
project_id: dismissalFeedback.project_id, project_id: dismissalFeedback.project_id,
...@@ -337,6 +353,7 @@ export const addDismissalComment = ({ state, dispatch }, { comment }) => { ...@@ -337,6 +353,7 @@ export const addDismissalComment = ({ state, dispatch }, { comment }) => {
.then(({ data }) => { .then(({ data }) => {
dispatch('closeDismissalCommentBox'); dispatch('closeDismissalCommentBox');
dispatch('receiveAddDismissalCommentSuccess', { data }); dispatch('receiveAddDismissalCommentSuccess', { data });
toast(toastMsg);
}) })
.catch(() => { .catch(() => {
dispatch( dispatch(
...@@ -352,6 +369,9 @@ export const deleteDismissalComment = ({ state, dispatch }) => { ...@@ -352,6 +369,9 @@ export const deleteDismissalComment = ({ state, dispatch }) => {
const { vulnerability } = state.modal; const { vulnerability } = state.modal;
const { dismissalFeedback } = vulnerability; const { dismissalFeedback } = vulnerability;
const url = `${state.createVulnerabilityFeedbackDismissalPath}/${dismissalFeedback.id}`; const url = `${state.createVulnerabilityFeedbackDismissalPath}/${dismissalFeedback.id}`;
const toastMsg = sprintf(s__("Security Reports|Comment deleted on '%{vulnerabilityName}'"), {
vulnerabilityName: vulnerability.name,
});
axios axios
.patch(url, { .patch(url, {
...@@ -361,6 +381,7 @@ export const deleteDismissalComment = ({ state, dispatch }) => { ...@@ -361,6 +381,7 @@ export const deleteDismissalComment = ({ state, dispatch }) => {
.then(({ data }) => { .then(({ data }) => {
dispatch('closeDismissalCommentBox'); dispatch('closeDismissalCommentBox');
dispatch('receiveDeleteDismissalCommentSuccess', { data }); dispatch('receiveDeleteDismissalCommentSuccess', { data });
toast(toastMsg);
}) })
.catch(() => { .catch(() => {
dispatch( dispatch(
......
---
title: Add browser notications to add/edit/delete vulnability dismissal reasons
merge_request: 15015
author:
type: changed
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import testAction from 'spec/helpers/vuex_action_helper'; import testAction from 'spec/helpers/vuex_action_helper';
...@@ -709,6 +710,31 @@ describe('vulnerability dismissal', () => { ...@@ -709,6 +710,31 @@ describe('vulnerability dismissal', () => {
done, done,
); );
}); });
it('should show the dismissal toast message', done => {
spyOn(Vue.toasted, 'show').and.callThrough();
const checkToastMessage = () => {
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
done();
};
testAction(
actions.dismissVulnerability,
{ vulnerability, comment },
{},
[],
[
{ type: 'requestDismissVulnerability' },
{ type: 'closeDismissalCommentBox' },
{
type: 'receiveDismissVulnerabilitySuccess',
payload: { data, vulnerability },
},
],
checkToastMessage,
);
});
}); });
describe('on error', () => { describe('on error', () => {
...@@ -811,9 +837,35 @@ describe('add vulnerability dismissal comment', () => { ...@@ -811,9 +837,35 @@ describe('add vulnerability dismissal comment', () => {
it('should dispatch the request and success actions', done => { it('should dispatch the request and success actions', done => {
const checkPassedData = () => { const checkPassedData = () => {
const { project_id, id } = vulnerability.dismissal_feedback; const { project_id, id } = vulnerability.dismissal_feedback;
const expected = { project_id, id, comment }; const expected = JSON.stringify({ project_id, id, comment });
expect(mock.history.patch[0].data).toBe(JSON.stringify(expected)); expect(mock.history.patch[0].data).toBe(expected);
done();
};
testAction(
actions.addDismissalComment,
{ vulnerability, comment },
{},
[],
[
{ type: 'requestAddDismissalComment' },
{ type: 'closeDismissalCommentBox' },
{ type: 'receiveAddDismissalCommentSuccess', payload: { data, vulnerability } },
],
checkPassedData,
);
});
it('should show the add dismissal toast message', done => {
spyOn(Vue.toasted, 'show').and.callThrough();
const checkPassedData = () => {
const { project_id, id } = vulnerability.dismissal_feedback;
const expected = JSON.stringify({ project_id, id, comment });
expect(mock.history.patch[0].data).toBe(expected);
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
done(); done();
}; };
...@@ -912,9 +964,38 @@ describe('add vulnerability dismissal comment', () => { ...@@ -912,9 +964,38 @@ describe('add vulnerability dismissal comment', () => {
it('should dispatch the request and success actions', done => { it('should dispatch the request and success actions', done => {
const checkPassedData = () => { const checkPassedData = () => {
const { project_id } = vulnerability.dismissal_feedback; const { project_id } = vulnerability.dismissal_feedback;
const expected = { project_id, comment }; const expected = JSON.stringify({ project_id, comment });
expect(mock.history.patch[0].data).toBe(expected);
done();
};
testAction(
actions.deleteDismissalComment,
{ vulnerability },
{},
[],
[
{ type: 'requestDeleteDismissalComment' },
{ type: 'closeDismissalCommentBox' },
{
type: 'receiveDeleteDismissalCommentSuccess',
payload: { data, id: vulnerability.id },
},
],
checkPassedData,
);
});
it('should show the delete dismissal comment toast message', done => {
spyOn(Vue.toasted, 'show').and.callThrough();
const checkPassedData = () => {
const { project_id } = vulnerability.dismissal_feedback;
const expected = JSON.stringify({ project_id, comment });
expect(mock.history.patch[0].data).toBe(JSON.stringify(expected)); expect(mock.history.patch[0].data).toBe(expected);
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
done(); done();
}; };
......
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import actions, { import actions, {
...@@ -987,6 +988,35 @@ describe('security reports actions', () => { ...@@ -987,6 +988,35 @@ describe('security reports actions', () => {
done, done,
); );
}); });
it('show dismiss vulnerability toast message', done => {
spyOn(Vue.toasted, 'show');
const checkToastMessage = () => {
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
done();
};
testAction(
dismissVulnerability,
payload,
mockedState,
[],
[
{
type: 'requestDismissVulnerability',
},
{
type: 'closeDismissalCommentBox',
},
{
type: 'receiveDismissVulnerability',
payload,
},
],
checkToastMessage,
);
});
}); });
it('with error should dispatch `receiveDismissVulnerabilityError`', done => { it('with error should dispatch `receiveDismissVulnerabilityError`', done => {
...@@ -1044,6 +1074,31 @@ describe('security reports actions', () => { ...@@ -1044,6 +1074,31 @@ describe('security reports actions', () => {
done, done,
); );
}); });
it('should show added dismissal comment toast message', done => {
spyOn(Vue.toasted, 'show').and.callThrough();
const checkToastMessage = () => {
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
done();
};
testAction(
addDismissalComment,
{ comment },
{ modal: { vulnerability } },
[],
[
{ type: 'requestAddDismissalComment' },
{ type: 'closeDismissalCommentBox' },
{
type: 'receiveAddDismissalCommentSuccess',
payload: { data },
},
],
checkToastMessage,
);
});
}); });
describe('on error', () => { describe('on error', () => {
...@@ -1146,6 +1201,31 @@ describe('security reports actions', () => { ...@@ -1146,6 +1201,31 @@ describe('security reports actions', () => {
done, done,
); );
}); });
it('should show deleted dismissal comment toast message', done => {
spyOn(Vue.toasted, 'show').and.callThrough();
const checkToastMessage = () => {
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
done();
};
testAction(
deleteDismissalComment,
{ comment },
{ modal: { vulnerability } },
[],
[
{ type: 'requestDeleteDismissalComment' },
{ type: 'closeDismissalCommentBox' },
{
type: 'receiveDeleteDismissalCommentSuccess',
payload: { data },
},
],
checkToastMessage,
);
});
}); });
describe('on error', () => { describe('on error', () => {
......
...@@ -13494,12 +13494,24 @@ msgstr "" ...@@ -13494,12 +13494,24 @@ msgstr ""
msgid "Security Dashboard|Issue Created" msgid "Security Dashboard|Issue Created"
msgstr "" msgstr ""
msgid "Security Reports|Comment added to '%{vulnerabilityName}'"
msgstr ""
msgid "Security Reports|Comment deleted on '%{vulnerabilityName}'"
msgstr ""
msgid "Security Reports|Comment edited on '%{vulnerabilityName}'"
msgstr ""
msgid "Security Reports|Create issue" msgid "Security Reports|Create issue"
msgstr "" msgstr ""
msgid "Security Reports|Dismiss vulnerability" msgid "Security Reports|Dismiss vulnerability"
msgstr "" msgstr ""
msgid "Security Reports|Dismissed '%{vulnerabilityName}'"
msgstr ""
msgid "Security Reports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed." msgid "Security Reports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed."
msgstr "" msgstr ""
......
import toast from '~/vue_shared/plugins/global_toast';
import Vue from 'vue';
describe('Global toast', () => {
let spyFunc;
beforeEach(() => {
spyFunc = jest.spyOn(Vue.toasted, 'show').mockImplementation(() => {});
});
afterEach(() => {
spyFunc.mockRestore();
});
it('should pass all args to Vue toasted', () => {
const arg1 = 'TestMessage';
const arg2 = { className: 'foo' };
toast(arg1, arg2);
expect(Vue.toasted.show).toHaveBeenCalledTimes(1);
expect(Vue.toasted.show).toHaveBeenCalledWith(arg1, arg2);
});
});
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