Commit f862daaf authored by Fernando Arias's avatar Fernando Arias Committed by Fernando

Allow for Edit/Delete of existing vuln dismissal comment

First pass at edit vuln dismiss reason

Add comment 'deletion' mutations, actions, constants to all vue apps

Dismissal Delete buttons and Cancel

Working delete dismissal comment

Pre-populate text area with existing comment

Add missing action, mutation, and state

Fix edit/delete vuln on pipeline tab

Run Prettier

Run prettier and linter

Update pot file

Fix broken existing unit tests

Add mutation specs

Add action specs

Add dismissal comment footer specs

Prettier and linter

Refactor to use inferred editing state

Refactor and remove isEditingDismissal state

* Remove uneeded state property
* Update actions to not take in edit param
* Update specs
* Run linter and prettier

Refactor CLEAR to DELETE

Code cleanup

* Fix bad action rename in action specs
* Remove redunand id in header for patch request
* Remove shorthand notation for vue slot

Remove default param in action

Fix broken specs and add changelog

Refactor event item vue spec to use shallowMount

Add addition even item action button specs

Add additional dismissal note specs

Add tracking for editing dismissal comment

MR feedback changes
parent f850162e
......@@ -97,6 +97,7 @@ export default {
methods: {
...mapActions('vulnerabilities', [
'addDismissalComment',
'deleteDismissalComment',
'closeDismissalCommentBox',
'createIssue',
'createMergeRequest',
......@@ -108,6 +109,8 @@ export default {
'setVulnerabilitiesCountEndpoint',
'setVulnerabilitiesEndpoint',
'setVulnerabilitiesHistoryEndpoint',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
'undoDismiss',
'downloadPatch',
]),
......@@ -138,6 +141,10 @@ export default {
:can-create-merge-request="canCreateMergeRequest"
:can-dismiss-vulnerability="canDismissVulnerability"
@addDismissalComment="addDismissalComment({ vulnerability, comment: $event })"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
@deleteDismissalComment="deleteDismissalComment({ vulnerability })"
@closeDismissalCommentBox="closeDismissalCommentBox()"
@createMergeRequest="createMergeRequest({ vulnerability })"
@createNewIssue="createIssue({ vulnerability })"
......
......@@ -213,6 +213,27 @@ export const addDismissalComment = ({ dispatch }, { vulnerability, comment }) =>
});
};
export const deleteDismissalComment = ({ dispatch }, { vulnerability }) => {
dispatch('requestDeleteDismissalComment');
const { dismissal_feedback } = vulnerability;
const url = `${vulnerability.create_vulnerability_feedback_dismissal_path}/${dismissal_feedback.id}`;
axios
.patch(url, {
project_id: dismissal_feedback.project_id,
comment: '',
})
.then(({ data }) => {
const { id } = vulnerability;
dispatch('closeDismissalCommentBox');
dispatch('receiveDeleteDismissalCommentSuccess', { id, data });
})
.catch(() => {
dispatch('receiveDeleteDismissalCommentError');
});
};
export const requestAddDismissalComment = ({ commit }) => {
commit(types.REQUEST_ADD_DISMISSAL_COMMENT);
};
......@@ -226,6 +247,27 @@ export const receiveAddDismissalCommentError = ({ commit }) => {
commit(types.RECEIVE_ADD_DISMISSAL_COMMENT_ERROR);
};
export const requestDeleteDismissalComment = ({ commit }) => {
commit(types.REQUEST_DELETE_DISMISSAL_COMMENT);
};
export const receiveDeleteDismissalCommentSuccess = ({ commit }, payload) => {
commit(types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS, payload);
hideModal();
};
export const receiveDeleteDismissalCommentError = ({ commit }) => {
commit(types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR);
};
export const showDismissalDeleteButtons = ({ commit }) => {
commit(types.SHOW_DISMISSAL_DELETE_BUTTONS);
};
export const hideDismissalDeleteButtons = ({ commit }) => {
commit(types.HIDE_DISMISSAL_DELETE_BUTTONS);
};
export const undoDismiss = ({ dispatch }, { vulnerability, flashError }) => {
const { destroy_vulnerability_feedback_dismissal_path } = vulnerability.dismissal_feedback;
......
......@@ -29,10 +29,17 @@ export const REQUEST_ADD_DISMISSAL_COMMENT = 'REQUEST_ADD_DISMISSAL_COMMENT';
export const RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS = 'RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS';
export const RECEIVE_ADD_DISMISSAL_COMMENT_ERROR = 'RECEIVE_ADD_DISMISSAL_COMMENT_ERROR';
export const REQUEST_DELETE_DISMISSAL_COMMENT = 'REQUEST_DELETE_DISMISSAL_COMMENT';
export const RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS = 'REQUEST_DELETE_DISMISSAL_COMMENT_SUCCESS';
export const RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR = 'RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR';
export const REQUEST_REVERT_DISMISSAL = 'REQUEST_REVERT_DISMISSAL';
export const RECEIVE_REVERT_DISMISSAL_SUCCESS = 'RECEIVE_REVERT_DISMISSAL_SUCCESS';
export const RECEIVE_REVERT_DISMISSAL_ERROR = 'RECEIVE_REVERT_DISMISSAL_ERROR';
export const SHOW_DISMISSAL_DELETE_BUTTONS = 'SHOW_DISMISSAL_DELETE_BUTTONS';
export const HIDE_DISMISSAL_DELETE_BUTTONS = 'HIDE_DISMISSAL_DELETE_BUTTONS';
export const REQUEST_CREATE_MERGE_REQUEST = 'REQUEST_CREATE_MERGE_REQUEST';
export const RECEIVE_CREATE_MERGE_REQUEST_SUCCESS = 'RECEIVE_CREATE_MERGE_REQUEST_SUCCESS';
export const RECEIVE_CREATE_MERGE_REQUEST_ERROR = 'RECEIVE_CREATE_MERGE_REQUEST_ERROR';
......
......@@ -186,19 +186,37 @@ export default {
},
[types.RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS](state, payload) {
const vulnerability = state.vulnerabilities.find(vuln => vuln.id === payload.id);
if (!vulnerability) {
return;
if (vulnerability) {
vulnerability.dismissal_feedback = payload.data;
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal.vulnerability, 'isDismissed', true);
}
vulnerability.dismissal_feedback = payload.data;
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal.vulnerability, 'isDismissed', true);
},
[types.RECEIVE_ADD_DISMISSAL_COMMENT_ERROR](state) {
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal, 'error', s__('Security Reports|There was an error adding the comment.'));
},
[types.REQUEST_DELETE_DISMISSAL_COMMENT](state) {
state.isDismissingVulnerability = true;
Vue.set(state.modal, 'isDismissingVulnerability', true);
Vue.set(state.modal, 'error', null);
},
[types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS](state, payload) {
const vulnerability = state.vulnerabilities.find(vuln => vuln.id === payload.id);
if (vulnerability) {
vulnerability.dismissal_feedback = payload.data;
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal.vulnerability, 'isDismissed', true);
}
},
[types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR](state) {
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal, 'error', s__('Security Reports|There was an error deleting the comment.'));
},
[types.REQUEST_REVERT_DISMISSAL](state) {
state.isDismissingVulnerability = true;
Vue.set(state.modal, 'isDismissingVulnerability', true);
......@@ -220,6 +238,12 @@ export default {
s__('Security Reports|There was an error reverting the dismissal.'),
);
},
[types.SHOW_DISMISSAL_DELETE_BUTTONS](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', true);
},
[types.HIDE_DISMISSAL_DELETE_BUTTONS](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', false);
},
[types.REQUEST_CREATE_MERGE_REQUEST](state) {
state.isCreatingMergeRequest = true;
Vue.set(state.modal, 'isCreatingMergeRequest', true);
......@@ -242,6 +266,8 @@ export default {
Vue.set(state.modal, 'isCommentingOnDismissal', true);
},
[types.CLOSE_DISMISSAL_COMMENT_BOX](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', false);
Vue.set(state.modal, 'isCommentingOnDismissal', false);
Vue.set(state.modal, 'isShowingDeleteButtons', false);
},
};
......@@ -40,8 +40,8 @@ export default () => ({
isCreatingMergeRequest: false,
isDismissingVulnerability: false,
isCommentingOnDismissal: false,
isShowingDeleteButtons: false,
},
isCreatingIssue: false,
isCreatingMergeRequest: false,
isDismissingVulnerability: false,
});
......@@ -24,6 +24,11 @@ export default {
type: Boolean,
required: true,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
buttonText() {
......@@ -48,7 +53,7 @@ export default {
<div class="btn-group" role="group">
<loading-button
:loading="isDismissing"
:disabled="isDismissing"
:disabled="isDismissing || disabled"
:label="buttonText"
container-class="js-dismiss-btn btn btn-close"
@click="handleDismissClick"
......@@ -57,6 +62,7 @@ export default {
v-if="!isDismissed"
v-gl-tooltip.hover
v-gl-tooltip.focus
:disabled="disabled"
:title="s__('vulnerability|Add comment & dismiss')"
variant="close"
class="js-dismiss-with-comment "
......
......@@ -21,6 +21,11 @@ export default {
required: false,
default: '',
},
dismissalComment: {
type: String,
required: false,
default: '',
},
errorMessage: {
type: String,
required: false,
......@@ -44,7 +49,8 @@ export default {
},
},
mounted() {
this.$emit('input', '');
this.$emit('input', this.dismissalComment);
this.$emit('clearError');
this.$refs.dismissalComment.$el.focus();
},
......
......@@ -21,6 +21,11 @@ export default {
required: false,
default: '',
},
dismissalComment: {
type: String,
required: false,
default: '',
},
errorMessage: {
type: String,
required: false,
......@@ -52,6 +57,7 @@ export default {
<dismissal-comment-box
v-if="isActive"
v-model="localComment"
:dismissal-comment="dismissalComment"
:error-message="errorMessage"
:placeholder="$options.PLACEHOLDER"
@submit="$emit('submit')"
......
......@@ -21,12 +21,21 @@ export default {
required: false,
default: false,
},
isEditingExistingFeedback: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
submitLabel() {
return this.isDismissed
? s__('vulnerability|Add comment')
: s__('vulnerability|Add comment & dismiss');
if (this.isEditingExistingFeedback) {
return s__('vulnerability|Save comment');
}
if (this.isDismissed) {
return s__('vulnerability|Add comment');
}
return s__('vulnerability|Add comment & dismiss');
},
},
methods: {
......@@ -35,7 +44,12 @@ export default {
this.$emit('addCommentAndDismiss');
},
addDismissalComment() {
Tracking.event(document.body.dataset.page, 'click_add_comment');
if (this.isEditingExistingFeedback) {
Tracking.event(document.body.dataset.page, 'click_edit_comment');
} else {
Tracking.event(document.body.dataset.page, 'click_add_comment');
}
this.$emit('addDismissalComment');
},
handleSubmit() {
......
......@@ -2,10 +2,14 @@
import _ from 'underscore';
import { __, sprintf } from '~/locale';
import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue';
import { GlButton } from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
export default {
components: {
EventItem,
GlButton,
LoadingButton,
},
props: {
feedback: {
......@@ -17,6 +21,26 @@ export default {
required: false,
default: () => ({}),
},
isCommentingOnDismissal: {
type: Boolean,
required: false,
default: false,
},
isShowingDeleteButtons: {
type: Boolean,
required: false,
default: false,
},
showDismissalCommentActions: {
type: Boolean,
required: false,
default: false,
},
isDismissingVulnerability: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
eventText() {
......@@ -49,6 +73,20 @@ export default {
commentDetails() {
return this.feedback.comment_details;
},
vulnDismissalActionButtons() {
return [
{
iconName: 'pencil',
emit: 'editVulnerabilityDismissalComment',
title: __('Edit Comment'),
},
{
iconName: 'remove',
emit: 'showDismissalDeleteButtons',
title: __('Delete Comment'),
},
];
},
},
};
</script>
......@@ -63,15 +101,36 @@ export default {
>
<div v-html="eventText"></div>
</event-item>
<template v-if="commentDetails">
<template v-if="commentDetails && !isCommentingOnDismissal">
<hr class="my-3" />
<event-item
:action-buttons="vulnDismissalActionButtons"
:author="commentDetails.comment_author"
:created-at="commentDetails.comment_timestamp"
:show-right-slot="isShowingDeleteButtons"
:show-action-buttons="showDismissalCommentActions"
icon-name="comment"
icon-style="ci-status-icon-pending"
@editVulnerabilityDismissalComment="$emit('editVulnerabilityDismissalComment')"
@showDismissalDeleteButtons="$emit('showDismissalDeleteButtons')"
@hideDismissalDeleteButtons="$emit('hideDismissalDeleteButtons')"
@deleteDismissalComment="$emit('deleteDismissalComment')"
>
{{ commentDetails.comment }}
<template v-slot:right-content>
<div class="d-flex flex-grow-1 align-self-start flex-row-reverse">
<loading-button
:label="__('Delete comment')"
container-class="btn btn-remove"
@click="$emit('deleteDismissalComment')"
/>
<gl-button class="mr-2" @click="$emit('hideDismissalDeleteButtons')">
{{ __('Cancel') }}
</gl-button>
</div>
</template>
</event-item>
</template>
</div>
......
<script>
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
......@@ -7,6 +8,10 @@ export default {
components: {
Icon,
TimeAgoTooltip,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
author: {
......@@ -28,6 +33,21 @@ export default {
required: false,
default: 'ci-status-icon-success',
},
actionButtons: {
type: Array,
required: false,
default: () => [],
},
showRightSlot: {
type: Boolean,
required: false,
default: false,
},
showActionButtons: {
type: Boolean,
required: false,
default: true,
},
},
};
</script>
......@@ -58,5 +78,24 @@ export default {
</div>
<slot></slot>
</div>
<slot v-if="showRightSlot" name="right-content"></slot>
<div v-else class="d-flex flex-grow-1 align-self-start flex-row-reverse">
<div v-if="showActionButtons" class="action-buttons">
<gl-button
v-for="button in actionButtons"
:key="button.title"
ref="button"
v-gl-tooltip
class="px-1"
variant="transparent"
:title="button.title"
@click="$emit(button.emit)"
>
<icon :name="button.iconName" css-classes="link-highlight" />
</gl-button>
</div>
</div>
</div>
</template>
......@@ -115,6 +115,9 @@ export default {
(this.vulnerability.dismissal_feedback || this.vulnerability.dismissalFeedback)
);
},
isEditingExistingFeedback() {
return this.dismissalFeedback && this.modal.isCommentingOnDismissal;
},
valuedFields() {
const { data } = this.modal;
const result = {};
......@@ -225,10 +228,29 @@ export default {
<div v-if="dismissalFeedback || modal.isCommentingOnDismissal" class="card my-4">
<div class="card-body">
<dismissal-note :feedback="dismissalFeedbackObject" :project="project" />
<dismissal-note
:feedback="dismissalFeedbackObject"
:is-commenting-on-dismissal="modal.isCommentingOnDismissal"
:is-showing-delete-buttons="modal.isShowingDeleteButtons"
:project="project"
:show-dismissal-comment-actions="
!dismissalFeedback || !dismissalFeedback.comment_details || !isEditingExistingFeedback
"
@editVulnerabilityDismissalComment="$emit('editVulnerabilityDismissalComment')"
@showDismissalDeleteButtons="$emit('showDismissalDeleteButtons')"
@hideDismissalDeleteButtons="$emit('hideDismissalDeleteButtons')"
@deleteDismissalComment="$emit('deleteDismissalComment')"
/>
<dismissal-comment-box-toggle
v-if="!dismissalFeedback || !dismissalFeedback.comment_details"
v-if="
!dismissalFeedback || !dismissalFeedback.comment_details || isEditingExistingFeedback
"
v-model="localDismissalComment"
:dismissal-comment="
dismissalFeedback &&
dismissalFeedback.comment_details &&
dismissalFeedback.comment_details.comment
"
:is-active="modal.isCommentingOnDismissal"
:error-message="dismissalCommentErrorMessage"
@openDismissalCommentBox="$emit('openDismissalCommentBox')"
......@@ -244,6 +266,7 @@ export default {
<dismissal-comment-modal-footer
v-if="modal.isCommentingOnDismissal"
:is-dismissed="vulnerability.isDismissed"
:is-editing-existing-feedback="isEditingExistingFeedback"
@addCommentAndDismiss="addCommentAndDismiss"
@addDismissalComment="addDismissalComment"
@cancel="$emit('closeDismissalCommentBox')"
......@@ -252,6 +275,7 @@ export default {
v-else-if="shouldRenderFooterSection"
:modal="modal"
:vulnerability="vulnerability"
:disabled="modal.isShowingDeleteButtons"
:can-create-issue="Boolean(!vulnerability.hasIssue && canCreateIssue)"
:can-create-merge-request="Boolean(!vulnerability.hasMergeRequest && remediation)"
:can-download-patch="canDownloadPatch"
......
......@@ -43,6 +43,11 @@ export default {
required: false,
default: false,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
actionButtons() {
......@@ -85,7 +90,7 @@ export default {
<template>
<div>
<gl-button data-dismiss="modal">
<gl-button data-dismiss="modal" :disabled="disabled">
{{ __('Cancel') }}
</gl-button>
......@@ -93,6 +98,7 @@ export default {
v-if="canDismissVulnerability"
:is-dismissing="modal.isDismissingVulnerability"
:is-dismissed="isDismissed"
:disabled="disabled"
@dismissVulnerability="$emit('dismissVulnerability')"
@openDismissalCommentBox="$emit('openDismissalCommentBox')"
@revertDismissVulnerability="$emit('revertDismissVulnerability')"
......@@ -103,6 +109,7 @@ export default {
:buttons="actionButtons"
class="js-split-button"
data-qa-selector="resolve_split_button"
:disabled="disabled"
@createMergeRequest="$emit('createMergeRequest')"
@createNewIssue="$emit('createNewIssue')"
@downloadPatch="$emit('downloadPatch')"
......@@ -111,7 +118,7 @@ export default {
<loading-button
v-else-if="actionButtons.length > 0"
:loading="actionButtons[0].isLoading"
:disabled="actionButtons[0].isLoading"
:disabled="actionButtons[0].isLoading || disabled"
:label="actionButtons[0].name"
container-class="btn btn-success btn-inverted"
class="js-action-button"
......
......@@ -13,6 +13,11 @@ export default {
type: Array,
required: true,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
},
data: () => ({
selectedButton: {},
......@@ -34,6 +39,7 @@ export default {
<template>
<gl-dropdown
v-if="selectedButton"
:disabled="disabled"
no-caret
right
split
......
......@@ -254,6 +254,9 @@ export default {
'closeDismissalCommentBox',
'downloadPatch',
'addDismissalComment',
'deleteDismissalComment',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
]),
...mapActions('sast', {
setSastHeadPath: 'setHeadPath',
......@@ -364,9 +367,13 @@ export default {
@createNewIssue="createNewIssue"
@dismissVulnerability="dismissVulnerability"
@openDismissalCommentBox="openDismissalCommentBox()"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@revertDismissVulnerability="revertDismissVulnerability"
@downloadPatch="downloadPatch"
@addDismissalComment="addDismissalComment({ comment: $event })"
@deleteDismissalComment="deleteDismissalComment"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
/>
</div>
</report-section>
......
......@@ -236,6 +236,9 @@ export default {
'closeDismissalCommentBox',
'downloadPatch',
'addDismissalComment',
'deleteDismissalComment',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
]),
...mapActions('sast', {
setSastHeadPath: 'setHeadPath',
......@@ -331,6 +334,10 @@ export default {
@revertDismissVulnerability="revertDismissVulnerability"
@downloadPatch="downloadPatch"
@addDismissalComment="addDismissalComment({ comment: $event })"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@deleteDismissalComment="deleteDismissalComment"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
/>
</div>
</template>
......@@ -254,6 +254,43 @@ export const addDismissalComment = ({ state, dispatch }, { comment }) => {
});
};
export const deleteDismissalComment = ({ state, dispatch }) => {
dispatch('requestDeleteDismissalComment');
const { vulnerability } = state.modal;
const { dismissalFeedback } = vulnerability;
const url = `${state.createVulnerabilityFeedbackDismissalPath}/${dismissalFeedback.id}`;
axios
.patch(url, {
project_id: dismissalFeedback.project_id,
comment: '',
})
.then(({ data }) => {
dispatch('closeDismissalCommentBox');
dispatch('receiveDeleteDismissalCommentSuccess', { data });
})
.catch(() => {
dispatch(
'receiveDeleteDismissalCommentError',
s__('Security Reports|There was an error deleting the comment.'),
);
});
};
export const requestDeleteDismissalComment = ({ commit }) => {
commit(types.REQUEST_DELETE_DISMISSAL_COMMENT);
};
export const receiveDeleteDismissalCommentSuccess = ({ commit }, payload) => {
commit(types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS, payload);
hideModal();
};
export const receiveDeleteDismissalCommentError = ({ commit }, error) => {
commit(types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR, error);
};
export const requestAddDismissalComment = ({ commit }) => {
commit(types.REQUEST_ADD_DISMISSAL_COMMENT);
};
......@@ -293,6 +330,14 @@ export const revertDismissVulnerability = ({ state, dispatch }) => {
);
};
export const showDismissalDeleteButtons = ({ commit }) => {
commit(types.SHOW_DISMISSAL_DELETE_BUTTONS);
};
export const hideDismissalDeleteButtons = ({ commit }) => {
commit(types.HIDE_DISMISSAL_DELETE_BUTTONS);
};
export const requestCreateIssue = ({ commit }) => commit(types.REQUEST_CREATE_ISSUE);
export const receiveCreateIssue = ({ commit }) => commit(types.RECEIVE_CREATE_ISSUE_SUCCESS);
export const receiveCreateIssueError = ({ commit }, error) =>
......
......@@ -43,6 +43,10 @@ export const REQUEST_ADD_DISMISSAL_COMMENT = 'REQUEST_ADD_DISMISSAL_COMMENT';
export const RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS = 'RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS';
export const RECEIVE_ADD_DISMISSAL_COMMENT_ERROR = 'RECEIVE_ADD_DISMISSAL_COMMENT_ERROR';
export const REQUEST_DELETE_DISMISSAL_COMMENT = 'REQUEST_DELETE_DISMISSAL_COMMENT';
export const RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS = 'REQUEST_DELETE_DISMISSAL_COMMENT_SUCCESS';
export const RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR = 'RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR';
export const REQUEST_CREATE_ISSUE = 'CREATE_DISMISS_VULNERABILITY';
export const RECEIVE_CREATE_ISSUE_SUCCESS = 'CREATE_DISMISS_VULNERABILITY_SUCCESS';
export const RECEIVE_CREATE_ISSUE_ERROR = 'CREATE_DISMISS_VULNERABILITY_ERROR';
......@@ -51,6 +55,9 @@ export const REQUEST_CREATE_MERGE_REQUEST = 'REQUEST_CREATE_MERGE_REQUEST';
export const RECEIVE_CREATE_MERGE_REQUEST_SUCCESS = 'RECEIVE_CREATE_MERGE_REQUEST_SUCCESS';
export const RECEIVE_CREATE_MERGE_REQUEST_ERROR = 'RECEIVE_CREATE_MERGE_REQUEST_ERROR';
export const SHOW_DISMISSAL_DELETE_BUTTONS = 'SHOW_DISMISSAL_DELETE_BUTTONS';
export const HIDE_DISMISSAL_DELETE_BUTTONS = 'HIDE_DISMISSAL_DELETE_BUTTONS';
export const UPDATE_DEPENDENCY_SCANNING_ISSUE = 'UPDATE_DEPENDENCY_SCANNING_ISSUE';
export const UPDATE_CONTAINER_SCANNING_ISSUE = 'UPDATE_CONTAINER_SCANNING_ISSUE';
export const UPDATE_DAST_ISSUE = 'UPDATE_DAST_ISSUE';
......
......@@ -282,7 +282,28 @@ export default {
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal, 'error', error);
},
[types.REQUEST_DELETE_DISMISSAL_COMMENT](state) {
state.isDismissingVulnerability = true;
Vue.set(state.modal, 'isDismissingVulnerability', true);
Vue.set(state.modal, 'error', null);
},
[types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS](state, payload) {
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal.vulnerability, 'isDismissed', true);
Vue.set(state.modal.vulnerability, 'dismissalFeedback', payload.data);
},
[types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR](state, error) {
state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal, 'error', error);
},
[types.SHOW_DISMISSAL_DELETE_BUTTONS](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', true);
},
[types.HIDE_DISMISSAL_DELETE_BUTTONS](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', false);
},
[types.UPDATE_DEPENDENCY_SCANNING_ISSUE](state, issue) {
// Find issue in the correct list and update it
......@@ -367,6 +388,7 @@ export default {
Vue.set(state.modal, 'isCommentingOnDismissal', true);
},
[types.CLOSE_DISMISSAL_COMMENT_BOX](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', false);
Vue.set(state.modal, 'isCommentingOnDismissal', false);
},
};
......@@ -127,7 +127,8 @@ export default () => ({
isCreatingNewIssue: false,
isDismissingVulnerability: false,
isShowingDeleteButtons: false,
isCommentingOnDismissal: false,
error: null,
},
});
---
title: Edit delete vuln dismissal message
merge_request: 14770
author:
type: added
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Event Item with action buttons renders the action buttons 1`] = `
VueWrapper {
"_emitted": Object {
"hook:mounted": Array [
Array [],
],
},
"_emittedByOrder": Array [
Object {
"args": Array [],
"name": "hook:mounted",
},
],
"isFunctionalComponent": undefined,
}
`;
......@@ -46,22 +46,45 @@ describe('DismissalCommentModalFooter', () => {
});
describe('with an already dismissed vulnerability', () => {
beforeEach(() => {
const propsData = {
isDismissed: true,
};
wrapper = mount(component, { propsData });
});
describe('and adding a comment', () => {
beforeEach(() => {
const propsData = {
isDismissed: true,
};
wrapper = mount(component, { propsData });
});
it('should render the "Add comment and dismiss" button', () => {
expect(wrapper.find(LoadingButton).text()).toBe('Add comment');
it('should render the "Add comment and dismiss" button', () => {
expect(wrapper.find(LoadingButton).text()).toBe('Add comment');
});
it('should emit the "addCommentAndDismiss" event when clicked', () => {
wrapper.find(LoadingButton).trigger('click');
expect(wrapper.emitted().addDismissalComment).toBeTruthy();
expect(Tracking.event).toHaveBeenCalledWith('_track_category_', 'click_add_comment');
});
});
it('should emit the "addCommentAndDismiss" event when clicked', () => {
wrapper.find(LoadingButton).trigger('click');
describe('and editing a comment', () => {
beforeEach(() => {
const propsData = {
isDismissed: true,
isEditingExistingFeedback: true,
};
wrapper = mount(component, { propsData });
});
it('should render the "Save comment" button', () => {
expect(wrapper.find(LoadingButton).text()).toBe('Save comment');
});
it('should emit the "addCommentAndDismiss" event when clicked', () => {
wrapper.find(LoadingButton).trigger('click');
expect(wrapper.emitted().addDismissalComment).toBeTruthy();
expect(Tracking.event).toHaveBeenCalledWith('_track_category_', 'click_add_comment');
expect(wrapper.emitted().addDismissalComment).toBeTruthy();
expect(Tracking.event).toHaveBeenCalledWith('_track_category_', 'click_edit_comment');
});
});
});
});
import { shallowMount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import component from 'ee/vue_shared/security_reports/components/dismissal_note.vue';
import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue';
......@@ -114,29 +114,53 @@ describe('dismissal note', () => {
};
let commentItem;
beforeEach(() => {
wrapper = shallowMount(component, {
propsData: {
feedback: {
...feedback,
comment_details: commentDetails,
describe('without confirm deletion buttons', () => {
beforeEach(() => {
wrapper = shallowMount(component, {
propsData: {
feedback: {
...feedback,
comment_details: commentDetails,
},
project,
},
project,
},
});
commentItem = wrapper.findAll(EventItem).at(1);
});
commentItem = wrapper.findAll(EventItem).at(1);
});
it('should render the comment', () => {
expect(commentItem.text()).toBe(commentDetails.comment);
});
it('should render the comment', () => {
expect(commentItem.text()).toBe(commentDetails.comment);
});
it('should render the comment author', () => {
expect(commentItem.props().author).toBe(commentDetails.comment_author);
it('should render the comment author', () => {
expect(commentItem.props().author).toBe(commentDetails.comment_author);
});
it('should render the comment timestamp', () => {
expect(commentItem.props().createdAt).toBe(commentDetails.comment_timestamp);
});
});
it('should render the comment timestamp', () => {
expect(commentItem.props().createdAt).toBe(commentDetails.comment_timestamp);
describe('with confirm deletion buttons', () => {
beforeEach(() => {
wrapper = mount(component, {
propsData: {
feedback: {
...feedback,
comment_details: commentDetails,
},
project,
isShowingDeleteButtons: true,
},
});
commentItem = wrapper.findAll(EventItem).at(1);
});
it('should render deletion buttons slot', () => {
const buttons = commentItem.findAll('button');
expect(buttons.at(1).text()).toEqual('Cancel');
expect(buttons.at(0).text()).toEqual('Delete comment');
});
});
});
});
import Vue from 'vue';
import component from 'ee/vue_shared/security_reports/components/event_item.vue';
import mountComponent from 'helpers/vue_mount_component_helper';
import Component from 'ee/vue_shared/security_reports/components/event_item.vue';
import { shallowMount, mount } from '@vue/test-utils';
describe('Event Item', () => {
const Component = Vue.extend(component);
const props = {
author: {
name: 'Tanuki',
username: 'gitlab',
},
};
let vm;
afterEach(() => {
vm.$destroy();
});
describe('initial state', () => {
let wrapper;
beforeEach(() => {
vm = mountComponent(Component, props);
});
const propsData = {
author: {
name: 'Tanuki',
username: 'gitlab',
},
};
it('uses the author name', () => {
expect(vm.$el.querySelector('.js-author').textContent).toContain(props.author.name);
});
afterEach(() => {
wrapper.destroy();
});
it('uses the author username', () => {
expect(vm.$el.querySelector('.js-author').textContent).toContain(`@${props.author.username}`);
});
beforeEach(() => {
wrapper = shallowMount(Component, { propsData });
});
it('uses the author name', () => {
expect(wrapper.find('.js-author').text()).toContain(propsData.author.name);
});
it('uses the author username', () => {
expect(wrapper.find('.js-author').text()).toContain(`@${propsData.author.username}`);
});
it('uses the fallback icon', () => {
expect(wrapper.props().iconName).toBe('plus');
});
it('uses the fallback icon', () => {
expect(vm.iconName).toBe('plus');
it('uses the fallback icon class', () => {
expect(wrapper.props().iconStyle).toBe('ci-status-icon-success');
});
it('renders the action buttons tontainer', () => {
expect(wrapper.find('.action-buttons')).toExist();
});
});
describe('with action buttons', () => {
let wrapper;
const propsData = {
author: {
name: 'Tanuki',
username: 'gitlab',
},
actionButtons: [
{
iconName: 'pencil',
emit: 'fooEvent',
title: 'Foo Action',
},
{
iconName: 'remove',
emit: 'barEvent',
title: 'Bar Action',
},
],
};
afterEach(() => {
wrapper.destroy();
});
beforeEach(() => {
wrapper = mount(Component, { propsData });
});
it('renders the action buttons container', () => {
expect(wrapper.find('.action-buttons')).toExist();
});
it('renders the action buttons', () => {
expect(wrapper.findAll('.action-buttons > button').length).toBe(2);
expect(wrapper).toMatchSnapshot();
});
it('emits the button events when clicked', () => {
const buttons = wrapper.findAll('.action-buttons > button');
buttons.at(0).trigger('click');
buttons.at(1).trigger('click');
it('uses the fallback icon class', () => {
expect(vm.iconStyle).toBe('ci-status-icon-success');
expect(wrapper.emitted().fooEvent.length).toEqual(1);
expect(wrapper.emitted().barEvent.length).toEqual(1);
});
});
});
......@@ -525,6 +525,113 @@ describe('security reports mutations', () => {
});
});
describe(types.REQUEST_DELETE_DISMISSAL_COMMENT, () => {
beforeEach(() => {
mutations[types.REQUEST_DELETE_DISMISSAL_COMMENT](stateCopy);
});
it('should set isDismissingVulnerability to true', () => {
expect(stateCopy.isDismissingVulnerability).toBe(true);
});
it('should set isDismissingVulnerability in the modal data to true', () => {
expect(stateCopy.modal.isDismissingVulnerability).toBe(true);
});
it('should nullify the error state on the modal', () => {
expect(stateCopy.modal.error).toBeNull();
});
});
describe(types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS, () => {
let payload;
let vulnerability;
let data;
beforeEach(() => {
vulnerability = { id: 1 };
data = { name: 'dismissal feedback' };
payload = { id: vulnerability.id, data };
mutations[types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS](stateCopy, payload);
});
it('should set isDismissingVulnerability to false', () => {
expect(stateCopy.isDismissingVulnerability).toBe(false);
});
it('should set isDismissingVulnerability on the modal to false', () => {
expect(stateCopy.modal.isDismissingVulnerability).toBe(false);
});
it('shoulfd set isDissmissed on the modal vulnerability to be true', () => {
expect(stateCopy.modal.vulnerability.isDismissed).toBe(true);
});
});
describe(types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR, () => {
const error = 'There was an error deleting the comment.';
beforeEach(() => {
mutations[types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR](stateCopy, error);
});
it('should set isDismissingVulnerability to false', () => {
expect(stateCopy.isDismissingVulnerability).toBe(false);
});
it('should set isDismissingVulnerability in the modal data to false', () => {
expect(stateCopy.modal.isDismissingVulnerability).toBe(false);
});
it('should set the error state on the modal', () => {
expect(stateCopy.modal.error).toEqual(error);
});
});
describe(types.SHOW_DISMISSAL_DELETE_BUTTONS, () => {
beforeEach(() => {
mutations[types.SHOW_DISMISSAL_DELETE_BUTTONS](stateCopy);
});
it('should set isShowingDeleteButtonsto to true', () => {
expect(stateCopy.modal.isShowingDeleteButtons).toBe(true);
});
});
describe(types.HIDE_DISMISSAL_DELETE_BUTTONS, () => {
beforeEach(() => {
mutations[types.HIDE_DISMISSAL_DELETE_BUTTONS](stateCopy);
});
it('should set isShowingDeleteButtons to false', () => {
expect(stateCopy.modal.isShowingDeleteButtons).toBe(false);
});
});
describe('OPEN_DISMISSAL_COMMENT_BOX', () => {
beforeEach(() => {
mutations[types.OPEN_DISMISSAL_COMMENT_BOX](stateCopy);
});
it('should set isCommentingOnDismissal to true', () => {
expect(stateCopy.modal.isCommentingOnDismissal).toBe(true);
});
});
describe('CLOSE_DISMISSAL_COMMENT_BOX', () => {
beforeEach(() => {
mutations[types.CLOSE_DISMISSAL_COMMENT_BOX](stateCopy);
});
it('should set isCommentingOnDismissal to false', () => {
expect(stateCopy.modal.isCommentingOnDismissal).toBe(false);
});
it('should set isShowingDeleteButtons to false', () => {
expect(stateCopy.modal.isShowingDeleteButtons).toBe(false);
});
});
describe('REQUEST_CREATE_ISSUE', () => {
it('sets isCreatingNewIssue prop to true and resets error', () => {
mutations[types.REQUEST_CREATE_ISSUE](stateCopy);
......
......@@ -847,6 +847,157 @@ describe('add vulnerability dismissal comment', () => {
});
});
});
describe('deleteDismissalComment', () => {
const vulnerability = mockDataVulnerabilities[2];
const data = { vulnerability };
const url = `${vulnerability.create_vulnerability_feedback_dismissal_path}/${vulnerability.dismissal_feedback.id}`;
const comment = '';
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('on success', () => {
beforeEach(() => {
mock.onPatch(url).replyOnce(200, data);
});
it('should dispatch the request and success actions', done => {
const checkPassedData = () => {
const { project_id } = vulnerability.dismissal_feedback;
const expected = { project_id, comment };
expect(mock.history.patch[0].data).toBe(JSON.stringify(expected));
done();
};
testAction(
actions.deleteDismissalComment,
{ vulnerability },
{},
[],
[
{ type: 'requestDeleteDismissalComment' },
{ type: 'closeDismissalCommentBox' },
{
type: 'receiveDeleteDismissalCommentSuccess',
payload: { data, id: vulnerability.id },
},
],
checkPassedData,
);
});
});
describe('on error', () => {
beforeEach(() => {
mock.onPatch(url).replyOnce(404);
});
it('should dispatch the request and error actions', done => {
testAction(
actions.deleteDismissalComment,
{ vulnerability },
{},
[],
[
{ type: 'requestDeleteDismissalComment' },
{ type: 'receiveDeleteDismissalCommentError' },
],
done,
);
});
});
describe('receiveDeleteDismissalCommentSuccess', () => {
it('should commit the success mutation', done => {
const state = initialState;
testAction(
actions.receiveDeleteDismissalCommentSuccess,
{ data },
state,
[{ type: types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS, payload: { data } }],
[],
done,
);
});
});
describe('receiveDeleteDismissalCommentError', () => {
it('should commit the error mutation', done => {
const state = initialState;
testAction(
actions.receiveDeleteDismissalCommentError,
{},
state,
[{ type: types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR }],
[],
done,
);
});
});
describe('requestDeleteDismissalComment', () => {
it('should commit the request mutation', done => {
const state = initialState;
testAction(
actions.requestDeleteDismissalComment,
{},
state,
[{ type: types.REQUEST_DELETE_DISMISSAL_COMMENT }],
[],
done,
);
});
});
});
});
describe('showDismissalDeleteButtons', () => {
it('commits show dismissal delete buttons', done => {
const state = initialState;
testAction(
actions.showDismissalDeleteButtons,
null,
state,
[
{
type: types.SHOW_DISMISSAL_DELETE_BUTTONS,
},
],
[],
done,
);
});
});
describe('hideDismissalDeleteButtons', () => {
it('commits hide dismissal delete buttons', done => {
const state = initialState;
testAction(
actions.hideDismissalDeleteButtons,
null,
state,
[
{
type: types.HIDE_DISMISSAL_DELETE_BUTTONS,
},
],
[],
done,
);
});
});
describe('revert vulnerability dismissal', () => {
......@@ -1128,12 +1279,12 @@ describe('vulnerabilities history actions', () => {
});
describe('openDismissalCommentBox', () => {
it('should commit the open comment mutation', done => {
it('should commit the open comment mutation with a default payload', done => {
const state = initialState();
testAction(
actions.openDismissalCommentBox,
{},
undefined,
state,
[{ type: types.OPEN_DISMISSAL_COMMENT_BOX }],
[],
......
......@@ -542,7 +542,107 @@ describe('vulnerabilities module mutations', () => {
});
});
describe('REQUEST_DISMISSAL_COMMENT', () => {
describe('REQUEST_DELETE_DISMISSAL_COMMENT', () => {
let state;
beforeEach(() => {
state = createState();
mutations[types.REQUEST_DELETE_DISMISSAL_COMMENT](state);
});
it('should set isDismissingVulnerability to true', () => {
expect(state.isDismissingVulnerability).toBe(true);
});
it('should set isDismissingVulnerability in the modal data to true', () => {
expect(state.modal.isDismissingVulnerability).toBe(true);
});
it('should nullify the error state on the modal', () => {
expect(state.modal.error).toBeNull();
});
});
describe('RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS', () => {
let state;
let payload;
let vulnerability;
let data;
beforeEach(() => {
state = createState();
state.vulnerabilities = mockData;
[vulnerability] = mockData;
data = { name: '' };
payload = { id: vulnerability.id, data };
mutations[types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS](state, payload);
});
it('should set the dismissal feedback on the passed vulnerability to an empty string', () => {
expect(state.vulnerabilities[0].dismissal_feedback).toEqual({ name: '' });
});
it('should set isDismissingVulnerability to false', () => {
expect(state.isDismissingVulnerability).toBe(false);
});
it('should set isDismissingVulnerability on the modal to false', () => {
expect(state.modal.isDismissingVulnerability).toBe(false);
});
it('should set isDissmissed on the modal vulnerability to be true', () => {
expect(state.modal.vulnerability.isDismissed).toBe(true);
});
});
describe('RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR', () => {
let state;
beforeEach(() => {
state = createState();
mutations[types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR](state);
});
it('should set isDismissingVulnerability to false', () => {
expect(state.isDismissingVulnerability).toBe(false);
});
it('should set isDismissingVulnerability in the modal data to false', () => {
expect(state.modal.isDismissingVulnerability).toBe(false);
});
it('should set the error state on the modal', () => {
expect(state.modal.error).toEqual('There was an error deleting the comment.');
});
});
describe(types.SHOW_DISMISSAL_DELETE_BUTTONS, () => {
let state;
beforeEach(() => {
state = createState();
mutations[types.SHOW_DISMISSAL_DELETE_BUTTONS](state);
});
it('should set isShowingDeleteButtonsto to true', () => {
expect(state.modal.isShowingDeleteButtons).toBe(true);
});
});
describe(types.HIDE_DISMISSAL_DELETE_BUTTONS, () => {
let state;
beforeEach(() => {
state = createState();
mutations[types.HIDE_DISMISSAL_DELETE_BUTTONS](state);
});
it('should set isShowingDeleteButtons to false', () => {
expect(state.modal.isShowingDeleteButtons).toBe(false);
});
});
describe('REQUEST_ADD_DISMISSAL_COMMENT', () => {
let state;
beforeEach(() => {
......@@ -563,7 +663,7 @@ describe('vulnerabilities module mutations', () => {
});
});
describe('RECEIVE_DISMISSAL_COMMENT_SUCCESS', () => {
describe('RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS', () => {
let state;
let payload;
let vulnerability;
......@@ -595,7 +695,7 @@ describe('vulnerabilities module mutations', () => {
});
});
describe('RECEIVE_DISMISSAL_COMMENT_ERROR', () => {
describe('RECEIVE_ADD_DISMISSAL_COMMENT_ERROR', () => {
let state;
beforeEach(() => {
......@@ -689,20 +789,32 @@ describe('vulnerabilities module mutations', () => {
});
describe('OPEN_DISMISSAL_COMMENT_BOX', () => {
it('should set isCommentingOnDismissal to true', () => {
const state = createState();
let state;
beforeEach(() => {
state = createState();
mutations[types.OPEN_DISMISSAL_COMMENT_BOX](state);
});
it('should set isCommentingOnDismissal to true', () => {
expect(state.modal.isCommentingOnDismissal).toBe(true);
});
});
describe('CLOSE_DISMISSAL_COMMENT_BOX', () => {
it('should set isCommentingOnDismissal to false', () => {
const state = createState();
let state;
beforeEach(() => {
state = createState();
mutations[types.CLOSE_DISMISSAL_COMMENT_BOX](state);
});
it('should set isCommentingOnDismissal to false', () => {
expect(state.modal.isCommentingOnDismissal).toBe(false);
});
it('should set isShowingDeleteButtons to false', () => {
expect(state.modal.isShowingDeleteButtons).toBe(false);
});
});
});
......@@ -49,6 +49,12 @@ import actions, {
receiveAddDismissalCommentError,
receiveAddDismissalCommentSuccess,
requestAddDismissalComment,
deleteDismissalComment,
receiveDeleteDismissalCommentError,
receiveDeleteDismissalCommentSuccess,
requestDeleteDismissalComment,
showDismissalDeleteButtons,
hideDismissalDeleteButtons,
} from 'ee/vue_shared/security_reports/store/actions';
import * as types from 'ee/vue_shared/security_reports/store/mutation_types';
import state from 'ee/vue_shared/security_reports/store/state';
......@@ -1095,6 +1101,142 @@ describe('security reports actions', () => {
});
});
describe('deleteDismissalComment', () => {
const vulnerability = {
id: 0,
vulnerability_feedback_dismissal_path: 'foo',
dismissalFeedback: { id: 1 },
};
const data = { vulnerability };
const url = `${state.createVulnerabilityFeedbackDismissalPath}/${vulnerability.dismissalFeedback.id}`;
const comment = '';
describe('on success', () => {
beforeEach(() => {
mock.onPatch(url).replyOnce(200, data);
});
it('should dispatch the request and success actions', done => {
testAction(
deleteDismissalComment,
{ comment },
{ modal: { vulnerability } },
[],
[
{ type: 'requestDeleteDismissalComment' },
{ type: 'closeDismissalCommentBox' },
{
type: 'receiveDeleteDismissalCommentSuccess',
payload: { data },
},
],
done,
);
});
});
describe('on error', () => {
beforeEach(() => {
mock.onPatch(url).replyOnce(404);
});
it('should dispatch the request and error actions', done => {
testAction(
deleteDismissalComment,
{ comment },
{ modal: { vulnerability } },
[],
[
{ type: 'requestDeleteDismissalComment' },
{
type: 'receiveDeleteDismissalCommentError',
payload: 'There was an error deleting the comment.',
},
],
done,
);
});
});
describe('receiveDeleteDismissalCommentSuccess', () => {
it('should commit the success mutation', done => {
testAction(
receiveDeleteDismissalCommentSuccess,
{ data },
state,
[{ type: types.RECEIVE_DELETE_DISMISSAL_COMMENT_SUCCESS, payload: { data } }],
[],
done,
);
});
});
describe('receiveDeleteDismissalCommentError', () => {
it('should commit the error mutation', done => {
testAction(
receiveDeleteDismissalCommentError,
{},
state,
[
{
type: types.RECEIVE_DELETE_DISMISSAL_COMMENT_ERROR,
payload: {},
},
],
[],
done,
);
});
});
describe('requestDeleteDismissalComment', () => {
it('should commit the request mutation', done => {
testAction(
requestDeleteDismissalComment,
{},
state,
[{ type: types.REQUEST_DELETE_DISMISSAL_COMMENT }],
[],
done,
);
});
});
});
describe('showDismissalDeleteButtons', () => {
it('commits show dismissal delete buttons', done => {
testAction(
showDismissalDeleteButtons,
null,
mockedState,
[
{
type: types.SHOW_DISMISSAL_DELETE_BUTTONS,
},
],
[],
done,
);
});
});
describe('hideDismissalDeleteButtons', () => {
it('commits hide dismissal delete buttons', done => {
testAction(
hideDismissalDeleteButtons,
null,
mockedState,
[
{
type: types.HIDE_DISMISSAL_DELETE_BUTTONS,
},
],
[],
done,
);
});
});
describe('revertDismissVulnerability', () => {
describe('with success', () => {
let payload;
......
......@@ -4572,6 +4572,9 @@ msgstr ""
msgid "Delete"
msgstr ""
msgid "Delete Comment"
msgstr ""
msgid "Delete Package"
msgstr ""
......@@ -5155,6 +5158,9 @@ msgstr ""
msgid "Edit %{name}"
msgstr ""
msgid "Edit Comment"
msgstr ""
msgid "Edit Deploy Key"
msgstr ""
......@@ -13161,6 +13167,9 @@ msgstr ""
msgid "Security Reports|There was an error creating the merge request."
msgstr ""
msgid "Security Reports|There was an error deleting the comment."
msgstr ""
msgid "Security Reports|There was an error dismissing the vulnerability."
msgstr ""
......@@ -18749,6 +18758,9 @@ msgstr ""
msgid "vulnerability|Dismiss vulnerability"
msgstr ""
msgid "vulnerability|Save comment"
msgstr ""
msgid "vulnerability|Undo dismiss"
msgstr ""
......
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