Commit 5e0423eb authored by Denys Mishunov's avatar Denys Mishunov

Set tree list accounting for already-opened files

Since we can create files from URL now, this means that these files will
not exist in the tree returned from API: they exist on the client
only before the first commit. In order to still show the newly-created
files in the tree, we should not override the tree, but merge the tree
existing on the client and the one coming from API.

Changelog entry

Moved trees merging into mutation
parent f6a038b3
import * as types from '../mutation_types';
import { sortTree } from '../utils';
import { sortTree, mergeTrees } from '../utils';
export default {
[types.TOGGLE_TREE_OPEN](state, path) {
......@@ -23,9 +23,15 @@ export default {
});
},
[types.SET_DIRECTORY_DATA](state, { data, treePath }) {
Object.assign(state.trees[treePath], {
tree: data,
});
const selectedTree = state.trees[treePath];
// If we opened files while loading the tree, we need to merge them
// Otherwise, simply overwrite the tree
const tree = !selectedTree.tree.length
? data
: selectedTree.loading && mergeTrees(selectedTree.tree, data);
Object.assign(selectedTree, { tree });
},
[types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
Object.assign(tree, {
......
......@@ -170,3 +170,31 @@ export const filePathMatches = (filePath, path) => filePath.indexOf(`${path}/`)
export const getChangesCountForFiles = (files, path) =>
files.filter(f => filePathMatches(f.path, path)).length;
export const mergeTrees = (fromTree, toTree) => {
if (!fromTree || !fromTree.length) {
return toTree;
}
const recurseTree = (n, t) => {
if (!n) {
return t;
}
const existingTreeNode = t.find(el => el.path === n.path);
if (existingTreeNode && n.tree.length > 0) {
existingTreeNode.opened = true;
recurseTree(n.tree[0], existingTreeNode.tree);
} else if (!existingTreeNode) {
const sorted = sortTree(t.concat(n));
t.splice(0, t.length + 1, ...sorted);
}
return t;
};
for (let i = 0, l = fromTree.length; i < l; i += 1) {
recurseTree(fromTree[i], toTree);
}
return toTree;
};
---
title: Implemented support for creation of new files from URL in Web IDE
merge_request: 26622
author:
type: added
import MockAdapter from 'axios-mock-adapter';
import testAction from 'spec/helpers/vuex_action_helper';
import { showTreeEntry, getFiles } from '~/ide/stores/actions/tree';
import { showTreeEntry, getFiles, setDirectoryData } from '~/ide/stores/actions/tree';
import * as types from '~/ide/stores/mutation_types';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
......@@ -206,4 +206,35 @@ describe('Multi-file store tree actions', () => {
);
});
});
describe('setDirectoryData', () => {
it('sets tree correctly if there are no opened files yet', done => {
const treeFile = file({ name: 'README.md' });
store.state.trees['abcproject/master'] = {};
testAction(
setDirectoryData,
{ projectId: 'abcproject', branchId: 'master', treeList: [treeFile] },
store.state,
[
{
type: types.SET_DIRECTORY_DATA,
payload: {
treePath: 'abcproject/master',
data: [treeFile],
},
},
{
type: types.TOGGLE_LOADING,
payload: {
entry: {},
forceValue: false,
},
},
],
[],
done,
);
});
});
});
......@@ -26,17 +26,11 @@ describe('Multi-file store tree mutations', () => {
});
describe('SET_DIRECTORY_DATA', () => {
const data = [
{
name: 'tree',
},
{
name: 'submodule',
},
{
name: 'blob',
},
];
let data;
beforeEach(() => {
data = [file('tree'), file('foo'), file('blob')];
});
it('adds directory data', () => {
localState.trees['project/master'] = {
......@@ -52,7 +46,7 @@ describe('Multi-file store tree mutations', () => {
expect(tree.tree.length).toBe(3);
expect(tree.tree[0].name).toBe('tree');
expect(tree.tree[1].name).toBe('submodule');
expect(tree.tree[1].name).toBe('foo');
expect(tree.tree[2].name).toBe('blob');
});
......@@ -65,6 +59,49 @@ describe('Multi-file store tree mutations', () => {
expect(localState.trees['project/master'].loading).toBe(true);
});
it('does not override tree already in state, but merges the two with correct order', () => {
const openedFile = file('new');
localState.trees['project/master'] = {
loading: true,
tree: [openedFile],
};
mutations.SET_DIRECTORY_DATA(localState, {
data,
treePath: 'project/master',
});
const { tree } = localState.trees['project/master'];
expect(tree.length).toBe(4);
expect(tree[0].name).toBe('blob');
expect(tree[1].name).toBe('foo');
expect(tree[2].name).toBe('new');
expect(tree[3].name).toBe('tree');
});
it('returns tree unchanged if the opened file is already in the tree', () => {
const openedFile = file('foo');
localState.trees['project/master'] = {
loading: true,
tree: [openedFile],
};
mutations.SET_DIRECTORY_DATA(localState, {
data,
treePath: 'project/master',
});
const { tree } = localState.trees['project/master'];
expect(tree.length).toBe(3);
expect(tree[0].name).toBe('tree');
expect(tree[1].name).toBe('foo');
expect(tree[2].name).toBe('blob');
});
});
describe('REMOVE_ALL_CHANGES_FILES', () => {
......
......@@ -235,4 +235,129 @@ describe('Multi-file store utils', () => {
]);
});
});
describe('mergeTrees', () => {
let fromTree;
let toTree;
beforeEach(() => {
fromTree = [file('foo')];
toTree = [file('bar')];
});
it('merges simple trees with sorting the result', () => {
toTree = [file('beta'), file('alpha'), file('gamma')];
const res = utils.mergeTrees(fromTree, toTree);
expect(res.length).toEqual(4);
expect(res[0].name).toEqual('alpha');
expect(res[1].name).toEqual('beta');
expect(res[2].name).toEqual('foo');
expect(res[3].name).toEqual('gamma');
expect(res[2]).toBe(fromTree[0]);
});
it('handles edge cases', () => {
expect(utils.mergeTrees({}, []).length).toEqual(0);
let res = utils.mergeTrees({}, toTree);
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('bar');
res = utils.mergeTrees(fromTree, []);
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('foo');
expect(res[0]).toBe(fromTree[0]);
});
it('merges simple trees without producing duplicates', () => {
toTree.push(file('foo'));
const res = utils.mergeTrees(fromTree, toTree);
expect(res.length).toEqual(2);
expect(res[0].name).toEqual('bar');
expect(res[1].name).toEqual('foo');
expect(res[1]).not.toBe(fromTree[0]);
});
it('merges nested tree into the main one without duplicates', () => {
fromTree[0].tree.push({
...file('alpha'),
path: 'foo/alpha',
tree: [
{
...file('beta.md'),
path: 'foo/alpha/beta.md',
},
],
});
toTree.push({
...file('foo'),
tree: [
{
...file('alpha'),
path: 'foo/alpha',
tree: [
{
...file('gamma.md'),
path: 'foo/alpha/gamma.md',
},
],
},
],
});
const res = utils.mergeTrees(fromTree, toTree);
expect(res.length).toEqual(2);
expect(res[1].name).toEqual('foo');
const finalBranch = res[1].tree[0].tree;
expect(finalBranch.length).toEqual(2);
expect(finalBranch[0].name).toEqual('beta.md');
expect(finalBranch[1].name).toEqual('gamma.md');
});
it('marks correct folders as opened as the parsing goes on', () => {
fromTree[0].tree.push({
...file('alpha'),
path: 'foo/alpha',
tree: [
{
...file('beta.md'),
path: 'foo/alpha/beta.md',
},
],
});
toTree.push({
...file('foo'),
tree: [
{
...file('alpha'),
path: 'foo/alpha',
tree: [
{
...file('gamma.md'),
path: 'foo/alpha/gamma.md',
},
],
},
],
});
const res = utils.mergeTrees(fromTree, toTree);
expect(res[1].name).toEqual('foo');
expect(res[1].opened).toEqual(true);
expect(res[1].tree[0].name).toEqual('alpha');
expect(res[1].tree[0].opened).toEqual(true);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment