updated karma specs

This commit is contained in:
Phil Hughes 2017-10-27 18:16:28 +01:00
parent 3811fbff7a
commit 47c906eb57
No known key found for this signature in database
GPG key ID: 32245528C52E0F9F
27 changed files with 621 additions and 1039 deletions

View file

@ -74,7 +74,7 @@ export default {
},
watch: {
activeFile(oldVal, newVal) {
if (!newVal.active) {
if (newVal && !newVal.active) {
this.initMonaco();
}
},

View file

@ -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;
}
});
};

View file

@ -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);
}
};

View file

@ -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',
});
}
};

View file

@ -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);

View file

@ -8,7 +8,7 @@ import mutations from './mutations';
Vue.use(Vuex);
export default new Vuex.Store({
state,
state: state(),
actions,
mutations,
getters,

View file

@ -1,4 +1,4 @@
export default {
export default () => ({
project: {
id: 0,
name: '',
@ -20,4 +20,4 @@ export default {
openFiles: [],
parentTreeUrl: '',
previousUrl: '',
};
});

View file

@ -36,8 +36,8 @@ export const decorateData = (entity, projectUrl = '') => {
last_commit,
tree_url,
path,
tempFile,
renderError,
tempFile = false,
active = false,
opened = false,
changed = false,

View file

@ -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);

View file

@ -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', () => {
describe('submitNewBranch with error', () => {
beforeEach(() => {
spyOn(vm, 'createNewBranch').and.returnValue(Promise.reject({
json: () => Promise.resolve({
message: 'error message',
}),
}));
});
it('sets loading to false', (done) => {
vm.loading = true;
vm.showErrorMessage();
vm.submitNewBranch();
expect(vm.loading).toBeFalsy();
setTimeout(() => {
expect(vm.loading).toBeFalsy();
done();
});
});
it('creates flash element', () => {
vm.showErrorMessage('error message');
it('creates flash element', (done) => {
vm.submitNewBranch();
expect(vm.$el.querySelector('.flash-alert')).not.toBeNull();
expect(vm.$el.querySelector('.flash-alert').textContent.trim()).toBe('error message');
});
});
setTimeout(() => {
expect(vm.$el.querySelector('.flash-alert')).not.toBeNull();
expect(vm.$el.querySelector('.flash-alert').textContent.trim()).toBe('error message');
describe('createdNewBranch', () => {
it('set loading to false', () => {
vm.loading = true;
vm.createdNewBranch();
expect(vm.loading).toBeFalsy();
});
it('resets branch name', () => {
vm.branchName = 'testing';
vm.createdNewBranch();
expect(vm.branchName).toBe('');
});
it('sets the dropdown toggle text', () => {
vm.dropdownText = document.createElement('span');
vm.createdNewBranch('branch name');
expect(vm.dropdownText.textContent).toBe('branch name');
done();
});
});
});
});

View file

@ -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);
});
});
});
});

View file

@ -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 += '<div class="js-test"></div>';
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');
});
});
});

View file

@ -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();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('renders a commit section', () => {
RepoStore.isCommitable = true;
RepoStore.currentBranch = branch;
RepoStore.targetBranch = branch;
RepoStore.openedFiles = openedFiles;
const vm = createComponent();
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';
let changedFiles;
beforeEach((done) => {
RepoStore.isCommitable = true;
RepoStore.currentBranch = branch;
RepoStore.targetBranch = branch;
RepoStore.openedFiles = openedFiles;
RepoStore.projectId = projectId;
beforeEach(() => {
vm.commitMessage = 'testing';
changedFiles = JSON.parse(JSON.stringify(vm.$store.getters.changedFiles));
// 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,
},
spyOn(service, 'commit').and.returnValue(Promise.resolve({
short_id: '1',
stats: {},
}));
// Wait for the vm data to be in place
Vue.nextTick(() => {
done();
});
});
afterEach(() => {
vm.$destroy();
el.remove();
RepoStore.openedFiles = [];
});
it('shows commit message', () => {
const commitMessageEl = vm.$el.querySelector('#commit-message');
expect(commitMessageEl.value).toBe(commitMessage);
});
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 = service.commit.calls.allArgs()[0];
const { commit_message, actions, branch: payloadBranch } = args[1];
const args = RepoService.commitFiles.calls.allArgs()[0];
const { commit_message, actions, branch: payloadBranch } = args[0];
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()
vm.makeCommit();
getSetTimeoutPromise()
.then(() => Vue.nextTick())
.then(() => {
const submitCommit = vm.$refs.submitCommit;
submitCommit.click();
})
// Wait for the branch check to finish
.then(() => getSetTimeoutPromise())
.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);
});
});
});
});

View file

@ -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');
});
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();
vm.$nextTick(() => {
expect(vm.$store.getters.changedFiles.length).toBe(0);
expect(vm.$el.querySelector('.modal')).toBeNull();
done();
});
});
});
it('does not render if not isCommitable', () => {
RepoStore.isCommitable = false;
const vm = createComponent();
expect(vm.$el.innerHTML).toBeUndefined();
});
});

View file

@ -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();
});
});

View file

