Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/work_items/components/work_item_notes_spec.js')
-rw-r--r--spec/frontend/work_items/components/work_item_notes_spec.js171
1 files changed, 162 insertions, 9 deletions
diff --git a/spec/frontend/work_items/components/work_item_notes_spec.js b/spec/frontend/work_items/components/work_item_notes_spec.js
index 23dd2b6bacb..3db848a0ad2 100644
--- a/spec/frontend/work_items/components/work_item_notes_spec.js
+++ b/spec/frontend/work_items/components/work_item_notes_spec.js
@@ -1,22 +1,26 @@
-import { GlSkeletonLoader } from '@gitlab/ui';
+import { GlSkeletonLoader, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
+import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises';
import SystemNote from '~/work_items/components/notes/system_note.vue';
import WorkItemNotes from '~/work_items/components/work_item_notes.vue';
-import WorkItemCommentForm from '~/work_items/components/work_item_comment_form.vue';
+import WorkItemDiscussion from '~/work_items/components/notes/work_item_discussion.vue';
+import WorkItemAddNote from '~/work_items/components/notes/work_item_add_note.vue';
import ActivityFilter from '~/work_items/components/notes/activity_filter.vue';
-import workItemNotesQuery from '~/work_items/graphql/work_item_notes.query.graphql';
-import workItemNotesByIidQuery from '~/work_items/graphql/work_item_notes_by_iid.query.graphql';
+import workItemNotesQuery from '~/work_items/graphql/notes/work_item_notes.query.graphql';
+import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql';
+import deleteWorkItemNoteMutation from '~/work_items/graphql/notes/delete_work_item_notes.mutation.graphql';
import { DEFAULT_PAGE_SIZE_NOTES, WIDGET_TYPE_NOTES } from '~/work_items/constants';
-import { DESC } from '~/notes/constants';
+import { ASC, DESC } from '~/notes/constants';
import {
mockWorkItemNotesResponse,
workItemQueryResponse,
mockWorkItemNotesByIidResponse,
mockMoreWorkItemNotesResponse,
+ mockWorkItemNotesResponseWithComments,
} from '../mock_data';
const mockWorkItemId = workItemQueryResponse.data.workItem.id;
@@ -32,34 +36,56 @@ const mockMoreNotesWidgetResponse = mockMoreWorkItemNotesResponse.data.workItem.
(widget) => widget.type === WIDGET_TYPE_NOTES,
);
+const mockWorkItemNotesWidgetResponseWithComments = mockWorkItemNotesResponseWithComments.data.workItem.widgets.find(
+ (widget) => widget.type === WIDGET_TYPE_NOTES,
+);
+
const firstSystemNodeId = mockNotesWidgetResponse.discussions.nodes[0].notes.nodes[0].id;
+const mockDiscussions = mockWorkItemNotesWidgetResponseWithComments.discussions.nodes;
+
describe('WorkItemNotes component', () => {
let wrapper;
Vue.use(VueApollo);
+ const showModal = jest.fn();
+
const findAllSystemNotes = () => wrapper.findAllComponents(SystemNote);
+ const findAllListItems = () => wrapper.findAll('ul.timeline > *');
const findActivityLabel = () => wrapper.find('label');
- const findWorkItemCommentForm = () => wrapper.findComponent(WorkItemCommentForm);
+ const findWorkItemAddNote = () => wrapper.findComponent(WorkItemAddNote);
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findSortingFilter = () => wrapper.findComponent(ActivityFilter);
const findSystemNoteAtIndex = (index) => findAllSystemNotes().at(index);
+ const findAllWorkItemCommentNotes = () => wrapper.findAllComponents(WorkItemDiscussion);
+ const findWorkItemCommentNoteAtIndex = (index) => findAllWorkItemCommentNotes().at(index);
+ const findDeleteNoteModal = () => wrapper.findComponent(GlModal);
+
const workItemNotesQueryHandler = jest.fn().mockResolvedValue(mockWorkItemNotesResponse);
const workItemNotesByIidQueryHandler = jest
.fn()
.mockResolvedValue(mockWorkItemNotesByIidResponse);
const workItemMoreNotesQueryHandler = jest.fn().mockResolvedValue(mockMoreWorkItemNotesResponse);
+ const workItemNotesWithCommentsQueryHandler = jest
+ .fn()
+ .mockResolvedValue(mockWorkItemNotesResponseWithComments);
+ const deleteWorkItemNoteMutationSuccessHandler = jest.fn().mockResolvedValue({
+ data: { destroyNote: { note: null, __typename: 'DestroyNote' } },
+ });
+ const errorHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
const createComponent = ({
workItemId = mockWorkItemId,
fetchByIid = false,
defaultWorkItemNotesQueryHandler = workItemNotesQueryHandler,
+ deleteWINoteMutationHandler = deleteWorkItemNoteMutationSuccessHandler,
} = {}) => {
wrapper = shallowMount(WorkItemNotes, {
apolloProvider: createMockApollo([
[workItemNotesQuery, defaultWorkItemNotesQueryHandler],
[workItemNotesByIidQuery, workItemNotesByIidQueryHandler],
+ [deleteWorkItemNoteMutation, deleteWINoteMutationHandler],
]),
propsData: {
workItemId,
@@ -75,6 +101,9 @@ describe('WorkItemNotes component', () => {
useIidInWorkItemsPath: fetchByIid,
},
},
+ stubs: {
+ GlModal: stubComponent(GlModal, { methods: { show: showModal } }),
+ },
});
};
@@ -87,10 +116,14 @@ describe('WorkItemNotes component', () => {
});
it('passes correct props to comment form component', async () => {
- createComponent({ workItemId: mockWorkItemId, fetchByIid: false });
+ createComponent({
+ workItemId: mockWorkItemId,
+ fetchByIid: false,
+ defaultWorkItemNotesQueryHandler: workItemNotesByIidQueryHandler,
+ });
await waitForPromises();
- expect(findWorkItemCommentForm().props('fetchByIid')).toEqual(false);
+ expect(findWorkItemAddNote().props('fetchByIid')).toEqual(false);
});
describe('when notes are loading', () => {
@@ -121,13 +154,14 @@ describe('WorkItemNotes component', () => {
});
it('renders the notes list to the length of the response', () => {
+ expect(workItemNotesByIidQueryHandler).toHaveBeenCalled();
expect(findAllSystemNotes()).toHaveLength(
mockNotesByIidWidgetResponse.discussions.nodes.length,
);
});
it('passes correct props to comment form component', () => {
- expect(findWorkItemCommentForm().props('fetchByIid')).toEqual(true);
+ expect(findWorkItemAddNote().props('fetchByIid')).toEqual(true);
});
});
@@ -180,5 +214,124 @@ describe('WorkItemNotes component', () => {
expect(findSystemNoteAtIndex(0).props('note').id).not.toEqual(firstSystemNodeId);
});
+
+ it('puts form at start of list in when sorting by newest first', async () => {
+ await findSortingFilter().vm.$emit('changeSortOrder', DESC);
+
+ expect(findAllListItems().at(0).is(WorkItemAddNote)).toEqual(true);
+ });
+
+ it('puts form at end of list in when sorting by oldest first', async () => {
+ await findSortingFilter().vm.$emit('changeSortOrder', ASC);
+
+ expect(findAllListItems().at(-1).is(WorkItemAddNote)).toEqual(true);
+ });
+ });
+
+ describe('Activity comments', () => {
+ beforeEach(async () => {
+ createComponent({
+ defaultWorkItemNotesQueryHandler: workItemNotesWithCommentsQueryHandler,
+ });
+ await waitForPromises();
+ });
+
+ it('should not have any system notes', () => {
+ expect(workItemNotesWithCommentsQueryHandler).toHaveBeenCalled();
+ expect(findAllSystemNotes()).toHaveLength(0);
+ });
+
+ it('should have work item notes', () => {
+ expect(workItemNotesWithCommentsQueryHandler).toHaveBeenCalled();
+ expect(findAllWorkItemCommentNotes()).toHaveLength(mockDiscussions.length);
+ });
+
+ it('should pass all the correct props to work item comment note', () => {
+ const commentIndex = 0;
+ const firstCommentNote = findWorkItemCommentNoteAtIndex(commentIndex);
+
+ expect(firstCommentNote.props('discussion')).toEqual(
+ mockDiscussions[commentIndex].notes.nodes,
+ );
+ });
+ });
+
+ it('should open delete modal confirmation when child discussion emits `deleteNote` event', async () => {
+ createComponent({
+ defaultWorkItemNotesQueryHandler: workItemNotesWithCommentsQueryHandler,
+ });
+ await waitForPromises();
+
+ findWorkItemCommentNoteAtIndex(0).vm.$emit('deleteNote', { id: '1', isLastNote: false });
+ expect(showModal).toHaveBeenCalled();
+ });
+
+ describe('when modal is open', () => {
+ beforeEach(() => {
+ createComponent({
+ defaultWorkItemNotesQueryHandler: workItemNotesWithCommentsQueryHandler,
+ });
+ return waitForPromises();
+ });
+
+ it('sends the mutation with correct variables', () => {
+ const noteId = 'some-test-id';
+
+ findWorkItemCommentNoteAtIndex(0).vm.$emit('deleteNote', { id: noteId });
+ findDeleteNoteModal().vm.$emit('primary');
+
+ expect(deleteWorkItemNoteMutationSuccessHandler).toHaveBeenCalledWith({
+ input: {
+ id: noteId,
+ },
+ });
+ });
+
+ it('successfully removes the note from the discussion', async () => {
+ expect(findWorkItemCommentNoteAtIndex(0).props('discussion')).toHaveLength(2);
+
+ findWorkItemCommentNoteAtIndex(0).vm.$emit('deleteNote', {
+ id: mockDiscussions[0].notes.nodes[0].id,
+ });
+ findDeleteNoteModal().vm.$emit('primary');
+
+ await waitForPromises();
+ expect(findWorkItemCommentNoteAtIndex(0).props('discussion')).toHaveLength(1);
+ });
+
+ it('successfully removes the discussion from work item if discussion only had one note', async () => {
+ const secondDiscussion = findWorkItemCommentNoteAtIndex(1);
+
+ expect(findAllWorkItemCommentNotes()).toHaveLength(2);
+ expect(secondDiscussion.props('discussion')).toHaveLength(1);
+
+ secondDiscussion.vm.$emit('deleteNote', {
+ id: mockDiscussions[1].notes.nodes[0].id,
+ discussion: { id: mockDiscussions[1].id },
+ });
+ findDeleteNoteModal().vm.$emit('primary');
+
+ await waitForPromises();
+ expect(findAllWorkItemCommentNotes()).toHaveLength(1);
+ });
+ });
+
+ it('emits `error` event if delete note mutation is rejected', async () => {
+ createComponent({
+ defaultWorkItemNotesQueryHandler: workItemNotesWithCommentsQueryHandler,
+ deleteWINoteMutationHandler: errorHandler,
+ });
+ await waitForPromises();
+
+ findWorkItemCommentNoteAtIndex(0).vm.$emit('deleteNote', {
+ id: mockDiscussions[0].notes.nodes[0].id,
+ });
+ findDeleteNoteModal().vm.$emit('primary');
+
+ await waitForPromises();
+
+ expect(wrapper.emitted('error')).toEqual([
+ ['Something went wrong when deleting a comment. Please try again'],
+ ]);
});
});