From 41fe97390ceddf945f3d967b8fdb3de4c66b7dea Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 18 Mar 2022 20:02:30 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-9-stable-ee --- .../components/blob_content_viewer_spec.js | 106 +++++++++++++++------ .../repository/components/blob_edit_spec.js | 100 ------------------- .../components/blob_viewers/audio_viewer_spec.js | 23 +++++ .../components/blob_viewers/csv_viewer_spec.js | 27 ++++++ .../blob_viewers/download_viewer_spec.js | 11 ++- .../components/blob_viewers/lfs_viewer_spec.js | 11 ++- .../components/blob_viewers/pdf_viewer_spec.js | 26 +++-- .../repository/components/breadcrumbs_spec.js | 14 +++ 8 files changed, 180 insertions(+), 138 deletions(-) delete mode 100644 spec/frontend/repository/components/blob_edit_spec.js create mode 100644 spec/frontend/repository/components/blob_viewers/audio_viewer_spec.js create mode 100644 spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js (limited to 'spec/frontend/repository/components') diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 109e5cef49b..96c03419dd6 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -1,5 +1,6 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { mount, shallowMount } from '@vue/test-utils'; +import Vuex from 'vuex'; import Vue, { nextTick } from 'vue'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; @@ -10,20 +11,26 @@ import BlobContent from '~/blob/components/blob_content.vue'; import BlobHeader from '~/blob/components/blob_header.vue'; import BlobButtonGroup from '~/repository/components/blob_button_group.vue'; import BlobContentViewer from '~/repository/components/blob_content_viewer.vue'; -import BlobEdit from '~/repository/components/blob_edit.vue'; +import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; import ForkSuggestion from '~/repository/components/fork_suggestion.vue'; import { loadViewer } from '~/repository/components/blob_viewers'; import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue'; import EmptyViewer from '~/repository/components/blob_viewers/empty_viewer.vue'; import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vue'; import blobInfoQuery from '~/repository/queries/blob_info.query.graphql'; +import userInfoQuery from '~/repository/queries/user_info.query.graphql'; +import applicationInfoQuery from '~/repository/queries/application_info.query.graphql'; +import CodeIntelligence from '~/code_navigation/components/app.vue'; import { redirectTo } from '~/lib/utils/url_utility'; import { isLoggedIn } from '~/lib/utils/common_utils'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import httpStatusCodes from '~/lib/utils/http_status'; import { simpleViewerMock, richViewerMock, projectMock, + userInfoMock, + applicationInfoMock, userPermissionsMock, propsMock, refMock, @@ -35,9 +42,14 @@ jest.mock('~/lib/utils/common_utils'); let wrapper; let mockResolver; +let userInfoMockResolver; +let applicationInfoMockResolver; const mockAxios = new MockAdapter(axios); +const createMockStore = () => + new Vuex.Store({ actions: { fetchData: jest.fn, setInitialData: jest.fn() } }); + const createComponent = async (mockData = {}, mountFn = shallowMount) => { Vue.use(VueApollo); @@ -71,10 +83,23 @@ const createComponent = async (mockData = {}, mountFn = shallowMount) => { data: { isBinary, project }, }); - const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]); + userInfoMockResolver = jest.fn().mockResolvedValue({ + data: { ...userInfoMock }, + }); + + applicationInfoMockResolver = jest.fn().mockResolvedValue({ + data: { ...applicationInfoMock }, + }); + + const fakeApollo = createMockApollo([ + [blobInfoQuery, mockResolver], + [userInfoQuery, userInfoMockResolver], + [applicationInfoQuery, applicationInfoMockResolver], + ]); wrapper = extendedWrapper( mountFn(BlobContentViewer, { + store: createMockStore(), apolloProvider: fakeApollo, propsData: propsMock, mixins: [{ data: () => ({ ref: refMock }) }], @@ -96,16 +121,21 @@ const createComponent = async (mockData = {}, mountFn = shallowMount) => { await waitForPromises(); }; +const execImmediately = (callback) => { + callback(); +}; + describe('Blob content viewer component', () => { const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findBlobHeader = () => wrapper.findComponent(BlobHeader); - const findBlobEdit = () => wrapper.findComponent(BlobEdit); - const findPipelineEditor = () => wrapper.findByTestId('pipeline-editor'); + const findWebIdeLink = () => wrapper.findComponent(WebIdeLink); const findBlobContent = () => wrapper.findComponent(BlobContent); const findBlobButtonGroup = () => wrapper.findComponent(BlobButtonGroup); const findForkSuggestion = () => wrapper.findComponent(ForkSuggestion); + const findCodeIntelligence = () => wrapper.findComponent(CodeIntelligence); beforeEach(() => { + jest.spyOn(window, 'requestIdleCallback').mockImplementation(execImmediately); isLoggedIn.mockReturnValue(true); }); @@ -219,6 +249,26 @@ describe('Blob content viewer component', () => { loadViewer.mockRestore(); }); + it('renders a CodeIntelligence component with the correct props', async () => { + loadViewer.mockReturnValue(SourceViewer); + + await createComponent(); + + expect(findCodeIntelligence().props()).toMatchObject({ + codeNavigationPath: simpleViewerMock.codeNavigationPath, + blobPath: simpleViewerMock.path, + pathPrefix: simpleViewerMock.projectBlobPathRoot, + }); + }); + + it('does not load a CodeIntelligence component when no viewers are loaded', async () => { + const url = 'some_file.js?format=json&viewer=rich'; + mockAxios.onGet(url).replyOnce(httpStatusCodes.INTERNAL_SERVER_ERROR); + await createComponent({ blob: { ...richViewerMock, fileType: 'unknown' } }); + + expect(findCodeIntelligence().exists()).toBe(false); + }); + it('does not render a BlobContent component if a Blob viewer is available', async () => { loadViewer.mockReturnValue(() => true); await createComponent({ blob: richViewerMock }); @@ -255,45 +305,43 @@ describe('Blob content viewer component', () => { describe('BlobHeader action slot', () => { const { ideEditPath, editBlobPath } = simpleViewerMock; - it('renders BlobHeaderEdit buttons in simple viewer', async () => { + it('renders WebIdeLink button in simple viewer', async () => { await createComponent({ inject: { BlobContent: true, BlobReplace: true } }, mount); - expect(findBlobEdit().props()).toMatchObject({ - editPath: editBlobPath, - webIdePath: ideEditPath, + expect(findWebIdeLink().props()).toMatchObject({ + editUrl: editBlobPath, + webIdeUrl: ideEditPath, showEditButton: true, + showGitpodButton: applicationInfoMock.gitpodEnabled, + gitpodEnabled: userInfoMock.currentUser.gitpodEnabled, + showPipelineEditorButton: true, + gitpodUrl: simpleViewerMock.gitpodBlobUrl, + pipelineEditorUrl: simpleViewerMock.pipelineEditorPath, + userPreferencesGitpodPath: userInfoMock.currentUser.preferencesGitpodPath, + userProfileEnableGitpodPath: userInfoMock.currentUser.profileEnableGitpodPath, }); }); - it('renders BlobHeaderEdit button in rich viewer', async () => { + it('renders WebIdeLink button in rich viewer', async () => { await createComponent({ blob: richViewerMock }, mount); - expect(findBlobEdit().props()).toMatchObject({ - editPath: editBlobPath, - webIdePath: ideEditPath, + expect(findWebIdeLink().props()).toMatchObject({ + editUrl: editBlobPath, + webIdeUrl: ideEditPath, showEditButton: true, }); }); - it('renders BlobHeaderEdit button for binary files', async () => { + it('renders WebIdeLink button for binary files', async () => { await createComponent({ blob: richViewerMock, isBinary: true }, mount); - expect(findBlobEdit().props()).toMatchObject({ - editPath: editBlobPath, - webIdePath: ideEditPath, + expect(findWebIdeLink().props()).toMatchObject({ + editUrl: editBlobPath, + webIdeUrl: ideEditPath, showEditButton: false, }); }); - it('renders Pipeline Editor button for .gitlab-ci files', async () => { - const pipelineEditorPath = 'some/path/.gitlab-ce'; - const blob = { ...simpleViewerMock, pipelineEditorPath }; - await createComponent({ blob, inject: { BlobContent: true, BlobReplace: true } }, mount); - - expect(findPipelineEditor().exists()).toBe(true); - expect(findPipelineEditor().attributes('href')).toBe(pipelineEditorPath); - }); - describe('blob header binary file', () => { it('passes the correct isBinary value when viewing a binary file', async () => { await createComponent({ blob: richViewerMock, isBinary: true }); @@ -318,7 +366,7 @@ describe('Blob content viewer component', () => { expect(findBlobHeader().props('hideViewerSwitcher')).toBe(true); expect(findBlobHeader().props('isBinary')).toBe(true); - expect(findBlobEdit().props('showEditButton')).toBe(false); + expect(findWebIdeLink().props('showEditButton')).toBe(false); }); }); @@ -401,12 +449,12 @@ describe('Blob content viewer component', () => { beforeEach(() => createComponent({}, mount)); it('simple edit redirects to the simple editor', () => { - findBlobEdit().vm.$emit('edit', 'simple'); + findWebIdeLink().vm.$emit('edit', 'simple'); expect(redirectTo).toHaveBeenCalledWith(simpleViewerMock.editBlobPath); }); it('IDE edit redirects to the IDE editor', () => { - findBlobEdit().vm.$emit('edit', 'ide'); + findWebIdeLink().vm.$emit('edit', 'ide'); expect(redirectTo).toHaveBeenCalledWith(simpleViewerMock.ideEditPath); }); @@ -435,7 +483,7 @@ describe('Blob content viewer component', () => { mount, ); - findBlobEdit().vm.$emit('edit', 'simple'); + findWebIdeLink().vm.$emit('edit', 'simple'); await nextTick(); expect(findForkSuggestion().exists()).toBe(showForkSuggestion); diff --git a/spec/frontend/repository/components/blob_edit_spec.js b/spec/frontend/repository/components/blob_edit_spec.js deleted file mode 100644 index e2de7bc2957..00000000000 --- a/spec/frontend/repository/components/blob_edit_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -import { GlButton } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import BlobEdit from '~/repository/components/blob_edit.vue'; -import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; - -const DEFAULT_PROPS = { - editPath: 'some_file.js/edit', - webIdePath: 'some_file.js/ide/edit', - showEditButton: true, - needsToFork: false, -}; - -describe('BlobEdit component', () => { - let wrapper; - - const createComponent = (consolidatedEditButton = false, props = {}) => { - wrapper = shallowMount(BlobEdit, { - propsData: { - ...DEFAULT_PROPS, - ...props, - }, - provide: { - glFeatures: { - consolidatedEditButton, - }, - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - const findButtons = () => wrapper.findAll(GlButton); - const findEditButton = () => wrapper.find('[data-testid="edit"]'); - const findWebIdeButton = () => wrapper.find('[data-testid="web-ide"]'); - const findWebIdeLink = () => wrapper.find(WebIdeLink); - - it('renders component', () => { - createComponent(); - - const { editPath, webIdePath } = DEFAULT_PROPS; - - expect(wrapper.props()).toMatchObject({ - editPath, - webIdePath, - }); - }); - - it('renders both buttons', () => { - createComponent(); - - expect(findButtons()).toHaveLength(2); - }); - - it('renders the Edit button', () => { - createComponent(); - - expect(findEditButton().text()).toBe('Edit'); - expect(findEditButton()).not.toBeDisabled(); - }); - - it('renders the Web IDE button', () => { - createComponent(); - - expect(findWebIdeButton().text()).toBe('Web IDE'); - expect(findWebIdeButton()).not.toBeDisabled(); - }); - - it('renders WebIdeLink component', () => { - createComponent(true); - - const { editPath: editUrl, webIdePath: webIdeUrl, needsToFork } = DEFAULT_PROPS; - - expect(findWebIdeLink().props()).toMatchObject({ - editUrl, - webIdeUrl, - isBlob: true, - showEditButton: true, - needsToFork, - }); - }); - - describe('Without Edit button', () => { - const showEditButton = false; - - it('renders WebIdeLink component without an edit button', () => { - createComponent(true, { showEditButton }); - - expect(findWebIdeLink().props()).toMatchObject({ showEditButton }); - }); - - it('does not render an Edit button', () => { - createComponent(false, { showEditButton }); - - expect(findEditButton().exists()).toBe(false); - }); - }); -}); diff --git a/spec/frontend/repository/components/blob_viewers/audio_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/audio_viewer_spec.js new file mode 100644 index 00000000000..baf16b57d7d --- /dev/null +++ b/spec/frontend/repository/components/blob_viewers/audio_viewer_spec.js @@ -0,0 +1,23 @@ +import { shallowMount } from '@vue/test-utils'; +import AudioViewer from '~/repository/components/blob_viewers/audio_viewer.vue'; + +describe('Audio Viewer', () => { + let wrapper; + + const DEFAULT_BLOB_DATA = { + rawPath: 'some/audio.mid', + }; + + const createComponent = () => { + wrapper = shallowMount(AudioViewer, { propsData: { blob: DEFAULT_BLOB_DATA } }); + }; + + const findContent = () => wrapper.find('[data-testid="audio"]'); + + it('renders an audio source component', () => { + createComponent(); + + expect(findContent().exists()).toBe(true); + expect(findContent().attributes('src')).toBe(DEFAULT_BLOB_DATA.rawPath); + }); +}); diff --git a/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js new file mode 100644 index 00000000000..7d43e4e660b --- /dev/null +++ b/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js @@ -0,0 +1,27 @@ +import { shallowMount } from '@vue/test-utils'; +import CsvViewer from '~/repository/components/blob_viewers/csv_viewer.vue'; + +describe('CSV Viewer', () => { + let wrapper; + + const DEFAULT_BLOB_DATA = { + rawPath: 'some/file.csv', + name: 'file.csv', + }; + + const createComponent = () => { + wrapper = shallowMount(CsvViewer, { + propsData: { blob: DEFAULT_BLOB_DATA }, + stubs: ['CsvViewer'], + }); + }; + + const findCsvViewerComp = () => wrapper.find('[data-testid="csv"]'); + + it('renders a Source Editor component', () => { + createComponent(); + expect(findCsvViewerComp().exists()).toBe(true); + expect(findCsvViewerComp().props('remoteFile')).toBeTruthy(); + expect(findCsvViewerComp().props('csv')).toBe(DEFAULT_BLOB_DATA.rawPath); + }); +}); diff --git a/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js index 5fe25ced302..0a91e5ce890 100644 --- a/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js +++ b/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js @@ -23,6 +23,8 @@ describe('Text Viewer', () => { }); }; + const findLink = () => wrapper.findComponent(GlLink); + it('renders download human readable file size text', () => { createComponent(); @@ -42,7 +44,7 @@ describe('Text Viewer', () => { createComponent(); const { rawPath, name } = DEFAULT_BLOB_DATA; - expect(wrapper.findComponent(GlLink).attributes()).toMatchObject({ + expect(findLink().attributes()).toMatchObject({ rel: 'nofollow', target: '_blank', href: rawPath, @@ -50,6 +52,13 @@ describe('Text Viewer', () => { }); }); + it('renders the correct link href when stored externally', () => { + const externalStorageUrl = 'https://cdn.test.com/project/some/file.js?token=1234'; + createComponent({ externalStorageUrl }); + + expect(findLink().attributes('href')).toBe(externalStorageUrl); + }); + it('renders download icon', () => { createComponent(); diff --git a/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js index 5caeb85834d..599443bf862 100644 --- a/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js +++ b/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js @@ -10,9 +10,9 @@ describe('LFS Viewer', () => { rawPath: '/some/file/path', }; - const createComponent = () => { + const createComponent = (blobData = {}) => { wrapper = shallowMount(LfsViewer, { - propsData: { blob: { ...DEFAULT_BLOB_DATA } }, + propsData: { blob: { ...DEFAULT_BLOB_DATA, ...blobData } }, stubs: { GlSprintf }, }); }; @@ -38,4 +38,11 @@ describe('LFS Viewer', () => { download: name, }); }); + + it('renders the correct link href when stored externally', () => { + const externalStorageUrl = 'https://cdn.test.com/project/some/file.js?token=1234'; + createComponent({ externalStorageUrl }); + + expect(findLink().attributes('href')).toBe(externalStorageUrl); + }); }); diff --git a/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js index 10eea691335..b61500ea0ad 100644 --- a/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js +++ b/spec/frontend/repository/components/blob_viewers/pdf_viewer_spec.js @@ -1,4 +1,5 @@ import { GlButton } from '@gitlab/ui'; +import { nextTick } from 'vue'; import Component from '~/repository/components/blob_viewers/pdf_viewer.vue'; import PdfViewer from '~/blob/pdf/pdf_viewer.vue'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; @@ -8,9 +9,9 @@ describe('PDF Viewer', () => { const DEFAULT_BLOB_DATA = { rawPath: 'some/pdf_blob.pdf' }; - const createComponent = (rawSize = 999) => { + const createComponent = (rawSize = 999, externalStorageUrl) => { wrapper = shallowMountExtended(Component, { - propsData: { blob: { ...DEFAULT_BLOB_DATA, rawSize } }, + propsData: { blob: { ...DEFAULT_BLOB_DATA, rawSize, externalStorageUrl } }, }); }; @@ -45,10 +46,14 @@ describe('PDF Viewer', () => { }); describe('Too many pages', () => { - beforeEach(() => { - createComponent(); - findPDFViewer().vm.$emit('pdflabload', 100); - }); + const loadComponent = (externalStorageUrl) => { + const rawSize = 999; + const totalPages = 100; + createComponent(rawSize, externalStorageUrl); + findPDFViewer().vm.$emit('pdflabload', totalPages); + }; + + beforeEach(() => loadComponent()); it('does not a PDF Viewer component', () => { expect(findPDFViewer().exists()).toBe(false); @@ -56,6 +61,15 @@ describe('PDF Viewer', () => { it('renders a download button', () => { expect(findDownLoadButton().exists()).toBe(true); + expect(findDownLoadButton().attributes('href')).toBe(DEFAULT_BLOB_DATA.rawPath); + }); + + it('renders the correct href when stored externally', async () => { + const externalStorageUrl = 'https://cdn.test.com/project/some/file.js?token=1234'; + loadComponent(externalStorageUrl); + await nextTick(); + + expect(findDownLoadButton().attributes('href')).toBe(externalStorageUrl); }); }); }); diff --git a/spec/frontend/repository/components/breadcrumbs_spec.js b/spec/frontend/repository/components/breadcrumbs_spec.js index 0e300291d05..0e3e7075e99 100644 --- a/spec/frontend/repository/components/breadcrumbs_spec.js +++ b/spec/frontend/repository/components/breadcrumbs_spec.js @@ -59,6 +59,20 @@ describe('Repository breadcrumbs component', () => { expect(wrapper.findAll(RouterLinkStub).length).toEqual(linkCount); }); + it.each` + routeName | path | linkTo + ${'treePath'} | ${'app/assets/javascripts'} | ${'/-/tree/app/assets/javascripts'} + ${'treePathDecoded'} | ${'app/assets/javascripts'} | ${'/-/tree/app/assets/javascripts'} + ${'blobPath'} | ${'app/assets/index.js'} | ${'/-/blob/app/assets/index.js'} + ${'blobPathDecoded'} | ${'app/assets/index.js'} | ${'/-/blob/app/assets/index.js'} + `( + 'links to the correct router path when routeName is $routeName', + ({ routeName, path, linkTo }) => { + factory(path, {}, { name: routeName }); + expect(wrapper.findAll(RouterLinkStub).at(3).props('to')).toEqual(linkTo); + }, + ); + it('escapes hash in directory path', () => { factory('app/assets/javascripts#'); -- cgit v1.2.1