summaryrefslogtreecommitdiff
path: root/spec/frontend/ide/components/new_dropdown
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ide/components/new_dropdown')
-rw-r--r--spec/frontend/ide/components/new_dropdown/button_spec.js65
-rw-r--r--spec/frontend/ide/components/new_dropdown/index_spec.js84
-rw-r--r--spec/frontend/ide/components/new_dropdown/modal_spec.js175
-rw-r--r--spec/frontend/ide/components/new_dropdown/upload_spec.js112
4 files changed, 436 insertions, 0 deletions
diff --git a/spec/frontend/ide/components/new_dropdown/button_spec.js b/spec/frontend/ide/components/new_dropdown/button_spec.js
new file mode 100644
index 00000000000..3c611b7de8f
--- /dev/null
+++ b/spec/frontend/ide/components/new_dropdown/button_spec.js
@@ -0,0 +1,65 @@
+import Vue from 'vue';
+import mountComponent from 'helpers/vue_mount_component_helper';
+import Button from '~/ide/components/new_dropdown/button.vue';
+
+describe('IDE new entry dropdown button component', () => {
+ let Component;
+ let vm;
+
+ beforeAll(() => {
+ Component = Vue.extend(Button);
+ });
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ label: 'Testing',
+ icon: 'doc-new',
+ });
+
+ jest.spyOn(vm, '$emit').mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders button with label', () => {
+ expect(vm.$el.textContent).toContain('Testing');
+ });
+
+ it('renders icon', () => {
+ expect(vm.$el.querySelector('.ic-doc-new')).not.toBe(null);
+ });
+
+ it('emits click event', () => {
+ vm.$el.click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('click');
+ });
+
+ it('hides label if showLabel is false', done => {
+ vm.showLabel = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).not.toContain('Testing');
+
+ done();
+ });
+ });
+
+ describe('tooltipTitle', () => {
+ it('returns empty string when showLabel is true', () => {
+ expect(vm.tooltipTitle).toBe('');
+ });
+
+ it('returns label', done => {
+ vm.showLabel = false;
+
+ vm.$nextTick(() => {
+ expect(vm.tooltipTitle).toBe('Testing');
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ide/components/new_dropdown/index_spec.js b/spec/frontend/ide/components/new_dropdown/index_spec.js
new file mode 100644
index 00000000000..00781c16609
--- /dev/null
+++ b/spec/frontend/ide/components/new_dropdown/index_spec.js
@@ -0,0 +1,84 @@
+import Vue from 'vue';
+import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
+import store from '~/ide/stores';
+import newDropdown from '~/ide/components/new_dropdown/index.vue';
+import { resetStore } from '../../helpers';
+
+describe('new dropdown component', () => {
+ let vm;
+
+ beforeEach(() => {
+ const component = Vue.extend(newDropdown);
+
+ vm = createComponentWithStore(component, store, {
+ branch: 'master',
+ path: '',
+ mouseOver: false,
+ type: 'tree',
+ });
+
+ vm.$store.state.currentProjectId = 'abcproject';
+ vm.$store.state.path = '';
+ vm.$store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+
+ vm.$mount();
+
+ jest.spyOn(vm.$refs.newModal, 'open').mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('renders new file, upload and new directory links', () => {
+ const buttons = vm.$el.querySelectorAll('.dropdown-menu button');
+
+ expect(buttons[0].textContent.trim()).toBe('New file');
+ expect(buttons[1].textContent.trim()).toBe('Upload file');
+ expect(buttons[2].textContent.trim()).toBe('New directory');
+ });
+
+ describe('createNewItem', () => {
+ it('opens modal for a blob when new file is clicked', () => {
+ vm.$el.querySelectorAll('.dropdown-menu button')[0].click();
+
+ expect(vm.$refs.newModal.open).toHaveBeenCalledWith('blob', '');
+ });
+
+ it('opens modal for a tree when new directory is clicked', () => {
+ vm.$el.querySelectorAll('.dropdown-menu button')[2].click();
+
+ expect(vm.$refs.newModal.open).toHaveBeenCalledWith('tree', '');
+ });
+ });
+
+ describe('isOpen', () => {
+ it('scrolls dropdown into view', done => {
+ jest.spyOn(vm.$refs.dropdownMenu, 'scrollIntoView').mockImplementation(() => {});
+
+ vm.isOpen = true;
+
+ setImmediate(() => {
+ expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({
+ block: 'nearest',
+ });
+
+ done();
+ });
+ });
+ });
+
+ describe('delete entry', () => {
+ it('calls delete action', () => {
+ jest.spyOn(vm, 'deleteEntry').mockImplementation(() => {});
+
+ vm.$el.querySelectorAll('.dropdown-menu button')[4].click();
+
+ expect(vm.deleteEntry).toHaveBeenCalledWith('');
+ });
+ });
+});
diff --git a/spec/frontend/ide/components/new_dropdown/modal_spec.js b/spec/frontend/ide/components/new_dropdown/modal_spec.js
new file mode 100644
index 00000000000..62a59a76bf4
--- /dev/null
+++ b/spec/frontend/ide/components/new_dropdown/modal_spec.js
@@ -0,0 +1,175 @@
+import Vue from 'vue';
+import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
+import { createStore } from '~/ide/stores';
+import modal from '~/ide/components/new_dropdown/modal.vue';
+import createFlash from '~/flash';
+
+jest.mock('~/flash');
+
+describe('new file modal component', () => {
+ const Component = Vue.extend(modal);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe.each`
+ entryType | modalTitle | btnTitle | showsFileTemplates
+ ${'tree'} | ${'Create new directory'} | ${'Create directory'} | ${false}
+ ${'blob'} | ${'Create new file'} | ${'Create file'} | ${true}
+ `('$entryType', ({ entryType, modalTitle, btnTitle, showsFileTemplates }) => {
+ beforeEach(done => {
+ const store = createStore();
+
+ vm = createComponentWithStore(Component, store).$mount();
+ vm.open(entryType);
+ vm.name = 'testing';
+
+ vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ vm.close();
+ });
+
+ it(`sets modal title as ${entryType}`, () => {
+ expect(document.querySelector('.modal-title').textContent.trim()).toBe(modalTitle);
+ });
+
+ it(`sets button label as ${entryType}`, () => {
+ expect(document.querySelector('.btn-success').textContent.trim()).toBe(btnTitle);
+ });
+
+ it(`sets form label as ${entryType}`, () => {
+ expect(document.querySelector('.label-bold').textContent.trim()).toBe('Name');
+ });
+
+ it(`shows file templates: ${showsFileTemplates}`, () => {
+ const templateFilesEl = document.querySelector('.file-templates');
+ expect(Boolean(templateFilesEl)).toBe(showsFileTemplates);
+ });
+ });
+
+ describe('rename entry', () => {
+ beforeEach(() => {
+ const store = createStore();
+ store.state.entries = {
+ 'test-path': {
+ name: 'test',
+ type: 'blob',
+ path: 'test-path',
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ it.each`
+ entryType | modalTitle | btnTitle
+ ${'tree'} | ${'Rename folder'} | ${'Rename folder'}
+ ${'blob'} | ${'Rename file'} | ${'Rename file'}
+ `(
+ 'renders title and button for renaming $entryType',
+ ({ entryType, modalTitle, btnTitle }, done) => {
+ vm.$store.state.entries['test-path'].type = entryType;
+ vm.open('rename', 'test-path');
+
+ vm.$nextTick(() => {
+ expect(document.querySelector('.modal-title').textContent.trim()).toBe(modalTitle);
+ expect(document.querySelector('.btn-success').textContent.trim()).toBe(btnTitle);
+
+ done();
+ });
+ },
+ );
+
+ describe('entryName', () => {
+ it('returns entries name', () => {
+ vm.open('rename', 'test-path');
+
+ expect(vm.entryName).toBe('test-path');
+ });
+
+ it('does not reset entryName to its old value if empty', () => {
+ vm.entryName = 'hello';
+ vm.entryName = '';
+
+ expect(vm.entryName).toBe('');
+ });
+ });
+
+ describe('open', () => {
+ it('sets entryName to path provided if modalType is rename', () => {
+ vm.open('rename', 'test-path');
+
+ expect(vm.entryName).toBe('test-path');
+ });
+
+ it("appends '/' to the path if modalType isn't rename", () => {
+ vm.open('blob', 'test-path');
+
+ expect(vm.entryName).toBe('test-path/');
+ });
+
+ it('leaves entryName blank if no path is provided', () => {
+ vm.open('blob');
+
+ expect(vm.entryName).toBe('');
+ });
+ });
+ });
+
+ describe('submitForm', () => {
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ store.state.entries = {
+ 'test-path/test': {
+ name: 'test',
+ deleted: false,
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ it('throws an error when target entry exists', () => {
+ vm.open('rename', 'test-path/test');
+
+ expect(createFlash).not.toHaveBeenCalled();
+
+ vm.submitForm();
+
+ expect(createFlash).toHaveBeenCalledWith(
+ 'The name "test-path/test" is already taken in this directory.',
+ 'alert',
+ expect.anything(),
+ null,
+ false,
+ true,
+ );
+ });
+
+ it('does not throw error when target entry does not exist', () => {
+ jest.spyOn(vm, 'renameEntry').mockImplementation();
+
+ vm.open('rename', 'test-path/test');
+ vm.entryName = 'test-path/test2';
+ vm.submitForm();
+
+ expect(createFlash).not.toHaveBeenCalled();
+ });
+
+ it('removes leading/trailing found in the new name', () => {
+ vm.open('rename', 'test-path/test');
+
+ vm.entryName = 'test-path /test';
+
+ vm.submitForm();
+
+ expect(vm.entryName).toBe('test-path/test');
+ });
+ });
+});
diff --git a/spec/frontend/ide/components/new_dropdown/upload_spec.js b/spec/frontend/ide/components/new_dropdown/upload_spec.js
new file mode 100644
index 00000000000..a418fdeb572
--- /dev/null
+++ b/spec/frontend/ide/components/new_dropdown/upload_spec.js
@@ -0,0 +1,112 @@
+import Vue from 'vue';
+import createComponent from 'helpers/vue_mount_component_helper';
+import upload from '~/ide/components/new_dropdown/upload.vue';
+
+describe('new dropdown upload', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(upload);
+
+ vm = createComponent(Component, {
+ path: '',
+ });
+
+ vm.entryName = 'testing';
+
+ jest.spyOn(vm, '$emit');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('openFile', () => {
+ it('calls for each file', () => {
+ const files = ['test', 'test2', 'test3'];
+
+ jest.spyOn(vm, 'readFile').mockImplementation(() => {});
+ jest.spyOn(vm.$refs.fileUpload, 'files', 'get').mockReturnValue(files);
+
+ vm.openFile();
+
+ expect(vm.readFile.mock.calls.length).toBe(3);
+
+ files.forEach((file, i) => {
+ expect(vm.readFile.mock.calls[i]).toEqual([file]);
+ });
+ });
+ });
+
+ describe('readFile', () => {
+ beforeEach(() => {
+ jest.spyOn(FileReader.prototype, 'readAsDataURL').mockImplementation(() => {});
+ });
+
+ it('calls readAsDataURL for all files', () => {
+ const file = {
+ type: 'images/png',
+ };
+
+ vm.readFile(file);
+
+ expect(FileReader.prototype.readAsDataURL).toHaveBeenCalledWith(file);
+ });
+ });
+
+ describe('createFile', () => {
+ const textTarget = {
+ result: 'base64,cGxhaW4gdGV4dA==',
+ };
+ const binaryTarget = {
+ result: 'base64,w4I=',
+ };
+ const textFile = new File(['plain text'], 'textFile');
+
+ const binaryFile = {
+ name: 'binaryFile',
+ type: 'image/png',
+ };
+
+ beforeEach(() => {
+ jest.spyOn(FileReader.prototype, 'readAsText');
+ });
+
+ it('calls readAsText and creates file in plain text (without encoding) if the file content is plain text', done => {
+ const waitForCreate = new Promise(resolve => vm.$on('create', resolve));
+
+ vm.createFile(textTarget, textFile);
+
+ expect(FileReader.prototype.readAsText).toHaveBeenCalledWith(textFile);
+
+ waitForCreate
+ .then(() => {
+ expect(vm.$emit).toHaveBeenCalledWith('create', {
+ name: textFile.name,
+ type: 'blob',
+ content: 'plain text',
+ base64: false,
+ binary: false,
+ rawPath: '',
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('splits content on base64 if binary', () => {
+ vm.createFile(binaryTarget, binaryFile);
+
+ expect(FileReader.prototype.readAsText).not.toHaveBeenCalledWith(textFile);
+
+ expect(vm.$emit).toHaveBeenCalledWith('create', {
+ name: binaryFile.name,
+ type: 'blob',
+ content: binaryTarget.result.split('base64,')[1],
+ base64: true,
+ binary: true,
+ rawPath: binaryTarget.result,
+ });
+ });
+ });
+});