diff options
Diffstat (limited to 'spec/frontend/design_management/pages')
5 files changed, 216 insertions, 236 deletions
diff --git a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap index 3881b2d7679..b80b7fdb43e 100644 --- a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap +++ b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap @@ -111,7 +111,7 @@ exports[`Design management index page designs renders designs list and header wi > <gl-button-stub category="primary" - class="gl-mr-3 js-select-all" + class="gl-mr-4 js-select-all" icon="" size="small" variant="link" diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap index 823294efc38..c849e4d4ed6 100644 --- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap +++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap @@ -32,6 +32,8 @@ exports[`Design management design index page renders design index 1`] = ` <div class="image-notes" > + <!----> + <h2 class="gl-font-weight-bold gl-mt-0" > @@ -57,11 +59,11 @@ exports[`Design management design index page renders design index 1`] = ` <design-discussion-stub data-testid="unresolved-discussion" - designid="test" + designid="gid::/gitlab/Design/1" discussion="[object Object]" discussionwithopenform="" markdownpreviewpath="/project-path/preview_markdown?target_type=Issue" - noteableid="design-id" + noteableid="gid::/gitlab/Design/1" /> <gl-button-stub @@ -92,7 +94,7 @@ exports[`Design management design index page renders design index 1`] = ` </p> <a - href="#" + href="https://docs.gitlab.com/ee/user/project/issues/design_management.html#resolve-design-threads" rel="noopener noreferrer" target="_blank" > @@ -105,11 +107,11 @@ exports[`Design management design index page renders design index 1`] = ` > <design-discussion-stub data-testid="resolved-discussion" - designid="test" + designid="gid::/gitlab/Design/1" discussion="[object Object]" discussionwithopenform="" markdownpreviewpath="/project-path/preview_markdown?target_type=Issue" - noteableid="design-id" + noteableid="gid::/gitlab/Design/1" /> </gl-collapse-stub> @@ -179,6 +181,8 @@ exports[`Design management design index page with error GlAlert is rendered in c <div class="image-notes" > + <!----> + <h2 class="gl-font-weight-bold gl-mt-0" > diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js index 369c8667f4d..d9f7146d258 100644 --- a/spec/frontend/design_management/pages/design/index_spec.js +++ b/spec/frontend/design_management/pages/design/index_spec.js @@ -7,24 +7,21 @@ import DesignIndex from '~/design_management/pages/design/index.vue'; import DesignSidebar from '~/design_management/components/design_sidebar.vue'; import DesignPresentation from '~/design_management/components/design_presentation.vue'; import createImageDiffNoteMutation from '~/design_management/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 updateActiveDiscussion from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql'; import { DESIGN_NOT_FOUND_ERROR, DESIGN_VERSION_NOT_EXIST_ERROR, } from '~/design_management/utils/error_messages'; -import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants'; +import { DESIGNS_ROUTE_NAME, DESIGN_ROUTE_NAME } from '~/design_management/router/constants'; import createRouter from '~/design_management/router'; import * as utils from '~/design_management/utils/design_management_utils'; import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants'; +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'; jest.mock('~/flash'); -jest.mock('mousetrap', () => ({ - bind: jest.fn(), - unbind: jest.fn(), -})); const focusInput = jest.fn(); @@ -34,6 +31,12 @@ const DesignReplyForm = { focusInput, }, }; +const mockDesignNoDiscussions = { + ...design, + discussions: { + nodes: [], + }, +}; const localVue = createLocalVue(); localVue.use(VueRouter); @@ -75,7 +78,7 @@ describe('Design management design index page', () => { const findSidebar = () => wrapper.find(DesignSidebar); const findDesignPresentation = () => wrapper.find(DesignPresentation); - function createComponent(loading = false, data = {}) { + function createComponent({ loading = false } = {}, { data = {}, intialRouteOptions = {} } = {}) { const $apollo = { queries: { design: { @@ -87,6 +90,8 @@ describe('Design management design index page', () => { router = createRouter(); + router.push({ name: DESIGN_ROUTE_NAME, params: { id: design.id }, ...intialRouteOptions }); + wrapper = shallowMount(DesignIndex, { propsData: { id: '1' }, mocks: { $apollo }, @@ -126,29 +131,28 @@ describe('Design management design index page', () => { }, }; jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockEl); - createComponent(true); + createComponent({ loading: 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); + createComponent({ loading: true }); expect(wrapper.element).toMatchSnapshot(); }); it('renders design index', () => { - createComponent(false, { design }); + createComponent({ loading: false }, { data: { design } }); expect(wrapper.element).toMatchSnapshot(); expect(wrapper.find(GlAlert).exists()).toBe(false); }); it('passes correct props to sidebar component', () => { - createComponent(false, { design }); + createComponent({ loading: false }, { data: { design } }); expect(findSidebar().props()).toEqual({ design, @@ -158,14 +162,14 @@ describe('Design management design index page', () => { }); it('opens a new discussion form', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], + createComponent( + { loading: false }, + { + data: { + design, }, }, - }); + ); findDesignPresentation().vm.$emit('openCommentForm', { x: 0, y: 0 }); @@ -175,15 +179,15 @@ describe('Design management design index page', () => { }); it('keeps new discussion form focused', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], + createComponent( + { loading: false }, + { + data: { + design, + annotationCoordinates, }, }, - annotationCoordinates, - }); + ); findDesignPresentation().vm.$emit('openCommentForm', { x: 10, y: 10 }); @@ -191,18 +195,18 @@ describe('Design management design index page', () => { }); it('sends a mutation on submitting form and closes form', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], + createComponent( + { loading: false }, + { + data: { + design, + annotationCoordinates, + comment: newComment, }, }, - annotationCoordinates, - comment: newComment, - }); + ); - findDiscussionForm().vm.$emit('submitForm'); + findDiscussionForm().vm.$emit('submit-form'); expect(mutate).toHaveBeenCalledWith(createDiscussionMutationVariables); return wrapper.vm @@ -216,18 +220,18 @@ describe('Design management design index page', () => { }); it('closes the form and clears the comment on canceling form', () => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], + createComponent( + { loading: false }, + { + data: { + design, + annotationCoordinates, + comment: newComment, }, }, - annotationCoordinates, - comment: newComment, - }); + ); - findDiscussionForm().vm.$emit('cancelForm'); + findDiscussionForm().vm.$emit('cancel-form'); expect(wrapper.vm.comment).toBe(''); @@ -238,15 +242,15 @@ describe('Design management design index page', () => { describe('with error', () => { beforeEach(() => { - createComponent(false, { - design: { - ...design, - discussions: { - nodes: [], + createComponent( + { loading: false }, + { + data: { + design: mockDesignNoDiscussions, + errorMessage: 'woops', }, }, - errorMessage: 'woops', - }); + ); }); it('GlAlert is rendered in correct position with correct content', () => { @@ -257,7 +261,7 @@ describe('Design management design index page', () => { describe('onDesignQueryResult', () => { describe('with no designs', () => { it('redirects to /designs', () => { - createComponent(true); + createComponent({ loading: true }); router.push = jest.fn(); wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false }); @@ -272,7 +276,7 @@ describe('Design management design index page', () => { describe('when no design exists for given version', () => { it('redirects to /designs', () => { - createComponent(true); + createComponent({ loading: true }); wrapper.setData({ allVersions: mockAllVersions, }); @@ -291,4 +295,24 @@ describe('Design management design index page', () => { }); }); }); + + describe('when hash present in current route', () => { + it('calls updateActiveDiscussion mutation', () => { + createComponent( + { loading: false }, + { + data: { + design, + }, + intialRouteOptions: { hash: '#note_123' }, + }, + ); + + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith({ + mutation: updateActiveDiscussion, + variables: { id: 'gid://gitlab/DiffNote/123', source: 'url' }, + }); + }); + }); }); diff --git a/spec/frontend/design_management/pages/index_apollo_spec.js b/spec/frontend/design_management/pages/index_apollo_spec.js deleted file mode 100644 index 3ea711c2cfa..00000000000 --- a/spec/frontend/design_management/pages/index_apollo_spec.js +++ /dev/null @@ -1,162 +0,0 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { createMockClient } from 'mock-apollo-client'; -import VueApollo from 'vue-apollo'; -import VueRouter from 'vue-router'; -import VueDraggable from 'vuedraggable'; -import { InMemoryCache } from 'apollo-cache-inmemory'; -import Design from '~/design_management/components/list/item.vue'; -import createRouter from '~/design_management/router'; -import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql'; -import permissionsQuery from '~/design_management/graphql/queries/design_permissions.query.graphql'; -import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; -import Index from '~/design_management/pages/index.vue'; -import { - designListQueryResponse, - permissionsQueryResponse, - moveDesignMutationResponse, - reorderedDesigns, - moveDesignMutationResponseWithErrors, -} from '../mock_data/apollo_mock'; - -jest.mock('~/flash'); - -const localVue = createLocalVue(); -localVue.use(VueApollo); - -const router = createRouter(); -localVue.use(VueRouter); - -const designToMove = { - __typename: 'Design', - id: '2', - event: 'NONE', - filename: 'fox_2.jpg', - notesCount: 2, - image: 'image-2', - imageV432x230: 'image-2', -}; - -describe('Design management index page with Apollo mock', () => { - let wrapper; - let mockClient; - let apolloProvider; - let moveDesignHandler; - - async function moveDesigns(localWrapper) { - await jest.runOnlyPendingTimers(); - await localWrapper.vm.$nextTick(); - - localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns); - localWrapper.find(VueDraggable).vm.$emit('change', { - moved: { - newIndex: 0, - element: designToMove, - }, - }); - } - - const fragmentMatcher = { match: () => true }; - - const cache = new InMemoryCache({ - fragmentMatcher, - addTypename: false, - }); - - const findDesigns = () => wrapper.findAll(Design); - - function createComponent({ - moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse), - }) { - mockClient = createMockClient({ cache }); - - mockClient.setRequestHandler( - getDesignListQuery, - jest.fn().mockResolvedValue(designListQueryResponse), - ); - - mockClient.setRequestHandler( - permissionsQuery, - jest.fn().mockResolvedValue(permissionsQueryResponse), - ); - - moveDesignHandler = moveHandler; - - mockClient.setRequestHandler(moveDesignMutation, moveDesignHandler); - - apolloProvider = new VueApollo({ - defaultClient: mockClient, - }); - - wrapper = shallowMount(Index, { - localVue, - apolloProvider, - router, - stubs: { VueDraggable }, - }); - } - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - mockClient = null; - apolloProvider = null; - }); - - it('has a design with id 1 as a first one', async () => { - createComponent({}); - - await jest.runOnlyPendingTimers(); - await wrapper.vm.$nextTick(); - - expect(findDesigns()).toHaveLength(3); - expect( - findDesigns() - .at(0) - .props('id'), - ).toBe('1'); - }); - - it('calls a mutation with correct parameters and reorders designs', async () => { - createComponent({}); - - await moveDesigns(wrapper); - - expect(moveDesignHandler).toHaveBeenCalled(); - - await wrapper.vm.$nextTick(); - - expect( - findDesigns() - .at(0) - .props('id'), - ).toBe('2'); - }); - - it('displays flash if mutation had a recoverable error', async () => { - createComponent({ - moveHandler: jest.fn().mockResolvedValue(moveDesignMutationResponseWithErrors), - }); - - await moveDesigns(wrapper); - - await wrapper.vm.$nextTick(); - - expect(createFlash).toHaveBeenCalledWith('Houston, we have a problem'); - }); - - it('displays flash if mutation had a non-recoverable error', async () => { - createComponent({ - moveHandler: jest.fn().mockRejectedValue('Error'), - }); - - await moveDesigns(wrapper); - - await jest.runOnlyPendingTimers(); - await wrapper.vm.$nextTick(); - - expect(createFlash).toHaveBeenCalledWith( - 'Something went wrong when reordering designs. Please try again', - ); - }); -}); diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js index 093fa155d2e..661717d29a3 100644 --- a/spec/frontend/design_management/pages/index_spec.js +++ b/spec/frontend/design_management/pages/index_spec.js @@ -1,13 +1,15 @@ import { createLocalVue, shallowMount } from '@vue/test-utils'; -import { ApolloMutation } from 'vue-apollo'; +import VueApollo, { ApolloMutation } from 'vue-apollo'; import VueDraggable from 'vuedraggable'; import VueRouter from 'vue-router'; import { GlEmptyState } from '@gitlab/ui'; +import createMockApollo from 'jest/helpers/mock_apollo_helper'; import Index from '~/design_management/pages/index.vue'; import uploadDesignQuery from '~/design_management/graphql/mutations/upload_design.mutation.graphql'; import DesignDestroyer from '~/design_management/components/design_destroyer.vue'; import DesignDropzone from '~/design_management/components/upload/design_dropzone.vue'; import DeleteButton from '~/design_management/components/delete_button.vue'; +import Design from '~/design_management/components/list/item.vue'; import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants'; import { EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE, @@ -17,6 +19,16 @@ import { deprecatedCreateFlash as createFlash } from '~/flash'; import createRouter from '~/design_management/router'; import * as utils from '~/design_management/utils/design_management_utils'; import { DESIGN_DETAIL_LAYOUT_CLASSLIST } from '~/design_management/constants'; +import { + designListQueryResponse, + permissionsQueryResponse, + moveDesignMutationResponse, + reorderedDesigns, + moveDesignMutationResponseWithErrors, +} from '../mock_data/apollo_mock'; +import getDesignListQuery from '~/design_management/graphql/queries/get_design_list.query.graphql'; +import permissionsQuery from '~/design_management/graphql/queries/design_permissions.query.graphql'; +import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql'; jest.mock('~/flash.js'); const mockPageEl = { @@ -61,9 +73,21 @@ const mockVersion = { id: 'gid://gitlab/DesignManagement::Version/1', }; +const designToMove = { + __typename: 'Design', + id: '2', + event: 'NONE', + filename: 'fox_2.jpg', + notesCount: 2, + image: 'image-2', + imageV432x230: 'image-2', +}; + describe('Design management index page', () => { let mutate; let wrapper; + let fakeApollo; + let moveDesignHandler; const findDesignCheckboxes = () => wrapper.findAll('.design-checkbox'); const findSelectAllButton = () => wrapper.find('.js-select-all'); @@ -74,6 +98,20 @@ describe('Design management index page', () => { const findDropzoneWrapper = () => wrapper.find('[data-testid="design-dropzone-wrapper"]'); const findFirstDropzoneWithDesign = () => wrapper.findAll(DesignDropzone).at(1); const findDesignsWrapper = () => wrapper.find('[data-testid="designs-root"]'); + const findDesigns = () => wrapper.findAll(Design); + + async function moveDesigns(localWrapper) { + await jest.runOnlyPendingTimers(); + await localWrapper.vm.$nextTick(); + + localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns); + localWrapper.find(VueDraggable).vm.$emit('change', { + moved: { + newIndex: 0, + element: designToMove, + }, + }); + } function createComponent({ loading = false, @@ -118,8 +156,30 @@ describe('Design management index page', () => { }); } + function createComponentWithApollo({ + moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse), + }) { + localVue.use(VueApollo); + moveDesignHandler = moveHandler; + + const requestHandlers = [ + [getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)], + [permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)], + [moveDesignMutation, moveDesignHandler], + ]; + + fakeApollo = createMockApollo(requestHandlers); + wrapper = shallowMount(Index, { + localVue, + apolloProvider: fakeApollo, + router, + stubs: { VueDraggable }, + }); + } + afterEach(() => { wrapper.destroy(); + wrapper = null; }); describe('designs', () => { @@ -478,16 +538,15 @@ describe('Design management index page', () => { describe('on non-latest version', () => { beforeEach(() => { createComponent({ designs: mockDesigns, allVersions: [mockVersion] }); + }); - router.replace({ + it('does not render design checkboxes', async () => { + await router.replace({ name: DESIGNS_ROUTE_NAME, query: { version: '2', }, }); - }); - - it('does not render design checkboxes', () => { expect(findDesignCheckboxes()).toHaveLength(0); }); @@ -514,13 +573,6 @@ describe('Design management index page', () => { files: [{ name: 'image.png', type: 'image/png' }], getData: () => 'test.png', }; - - router.replace({ - name: DESIGNS_ROUTE_NAME, - query: { - version: '2', - }, - }); }); it('does not call paste event if designs wrapper is not hovered', () => { @@ -587,7 +639,69 @@ describe('Design management index page', () => { }); createComponent(true); - expect(scrollIntoViewMock).toHaveBeenCalled(); + return wrapper.vm.$nextTick().then(() => { + expect(scrollIntoViewMock).toHaveBeenCalled(); + }); + }); + }); + + describe('with mocked Apollo client', () => { + it('has a design with id 1 as a first one', async () => { + createComponentWithApollo({}); + + await jest.runOnlyPendingTimers(); + await wrapper.vm.$nextTick(); + + expect(findDesigns()).toHaveLength(3); + expect( + findDesigns() + .at(0) + .props('id'), + ).toBe('1'); + }); + + it('calls a mutation with correct parameters and reorders designs', async () => { + createComponentWithApollo({}); + + await moveDesigns(wrapper); + + expect(moveDesignHandler).toHaveBeenCalled(); + + await wrapper.vm.$nextTick(); + + expect( + findDesigns() + .at(0) + .props('id'), + ).toBe('2'); + }); + + it('displays flash if mutation had a recoverable error', async () => { + createComponentWithApollo({ + moveHandler: jest.fn().mockResolvedValue(moveDesignMutationResponseWithErrors), + }); + + await moveDesigns(wrapper); + + await wrapper.vm.$nextTick(); + + expect(createFlash).toHaveBeenCalledWith('Houston, we have a problem'); + }); + + it('displays flash if mutation had a non-recoverable error', async () => { + createComponentWithApollo({ + moveHandler: jest.fn().mockRejectedValue('Error'), + }); + + await moveDesigns(wrapper); + + await wrapper.vm.$nextTick(); // kick off the DOM update + await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises) + await wrapper.vm.$nextTick(); // kick off the DOM update for flash + + expect(createFlash).toHaveBeenCalledWith( + 'Something went wrong when reordering designs. Please try again', + ); }); }); }); |