diff options
Diffstat (limited to 'spec/frontend/design_management_new/pages/design/index_spec.js')
-rw-r--r-- | spec/frontend/design_management_new/pages/design/index_spec.js | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/spec/frontend/design_management_new/pages/design/index_spec.js b/spec/frontend/design_management_new/pages/design/index_spec.js new file mode 100644 index 00000000000..3822b0b3b71 --- /dev/null +++ b/spec/frontend/design_management_new/pages/design/index_spec.js @@ -0,0 +1,294 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import VueRouter from 'vue-router'; +import { GlAlert } from '@gitlab/ui'; +import { ApolloMutation } from 'vue-apollo'; +import createFlash from '~/flash'; +import DesignIndex from '~/design_management_new/pages/design/index.vue'; +import DesignSidebar from '~/design_management_new/components/design_sidebar.vue'; +import DesignPresentation from '~/design_management_new/components/design_presentation.vue'; +import createImageDiffNoteMutation from '~/design_management_new/graphql/mutations/create_image_diff_note.mutation.graphql'; +import design from '../../mock_data/design'; +import mockResponseWithDesigns from '../../mock_data/designs'; +import mockResponseNoDesigns from '../../mock_data/no_designs'; +import mockAllVersions from '../../mock_data/all_versions'; +import { + DESIGN_NOT_FOUND_ERROR, + DESIGN_VERSION_NOT_EXIST_ERROR, +} from '~/design_management_new/utils/error_messages'; +import { DESIGNS_ROUTE_NAME } from '~/design_management_new/router/constants'; +import createRouter from '~/design_management_new/router'; +import * as utils from '~/design_management_new/utils/design_management_utils'; +import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management_new/constants'; + +jest.mock('~/flash'); +jest.mock('mousetrap', () => ({ + bind: jest.fn(), + unbind: jest.fn(), +})); + +const focusInput = jest.fn(); + +const DesignReplyForm = { + template: '<div><textarea ref="textarea"></textarea></div>', + methods: { + focusInput, + }, +}; + +const localVue = createLocalVue(); +localVue.use(VueRouter); + +describe('Design management design index page', () => { + let wrapper; + let router; + + const newComment = 'new comment'; + const annotationCoordinates = { + x: 10, + y: 10, + width: 100, + height: 100, + }; + const createDiscussionMutationVariables = { + mutation: createImageDiffNoteMutation, + update: expect.anything(), + variables: { + input: { + body: newComment, + noteableId: design.id, + position: { + headSha: 'headSha', + baseSha: 'baseSha', + startSha: 'startSha', + paths: { + newPath: 'full-design-path', + }, + ...annotationCoordinates, + }, + }, + }, + }; + + const mutate = jest.fn().mockResolvedValue(); + + const findDiscussionForm = () => wrapper.find(DesignReplyForm); + const findSidebar = () => wrapper.find(DesignSidebar); + const findDesignPresentation = () => wrapper.find(DesignPresentation); + + function createComponent(loading = false, data = {}) { + const $apollo = { + queries: { + design: { + loading, + }, + }, + mutate, + }; + + router = createRouter(); + + wrapper = shallowMount(DesignIndex, { + propsData: { id: '1' }, + mocks: { $apollo }, + stubs: { + ApolloMutation, + DesignSidebar, + DesignReplyForm, + }, + provide: { + issueIid: '1', + projectPath: 'project-path', + }, + data() { + return { + activeDiscussion: { + id: null, + source: null, + }, + ...data, + }; + }, + localVue, + router, + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when navigating', () => { + it('applies fullscreen layout', () => { + const mockEl = { + classList: { + add: jest.fn(), + remove: jest.fn(), + }, + }; + jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl); + createComponent(true); + + wrapper.vm.$router.push('/designs/test'); + expect(mockEl.classList.add).toHaveBeenCalledTimes(1); + expect(mockEl.classList.add).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST); + }); + }); + + it('sets loading state', () => { + createComponent(true); + + expect(wrapper.element).toMatchSnapshot(); + }); + + it('renders design index', () => { + createComponent(false, { design }); + + expect(wrapper.element).toMatchSnapshot(); + expect(wrapper.find(GlAlert).exists()).toBe(false); + }); + + it('passes correct props to sidebar component', () => { + createComponent(false, { design }); + + expect(findSidebar().props()).toEqual({ + design, + markdownPreviewPath: '/project-path/preview_markdown?target_type=Issue', + resolvedDiscussionsExpanded: false, + }); + }); + + it('opens a new discussion form', () => { + createComponent(false, { + design: { + ...design, + discussions: { + nodes: [], + }, + }, + }); + + findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 }); + + return wrapper.vm.$nextTick().then(() => { + expect(findDiscussionForm().exists()).toBe(true); + }); + }); + + it('keeps new discussion form focused', () => { + createComponent(false, { + design: { + ...design, + discussions: { + nodes: [], + }, + }, + annotationCoordinates, + }); + + findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 }); + + expect(focusInput).toHaveBeenCalled(); + }); + + it('sends a mutation on submitting form and closes form', () => { + createComponent(false, { + design: { + ...design, + discussions: { + nodes: [], + }, + }, + annotationCoordinates, + comment: newComment, + }); + + findDiscussionForm().vm.$emit('submitForm'); + expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables); + + return wrapper.vm + .$nextTick() + .then(() => { + return mutate({ variables: createDiscussionMutationVariables }); + }) + .then(() => { + expect(findDiscussionForm().exists()).toBe(false); + }); + }); + + it('closes the form and clears the comment on canceling form', () => { + createComponent(false, { + design: { + ...design, + discussions: { + nodes: [], + }, + }, + annotationCoordinates, + comment: newComment, + }); + + findDiscussionForm().vm.$emit('cancelForm'); + + expect(wrapper.vm.comment).toBe(''); + + return wrapper.vm.$nextTick().then(() => { + expect(findDiscussionForm().exists()).toBe(false); + }); + }); + + describe('with error', () => { + beforeEach(() => { + createComponent(false, { + design: { + ...design, + discussions: { + nodes: [], + }, + }, + errorMessage: 'woops', + }); + }); + + it('GlAlert is rendered in correct position with correct content', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + }); + + describe('onDesignQueryResult', () => { + describe('with no designs', () => { + it('redirects to /designs', () => { + createComponent(true); + router.push = jest.fn(); + + wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false }); + return wrapper.vm.$nextTick().then(() => { + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith(DESIGN_NOT_FOUND_ERROR); + expect(router.push).toHaveBeenCalledTimes(1); + expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME }); + }); + }); + }); + + describe('when no design exists for given version', () => { + it('redirects to /designs', () => { + createComponent(true); + wrapper.setData({ + allVersions: mockAllVersions, + }); + + // attempt to query for a version of the design that doesn't exist + router.push({ query: { version: '999' } }); + router.push = jest.fn(); + + wrapper.vm.onDesignQueryResult({ data: mockResponseWithDesigns, loading: false }); + return wrapper.vm.$nextTick().then(() => { + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith(DESIGN_VERSION_NOT_EXIST_ERROR); + expect(router.push).toHaveBeenCalledTimes(1); + expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME }); + }); + }); + }); + }); +}); |