diff options
7 files changed, 250 insertions, 4 deletions
diff --git a/app/assets/javascripts/repo/components/new_dropdown/index.vue b/app/assets/javascripts/repo/components/new_dropdown/index.vue index 864db679e82..53baa02c344 100644 --- a/app/assets/javascripts/repo/components/new_dropdown/index.vue +++ b/app/assets/javascripts/repo/components/new_dropdown/index.vue @@ -31,6 +31,7 @@ type="button" class="btn btn-default dropdown-toggle add-to-tree" data-toggle="dropdown" + aria-label="Create new file or directory" > <i class="fa fa-plus" diff --git a/app/assets/javascripts/repo/index.js b/app/assets/javascripts/repo/index.js index b1b40d8b8cc..3586b5fea67 100644 --- a/app/assets/javascripts/repo/index.js +++ b/app/assets/javascripts/repo/index.js @@ -28,6 +28,7 @@ function setInitialStore(data) { Store.service = Service; Store.service.url = data.url; Store.service.refsUrl = data.refsUrl; + Store.path = data.currentPath; Store.projectId = data.projectId; Store.projectName = data.projectName; Store.projectUrl = data.projectUrl; diff --git a/app/assets/javascripts/repo/stores/repo_store.js b/app/assets/javascripts/repo/stores/repo_store.js index f4ab80b2c98..530c725ceb6 100644 --- a/app/assets/javascripts/repo/stores/repo_store.js +++ b/app/assets/javascripts/repo/stores/repo_store.js @@ -38,6 +38,7 @@ const RepoStore = { newMrTemplateUrl: '', branchChanged: false, commitMessage: '', + path: '', loading: { tree: false, blob: false, diff --git a/app/views/shared/repo/_repo.html.haml b/app/views/shared/repo/_repo.html.haml index 7185f5bcc5b..7861f92b33f 100644 --- a/app/views/shared/repo/_repo.html.haml +++ b/app/views/shared/repo/_repo.html.haml @@ -7,4 +7,5 @@ blob_url: namespace_project_blob_path(project.namespace, project, '{{branch}}'), new_mr_template_url: namespace_project_new_merge_request_path(project.namespace, project, merge_request: { source_branch: '{{source_branch}}' }), can_commit: (!!can_push_branch?(project, @ref)).to_s, - on_top_of_branch: (!!on_top_of_branch?(project, @ref)).to_s } } + on_top_of_branch: (!!on_top_of_branch?(project, @ref)).to_s, + current_path: @path } } diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index d7a2e86771c..b71136c4114 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -1,4 +1,3 @@ -export default (Component, props = {}) => new Component({ +export default (Component, props = {}, el = null) => new Component({ propsData: props, -}).$mount(); - +}).$mount(el); diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js new file mode 100644 index 00000000000..884d065718b --- /dev/null +++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js @@ -0,0 +1,63 @@ +import Vue from 'vue'; +import newDropdown from '~/repo/components/new_dropdown/index.vue'; +import createComponent from '../../../helpers/vue_mount_component_helper'; + +describe('new dropdown component', () => { + let vm; + + beforeEach(() => { + const component = Vue.extend(newDropdown); + + vm = createComponent(component); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders new file and new directory links', () => { + expect(vm.$el.querySelectorAll('a')[0].textContent.trim()).toBe('New file'); + expect(vm.$el.querySelectorAll('a')[1].textContent.trim()).toBe('New directory'); + }); + + describe('createNewItem', () => { + it('sets modalType to blob when new file is clicked', () => { + vm.$el.querySelectorAll('a')[0].click(); + + expect(vm.modalType).toBe('blob'); + }); + + it('sets modalType to tree when new directory is clicked', () => { + vm.$el.querySelectorAll('a')[1].click(); + + expect(vm.modalType).toBe('tree'); + }); + + it('opens modal when link is clicked', (done) => { + vm.$el.querySelectorAll('a')[0].click(); + + Vue.nextTick(() => { + expect(vm.$el.querySelector('.modal')).not.toBeNull(); + + done(); + }); + }); + }); + + describe('toggleModalOpen', () => { + it('closes modal after toggling', (done) => { + vm.toggleModalOpen(); + + Vue.nextTick() + .then(() => { + expect(vm.$el.querySelector('.modal')).not.toBeNull(); + }) + .then(vm.toggleModalOpen) + .then(() => { + expect(vm.$el.querySelector('.modal')).toBeNull(); + }) + .then(done) + .catch(done.fail); + }); + }); +}); diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js new file mode 100644 index 00000000000..4be92e9d944 --- /dev/null +++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js @@ -0,0 +1,180 @@ +import Vue from 'vue'; +import RepoStore from '~/repo/stores/repo_store'; +import RepoHelper from '~/repo/helpers/repo_helper'; +import modal from '~/repo/components/new_dropdown/modal.vue'; +import createComponent from '../../../helpers/vue_mount_component_helper'; + +describe('new file modal component', () => { + const Component = Vue.extend(modal); + let vm; + + afterEach(() => { + vm.$destroy(); + + RepoStore.files = []; + RepoStore.openedFiles = []; + RepoStore.setViewToPreview(); + }); + + ['tree', 'blob'].forEach((type) => { + describe(type, () => { + beforeEach(() => { + vm = createComponent(Component, { + type, + }); + }); + + it(`sets modal title as ${type}`, () => { + const title = type === 'tree' ? 'directory' : 'file'; + + expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Create new ${title}`); + }); + + it(`sets button label as ${type}`, () => { + const title = type === 'tree' ? 'directory' : 'file'; + + expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Create ${title}`); + }); + + it(`sets form label as ${type}`, () => { + const title = type === 'tree' ? 'Directory' : 'File'; + + expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe(`${title} name`); + }); + + it('emits toggle event after creating file', () => { + spyOn(vm, '$emit'); + + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(vm.$emit).toHaveBeenCalledWith('toggle'); + }); + + it('sets editMode to true', () => { + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.editMode).toBeTruthy(); + }); + + it('toggles blob view', () => { + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.isPreviewView()).toBeFalsy(); + }); + + it('adds file into activeFiles', () => { + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.openedFiles.length).toBe(1); + }); + }); + }); + + describe('file', () => { + beforeEach(() => { + vm = createComponent(Component, { + type: 'blob', + }); + }); + + it('creates new file', () => { + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.files.length).toBe(1); + expect(RepoStore.files[0].name).toBe('testing'); + expect(RepoStore.files[0].type).toBe('blob'); + expect(RepoStore.files[0].tempFile).toBeTruthy(); + }); + + it('does not create temp file when file already exists', () => { + RepoStore.files.push(RepoHelper.serializeRepoEntity('blob', { + name: 'testing', + })); + + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.files.length).toBe(1); + expect(RepoStore.files[0].name).toBe('testing'); + expect(RepoStore.files[0].type).toBe('blob'); + expect(RepoStore.files[0].tempFile).toBeUndefined(); + }); + }); + + describe('tree', () => { + beforeEach(() => { + vm = createComponent(Component, { + type: 'tree', + }); + }); + + it('creates new tree', () => { + vm.entryName = 'testing'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.files.length).toBe(1); + expect(RepoStore.files[0].name).toBe('testing'); + expect(RepoStore.files[0].type).toBe('tree'); + expect(RepoStore.files[0].tempFile).toBeTruthy(); + expect(RepoStore.files[0].files.length).toBe(1); + expect(RepoStore.files[0].files[0].name).toBe('.gitkeep'); + }); + + it('creates multiple trees when entryName has slashes', () => { + vm.entryName = 'app/test'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.files.length).toBe(1); + expect(RepoStore.files[0].name).toBe('app'); + expect(RepoStore.files[0].files[0].name).toBe('test'); + expect(RepoStore.files[0].files[0].files[0].name).toBe('.gitkeep'); + }); + + it('creates tree in existing tree', () => { + RepoStore.files.push(RepoHelper.serializeRepoEntity('tree', { + name: 'app', + })); + + vm.entryName = 'app/test'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.files.length).toBe(1); + expect(RepoStore.files[0].name).toBe('app'); + expect(RepoStore.files[0].tempFile).toBeUndefined(); + expect(RepoStore.files[0].files[0].tempFile).toBeTruthy(); + expect(RepoStore.files[0].files[0].name).toBe('test'); + expect(RepoStore.files[0].files[0].files[0].name).toBe('.gitkeep'); + }); + + it('does not create new tree when already exists', () => { + RepoStore.files.push(RepoHelper.serializeRepoEntity('tree', { + name: 'app', + })); + + vm.entryName = 'app'; + vm.$el.querySelector('.btn-success').click(); + + expect(RepoStore.files.length).toBe(1); + expect(RepoStore.files[0].name).toBe('app'); + expect(RepoStore.files[0].tempFile).toBeUndefined(); + expect(RepoStore.files[0].files.length).toBe(0); + }); + }); + + it('focuses field on mount', () => { + document.body.innerHTML += '<div class="js-test"></div>'; + + vm = createComponent(Component, { + type: 'tree', + }, '.js-test'); + + expect(document.activeElement).toBe(vm.$refs.fieldName); + + vm.$el.remove(); + }); +}); |