Commit 2506fdbd authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '228745-use-graphql-to-delete-note' into 'master'

Use GraphQL to delete a note

See merge request gitlab-org/gitlab!68757
parents 2d5a907d 29c13649
...@@ -11,3 +11,4 @@ export const TYPE_SCANNER_PROFILE = 'DastScannerProfile'; ...@@ -11,3 +11,4 @@ export const TYPE_SCANNER_PROFILE = 'DastScannerProfile';
export const TYPE_SITE_PROFILE = 'DastSiteProfile'; export const TYPE_SITE_PROFILE = 'DastSiteProfile';
export const TYPE_USER = 'User'; export const TYPE_USER = 'User';
export const TYPE_VULNERABILITY = 'Vulnerability'; export const TYPE_VULNERABILITY = 'Vulnerability';
export const TYPE_NOTE = 'Note';
mutation($id: ID!) {
destroyNote(input: { id: $id }) {
errors
note {
id
}
}
}
<script> <script>
import { GlButton, GlSafeHtmlDirective as SafeHtml, GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlSafeHtmlDirective as SafeHtml, GlLoadingIcon } from '@gitlab/ui';
import deleteNoteMutation from 'ee/security_dashboard/graphql/mutations/note_delete.mutation.graphql';
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 createFlash from '~/flash'; import createFlash from '~/flash';
import { TYPE_NOTE } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import HistoryCommentEditor from './history_comment_editor.vue'; import HistoryCommentEditor from './history_comment_editor.vue';
...@@ -115,25 +118,26 @@ export default { ...@@ -115,25 +118,26 @@ export default {
}); });
}); });
}, },
deleteComment() { async deleteComment() {
this.isDeletingComment = true; this.isDeletingComment = true;
const deleteUrl = this.comment.path;
axios try {
.delete(deleteUrl) await this.$apollo.mutate({
.then(() => { mutation: deleteNoteMutation,
this.$emit('onCommentDeleted', this.comment); variables: {
}) id: convertToGraphQLId(TYPE_NOTE, this.comment.id),
.catch(() => },
createFlash({
message: s__(
'VulnerabilityManagement|Something went wrong while trying to delete the comment. Please try again later.',
),
}),
)
.finally(() => {
this.isDeletingComment = false;
}); });
this.$emit('onCommentDeleted', this.comment);
} catch (e) {
createFlash({
message: s__(
'VulnerabilityManagement|Something went wrong while trying to delete the comment. Please try again later.',
),
});
}
this.isDeletingComment = false;
}, },
cancelEditingComment() { cancelEditingComment() {
this.isEditingComment = false; this.isEditingComment = false;
......
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import deleteNoteMutation from 'ee/security_dashboard/graphql/mutations/note_delete.mutation.graphql';
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 HistoryComment from 'ee/vulnerabilities/components/history_comment.vue'; import HistoryComment from 'ee/vulnerabilities/components/history_comment.vue';
import HistoryCommentEditor from 'ee/vulnerabilities/components/history_comment_editor.vue'; import HistoryCommentEditor from 'ee/vulnerabilities/components/history_comment_editor.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import createFlash from '~/flash'; import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
const mockAxios = new MockAdapter(axios); const mockAxios = new MockAdapter(axios);
jest.mock('~/flash'); jest.mock('~/flash');
Vue.use(VueApollo);
describe('History Comment', () => { describe('History Comment', () => {
let wrapper; let wrapper;
const createWrapper = (comment) => { const createApolloProvider = (...queries) => {
return createMockApollo([...queries]);
};
const createWrapper = ({ comment, apolloProvider } = {}) => {
wrapper = mount(HistoryComment, { wrapper = mount(HistoryComment, {
apolloProvider,
propsData: { propsData: {
comment, comment,
notesUrl: '/notes', notesUrl: '/notes',
...@@ -138,7 +149,7 @@ describe('History Comment', () => { ...@@ -138,7 +149,7 @@ describe('History Comment', () => {
}); });
describe(`when there's an existing comment`, () => { describe(`when there's an existing comment`, () => {
beforeEach(() => createWrapper(comment)); beforeEach(() => createWrapper({ comment }));
it('shows the comment with the correct user author and timestamp and the edit/delete buttons', () => { it('shows the comment with the correct user author and timestamp and the edit/delete buttons', () => {
expectExistingCommentView(); expectExistingCommentView();
...@@ -187,44 +198,6 @@ describe('History Comment', () => { ...@@ -187,44 +198,6 @@ describe('History Comment', () => {
}); });
}); });
it('deletes the comment when the confirm delete button is clicked', () => {
mockAxios.onDelete().replyOnce(200);
deleteButton().trigger('click');
return wrapper.vm
.$nextTick()
.then(() => {
confirmDeleteButton().trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
expect(confirmDeleteButton().props('loading')).toBe(true);
expect(cancelDeleteButton().props('disabled')).toBe(true);
return axios.waitForAll();
})
.then(() => {
expect(mockAxios.history.delete).toHaveLength(1);
expect(wrapper.emitted().onCommentDeleted).toBeTruthy();
expect(wrapper.emitted().onCommentDeleted[0][0]).toEqual(comment);
});
});
it('shows an error message when the comment cannot be deleted', () => {
mockAxios.onDelete().replyOnce(500);
deleteButton().trigger('click');
return wrapper.vm
.$nextTick()
.then(() => {
confirmDeleteButton().trigger('click');
return axios.waitForAll();
})
.then(() => {
expect(mockAxios.history.delete).toHaveLength(1);
expect(createFlash).toHaveBeenCalledTimes(1);
});
});
it('saves the comment when the save button is clicked on the comment editor', () => { it('saves the comment when the save button is clicked on the comment editor', () => {
const responseData = { ...comment, note: 'new comment' }; const responseData = { ...comment, note: 'new comment' };
mockAxios.onPut().replyOnce(200, responseData); mockAxios.onPut().replyOnce(200, responseData);
...@@ -261,9 +234,66 @@ describe('History Comment', () => { ...@@ -261,9 +234,66 @@ describe('History Comment', () => {
}); });
}); });
describe('deleting a note', () => {
it('deletes the comment when the confirm delete button is clicked', async () => {
createWrapper({
comment,
apolloProvider: createApolloProvider([
deleteNoteMutation,
jest.fn().mockResolvedValue({
data: {
destroyNote: {
errors: [],
note: null,
},
},
}),
]),
});
deleteButton().trigger('click');
await wrapper.vm.$nextTick();
confirmDeleteButton().trigger('click');
await wrapper.vm.$nextTick();
expect(confirmDeleteButton().props('loading')).toBe(true);
expect(cancelDeleteButton().props('disabled')).toBe(true);
await axios.waitForAll();
expect(wrapper.emitted().onCommentDeleted).toBeTruthy();
expect(wrapper.emitted().onCommentDeleted[0][0]).toEqual(comment);
});
it('shows an error message when the comment cannot be deleted', async () => {
createWrapper({
comment,
apolloProvider: createApolloProvider([
deleteNoteMutation,
jest.fn().mockRejectedValue({
data: {
destroyNote: {
errors: [{ message: 'Something went wrong' }],
note: null,
},
},
}),
]),
});
deleteButton().trigger('click');
await wrapper.vm.$nextTick();
confirmDeleteButton().trigger('click');
await axios.waitForAll();
expect(createFlash).toHaveBeenCalledTimes(1);
});
});
describe('no permission to edit existing comment', () => { describe('no permission to edit existing comment', () => {
it('does not show the edit/delete buttons if the current user has no edit permissions', () => { it('does not show the edit/delete buttons if the current user has no edit permissions', () => {
createWrapper({ ...comment, currentUser: { canEdit: false } }); createWrapper({ comment: { ...comment, currentUser: { canEdit: false } } });
expect(editButton().exists()).toBe(false); expect(editButton().exists()).toBe(false);
expect(deleteButton().exists()).toBe(false); expect(deleteButton().exists()).toBe(false);
......
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