Commit 5b97ea3e authored by Phil Hughes's avatar Phil Hughes

Merge branch '58802-rename-webide' into 'master'

Resolve "Re-name files in Web IDE in a more natural way"

Closes #58802

See merge request gitlab-org/gitlab-ce!29948
parents 4b7f053a f0fc5358
...@@ -30,6 +30,9 @@ export default { ...@@ -30,6 +30,9 @@ export default {
showLoading() { showLoading() {
return !this.currentTree || this.currentTree.loading; return !this.currentTree || this.currentTree.loading;
}, },
actualTreeList() {
return this.currentTree.tree.filter(entry => !entry.moved);
},
}, },
mounted() { mounted() {
this.updateViewer(this.viewerType); this.updateViewer(this.viewerType);
...@@ -54,9 +57,9 @@ export default { ...@@ -54,9 +57,9 @@ export default {
<slot name="header"></slot> <slot name="header"></slot>
</header> </header>
<div class="ide-tree-body h-100"> <div class="ide-tree-body h-100">
<template v-if="currentTree.tree.length"> <template v-if="actualTreeList.length">
<file-row <file-row
v-for="file in currentTree.tree" v-for="file in actualTreeList"
:key="file.key" :key="file.key"
:file="file" :file="file"
:level="0" :level="0"
......
...@@ -8,6 +8,7 @@ import * as types from './mutation_types'; ...@@ -8,6 +8,7 @@ import * as types from './mutation_types';
import { decorateFiles } from '../lib/files'; import { decorateFiles } from '../lib/files';
import { stageKeys } from '../constants'; import { stageKeys } from '../constants';
import service from '../services'; import service from '../services';
import router from '../ide_router';
export const redirectToUrl = (self, url) => visitUrl(url); export const redirectToUrl = (self, url) => visitUrl(url);
...@@ -234,10 +235,15 @@ export const renameEntry = ( ...@@ -234,10 +235,15 @@ export const renameEntry = (
parentPath: newParentPath, parentPath: newParentPath,
}); });
}); });
} } else {
const newPath = parentPath ? `${parentPath}/${name}` : name;
const newEntry = state.entries[newPath];
commit(types.TOGGLE_FILE_CHANGED, { file: newEntry, changed: true });
if (!entryPath && !entry.tempFile) { if (entry.opened) {
dispatch('deleteEntry', path); router.push(`/project${newEntry.url}`);
commit(types.TOGGLE_FILE_OPEN, entry.path);
}
} }
dispatch('triggerFilesChange'); dispatch('triggerFilesChange');
......
...@@ -73,7 +73,9 @@ export const getFileData = ( ...@@ -73,7 +73,9 @@ export const getFileData = (
.getFileData(joinPaths(gon.relative_url_root || '', url.replace('/-/', '/'))) .getFileData(joinPaths(gon.relative_url_root || '', url.replace('/-/', '/')))
.then(({ data, headers }) => { .then(({ data, headers }) => {
const normalizedHeaders = normalizeHeaders(headers); const normalizedHeaders = normalizeHeaders(headers);
setPageTitle(decodeURI(normalizedHeaders['PAGE-TITLE'])); let title = normalizedHeaders['PAGE-TITLE'];
title = file.prevPath ? title.replace(file.prevPath, file.path) : title;
setPageTitle(decodeURI(title));
if (data) commit(types.SET_FILE_DATA, { data, file }); if (data) commit(types.SET_FILE_DATA, { data, file });
if (openFile) commit(types.TOGGLE_FILE_OPEN, path); if (openFile) commit(types.TOGGLE_FILE_OPEN, path);
......
...@@ -216,15 +216,16 @@ export default { ...@@ -216,15 +216,16 @@ export default {
Vue.set(state.entries, newPath, { Vue.set(state.entries, newPath, {
...oldEntry, ...oldEntry,
id: newPath, id: newPath,
key: `${newPath}-${oldEntry.type}-${oldEntry.id}`, key: `${newPath}-${oldEntry.type}-${oldEntry.path}`,
path: newPath, path: newPath,
name: entryPath ? oldEntry.name : name, name: entryPath ? oldEntry.name : name,
tempFile: true, tempFile: true,
prevPath: oldEntry.tempFile ? null : oldEntry.path, prevPath: oldEntry.tempFile ? null : oldEntry.path,
url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath), url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath),
tree: [], tree: [],
parentPath,
raw: '', raw: '',
opened: false,
parentPath,
}); });
oldEntry.moved = true; oldEntry.moved = true;
...@@ -241,10 +242,6 @@ export default { ...@@ -241,10 +242,6 @@ export default {
state.changedFiles = state.changedFiles.concat(newEntry); state.changedFiles = state.changedFiles.concat(newEntry);
} }
if (state.entries[newPath].opened) {
state.openFiles.push(state.entries[newPath]);
}
if (oldEntry.tempFile) { if (oldEntry.tempFile) {
const filterMethod = f => f.path !== oldEntry.path; const filterMethod = f => f.path !== oldEntry.path;
......
...@@ -147,9 +147,9 @@ export const createCommitPayload = ({ ...@@ -147,9 +147,9 @@ export const createCommitPayload = ({
commit_message: state.commitMessage || getters.preBuiltCommitMessage, commit_message: state.commitMessage || getters.preBuiltCommitMessage,
actions: getCommitFiles(rootState.stagedFiles).map(f => ({ actions: getCommitFiles(rootState.stagedFiles).map(f => ({
action: commitActionForFile(f), action: commitActionForFile(f),
file_path: f.path, file_path: f.moved ? f.movedPath : f.path,
previous_path: f.prevPath === '' ? undefined : f.prevPath, previous_path: f.prevPath === '' ? undefined : f.prevPath,
content: f.content || undefined, content: f.prevPath ? null : f.content || undefined,
encoding: f.base64 ? 'base64' : 'text', encoding: f.base64 ? 'base64' : 'text',
last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha, last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
})), })),
......
...@@ -3,7 +3,7 @@ import { commitItemIconMap } from './constants'; ...@@ -3,7 +3,7 @@ import { commitItemIconMap } from './constants';
export const getCommitIconMap = file => { export const getCommitIconMap = file => {
if (file.deleted) { if (file.deleted) {
return commitItemIconMap.deleted; return commitItemIconMap.deleted;
} else if (file.tempFile) { } else if (file.tempFile && !file.prevPath) {
return commitItemIconMap.addition; return commitItemIconMap.addition;
} }
......
---
title: Re-name files in Web IDE in a more natural way
merge_request: 29948
author:
type: changed
import { commitItemIconMap } from '~/ide/constants';
import { getCommitIconMap } from '~/ide/utils';
import { decorateData } from '~/ide/stores/utils';
describe('WebIDE utils', () => {
const createFile = (name = 'name', id = name, type = '', parent = null) =>
decorateData({
id,
type,
icon: 'icon',
url: 'url',
name,
path: parent ? `${parent.path}/${name}` : name,
parentPath: parent ? parent.path : '',
lastCommit: {},
});
describe('getCommitIconMap', () => {
let entry;
beforeEach(() => {
entry = createFile('Entry item');
});
it('renders "deleted" icon for deleted entries', () => {
entry.deleted = true;
expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.deleted);
});
it('renders "addition" icon for temp entries', () => {
entry.tempFile = true;
expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.addition);
});
it('renders "modified" icon for newly-renamed entries', () => {
entry.prevPath = 'foo/bar';
entry.tempFile = false;
expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
});
it('renders "modified" icon even for temp entries if they are newly-renamed', () => {
entry.prevPath = 'foo/bar';
entry.tempFile = true;
expect(getCommitIconMap(entry)).toEqual(commitItemIconMap.modified);
});
});
});
...@@ -58,6 +58,20 @@ describe('IDE tree list', () => { ...@@ -58,6 +58,20 @@ describe('IDE tree list', () => {
it('renders list of files', () => { it('renders list of files', () => {
expect(vm.$el.textContent).toContain('fileName'); expect(vm.$el.textContent).toContain('fileName');
}); });
it('does not render moved entries', done => {
const tree = [file('moved entry'), file('normal entry')];
tree[0].moved = true;
store.state.trees['abcproject/master'].tree = tree;
const container = vm.$el.querySelector('.ide-tree-body');
vm.$nextTick(() => {
expect(container.children.length).toBe(1);
expect(vm.$el.textContent).not.toContain('moved entry');
expect(vm.$el.textContent).toContain('normal entry');
done();
});
});
}); });
describe('empty-branch state', () => { describe('empty-branch state', () => {
......
...@@ -275,6 +275,43 @@ describe('IDE store file actions', () => { ...@@ -275,6 +275,43 @@ describe('IDE store file actions', () => {
}); });
}); });
describe('Re-named success', () => {
beforeEach(() => {
localFile = file(`newCreate-${Math.random()}`);
localFile.url = `project/getFileDataURL`;
localFile.prevPath = 'old-dull-file';
localFile.path = 'new-shiny-file';
store.state.entries[localFile.path] = localFile;
mock.onGet(`${RELATIVE_URL_ROOT}/project/getFileDataURL`).replyOnce(
200,
{
blame_path: 'blame_path',
commits_path: 'commits_path',
permalink: 'permalink',
raw_path: 'raw_path',
binary: false,
html: '123',
render_error: '',
},
{
'page-title': 'testing old-dull-file',
},
);
});
it('sets document title considering `prevPath` on a file', done => {
store
.dispatch('getFileData', { path: localFile.path })
.then(() => {
expect(document.title).toBe('testing new-shiny-file');
done();
})
.catch(done.fail);
});
});
describe('error', () => { describe('error', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet(`project/getFileDataURL`).networkError(); mock.onGet(`project/getFileDataURL`).networkError();
......
...@@ -536,8 +536,15 @@ describe('Multi-file store actions', () => { ...@@ -536,8 +536,15 @@ describe('Multi-file store actions', () => {
type: types.RENAME_ENTRY, type: types.RENAME_ENTRY,
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' }, payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
}, },
{
type: types.TOGGLE_FILE_CHANGED,
payload: {
file: store.state.entries['parent-path/new-name'],
changed: true,
},
},
], ],
[{ type: 'deleteEntry', payload: 'test' }, { type: 'triggerFilesChange' }], [{ type: 'triggerFilesChange' }],
done, done,
); );
}); });
...@@ -584,7 +591,6 @@ describe('Multi-file store actions', () => { ...@@ -584,7 +591,6 @@ describe('Multi-file store actions', () => {
parentPath: 'parent-path/new-name', parentPath: 'parent-path/new-name',
}, },
}, },
{ type: 'deleteEntry', payload: 'test' },
{ type: 'triggerFilesChange' }, { type: 'triggerFilesChange' },
], ],
done, done,
......
...@@ -309,7 +309,7 @@ describe('Multi-file store mutations', () => { ...@@ -309,7 +309,7 @@ describe('Multi-file store mutations', () => {
...localState.entries.oldPath, ...localState.entries.oldPath,
id: 'newPath', id: 'newPath',
name: 'newPath', name: 'newPath',
key: 'newPath-blob-name', key: 'newPath-blob-oldPath',
path: 'newPath', path: 'newPath',
tempFile: true, tempFile: true,
prevPath: 'oldPath', prevPath: 'oldPath',
...@@ -318,6 +318,7 @@ describe('Multi-file store mutations', () => { ...@@ -318,6 +318,7 @@ describe('Multi-file store mutations', () => {
url: `${gl.TEST_HOST}/newPath`, url: `${gl.TEST_HOST}/newPath`,
moved: jasmine.anything(), moved: jasmine.anything(),
movedPath: jasmine.anything(), movedPath: jasmine.anything(),
opened: false,
}); });
}); });
...@@ -349,13 +350,5 @@ describe('Multi-file store mutations', () => { ...@@ -349,13 +350,5 @@ describe('Multi-file store mutations', () => {
expect(localState.entries.parentPath.tree.length).toBe(1); expect(localState.entries.parentPath.tree.length).toBe(1);
}); });
it('adds to openFiles if previously opened', () => {
localState.entries.oldPath.opened = true;
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
expect(localState.openFiles).toEqual([localState.entries.newPath]);
});
}); });
}); });
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