From 47c906eb57d95eacd336aa89541f8a7071b746bf Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 27 Oct 2017 18:16:28 +0100 Subject: updated karma specs --- .../javascripts/repo/components/repo_editor.vue | 2 +- app/assets/javascripts/repo/stores/actions.js | 17 +- app/assets/javascripts/repo/stores/actions/file.js | 8 +- app/assets/javascripts/repo/stores/actions/tree.js | 12 +- app/assets/javascripts/repo/stores/getters.js | 6 +- app/assets/javascripts/repo/stores/index.js | 2 +- app/assets/javascripts/repo/stores/state.js | 4 +- app/assets/javascripts/repo/stores/utils.js | 2 +- .../helpers/vue_mount_component_helper.js | 5 + .../repo/components/new_branch_form_spec.js | 76 ++++---- .../repo/components/new_dropdown/index_spec.js | 134 +------------ .../repo/components/new_dropdown/modal_spec.js | 176 +++++++++++++++--- .../repo/components/repo_commit_section_spec.js | 207 ++++++--------------- .../repo/components/repo_edit_button_spec.js | 81 +++++--- .../repo/components/repo_editor_spec.js | 48 +++-- .../repo/components/repo_file_buttons_spec.js | 83 +++------ spec/javascripts/repo/components/repo_file_spec.js | 57 ++---- .../repo/components/repo_loading_file_spec.js | 42 ++--- .../repo/components/repo_prev_directory_spec.js | 56 +++--- .../repo/components/repo_preview_spec.js | 32 +++- .../repo/components/repo_sidebar_spec.js | 168 ++++------------- spec/javascripts/repo/components/repo_spec.js | 87 ++------- spec/javascripts/repo/components/repo_tab_spec.js | 94 ++++++---- spec/javascripts/repo/components/repo_tabs_spec.js | 38 ++-- spec/javascripts/repo/helpers.js | 20 ++ spec/javascripts/repo/mock_data.js | 14 -- .../javascripts/repo/services/repo_service_spec.js | 171 ----------------- 27 files changed, 612 insertions(+), 1030 deletions(-) create mode 100644 spec/javascripts/repo/helpers.js delete mode 100644 spec/javascripts/repo/mock_data.js delete mode 100644 spec/javascripts/repo/services/repo_service_spec.js diff --git a/app/assets/javascripts/repo/components/repo_editor.vue b/app/assets/javascripts/repo/components/repo_editor.vue index 9114b3a1acd..6e9669403f2 100644 --- a/app/assets/javascripts/repo/components/repo_editor.vue +++ b/app/assets/javascripts/repo/components/repo_editor.vue @@ -74,7 +74,7 @@ export default { }, watch: { activeFile(oldVal, newVal) { - if (!newVal.active) { + if (newVal && !newVal.active) { this.initMonaco(); } }, diff --git a/app/assets/javascripts/repo/stores/actions.js b/app/assets/javascripts/repo/stores/actions.js index c8be50116eb..d5dc57e331b 100644 --- a/app/assets/javascripts/repo/stores/actions.js +++ b/app/assets/javascripts/repo/stores/actions.js @@ -2,9 +2,8 @@ import Vue from 'vue'; import flash from '../../flash'; import service from '../services'; import * as types from './mutation_types'; -import { visitUrl } from '../../lib/utils/url_utility'; -export const redirectToUrl = url => visitUrl(url); +export const redirectToUrl = url => gl.utils.visitUrl(url); export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data); @@ -26,7 +25,7 @@ export const closeAllFiles = ({ state, dispatch }) => { state.openFiles.forEach(file => dispatch('closeFile', { file })); }; -export const toggleEditMode = ({ commit, getters, dispatch }, force = false) => { +export const toggleEditMode = ({ state, commit, getters, dispatch }, force = false) => { const changedFiles = getters.changedFiles; if (changedFiles.length && !force) { @@ -35,7 +34,10 @@ export const toggleEditMode = ({ commit, getters, dispatch }, force = false) => commit(types.TOGGLE_EDIT_MODE); commit(types.TOGGLE_DISCARD_POPUP, false); dispatch('toggleBlobView'); - dispatch('discardAllChanges'); + + if (!state.editMode) { + dispatch('discardAllChanges'); + } } }; @@ -111,9 +113,12 @@ export const popHistoryState = ({ state, dispatch, getters }) => { export const scrollToTab = () => { Vue.nextTick(() => { const tabs = document.getElementById('tabs'); - const tabEl = tabs.querySelector('.active'); - tabs.scrollLeft = tabEl.offsetLeft; + if (tabs) { + const tabEl = tabs.querySelector('.active'); + + tabs.scrollLeft = tabEl.offsetLeft; + } }); }; diff --git a/app/assets/javascripts/repo/stores/actions/file.js b/app/assets/javascripts/repo/stores/actions/file.js index 5759ff07aef..013a0b3f4da 100644 --- a/app/assets/javascripts/repo/stores/actions/file.js +++ b/app/assets/javascripts/repo/stores/actions/file.js @@ -3,6 +3,7 @@ import flash from '../../../flash'; import service from '../../services'; import * as types from '../mutation_types'; import { + findEntry, pushState, setPageTitle, createTemp, @@ -88,11 +89,16 @@ export const createTempFile = ({ state, commit, dispatch }, { tree, name }) => { changed: true, }); + if (findEntry(tree, 'blob', file.name)) return; + commit(types.CREATE_TMP_FILE, { parent: tree, file, }); commit(types.TOGGLE_FILE_OPEN, file); dispatch('setFileActive', file); - dispatch('toggleEditMode', true); + + if (!state.editMode) { + dispatch('toggleEditMode', true); + } }; diff --git a/app/assets/javascripts/repo/stores/actions/tree.js b/app/assets/javascripts/repo/stores/actions/tree.js index 9d084a952a2..129743c66c2 100644 --- a/app/assets/javascripts/repo/stores/actions/tree.js +++ b/app/assets/javascripts/repo/stores/actions/tree.js @@ -76,7 +76,7 @@ export const clickedTreeRow = ({ commit, dispatch }, row) => { export const createTempTree = ({ state, commit, dispatch }, name) => { let tree = state; - const dirNames = name.replace(`${state.path}/`, '').split('/'); + const dirNames = name.replace(new RegExp(`^${state.path}/`), '').split('/'); dirNames.forEach((dirName) => { const foundEntry = findEntry(tree, 'tree', dirName); @@ -101,8 +101,10 @@ export const createTempTree = ({ state, commit, dispatch }, name) => { } }); - dispatch('createTempFile', { - tree, - name: '.gitkeep', - }); + if (tree.tempFile) { + dispatch('createTempFile', { + tree, + name: '.gitkeep', + }); + } }; diff --git a/app/assets/javascripts/repo/stores/getters.js b/app/assets/javascripts/repo/stores/getters.js index 1c83c943ef7..9122578cc79 100644 --- a/app/assets/javascripts/repo/stores/getters.js +++ b/app/assets/javascripts/repo/stores/getters.js @@ -15,11 +15,7 @@ export const changedFiles = (state) => { return files.filter(file => file.changed); }; -export const activeFile = (state) => { - const openedFiles = state.openFiles; - - return openedFiles.find(file => file.active); -}; +export const activeFile = state => state.openFiles.find(file => file.active); export const activeFileExtension = (state) => { const file = activeFile(state); diff --git a/app/assets/javascripts/repo/stores/index.js b/app/assets/javascripts/repo/stores/index.js index f8565fb244c..6ac9bfd8189 100644 --- a/app/assets/javascripts/repo/stores/index.js +++ b/app/assets/javascripts/repo/stores/index.js @@ -8,7 +8,7 @@ import mutations from './mutations'; Vue.use(Vuex); export default new Vuex.Store({ - state, + state: state(), actions, mutations, getters, diff --git a/app/assets/javascripts/repo/stores/state.js b/app/assets/javascripts/repo/stores/state.js index 643239eee19..1301350c627 100644 --- a/app/assets/javascripts/repo/stores/state.js +++ b/app/assets/javascripts/repo/stores/state.js @@ -1,4 +1,4 @@ -export default { +export default () => ({ project: { id: 0, name: '', @@ -20,4 +20,4 @@ export default { openFiles: [], parentTreeUrl: '', previousUrl: '', -}; +}); diff --git a/app/assets/javascripts/repo/stores/utils.js b/app/assets/javascripts/repo/stores/utils.js index feafcebcfe2..5d5ee99ed43 100644 --- a/app/assets/javascripts/repo/stores/utils.js +++ b/app/assets/javascripts/repo/stores/utils.js @@ -36,8 +36,8 @@ export const decorateData = (entity, projectUrl = '') => { last_commit, tree_url, path, - tempFile, renderError, + tempFile = false, active = false, opened = false, changed = false, diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index b71136c4114..34acdfbfba9 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -1,3 +1,8 @@ +export const createComponentWithStore = (Component, store, propsData = {}) => new Component({ + store, + propsData, +}); + export default (Component, props = {}, el = null) => new Component({ propsData: props, }).$mount(el); diff --git a/spec/javascripts/repo/components/new_branch_form_spec.js b/spec/javascripts/repo/components/new_branch_form_spec.js index c9c5ce096fc..9a705a1f0ed 100644 --- a/spec/javascripts/repo/components/new_branch_form_spec.js +++ b/spec/javascripts/repo/components/new_branch_form_spec.js @@ -1,8 +1,8 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import newBranchForm from '~/repo/components/new_branch_form.vue'; -import eventHub from '~/repo/event_hub'; -import RepoStore from '~/repo/stores/repo_store'; -import createComponent from '../../helpers/vue_mount_component_helper'; +import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; +import { resetStore } from '../helpers'; describe('Multi-file editor new branch form', () => { let vm; @@ -10,17 +10,17 @@ describe('Multi-file editor new branch form', () => { beforeEach(() => { const Component = Vue.extend(newBranchForm); - RepoStore.currentBranch = 'master'; + vm = createComponentWithStore(Component, store); - vm = createComponent(Component, { - currentBranch: RepoStore.currentBranch, - }); + vm.$store.state.currentBranch = 'master'; + + vm.$mount(); }); afterEach(() => { vm.$destroy(); - RepoStore.currentBranch = ''; + resetStore(vm.$store); }); describe('template', () => { @@ -48,6 +48,10 @@ describe('Multi-file editor new branch form', () => { }); describe('submitNewBranch', () => { + beforeEach(() => { + spyOn(vm, 'createNewBranch').and.returnValue(Promise.resolve()); + }); + it('sets to loading', () => { vm.submitNewBranch(); @@ -66,57 +70,45 @@ describe('Multi-file editor new branch form', () => { }); }); - it('emits an event with branchName', () => { - spyOn(eventHub, '$emit'); - + it('calls createdNewBranch with branchName', () => { vm.branchName = 'testing'; vm.submitNewBranch(); - expect(eventHub.$emit).toHaveBeenCalledWith('createNewBranch', 'testing'); + expect(vm.createNewBranch).toHaveBeenCalledWith('testing'); }); }); - describe('showErrorMessage', () => { - it('sets loading to false', () => { - vm.loading = true; - - vm.showErrorMessage(); - - expect(vm.loading).toBeFalsy(); - }); - - it('creates flash element', () => { - vm.showErrorMessage('error message'); - - expect(vm.$el.querySelector('.flash-alert')).not.toBeNull(); - expect(vm.$el.querySelector('.flash-alert').textContent.trim()).toBe('error message'); + describe('submitNewBranch with error', () => { + beforeEach(() => { + spyOn(vm, 'createNewBranch').and.returnValue(Promise.reject({ + json: () => Promise.resolve({ + message: 'error message', + }), + })); }); - }); - describe('createdNewBranch', () => { - it('set loading to false', () => { + it('sets loading to false', (done) => { vm.loading = true; - vm.createdNewBranch(); - - expect(vm.loading).toBeFalsy(); - }); - - it('resets branch name', () => { - vm.branchName = 'testing'; + vm.submitNewBranch(); - vm.createdNewBranch(); + setTimeout(() => { + expect(vm.loading).toBeFalsy(); - expect(vm.branchName).toBe(''); + done(); + }); }); - it('sets the dropdown toggle text', () => { - vm.dropdownText = document.createElement('span'); + it('creates flash element', (done) => { + vm.submitNewBranch(); - vm.createdNewBranch('branch name'); + setTimeout(() => { + expect(vm.$el.querySelector('.flash-alert')).not.toBeNull(); + expect(vm.$el.querySelector('.flash-alert').textContent.trim()).toBe('error message'); - expect(vm.dropdownText.textContent).toBe('branch name'); + done(); + }); }); }); }); diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js index ddbfdab582d..dd751f32333 100644 --- a/spec/javascripts/repo/components/new_dropdown/index_spec.js +++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js @@ -1,9 +1,8 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import newDropdown from '~/repo/components/new_dropdown/index.vue'; -import RepoStore from '~/repo/stores/repo_store'; -import RepoHelper from '~/repo/helpers/repo_helper'; -import eventHub from '~/repo/event_hub'; -import createComponent from '../../../helpers/vue_mount_component_helper'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { resetStore } from '../../helpers'; describe('new dropdown component', () => { let vm; @@ -11,15 +10,13 @@ describe('new dropdown component', () => { beforeEach(() => { const component = Vue.extend(newDropdown); - vm = createComponent(component); + vm = createComponentWithStore(component, store).$mount(); }); afterEach(() => { vm.$destroy(); - RepoStore.files = []; - RepoStore.openedFiles = []; - RepoStore.setViewToPreview(); + resetStore(vm.$store); }); it('renders new file and new directory links', () => { @@ -67,125 +64,4 @@ describe('new dropdown component', () => { .catch(done.fail); }); }); - - describe('createEntryInStore', () => { - ['tree', 'blob'].forEach((type) => { - describe(type, () => { - it('closes modal after creating file', () => { - vm.openModal = true; - - eventHub.$emit('createNewEntry', 'testing', type); - - expect(vm.openModal).toBeFalsy(); - }); - - it('sets editMode to true', () => { - eventHub.$emit('createNewEntry', 'testing', type); - - expect(RepoStore.editMode).toBeTruthy(); - }); - - it('toggles blob view', () => { - eventHub.$emit('createNewEntry', 'testing', type); - - expect(RepoStore.isPreviewView()).toBeFalsy(); - }); - - it('adds file into activeFiles', () => { - eventHub.$emit('createNewEntry', 'testing', type); - - expect(RepoStore.openedFiles.length).toBe(1); - }); - - it(`creates ${type} in the current stores path`, () => { - RepoStore.path = 'testing'; - - eventHub.$emit('createNewEntry', 'testing/app', type); - - expect(RepoStore.files[0].path).toBe('testing/app'); - expect(RepoStore.files[0].name).toBe('app'); - - if (type === 'tree') { - expect(RepoStore.files[0].files.length).toBe(1); - } - - RepoStore.path = ''; - }); - }); - }); - - describe('file', () => { - it('creates new file', () => { - eventHub.$emit('createNewEntry', 'testing', 'blob'); - - 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', - })); - - eventHub.$emit('createNewEntry', 'testing', 'blob'); - - 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', () => { - it('creates new tree', () => { - eventHub.$emit('createNewEntry', 'testing', 'tree'); - - 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', () => { - eventHub.$emit('createNewEntry', 'app/test', 'tree'); - - 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', - })); - - eventHub.$emit('createNewEntry', 'app/test', 'tree'); - - 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', - })); - - eventHub.$emit('createNewEntry', 'app', 'tree'); - - 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); - }); - }); - }); }); diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js index 4c5cdc47c6e..ed0a443fded 100644 --- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js +++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js @@ -1,8 +1,8 @@ import Vue from 'vue'; -import RepoStore from '~/repo/stores/repo_store'; +import store from '~/repo/stores'; import modal from '~/repo/components/new_dropdown/modal.vue'; -import eventHub from '~/repo/event_hub'; -import createComponent from '../../../helpers/vue_mount_component_helper'; +import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; +import { file, resetStore } from '../../helpers'; describe('new file modal component', () => { const Component = Vue.extend(modal); @@ -11,18 +11,17 @@ describe('new file modal component', () => { afterEach(() => { vm.$destroy(); - RepoStore.files = []; - RepoStore.openedFiles = []; - RepoStore.setViewToPreview(); + resetStore(vm.$store); }); ['tree', 'blob'].forEach((type) => { describe(type, () => { beforeEach(() => { - vm = createComponent(Component, { + vm = createComponentWithStore(Component, store, { type, - currentPath: RepoStore.path, - }); + }).$mount(); + + vm.entryName = 'testing'; }); it(`sets modal title as ${type}`, () => { @@ -42,35 +41,156 @@ describe('new file modal component', () => { expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe(`${title} name`); }); + + describe('createEntryInStore', () => { + it('calls createTempEntry', () => { + spyOn(vm, 'createTempEntry'); + + vm.createEntryInStore(); + + expect(vm.createTempEntry).toHaveBeenCalledWith({ + name: 'testing', + type, + }); + }); + + it('sets editMode to true', (done) => { + vm.createEntryInStore(); + + setTimeout(() => { + expect(vm.$store.state.editMode).toBeTruthy(); + + done(); + }); + }); + + it('toggles blob view', (done) => { + vm.createEntryInStore(); + + setTimeout(() => { + expect(vm.$store.state.currentBlobView).toBe('repo-editor'); + + done(); + }); + }); + + it('opens newly created file', (done) => { + vm.createEntryInStore(); + + setTimeout(() => { + expect(vm.$store.state.openFiles.length).toBe(1); + expect(vm.$store.state.openFiles[0].name).toBe(type === 'blob' ? 'testing' : '.gitkeep'); + + done(); + }); + }); + + it(`creates ${type} in the current stores path`, (done) => { + vm.$store.state.path = 'app'; + + vm.createEntryInStore(); + + setTimeout(() => { + expect(vm.$store.state.tree[0].path).toBe('app/testing'); + expect(vm.$store.state.tree[0].name).toBe('testing'); + + if (type === 'tree') { + expect(vm.$store.state.tree[0].tree.length).toBe(1); + } + + done(); + }); + }); + + if (type === 'blob') { + it('creates new file', (done) => { + vm.createEntryInStore(); + + setTimeout(() => { + expect(vm.$store.state.tree.length).toBe(1); + expect(vm.$store.state.tree[0].name).toBe('testing'); + expect(vm.$store.state.tree[0].type).toBe('blob'); + expect(vm.$store.state.tree[0].tempFile).toBeTruthy(); + + done(); + }); + }); + + it('does not create temp file when file already exists', (done) => { + vm.$store.state.tree.push(file('testing', '1', type)); + + vm.createEntryInStore(); + + setTimeout(() => { + expect(vm.$store.state.tree.length).toBe(1); + expect(vm.$store.state.tree[0].name).toBe('testing'); + expect(vm.$store.state.tree[0].type).toBe('blob'); + expect(vm.$store.state.tree[0].tempFile).toBeFalsy(); + + done(); + }); + }); + } else { + it('creates new tree', () => { + vm.createEntryInStore(); + + expect(vm.$store.state.tree.length).toBe(1); + expect(vm.$store.state.tree[0].name).toBe('testing'); + expect(vm.$store.state.tree[0].type).toBe('tree'); + expect(vm.$store.state.tree[0].tempFile).toBeTruthy(); + expect(vm.$store.state.tree[0].tree.length).toBe(1); + expect(vm.$store.state.tree[0].tree[0].name).toBe('.gitkeep'); + }); + + it('creates multiple trees when entryName has slashes', () => { + vm.entryName = 'app/test'; + vm.createEntryInStore(); + + expect(vm.$store.state.tree.length).toBe(1); + expect(vm.$store.state.tree[0].name).toBe('app'); + expect(vm.$store.state.tree[0].tree[0].name).toBe('test'); + expect(vm.$store.state.tree[0].tree[0].tree[0].name).toBe('.gitkeep'); + }); + + it('creates tree in existing tree', () => { + vm.$store.state.tree.push(file('app', '1', 'tree')); + + vm.entryName = 'app/test'; + vm.createEntryInStore(); + + expect(vm.$store.state.tree.length).toBe(1); + expect(vm.$store.state.tree[0].name).toBe('app'); + expect(vm.$store.state.tree[0].tempFile).toBeFalsy(); + expect(vm.$store.state.tree[0].tree[0].tempFile).toBeTruthy(); + expect(vm.$store.state.tree[0].tree[0].name).toBe('test'); + expect(vm.$store.state.tree[0].tree[0].tree[0].name).toBe('.gitkeep'); + }); + + it('does not create new tree when already exists', () => { + vm.$store.state.tree.push(file('app', '1', 'tree')); + + vm.entryName = 'app'; + vm.createEntryInStore(); + + expect(vm.$store.state.tree.length).toBe(1); + expect(vm.$store.state.tree[0].name).toBe('app'); + expect(vm.$store.state.tree[0].tempFile).toBeFalsy(); + expect(vm.$store.state.tree[0].tree.length).toBe(0); + }); + } + }); }); }); it('focuses field on mount', () => { document.body.innerHTML += '
'; - vm = createComponent(Component, { + vm = createComponentWithStore(Component, store, { type: 'tree', - currentPath: RepoStore.path, - }, '.js-test'); + }).$mount('.js-test'); expect(document.activeElement).toBe(vm.$refs.fieldName); vm.$el.remove(); }); - - describe('createEntryInStore', () => { - it('emits createNewEntry event', () => { - spyOn(eventHub, '$emit'); - - vm = createComponent(Component, { - type: 'tree', - currentPath: RepoStore.path, - }); - vm.entryName = 'testing'; - - vm.createEntryInStore(); - - expect(eventHub.$emit).toHaveBeenCalledWith('createNewEntry', 'testing', 'tree'); - }); - }); }); diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js index e09d593f04c..0f991e1b727 100644 --- a/spec/javascripts/repo/components/repo_commit_section_spec.js +++ b/spec/javascripts/repo/components/repo_commit_section_spec.js @@ -1,56 +1,43 @@ import Vue from 'vue'; +import store from '~/repo/stores'; +import service from '~/repo/services'; import repoCommitSection from '~/repo/components/repo_commit_section.vue'; -import RepoStore from '~/repo/stores/repo_store'; -import RepoService from '~/repo/services/repo_service'; import getSetTimeoutPromise from '../../helpers/set_timeout_promise_helper'; +import { file, resetStore } from '../helpers'; describe('RepoCommitSection', () => { - const branch = 'master'; - const projectUrl = 'projectUrl'; - let changedFiles; - let openedFiles; + let vm; - RepoStore.projectUrl = projectUrl; - - function createComponent(el) { + function createComponent() { const RepoCommitSection = Vue.extend(repoCommitSection); - return new RepoCommitSection().$mount(el); + const comp = new RepoCommitSection({ + store, + }).$mount(); + + comp.$store.state.currentBranch = 'master'; + comp.$store.state.openFiles = [file(), file()]; + comp.$store.state.openFiles.forEach(f => Object.assign(f, { + changed: true, + content: 'testing', + })); + + return comp.$mount(); } beforeEach(() => { - // Create a copy for each test because these can get modified directly - changedFiles = [{ - id: 0, - changed: true, - url: `/namespace/${projectUrl}/blob/${branch}/dir/file0.ext`, - path: 'dir/file0.ext', - newContent: 'a', - }, { - id: 1, - changed: true, - url: `/namespace/${projectUrl}/blob/${branch}/dir/file1.ext`, - path: 'dir/file1.ext', - newContent: 'b', - }]; - openedFiles = changedFiles.concat([{ - id: 2, - url: `/namespace/${projectUrl}/blob/${branch}/dir/file2.ext`, - path: 'dir/file2.ext', - changed: false, - }]); + vm = createComponent(); }); - it('renders a commit section', () => { - RepoStore.isCommitable = true; - RepoStore.currentBranch = branch; - RepoStore.targetBranch = branch; - RepoStore.openedFiles = openedFiles; + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); - const vm = createComponent(); + it('renders a commit section', () => { const changedFileElements = [...vm.$el.querySelectorAll('.changed-files > li')]; - const commitMessage = vm.$el.querySelector('#commit-message'); - const submitCommit = vm.$refs.submitCommit; + const submitCommit = vm.$el.querySelector('.btn'); const targetBranch = vm.$el.querySelector('.target-branch'); expect(vm.$el.querySelector(':scope > form')).toBeTruthy(); @@ -58,160 +45,70 @@ describe('RepoCommitSection', () => { expect(changedFileElements.length).toEqual(2); changedFileElements.forEach((changedFile, i) => { - expect(changedFile.textContent.trim()).toEqual(changedFiles[i].path); + expect(changedFile.textContent.trim()).toEqual(vm.$store.getters.changedFiles[i].path); }); - expect(commitMessage.tagName).toEqual('TEXTAREA'); - expect(commitMessage.name).toEqual('commit-message'); - expect(submitCommit.type).toEqual('submit'); expect(submitCommit.disabled).toBeTruthy(); expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeFalsy(); expect(vm.$el.querySelector('.commit-summary').textContent.trim()).toEqual('Commit 2 files'); expect(targetBranch.querySelector(':scope > label').textContent.trim()).toEqual('Target branch'); - expect(targetBranch.querySelector('.help-block').textContent.trim()).toEqual(branch); - }); - - it('does not render if not isCommitable', () => { - RepoStore.isCommitable = false; - RepoStore.openedFiles = [{ - id: 0, - changed: true, - }]; - - const vm = createComponent(); - - expect(vm.$el.innerHTML).toBeFalsy(); - }); - - it('does not render if no changedFiles', () => { - RepoStore.isCommitable = true; - RepoStore.openedFiles = []; - - const vm = createComponent(); - - expect(vm.$el.innerHTML).toBeFalsy(); + expect(targetBranch.querySelector('.help-block').textContent.trim()).toEqual('master'); }); describe('when submitting', () => { - let el; - let vm; - const projectId = 'projectId'; - const commitMessage = 'commitMessage'; - - beforeEach((done) => { - RepoStore.isCommitable = true; - RepoStore.currentBranch = branch; - RepoStore.targetBranch = branch; - RepoStore.openedFiles = openedFiles; - RepoStore.projectId = projectId; - - // We need to append to body to get form `submit` events working - // Otherwise we run into, "Form submission canceled because the form is not connected" - // See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm - el = document.createElement('div'); - document.body.appendChild(el); - - vm = createComponent(el); - vm.commitMessage = commitMessage; - - spyOn(vm, 'tryCommit').and.callThrough(); - spyOn(vm, 'redirectToNewMr').and.stub(); - spyOn(vm, 'redirectToBranch').and.stub(); - spyOn(RepoService, 'commitFiles').and.returnValue(Promise.resolve()); - spyOn(RepoService, 'getBranch').and.returnValue(Promise.resolve({ - commit: { - id: 1, - short_id: 1, - }, - })); - - // Wait for the vm data to be in place - Vue.nextTick(() => { - done(); - }); - }); + let changedFiles; - afterEach(() => { - vm.$destroy(); - el.remove(); - RepoStore.openedFiles = []; - }); + beforeEach(() => { + vm.commitMessage = 'testing'; + changedFiles = JSON.parse(JSON.stringify(vm.$store.getters.changedFiles)); - it('shows commit message', () => { - const commitMessageEl = vm.$el.querySelector('#commit-message'); - expect(commitMessageEl.value).toBe(commitMessage); + spyOn(service, 'commit').and.returnValue(Promise.resolve({ + short_id: '1', + stats: {}, + })); }); it('allows you to submit', () => { - const submitCommit = vm.$refs.submitCommit; - expect(submitCommit.disabled).toBeFalsy(); + expect(vm.$el.querySelector('.btn').disabled).toBeTruthy(); }); - it('shows commit submit and summary if commitMessage and spinner if submitCommitsLoading', (done) => { - const submitCommit = vm.$refs.submitCommit; - submitCommit.click(); + it('submits commit', (done) => { + vm.makeCommit(); // Wait for the branch check to finish getSetTimeoutPromise() .then(() => Vue.nextTick()) .then(() => { - expect(vm.tryCommit).toHaveBeenCalled(); - expect(submitCommit.querySelector('.js-commit-loading-icon')).toBeTruthy(); - expect(vm.redirectToBranch).toHaveBeenCalled(); - - const args = RepoService.commitFiles.calls.allArgs()[0]; - const { commit_message, actions, branch: payloadBranch } = args[0]; + const args = service.commit.calls.allArgs()[0]; + const { commit_message, actions, branch: payloadBranch } = args[1]; - expect(commit_message).toBe(commitMessage); + expect(commit_message).toBe('testing'); expect(actions.length).toEqual(2); - expect(payloadBranch).toEqual(branch); + expect(payloadBranch).toEqual('master'); expect(actions[0].action).toEqual('update'); expect(actions[1].action).toEqual('update'); - expect(actions[0].content).toEqual(openedFiles[0].newContent); - expect(actions[1].content).toEqual(openedFiles[1].newContent); - expect(actions[0].file_path).toEqual(openedFiles[0].path); - expect(actions[1].file_path).toEqual(openedFiles[1].path); + expect(actions[0].content).toEqual(changedFiles[0].content); + expect(actions[1].content).toEqual(changedFiles[1].content); + expect(actions[0].file_path).toEqual(changedFiles[0].path); + expect(actions[1].file_path).toEqual(changedFiles[1].path); }) .then(done) .catch(done.fail); }); it('redirects to MR creation page if start new MR checkbox checked', (done) => { + spyOn(gl.utils, 'visitUrl'); vm.startNewMR = true; - Vue.nextTick() - .then(() => { - const submitCommit = vm.$refs.submitCommit; - submitCommit.click(); - }) - // Wait for the branch check to finish - .then(() => getSetTimeoutPromise()) + vm.makeCommit(); + + getSetTimeoutPromise() + .then(() => Vue.nextTick()) .then(() => { - expect(vm.redirectToNewMr).toHaveBeenCalled(); + expect(gl.utils.visitUrl).toHaveBeenCalled(); }) .then(done) .catch(done.fail); }); }); - - describe('methods', () => { - describe('resetCommitState', () => { - it('should reset store vars and scroll to top', () => { - const vm = { - submitCommitsLoading: true, - changedFiles: new Array(10), - openedFiles: new Array(3), - commitMessage: 'commitMessage', - editMode: true, - }; - - repoCommitSection.methods.resetCommitState.call(vm); - - expect(vm.submitCommitsLoading).toEqual(false); - expect(vm.changedFiles).toEqual([]); - expect(vm.commitMessage).toEqual(''); - expect(vm.editMode).toEqual(false); - }); - }); - }); }); diff --git a/spec/javascripts/repo/components/repo_edit_button_spec.js b/spec/javascripts/repo/components/repo_edit_button_spec.js index dff2fac191d..aee2dd6ad55 100644 --- a/spec/javascripts/repo/components/repo_edit_button_spec.js +++ b/spec/javascripts/repo/components/repo_edit_button_spec.js @@ -1,45 +1,78 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repoEditButton from '~/repo/components/repo_edit_button.vue'; -import RepoStore from '~/repo/stores/repo_store'; +import { file, resetStore } from '../helpers'; describe('RepoEditButton', () => { - function createComponent() { + let vm; + + beforeEach(() => { + const f = file(); const RepoEditButton = Vue.extend(repoEditButton); - return new RepoEditButton().$mount(); - } + vm = new RepoEditButton({ + store, + }); + + f.active = true; + f.changed = true; + vm.$store.dispatch('setInitialData', { + canCommit: true, + onTopOfBranch: true, + }); + vm.$store.state.openFiles.push(f); + }); afterEach(() => { - RepoStore.openedFiles = []; + vm.$destroy(); + + resetStore(vm.$store); }); - it('renders an edit button that toggles the view state', (done) => { - RepoStore.isCommitable = true; - RepoStore.changedFiles = []; - RepoStore.binary = false; - RepoStore.openedFiles = [{}, {}]; + it('renders an edit button', () => { + vm.$mount(); - const vm = createComponent(); + expect(vm.$el.querySelector('.btn')).not.toBeNull(); + expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Edit'); + }); - expect(vm.$el.tagName).toEqual('BUTTON'); - expect(vm.$el.textContent).toMatch('Edit'); + it('renders edit button with cancel text', () => { + vm.$store.state.editMode = true; - spyOn(vm, 'editCancelClicked').and.callThrough(); + vm.$mount(); - vm.$el.click(); + expect(vm.$el.querySelector('.btn')).not.toBeNull(); + expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit'); + }); - Vue.nextTick(() => { - expect(vm.editCancelClicked).toHaveBeenCalled(); - expect(vm.$el.textContent).toMatch('Cancel edit'); - done(); - }); + it('toggles edit mode on click', () => { + vm.$mount(); + + vm.$el.querySelector('.btn').click(); + + expect(vm.$el.querySelector('.btn').textContent.trim()).toBe('Cancel edit'); }); - it('does not render if not isCommitable', () => { - RepoStore.isCommitable = false; + describe('discardPopupOpen', () => { + beforeEach(() => { + vm.$store.state.discardPopupOpen = true; + + vm.$mount(); + }); + + it('renders popup', () => { + expect(vm.$el.querySelector('.modal')).not.toBeNull(); + }); + + it('removes all changed files', (done) => { + vm.$el.querySelector('.btn-warning').click(); - const vm = createComponent(); + vm.$nextTick(() => { + expect(vm.$store.getters.changedFiles.length).toBe(0); + expect(vm.$el.querySelector('.modal')).toBeNull(); - expect(vm.$el.innerHTML).toBeUndefined(); + done(); + }); + }); }); }); diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js index a25a600b3be..82f914b7c9d 100644 --- a/spec/javascripts/repo/components/repo_editor_spec.js +++ b/spec/javascripts/repo/components/repo_editor_spec.js @@ -1,52 +1,46 @@ import Vue from 'vue'; -import RepoStore from '~/repo/stores/repo_store'; +import store from '~/repo/stores'; import repoEditor from '~/repo/components/repo_editor.vue'; +import { file, resetStore } from '../helpers'; describe('RepoEditor', () => { + let vm; + beforeEach(() => { + const f = file(); const RepoEditor = Vue.extend(repoEditor); - this.vm = new RepoEditor().$mount(); + vm = new RepoEditor({ + store, + }); + + f.active = true; + f.tempFile = true; + vm.$store.state.openFiles.push(f); + vm.monaco = true; + + vm.$mount(); }); afterEach(() => { - RepoStore.openedFiles = []; + vm.$destroy(); + + resetStore(vm.$store); }); it('renders an ide container', (done) => { - this.vm.openedFiles = ['idiidid']; - this.vm.binary = false; - Vue.nextTick(() => { - expect(this.vm.shouldHideEditor).toBe(false); - expect(this.vm.$el.id).toEqual('ide'); - expect(this.vm.$el.tagName).toBe('DIV'); + expect(vm.shouldHideEditor).toBeFalsy(); done(); }); }); - describe('when there are no open files', () => { - it('does not render the ide', (done) => { - this.vm.openedFiles = []; - - Vue.nextTick(() => { - expect(this.vm.shouldHideEditor).toBe(true); - expect(this.vm.$el.tagName).not.toBeDefined(); - done(); - }); - }); - }); - describe('when open file is binary and not raw', () => { it('does not render the IDE', (done) => { - this.vm.binary = true; - this.vm.activeFile = { - raw: false, - }; + vm.$store.getters.activeFile.binary = true; Vue.nextTick(() => { - expect(this.vm.shouldHideEditor).toBe(true); - expect(this.vm.$el.tagName).not.toBeDefined(); + expect(vm.shouldHideEditor).toBeTruthy(); done(); }); }); diff --git a/spec/javascripts/repo/components/repo_file_buttons_spec.js b/spec/javascripts/repo/components/repo_file_buttons_spec.js index 111c83ee50d..d6e255e4810 100644 --- a/spec/javascripts/repo/components/repo_file_buttons_spec.js +++ b/spec/javascripts/repo/components/repo_file_buttons_spec.js @@ -1,72 +1,49 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repoFileButtons from '~/repo/components/repo_file_buttons.vue'; -import RepoStore from '~/repo/stores/repo_store'; +import { file, resetStore } from '../helpers'; describe('RepoFileButtons', () => { - const activeFile = { - extension: 'md', - url: 'url', - raw_path: 'raw_path', - blame_path: 'blame_path', - commits_path: 'commits_path', - permalink: 'permalink', - }; + const activeFile = file(); + let vm; function createComponent() { const RepoFileButtons = Vue.extend(repoFileButtons); - return new RepoFileButtons().$mount(); + activeFile.rawPath = 'test'; + activeFile.blamePath = 'test'; + activeFile.commitsPath = 'test'; + activeFile.active = true; + store.state.openFiles.push(activeFile); + + return new RepoFileButtons({ + store, + }).$mount(); } afterEach(() => { - RepoStore.openedFiles = []; - }); - - it('renders Raw, Blame, History, Permalink and Preview toggle', () => { - const activeFileLabel = 'activeFileLabel'; - RepoStore.openedFiles = new Array(1); - RepoStore.activeFile = activeFile; - RepoStore.activeFileLabel = activeFileLabel; - RepoStore.editMode = true; - RepoStore.binary = false; + vm.$destroy(); - const vm = createComponent(); - const raw = vm.$el.querySelector('.raw'); - const blame = vm.$el.querySelector('.blame'); - const history = vm.$el.querySelector('.history'); - - expect(raw.href).toMatch(`/${activeFile.raw_path}`); - expect(raw.textContent.trim()).toEqual('Raw'); - expect(blame.href).toMatch(`/${activeFile.blame_path}`); - expect(blame.textContent.trim()).toEqual('Blame'); - expect(history.href).toMatch(`/${activeFile.commits_path}`); - expect(history.textContent.trim()).toEqual('History'); - expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual('Permalink'); - expect(vm.$el.querySelector('.preview').textContent.trim()).toEqual(activeFileLabel); + resetStore(vm.$store); }); - it('triggers rawPreviewToggle on preview click', () => { - RepoStore.openedFiles = new Array(1); - RepoStore.activeFile = activeFile; - RepoStore.editMode = true; - - const vm = createComponent(); - const preview = vm.$el.querySelector('.preview'); - - spyOn(vm, 'rawPreviewToggle'); - - preview.click(); - - expect(vm.rawPreviewToggle).toHaveBeenCalled(); - }); + it('renders Raw, Blame, History, Permalink and Preview toggle', (done) => { + vm = createComponent(); - it('does not render preview toggle if not canPreview', () => { - activeFile.extension = 'js'; - RepoStore.openedFiles = new Array(1); - RepoStore.activeFile = activeFile; + vm.$nextTick(() => { + const raw = vm.$el.querySelector('.raw'); + const blame = vm.$el.querySelector('.blame'); + const history = vm.$el.querySelector('.history'); - const vm = createComponent(); + expect(raw.href).toMatch(`/${activeFile.rawPath}`); + expect(raw.textContent.trim()).toEqual('Raw'); + expect(blame.href).toMatch(`/${activeFile.blamePath}`); + expect(blame.textContent.trim()).toEqual('Blame'); + expect(history.href).toMatch(`/${activeFile.commitsPath}`); + expect(history.textContent.trim()).toEqual('History'); + expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual('Permalink'); - expect(vm.$el.querySelector('.preview')).toBeFalsy(); + done(); + }); }); }); diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js index 8403df9be64..18ae461128e 100644 --- a/spec/javascripts/repo/components/repo_file_spec.js +++ b/spec/javascripts/repo/components/repo_file_spec.js @@ -1,32 +1,28 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repoFile from '~/repo/components/repo_file.vue'; -import RepoStore from '~/repo/stores/repo_store'; -import eventHub from '~/repo/event_hub'; -import { file } from '../mock_data'; +import { file, resetStore } from '../helpers'; describe('RepoFile', () => { const updated = 'updated'; - const otherFile = { - id: 'test', - html: '

html

', - pageTitle: 'otherpageTitle', - }; function createComponent(propsData) { const RepoFile = Vue.extend(repoFile); return new RepoFile({ + store, propsData, }).$mount(); } - beforeEach(() => { - RepoStore.openedFiles = []; + afterEach(() => { + resetStore(vm.$store); }); it('renders link, icon, name and last commit details', () => { const RepoFile = Vue.extend(repoFile); const vm = new RepoFile({ + store, propsData: { file: file(), }, @@ -54,12 +50,6 @@ describe('RepoFile', () => { expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy(); }); - it('sets the document title correctly', () => { - RepoStore.setActiveFiles(otherFile); - - expect(document.title.trim()).toEqual(otherFile.pageTitle); - }); - it('renders a spinner if the file is loading', () => { const f = file(); f.loading = true; @@ -71,27 +61,30 @@ describe('RepoFile', () => { expect(vm.$el.querySelector('.fa-spin.fa-spinner').style.marginLeft).toEqual(`${vm.file.level * 16}px`); }); - it('does not render commit message and datetime if mini', () => { - RepoStore.openedFiles.push(file()); - + it('does not render commit message and datetime if mini', (done) => { const vm = createComponent({ file: file(), }); + vm.$store.state.openFiles.push(vm.file); - expect(vm.$el.querySelector('.commit-message')).toBeFalsy(); - expect(vm.$el.querySelector('.commit-update')).toBeFalsy(); + vm.$nextTick(() => { + expect(vm.$el.querySelector('.commit-message')).toBeFalsy(); + expect(vm.$el.querySelector('.commit-update')).toBeFalsy(); + + done(); + }); }); - it('fires linkClicked when the link is clicked', () => { + it('fires clickedTreeRow when the link is clicked', () => { const vm = createComponent({ file: file(), }); - spyOn(vm, 'linkClicked'); + spyOn(vm, 'clickedTreeRow'); vm.$el.click(); - expect(vm.linkClicked).toHaveBeenCalledWith(vm.file); + expect(vm.clickedTreeRow).toHaveBeenCalledWith(vm.file); }); describe('submodule', () => { @@ -119,20 +112,4 @@ describe('RepoFile', () => { expect(vm.$el.querySelector('td').textContent.replace(/\s+/g, ' ')).toContain('submodule name @ 12345678'); }); }); - - describe('methods', () => { - describe('linkClicked', () => { - it('$emits fileNameClicked with file obj', () => { - spyOn(eventHub, '$emit'); - - const vm = createComponent({ - file: file(), - }); - - vm.linkClicked(vm.file); - - expect(eventHub.$emit).toHaveBeenCalledWith('fileNameClicked', vm.file); - }); - }); - }); }); diff --git a/spec/javascripts/repo/components/repo_loading_file_spec.js b/spec/javascripts/repo/components/repo_loading_file_spec.js index e9f95a02028..031f2a9c0b2 100644 --- a/spec/javascripts/repo/components/repo_loading_file_spec.js +++ b/spec/javascripts/repo/components/repo_loading_file_spec.js @@ -1,13 +1,16 @@ import Vue from 'vue'; -import RepoStore from '~/repo/stores/repo_store'; +import store from '~/repo/stores'; import repoLoadingFile from '~/repo/components/repo_loading_file.vue'; +import { resetStore } from '../helpers'; describe('RepoLoadingFile', () => { - function createComponent(propsData) { + let vm; + + function createComponent() { const RepoLoadingFile = Vue.extend(repoLoadingFile); return new RepoLoadingFile({ - propsData, + store, }).$mount(); } @@ -30,33 +33,30 @@ describe('RepoLoadingFile', () => { } afterEach(() => { - RepoStore.openedFiles = []; + vm.$destroy(); + + resetStore(vm.$store); }); it('renders 3 columns of animated LoC', () => { - const vm = createComponent({ - loading: { - tree: true, - }, - hasFiles: false, - }); + vm = createComponent(); const columns = [...vm.$el.querySelectorAll('td')]; expect(columns.length).toEqual(3); assertColumns(columns); }); - it('renders 1 column of animated LoC if isMini', () => { - RepoStore.openedFiles = new Array(1); - const vm = createComponent({ - loading: { - tree: true, - }, - hasFiles: false, - }); - const columns = [...vm.$el.querySelectorAll('td')]; + it('renders 1 column of animated LoC if isMini', (done) => { + vm = createComponent(); + vm.$store.state.openFiles.push('test'); - expect(columns.length).toEqual(1); - assertColumns(columns); + vm.$nextTick(() => { + const columns = [...vm.$el.querySelectorAll('td')]; + + expect(columns.length).toEqual(1); + assertColumns(columns); + + done(); + }); }); }); diff --git a/spec/javascripts/repo/components/repo_prev_directory_spec.js b/spec/javascripts/repo/components/repo_prev_directory_spec.js index 4c064f21084..7f82ae36a64 100644 --- a/spec/javascripts/repo/components/repo_prev_directory_spec.js +++ b/spec/javascripts/repo/components/repo_prev_directory_spec.js @@ -1,47 +1,45 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repoPrevDirectory from '~/repo/components/repo_prev_directory.vue'; -import eventHub from '~/repo/event_hub'; +import { resetStore } from '../helpers'; describe('RepoPrevDirectory', () => { - function createComponent(propsData) { + let vm; + const parentLink = 'parent'; + function createComponent() { const RepoPrevDirectory = Vue.extend(repoPrevDirectory); - return new RepoPrevDirectory({ - propsData, - }).$mount(); - } - - it('renders a prev dir link', () => { - const prevUrl = 'prevUrl'; - const vm = createComponent({ - prevUrl, + const comp = new RepoPrevDirectory({ + store, }); - const link = vm.$el.querySelector('a'); - spyOn(vm, 'linkClicked'); + comp.$store.state.parentTreeUrl = parentLink; - expect(link.href).toMatch(`/${prevUrl}`); - expect(link.textContent).toEqual('...'); + return comp.$mount(); + } + + beforeEach(() => { + vm = createComponent(); + }); - link.click(); + afterEach(() => { + vm.$destroy(); - expect(vm.linkClicked).toHaveBeenCalledWith(prevUrl); + resetStore(vm.$store); }); - describe('methods', () => { - describe('linkClicked', () => { - it('$emits linkclicked with prevUrl', () => { - const prevUrl = 'prevUrl'; - const vm = createComponent({ - prevUrl, - }); + it('renders a prev dir link', () => { + const link = vm.$el.querySelector('a'); - spyOn(eventHub, '$emit'); + expect(link.href).toMatch(`/${parentLink}`); + expect(link.textContent).toEqual('...'); + }); - vm.linkClicked(prevUrl); + it('clicking row triggers getTreeData', () => { + spyOn(vm, 'getTreeData'); - expect(eventHub.$emit).toHaveBeenCalledWith('goToPreviousDirectoryClicked', prevUrl); - }); - }); + vm.$el.querySelector('td').click(); + + expect(vm.getTreeData).toHaveBeenCalledWith({ endpoint: parentLink }); }); }); diff --git a/spec/javascripts/repo/components/repo_preview_spec.js b/spec/javascripts/repo/components/repo_preview_spec.js index 4920cf02083..8d1a87494cf 100644 --- a/spec/javascripts/repo/components/repo_preview_spec.js +++ b/spec/javascripts/repo/components/repo_preview_spec.js @@ -1,23 +1,37 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repoPreview from '~/repo/components/repo_preview.vue'; -import RepoStore from '~/repo/stores/repo_store'; +import { file, resetStore } from '../helpers'; describe('RepoPreview', () => { + let vm; + function createComponent() { + const f = file(); const RepoPreview = Vue.extend(repoPreview); - return new RepoPreview().$mount(); + const comp = new RepoPreview({ + store, + }); + + f.active = true; + f.html = 'test'; + + comp.$store.state.openFiles.push(f); + + return comp.$mount(); } - it('renders a div with the activeFile html', () => { - const activeFile = { - html: '

html

', - }; - RepoStore.activeFile = activeFile; + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); - const vm = createComponent(); + it('renders a div with the activeFile html', () => { + vm = createComponent(); expect(vm.$el.tagName).toEqual('DIV'); - expect(vm.$el.innerHTML).toContain(activeFile.html); + expect(vm.$el.innerHTML).toContain('test'); }); }); diff --git a/spec/javascripts/repo/components/repo_sidebar_spec.js b/spec/javascripts/repo/components/repo_sidebar_spec.js index 148f275e03d..7cb4dace491 100644 --- a/spec/javascripts/repo/components/repo_sidebar_spec.js +++ b/spec/javascripts/repo/components/repo_sidebar_spec.js @@ -1,32 +1,31 @@ import Vue from 'vue'; -import Helper from '~/repo/helpers/repo_helper'; -import RepoService from '~/repo/services/repo_service'; -import RepoStore from '~/repo/stores/repo_store'; +import store from '~/repo/stores'; import repoSidebar from '~/repo/components/repo_sidebar.vue'; -import { file } from '../mock_data'; +import { file, resetStore } from '../helpers'; describe('RepoSidebar', () => { let vm; - function createComponent() { + beforeEach(() => { const RepoSidebar = Vue.extend(repoSidebar); - return new RepoSidebar().$mount(); - } + vm = new RepoSidebar({ + store, + }); + + vm.$store.state.isRoot = true; + vm.$store.state.tree.push(file()); + + vm.$mount(); + }); afterEach(() => { vm.$destroy(); - RepoStore.files = []; - RepoStore.openedFiles = []; + resetStore(vm.$store); }); it('renders a sidebar', () => { - RepoStore.files = [file()]; - RepoStore.openedFiles = []; - RepoStore.isRoot = true; - - vm = createComponent(); const thead = vm.$el.querySelector('thead'); const tbody = vm.$el.querySelector('tbody'); @@ -41,139 +40,36 @@ describe('RepoSidebar', () => { expect(tbody.querySelector('.file')).toBeTruthy(); }); - it('does not render a thead, renders repo-file-options and sets sidebar-mini class if isMini', () => { - RepoStore.openedFiles = [{ - id: 0, - }]; - vm = createComponent(); - - expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy(); - expect(vm.$el.querySelector('thead')).toBeTruthy(); - expect(vm.$el.querySelector('thead .repo-file-options')).toBeTruthy(); - }); - - it('renders 5 loading files if tree is loading and not hasFiles', () => { - RepoStore.loading.tree = true; - RepoStore.files = []; - vm = createComponent(); + it('does not render a thead, renders repo-file-options and sets sidebar-mini class if isMini', (done) => { + vm.$store.state.openFiles.push(vm.$store.state.tree[0]); - expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5); - }); - - it('renders a prev directory if is not root', () => { - RepoStore.files = [file()]; - RepoStore.isRoot = false; - RepoStore.loading.tree = false; - vm = createComponent(); - - expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy(); - }); + Vue.nextTick(() => { + expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy(); + expect(vm.$el.querySelector('thead')).toBeTruthy(); + expect(vm.$el.querySelector('thead .repo-file-options')).toBeTruthy(); - describe('flattendFiles', () => { - it('returns a flattend array of files', () => { - const f = file(); - f.files.push(file('testing 123')); - const files = [f, file()]; - vm = createComponent(); - vm.files = files; - - expect(vm.flattendFiles.length).toBe(3); - expect(vm.flattendFiles[1].name).toBe('testing 123'); + done(); }); }); - describe('methods', () => { - describe('fileClicked', () => { - it('should fetch data for new file', () => { - spyOn(Helper, 'getContent').and.callThrough(); - RepoStore.files = [file()]; - RepoStore.isRoot = true; - vm = createComponent(); - - vm.fileClicked(RepoStore.files[0]); - - expect(Helper.getContent).toHaveBeenCalledWith(RepoStore.files[0]); - }); - - it('should not fetch data for already opened files', () => { - const f = file(); - spyOn(Helper, 'getFileFromPath').and.returnValue(f); - spyOn(RepoStore, 'setActiveFiles'); - vm = createComponent(); - vm.fileClicked(f); + it('renders 5 loading files if tree is loading', (done) => { + vm.$store.state.tree = []; + vm.$store.state.loading = true; - expect(RepoStore.setActiveFiles).toHaveBeenCalledWith(f); - }); + Vue.nextTick(() => { + expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5); - it('should hide files in directory if already open', () => { - spyOn(Helper, 'setDirectoryToClosed').and.callThrough(); - const f = file(); - f.opened = true; - f.type = 'tree'; - RepoStore.files = [f]; - vm = createComponent(); - - vm.fileClicked(RepoStore.files[0]); - - expect(Helper.setDirectoryToClosed).toHaveBeenCalledWith(RepoStore.files[0]); - }); - - describe('submodule', () => { - it('opens submodule project URL', () => { - spyOn(gl.utils, 'visitUrl'); - - const f = file(); - f.type = 'submodule'; - - vm = createComponent(); - - vm.fileClicked(f); - - expect(gl.utils.visitUrl).toHaveBeenCalledWith('url'); - }); - }); - }); - - describe('goToPreviousDirectoryClicked', () => { - it('should hide files in directory if already open', () => { - const prevUrl = 'foo/bar'; - vm = createComponent(); - - vm.goToPreviousDirectoryClicked(prevUrl); - - expect(RepoService.url).toEqual(prevUrl); - }); + done(); }); + }); - describe('back button', () => { - beforeEach(() => { - const f = file(); - const file2 = Object.assign({}, file()); - file2.url = 'test'; - RepoStore.files = [f, file2]; - RepoStore.openedFiles = []; - RepoStore.isRoot = true; - - vm = createComponent(); - }); - - it('render previous file when using back button', () => { - spyOn(Helper, 'getContent').and.callThrough(); - - vm.fileClicked(RepoStore.files[1]); - expect(Helper.getContent).toHaveBeenCalledWith(RepoStore.files[1]); - - history.pushState({ - key: Math.random(), - }, '', RepoStore.files[1].url); - const popEvent = document.createEvent('Event'); - popEvent.initEvent('popstate', true, true); - window.dispatchEvent(popEvent); + it('renders a prev directory if is not root', (done) => { + vm.$store.state.isRoot = false; - expect(Helper.getContent.calls.mostRecent().args[0].url).toContain(RepoStore.files[1].url); + Vue.nextTick(() => { + expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy(); - window.history.pushState({}, null, '/'); - }); + done(); }); }); }); diff --git a/spec/javascripts/repo/components/repo_spec.js b/spec/javascripts/repo/components/repo_spec.js index 3558a155728..b32d2c13af8 100644 --- a/spec/javascripts/repo/components/repo_spec.js +++ b/spec/javascripts/repo/components/repo_spec.js @@ -1,9 +1,8 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repo from '~/repo/components/repo.vue'; -import RepoStore from '~/repo/stores/repo_store'; -import Service from '~/repo/services/repo_service'; -import eventHub from '~/repo/event_hub'; -import createComponent from '../../helpers/vue_mount_component_helper'; +import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; +import { file, resetStore } from '../helpers'; describe('repo component', () => { let vm; @@ -11,86 +10,26 @@ describe('repo component', () => { beforeEach(() => { const Component = Vue.extend(repo); - RepoStore.currentBranch = 'master'; - - vm = createComponent(Component); + vm = createComponentWithStore(Component, store).$mount(); }); afterEach(() => { vm.$destroy(); - RepoStore.currentBranch = ''; + resetStore(vm.$store); }); - describe('createNewBranch', () => { - beforeEach(() => { - spyOn(history, 'pushState'); - }); - - describe('success', () => { - beforeEach(() => { - spyOn(Service, 'createBranch').and.returnValue(Promise.resolve({ - data: { - name: 'test', - }, - })); - }); - - it('calls createBranch with branchName', () => { - eventHub.$emit('createNewBranch', 'test'); - - expect(Service.createBranch).toHaveBeenCalledWith({ - branch: 'test', - ref: RepoStore.currentBranch, - }); - }); - - it('pushes new history state', (done) => { - RepoStore.currentBranch = 'master'; - - spyOn(vm, 'getCurrentLocation').and.returnValue('http://test.com/master'); - - eventHub.$emit('createNewBranch', 'test'); - - setTimeout(() => { - expect(history.pushState).toHaveBeenCalledWith(jasmine.anything(), '', 'http://test.com/test'); - done(); - }); - }); - - it('updates stores currentBranch', (done) => { - eventHub.$emit('createNewBranch', 'test'); - - setTimeout(() => { - expect(RepoStore.currentBranch).toBe('test'); - - done(); - }); - }); - }); - - describe('failure', () => { - beforeEach(() => { - spyOn(Service, 'createBranch').and.returnValue(Promise.reject({ - response: { - data: { - message: 'test', - }, - }, - })); - }); - - it('emits createNewBranchError event', (done) => { - spyOn(eventHub, '$emit').and.callThrough(); + it('does not render panel right when no files open', () => { + expect(vm.$el.querySelector('.panel-right')).toBeNull(); + }); - eventHub.$emit('createNewBranch', 'test'); + it('renders panel right when files are open', (done) => { + vm.$store.state.tree.push(file()); - setTimeout(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('createNewBranchError', 'test'); + Vue.nextTick(() => { + expect(vm.$el.querySelector('.panel-right')).toBeNull(); - done(); - }); - }); + done(); }); }); }); diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js index 37e297437f0..0d1c22a2f89 100644 --- a/spec/javascripts/repo/components/repo_tab_spec.js +++ b/spec/javascripts/repo/components/repo_tab_spec.js @@ -1,46 +1,61 @@ import Vue from 'vue'; +import store from '~/repo/stores'; import repoTab from '~/repo/components/repo_tab.vue'; -import RepoStore from '~/repo/stores/repo_store'; +import { file, resetStore } from '../helpers'; describe('RepoTab', () => { function createComponent(propsData) { const RepoTab = Vue.extend(repoTab); return new RepoTab({ + store, propsData, }).$mount(); } + afterEach(() => { + resetStore(vm.$store); + }); + it('renders a close link and a name link', () => { - const tab = { - url: 'url', - name: 'name', - }; const vm = createComponent({ - tab, + tab: file(), }); + vm.$store.state.openFiles.push(vm.tab); const close = vm.$el.querySelector('.close-btn'); - const name = vm.$el.querySelector(`a[title="${tab.url}"]`); - - spyOn(vm, 'closeTab'); - spyOn(vm, 'tabClicked'); + const name = vm.$el.querySelector(`a[title="${vm.tab.url}"]`); expect(close.querySelector('.fa-times')).toBeTruthy(); - expect(name.textContent.trim()).toEqual(tab.name); + expect(name.textContent.trim()).toEqual(vm.tab.name); + }); + + it('calls setFileActive when clicking tab', () => { + const vm = createComponent({ + tab: file(), + }); + + spyOn(vm, 'setFileActive'); + + vm.$el.click(); + + expect(vm.setFileActive).toHaveBeenCalledWith(vm.tab); + }); + + it('calls closeFile when clicking close button', () => { + const vm = createComponent({ + tab: file(), + }); + + spyOn(vm, 'closeFile'); - close.click(); - name.click(); + vm.$el.querySelector('.close-btn').click(); - expect(vm.closeTab).toHaveBeenCalledWith(tab); - expect(vm.tabClicked).toHaveBeenCalledWith(tab); + expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab }); }); it('renders an fa-circle icon if tab is changed', () => { - const tab = { - url: 'url', - name: 'name', - changed: true, - }; + const tab = file(); + tab.changed = true; const vm = createComponent({ tab, }); @@ -50,38 +65,41 @@ describe('RepoTab', () => { describe('methods', () => { describe('closeTab', () => { - it('returns undefined and does not $emit if file is changed', () => { - const tab = { - url: 'url', - name: 'name', - changed: true, - }; + it('does not close tab if is changed', (done) => { + const tab = file(); + tab.changed = true; + tab.opened = true; const vm = createComponent({ tab, }); - - spyOn(RepoStore, 'removeFromOpenedFiles'); + vm.$store.state.openFiles.push(tab); + vm.$store.dispatch('setFileActive', tab); vm.$el.querySelector('.close-btn').click(); - expect(RepoStore.removeFromOpenedFiles).not.toHaveBeenCalled(); + vm.$nextTick(() => { + expect(tab.opened).toBeTruthy(); + + done(); + }); }); - it('$emits tabclosed event with file obj', () => { - const tab = { - url: 'url', - name: 'name', - changed: false, - }; + it('closes tab when clicking close btn', (done) => { + const tab = file('lose'); + tab.opened = true; const vm = createComponent({ tab, }); - - spyOn(RepoStore, 'removeFromOpenedFiles'); + vm.$store.state.openFiles.push(tab); + vm.$store.dispatch('setFileActive', tab); vm.$el.querySelector('.close-btn').click(); - expect(RepoStore.removeFromOpenedFiles).toHaveBeenCalledWith(tab); + vm.$nextTick(() => { + expect(tab.opened).toBeFalsy(); + + done(); + }); }); }); }); diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js index 431129bc866..73ecdab6967 100644 --- a/spec/javascripts/repo/components/repo_tabs_spec.js +++ b/spec/javascripts/repo/components/repo_tabs_spec.js @@ -1,35 +1,37 @@ import Vue from 'vue'; -import RepoStore from '~/repo/stores/repo_store'; +import store from '~/repo/stores'; import repoTabs from '~/repo/components/repo_tabs.vue'; +import { file, resetStore } from '../helpers'; describe('RepoTabs', () => { - const openedFiles = [{ - id: 0, - active: true, - }, { - id: 1, - }]; + const openedFiles = [file(), file()]; function createComponent() { const RepoTabs = Vue.extend(repoTabs); - return new RepoTabs().$mount(); + return new RepoTabs({ + store, + }).$mount(); } afterEach(() => { - RepoStore.openedFiles = []; + resetStore(vm.$store); }); - it('renders a list of tabs', () => { - RepoStore.openedFiles = openedFiles; - + it('renders a list of tabs', (done) => { const vm = createComponent(); - const tabs = [...vm.$el.querySelectorAll(':scope > li')]; + openedFiles[0].active = true; + vm.$store.state.openFiles = openedFiles; + + vm.$nextTick(() => { + const tabs = [...vm.$el.querySelectorAll(':scope > li')]; + + expect(tabs.length).toEqual(3); + expect(tabs[0].classList.contains('active')).toBeTruthy(); + expect(tabs[1].classList.contains('active')).toBeFalsy(); + expect(tabs[2].classList.contains('tabs-divider')).toBeTruthy(); - expect(vm.$el.id).toEqual('tabs'); - expect(tabs.length).toEqual(3); - expect(tabs[0].classList.contains('active')).toBeTruthy(); - expect(tabs[1].classList.contains('active')).toBeFalsy(); - expect(tabs[2].classList.contains('tabs-divider')).toBeTruthy(); + done(); + }); }); }); diff --git a/spec/javascripts/repo/helpers.js b/spec/javascripts/repo/helpers.js new file mode 100644 index 00000000000..376c291c64b --- /dev/null +++ b/spec/javascripts/repo/helpers.js @@ -0,0 +1,20 @@ +import { decorateData } from '~/repo/stores/utils'; +import state from '~/repo/stores/state'; + +export const resetStore = (store) => { + store.replaceState(state()); +}; + +export const file = (name = 'name', id = name, type = '') => decorateData({ + id, + type, + icon: 'icon', + url: 'url', + name, + path: name, + last_commit: { + id: '123', + message: 'test', + committed_date: new Date().toISOString(), + }, +}); diff --git a/spec/javascripts/repo/mock_data.js b/spec/javascripts/repo/mock_data.js deleted file mode 100644 index 71e275caf09..00000000000 --- a/spec/javascripts/repo/mock_data.js +++ /dev/null @@ -1,14 +0,0 @@ -import RepoHelper from '~/repo/helpers/repo_helper'; - -// eslint-disable-next-line import/prefer-default-export -export const file = (name = 'name', id = name) => RepoHelper.serializeRepoEntity('blob', { - id, - icon: 'icon', - url: 'url', - name, - last_commit: { - id: '123', - message: 'test', - committed_date: new Date().toISOString(), - }, -}); diff --git a/spec/javascripts/repo/services/repo_service_spec.js b/spec/javascripts/repo/services/repo_service_spec.js deleted file mode 100644 index 6f530770525..00000000000 --- a/spec/javascripts/repo/services/repo_service_spec.js +++ /dev/null @@ -1,171 +0,0 @@ -import axios from 'axios'; -import RepoService from '~/repo/services/repo_service'; -import RepoStore from '~/repo/stores/repo_store'; -import Api from '~/api'; - -describe('RepoService', () => { - it('has default json format param', () => { - expect(RepoService.options.params.format).toBe('json'); - }); - - describe('buildParams', () => { - let newParams; - const url = 'url'; - - beforeEach(() => { - newParams = {}; - - spyOn(Object, 'assign').and.returnValue(newParams); - }); - - it('clones params', () => { - const params = RepoService.buildParams(url); - - expect(Object.assign).toHaveBeenCalledWith({}, RepoService.options.params); - - expect(params).toBe(newParams); - }); - - it('sets and returns viewer params to richif urlIsRichBlob is true', () => { - spyOn(RepoService, 'urlIsRichBlob').and.returnValue(true); - - const params = RepoService.buildParams(url); - - expect(params.viewer).toEqual('rich'); - }); - - it('returns params urlIsRichBlob is false', () => { - spyOn(RepoService, 'urlIsRichBlob').and.returnValue(false); - - const params = RepoService.buildParams(url); - - expect(params.viewer).toBeUndefined(); - }); - - it('calls urlIsRichBlob with the objects url prop if no url arg is provided', () => { - spyOn(RepoService, 'urlIsRichBlob'); - RepoService.url = url; - - RepoService.buildParams(); - - expect(RepoService.urlIsRichBlob).toHaveBeenCalledWith(url); - }); - }); - - describe('urlIsRichBlob', () => { - it('returns true for md extension', () => { - const isRichBlob = RepoService.urlIsRichBlob('url.md'); - - expect(isRichBlob).toBeTruthy(); - }); - - it('returns false for js extension', () => { - const isRichBlob = RepoService.urlIsRichBlob('url.js'); - - expect(isRichBlob).toBeFalsy(); - }); - }); - - describe('getContent', () => { - const params = {}; - const url = 'url'; - const requestPromise = Promise.resolve(); - - beforeEach(() => { - spyOn(RepoService, 'buildParams').and.returnValue(params); - spyOn(axios, 'get').and.returnValue(requestPromise); - }); - - it('calls buildParams and axios.get', () => { - const request = RepoService.getContent(url); - - expect(RepoService.buildParams).toHaveBeenCalledWith(url); - expect(axios.get).toHaveBeenCalledWith(url, { - params, - }); - expect(request).toBe(requestPromise); - }); - - it('uses object url prop if no url arg is provided', () => { - RepoService.url = url; - - RepoService.getContent(); - - expect(axios.get).toHaveBeenCalledWith(url, { - params, - }); - }); - }); - - describe('getBase64Content', () => { - const url = 'url'; - const response = { data: 'data' }; - - beforeEach(() => { - spyOn(RepoService, 'bufferToBase64'); - spyOn(axios, 'get').and.returnValue(Promise.resolve(response)); - }); - - it('calls axios.get and bufferToBase64 on completion', (done) => { - const request = RepoService.getBase64Content(url); - - expect(axios.get).toHaveBeenCalledWith(url, { - responseType: 'arraybuffer', - }); - expect(request).toEqual(jasmine.any(Promise)); - - request.then(() => { - expect(RepoService.bufferToBase64).toHaveBeenCalledWith(response.data); - done(); - }).catch(done.fail); - }); - }); - - describe('commitFiles', () => { - it('calls commitMultiple and .then commitFlash', (done) => { - const projectId = 'projectId'; - const payload = {}; - RepoStore.projectId = projectId; - - spyOn(Api, 'commitMultiple').and.returnValue(Promise.resolve()); - spyOn(RepoService, 'commitFlash'); - - const apiPromise = RepoService.commitFiles(payload); - - expect(Api.commitMultiple).toHaveBeenCalledWith(projectId, payload); - - apiPromise.then(() => { - expect(RepoService.commitFlash).toHaveBeenCalled(); - done(); - }).catch(done.fail); - }); - }); - - describe('commitFlash', () => { - it('calls Flash with data.message', () => { - const data = { - message: 'message', - }; - spyOn(window, 'Flash'); - - RepoService.commitFlash(data); - - expect(window.Flash).toHaveBeenCalledWith(data.message); - }); - - it('calls Flash with success string if short_id and stats', () => { - const data = { - short_id: 'short_id', - stats: { - additions: '4', - deletions: '5', - }, - }; - spyOn(window, 'Flash'); - - RepoService.commitFlash(data); - - expect(window.Flash).toHaveBeenCalledWith(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); - }); - }); -}); -- cgit v1.2.1