diff options
Diffstat (limited to 'spec/frontend/snippets/components/snippet_blob_view_spec.js')
-rw-r--r-- | spec/frontend/snippets/components/snippet_blob_view_spec.js | 222 |
1 files changed, 132 insertions, 90 deletions
diff --git a/spec/frontend/snippets/components/snippet_blob_view_spec.js b/spec/frontend/snippets/components/snippet_blob_view_spec.js index c7ff8c21d80..05ff64c2296 100644 --- a/spec/frontend/snippets/components/snippet_blob_view_spec.js +++ b/spec/frontend/snippets/components/snippet_blob_view_spec.js @@ -1,5 +1,6 @@ -import { mount } from '@vue/test-utils'; -import { nextTick } from 'vue'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { shallowMount } from '@vue/test-utils'; import { Blob as BlobMock, SimpleViewerMock, @@ -7,6 +8,7 @@ import { RichBlobContentMock, SimpleBlobContentMock, } from 'jest/blob/components/mock_data'; +import GetBlobContent from 'shared_queries/snippet/snippet_blob_content.query.graphql'; import BlobContent from '~/blob/components/blob_content.vue'; import BlobHeader from '~/blob/components/blob_header.vue'; import { @@ -17,9 +19,13 @@ import { import SnippetBlobView from '~/snippets/components/snippet_blob_view.vue'; import { VISIBILITY_LEVEL_PUBLIC_STRING } from '~/visibility_level/constants'; import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; describe('Blob Embeddable', () => { let wrapper; + let requestHandlers; + const snippet = { id: 'gid://foo.bar/snippet', webUrl: 'https://foo.bar', @@ -29,23 +35,47 @@ describe('Blob Embeddable', () => { activeViewerType: SimpleViewerMock.type, }; + const mockDefaultHandler = ({ path, nodes } = { path: BlobMock.path }) => { + const renderedNodes = nodes || [ + { __typename: 'Blob', path, richData: 'richData', plainData: 'plainData' }, + ]; + + return jest.fn().mockResolvedValue({ + data: { + snippets: { + __typename: 'Snippet', + id: '1', + nodes: [ + { + __typename: 'Snippet', + id: '2', + blobs: { + __typename: 'Blob', + hasUnretrievableBlobs: false, + nodes: renderedNodes, + }, + }, + ], + }, + }, + }); + }; + + const createMockApolloProvider = (handler) => { + Vue.use(VueApollo); + + requestHandlers = handler; + return createMockApollo([[GetBlobContent, requestHandlers]]); + }; + function createComponent({ snippetProps = {}, data = dataMock, blob = BlobMock, - contentLoading = false, + handler = mockDefaultHandler(), } = {}) { - const $apollo = { - queries: { - blobContent: { - loading: contentLoading, - refetch: jest.fn(), - skip: true, - }, - }, - }; - - wrapper = mount(SnippetBlobView, { + wrapper = shallowMount(SnippetBlobView, { + apolloProvider: createMockApolloProvider(handler), propsData: { snippet: { ...snippet, @@ -58,45 +88,56 @@ describe('Blob Embeddable', () => { ...data, }; }, - mocks: { $apollo }, + stubs: { + BlobHeader, + BlobContent, + }, }); } - afterEach(() => { - wrapper.destroy(); - }); + const findBlobHeader = () => wrapper.findComponent(BlobHeader); + const findBlobContent = () => wrapper.findComponent(BlobContent); + const findSimpleViewer = () => wrapper.findComponent(SimpleViewer); + const findRichViewer = () => wrapper.findComponent(RichViewer); describe('rendering', () => { it('renders correct components', () => { createComponent(); - expect(wrapper.findComponent(BlobHeader).exists()).toBe(true); - expect(wrapper.findComponent(BlobContent).exists()).toBe(true); + expect(findBlobHeader().exists()).toBe(true); + expect(findBlobContent().exists()).toBe(true); }); - it('sets simple viewer correctly', () => { + it('sets simple viewer correctly', async () => { createComponent(); - expect(wrapper.findComponent(SimpleViewer).exists()).toBe(true); + await waitForPromises(); + + expect(findSimpleViewer().exists()).toBe(true); }); - it('sets rich viewer correctly', () => { + it('sets rich viewer correctly', async () => { const data = { ...dataMock, activeViewerType: RichViewerMock.type }; createComponent({ data, }); - expect(wrapper.findComponent(RichViewer).exists()).toBe(true); + await waitForPromises(); + expect(findRichViewer().exists()).toBe(true); }); it('correctly switches viewer type', async () => { createComponent(); - expect(wrapper.findComponent(SimpleViewer).exists()).toBe(true); + await waitForPromises(); + + expect(findSimpleViewer().exists()).toBe(true); - wrapper.vm.switchViewer(RichViewerMock.type); + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE, RichViewerMock.type); + await waitForPromises(); - await nextTick(); - expect(wrapper.findComponent(RichViewer).exists()).toBe(true); - await wrapper.vm.switchViewer(SimpleViewerMock.type); + expect(findRichViewer().exists()).toBe(true); - expect(wrapper.findComponent(SimpleViewer).exists()).toBe(true); + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE, SimpleViewerMock.type); + await waitForPromises(); + + expect(findSimpleViewer().exists()).toBe(true); }); it('passes information about render error down to blob header', () => { @@ -110,7 +151,7 @@ describe('Blob Embeddable', () => { }, }); - expect(wrapper.findComponent(BlobHeader).props('hasRenderError')).toBe(true); + expect(findBlobHeader().props('hasRenderError')).toBe(true); }); describe('bob content in multi-file scenario', () => { @@ -123,47 +164,38 @@ describe('Blob Embeddable', () => { richData: 'Another Rich Foo', }; + const MixedSimpleBlobContentMock = { + ...SimpleBlobContentMock, + richData: '<h1>Rich</h1>', + }; + + const MixedRichBlobContentMock = { + ...RichBlobContentMock, + plainData: 'Plain', + }; + it.each` - snippetBlobs | description | currentBlob | expectedContent - ${[SimpleBlobContentMock]} | ${'one existing textual blob'} | ${SimpleBlobContentMock} | ${SimpleBlobContentMock.plainData} - ${[RichBlobContentMock]} | ${'one existing rich blob'} | ${RichBlobContentMock} | ${RichBlobContentMock.richData} - ${[SimpleBlobContentMock, RichBlobContentMock]} | ${'mixed blobs with current textual blob'} | ${SimpleBlobContentMock} | ${SimpleBlobContentMock.plainData} - ${[SimpleBlobContentMock, RichBlobContentMock]} | ${'mixed blobs with current rich blob'} | ${RichBlobContentMock} | ${RichBlobContentMock.richData} - ${[SimpleBlobContentMock, SimpleBlobContentMock2]} | ${'textual blobs with current textual blob'} | ${SimpleBlobContentMock} | ${SimpleBlobContentMock.plainData} - ${[RichBlobContentMock, RichBlobContentMock2]} | ${'rich blobs with current rich blob'} | ${RichBlobContentMock} | ${RichBlobContentMock.richData} + snippetBlobs | description | currentBlob | expectedContent | activeViewerType + ${[SimpleBlobContentMock]} | ${'one existing textual blob'} | ${SimpleBlobContentMock} | ${SimpleBlobContentMock.plainData} | ${SimpleViewerMock.type} + ${[RichBlobContentMock]} | ${'one existing rich blob'} | ${RichBlobContentMock} | ${RichBlobContentMock.richData} | ${RichViewerMock.type} + ${[SimpleBlobContentMock, MixedRichBlobContentMock]} | ${'mixed blobs with current textual blob'} | ${SimpleBlobContentMock} | ${SimpleBlobContentMock.plainData} | ${SimpleViewerMock.type} + ${[MixedSimpleBlobContentMock, RichBlobContentMock]} | ${'mixed blobs with current rich blob'} | ${RichBlobContentMock} | ${RichBlobContentMock.richData} | ${RichViewerMock.type} + ${[SimpleBlobContentMock, SimpleBlobContentMock2]} | ${'textual blobs with current textual blob'} | ${SimpleBlobContentMock} | ${SimpleBlobContentMock.plainData} | ${SimpleViewerMock.type} + ${[RichBlobContentMock, RichBlobContentMock2]} | ${'rich blobs with current rich blob'} | ${RichBlobContentMock} | ${RichBlobContentMock.richData} | ${RichViewerMock.type} `( 'renders correct content for $description', - async ({ snippetBlobs, currentBlob, expectedContent }) => { - const apolloData = { - snippets: { - nodes: [ - { - blobs: { - nodes: snippetBlobs, - }, - }, - ], - }, - }; + async ({ snippetBlobs, currentBlob, expectedContent, activeViewerType }) => { createComponent({ + handler: mockDefaultHandler({ path: currentBlob.path, nodes: snippetBlobs }), + data: { activeViewerType }, blob: { ...BlobMock, path: currentBlob.path, }, }); + await waitForPromises(); - // mimic apollo's update - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - blobContent: wrapper.vm.onContentUpdate(apolloData), - }); - - await nextTick(); - - const findContent = () => wrapper.findComponent(BlobContent); - - expect(findContent().props('content')).toBe(expectedContent); + expect(findBlobContent().props('content')).toBe(expectedContent); }, ); }); @@ -178,28 +210,32 @@ describe('Blob Embeddable', () => { window.location.hash = '#LC2'; }); - it('renders simple viewer by default', () => { + it('renders simple viewer by default', async () => { createComponent({ data: {}, }); + await waitForPromises(); - expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type); - expect(wrapper.findComponent(SimpleViewer).exists()).toBe(true); + expect(findBlobHeader().props('activeViewerType')).toBe(SimpleViewerMock.type); + expect(findSimpleViewer().exists()).toBe(true); }); describe('switchViewer()', () => { it('switches to the passed viewer', async () => { createComponent(); + await waitForPromises(); + + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE, RichViewerMock.type); + await waitForPromises(); - wrapper.vm.switchViewer(RichViewerMock.type); + expect(findBlobHeader().props('activeViewerType')).toBe(RichViewerMock.type); + expect(findRichViewer().exists()).toBe(true); - await nextTick(); - expect(wrapper.vm.activeViewerType).toBe(RichViewerMock.type); - expect(wrapper.findComponent(RichViewer).exists()).toBe(true); + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE, SimpleViewerMock.type); + await waitForPromises(); - await wrapper.vm.switchViewer(SimpleViewerMock.type); - expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type); - expect(wrapper.findComponent(SimpleViewer).exists()).toBe(true); + expect(findBlobHeader().props('activeViewerType')).toBe(SimpleViewerMock.type); + expect(findSimpleViewer().exists()).toBe(true); }); }); }); @@ -209,28 +245,32 @@ describe('Blob Embeddable', () => { window.location.hash = '#last-headline'; }); - it('renders rich viewer by default', () => { + it('renders rich viewer by default', async () => { createComponent({ data: {}, }); + await waitForPromises(); - expect(wrapper.vm.activeViewerType).toBe(RichViewerMock.type); - expect(wrapper.findComponent(RichViewer).exists()).toBe(true); + expect(findBlobHeader().props('activeViewerType')).toBe(RichViewerMock.type); + expect(findRichViewer().exists()).toBe(true); }); describe('switchViewer()', () => { it('switches to the passed viewer', async () => { createComponent(); + await waitForPromises(); - wrapper.vm.switchViewer(SimpleViewerMock.type); + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE, SimpleViewerMock.type); + await waitForPromises(); - await nextTick(); - expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type); - expect(wrapper.findComponent(SimpleViewer).exists()).toBe(true); + expect(findBlobHeader().props('activeViewerType')).toBe(SimpleViewerMock.type); + expect(findSimpleViewer().exists()).toBe(true); - await wrapper.vm.switchViewer(RichViewerMock.type); - expect(wrapper.vm.activeViewerType).toBe(RichViewerMock.type); - expect(wrapper.findComponent(RichViewer).exists()).toBe(true); + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE, RichViewerMock.type); + await waitForPromises(); + + expect(findBlobHeader().props('activeViewerType')).toBe(RichViewerMock.type); + expect(findRichViewer().exists()).toBe(true); }); }); }); @@ -239,19 +279,21 @@ describe('Blob Embeddable', () => { describe('functionality', () => { describe('render error', () => { - const findContentEl = () => wrapper.findComponent(BlobContent); - it('correctly sets blob on the blob-content-error component', () => { createComponent(); - expect(findContentEl().props('blob')).toEqual(BlobMock); + expect(findBlobContent().props('blob')).toEqual(BlobMock); }); - it(`refetches blob content on ${BLOB_RENDER_EVENT_LOAD} event`, () => { + it(`refetches blob content on ${BLOB_RENDER_EVENT_LOAD} event`, async () => { createComponent(); + await waitForPromises(); + + expect(requestHandlers).toHaveBeenCalledTimes(1); + + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_LOAD); + await waitForPromises(); - expect(wrapper.vm.$apollo.queries.blobContent.refetch).not.toHaveBeenCalled(); - findContentEl().vm.$emit(BLOB_RENDER_EVENT_LOAD); - expect(wrapper.vm.$apollo.queries.blobContent.refetch).toHaveBeenCalledTimes(1); + expect(requestHandlers).toHaveBeenCalledTimes(2); }); it(`sets '${SimpleViewerMock.type}' as active on ${BLOB_RENDER_EVENT_SHOW_SOURCE} event`, () => { @@ -261,7 +303,7 @@ describe('Blob Embeddable', () => { }, }); - findContentEl().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE); + findBlobContent().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE); expect(wrapper.vm.activeViewerType).toEqual(SimpleViewerMock.type); }); }); |