@ -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 = [];
vm.$destroy();
resetStore(vm.$store);
});
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;
it('renders Raw, Blame, History, Permalink and Preview toggle', (done) => {
vm = createComponent();
const vm = createComponent();
const raw = vm.$el.querySelector('.raw');
const blame = vm.$el.querySelector('.blame');
const history = vm.$el.querySelector('.history');
vm.$nextTick(() => {
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);
});
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');
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('does not render preview toggle if not canPreview', () => {
activeFile.extension = 'js';
RepoStore.openedFiles = new Array(1);
RepoStore.activeFile = activeFile;
const vm = createComponent();
expect(vm.$el.querySelector('.preview')).toBeFalsy();
done();
});
});
});

View file

@ -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: '<p class="file-content">html</p>',
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);
});
});
});
});

View file

@ -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();
});
});
});

View file

@ -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();
const comp = new RepoPrevDirectory({
store,
});
comp.$store.state.parentTreeUrl = parentLink;
return comp.$mount();
}
beforeEach(() => {
vm = createComponent();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('renders a prev dir link', () => {
const prevUrl = 'prevUrl';
const vm = createComponent({
prevUrl,
});
const link = vm.$el.querySelector('a');
spyOn(vm, 'linkClicked');
expect(link.href).toMatch(`/${prevUrl}`);
expect(link.href).toMatch(`/${parentLink}`);
expect(link.textContent).toEqual('...');
link.click();
expect(vm.linkClicked).toHaveBeenCalledWith(prevUrl);
});
describe('methods', () => {
describe('linkClicked', () => {
it('$emits linkclicked with prevUrl', () => {
const prevUrl = 'prevUrl';
const vm = createComponent({
prevUrl,
});
it('clicking row triggers getTreeData', () => {
spyOn(vm, 'getTreeData');
spyOn(eventHub, '$emit');
vm.$el.querySelector('td').click();
vm.linkClicked(prevUrl);
expect(eventHub.$emit).toHaveBeenCalledWith('goToPreviousDirectoryClicked', prevUrl);
});
});
expect(vm.getTreeData).toHaveBeenCalledWith({ endpoint: parentLink });
});
});

View file

@ -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: '<p class="file-content">html</p>',
};
RepoStore.activeFile = activeFile;
afterEach(() => {
vm.$destroy();
const vm = createComponent();
resetStore(vm.$store);
});
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');
});
});

View file

@ -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();
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.classList.contains('sidebar-mini')).toBeTruthy();
expect(vm.$el.querySelector('thead')).toBeTruthy();
expect(vm.$el.querySelector('thead .repo-file-options')).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();
it('renders 5 loading files if tree is loading and not hasFiles', () => {
RepoStore.loading.tree = true;
RepoStore.files = [];
vm = createComponent();
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();
});
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();
it('renders 5 loading files if tree is loading', (done) => {
vm.$store.state.tree = [];
vm.$store.state.loading = true;
vm.fileClicked(RepoStore.files[0]);
Vue.nextTick(() => {
expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5);
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);
expect(RepoStore.setActiveFiles).toHaveBeenCalledWith(f);
});
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');
});
});
done();
});
});
describe('goToPreviousDirectoryClicked', () => {
it('should hide files in directory if already open', () => {
const prevUrl = 'foo/bar';
vm = createComponent();
it('renders a prev directory if is not root', (done) => {
vm.$store.state.isRoot = false;
vm.goToPreviousDirectoryClicked(prevUrl);
Vue.nextTick(() => {
expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy();
expect(RepoService.url).toEqual(prevUrl);
});
});
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);
expect(Helper.getContent.calls.mostRecent().args[0].url).toContain(RepoStore.files[1].url);
window.history.pushState({}, null, '/');
});
done();
});
});
});

View file

@ -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');
});
it('does not render panel right when no files open', () => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
});
describe('success', () => {
beforeEach(() => {
spyOn(Service, 'createBranch').and.returnValue(Promise.resolve({
data: {
name: 'test',
},
}));
});
it('renders panel right when files are open', (done) => {
vm.$store.state.tree.push(file());
it('calls createBranch with branchName', () => {
eventHub.$emit('createNewBranch', 'test');
Vue.nextTick(() => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
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();
eventHub.$emit('createNewBranch', 'test');
setTimeout(() => {
expect(eventHub.$emit).toHaveBeenCalledWith('createNewBranchError', 'test');
done();
});
});
done();
});
});
});

View file

@ -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();
}
it('renders a close link and a name link', () => {
const tab = {
url: 'url',
name: 'name',
};
const vm = createComponent({
tab,
});
const close = vm.$el.querySelector('.close-btn');
const name = vm.$el.querySelector(`a[title="${tab.url}"]`);
afterEach(() => {
resetStore(vm.$store);
});
spyOn(vm, 'closeTab');
spyOn(vm, 'tabClicked');
it('renders a close link and a name link', () => {
const vm = createComponent({
tab: file(),
});
vm.$store.state.openFiles.push(vm.tab);
const close = vm.$el.querySelector('.close-btn');
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);
});
close.click();
name.click();
it('calls setFileActive when clicking tab', () => {
const vm = createComponent({
tab: file(),
});
expect(vm.closeTab).toHaveBeenCalledWith(tab);
expect(vm.tabClicked).toHaveBeenCalledWith(tab);
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');
vm.$el.querySelector('.close-btn').click();
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();
});
});
});
});

View file

@ -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;
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();
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();
done();
});
});
});

View file

@ -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(),
},
});

View file

@ -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(),
},
});

View file

@ -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');
});
});
});