summaryrefslogtreecommitdiff
path: root/spec/frontend/repository
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/repository')
-rw-r--r--spec/frontend/repository/components/blob_button_group_spec.js5
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js124
-rw-r--r--spec/frontend/repository/components/blob_edit_spec.js22
-rw-r--r--spec/frontend/repository/components/breadcrumbs_spec.js29
-rw-r--r--spec/frontend/repository/components/delete_blob_modal_spec.js119
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();
+ });
+ });
+ });
});