diff options
Diffstat (limited to 'spec/frontend/work_items/components/work_item_description_spec.js')
-rw-r--r-- | spec/frontend/work_items/components/work_item_description_spec.js | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/spec/frontend/work_items/components/work_item_description_spec.js b/spec/frontend/work_items/components/work_item_description_spec.js new file mode 100644 index 00000000000..8017c46dea8 --- /dev/null +++ b/spec/frontend/work_items/components/work_item_description_spec.js @@ -0,0 +1,222 @@ +import { shallowMount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import { mockTracking } from 'helpers/tracking_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { updateDraft } from '~/lib/utils/autosave'; +import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; +import MarkdownField from '~/vue_shared/components/markdown/field.vue'; +import WorkItemDescription from '~/work_items/components/work_item_description.vue'; +import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants'; +import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; +import updateWorkItemWidgetsMutation from '~/work_items/graphql/update_work_item_widgets.mutation.graphql'; +import { + updateWorkItemWidgetsResponse, + workItemResponseFactory, + workItemQueryResponse, +} from '../mock_data'; + +jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal', () => { + return { + confirmAction: jest.fn(), + }; +}); +jest.mock('~/lib/utils/autosave'); + +const workItemId = workItemQueryResponse.data.workItem.id; + +describe('WorkItemDescription', () => { + let wrapper; + + Vue.use(VueApollo); + + const mutationSuccessHandler = jest.fn().mockResolvedValue(updateWorkItemWidgetsResponse); + + const findEditButton = () => wrapper.find('[data-testid="edit-description"]'); + const findMarkdownField = () => wrapper.findComponent(MarkdownField); + + const editDescription = (newText) => wrapper.find('textarea').setValue(newText); + + const clickCancel = () => wrapper.find('[data-testid="cancel"]').vm.$emit('click'); + const clickSave = () => wrapper.find('[data-testid="save-description"]').vm.$emit('click', {}); + + const createComponent = async ({ + mutationHandler = mutationSuccessHandler, + canUpdate = true, + isEditing = false, + } = {}) => { + const workItemResponse = workItemResponseFactory({ canUpdate }); + const workItemResponseHandler = jest.fn().mockResolvedValue(workItemResponse); + + const { id } = workItemQueryResponse.data.workItem; + wrapper = shallowMount(WorkItemDescription, { + apolloProvider: createMockApollo([ + [workItemQuery, workItemResponseHandler], + [updateWorkItemWidgetsMutation, mutationHandler], + ]), + propsData: { + workItemId: id, + }, + provide: { + fullPath: '/group/project', + }, + stubs: { + MarkdownField, + }, + }); + + await waitForPromises(); + + if (isEditing) { + findEditButton().vm.$emit('click'); + + await nextTick(); + } + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('Edit button', () => { + it('is not visible when canUpdate = false', async () => { + await createComponent({ + canUpdate: false, + }); + + expect(findEditButton().exists()).toBe(false); + }); + + it('toggles edit mode', async () => { + await createComponent({ + canUpdate: true, + }); + + findEditButton().vm.$emit('click'); + + await nextTick(); + + expect(findMarkdownField().exists()).toBe(true); + }); + }); + + describe('editing description', () => { + it('cancels when clicking cancel', async () => { + await createComponent({ + isEditing: true, + }); + + clickCancel(); + + await nextTick(); + + expect(confirmAction).not.toHaveBeenCalled(); + expect(findMarkdownField().exists()).toBe(false); + }); + + it('prompts for confirmation when clicking cancel after changes', async () => { + await createComponent({ + isEditing: true, + }); + + editDescription('updated desc'); + + clickCancel(); + + await nextTick(); + + expect(confirmAction).toHaveBeenCalled(); + }); + + it('calls update widgets mutation', async () => { + await createComponent({ + isEditing: true, + }); + + editDescription('updated desc'); + + clickSave(); + + await waitForPromises(); + + expect(mutationSuccessHandler).toHaveBeenCalledWith({ + input: { + id: workItemId, + descriptionWidget: { + description: 'updated desc', + }, + }, + }); + }); + + it('tracks editing description', async () => { + await createComponent({ + isEditing: true, + markdownPreviewPath: '/preview', + }); + const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + + clickSave(); + + await waitForPromises(); + + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_description', { + category: TRACKING_CATEGORY_SHOW, + label: 'item_description', + property: 'type_Task', + }); + }); + + it('emits error when mutation returns error', async () => { + const error = 'eror'; + + await createComponent({ + isEditing: true, + mutationHandler: jest.fn().mockResolvedValue({ + data: { + workItemUpdateWidgets: { + workItem: {}, + errors: [error], + }, + }, + }), + }); + + editDescription('updated desc'); + + clickSave(); + + await waitForPromises(); + + expect(wrapper.emitted('error')).toEqual([[error]]); + }); + + it('emits error when mutation fails', async () => { + const error = 'eror'; + + await createComponent({ + isEditing: true, + mutationHandler: jest.fn().mockRejectedValue(new Error(error)), + }); + + editDescription('updated desc'); + + clickSave(); + + await waitForPromises(); + + expect(wrapper.emitted('error')).toEqual([[error]]); + }); + + it('autosaves description', async () => { + await createComponent({ + isEditing: true, + }); + + editDescription('updated desc'); + + expect(updateDraft).toHaveBeenCalled(); + }); + }); +}); |