diff options
Diffstat (limited to 'spec/frontend/repository')
5 files changed, 266 insertions, 33 deletions
diff --git a/spec/frontend/repository/components/blob_button_group_spec.js b/spec/frontend/repository/components/blob_button_group_spec.js index a449fd6f06c..f2a3354f204 100644 --- a/spec/frontend/repository/components/blob_button_group_spec.js +++ b/spec/frontend/repository/components/blob_button_group_spec.js @@ -12,6 +12,9 @@ const DEFAULT_PROPS = { replacePath: 'some/replace/path', deletePath: 'some/delete/path', emptyRepo: false, + projectPath: 'some/project/path', + isLocked: false, + canLock: true, }; const DEFAULT_INJECT = { @@ -43,7 +46,7 @@ describe('BlobButtonGroup component', () => { const findDeleteBlobModal = () => wrapper.findComponent(DeleteBlobModal); const findUploadBlobModal = () => wrapper.findComponent(UploadBlobModal); - const findReplaceButton = () => wrapper.findAll(GlButton).at(0); + const findReplaceButton = () => wrapper.find('[data-testid="replace"]'); it('renders component', () => { createComponent(); diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index a83d0a607f2..d462995328b 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -20,6 +20,8 @@ import blobInfoQuery from '~/repository/queries/blob_info.query.graphql'; jest.mock('~/repository/components/blob_viewers'); let wrapper; +let mockResolver; + const simpleMockData = { name: 'some_file.js', size: 123, @@ -37,9 +39,6 @@ const simpleMockData = { externalStorageUrl: 'some_file.js', replacePath: 'some_file.js/replace', deletePath: 'some_file.js/delete', - canLock: true, - isLocked: false, - lockLink: 'some_file.js/lock', forkPath: 'some_file.js/fork', simpleViewer: { fileType: 'text', @@ -62,6 +61,7 @@ const richMockData = { const projectMockData = { userPermissions: { pushCode: true, + downloadCode: true, }, repository: { empty: false, @@ -71,17 +71,28 @@ const projectMockData = { const localVue = createLocalVue(); const mockAxios = new MockAdapter(axios); -const createComponentWithApollo = (mockData = {}) => { +const createComponentWithApollo = (mockData = {}, inject = {}) => { localVue.use(VueApollo); const defaultPushCode = projectMockData.userPermissions.pushCode; + const defaultDownloadCode = projectMockData.userPermissions.downloadCode; const defaultEmptyRepo = projectMockData.repository.empty; - const { blobs, emptyRepo = defaultEmptyRepo, canPushCode = defaultPushCode } = mockData; - - const mockResolver = jest.fn().mockResolvedValue({ + const { + blobs, + emptyRepo = defaultEmptyRepo, + canPushCode = defaultPushCode, + canDownloadCode = defaultDownloadCode, + pathLocks = [], + } = mockData; + + mockResolver = jest.fn().mockResolvedValue({ data: { project: { - userPermissions: { pushCode: canPushCode }, + id: '1234', + userPermissions: { pushCode: canPushCode, downloadCode: canDownloadCode }, + pathLocks: { + nodes: pathLocks, + }, repository: { empty: emptyRepo, blobs: { @@ -101,6 +112,14 @@ const createComponentWithApollo = (mockData = {}) => { path: 'some_file.js', projectPath: 'some/path', }, + mixins: [ + { + data: () => ({ ref: 'default-ref' }), + }, + ], + provide: { + ...inject, + }, }); }; @@ -119,6 +138,7 @@ const createFactory = (mountFn) => ( queries: { project: { loading, + refetch: jest.fn(), }, }, }, @@ -298,6 +318,7 @@ describe('Blob content viewer component', () => { expect(findBlobEdit().props()).toMatchObject({ editPath: editBlobPath, webIdePath: ideEditPath, + showEditButton: true, }); }); @@ -315,10 +336,11 @@ describe('Blob content viewer component', () => { expect(findBlobEdit().props()).toMatchObject({ editPath: editBlobPath, webIdePath: ideEditPath, + showEditButton: true, }); }); - it('does not render BlobHeaderEdit button when viewing a binary file', async () => { + it('renders BlobHeaderEdit button for binary files', async () => { fullFactory({ mockData: { blobInfo: richMockData, isBinary: true }, stubs: { @@ -329,13 +351,36 @@ describe('Blob content viewer component', () => { await nextTick(); - expect(findBlobEdit().exists()).toBe(false); + expect(findBlobEdit().props()).toMatchObject({ + editPath: editBlobPath, + webIdePath: ideEditPath, + showEditButton: false, + }); + }); + + describe('blob header binary file', () => { + it.each([richMockData, { simpleViewer: { fileType: 'download' } }])( + 'passes the correct isBinary value when viewing a binary file', + async (blobInfo) => { + fullFactory({ + mockData: { + blobInfo, + isBinary: true, + }, + stubs: { BlobContent: true, BlobReplace: true }, + }); + + await nextTick(); + + expect(findBlobHeader().props('isBinary')).toBe(true); + }, + ); }); describe('BlobButtonGroup', () => { const { name, path, replacePath, webPath } = simpleMockData; const { - userPermissions: { pushCode }, + userPermissions: { pushCode, downloadCode }, repository: { empty }, } = projectMockData; @@ -345,7 +390,7 @@ describe('Blob content viewer component', () => { fullFactory({ mockData: { blobInfo: simpleMockData, - project: { userPermissions: { pushCode }, repository: { empty } }, + project: { userPermissions: { pushCode, downloadCode }, repository: { empty } }, }, stubs: { BlobContent: true, @@ -361,10 +406,37 @@ describe('Blob content viewer component', () => { replacePath, deletePath: webPath, canPushCode: pushCode, + canLock: true, + isLocked: false, emptyRepo: empty, }); }); + it.each` + canPushCode | canDownloadCode | canLock + ${true} | ${true} | ${true} + ${false} | ${true} | ${false} + ${true} | ${false} | ${false} + `('passes the correct lock states', async ({ canPushCode, canDownloadCode, canLock }) => { + fullFactory({ + mockData: { + blobInfo: simpleMockData, + project: { + userPermissions: { pushCode: canPushCode, downloadCode: canDownloadCode }, + repository: { empty }, + }, + }, + stubs: { + BlobContent: true, + BlobButtonGroup: true, + }, + }); + + await nextTick(); + + expect(findBlobButtonGroup().props('canLock')).toBe(canLock); + }); + it('does not render if not logged in', async () => { window.gon.current_user_id = null; @@ -382,4 +454,32 @@ describe('Blob content viewer component', () => { }); }); }); + + describe('blob info query', () => { + it('is called with originalBranch value if the prop has a value', async () => { + const inject = { originalBranch: 'some-branch' }; + createComponentWithApollo({ blobs: simpleMockData }, inject); + + await waitForPromises(); + + expect(mockResolver).toHaveBeenCalledWith( + expect.objectContaining({ + ref: 'some-branch', + }), + ); + }); + + it('is called with ref value if the originalBranch prop has no value', async () => { + const inject = { originalBranch: null }; + createComponentWithApollo({ blobs: simpleMockData }, inject); + + await waitForPromises(); + + expect(mockResolver).toHaveBeenCalledWith( + expect.objectContaining({ + ref: 'default-ref', + }), + ); + }); + }); }); diff --git a/spec/frontend/repository/components/blob_edit_spec.js b/spec/frontend/repository/components/blob_edit_spec.js index e6e69cd8549..11739674bc9 100644 --- a/spec/frontend/repository/components/blob_edit_spec.js +++ b/spec/frontend/repository/components/blob_edit_spec.js @@ -6,6 +6,7 @@ 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, }; describe('BlobEdit component', () => { @@ -31,8 +32,8 @@ describe('BlobEdit component', () => { }); const findButtons = () => wrapper.findAll(GlButton); - const findEditButton = () => findButtons().at(0); - const findWebIdeButton = () => findButtons().at(1); + const findEditButton = () => wrapper.find('[data-testid="edit"]'); + const findWebIdeButton = () => wrapper.find('[data-testid="web-ide"]'); const findWebIdeLink = () => wrapper.find(WebIdeLink); it('renders component', () => { @@ -77,6 +78,23 @@ describe('BlobEdit component', () => { editUrl, webIdeUrl, isBlob: true, + showEditButton: true, + }); + }); + + 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/breadcrumbs_spec.js b/spec/frontend/repository/components/breadcrumbs_spec.js index 93bfd3d9d32..0733cffe4f4 100644 --- a/spec/frontend/repository/components/breadcrumbs_spec.js +++ b/spec/frontend/repository/components/breadcrumbs_spec.js @@ -3,10 +3,14 @@ import { shallowMount, RouterLinkStub } from '@vue/test-utils'; import Breadcrumbs from '~/repository/components/breadcrumbs.vue'; import UploadBlobModal from '~/repository/components/upload_blob_modal.vue'; +const defaultMockRoute = { + name: 'blobPath', +}; + describe('Repository breadcrumbs component', () => { let wrapper; - const factory = (currentPath, extraProps = {}) => { + const factory = (currentPath, extraProps = {}, mockRoute = {}) => { const $apollo = { queries: { userPermissions: { @@ -23,7 +27,13 @@ describe('Repository breadcrumbs component', () => { stubs: { RouterLink: RouterLinkStub, }, - mocks: { $apollo }, + mocks: { + $route: { + defaultMockRoute, + ...mockRoute, + }, + $apollo, + }, }); }; @@ -69,6 +79,21 @@ describe('Repository breadcrumbs component', () => { expect(wrapper.find(GlDropdown).exists()).toBe(false); }); + it.each` + routeName | isRendered + ${'blobPath'} | ${false} + ${'blobPathDecoded'} | ${false} + ${'treePath'} | ${true} + ${'treePathDecoded'} | ${true} + ${'projectRoot'} | ${true} + `( + 'does render add to tree dropdown $isRendered when route is $routeName', + ({ routeName, isRendered }) => { + factory('app/assets/javascripts.js', { canCollaborate: true }, { name: routeName }); + expect(wrapper.find(GlDropdown).exists()).toBe(isRendered); + }, + ); + it('renders add to tree dropdown when permissions are true', async () => { factory('/', { canCollaborate: true }); diff --git a/spec/frontend/repository/components/delete_blob_modal_spec.js b/spec/frontend/repository/components/delete_blob_modal_spec.js index a74e3e6d325..2c62868f391 100644 --- a/spec/frontend/repository/components/delete_blob_modal_spec.js +++ b/spec/frontend/repository/components/delete_blob_modal_spec.js @@ -1,5 +1,5 @@ -import { GlFormTextarea, GlModal, GlFormInput, GlToggle } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; +import { GlFormTextarea, GlModal, GlFormInput, GlToggle, GlForm } from '@gitlab/ui'; +import { shallowMount, mount } from '@vue/test-utils'; import { nextTick } from 'vue'; import DeleteBlobModal from '~/repository/components/delete_blob_modal.vue'; @@ -19,17 +19,34 @@ const initialProps = { describe('DeleteBlobModal', () => { let wrapper; - const createComponent = (props = {}) => { - wrapper = shallowMount(DeleteBlobModal, { + const createComponentFactory = (mountFn) => (props = {}) => { + wrapper = mountFn(DeleteBlobModal, { propsData: { ...initialProps, ...props, }, + attrs: { + static: true, + visible: true, + }, }); }; + const createComponent = createComponentFactory(shallowMount); + const createFullComponent = createComponentFactory(mount); + const findModal = () => wrapper.findComponent(GlModal); - const findForm = () => wrapper.findComponent({ ref: 'form' }); + const findForm = () => findModal().findComponent(GlForm); + const findCommitTextarea = () => findForm().findComponent(GlFormTextarea); + const findTargetInput = () => findForm().findComponent(GlFormInput); + const findCommitHint = () => wrapper.find('[data-testid="hint"]'); + + const fillForm = async (inputValue = {}) => { + const { targetText, commitText } = inputValue; + + await findTargetInput().vm.$emit('input', targetText); + await findCommitTextarea().vm.$emit('input', commitText); + }; afterEach(() => { wrapper.destroy(); @@ -58,17 +75,6 @@ describe('DeleteBlobModal', () => { expect(findForm().attributes('action')).toBe(initialProps.deletePath); }); - it('submits the form', async () => { - createComponent(); - - const submitSpy = jest.spyOn(findForm().element, 'submit'); - findModal().vm.$emit('primary', { preventDefault: () => {} }); - await nextTick(); - - expect(submitSpy).toHaveBeenCalled(); - submitSpy.mockRestore(); - }); - it.each` component | defaultValue | canPushCode | targetBranch | originalBranch | exist ${GlFormTextarea} | ${initialProps.commitMessage} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true} @@ -127,4 +133,85 @@ describe('DeleteBlobModal', () => { }, ); }); + + describe('hint', () => { + const targetText = 'some target branch'; + const hintText = 'Try to keep the first line under 52 characters and the others under 72.'; + const charsGenerator = (length) => 'lorem'.repeat(length); + + beforeEach(async () => { + createFullComponent(); + await nextTick(); + }); + + it.each` + commitText | exist | desc + ${charsGenerator(53)} | ${true} | ${'first line length > 52'} + ${`lorem\n${charsGenerator(73)}`} | ${true} | ${'other line length > 72'} + ${charsGenerator(52)} | ${true} | ${'other line length = 52'} + ${`lorem\n${charsGenerator(72)}`} | ${true} | ${'other line length = 72'} + ${`lorem`} | ${false} | ${'first line length < 53'} + ${`lorem\nlorem`} | ${false} | ${'other line length < 53'} + `('displays hint $exist for $desc', async ({ commitText, exist }) => { + await fillForm({ targetText, commitText }); + + if (!exist) { + expect(findCommitHint().exists()).toBe(false); + return; + } + + expect(findCommitHint().text()).toBe(hintText); + }); + }); + + describe('form submission', () => { + let submitSpy; + + beforeEach(async () => { + createFullComponent(); + await nextTick(); + submitSpy = jest.spyOn(findForm().element, 'submit'); + }); + + afterEach(() => { + submitSpy.mockRestore(); + }); + + describe('invalid form', () => { + beforeEach(async () => { + await fillForm({ targetText: '', commitText: '' }); + }); + + it('disables submit button', async () => { + expect(findModal().props('actionPrimary').attributes[0]).toEqual( + expect.objectContaining({ disabled: true }), + ); + }); + + it('does not submit form', async () => { + findModal().vm.$emit('primary', { preventDefault: () => {} }); + expect(submitSpy).not.toHaveBeenCalled(); + }); + }); + + describe('valid form', () => { + beforeEach(async () => { + await fillForm({ + targetText: 'some valid target branch', + commitText: 'some valid commit message', + }); + }); + + it('enables submit button', async () => { + expect(findModal().props('actionPrimary').attributes[0]).toEqual( + expect.objectContaining({ disabled: false }), + ); + }); + + it('submits form', async () => { + findModal().vm.$emit('primary', { preventDefault: () => {} }); + expect(submitSpy).toHaveBeenCalled(); + }); + }); + }); }); |