summaryrefslogtreecommitdiff
path: root/spec/frontend/repository
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 09:55:51 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 09:55:51 +0000
commite8d2c2579383897a1dd7f9debd359abe8ae8373d (patch)
treec42be41678c2586d49a75cabce89322082698334 /spec/frontend/repository
parentfc845b37ec3a90aaa719975f607740c22ba6a113 (diff)
downloadgitlab-ce-e8d2c2579383897a1dd7f9debd359abe8ae8373d.tar.gz
Add latest changes from gitlab-org/gitlab@14-1-stable-eev14.1.0-rc42
Diffstat (limited to 'spec/frontend/repository')
-rw-r--r--spec/frontend/repository/components/blob_button_group_spec.js117
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js171
-rw-r--r--spec/frontend/repository/components/blob_edit_spec.js (renamed from spec/frontend/repository/components/blob_header_edit_spec.js)6
-rw-r--r--spec/frontend/repository/components/blob_replace_spec.js67
-rw-r--r--spec/frontend/repository/components/blob_viewers/__snapshots__/empty_viewer_spec.js.snap9
-rw-r--r--spec/frontend/repository/components/blob_viewers/download_viewer_spec.js70
-rw-r--r--spec/frontend/repository/components/blob_viewers/empty_viewer_spec.js14
-rw-r--r--spec/frontend/repository/components/blob_viewers/text_viewer_spec.js30
-rw-r--r--spec/frontend/repository/components/delete_blob_modal_spec.js130
-rw-r--r--spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap3
-rw-r--r--spec/frontend/repository/components/tree_content_spec.js63
-rw-r--r--spec/frontend/repository/components/upload_blob_modal_spec.js4
-rw-r--r--spec/frontend/repository/log_tree_spec.js51
13 files changed, 614 insertions, 121 deletions
diff --git a/spec/frontend/repository/components/blob_button_group_spec.js b/spec/frontend/repository/components/blob_button_group_spec.js
new file mode 100644
index 00000000000..a449fd6f06c
--- /dev/null
+++ b/spec/frontend/repository/components/blob_button_group_spec.js
@@ -0,0 +1,117 @@
+import { GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import BlobButtonGroup from '~/repository/components/blob_button_group.vue';
+import DeleteBlobModal from '~/repository/components/delete_blob_modal.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',
+ deletePath: 'some/delete/path',
+ emptyRepo: false,
+};
+
+const DEFAULT_INJECT = {
+ targetBranch: 'master',
+ originalBranch: 'master',
+};
+
+describe('BlobButtonGroup component', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(BlobButtonGroup, {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...props,
+ },
+ provide: {
+ ...DEFAULT_INJECT,
+ },
+ directives: {
+ GlModal: createMockDirective(),
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findDeleteBlobModal = () => wrapper.findComponent(DeleteBlobModal);
+ const findUploadBlobModal = () => wrapper.findComponent(UploadBlobModal);
+ const findReplaceButton = () => wrapper.findAll(GlButton).at(0);
+
+ it('renders component', () => {
+ createComponent();
+
+ const { name, path } = DEFAULT_PROPS;
+
+ expect(wrapper.props()).toMatchObject({
+ name,
+ path,
+ });
+ });
+
+ describe('buttons', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders both the replace and delete button', () => {
+ expect(wrapper.findAll(GlButton)).toHaveLength(2);
+ });
+
+ it('renders the buttons in the correct order', () => {
+ expect(wrapper.findAll(GlButton).at(0).text()).toBe('Replace');
+ expect(wrapper.findAll(GlButton).at(1).text()).toBe('Delete');
+ });
+
+ it('triggers the UploadBlobModal from the replace button', () => {
+ const { value } = getBinding(findReplaceButton().element, 'gl-modal');
+ const modalId = findUploadBlobModal().props('modalId');
+
+ expect(modalId).toEqual(value);
+ });
+ });
+
+ 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',
+ });
+ });
+
+ it('renders DeleteBlobModel', () => {
+ createComponent();
+
+ const { targetBranch, originalBranch } = DEFAULT_INJECT;
+ const { name, canPushCode, deletePath, emptyRepo } = DEFAULT_PROPS;
+ const title = `Delete ${name}`;
+
+ expect(findDeleteBlobModal().props()).toMatchObject({
+ modalTitle: title,
+ commitMessage: title,
+ targetBranch,
+ originalBranch,
+ canPushCode,
+ deletePath,
+ emptyRepo,
+ });
+ });
+});
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index 495039b4ccb..a83d0a607f2 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -1,11 +1,23 @@
import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
+import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
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 BlobHeaderEdit from '~/repository/components/blob_header_edit.vue';
-import BlobReplace from '~/repository/components/blob_replace.vue';
+import BlobEdit from '~/repository/components/blob_edit.vue';
+import { loadViewer, viewerProps } 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 TextViewer from '~/repository/components/blob_viewers/text_viewer.vue';
+import blobInfoQuery from '~/repository/queries/blob_info.query.graphql';
+
+jest.mock('~/repository/components/blob_viewers');
let wrapper;
const simpleMockData = {
@@ -17,6 +29,7 @@ const simpleMockData = {
fileType: 'text',
tooLarge: false,
path: 'some_file.js',
+ webPath: 'some_file.js',
editBlobPath: 'some_file.js/edit',
ideEditPath: 'some_file.js/ide/edit',
storedExternally: false,
@@ -27,7 +40,6 @@ const simpleMockData = {
canLock: true,
isLocked: false,
lockLink: 'some_file.js/lock',
- canModifyBlob: true,
forkPath: 'some_file.js/fork',
simpleViewer: {
fileType: 'text',
@@ -47,6 +59,51 @@ const richMockData = {
},
};
+const projectMockData = {
+ userPermissions: {
+ pushCode: true,
+ },
+ repository: {
+ empty: false,
+ },
+};
+
+const localVue = createLocalVue();
+const mockAxios = new MockAdapter(axios);
+
+const createComponentWithApollo = (mockData = {}) => {
+ localVue.use(VueApollo);
+
+ const defaultPushCode = projectMockData.userPermissions.pushCode;
+ const defaultEmptyRepo = projectMockData.repository.empty;
+ const { blobs, emptyRepo = defaultEmptyRepo, canPushCode = defaultPushCode } = mockData;
+
+ const mockResolver = jest.fn().mockResolvedValue({
+ data: {
+ project: {
+ userPermissions: { pushCode: canPushCode },
+ repository: {
+ empty: emptyRepo,
+ blobs: {
+ nodes: [blobs],
+ },
+ },
+ },
+ },
+ });
+
+ const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]);
+
+ wrapper = shallowMount(BlobContentViewer, {
+ localVue,
+ apolloProvider: fakeApollo,
+ propsData: {
+ path: 'some_file.js',
+ projectPath: 'some/path',
+ },
+ });
+};
+
const createFactory = (mountFn) => (
{ props = {}, mockData = {}, stubs = {} } = {},
loading = false,
@@ -78,9 +135,9 @@ const fullFactory = createFactory(mount);
describe('Blob content viewer component', () => {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findBlobHeader = () => wrapper.findComponent(BlobHeader);
- const findBlobHeaderEdit = () => wrapper.findComponent(BlobHeaderEdit);
+ const findBlobEdit = () => wrapper.findComponent(BlobEdit);
const findBlobContent = () => wrapper.findComponent(BlobContent);
- const findBlobReplace = () => wrapper.findComponent(BlobReplace);
+ const findBlobButtonGroup = () => wrapper.findComponent(BlobButtonGroup);
afterEach(() => {
wrapper.destroy();
@@ -163,6 +220,67 @@ describe('Blob content viewer component', () => {
});
});
+ describe('legacy viewers', () => {
+ it('does not load a legacy viewer when a rich viewer is not available', async () => {
+ createComponentWithApollo({ blobs: simpleMockData });
+ await waitForPromises();
+
+ expect(mockAxios.history.get).toHaveLength(0);
+ });
+
+ it('loads a legacy viewer when a rich viewer is available', async () => {
+ createComponentWithApollo({ blobs: richMockData });
+ await waitForPromises();
+
+ expect(mockAxios.history.get).toHaveLength(1);
+ });
+ });
+
+ describe('Blob viewer', () => {
+ afterEach(() => {
+ loadViewer.mockRestore();
+ viewerProps.mockRestore();
+ });
+
+ it('does not render a BlobContent component if a Blob viewer is available', () => {
+ loadViewer.mockReturnValueOnce(() => true);
+ factory({ mockData: { blobInfo: richMockData } });
+
+ expect(findBlobContent().exists()).toBe(false);
+ });
+
+ it.each`
+ viewer | loadViewerReturnValue | viewerPropsReturnValue
+ ${'empty'} | ${EmptyViewer} | ${{}}
+ ${'download'} | ${DownloadViewer} | ${{ filePath: '/some/file/path', fileName: 'test.js', fileSize: 100 }}
+ ${'text'} | ${TextViewer} | ${{ content: 'test', fileName: 'test.js', readOnly: true }}
+ `(
+ 'renders viewer component for $viewer files',
+ async ({ viewer, loadViewerReturnValue, viewerPropsReturnValue }) => {
+ loadViewer.mockReturnValue(loadViewerReturnValue);
+ viewerProps.mockReturnValue(viewerPropsReturnValue);
+
+ factory({
+ mockData: {
+ blobInfo: {
+ ...simpleMockData,
+ fileType: null,
+ simpleViewer: {
+ ...simpleMockData.simpleViewer,
+ fileType: viewer,
+ },
+ },
+ },
+ });
+
+ await nextTick();
+
+ expect(loadViewer).toHaveBeenCalledWith(viewer);
+ expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true);
+ },
+ );
+ });
+
describe('BlobHeader action slot', () => {
const { ideEditPath, editBlobPath } = simpleMockData;
@@ -177,7 +295,7 @@ describe('Blob content viewer component', () => {
await nextTick();
- expect(findBlobHeaderEdit().props()).toMatchObject({
+ expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath,
webIdePath: ideEditPath,
});
@@ -194,31 +312,56 @@ describe('Blob content viewer component', () => {
await nextTick();
- expect(findBlobHeaderEdit().props()).toMatchObject({
+ expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath,
webIdePath: ideEditPath,
});
});
- describe('BlobReplace', () => {
- const { name, path } = simpleMockData;
+ it('does not render BlobHeaderEdit button when viewing a binary file', async () => {
+ fullFactory({
+ mockData: { blobInfo: richMockData, isBinary: true },
+ stubs: {
+ BlobContent: true,
+ BlobReplace: true,
+ },
+ });
+
+ await nextTick();
+
+ expect(findBlobEdit().exists()).toBe(false);
+ });
+
+ describe('BlobButtonGroup', () => {
+ const { name, path, replacePath, webPath } = simpleMockData;
+ const {
+ userPermissions: { pushCode },
+ repository: { empty },
+ } = projectMockData;
it('renders component', async () => {
window.gon.current_user_id = 1;
fullFactory({
- mockData: { blobInfo: simpleMockData },
+ mockData: {
+ blobInfo: simpleMockData,
+ project: { userPermissions: { pushCode }, repository: { empty } },
+ },
stubs: {
BlobContent: true,
- BlobReplace: true,
+ BlobButtonGroup: true,
},
});
await nextTick();
- expect(findBlobReplace().props()).toMatchObject({
+ expect(findBlobButtonGroup().props()).toMatchObject({
name,
path,
+ replacePath,
+ deletePath: webPath,
+ canPushCode: pushCode,
+ emptyRepo: empty,
});
});
@@ -235,7 +378,7 @@ describe('Blob content viewer component', () => {
await nextTick();
- expect(findBlobReplace().exists()).toBe(false);
+ expect(findBlobButtonGroup().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/repository/components/blob_header_edit_spec.js b/spec/frontend/repository/components/blob_edit_spec.js
index c0eb7c523c4..e6e69cd8549 100644
--- a/spec/frontend/repository/components/blob_header_edit_spec.js
+++ b/spec/frontend/repository/components/blob_edit_spec.js
@@ -1,6 +1,6 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import BlobHeaderEdit from '~/repository/components/blob_header_edit.vue';
+import BlobEdit from '~/repository/components/blob_edit.vue';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
const DEFAULT_PROPS = {
@@ -8,11 +8,11 @@ const DEFAULT_PROPS = {
webIdePath: 'some_file.js/ide/edit',
};
-describe('BlobHeaderEdit component', () => {
+describe('BlobEdit component', () => {
let wrapper;
const createComponent = (consolidatedEditButton = false, props = {}) => {
- wrapper = shallowMount(BlobHeaderEdit, {
+ wrapper = shallowMount(BlobEdit, {
propsData: {
...DEFAULT_PROPS,
...props,
diff --git a/spec/frontend/repository/components/blob_replace_spec.js b/spec/frontend/repository/components/blob_replace_spec.js
deleted file mode 100644
index 4a6f147da22..00000000000
--- a/spec/frontend/repository/components/blob_replace_spec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-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/blob_viewers/__snapshots__/empty_viewer_spec.js.snap b/spec/frontend/repository/components/blob_viewers/__snapshots__/empty_viewer_spec.js.snap
new file mode 100644
index 00000000000..e702ea5fd00
--- /dev/null
+++ b/spec/frontend/repository/components/blob_viewers/__snapshots__/empty_viewer_spec.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Empty Viewer matches the snapshot 1`] = `
+<div
+ class="nothing-here-block"
+>
+ Empty file
+</div>
+`;
diff --git a/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js
new file mode 100644
index 00000000000..c71b2b3c55c
--- /dev/null
+++ b/spec/frontend/repository/components/blob_viewers/download_viewer_spec.js
@@ -0,0 +1,70 @@
+import { GlLink, GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue';
+
+describe('Text Viewer', () => {
+ let wrapper;
+
+ const DEFAULT_PROPS = {
+ fileName: 'file_name.js',
+ filePath: '/some/file/path',
+ fileSize: 2269674,
+ };
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(DownloadViewer, {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...props,
+ },
+ });
+ };
+
+ it('renders component', () => {
+ createComponent();
+
+ const { fileName, filePath, fileSize } = DEFAULT_PROPS;
+ expect(wrapper.props()).toMatchObject({
+ fileName,
+ filePath,
+ fileSize,
+ });
+ });
+
+ it('renders download human readable file size text', () => {
+ createComponent();
+
+ const downloadText = `Download (${numberToHumanSize(DEFAULT_PROPS.fileSize)})`;
+ expect(wrapper.text()).toBe(downloadText);
+ });
+
+ it('renders download text', () => {
+ createComponent({
+ fileSize: 0,
+ });
+
+ expect(wrapper.text()).toBe('Download');
+ });
+
+ it('renders download link', () => {
+ createComponent();
+ const { filePath, fileName } = DEFAULT_PROPS;
+
+ expect(wrapper.findComponent(GlLink).attributes()).toMatchObject({
+ rel: 'nofollow',
+ target: '_blank',
+ href: filePath,
+ download: fileName,
+ });
+ });
+
+ it('renders download icon', () => {
+ createComponent();
+
+ expect(wrapper.findComponent(GlIcon).props()).toMatchObject({
+ name: 'download',
+ size: 16,
+ });
+ });
+});
diff --git a/spec/frontend/repository/components/blob_viewers/empty_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/empty_viewer_spec.js
new file mode 100644
index 00000000000..e65f20ea0af
--- /dev/null
+++ b/spec/frontend/repository/components/blob_viewers/empty_viewer_spec.js
@@ -0,0 +1,14 @@
+import { shallowMount } from '@vue/test-utils';
+import EmptyViewer from '~/repository/components/blob_viewers/empty_viewer.vue';
+
+describe('Empty Viewer', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(EmptyViewer);
+ });
+
+ it('matches the snapshot', () => {
+ expect(wrapper.element).toMatchSnapshot();
+ });
+});
diff --git a/spec/frontend/repository/components/blob_viewers/text_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/text_viewer_spec.js
new file mode 100644
index 00000000000..88c5bee6564
--- /dev/null
+++ b/spec/frontend/repository/components/blob_viewers/text_viewer_spec.js
@@ -0,0 +1,30 @@
+import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import TextViewer from '~/repository/components/blob_viewers/text_viewer.vue';
+import SourceEditor from '~/vue_shared/components/source_editor.vue';
+
+describe('Text Viewer', () => {
+ let wrapper;
+ const propsData = {
+ content: 'Some content',
+ fileName: 'file_name.js',
+ readOnly: true,
+ };
+
+ const createComponent = () => {
+ wrapper = shallowMount(TextViewer, { propsData });
+ };
+
+ const findEditor = () => wrapper.findComponent(SourceEditor);
+
+ it('renders a Source Editor component', async () => {
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findEditor().exists()).toBe(true);
+ expect(findEditor().props('value')).toBe(propsData.content);
+ expect(findEditor().props('fileName')).toBe(propsData.fileName);
+ expect(findEditor().props('editorOptions')).toEqual({ readOnly: propsData.readOnly });
+ });
+});
diff --git a/spec/frontend/repository/components/delete_blob_modal_spec.js b/spec/frontend/repository/components/delete_blob_modal_spec.js
new file mode 100644
index 00000000000..a74e3e6d325
--- /dev/null
+++ b/spec/frontend/repository/components/delete_blob_modal_spec.js
@@ -0,0 +1,130 @@
+import { GlFormTextarea, GlModal, GlFormInput, GlToggle } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import DeleteBlobModal from '~/repository/components/delete_blob_modal.vue';
+
+jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
+
+const initialProps = {
+ modalId: 'Delete-blob',
+ modalTitle: 'Delete File',
+ deletePath: 'some/path',
+ commitMessage: 'Delete File',
+ targetBranch: 'some-target-branch',
+ originalBranch: 'main',
+ canPushCode: true,
+ emptyRepo: false,
+};
+
+describe('DeleteBlobModal', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(DeleteBlobModal, {
+ propsData: {
+ ...initialProps,
+ ...props,
+ },
+ });
+ };
+
+ const findModal = () => wrapper.findComponent(GlModal);
+ const findForm = () => wrapper.findComponent({ ref: 'form' });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders Modal component', () => {
+ createComponent();
+
+ const { modalTitle: title } = initialProps;
+
+ expect(findModal().props()).toMatchObject({
+ title,
+ size: 'md',
+ actionPrimary: {
+ text: 'Delete file',
+ },
+ actionCancel: {
+ text: 'Cancel',
+ },
+ });
+ });
+
+ describe('form', () => {
+ it('gets passed the path for action attribute', () => {
+ createComponent();
+ 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}
+ ${GlFormInput} | ${initialProps.targetBranch} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true}
+ ${GlFormInput} | ${undefined} | ${false} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${false}
+ ${GlToggle} | ${'true'} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true}
+ ${GlToggle} | ${undefined} | ${true} | ${'same-branch'} | ${'same-branch'} | ${false}
+ `(
+ 'has the correct form fields ',
+ ({ component, defaultValue, canPushCode, targetBranch, originalBranch, exist }) => {
+ createComponent({
+ canPushCode,
+ targetBranch,
+ originalBranch,
+ });
+ const formField = wrapper.findComponent(component);
+
+ if (!exist) {
+ expect(formField.exists()).toBe(false);
+ return;
+ }
+
+ expect(formField.exists()).toBe(true);
+ expect(formField.attributes('value')).toBe(defaultValue);
+ },
+ );
+
+ it.each`
+ input | value | emptyRepo | canPushCode | exist
+ ${'authenticity_token'} | ${'mock-csrf-token'} | ${false} | ${true} | ${true}
+ ${'authenticity_token'} | ${'mock-csrf-token'} | ${true} | ${false} | ${true}
+ ${'_method'} | ${'delete'} | ${false} | ${true} | ${true}
+ ${'_method'} | ${'delete'} | ${true} | ${false} | ${true}
+ ${'original_branch'} | ${initialProps.originalBranch} | ${false} | ${true} | ${true}
+ ${'original_branch'} | ${undefined} | ${true} | ${true} | ${false}
+ ${'create_merge_request'} | ${'1'} | ${false} | ${false} | ${true}
+ ${'create_merge_request'} | ${'1'} | ${false} | ${true} | ${true}
+ ${'create_merge_request'} | ${undefined} | ${true} | ${false} | ${false}
+ `(
+ 'passes $input as a hidden input with the correct value',
+ ({ input, value, emptyRepo, canPushCode, exist }) => {
+ createComponent({
+ emptyRepo,
+ canPushCode,
+ });
+
+ const inputMethod = findForm().find(`input[name="${input}"]`);
+
+ if (!exist) {
+ expect(inputMethod.exists()).toBe(false);
+ return;
+ }
+
+ expect(inputMethod.attributes('type')).toBe('hidden');
+ expect(inputMethod.attributes('value')).toBe(value);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
index ac60fc4917d..6f461f4c69b 100644
--- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
+++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
@@ -11,6 +11,7 @@ exports[`Repository table row component renders a symlink table row 1`] = `
class="tree-item-link str-truncated"
data-qa-selector="file_name_link"
href="https://test.com"
+ title="test"
>
<file-icon-stub
class="mr-1 position-relative text-secondary"
@@ -64,6 +65,7 @@ exports[`Repository table row component renders table row 1`] = `
class="tree-item-link str-truncated"
data-qa-selector="file_name_link"
href="https://test.com"
+ title="test"
>
<file-icon-stub
class="mr-1 position-relative text-secondary"
@@ -117,6 +119,7 @@ exports[`Repository table row component renders table row for path with special
class="tree-item-link str-truncated"
data-qa-selector="file_name_link"
href="https://test.com"
+ title="test"
>
<file-icon-stub
class="mr-1 position-relative text-secondary"
diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js
index d397bc185e2..1d1ec58100f 100644
--- a/spec/frontend/repository/components/tree_content_spec.js
+++ b/spec/frontend/repository/components/tree_content_spec.js
@@ -1,8 +1,8 @@
import { shallowMount } from '@vue/test-utils';
+import filesQuery from 'shared_queries/repository/files.query.graphql';
import FilePreview from '~/repository/components/preview/index.vue';
import FileTable from '~/repository/components/table/index.vue';
import TreeContent from '~/repository/components/tree_content.vue';
-import { TREE_INITIAL_FETCH_COUNT } from '~/repository/constants';
let vm;
let $apollo;
@@ -19,10 +19,17 @@ function factory(path, data = () => ({})) {
mocks: {
$apollo,
},
+ provide: {
+ glFeatures: {
+ increasePageSizeExponentially: true,
+ },
+ },
});
}
describe('Repository table component', () => {
+ const findFileTable = () => vm.find(FileTable);
+
afterEach(() => {
vm.destroy();
});
@@ -85,14 +92,12 @@ describe('Repository table component', () => {
describe('FileTable showMore', () => {
describe('when is present', () => {
- const fileTable = () => vm.find(FileTable);
-
beforeEach(async () => {
factory('/');
});
it('is changes hasShowMore to false when "showMore" event is emitted', async () => {
- fileTable().vm.$emit('showMore');
+ findFileTable().vm.$emit('showMore');
await vm.vm.$nextTick();
@@ -100,7 +105,7 @@ describe('Repository table component', () => {
});
it('changes clickedShowMore when "showMore" event is emitted', async () => {
- fileTable().vm.$emit('showMore');
+ findFileTable().vm.$emit('showMore');
await vm.vm.$nextTick();
@@ -110,7 +115,7 @@ describe('Repository table component', () => {
it('triggers fetchFiles when "showMore" event is emitted', () => {
jest.spyOn(vm.vm, 'fetchFiles');
- fileTable().vm.$emit('showMore');
+ findFileTable().vm.$emit('showMore');
expect(vm.vm.fetchFiles).toHaveBeenCalled();
});
@@ -126,10 +131,52 @@ describe('Repository table component', () => {
expect(vm.vm.hasShowMore).toBe(false);
});
- it('has limit of 1000 files on initial load', () => {
+ it.each`
+ totalBlobs | pagesLoaded | limitReached
+ ${900} | ${1} | ${false}
+ ${1000} | ${1} | ${true}
+ ${1002} | ${1} | ${true}
+ ${1002} | ${2} | ${false}
+ ${1900} | ${2} | ${false}
+ ${2000} | ${2} | ${true}
+ `('has limit of 1000 entries per page', async ({ totalBlobs, pagesLoaded, limitReached }) => {
factory('/');
- expect(TREE_INITIAL_FETCH_COUNT * vm.vm.pageSize).toBe(1000);
+ const blobs = new Array(totalBlobs).fill('fakeBlob');
+ vm.setData({ entries: { blobs }, pagesLoaded });
+
+ await vm.vm.$nextTick();
+
+ expect(findFileTable().props('hasMore')).toBe(limitReached);
+ });
+
+ it.each`
+ fetchCounter | pageSize
+ ${0} | ${10}
+ ${2} | ${30}
+ ${4} | ${50}
+ ${6} | ${70}
+ ${8} | ${90}
+ ${10} | ${100}
+ ${20} | ${100}
+ ${100} | ${100}
+ ${200} | ${100}
+ `('exponentially increases page size, to a maximum of 100', ({ fetchCounter, pageSize }) => {
+ factory('/');
+ vm.setData({ fetchCounter });
+
+ vm.vm.fetchFiles();
+
+ expect($apollo.query).toHaveBeenCalledWith({
+ query: filesQuery,
+ variables: {
+ pageSize,
+ nextPageCursor: '',
+ path: '/',
+ projectPath: '',
+ ref: '',
+ },
+ });
});
});
});
diff --git a/spec/frontend/repository/components/upload_blob_modal_spec.js b/spec/frontend/repository/components/upload_blob_modal_spec.js
index d93b1d7e5f1..08a6583b60c 100644
--- a/spec/frontend/repository/components/upload_blob_modal_spec.js
+++ b/spec/frontend/repository/components/upload_blob_modal_spec.js
@@ -190,7 +190,9 @@ describe('UploadBlobModal', () => {
});
it('creates a flash error', () => {
- expect(createFlash).toHaveBeenCalledWith('Error uploading file. Please try again.');
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'Error uploading file. Please try again.',
+ });
});
afterEach(() => {
diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js
index 8cabf902a4f..5186c9a8992 100644
--- a/spec/frontend/repository/log_tree_spec.js
+++ b/spec/frontend/repository/log_tree_spec.js
@@ -1,6 +1,10 @@
import MockAdapter from 'axios-mock-adapter';
+import { createMockClient } from 'helpers/mock_apollo_helper';
import axios from '~/lib/utils/axios_utils';
import { resolveCommit, fetchLogsTree } from '~/repository/log_tree';
+import commitsQuery from '~/repository/queries/commits.query.graphql';
+import projectPathQuery from '~/repository/queries/project_path.query.graphql';
+import refQuery from '~/repository/queries/ref.query.graphql';
const mockData = [
{
@@ -10,6 +14,7 @@ const mockData = [
committed_date: '2019-01-01',
},
commit_path: `https://test.com`,
+ commit_title_html: 'commit title',
file_name: 'index.js',
type: 'blob',
},
@@ -50,19 +55,15 @@ describe('fetchLogsTree', () => {
global.gon = { relative_url_root: '' };
- client = {
- readQuery: () => ({
- projectPath: 'gitlab-org/gitlab-foss',
- escapedRef: 'main',
- commits: [],
- }),
- writeQuery: jest.fn(),
- };
-
resolver = {
entry: { name: 'index.js', type: 'blob' },
resolve: jest.fn(),
};
+
+ client = createMockClient();
+ client.writeQuery({ query: projectPathQuery, data: { projectPath: 'gitlab-org/gitlab-foss' } });
+ client.writeQuery({ query: refQuery, data: { ref: 'main', escapedRef: 'main' } });
+ client.writeQuery({ query: commitsQuery, data: { commits: [] } });
});
afterEach(() => {
@@ -125,25 +126,19 @@ describe('fetchLogsTree', () => {
it('writes query to client', async () => {
await fetchLogsTree(client, '', '0', resolver);
- expect(client.writeQuery).toHaveBeenCalledWith({
- query: expect.anything(),
- data: {
- projectPath: 'gitlab-org/gitlab-foss',
- escapedRef: 'main',
- commits: [
- expect.objectContaining({
- __typename: 'LogTreeCommit',
- commitPath: 'https://test.com',
- committedDate: '2019-01-01',
- fileName: 'index.js',
- filePath: '/index.js',
- message: 'testing message',
- sha: '123',
- titleHtml: undefined,
- type: 'blob',
- }),
- ],
- },
+ expect(client.readQuery({ query: commitsQuery })).toEqual({
+ commits: [
+ expect.objectContaining({
+ commitPath: 'https://test.com',
+ committedDate: '2019-01-01',
+ fileName: 'index.js',
+ filePath: '/index.js',
+ message: 'testing message',
+ sha: '123',
+ titleHtml: 'commit title',
+ type: 'blob',
+ }),
+ ],
});
});
});