diff options
Diffstat (limited to 'spec/frontend/repository')
7 files changed, 331 insertions, 9 deletions
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index f03df8cf2ac..495039b4ccb 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -5,6 +5,7 @@ import BlobContent from '~/blob/components/blob_content.vue'; import BlobHeader from '~/blob/components/blob_header.vue'; import BlobContentViewer from '~/repository/components/blob_content_viewer.vue'; import BlobHeaderEdit from '~/repository/components/blob_header_edit.vue'; +import BlobReplace from '~/repository/components/blob_replace.vue'; let wrapper; const simpleMockData = { @@ -75,10 +76,11 @@ const factory = createFactory(shallowMount); const fullFactory = createFactory(mount); describe('Blob content viewer component', () => { - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); - const findBlobHeader = () => wrapper.find(BlobHeader); - const findBlobHeaderEdit = () => wrapper.find(BlobHeaderEdit); - const findBlobContent = () => wrapper.find(BlobContent); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findBlobHeader = () => wrapper.findComponent(BlobHeader); + const findBlobHeaderEdit = () => wrapper.findComponent(BlobHeaderEdit); + const findBlobContent = () => wrapper.findComponent(BlobContent); + const findBlobReplace = () => wrapper.findComponent(BlobReplace); afterEach(() => { wrapper.destroy(); @@ -162,15 +164,23 @@ describe('Blob content viewer component', () => { }); describe('BlobHeader action slot', () => { - it('renders BlobHeaderEdit button in simple viewer', async () => { + const { ideEditPath, editBlobPath } = simpleMockData; + + it('renders BlobHeaderEdit buttons in simple viewer', async () => { fullFactory({ mockData: { blobInfo: simpleMockData }, stubs: { BlobContent: true, + BlobReplace: true, }, }); + await nextTick(); - expect(findBlobHeaderEdit().props('editPath')).toEqual('some_file.js/edit'); + + expect(findBlobHeaderEdit().props()).toMatchObject({ + editPath: editBlobPath, + webIdePath: ideEditPath, + }); }); it('renders BlobHeaderEdit button in rich viewer', async () => { @@ -178,10 +188,55 @@ describe('Blob content viewer component', () => { mockData: { blobInfo: richMockData }, stubs: { BlobContent: true, + BlobReplace: true, }, }); + await nextTick(); - expect(findBlobHeaderEdit().props('editPath')).toEqual('some_file.js/edit'); + + expect(findBlobHeaderEdit().props()).toMatchObject({ + editPath: editBlobPath, + webIdePath: ideEditPath, + }); + }); + + describe('BlobReplace', () => { + const { name, path } = simpleMockData; + + it('renders component', async () => { + window.gon.current_user_id = 1; + + fullFactory({ + mockData: { blobInfo: simpleMockData }, + stubs: { + BlobContent: true, + BlobReplace: true, + }, + }); + + await nextTick(); + + expect(findBlobReplace().props()).toMatchObject({ + name, + path, + }); + }); + + it('does not render if not logged in', async () => { + window.gon.current_user_id = null; + + fullFactory({ + mockData: { blobInfo: simpleMockData }, + stubs: { + BlobContent: true, + BlobReplace: true, + }, + }); + + await nextTick(); + + expect(findBlobReplace().exists()).toBe(false); + }); }); }); }); diff --git a/spec/frontend/repository/components/blob_header_edit_spec.js b/spec/frontend/repository/components/blob_header_edit_spec.js new file mode 100644 index 00000000000..c0eb7c523c4 --- /dev/null +++ b/spec/frontend/repository/components/blob_header_edit_spec.js @@ -0,0 +1,82 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import BlobHeaderEdit from '~/repository/components/blob_header_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', +}; + +describe('BlobHeaderEdit component', () => { + let wrapper; + + const createComponent = (consolidatedEditButton = false, props = {}) => { + wrapper = shallowMount(BlobHeaderEdit, { + propsData: { + ...DEFAULT_PROPS, + ...props, + }, + provide: { + glFeatures: { + consolidatedEditButton, + }, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findButtons = () => wrapper.findAll(GlButton); + const findEditButton = () => findButtons().at(0); + const findWebIdeButton = () => findButtons().at(1); + 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().attributes('href')).toBe(DEFAULT_PROPS.editPath); + expect(findEditButton().text()).toBe('Edit'); + expect(findEditButton()).not.toBeDisabled(); + }); + + it('renders the Web IDE button', () => { + createComponent(); + + expect(findWebIdeButton().attributes('href')).toBe(DEFAULT_PROPS.webIdePath); + expect(findWebIdeButton().text()).toBe('Web IDE'); + expect(findWebIdeButton()).not.toBeDisabled(); + }); + + it('renders WebIdeLink component', () => { + createComponent(true); + + const { editPath: editUrl, webIdePath: webIdeUrl } = DEFAULT_PROPS; + + expect(findWebIdeLink().props()).toMatchObject({ + editUrl, + webIdeUrl, + isBlob: true, + }); + }); +}); diff --git a/spec/frontend/repository/components/blob_replace_spec.js b/spec/frontend/repository/components/blob_replace_spec.js new file mode 100644 index 00000000000..4a6f147da22 --- /dev/null +++ b/spec/frontend/repository/components/blob_replace_spec.js @@ -0,0 +1,67 @@ +import { shallowMount } from '@vue/test-utils'; +import BlobReplace from '~/repository/components/blob_replace.vue'; +import UploadBlobModal from '~/repository/components/upload_blob_modal.vue'; + +const DEFAULT_PROPS = { + name: 'some name', + path: 'some/path', + canPushCode: true, + replacePath: 'some/replace/path', +}; + +const DEFAULT_INJECT = { + targetBranch: 'master', + originalBranch: 'master', +}; + +describe('BlobReplace component', () => { + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(BlobReplace, { + propsData: { + ...DEFAULT_PROPS, + ...props, + }, + provide: { + ...DEFAULT_INJECT, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + const findUploadBlobModal = () => wrapper.findComponent(UploadBlobModal); + + it('renders component', () => { + createComponent(); + + const { name, path } = DEFAULT_PROPS; + + expect(wrapper.props()).toMatchObject({ + name, + path, + }); + }); + + it('renders UploadBlobModal', () => { + createComponent(); + + const { targetBranch, originalBranch } = DEFAULT_INJECT; + const { name, path, canPushCode, replacePath } = DEFAULT_PROPS; + const title = `Replace ${name}`; + + expect(findUploadBlobModal().props()).toMatchObject({ + modalTitle: title, + commitMessage: title, + targetBranch, + originalBranch, + canPushCode, + path, + replacePath, + primaryBtnText: 'Replace file', + }); + }); +}); diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js index 6ba6f993db1..da28c9873d9 100644 --- a/spec/frontend/repository/components/table/row_spec.js +++ b/spec/frontend/repository/components/table/row_spec.js @@ -1,5 +1,6 @@ import { GlBadge, GlLink, GlIcon } from '@gitlab/ui'; import { shallowMount, RouterLinkStub } from '@vue/test-utils'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import TableRow from '~/repository/components/table/row.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import { FILE_SYMLINK_MODE } from '~/vue_shared/constants'; @@ -18,6 +19,10 @@ function factory(propsData = {}) { name: propsData.path, projectPath: 'gitlab-org/gitlab-ce', url: `https://test.com`, + totalEntries: 10, + }, + directives: { + GlHoverLoad: createMockDirective(), }, provide: { glFeatures: { refactorBlobViewer: true }, @@ -34,6 +39,8 @@ function factory(propsData = {}) { } describe('Repository table row component', () => { + const findRouterLink = () => vm.find(RouterLinkStub); + afterEach(() => { vm.destroy(); }); @@ -81,6 +88,21 @@ describe('Repository table row component', () => { }); }); + it('renders a gl-hover-load directive', () => { + factory({ + id: '1', + sha: '123', + path: 'test', + type: 'blob', + currentPath: '/', + }); + + const hoverLoadDirective = getBinding(findRouterLink().element, 'gl-hover-load'); + + expect(hoverLoadDirective).not.toBeUndefined(); + expect(hoverLoadDirective.value).toBeInstanceOf(Function); + }); + it.each` type | component | componentName ${'tree'} | ${RouterLinkStub} | ${'RouterLink'} diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js index 2930e39df8a..d397bc185e2 100644 --- a/spec/frontend/repository/components/tree_content_spec.js +++ b/spec/frontend/repository/components/tree_content_spec.js @@ -1,7 +1,8 @@ import { shallowMount } from '@vue/test-utils'; import FilePreview from '~/repository/components/preview/index.vue'; import FileTable from '~/repository/components/table/index.vue'; -import TreeContent, { INITIAL_FETCH_COUNT } from '~/repository/components/tree_content.vue'; +import TreeContent from '~/repository/components/tree_content.vue'; +import { TREE_INITIAL_FETCH_COUNT } from '~/repository/constants'; let vm; let $apollo; @@ -128,7 +129,7 @@ describe('Repository table component', () => { it('has limit of 1000 files on initial load', () => { factory('/'); - expect(INITIAL_FETCH_COUNT * vm.vm.pageSize).toBe(1000); + expect(TREE_INITIAL_FETCH_COUNT * vm.vm.pageSize).toBe(1000); }); }); }); diff --git a/spec/frontend/repository/components/upload_blob_modal_spec.js b/spec/frontend/repository/components/upload_blob_modal_spec.js index ec85d5666fb..d93b1d7e5f1 100644 --- a/spec/frontend/repository/components/upload_blob_modal_spec.js +++ b/spec/frontend/repository/components/upload_blob_modal_spec.js @@ -200,4 +200,84 @@ describe('UploadBlobModal', () => { }); }, ); + + describe('blob file submission type', () => { + const submitForm = async () => { + wrapper.vm.uploadFile = jest.fn(); + wrapper.vm.replaceFile = jest.fn(); + wrapper.vm.submitForm(); + await wrapper.vm.$nextTick(); + }; + + const submitRequest = async () => { + mock = new MockAdapter(axios); + findModal().vm.$emit('primary', mockEvent); + await waitForPromises(); + }; + + describe('upload blob file', () => { + beforeEach(() => { + createComponent(); + }); + + it('displays the default "Upload New File" modal title ', () => { + expect(findModal().props('title')).toBe('Upload New File'); + }); + + it('display the defaul primary button text', () => { + expect(findModal().props('actionPrimary').text).toBe('Upload file'); + }); + + it('calls the default uploadFile when the form submit', async () => { + await submitForm(); + + expect(wrapper.vm.uploadFile).toHaveBeenCalled(); + expect(wrapper.vm.replaceFile).not.toHaveBeenCalled(); + }); + + it('makes a POST request', async () => { + await submitRequest(); + + expect(mock.history.put).toHaveLength(0); + expect(mock.history.post).toHaveLength(1); + }); + }); + + describe('replace blob file', () => { + const modalTitle = 'Replace foo.js'; + const replacePath = 'replace-path'; + const primaryBtnText = 'Replace file'; + + beforeEach(() => { + createComponent({ + modalTitle, + replacePath, + primaryBtnText, + }); + }); + + it('displays the passed modal title', () => { + expect(findModal().props('title')).toBe(modalTitle); + }); + + it('display the passed primary button text', () => { + expect(findModal().props('actionPrimary').text).toBe(primaryBtnText); + }); + + it('calls the replaceFile when the form submit', async () => { + await submitForm(); + + expect(wrapper.vm.replaceFile).toHaveBeenCalled(); + expect(wrapper.vm.uploadFile).not.toHaveBeenCalled(); + }); + + it('makes a PUT request', async () => { + await submitRequest(); + + expect(mock.history.put).toHaveLength(1); + expect(mock.history.post).toHaveLength(0); + expect(mock.history.put[0].url).toBe(replacePath); + }); + }); + }); }); diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js index a842053caad..8cabf902a4f 100644 --- a/spec/frontend/repository/log_tree_spec.js +++ b/spec/frontend/repository/log_tree_spec.js @@ -69,6 +69,21 @@ describe('fetchLogsTree', () => { mock.restore(); }); + it('persists the offset for a given page if offset is larger than maximum offset', async () => { + await fetchLogsTree(client, 'path', '1000', resolver, 900).then(() => {}); + + await fetchLogsTree(client, 'path', '1100', resolver, 1200).then(() => { + expect(axios.get).toHaveBeenCalledWith('/gitlab-org/gitlab-foss/-/refs/main/logs_tree/path', { + params: { format: 'json', offset: 975 }, + }); + }); + }); + + it('does not call axios get if offset is larger than the maximum offset', () => + fetchLogsTree(client, '', '1000', resolver, 900).then(() => { + expect(axios.get).not.toHaveBeenCalled(); + })); + it('calls axios get', () => fetchLogsTree(client, '', '0', resolver).then(() => { expect(axios.get).toHaveBeenCalledWith('/gitlab-org/gitlab-foss/-/refs/main/logs_tree/', { |