summaryrefslogtreecommitdiff
path: root/spec/frontend/ide/stores/actions_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ide/stores/actions_spec.js')
-rw-r--r--spec/frontend/ide/stores/actions_spec.js1062
1 files changed, 1062 insertions, 0 deletions
diff --git a/spec/frontend/ide/stores/actions_spec.js b/spec/frontend/ide/stores/actions_spec.js
new file mode 100644
index 00000000000..f77dbd80025
--- /dev/null
+++ b/spec/frontend/ide/stores/actions_spec.js
@@ -0,0 +1,1062 @@
+import MockAdapter from 'axios-mock-adapter';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { createStore } from '~/ide/stores';
+import { createRouter } from '~/ide/ide_router';
+import {
+ stageAllChanges,
+ unstageAllChanges,
+ toggleFileFinder,
+ setCurrentBranchId,
+ setEmptyStateSvgs,
+ updateActivityBarView,
+ updateTempFlagForEntry,
+ setErrorMessage,
+ deleteEntry,
+ renameEntry,
+ getBranchData,
+ createTempEntry,
+ discardAllChanges,
+} from '~/ide/stores/actions';
+import axios from '~/lib/utils/axios_utils';
+import * as types from '~/ide/stores/mutation_types';
+import { file } from '../helpers';
+import testAction from '../../helpers/vuex_action_helper';
+import eventHub from '~/ide/eventhub';
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn(),
+ joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
+}));
+
+describe('Multi-file store actions', () => {
+ let store;
+ let router;
+
+ beforeEach(() => {
+ store = createStore();
+ router = createRouter(store);
+
+ jest.spyOn(store, 'commit');
+ jest.spyOn(store, 'dispatch');
+ jest.spyOn(router, 'push').mockImplementation();
+ });
+
+ describe('redirectToUrl', () => {
+ it('calls visitUrl', done => {
+ store
+ .dispatch('redirectToUrl', 'test')
+ .then(() => {
+ expect(visitUrl).toHaveBeenCalledWith('test');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('setInitialData', () => {
+ it('commits initial data', done => {
+ store
+ .dispatch('setInitialData', { canCommit: true })
+ .then(() => {
+ expect(store.state.canCommit).toBeTruthy();
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('discardAllChanges', () => {
+ const paths = ['to_discard', 'another_one_to_discard'];
+
+ beforeEach(() => {
+ paths.forEach(path => {
+ const f = file(path);
+ f.changed = true;
+
+ store.state.openFiles.push(f);
+ store.state.changedFiles.push(f);
+ store.state.entries[f.path] = f;
+ });
+ });
+
+ it('discards all changes in file', () => {
+ const expectedCalls = paths.map(path => ['restoreOriginalFile', path]);
+
+ discardAllChanges(store);
+
+ expect(store.dispatch.mock.calls).toEqual(expect.arrayContaining(expectedCalls));
+ });
+
+ it('removes all files from changedFiles state', done => {
+ store
+ .dispatch('discardAllChanges')
+ .then(() => {
+ expect(store.state.changedFiles.length).toBe(0);
+ expect(store.state.openFiles.length).toBe(2);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('createTempEntry', () => {
+ beforeEach(() => {
+ document.body.innerHTML += '<div class="flash-container"></div>';
+
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'mybranch';
+
+ store.state.trees['abcproject/mybranch'] = {
+ tree: [],
+ };
+ store.state.projects.abcproject = {
+ web_url: '',
+ };
+ });
+
+ afterEach(() => {
+ document.querySelector('.flash-container').remove();
+ });
+
+ describe('tree', () => {
+ it('creates temp tree', done => {
+ store
+ .dispatch('createTempEntry', {
+ branchId: store.state.currentBranchId,
+ name: 'test',
+ type: 'tree',
+ })
+ .then(() => {
+ const entry = store.state.entries.test;
+
+ expect(entry).not.toBeNull();
+ expect(entry.type).toBe('tree');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('creates new folder inside another tree', done => {
+ const tree = {
+ type: 'tree',
+ name: 'testing',
+ path: 'testing',
+ tree: [],
+ };
+
+ store.state.entries[tree.path] = tree;
+
+ store
+ .dispatch('createTempEntry', {
+ branchId: store.state.currentBranchId,
+ name: 'testing/test',
+ type: 'tree',
+ })
+ .then(() => {
+ expect(tree.tree[0].tempFile).toBeTruthy();
+ expect(tree.tree[0].name).toBe('test');
+ expect(tree.tree[0].type).toBe('tree');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('does not create new tree if already exists', done => {
+ const tree = {
+ type: 'tree',
+ path: 'testing',
+ tempFile: false,
+ tree: [],
+ };
+
+ store.state.entries[tree.path] = tree;
+
+ store
+ .dispatch('createTempEntry', {
+ branchId: store.state.currentBranchId,
+ name: 'testing',
+ type: 'tree',
+ })
+ .then(() => {
+ expect(store.state.entries[tree.path].tempFile).toEqual(false);
+ expect(document.querySelector('.flash-alert')).not.toBeNull();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('blob', () => {
+ it('creates temp file', done => {
+ const name = 'test';
+
+ store
+ .dispatch('createTempEntry', {
+ name,
+ branchId: 'mybranch',
+ type: 'blob',
+ })
+ .then(() => {
+ const f = store.state.entries[name];
+
+ expect(f.tempFile).toBeTruthy();
+ expect(store.state.trees['abcproject/mybranch'].tree.length).toBe(1);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('adds tmp file to open files', done => {
+ const name = 'test';
+
+ store
+ .dispatch('createTempEntry', {
+ name,
+ branchId: 'mybranch',
+ type: 'blob',
+ })
+ .then(() => {
+ const f = store.state.entries[name];
+
+ expect(store.state.openFiles.length).toBe(1);
+ expect(store.state.openFiles[0].name).toBe(f.name);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('adds tmp file to staged files', done => {
+ const name = 'test';
+
+ store
+ .dispatch('createTempEntry', {
+ name,
+ branchId: 'mybranch',
+ type: 'blob',
+ })
+ .then(() => {
+ expect(store.state.stagedFiles).toEqual([expect.objectContaining({ name })]);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('sets tmp file as active', () => {
+ createTempEntry(store, { name: 'test', branchId: 'mybranch', type: 'blob' });
+
+ expect(store.dispatch).toHaveBeenCalledWith('setFileActive', 'test');
+ });
+
+ it('creates flash message if file already exists', done => {
+ const f = file('test', '1', 'blob');
+ store.state.trees['abcproject/mybranch'].tree = [f];
+ store.state.entries[f.path] = f;
+
+ store
+ .dispatch('createTempEntry', {
+ name: 'test',
+ branchId: 'mybranch',
+ type: 'blob',
+ })
+ .then(() => {
+ expect(document.querySelector('.flash-alert')?.textContent.trim()).toEqual(
+ `The name "${f.name}" is already taken in this directory.`,
+ );
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('scrollToTab', () => {
+ it('focuses the current active element', done => {
+ document.body.innerHTML +=
+ '<div id="tabs"><div class="active"><div class="repo-tab"></div></div></div>';
+ const el = document.querySelector('.repo-tab');
+ jest.spyOn(el, 'focus').mockImplementation();
+
+ store
+ .dispatch('scrollToTab')
+ .then(() => {
+ setImmediate(() => {
+ expect(el.focus).toHaveBeenCalled();
+
+ document.getElementById('tabs').remove();
+
+ done();
+ });
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('stage/unstageAllChanges', () => {
+ let file1;
+ let file2;
+
+ beforeEach(() => {
+ file1 = { ...file('test'), content: 'changed test', raw: 'test' };
+ file2 = { ...file('test2'), content: 'changed test2', raw: 'test2' };
+
+ store.state.openFiles = [file1];
+ store.state.changedFiles = [file1];
+ store.state.stagedFiles = [{ ...file2, content: 'staged test' }];
+
+ store.state.entries = {
+ [file1.path]: { ...file1 },
+ [file2.path]: { ...file2 },
+ };
+ });
+
+ describe('stageAllChanges', () => {
+ it('adds all files from changedFiles to stagedFiles', () => {
+ stageAllChanges(store);
+
+ expect(store.commit.mock.calls).toEqual(
+ expect.arrayContaining([
+ [types.SET_LAST_COMMIT_MSG, ''],
+ [types.STAGE_CHANGE, expect.objectContaining({ path: file1.path })],
+ ]),
+ );
+ });
+
+ it('opens pending tab if a change exists in that file', () => {
+ stageAllChanges(store);
+
+ expect(store.dispatch.mock.calls).toEqual([
+ [
+ 'openPendingTab',
+ { file: { ...file1, staged: true, changed: true }, keyPrefix: 'staged' },
+ ],
+ ]);
+ });
+
+ it('does not open pending tab if no change exists in that file', () => {
+ store.state.entries[file1.path].content = 'test';
+ store.state.stagedFiles = [file1];
+ store.state.changedFiles = [store.state.entries[file1.path]];
+
+ stageAllChanges(store);
+
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('unstageAllChanges', () => {
+ it('removes all files from stagedFiles after unstaging', () => {
+ unstageAllChanges(store);
+
+ expect(store.commit.mock.calls).toEqual(
+ expect.arrayContaining([
+ [types.UNSTAGE_CHANGE, expect.objectContaining({ path: file2.path })],
+ ]),
+ );
+ });
+
+ it('opens pending tab if a change exists in that file', () => {
+ unstageAllChanges(store);
+
+ expect(store.dispatch.mock.calls).toEqual([
+ ['openPendingTab', { file: file1, keyPrefix: 'unstaged' }],
+ ]);
+ });
+
+ it('does not open pending tab if no change exists in that file', () => {
+ store.state.entries[file1.path].content = 'test';
+ store.state.stagedFiles = [file1];
+ store.state.changedFiles = [store.state.entries[file1.path]];
+
+ unstageAllChanges(store);
+
+ expect(store.dispatch).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('updateViewer', () => {
+ it('updates viewer state', done => {
+ store
+ .dispatch('updateViewer', 'diff')
+ .then(() => {
+ expect(store.state.viewer).toBe('diff');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('updateActivityBarView', () => {
+ it('commits UPDATE_ACTIVITY_BAR_VIEW', done => {
+ testAction(
+ updateActivityBarView,
+ 'test',
+ {},
+ [{ type: 'UPDATE_ACTIVITY_BAR_VIEW', payload: 'test' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setEmptyStateSvgs', () => {
+ it('commits setEmptyStateSvgs', done => {
+ testAction(
+ setEmptyStateSvgs,
+ 'svg',
+ {},
+ [{ type: 'SET_EMPTY_STATE_SVGS', payload: 'svg' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('updateTempFlagForEntry', () => {
+ it('commits UPDATE_TEMP_FLAG', done => {
+ const f = {
+ ...file(),
+ path: 'test',
+ tempFile: true,
+ };
+ store.state.entries[f.path] = f;
+
+ testAction(
+ updateTempFlagForEntry,
+ { file: f, tempFile: false },
+ store.state,
+ [{ type: 'UPDATE_TEMP_FLAG', payload: { path: f.path, tempFile: false } }],
+ [],
+ done,
+ );
+ });
+
+ it('commits UPDATE_TEMP_FLAG and dispatches for parent', done => {
+ const parent = {
+ ...file(),
+ path: 'testing',
+ };
+ const f = {
+ ...file(),
+ path: 'test',
+ parentPath: 'testing',
+ };
+ store.state.entries[parent.path] = parent;
+ store.state.entries[f.path] = f;
+
+ testAction(
+ updateTempFlagForEntry,
+ { file: f, tempFile: false },
+ store.state,
+ [{ type: 'UPDATE_TEMP_FLAG', payload: { path: f.path, tempFile: false } }],
+ [{ type: 'updateTempFlagForEntry', payload: { file: parent, tempFile: false } }],
+ done,
+ );
+ });
+
+ it('does not dispatch for parent, if parent does not exist', done => {
+ const f = {
+ ...file(),
+ path: 'test',
+ parentPath: 'testing',
+ };
+ store.state.entries[f.path] = f;
+
+ testAction(
+ updateTempFlagForEntry,
+ { file: f, tempFile: false },
+ store.state,
+ [{ type: 'UPDATE_TEMP_FLAG', payload: { path: f.path, tempFile: false } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setCurrentBranchId', () => {
+ it('commits setCurrentBranchId', done => {
+ testAction(
+ setCurrentBranchId,
+ 'branchId',
+ {},
+ [{ type: 'SET_CURRENT_BRANCH', payload: 'branchId' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('toggleFileFinder', () => {
+ it('commits TOGGLE_FILE_FINDER', done => {
+ testAction(
+ toggleFileFinder,
+ true,
+ null,
+ [{ type: 'TOGGLE_FILE_FINDER', payload: true }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setErrorMessage', () => {
+ it('commis error messsage', done => {
+ testAction(
+ setErrorMessage,
+ 'error',
+ null,
+ [{ type: types.SET_ERROR_MESSAGE, payload: 'error' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('deleteEntry', () => {
+ it('commits entry deletion', done => {
+ store.state.entries.path = 'testing';
+
+ testAction(
+ deleteEntry,
+ 'path',
+ store.state,
+ [{ type: types.DELETE_ENTRY, payload: 'path' }],
+ [{ type: 'stageChange', payload: 'path' }, { type: 'triggerFilesChange' }],
+ done,
+ );
+ });
+
+ it('does not delete a folder after it is emptied', done => {
+ const testFolder = {
+ type: 'tree',
+ tree: [],
+ };
+ const testEntry = {
+ path: 'testFolder/entry-to-delete',
+ parentPath: 'testFolder',
+ opened: false,
+ tree: [],
+ };
+ testFolder.tree.push(testEntry);
+ store.state.entries = {
+ testFolder,
+ 'testFolder/entry-to-delete': testEntry,
+ };
+
+ testAction(
+ deleteEntry,
+ 'testFolder/entry-to-delete',
+ store.state,
+ [{ type: types.DELETE_ENTRY, payload: 'testFolder/entry-to-delete' }],
+ [
+ { type: 'stageChange', payload: 'testFolder/entry-to-delete' },
+ { type: 'triggerFilesChange' },
+ ],
+ done,
+ );
+ });
+
+ describe('when renamed', () => {
+ let testEntry;
+
+ beforeEach(() => {
+ testEntry = {
+ path: 'test',
+ name: 'test',
+ prevPath: 'test_old',
+ prevName: 'test_old',
+ prevParentPath: '',
+ };
+
+ store.state.entries = { test: testEntry };
+ });
+
+ describe('and previous does not exist', () => {
+ it('reverts the rename before deleting', done => {
+ testAction(
+ deleteEntry,
+ testEntry.path,
+ store.state,
+ [],
+ [
+ {
+ type: 'renameEntry',
+ payload: {
+ path: testEntry.path,
+ name: testEntry.prevName,
+ parentPath: testEntry.prevParentPath,
+ },
+ },
+ {
+ type: 'deleteEntry',
+ payload: testEntry.prevPath,
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('and previous exists', () => {
+ beforeEach(() => {
+ const oldEntry = {
+ path: testEntry.prevPath,
+ name: testEntry.prevName,
+ };
+
+ store.state.entries[oldEntry.path] = oldEntry;
+ });
+
+ it('does not revert rename before deleting', done => {
+ testAction(
+ deleteEntry,
+ testEntry.path,
+ store.state,
+ [{ type: types.DELETE_ENTRY, payload: testEntry.path }],
+ [{ type: 'stageChange', payload: testEntry.path }, { type: 'triggerFilesChange' }],
+ done,
+ );
+ });
+
+ it('when previous is deleted, it reverts rename before deleting', done => {
+ store.state.entries[testEntry.prevPath].deleted = true;
+
+ testAction(
+ deleteEntry,
+ testEntry.path,
+ store.state,
+ [],
+ [
+ {
+ type: 'renameEntry',
+ payload: {
+ path: testEntry.path,
+ name: testEntry.prevName,
+ parentPath: testEntry.prevParentPath,
+ },
+ },
+ {
+ type: 'deleteEntry',
+ payload: testEntry.prevPath,
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+ });
+
+ describe('renameEntry', () => {
+ describe('purging of file model cache', () => {
+ beforeEach(() => {
+ jest.spyOn(eventHub, '$emit').mockImplementation();
+ });
+
+ it('does not purge model cache for temporary entries that got renamed', done => {
+ Object.assign(store.state.entries, {
+ test: {
+ ...file('test'),
+ key: 'foo-key',
+ type: 'blob',
+ tempFile: true,
+ },
+ });
+
+ store
+ .dispatch('renameEntry', {
+ path: 'test',
+ name: 'new',
+ })
+ .then(() => {
+ expect(eventHub.$emit.mock.calls).not.toContain('editor.update.model.dispose.foo-bar');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('purges model cache for renamed entry', done => {
+ Object.assign(store.state.entries, {
+ test: {
+ ...file('test'),
+ key: 'foo-key',
+ type: 'blob',
+ tempFile: false,
+ },
+ });
+
+ store
+ .dispatch('renameEntry', {
+ path: 'test',
+ name: 'new',
+ })
+ .then(() => {
+ expect(eventHub.$emit).toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalledWith(`editor.update.model.dispose.foo-key`);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('single entry', () => {
+ let origEntry;
+ let renamedEntry;
+
+ beforeEach(() => {
+ // Need to insert both because `testAction` doesn't actually call the mutation
+ origEntry = file('orig', 'orig', 'blob');
+ renamedEntry = {
+ ...file('renamed', 'renamed', 'blob'),
+ prevKey: origEntry.key,
+ prevName: origEntry.name,
+ prevPath: origEntry.path,
+ };
+
+ Object.assign(store.state.entries, {
+ orig: origEntry,
+ renamed: renamedEntry,
+ });
+ });
+
+ it('by default renames an entry and stages it', () => {
+ const dispatch = jest.fn();
+ const commit = jest.fn();
+
+ renameEntry(
+ { dispatch, commit, state: store.state, getters: store.getters },
+ { path: 'orig', name: 'renamed' },
+ );
+
+ expect(commit.mock.calls).toEqual([
+ [types.RENAME_ENTRY, { path: 'orig', name: 'renamed', parentPath: undefined }],
+ [types.STAGE_CHANGE, expect.objectContaining({ path: 'renamed' })],
+ ]);
+ });
+
+ it('if not changed, completely unstages and discards entry if renamed to original', done => {
+ testAction(
+ renameEntry,
+ { path: 'renamed', name: 'orig' },
+ store.state,
+ [
+ {
+ type: types.RENAME_ENTRY,
+ payload: {
+ path: 'renamed',
+ name: 'orig',
+ parentPath: undefined,
+ },
+ },
+ {
+ type: types.REMOVE_FILE_FROM_STAGED_AND_CHANGED,
+ payload: origEntry,
+ },
+ ],
+ [{ type: 'triggerFilesChange' }],
+ done,
+ );
+ });
+
+ it('if already in changed, does not add to change', done => {
+ store.state.changedFiles.push(renamedEntry);
+
+ testAction(
+ renameEntry,
+ { path: 'orig', name: 'renamed' },
+ store.state,
+ [expect.objectContaining({ type: types.RENAME_ENTRY })],
+ [{ type: 'triggerFilesChange' }],
+ done,
+ );
+ });
+
+ it('routes to the renamed file if the original file has been opened', done => {
+ Object.assign(store.state.entries.orig, {
+ opened: true,
+ url: '/foo-bar.md',
+ });
+
+ store
+ .dispatch('renameEntry', {
+ path: 'orig',
+ name: 'renamed',
+ })
+ .then(() => {
+ expect(router.push.mock.calls).toHaveLength(1);
+ expect(router.push).toHaveBeenCalledWith(`/project/foo-bar.md`);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('folder', () => {
+ let folder;
+ let file1;
+ let file2;
+
+ beforeEach(() => {
+ folder = file('folder', 'folder', 'tree');
+ file1 = file('file-1', 'file-1', 'blob', folder);
+ file2 = file('file-2', 'file-2', 'blob', folder);
+
+ folder.tree = [file1, file2];
+
+ Object.assign(store.state.entries, {
+ [folder.path]: folder,
+ [file1.path]: file1,
+ [file2.path]: file2,
+ });
+ });
+
+ it('updates entries in a folder correctly, when folder is renamed', done => {
+ store
+ .dispatch('renameEntry', {
+ path: 'folder',
+ name: 'new-folder',
+ })
+ .then(() => {
+ const keys = Object.keys(store.state.entries);
+
+ expect(keys.length).toBe(3);
+ expect(keys.indexOf('new-folder')).toBe(0);
+ expect(keys.indexOf('new-folder/file-1')).toBe(1);
+ expect(keys.indexOf('new-folder/file-2')).toBe(2);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('discards renaming of an entry if the root folder is renamed back to a previous name', done => {
+ const rootFolder = file('old-folder', 'old-folder', 'tree');
+ const testEntry = file('test', 'test', 'blob', rootFolder);
+
+ Object.assign(store.state, {
+ entries: {
+ 'old-folder': {
+ ...rootFolder,
+ tree: [testEntry],
+ },
+ 'old-folder/test': testEntry,
+ },
+ });
+
+ store
+ .dispatch('renameEntry', {
+ path: 'old-folder',
+ name: 'new-folder',
+ })
+ .then(() => {
+ const { entries } = store.state;
+
+ expect(Object.keys(entries).length).toBe(2);
+ expect(entries['old-folder']).toBeUndefined();
+ expect(entries['old-folder/test']).toBeUndefined();
+
+ expect(entries['new-folder']).toBeDefined();
+ expect(entries['new-folder/test']).toEqual(
+ expect.objectContaining({
+ path: 'new-folder/test',
+ name: 'test',
+ prevPath: 'old-folder/test',
+ prevName: 'test',
+ }),
+ );
+ })
+ .then(() =>
+ store.dispatch('renameEntry', {
+ path: 'new-folder',
+ name: 'old-folder',
+ }),
+ )
+ .then(() => {
+ const { entries } = store.state;
+
+ expect(Object.keys(entries).length).toBe(2);
+ expect(entries['new-folder']).toBeUndefined();
+ expect(entries['new-folder/test']).toBeUndefined();
+
+ expect(entries['old-folder']).toBeDefined();
+ expect(entries['old-folder/test']).toEqual(
+ expect.objectContaining({
+ path: 'old-folder/test',
+ name: 'test',
+ prevPath: undefined,
+ prevName: undefined,
+ }),
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ describe('with file in directory', () => {
+ const parentPath = 'original-dir';
+ const newParentPath = 'new-dir';
+ const fileName = 'test.md';
+ const filePath = `${parentPath}/${fileName}`;
+
+ let rootDir;
+
+ beforeEach(() => {
+ const parentEntry = file(parentPath, parentPath, 'tree');
+ const fileEntry = file(filePath, filePath, 'blob', parentEntry);
+ rootDir = {
+ tree: [],
+ };
+
+ Object.assign(store.state, {
+ entries: {
+ [parentPath]: {
+ ...parentEntry,
+ tree: [fileEntry],
+ },
+ [filePath]: fileEntry,
+ },
+ trees: {
+ '/': rootDir,
+ },
+ });
+ });
+
+ it('creates new directory', done => {
+ expect(store.state.entries[newParentPath]).toBeUndefined();
+
+ store
+ .dispatch('renameEntry', { path: filePath, name: fileName, parentPath: newParentPath })
+ .then(() => {
+ expect(store.state.entries[newParentPath]).toEqual(
+ expect.objectContaining({
+ path: newParentPath,
+ type: 'tree',
+ tree: expect.arrayContaining([
+ store.state.entries[`${newParentPath}/${fileName}`],
+ ]),
+ }),
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ describe('when new directory exists', () => {
+ let newDir;
+
+ beforeEach(() => {
+ newDir = file(newParentPath, newParentPath, 'tree');
+
+ store.state.entries[newDir.path] = newDir;
+ rootDir.tree.push(newDir);
+ });
+
+ it('inserts in new directory', done => {
+ expect(newDir.tree).toEqual([]);
+
+ store
+ .dispatch('renameEntry', {
+ path: filePath,
+ name: fileName,
+ parentPath: newParentPath,
+ })
+ .then(() => {
+ expect(newDir.tree).toEqual([store.state.entries[`${newParentPath}/${fileName}`]]);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('when new directory is deleted, it undeletes it', done => {
+ store.dispatch('deleteEntry', newParentPath);
+
+ expect(store.state.entries[newParentPath].deleted).toBe(true);
+ expect(rootDir.tree.some(x => x.path === newParentPath)).toBe(false);
+
+ store
+ .dispatch('renameEntry', {
+ path: filePath,
+ name: fileName,
+ parentPath: newParentPath,
+ })
+ .then(() => {
+ expect(store.state.entries[newParentPath].deleted).toBe(false);
+ expect(rootDir.tree.some(x => x.path === newParentPath)).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+ });
+ });
+
+ describe('getBranchData', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('error', () => {
+ let dispatch;
+ let callParams;
+
+ beforeEach(() => {
+ callParams = [
+ {
+ commit() {},
+ state: store.state,
+ },
+ {
+ projectId: 'abc/def',
+ branchId: 'master-testing',
+ },
+ ];
+ dispatch = jest.fn();
+ document.body.innerHTML += '<div class="flash-container"></div>';
+ });
+
+ afterEach(() => {
+ document.querySelector('.flash-container').remove();
+ });
+
+ it('passes the error further unchanged without dispatching any action when response is 404', done => {
+ mock.onGet(/(.*)/).replyOnce(404);
+
+ getBranchData(...callParams)
+ .then(done.fail)
+ .catch(e => {
+ expect(dispatch.mock.calls).toHaveLength(0);
+ expect(e.response.status).toEqual(404);
+ expect(document.querySelector('.flash-alert')).toBeNull();
+ done();
+ });
+ });
+
+ it('does not pass the error further and flashes an alert if error is not 404', done => {
+ mock.onGet(/(.*)/).replyOnce(418);
+
+ getBranchData(...callParams)
+ .then(done.fail)
+ .catch(e => {
+ expect(dispatch.mock.calls).toHaveLength(0);
+ expect(e.response).toBeUndefined();
+ expect(document.querySelector('.flash-alert')).not.toBeNull();
+ done();
+ });
+ });
+ });
+ });
+});