Commit 17010a6e authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'edit-delete-vuln-dismissal-message' into 'master'

Edit delete vuln dismissal message

See merge request gitlab-org/gitlab-ee!14770
parents eebca4db f862daaf
...@@ -97,6 +97,7 @@ export default { ...@@ -97,6 +97,7 @@ export default {
methods: { methods: {
...mapActions('vulnerabilities', [ ...mapActions('vulnerabilities', [
'addDismissalComment', 'addDismissalComment',
'deleteDismissalComment',
'closeDismissalCommentBox', 'closeDismissalCommentBox',
'createIssue', 'createIssue',
'createMergeRequest', 'createMergeRequest',
...@@ -108,6 +109,8 @@ export default { ...@@ -108,6 +109,8 @@ export default {
'setVulnerabilitiesCountEndpoint', 'setVulnerabilitiesCountEndpoint',
'setVulnerabilitiesEndpoint', 'setVulnerabilitiesEndpoint',
'setVulnerabilitiesHistoryEndpoint', 'setVulnerabilitiesHistoryEndpoint',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
'undoDismiss', 'undoDismiss',
'downloadPatch', 'downloadPatch',
]), ]),
...@@ -138,6 +141,10 @@ export default { ...@@ -138,6 +141,10 @@ export default {
:can-create-merge-request="canCreateMergeRequest" :can-create-merge-request="canCreateMergeRequest"
:can-dismiss-vulnerability="canDismissVulnerability" :can-dismiss-vulnerability="canDismissVulnerability"
@addDismissalComment="addDismissalComment({ vulnerability, comment: $event })" @addDismissalComment="addDismissalComment({ vulnerability, comment: $event })"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
@deleteDismissalComment="deleteDismissalComment({ vulnerability })"
@closeDismissalCommentBox="closeDismissalCommentBox()" @closeDismissalCommentBox="closeDismissalCommentBox()"
@createMergeRequest="createMergeRequest({ vulnerability })" @createMergeRequest="createMergeRequest({ vulnerability })"
@createNewIssue="createIssue({ vulnerability })" @createNewIssue="createIssue({ vulnerability })"
......
...@@ -213,6 +213,27 @@ export const addDismissalComment = ({ dispatch }, { vulnerability, comment }) => ...@@ -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 }) => { export const requestAddDismissalComment = ({ commit }) => {
commit(types.REQUEST_ADD_DISMISSAL_COMMENT); commit(types.REQUEST_ADD_DISMISSAL_COMMENT);
}; };
...@@ -226,6 +247,27 @@ export const receiveAddDismissalCommentError = ({ commit }) => { ...@@ -226,6 +247,27 @@ export const receiveAddDismissalCommentError = ({ commit }) => {
commit(types.RECEIVE_ADD_DISMISSAL_COMMENT_ERROR); 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 }) => { export const undoDismiss = ({ dispatch }, { vulnerability, flashError }) => {
const { destroy_vulnerability_feedback_dismissal_path } = vulnerability.dismissal_feedback; const { destroy_vulnerability_feedback_dismissal_path } = vulnerability.dismissal_feedback;
......
...@@ -29,10 +29,17 @@ export const REQUEST_ADD_DISMISSAL_COMMENT = 'REQUEST_ADD_DISMISSAL_COMMENT'; ...@@ -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_SUCCESS = 'RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS';
export const RECEIVE_ADD_DISMISSAL_COMMENT_ERROR = 'RECEIVE_ADD_DISMISSAL_COMMENT_ERROR'; 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 REQUEST_REVERT_DISMISSAL = 'REQUEST_REVERT_DISMISSAL';
export const RECEIVE_REVERT_DISMISSAL_SUCCESS = 'RECEIVE_REVERT_DISMISSAL_SUCCESS'; export const RECEIVE_REVERT_DISMISSAL_SUCCESS = 'RECEIVE_REVERT_DISMISSAL_SUCCESS';
export const RECEIVE_REVERT_DISMISSAL_ERROR = 'RECEIVE_REVERT_DISMISSAL_ERROR'; 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 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_SUCCESS = 'RECEIVE_CREATE_MERGE_REQUEST_SUCCESS';
export const RECEIVE_CREATE_MERGE_REQUEST_ERROR = 'RECEIVE_CREATE_MERGE_REQUEST_ERROR'; export const RECEIVE_CREATE_MERGE_REQUEST_ERROR = 'RECEIVE_CREATE_MERGE_REQUEST_ERROR';
......
...@@ -186,19 +186,37 @@ export default { ...@@ -186,19 +186,37 @@ export default {
}, },
[types.RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS](state, payload) { [types.RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS](state, payload) {
const vulnerability = state.vulnerabilities.find(vuln => vuln.id === payload.id); const vulnerability = state.vulnerabilities.find(vuln => vuln.id === payload.id);
if (!vulnerability) { if (vulnerability) {
return;
}
vulnerability.dismissal_feedback = payload.data; vulnerability.dismissal_feedback = payload.data;
state.isDismissingVulnerability = false; state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false); Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal.vulnerability, 'isDismissed', true); Vue.set(state.modal.vulnerability, 'isDismissed', true);
}
}, },
[types.RECEIVE_ADD_DISMISSAL_COMMENT_ERROR](state) { [types.RECEIVE_ADD_DISMISSAL_COMMENT_ERROR](state) {
state.isDismissingVulnerability = false; state.isDismissingVulnerability = false;
Vue.set(state.modal, 'isDismissingVulnerability', false); Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal, 'error', s__('Security Reports|There was an error adding the comment.')); 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) { [types.REQUEST_REVERT_DISMISSAL](state) {
state.isDismissingVulnerability = true; state.isDismissingVulnerability = true;
Vue.set(state.modal, 'isDismissingVulnerability', true); Vue.set(state.modal, 'isDismissingVulnerability', true);
...@@ -220,6 +238,12 @@ export default { ...@@ -220,6 +238,12 @@ export default {
s__('Security Reports|There was an error reverting the dismissal.'), 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) { [types.REQUEST_CREATE_MERGE_REQUEST](state) {
state.isCreatingMergeRequest = true; state.isCreatingMergeRequest = true;
Vue.set(state.modal, 'isCreatingMergeRequest', true); Vue.set(state.modal, 'isCreatingMergeRequest', true);
...@@ -242,6 +266,8 @@ export default { ...@@ -242,6 +266,8 @@ export default {
Vue.set(state.modal, 'isCommentingOnDismissal', true); Vue.set(state.modal, 'isCommentingOnDismissal', true);
}, },
[types.CLOSE_DISMISSAL_COMMENT_BOX](state) { [types.CLOSE_DISMISSAL_COMMENT_BOX](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', false);
Vue.set(state.modal, 'isCommentingOnDismissal', false); Vue.set(state.modal, 'isCommentingOnDismissal', false);
Vue.set(state.modal, 'isShowingDeleteButtons', false);
}, },
}; };
...@@ -40,8 +40,8 @@ export default () => ({ ...@@ -40,8 +40,8 @@ export default () => ({
isCreatingMergeRequest: false, isCreatingMergeRequest: false,
isDismissingVulnerability: false, isDismissingVulnerability: false,
isCommentingOnDismissal: false, isCommentingOnDismissal: false,
isShowingDeleteButtons: false,
}, },
isCreatingIssue: false, isCreatingIssue: false,
isCreatingMergeRequest: false, isCreatingMergeRequest: false,
isDismissingVulnerability: false,
}); });
...@@ -24,6 +24,11 @@ export default { ...@@ -24,6 +24,11 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
disabled: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
buttonText() { buttonText() {
...@@ -48,7 +53,7 @@ export default { ...@@ -48,7 +53,7 @@ export default {
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<loading-button <loading-button
:loading="isDismissing" :loading="isDismissing"
:disabled="isDismissing" :disabled="isDismissing || disabled"
:label="buttonText" :label="buttonText"
container-class="js-dismiss-btn btn btn-close" container-class="js-dismiss-btn btn btn-close"
@click="handleDismissClick" @click="handleDismissClick"
...@@ -57,6 +62,7 @@ export default { ...@@ -57,6 +62,7 @@ export default {
v-if="!isDismissed" v-if="!isDismissed"
v-gl-tooltip.hover v-gl-tooltip.hover
v-gl-tooltip.focus v-gl-tooltip.focus
:disabled="disabled"
:title="s__('vulnerability|Add comment & dismiss')" :title="s__('vulnerability|Add comment & dismiss')"
variant="close" variant="close"
class="js-dismiss-with-comment " class="js-dismiss-with-comment "
......
...@@ -21,6 +21,11 @@ export default { ...@@ -21,6 +21,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
dismissalComment: {
type: String,
required: false,
default: '',
},
errorMessage: { errorMessage: {
type: String, type: String,
required: false, required: false,
...@@ -44,7 +49,8 @@ export default { ...@@ -44,7 +49,8 @@ export default {
}, },
}, },
mounted() { mounted() {
this.$emit('input', ''); this.$emit('input', this.dismissalComment);
this.$emit('clearError'); this.$emit('clearError');
this.$refs.dismissalComment.$el.focus(); this.$refs.dismissalComment.$el.focus();
}, },
......
...@@ -21,6 +21,11 @@ export default { ...@@ -21,6 +21,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
dismissalComment: {
type: String,
required: false,
default: '',
},
errorMessage: { errorMessage: {
type: String, type: String,
required: false, required: false,
...@@ -52,6 +57,7 @@ export default { ...@@ -52,6 +57,7 @@ export default {
<dismissal-comment-box <dismissal-comment-box
v-if="isActive" v-if="isActive"
v-model="localComment" v-model="localComment"
:dismissal-comment="dismissalComment"
:error-message="errorMessage" :error-message="errorMessage"
:placeholder="$options.PLACEHOLDER" :placeholder="$options.PLACEHOLDER"
@submit="$emit('submit')" @submit="$emit('submit')"
......
...@@ -21,12 +21,21 @@ export default { ...@@ -21,12 +21,21 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
isEditingExistingFeedback: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
submitLabel() { submitLabel() {
return this.isDismissed if (this.isEditingExistingFeedback) {
? s__('vulnerability|Add comment') return s__('vulnerability|Save comment');
: s__('vulnerability|Add comment & dismiss'); }
if (this.isDismissed) {
return s__('vulnerability|Add comment');
}
return s__('vulnerability|Add comment & dismiss');
}, },
}, },
methods: { methods: {
...@@ -35,7 +44,12 @@ export default { ...@@ -35,7 +44,12 @@ export default {
this.$emit('addCommentAndDismiss'); this.$emit('addCommentAndDismiss');
}, },
addDismissalComment() { addDismissalComment() {
if (this.isEditingExistingFeedback) {
Tracking.event(document.body.dataset.page, 'click_edit_comment');
} else {
Tracking.event(document.body.dataset.page, 'click_add_comment'); Tracking.event(document.body.dataset.page, 'click_add_comment');
}
this.$emit('addDismissalComment'); this.$emit('addDismissalComment');
}, },
handleSubmit() { handleSubmit() {
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
import _ from 'underscore'; import _ from 'underscore';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue'; 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 { export default {
components: { components: {
EventItem, EventItem,
GlButton,
LoadingButton,
}, },
props: { props: {
feedback: { feedback: {
...@@ -17,6 +21,26 @@ export default { ...@@ -17,6 +21,26 @@ export default {
required: false, required: false,
default: () => ({}), 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: { computed: {
eventText() { eventText() {
...@@ -49,6 +73,20 @@ export default { ...@@ -49,6 +73,20 @@ export default {
commentDetails() { commentDetails() {
return this.feedback.comment_details; return this.feedback.comment_details;
}, },
vulnDismissalActionButtons() {
return [
{
iconName: 'pencil',
emit: 'editVulnerabilityDismissalComment',
title: __('Edit Comment'),
},
{
iconName: 'remove',
emit: 'showDismissalDeleteButtons',
title: __('Delete Comment'),
},
];
},
}, },
}; };
</script> </script>
...@@ -63,15 +101,36 @@ export default { ...@@ -63,15 +101,36 @@ export default {
> >
<div v-html="eventText"></div> <div v-html="eventText"></div>
</event-item> </event-item>
<template v-if="commentDetails"> <template v-if="commentDetails && !isCommentingOnDismissal">
<hr class="my-3" /> <hr class="my-3" />
<event-item <event-item
:action-buttons="vulnDismissalActionButtons"
:author="commentDetails.comment_author" :author="commentDetails.comment_author"
:created-at="commentDetails.comment_timestamp" :created-at="commentDetails.comment_timestamp"
:show-right-slot="isShowingDeleteButtons"
:show-action-buttons="showDismissalCommentActions"
icon-name="comment" icon-name="comment"
icon-style="ci-status-icon-pending" icon-style="ci-status-icon-pending"
@editVulnerabilityDismissalComment="$emit('editVulnerabilityDismissalComment')"
@showDismissalDeleteButtons="$emit('showDismissalDeleteButtons')"
@hideDismissalDeleteButtons="$emit('hideDismissalDeleteButtons')"
@deleteDismissalComment="$emit('deleteDismissalComment')"
> >
{{ commentDetails.comment }} {{ 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> </event-item>
</template> </template>
</div> </div>
......
<script> <script>
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
...@@ -7,6 +8,10 @@ export default { ...@@ -7,6 +8,10 @@ export default {
components: { components: {
Icon, Icon,
TimeAgoTooltip, TimeAgoTooltip,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
}, },
props: { props: {
author: { author: {
...@@ -28,6 +33,21 @@ export default { ...@@ -28,6 +33,21 @@ export default {
required: false, required: false,
default: 'ci-status-icon-success', 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> </script>
...@@ -58,5 +78,24 @@ export default { ...@@ -58,5 +78,24 @@ export default {
</div> </div>
<slot></slot> <slot></slot>
</div> </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> </div>
</template> </template>
...@@ -115,6 +115,9 @@ export default { ...@@ -115,6 +115,9 @@ export default {
(this.vulnerability.dismissal_feedback || this.vulnerability.dismissalFeedback) (this.vulnerability.dismissal_feedback || this.vulnerability.dismissalFeedback)
); );
}, },
isEditingExistingFeedback() {
return this.dismissalFeedback && this.modal.isCommentingOnDismissal;
},
valuedFields() { valuedFields() {
const { data } = this.modal; const { data } = this.modal;
const result = {}; const result = {};
...@@ -225,10 +228,29 @@ export default { ...@@ -225,10 +228,29 @@ export default {
<div v-if="dismissalFeedback || modal.isCommentingOnDismissal" class="card my-4"> <div v-if="dismissalFeedback || modal.isCommentingOnDismissal" class="card my-4">
<div class="card-body"> <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 <dismissal-comment-box-toggle
v-if="!dismissalFeedback || !dismissalFeedback.comment_details" v-if="
!dismissalFeedback || !dismissalFeedback.comment_details || isEditingExistingFeedback
"
v-model="localDismissalComment" v-model="localDismissalComment"
:dismissal-comment="
dismissalFeedback &&
dismissalFeedback.comment_details &&
dismissalFeedback.comment_details.comment
"
:is-active="modal.isCommentingOnDismissal" :is-active="modal.isCommentingOnDismissal"
:error-message="dismissalCommentErrorMessage" :error-message="dismissalCommentErrorMessage"
@openDismissalCommentBox="$emit('openDismissalCommentBox')" @openDismissalCommentBox="$emit('openDismissalCommentBox')"
...@@ -244,6 +266,7 @@ export default { ...@@ -244,6 +266,7 @@ export default {
<dismissal-comment-modal-footer <dismissal-comment-modal-footer
v-if="modal.isCommentingOnDismissal" v-if="modal.isCommentingOnDismissal"
:is-dismissed="vulnerability.isDismissed" :is-dismissed="vulnerability.isDismissed"
:is-editing-existing-feedback="isEditingExistingFeedback"
@addCommentAndDismiss="addCommentAndDismiss" @addCommentAndDismiss="addCommentAndDismiss"
@addDismissalComment="addDismissalComment" @addDismissalComment="addDismissalComment"
@cancel="$emit('closeDismissalCommentBox')" @cancel="$emit('closeDismissalCommentBox')"
...@@ -252,6 +275,7 @@ export default { ...@@ -252,6 +275,7 @@ export default {
v-else-if="shouldRenderFooterSection" v-else-if="shouldRenderFooterSection"
:modal="modal" :modal="modal"
:vulnerability="vulnerability" :vulnerability="vulnerability"
:disabled="modal.isShowingDeleteButtons"
:can-create-issue="Boolean(!vulnerability.hasIssue && canCreateIssue)" :can-create-issue="Boolean(!vulnerability.hasIssue && canCreateIssue)"
:can-create-merge-request="Boolean(!vulnerability.hasMergeRequest && remediation)" :can-create-merge-request="Boolean(!vulnerability.hasMergeRequest && remediation)"
:can-download-patch="canDownloadPatch" :can-download-patch="canDownloadPatch"
......
...@@ -43,6 +43,11 @@ export default { ...@@ -43,6 +43,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
disabled: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
actionButtons() { actionButtons() {
...@@ -85,7 +90,7 @@ export default { ...@@ -85,7 +90,7 @@ export default {
<template> <template>
<div> <div>
<gl-button data-dismiss="modal"> <gl-button data-dismiss="modal" :disabled="disabled">
{{ __('Cancel') }} {{ __('Cancel') }}
</gl-button> </gl-button>
...@@ -93,6 +98,7 @@ export default { ...@@ -93,6 +98,7 @@ export default {
v-if="canDismissVulnerability" v-if="canDismissVulnerability"
:is-dismissing="modal.isDismissingVulnerability" :is-dismissing="modal.isDismissingVulnerability"
:is-dismissed="isDismissed" :is-dismissed="isDismissed"
:disabled="disabled"
@dismissVulnerability="$emit('dismissVulnerability')" @dismissVulnerability="$emit('dismissVulnerability')"
@openDismissalCommentBox="$emit('openDismissalCommentBox')" @openDismissalCommentBox="$emit('openDismissalCommentBox')"
@revertDismissVulnerability="$emit('revertDismissVulnerability')" @revertDismissVulnerability="$emit('revertDismissVulnerability')"
...@@ -103,6 +109,7 @@ export default { ...@@ -103,6 +109,7 @@ export default {
:buttons="actionButtons" :buttons="actionButtons"
class="js-split-button" class="js-split-button"
data-qa-selector="resolve_split_button" data-qa-selector="resolve_split_button"
:disabled="disabled"
@createMergeRequest="$emit('createMergeRequest')" @createMergeRequest="$emit('createMergeRequest')"
@createNewIssue="$emit('createNewIssue')" @createNewIssue="$emit('createNewIssue')"
@downloadPatch="$emit('downloadPatch')" @downloadPatch="$emit('downloadPatch')"
...@@ -111,7 +118,7 @@ export default { ...@@ -111,7 +118,7 @@ export default {
<loading-button <loading-button
v-else-if="actionButtons.length > 0" v-else-if="actionButtons.length > 0"
:loading="actionButtons[0].isLoading" :loading="actionButtons[0].isLoading"
:disabled="actionButtons[0].isLoading" :disabled="actionButtons[0].isLoading || disabled"
:label="actionButtons[0].name" :label="actionButtons[0].name"
container-class="btn btn-success btn-inverted" container-class="btn btn-success btn-inverted"
class="js-action-button" class="js-action-button"
......
...@@ -13,6 +13,11 @@ export default { ...@@ -13,6 +13,11 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
disabled: {
type: Boolean,
required: false,
default: false,
},
}, },
data: () => ({ data: () => ({
selectedButton: {}, selectedButton: {},
...@@ -34,6 +39,7 @@ export default { ...@@ -34,6 +39,7 @@ export default {
<template> <template>
<gl-dropdown <gl-dropdown
v-if="selectedButton" v-if="selectedButton"
:disabled="disabled"
no-caret no-caret
right right
split split
......
...@@ -254,6 +254,9 @@ export default { ...@@ -254,6 +254,9 @@ export default {
'closeDismissalCommentBox', 'closeDismissalCommentBox',
'downloadPatch', 'downloadPatch',
'addDismissalComment', 'addDismissalComment',
'deleteDismissalComment',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
]), ]),
...mapActions('sast', { ...mapActions('sast', {
setSastHeadPath: 'setHeadPath', setSastHeadPath: 'setHeadPath',
...@@ -364,9 +367,13 @@ export default { ...@@ -364,9 +367,13 @@ export default {
@createNewIssue="createNewIssue" @createNewIssue="createNewIssue"
@dismissVulnerability="dismissVulnerability" @dismissVulnerability="dismissVulnerability"
@openDismissalCommentBox="openDismissalCommentBox()" @openDismissalCommentBox="openDismissalCommentBox()"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@revertDismissVulnerability="revertDismissVulnerability" @revertDismissVulnerability="revertDismissVulnerability"
@downloadPatch="downloadPatch" @downloadPatch="downloadPatch"
@addDismissalComment="addDismissalComment({ comment: $event })" @addDismissalComment="addDismissalComment({ comment: $event })"
@deleteDismissalComment="deleteDismissalComment"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
/> />
</div> </div>
</report-section> </report-section>
......
...@@ -236,6 +236,9 @@ export default { ...@@ -236,6 +236,9 @@ export default {
'closeDismissalCommentBox', 'closeDismissalCommentBox',
'downloadPatch', 'downloadPatch',
'addDismissalComment', 'addDismissalComment',
'deleteDismissalComment',
'showDismissalDeleteButtons',
'hideDismissalDeleteButtons',
]), ]),
...mapActions('sast', { ...mapActions('sast', {
setSastHeadPath: 'setHeadPath', setSastHeadPath: 'setHeadPath',
...@@ -331,6 +334,10 @@ export default { ...@@ -331,6 +334,10 @@ export default {
@revertDismissVulnerability="revertDismissVulnerability" @revertDismissVulnerability="revertDismissVulnerability"
@downloadPatch="downloadPatch" @downloadPatch="downloadPatch"
@addDismissalComment="addDismissalComment({ comment: $event })" @addDismissalComment="addDismissalComment({ comment: $event })"
@editVulnerabilityDismissalComment="openDismissalCommentBox()"
@deleteDismissalComment="deleteDismissalComment"
@showDismissalDeleteButtons="showDismissalDeleteButtons"
@hideDismissalDeleteButtons="hideDismissalDeleteButtons"
/> />
</div> </div>
</template> </template>
...@@ -254,6 +254,43 @@ export const addDismissalComment = ({ state, dispatch }, { comment }) => { ...@@ -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 }) => { export const requestAddDismissalComment = ({ commit }) => {
commit(types.REQUEST_ADD_DISMISSAL_COMMENT); commit(types.REQUEST_ADD_DISMISSAL_COMMENT);
}; };
...@@ -293,6 +330,14 @@ export const revertDismissVulnerability = ({ state, dispatch }) => { ...@@ -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 requestCreateIssue = ({ commit }) => commit(types.REQUEST_CREATE_ISSUE);
export const receiveCreateIssue = ({ commit }) => commit(types.RECEIVE_CREATE_ISSUE_SUCCESS); export const receiveCreateIssue = ({ commit }) => commit(types.RECEIVE_CREATE_ISSUE_SUCCESS);
export const receiveCreateIssueError = ({ commit }, error) => export const receiveCreateIssueError = ({ commit }, error) =>
......
...@@ -43,6 +43,10 @@ export const REQUEST_ADD_DISMISSAL_COMMENT = 'REQUEST_ADD_DISMISSAL_COMMENT'; ...@@ -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_SUCCESS = 'RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS';
export const RECEIVE_ADD_DISMISSAL_COMMENT_ERROR = 'RECEIVE_ADD_DISMISSAL_COMMENT_ERROR'; 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 REQUEST_CREATE_ISSUE = 'CREATE_DISMISS_VULNERABILITY';
export const RECEIVE_CREATE_ISSUE_SUCCESS = 'CREATE_DISMISS_VULNERABILITY_SUCCESS'; export const RECEIVE_CREATE_ISSUE_SUCCESS = 'CREATE_DISMISS_VULNERABILITY_SUCCESS';
export const RECEIVE_CREATE_ISSUE_ERROR = 'CREATE_DISMISS_VULNERABILITY_ERROR'; export const RECEIVE_CREATE_ISSUE_ERROR = 'CREATE_DISMISS_VULNERABILITY_ERROR';
...@@ -51,6 +55,9 @@ export const REQUEST_CREATE_MERGE_REQUEST = 'REQUEST_CREATE_MERGE_REQUEST'; ...@@ -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_SUCCESS = 'RECEIVE_CREATE_MERGE_REQUEST_SUCCESS';
export const RECEIVE_CREATE_MERGE_REQUEST_ERROR = 'RECEIVE_CREATE_MERGE_REQUEST_ERROR'; 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_DEPENDENCY_SCANNING_ISSUE = 'UPDATE_DEPENDENCY_SCANNING_ISSUE';
export const UPDATE_CONTAINER_SCANNING_ISSUE = 'UPDATE_CONTAINER_SCANNING_ISSUE'; export const UPDATE_CONTAINER_SCANNING_ISSUE = 'UPDATE_CONTAINER_SCANNING_ISSUE';
export const UPDATE_DAST_ISSUE = 'UPDATE_DAST_ISSUE'; export const UPDATE_DAST_ISSUE = 'UPDATE_DAST_ISSUE';
......
...@@ -282,7 +282,28 @@ export default { ...@@ -282,7 +282,28 @@ export default {
Vue.set(state.modal, 'isDismissingVulnerability', false); Vue.set(state.modal, 'isDismissingVulnerability', false);
Vue.set(state.modal, 'error', error); 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) { [types.UPDATE_DEPENDENCY_SCANNING_ISSUE](state, issue) {
// Find issue in the correct list and update it // Find issue in the correct list and update it
...@@ -367,6 +388,7 @@ export default { ...@@ -367,6 +388,7 @@ export default {
Vue.set(state.modal, 'isCommentingOnDismissal', true); Vue.set(state.modal, 'isCommentingOnDismissal', true);
}, },
[types.CLOSE_DISMISSAL_COMMENT_BOX](state) { [types.CLOSE_DISMISSAL_COMMENT_BOX](state) {
Vue.set(state.modal, 'isShowingDeleteButtons', false);
Vue.set(state.modal, 'isCommentingOnDismissal', false); Vue.set(state.modal, 'isCommentingOnDismissal', false);
}, },
}; };
...@@ -127,7 +127,8 @@ export default () => ({ ...@@ -127,7 +127,8 @@ export default () => ({
isCreatingNewIssue: false, isCreatingNewIssue: false,
isDismissingVulnerability: false, isDismissingVulnerability: false,
isShowingDeleteButtons: false,
isCommentingOnDismissal: false,
error: null, 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,6 +46,7 @@ describe('DismissalCommentModalFooter', () => { ...@@ -46,6 +46,7 @@ describe('DismissalCommentModalFooter', () => {
}); });
describe('with an already dismissed vulnerability', () => { describe('with an already dismissed vulnerability', () => {
describe('and adding a comment', () => {
beforeEach(() => { beforeEach(() => {
const propsData = { const propsData = {
isDismissed: true, isDismissed: true,
...@@ -64,4 +65,26 @@ describe('DismissalCommentModalFooter', () => { ...@@ -64,4 +65,26 @@ describe('DismissalCommentModalFooter', () => {
expect(Tracking.event).toHaveBeenCalledWith('_track_category_', 'click_add_comment'); expect(Tracking.event).toHaveBeenCalledWith('_track_category_', 'click_add_comment');
}); });
}); });
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_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 component from 'ee/vue_shared/security_reports/components/dismissal_note.vue';
import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue'; import EventItem from 'ee/vue_shared/security_reports/components/event_item.vue';
...@@ -114,6 +114,7 @@ describe('dismissal note', () => { ...@@ -114,6 +114,7 @@ describe('dismissal note', () => {
}; };
let commentItem; let commentItem;
describe('without confirm deletion buttons', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(component, { wrapper = shallowMount(component, {
propsData: { propsData: {
...@@ -139,4 +140,27 @@ describe('dismissal note', () => { ...@@ -139,4 +140,27 @@ describe('dismissal note', () => {
expect(commentItem.props().createdAt).toBe(commentDetails.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 component from 'ee/vue_shared/security_reports/components/event_item.vue'; import { shallowMount, mount } from '@vue/test-utils';
import mountComponent from 'helpers/vue_mount_component_helper';
describe('Event Item', () => { describe('Event Item', () => {
const Component = Vue.extend(component); describe('initial state', () => {
const props = { let wrapper;
const propsData = {
author: { author: {
name: 'Tanuki', name: 'Tanuki',
username: 'gitlab', username: 'gitlab',
}, },
}; };
let vm;
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
}); });
beforeEach(() => { beforeEach(() => {
vm = mountComponent(Component, props); wrapper = shallowMount(Component, { propsData });
}); });
it('uses the author name', () => { it('uses the author name', () => {
expect(vm.$el.querySelector('.js-author').textContent).toContain(props.author.name); expect(wrapper.find('.js-author').text()).toContain(propsData.author.name);
}); });
it('uses the author username', () => { it('uses the author username', () => {
expect(vm.$el.querySelector('.js-author').textContent).toContain(`@${props.author.username}`); expect(wrapper.find('.js-author').text()).toContain(`@${propsData.author.username}`);
}); });
it('uses the fallback icon', () => { it('uses the fallback icon', () => {
expect(vm.iconName).toBe('plus'); expect(wrapper.props().iconName).toBe('plus');
}); });
it('uses the fallback icon class', () => { it('uses the fallback icon class', () => {
expect(vm.iconStyle).toBe('ci-status-icon-success'); 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');
expect(wrapper.emitted().fooEvent.length).toEqual(1);
expect(wrapper.emitted().barEvent.length).toEqual(1);
});
}); });
}); });
...@@ -525,6 +525,113 @@ describe('security reports mutations', () => { ...@@ -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', () => { describe('REQUEST_CREATE_ISSUE', () => {
it('sets isCreatingNewIssue prop to true and resets error', () => { it('sets isCreatingNewIssue prop to true and resets error', () => {
mutations[types.REQUEST_CREATE_ISSUE](stateCopy); mutations[types.REQUEST_CREATE_ISSUE](stateCopy);
......
...@@ -847,6 +847,157 @@ describe('add vulnerability dismissal comment', () => { ...@@ -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', () => { describe('revert vulnerability dismissal', () => {
...@@ -1128,12 +1279,12 @@ describe('vulnerabilities history actions', () => { ...@@ -1128,12 +1279,12 @@ describe('vulnerabilities history actions', () => {
}); });
describe('openDismissalCommentBox', () => { 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(); const state = initialState();
testAction( testAction(
actions.openDismissalCommentBox, actions.openDismissalCommentBox,
{}, undefined,
state, state,
[{ type: types.OPEN_DISMISSAL_COMMENT_BOX }], [{ type: types.OPEN_DISMISSAL_COMMENT_BOX }],
[], [],
......
...@@ -542,7 +542,107 @@ describe('vulnerabilities module mutations', () => { ...@@ -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; let state;
beforeEach(() => { beforeEach(() => {
...@@ -563,7 +663,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -563,7 +663,7 @@ describe('vulnerabilities module mutations', () => {
}); });
}); });
describe('RECEIVE_DISMISSAL_COMMENT_SUCCESS', () => { describe('RECEIVE_ADD_DISMISSAL_COMMENT_SUCCESS', () => {
let state; let state;
let payload; let payload;
let vulnerability; let vulnerability;
...@@ -595,7 +695,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -595,7 +695,7 @@ describe('vulnerabilities module mutations', () => {
}); });
}); });
describe('RECEIVE_DISMISSAL_COMMENT_ERROR', () => { describe('RECEIVE_ADD_DISMISSAL_COMMENT_ERROR', () => {
let state; let state;
beforeEach(() => { beforeEach(() => {
...@@ -689,20 +789,32 @@ describe('vulnerabilities module mutations', () => { ...@@ -689,20 +789,32 @@ describe('vulnerabilities module mutations', () => {
}); });
describe('OPEN_DISMISSAL_COMMENT_BOX', () => { describe('OPEN_DISMISSAL_COMMENT_BOX', () => {
it('should set isCommentingOnDismissal to true', () => { let state;
const state = createState();
beforeEach(() => {
state = createState();
mutations[types.OPEN_DISMISSAL_COMMENT_BOX](state); mutations[types.OPEN_DISMISSAL_COMMENT_BOX](state);
});
it('should set isCommentingOnDismissal to true', () => {
expect(state.modal.isCommentingOnDismissal).toBe(true); expect(state.modal.isCommentingOnDismissal).toBe(true);
}); });
}); });
describe('CLOSE_DISMISSAL_COMMENT_BOX', () => { describe('CLOSE_DISMISSAL_COMMENT_BOX', () => {
it('should set isCommentingOnDismissal to false', () => { let state;
const state = createState();
beforeEach(() => {
state = createState();
mutations[types.CLOSE_DISMISSAL_COMMENT_BOX](state); mutations[types.CLOSE_DISMISSAL_COMMENT_BOX](state);
});
it('should set isCommentingOnDismissal to false', () => {
expect(state.modal.isCommentingOnDismissal).toBe(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, { ...@@ -49,6 +49,12 @@ import actions, {
receiveAddDismissalCommentError, receiveAddDismissalCommentError,
receiveAddDismissalCommentSuccess, receiveAddDismissalCommentSuccess,
requestAddDismissalComment, requestAddDismissalComment,
deleteDismissalComment,
receiveDeleteDismissalCommentError,
receiveDeleteDismissalCommentSuccess,
requestDeleteDismissalComment,
showDismissalDeleteButtons,
hideDismissalDeleteButtons,
} from 'ee/vue_shared/security_reports/store/actions'; } from 'ee/vue_shared/security_reports/store/actions';
import * as types from 'ee/vue_shared/security_reports/store/mutation_types'; import * as types from 'ee/vue_shared/security_reports/store/mutation_types';
import state from 'ee/vue_shared/security_reports/store/state'; import state from 'ee/vue_shared/security_reports/store/state';
...@@ -1095,6 +1101,142 @@ describe('security reports actions', () => { ...@@ -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('revertDismissVulnerability', () => {
describe('with success', () => { describe('with success', () => {
let payload; let payload;
......
...@@ -4581,6 +4581,9 @@ msgstr "" ...@@ -4581,6 +4581,9 @@ msgstr ""
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
msgid "Delete Comment"
msgstr ""
msgid "Delete Package" msgid "Delete Package"
msgstr "" msgstr ""
...@@ -5164,6 +5167,9 @@ msgstr "" ...@@ -5164,6 +5167,9 @@ msgstr ""
msgid "Edit %{name}" msgid "Edit %{name}"
msgstr "" msgstr ""
msgid "Edit Comment"
msgstr ""
msgid "Edit Deploy Key" msgid "Edit Deploy Key"
msgstr "" msgstr ""
...@@ -13167,6 +13173,9 @@ msgstr "" ...@@ -13167,6 +13173,9 @@ msgstr ""
msgid "Security Reports|There was an error creating the merge request." msgid "Security Reports|There was an error creating the merge request."
msgstr "" msgstr ""
msgid "Security Reports|There was an error deleting the comment."
msgstr ""
msgid "Security Reports|There was an error dismissing the vulnerability." msgid "Security Reports|There was an error dismissing the vulnerability."
msgstr "" msgstr ""
...@@ -18755,6 +18764,9 @@ msgstr "" ...@@ -18755,6 +18764,9 @@ msgstr ""
msgid "vulnerability|Dismiss vulnerability" msgid "vulnerability|Dismiss vulnerability"
msgstr "" msgstr ""
msgid "vulnerability|Save comment"
msgstr ""
msgid "vulnerability|Undo dismiss" msgid "vulnerability|Undo dismiss"
msgstr "" 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