mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-10 01:31:45 +00:00
Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
@ -123,7 +123,7 @@ compile-assets pull-cache:
|
|||||||
- .use-pg9
|
- .use-pg9
|
||||||
dependencies: ["compile-assets", "compile-assets pull-cache", "setup-test-env"]
|
dependencies: ["compile-assets", "compile-assets pull-cache", "setup-test-env"]
|
||||||
|
|
||||||
karma:
|
.karma-base:
|
||||||
extends: .only-code-frontend-job-base
|
extends: .only-code-frontend-job-base
|
||||||
variables:
|
variables:
|
||||||
# we override the max_old_space_size to prevent OOM errors
|
# we override the max_old_space_size to prevent OOM errors
|
||||||
@ -134,6 +134,9 @@ karma:
|
|||||||
- scripts/gitaly-test-spawn
|
- scripts/gitaly-test-spawn
|
||||||
- date
|
- date
|
||||||
- bundle exec rake karma
|
- bundle exec rake karma
|
||||||
|
|
||||||
|
karma:
|
||||||
|
extends: .karma-base
|
||||||
coverage: '/^Statements *: (\d+\.\d+%)/'
|
coverage: '/^Statements *: (\d+\.\d+%)/'
|
||||||
artifacts:
|
artifacts:
|
||||||
name: coverage-javascript
|
name: coverage-javascript
|
||||||
@ -146,7 +149,12 @@ karma:
|
|||||||
reports:
|
reports:
|
||||||
junit: junit_karma.xml
|
junit: junit_karma.xml
|
||||||
|
|
||||||
jest:
|
karma-foss:
|
||||||
|
extends:
|
||||||
|
- .karma-base
|
||||||
|
- .only-ee-as-if-foss
|
||||||
|
|
||||||
|
.jest-base:
|
||||||
extends: .only-code-frontend-job-base
|
extends: .only-code-frontend-job-base
|
||||||
script:
|
script:
|
||||||
- scripts/gitaly-test-spawn
|
- scripts/gitaly-test-spawn
|
||||||
@ -154,6 +162,14 @@ jest:
|
|||||||
- bundle exec rake frontend:fixtures
|
- bundle exec rake frontend:fixtures
|
||||||
- date
|
- date
|
||||||
- yarn jest --ci --coverage
|
- yarn jest --ci --coverage
|
||||||
|
cache:
|
||||||
|
key: jest
|
||||||
|
paths:
|
||||||
|
- tmp/jest/jest/
|
||||||
|
policy: pull-push
|
||||||
|
|
||||||
|
jest:
|
||||||
|
extends: .jest-base
|
||||||
artifacts:
|
artifacts:
|
||||||
name: coverage-frontend
|
name: coverage-frontend
|
||||||
expire_in: 31d
|
expire_in: 31d
|
||||||
@ -164,11 +180,13 @@ jest:
|
|||||||
- tmp/tests/frontend/
|
- tmp/tests/frontend/
|
||||||
reports:
|
reports:
|
||||||
junit: junit_jest.xml
|
junit: junit_jest.xml
|
||||||
|
|
||||||
|
jest-foss:
|
||||||
|
extends:
|
||||||
|
- .jest-base
|
||||||
|
- .only-ee-as-if-foss
|
||||||
cache:
|
cache:
|
||||||
key: jest
|
policy: pull
|
||||||
paths:
|
|
||||||
- tmp/jest/jest/
|
|
||||||
policy: pull-push
|
|
||||||
|
|
||||||
.qa-job-base:
|
.qa-job-base:
|
||||||
extends:
|
extends:
|
||||||
|
@ -149,3 +149,8 @@
|
|||||||
variables:
|
variables:
|
||||||
- $CI_PROJECT_NAME == "gitlab"
|
- $CI_PROJECT_NAME == "gitlab"
|
||||||
- $CI_PROJECT_NAME == "gitlab-ee" # Support former project name for forks/mirrors
|
- $CI_PROJECT_NAME == "gitlab-ee" # Support former project name for forks/mirrors
|
||||||
|
|
||||||
|
.only-ee-as-if-foss:
|
||||||
|
extends: .only-ee
|
||||||
|
variables:
|
||||||
|
IS_GITLAB_EE: '0'
|
||||||
|
@ -74,6 +74,12 @@ setup-test-env:
|
|||||||
- .rspec-base
|
- .rspec-base
|
||||||
- .use-pg9
|
- .use-pg9
|
||||||
|
|
||||||
|
.rspec-base-pg9-foss:
|
||||||
|
extends:
|
||||||
|
- .rspec-base
|
||||||
|
- .use-pg9
|
||||||
|
- .only-ee-as-if-foss
|
||||||
|
|
||||||
.rspec-base-pg10:
|
.rspec-base-pg10:
|
||||||
extends:
|
extends:
|
||||||
- .rspec-base
|
- .rspec-base
|
||||||
@ -84,14 +90,27 @@ rspec unit pg9:
|
|||||||
extends: .rspec-base-pg9
|
extends: .rspec-base-pg9
|
||||||
parallel: 20
|
parallel: 20
|
||||||
|
|
||||||
|
rspec unit pg9-foss:
|
||||||
|
extends: .rspec-base-pg9-foss
|
||||||
|
parallel: 20
|
||||||
|
|
||||||
rspec integration pg9:
|
rspec integration pg9:
|
||||||
extends: .rspec-base-pg9
|
extends: .rspec-base-pg9
|
||||||
parallel: 6
|
parallel: 6
|
||||||
|
|
||||||
|
rspec integration pg9-foss:
|
||||||
|
extends: .rspec-base-pg9-foss
|
||||||
|
parallel: 6
|
||||||
|
|
||||||
rspec system pg9:
|
rspec system pg9:
|
||||||
extends: .rspec-base-pg9
|
extends: .rspec-base-pg9
|
||||||
parallel: 24
|
parallel: 24
|
||||||
|
|
||||||
|
# TODO: This requires FOSS assets
|
||||||
|
# rspec system pg9-foss:
|
||||||
|
# extends: .rspec-base-pg9-foss
|
||||||
|
# parallel: 24
|
||||||
|
|
||||||
rspec unit pg10:
|
rspec unit pg10:
|
||||||
extends: .rspec-base-pg10
|
extends: .rspec-base-pg10
|
||||||
parallel: 20
|
parallel: 20
|
||||||
|
@ -337,6 +337,7 @@ class GfmAutoComplete {
|
|||||||
},
|
},
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
insertTpl: '${atwho-at}${title}',
|
insertTpl: '${atwho-at}${title}',
|
||||||
|
limit: 20,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
...this.getDefaultCallbacks(),
|
...this.getDefaultCallbacks(),
|
||||||
beforeSave(merges) {
|
beforeSave(merges) {
|
||||||
|
@ -43,7 +43,12 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<div class="d-flex ide-commit-editor-header align-items-center">
|
<div class="d-flex ide-commit-editor-header align-items-center">
|
||||||
<file-icon :file-name="activeFile.name" :size="16" class="mr-2" />
|
<file-icon :file-name="activeFile.name" :size="16" class="mr-2" />
|
||||||
<strong class="mr-2"> {{ activeFile.path }} </strong>
|
<strong class="mr-2">
|
||||||
|
<template v-if="activeFile.prevPath && activeFile.prevPath !== activeFile.path">
|
||||||
|
{{ activeFile.prevPath }} →
|
||||||
|
</template>
|
||||||
|
{{ activeFile.path }}
|
||||||
|
</strong>
|
||||||
<changed-file-icon :file="activeFile" :is-centered="false" />
|
<changed-file-icon :file="activeFile" :is-centered="false" />
|
||||||
<div class="ml-auto">
|
<div class="ml-auto">
|
||||||
<button
|
<button
|
||||||
|
@ -110,6 +110,9 @@ export default {
|
|||||||
>
|
>
|
||||||
<span class="multi-file-commit-list-file-path d-flex align-items-center">
|
<span class="multi-file-commit-list-file-path d-flex align-items-center">
|
||||||
<file-icon :file-name="file.name" class="append-right-8" />
|
<file-icon :file-name="file.name" class="append-right-8" />
|
||||||
|
<template v-if="file.prevName && file.prevName !== file.name">
|
||||||
|
{{ file.prevName }} →
|
||||||
|
</template>
|
||||||
{{ file.name }}
|
{{ file.name }}
|
||||||
</span>
|
</span>
|
||||||
<div class="ml-auto d-flex align-items-center">
|
<div class="ml-auto d-flex align-items-center">
|
||||||
|
@ -34,6 +34,9 @@ export default {
|
|||||||
'getUnstagedFilesCountForPath',
|
'getUnstagedFilesCountForPath',
|
||||||
'getStagedFilesCountForPath',
|
'getStagedFilesCountForPath',
|
||||||
]),
|
]),
|
||||||
|
isTree() {
|
||||||
|
return this.file.type === 'tree';
|
||||||
|
},
|
||||||
folderUnstagedCount() {
|
folderUnstagedCount() {
|
||||||
return this.getUnstagedFilesCountForPath(this.file.path);
|
return this.getUnstagedFilesCountForPath(this.file.path);
|
||||||
},
|
},
|
||||||
@ -58,10 +61,13 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
showTreeChangesCount() {
|
showTreeChangesCount() {
|
||||||
return this.file.type === 'tree' && this.changesCount > 0 && !this.file.opened;
|
return this.isTree && this.changesCount > 0 && !this.file.opened;
|
||||||
|
},
|
||||||
|
isModified() {
|
||||||
|
return this.file.changed || this.file.tempFile || this.file.staged || this.file.prevPath;
|
||||||
},
|
},
|
||||||
showChangedFileIcon() {
|
showChangedFileIcon() {
|
||||||
return this.file.changed || this.file.tempFile || this.file.staged;
|
return !this.isTree && this.isModified;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -30,9 +30,6 @@ 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);
|
||||||
@ -57,9 +54,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="actualTreeList.length">
|
<template v-if="currentTree.tree.length">
|
||||||
<file-row
|
<file-row
|
||||||
v-for="file in actualTreeList"
|
v-for="file in currentTree.tree"
|
||||||
:key="file.key"
|
:key="file.key"
|
||||||
:file="file"
|
:file="file"
|
||||||
:level="0"
|
:level="0"
|
||||||
|
@ -91,7 +91,6 @@ export default {
|
|||||||
this.renameEntry({
|
this.renameEntry({
|
||||||
path: this.entryModal.entry.path,
|
path: this.entryModal.entry.path,
|
||||||
name: entryName,
|
name: entryName,
|
||||||
entryPath: null,
|
|
||||||
parentPath,
|
parentPath,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -155,15 +155,7 @@ export default {
|
|||||||
|
|
||||||
this.editor.clearEditor();
|
this.editor.clearEditor();
|
||||||
|
|
||||||
this.getFileData({
|
this.fetchFileData()
|
||||||
path: this.file.path,
|
|
||||||
makeFileActive: false,
|
|
||||||
})
|
|
||||||
.then(() =>
|
|
||||||
this.getRawFileData({
|
|
||||||
path: this.file.path,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.createEditorInstance();
|
this.createEditorInstance();
|
||||||
})
|
})
|
||||||
@ -179,6 +171,20 @@ export default {
|
|||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
fetchFileData() {
|
||||||
|
if (this.file.tempFile) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getFileData({
|
||||||
|
path: this.file.path,
|
||||||
|
makeFileActive: false,
|
||||||
|
}).then(() =>
|
||||||
|
this.getRawFileData({
|
||||||
|
path: this.file.path,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
createEditorInstance() {
|
createEditorInstance() {
|
||||||
this.editor.dispose();
|
this.editor.dispose();
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
|
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
|
||||||
import { decorateData, sortTree } from '../stores/utils';
|
import { decorateData, sortTree, escapeFileUrl } from '../stores/utils';
|
||||||
|
|
||||||
export const escapeFileUrl = fileUrl => encodeURIComponent(fileUrl).replace(/%2F/g, '/');
|
|
||||||
|
|
||||||
export const splitParent = path => {
|
export const splitParent = path => {
|
||||||
const idx = path.lastIndexOf('/');
|
const idx = path.lastIndexOf('/');
|
||||||
|
@ -9,6 +9,7 @@ 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';
|
import router from '../ide_router';
|
||||||
|
import eventHub from '../eventhub';
|
||||||
|
|
||||||
export const redirectToUrl = (self, url) => visitUrl(url);
|
export const redirectToUrl = (self, url) => visitUrl(url);
|
||||||
|
|
||||||
@ -171,8 +172,10 @@ export const setCurrentBranchId = ({ commit }, currentBranchId) => {
|
|||||||
export const updateTempFlagForEntry = ({ commit, dispatch, state }, { file, tempFile }) => {
|
export const updateTempFlagForEntry = ({ commit, dispatch, state }, { file, tempFile }) => {
|
||||||
commit(types.UPDATE_TEMP_FLAG, { path: file.path, tempFile });
|
commit(types.UPDATE_TEMP_FLAG, { path: file.path, tempFile });
|
||||||
|
|
||||||
if (file.parentPath) {
|
const parent = file.parentPath && state.entries[file.parentPath];
|
||||||
dispatch('updateTempFlagForEntry', { file: state.entries[file.parentPath], tempFile });
|
|
||||||
|
if (parent) {
|
||||||
|
dispatch('updateTempFlagForEntry', { file: parent, tempFile });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -199,51 +202,71 @@ export const openNewEntryModal = ({ commit }, { type, path = '' }) => {
|
|||||||
|
|
||||||
export const deleteEntry = ({ commit, dispatch, state }, path) => {
|
export const deleteEntry = ({ commit, dispatch, state }, path) => {
|
||||||
const entry = state.entries[path];
|
const entry = state.entries[path];
|
||||||
|
const { prevPath, prevName, prevParentPath } = entry;
|
||||||
|
const isTree = entry.type === 'tree';
|
||||||
|
|
||||||
|
if (prevPath) {
|
||||||
|
dispatch('renameEntry', {
|
||||||
|
path,
|
||||||
|
name: prevName,
|
||||||
|
parentPath: prevParentPath,
|
||||||
|
});
|
||||||
|
dispatch('deleteEntry', prevPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (state.unusedSeal) dispatch('burstUnusedSeal');
|
if (state.unusedSeal) dispatch('burstUnusedSeal');
|
||||||
if (entry.opened) dispatch('closeFile', entry);
|
if (entry.opened) dispatch('closeFile', entry);
|
||||||
|
|
||||||
if (entry.type === 'tree') {
|
if (isTree) {
|
||||||
entry.tree.forEach(f => dispatch('deleteEntry', f.path));
|
entry.tree.forEach(f => dispatch('deleteEntry', f.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
commit(types.DELETE_ENTRY, path);
|
commit(types.DELETE_ENTRY, path);
|
||||||
dispatch('stageChange', path);
|
|
||||||
|
// Only stage if we're not a directory or a new file
|
||||||
|
if (!isTree && !entry.tempFile) {
|
||||||
|
dispatch('stageChange', path);
|
||||||
|
}
|
||||||
|
|
||||||
dispatch('triggerFilesChange');
|
dispatch('triggerFilesChange');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
|
export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
|
||||||
|
|
||||||
export const renameEntry = (
|
export const renameEntry = ({ dispatch, commit, state }, { path, name, parentPath }) => {
|
||||||
{ dispatch, commit, state },
|
const entry = state.entries[path];
|
||||||
{ path, name, entryPath = null, parentPath },
|
const newPath = parentPath ? `${parentPath}/${name}` : name;
|
||||||
) => {
|
|
||||||
const entry = state.entries[entryPath || path];
|
|
||||||
|
|
||||||
commit(types.RENAME_ENTRY, { path, name, entryPath, parentPath });
|
commit(types.RENAME_ENTRY, { path, name, parentPath });
|
||||||
|
|
||||||
if (entry.type === 'tree') {
|
if (entry.type === 'tree') {
|
||||||
const slashedParentPath = parentPath ? `${parentPath}/` : '';
|
state.entries[newPath].tree.forEach(f => {
|
||||||
const targetEntry = entryPath ? entryPath.split('/').pop() : name;
|
|
||||||
const newParentPath = `${slashedParentPath}${targetEntry}`;
|
|
||||||
|
|
||||||
state.entries[entryPath || path].tree.forEach(f => {
|
|
||||||
dispatch('renameEntry', {
|
dispatch('renameEntry', {
|
||||||
path,
|
path: f.path,
|
||||||
name,
|
name: f.name,
|
||||||
entryPath: f.path,
|
parentPath: newPath,
|
||||||
parentPath: newParentPath,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const newPath = parentPath ? `${parentPath}/${name}` : name;
|
|
||||||
const newEntry = state.entries[newPath];
|
const newEntry = state.entries[newPath];
|
||||||
commit(types.TOGGLE_FILE_CHANGED, { file: newEntry, changed: true });
|
const isRevert = newPath === entry.prevPath;
|
||||||
|
const isReset = isRevert && !newEntry.changed && !newEntry.tempFile;
|
||||||
|
const isInChanges = state.changedFiles
|
||||||
|
.concat(state.stagedFiles)
|
||||||
|
.some(({ key }) => key === newEntry.key);
|
||||||
|
|
||||||
if (entry.opened) {
|
if (isReset) {
|
||||||
|
commit(types.REMOVE_FILE_FROM_STAGED_AND_CHANGED, newEntry);
|
||||||
|
} else if (!isInChanges) {
|
||||||
|
commit(types.ADD_FILE_TO_CHANGED, newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newEntry.tempFile) {
|
||||||
|
eventHub.$emit(`editor.update.model.dispose.${entry.key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newEntry.opened) {
|
||||||
router.push(`/project${newEntry.url}`);
|
router.push(`/project${newEntry.url}`);
|
||||||
commit(types.TOGGLE_FILE_OPEN, entry.path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import eventHub from '../../eventhub';
|
|||||||
import service from '../../services';
|
import service from '../../services';
|
||||||
import * as types from '../mutation_types';
|
import * as types from '../mutation_types';
|
||||||
import router from '../../ide_router';
|
import router from '../../ide_router';
|
||||||
import { setPageTitle } from '../utils';
|
import { setPageTitle, replaceFileUrl } from '../utils';
|
||||||
import { viewerTypes, stageKeys } from '../../constants';
|
import { viewerTypes, stageKeys } from '../../constants';
|
||||||
|
|
||||||
export const closeFile = ({ commit, state, dispatch }, file) => {
|
export const closeFile = ({ commit, state, dispatch }, file) => {
|
||||||
@ -67,7 +67,7 @@ export const getFileData = (
|
|||||||
|
|
||||||
commit(types.TOGGLE_LOADING, { entry: file });
|
commit(types.TOGGLE_LOADING, { entry: file });
|
||||||
|
|
||||||
const url = file.prevPath ? file.url.replace(file.path, file.prevPath) : file.url;
|
const url = file.prevPath ? replaceFileUrl(file.url, file.path, file.prevPath) : file.url;
|
||||||
|
|
||||||
return service
|
return service
|
||||||
.getFileData(joinPaths(gon.relative_url_root || '', url.replace('/-/', '/')))
|
.getFileData(joinPaths(gon.relative_url_root || '', url.replace('/-/', '/')))
|
||||||
@ -186,11 +186,6 @@ export const discardFileChanges = ({ dispatch, state, commit, getters }, path) =
|
|||||||
dispatch('restoreTree', file.parentPath);
|
dispatch('restoreTree', file.parentPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.movedPath) {
|
|
||||||
commit(types.DISCARD_FILE_CHANGES, file.movedPath);
|
|
||||||
commit(types.REMOVE_FILE_FROM_CHANGED, file.movedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
commit(types.DISCARD_FILE_CHANGES, path);
|
commit(types.DISCARD_FILE_CHANGES, path);
|
||||||
commit(types.REMOVE_FILE_FROM_CHANGED, path);
|
commit(types.REMOVE_FILE_FROM_CHANGED, path);
|
||||||
|
|
||||||
|
@ -92,13 +92,27 @@ export const showEmptyState = ({ commit, state }, { projectId, branchId }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openBranch = ({ dispatch, state, getters }, { projectId, branchId, basePath }) => {
|
export const loadFile = ({ dispatch, state }, { basePath }) => {
|
||||||
dispatch('setCurrentBranchId', branchId);
|
if (basePath) {
|
||||||
|
const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
|
||||||
|
const treeEntryKey = Object.keys(state.entries).find(
|
||||||
|
key => key === path && !state.entries[key].pending,
|
||||||
|
);
|
||||||
|
const treeEntry = state.entries[treeEntryKey];
|
||||||
|
|
||||||
if (getters.emptyRepo) {
|
if (treeEntry) {
|
||||||
return dispatch('showEmptyState', { projectId, branchId });
|
dispatch('handleTreeEntryAction', treeEntry);
|
||||||
|
} else {
|
||||||
|
dispatch('createTempEntry', {
|
||||||
|
name: path,
|
||||||
|
type: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return dispatch('getBranchData', {
|
};
|
||||||
|
|
||||||
|
export const loadBranch = ({ dispatch }, { projectId, branchId }) =>
|
||||||
|
dispatch('getBranchData', {
|
||||||
projectId,
|
projectId,
|
||||||
branchId,
|
branchId,
|
||||||
})
|
})
|
||||||
@ -107,42 +121,38 @@ export const openBranch = ({ dispatch, state, getters }, { projectId, branchId,
|
|||||||
projectId,
|
projectId,
|
||||||
branchId,
|
branchId,
|
||||||
});
|
});
|
||||||
dispatch('getFiles', {
|
return dispatch('getFiles', {
|
||||||
projectId,
|
projectId,
|
||||||
branchId,
|
branchId,
|
||||||
})
|
});
|
||||||
.then(() => {
|
|
||||||
if (basePath) {
|
|
||||||
const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
|
|
||||||
const treeEntryKey = Object.keys(state.entries).find(
|
|
||||||
key => key === path && !state.entries[key].pending,
|
|
||||||
);
|
|
||||||
const treeEntry = state.entries[treeEntryKey];
|
|
||||||
|
|
||||||
if (treeEntry) {
|
|
||||||
dispatch('handleTreeEntryAction', treeEntry);
|
|
||||||
} else {
|
|
||||||
dispatch('createTempEntry', {
|
|
||||||
name: path,
|
|
||||||
type: 'blob',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(
|
|
||||||
() =>
|
|
||||||
new Error(
|
|
||||||
sprintf(
|
|
||||||
__('An error occurred whilst getting files for - %{branchId}'),
|
|
||||||
{
|
|
||||||
branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
dispatch('showBranchNotFoundError', branchId);
|
dispatch('showBranchNotFoundError', branchId);
|
||||||
|
return Promise.reject();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const openBranch = ({ dispatch, state, getters }, { projectId, branchId, basePath }) => {
|
||||||
|
const currentProject = state.projects[projectId];
|
||||||
|
if (getters.emptyRepo) {
|
||||||
|
return dispatch('showEmptyState', { projectId, branchId });
|
||||||
|
}
|
||||||
|
if (!currentProject || !currentProject.branches[branchId]) {
|
||||||
|
dispatch('setCurrentBranchId', branchId);
|
||||||
|
|
||||||
|
return dispatch('loadBranch', { projectId, branchId })
|
||||||
|
.then(() => dispatch('loadFile', { basePath }))
|
||||||
|
.catch(
|
||||||
|
() =>
|
||||||
|
new Error(
|
||||||
|
sprintf(
|
||||||
|
__('An error occurred whilst getting files for - %{branchId}'),
|
||||||
|
{
|
||||||
|
branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Promise.resolve(dispatch('loadFile', { basePath }));
|
||||||
};
|
};
|
||||||
|
@ -154,8 +154,6 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true });
|
commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true });
|
||||||
|
|
||||||
commit(rootTypes.CLEAR_REPLACED_FILES, null, { root: true });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
|
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
@ -59,8 +59,7 @@ export const UPDATE_DELAY_VIEWER_CHANGE = 'UPDATE_DELAY_VIEWER_CHANGE';
|
|||||||
export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES';
|
export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES';
|
||||||
export const STAGE_CHANGE = 'STAGE_CHANGE';
|
export const STAGE_CHANGE = 'STAGE_CHANGE';
|
||||||
export const UNSTAGE_CHANGE = 'UNSTAGE_CHANGE';
|
export const UNSTAGE_CHANGE = 'UNSTAGE_CHANGE';
|
||||||
|
export const REMOVE_FILE_FROM_STAGED_AND_CHANGED = 'REMOVE_FILE_FROM_STAGED_AND_CHANGED';
|
||||||
export const CLEAR_REPLACED_FILES = 'CLEAR_REPLACED_FILES';
|
|
||||||
|
|
||||||
export const UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
|
export const UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
|
||||||
export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
|
export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
|
||||||
@ -79,5 +78,6 @@ export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
|
|||||||
export const OPEN_NEW_ENTRY_MODAL = 'OPEN_NEW_ENTRY_MODAL';
|
export const OPEN_NEW_ENTRY_MODAL = 'OPEN_NEW_ENTRY_MODAL';
|
||||||
export const DELETE_ENTRY = 'DELETE_ENTRY';
|
export const DELETE_ENTRY = 'DELETE_ENTRY';
|
||||||
export const RENAME_ENTRY = 'RENAME_ENTRY';
|
export const RENAME_ENTRY = 'RENAME_ENTRY';
|
||||||
|
export const REVERT_RENAME_ENTRY = 'REVERT_RENAME_ENTRY';
|
||||||
|
|
||||||
export const RESTORE_TREE = 'RESTORE_TREE';
|
export const RESTORE_TREE = 'RESTORE_TREE';
|
||||||
|
@ -5,7 +5,14 @@ import mergeRequestMutation from './mutations/merge_request';
|
|||||||
import fileMutations from './mutations/file';
|
import fileMutations from './mutations/file';
|
||||||
import treeMutations from './mutations/tree';
|
import treeMutations from './mutations/tree';
|
||||||
import branchMutations from './mutations/branch';
|
import branchMutations from './mutations/branch';
|
||||||
import { sortTree } from './utils';
|
import {
|
||||||
|
sortTree,
|
||||||
|
replaceFileUrl,
|
||||||
|
swapInParentTreeWithSorting,
|
||||||
|
updateFileCollections,
|
||||||
|
removeFromParentTree,
|
||||||
|
pathsAreEqual,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
[types.SET_INITIAL_DATA](state, data) {
|
[types.SET_INITIAL_DATA](state, data) {
|
||||||
@ -56,11 +63,6 @@ export default {
|
|||||||
stagedFiles: [],
|
stagedFiles: [],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[types.CLEAR_REPLACED_FILES](state) {
|
|
||||||
Object.assign(state, {
|
|
||||||
replacedFiles: [],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[types.SET_ENTRIES](state, entries) {
|
[types.SET_ENTRIES](state, entries) {
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
entries,
|
entries,
|
||||||
@ -157,9 +159,14 @@ export default {
|
|||||||
changed: Boolean(changedFile),
|
changed: Boolean(changedFile),
|
||||||
staged: false,
|
staged: false,
|
||||||
replaces: false,
|
replaces: false,
|
||||||
prevPath: '',
|
|
||||||
moved: false,
|
|
||||||
lastCommitSha: lastCommit.commit.id,
|
lastCommitSha: lastCommit.commit.id,
|
||||||
|
|
||||||
|
prevId: undefined,
|
||||||
|
prevPath: undefined,
|
||||||
|
prevName: undefined,
|
||||||
|
prevUrl: undefined,
|
||||||
|
prevKey: undefined,
|
||||||
|
prevParentPath: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (prevPath) {
|
if (prevPath) {
|
||||||
@ -209,7 +216,9 @@ export default {
|
|||||||
|
|
||||||
entry.deleted = true;
|
entry.deleted = true;
|
||||||
|
|
||||||
parent.tree = parent.tree.filter(f => f.path !== entry.path);
|
if (parent) {
|
||||||
|
parent.tree = parent.tree.filter(f => f.path !== entry.path);
|
||||||
|
}
|
||||||
|
|
||||||
if (entry.type === 'blob') {
|
if (entry.type === 'blob') {
|
||||||
if (tempFile) {
|
if (tempFile) {
|
||||||
@ -219,51 +228,61 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[types.RENAME_ENTRY](state, { path, name, entryPath = null, parentPath }) {
|
[types.RENAME_ENTRY](state, { path, name, parentPath }) {
|
||||||
const oldEntry = state.entries[entryPath || path];
|
const oldEntry = state.entries[path];
|
||||||
const slashedParentPath = parentPath ? `${parentPath}/` : '';
|
const newPath = parentPath ? `${parentPath}/${name}` : name;
|
||||||
const newPath = entryPath
|
const isRevert = newPath === oldEntry.prevPath;
|
||||||
? `${slashedParentPath}${oldEntry.name}`
|
|
||||||
: `${slashedParentPath}${name}`;
|
const newUrl = replaceFileUrl(oldEntry.url, oldEntry.path, newPath);
|
||||||
|
|
||||||
|
const newKey = oldEntry.key.replace(new RegExp(oldEntry.path, 'g'), newPath);
|
||||||
|
|
||||||
|
const baseProps = {
|
||||||
|
...oldEntry,
|
||||||
|
name,
|
||||||
|
id: newPath,
|
||||||
|
path: newPath,
|
||||||
|
url: newUrl,
|
||||||
|
key: newKey,
|
||||||
|
parentPath: parentPath || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevProps =
|
||||||
|
oldEntry.tempFile || isRevert
|
||||||
|
? {
|
||||||
|
prevId: undefined,
|
||||||
|
prevPath: undefined,
|
||||||
|
prevName: undefined,
|
||||||
|
prevUrl: undefined,
|
||||||
|
prevKey: undefined,
|
||||||
|
prevParentPath: undefined,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
prevId: oldEntry.prevId || oldEntry.id,
|
||||||
|
prevPath: oldEntry.prevPath || oldEntry.path,
|
||||||
|
prevName: oldEntry.prevName || oldEntry.name,
|
||||||
|
prevUrl: oldEntry.prevUrl || oldEntry.url,
|
||||||
|
prevKey: oldEntry.prevKey || oldEntry.key,
|
||||||
|
prevParentPath: oldEntry.prevParentPath || oldEntry.parentPath,
|
||||||
|
};
|
||||||
|
|
||||||
Vue.set(state.entries, newPath, {
|
Vue.set(state.entries, newPath, {
|
||||||
...oldEntry,
|
...baseProps,
|
||||||
id: newPath,
|
...prevProps,
|
||||||
key: `${newPath}-${oldEntry.type}-${oldEntry.path}`,
|
|
||||||
path: newPath,
|
|
||||||
name: entryPath ? oldEntry.name : name,
|
|
||||||
tempFile: true,
|
|
||||||
prevPath: oldEntry.tempFile ? null : oldEntry.path,
|
|
||||||
url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath),
|
|
||||||
tree: [],
|
|
||||||
raw: '',
|
|
||||||
opened: false,
|
|
||||||
parentPath,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
oldEntry.moved = true;
|
if (pathsAreEqual(oldEntry.parentPath, parentPath)) {
|
||||||
oldEntry.movedPath = newPath;
|
swapInParentTreeWithSorting(state, oldEntry.key, newPath, parentPath);
|
||||||
|
} else {
|
||||||
const parent = parentPath
|
removeFromParentTree(state, oldEntry.key, oldEntry.parentPath);
|
||||||
? state.entries[parentPath]
|
swapInParentTreeWithSorting(state, oldEntry.key, newPath, parentPath);
|
||||||
: state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
|
|
||||||
const newEntry = state.entries[newPath];
|
|
||||||
|
|
||||||
parent.tree = sortTree(parent.tree.concat(newEntry));
|
|
||||||
|
|
||||||
if (newEntry.type === 'blob') {
|
|
||||||
state.changedFiles = state.changedFiles.concat(newEntry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldEntry.tempFile) {
|
if (oldEntry.type === 'blob') {
|
||||||
const filterMethod = f => f.path !== oldEntry.path;
|
updateFileCollections(state, oldEntry.key, newPath);
|
||||||
|
|
||||||
state.openFiles = state.openFiles.filter(filterMethod);
|
|
||||||
state.changedFiles = state.changedFiles.filter(filterMethod);
|
|
||||||
parent.tree = parent.tree.filter(filterMethod);
|
|
||||||
|
|
||||||
Vue.delete(state.entries, oldEntry.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vue.delete(state.entries, oldEntry.path);
|
||||||
},
|
},
|
||||||
|
|
||||||
...projectMutations,
|
...projectMutations,
|
||||||
|
@ -138,8 +138,6 @@ export default {
|
|||||||
content: stagedFile ? stagedFile.content : state.entries[path].raw,
|
content: stagedFile ? stagedFile.content : state.entries[path].raw,
|
||||||
changed: false,
|
changed: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
moved: false,
|
|
||||||
movedPath: '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
@ -179,11 +177,6 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (stagedFile) {
|
if (stagedFile) {
|
||||||
Object.assign(state, {
|
|
||||||
replacedFiles: state.replacedFiles.concat({
|
|
||||||
...stagedFile,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
Object.assign(stagedFile, {
|
Object.assign(stagedFile, {
|
||||||
...state.entries[path],
|
...state.entries[path],
|
||||||
});
|
});
|
||||||
@ -252,4 +245,15 @@ export default {
|
|||||||
openFiles: state.openFiles.filter(f => f.key !== file.key),
|
openFiles: state.openFiles.filter(f => f.key !== file.key),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
[types.REMOVE_FILE_FROM_STAGED_AND_CHANGED](state, file) {
|
||||||
|
Object.assign(state, {
|
||||||
|
changedFiles: state.changedFiles.filter(f => f.key !== file.key),
|
||||||
|
stagedFiles: state.stagedFiles.filter(f => f.key !== file.key),
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(state.entries[file.path], {
|
||||||
|
changed: false,
|
||||||
|
staged: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,6 @@ export default () => ({
|
|||||||
currentMergeRequestId: '',
|
currentMergeRequestId: '',
|
||||||
changedFiles: [],
|
changedFiles: [],
|
||||||
stagedFiles: [],
|
stagedFiles: [],
|
||||||
replacedFiles: [],
|
|
||||||
endpoints: {},
|
endpoints: {},
|
||||||
lastCommitMsg: '',
|
lastCommitMsg: '',
|
||||||
lastCommitPath: '',
|
lastCommitPath: '',
|
||||||
|
@ -50,9 +50,7 @@ export const dataStructure = () => ({
|
|||||||
lastOpenedAt: 0,
|
lastOpenedAt: 0,
|
||||||
mrChange: null,
|
mrChange: null,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
prevPath: '',
|
prevPath: undefined,
|
||||||
movedPath: '',
|
|
||||||
moved: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const decorateData = entity => {
|
export const decorateData = entity => {
|
||||||
@ -129,7 +127,7 @@ export const commitActionForFile = file => {
|
|||||||
|
|
||||||
export const getCommitFiles = stagedFiles =>
|
export const getCommitFiles = stagedFiles =>
|
||||||
stagedFiles.reduce((acc, file) => {
|
stagedFiles.reduce((acc, file) => {
|
||||||
if (file.moved || file.type === 'tree') return acc;
|
if (file.type === 'tree') return acc;
|
||||||
|
|
||||||
return acc.concat({
|
return acc.concat({
|
||||||
...file,
|
...file,
|
||||||
@ -148,9 +146,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.moved ? f.movedPath : f.path,
|
file_path: f.path,
|
||||||
previous_path: f.prevPath === '' ? undefined : f.prevPath,
|
previous_path: f.prevPath || undefined,
|
||||||
content: f.prevPath ? null : f.content || undefined,
|
content: f.prevPath && !f.changed ? null : f.content || undefined,
|
||||||
encoding: f.base64 ? 'base64' : 'text',
|
encoding: f.base64 ? 'base64' : 'text',
|
||||||
last_commit_id:
|
last_commit_id:
|
||||||
newBranch || f.deleted || f.prevPath || f.replaces ? undefined : f.lastCommitSha,
|
newBranch || f.deleted || f.prevPath || f.replaces ? undefined : f.lastCommitSha,
|
||||||
@ -213,3 +211,61 @@ export const mergeTrees = (fromTree, toTree) => {
|
|||||||
|
|
||||||
return toTree;
|
return toTree;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const escapeFileUrl = fileUrl => encodeURIComponent(fileUrl).replace(/%2F/g, '/');
|
||||||
|
|
||||||
|
export const replaceFileUrl = (url, oldPath, newPath) => {
|
||||||
|
// Add `/-/` so that we don't accidentally replace project path
|
||||||
|
const result = url.replace(`/-/${escapeFileUrl(oldPath)}`, `/-/${escapeFileUrl(newPath)}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const swapInStateArray = (state, arr, key, entryPath) =>
|
||||||
|
Object.assign(state, {
|
||||||
|
[arr]: state[arr].map(f => (f.key === key ? state.entries[entryPath] : f)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getEntryOrRoot = (state, path) =>
|
||||||
|
path ? state.entries[path] : state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
|
||||||
|
|
||||||
|
export const swapInParentTreeWithSorting = (state, oldKey, newPath, parentPath) => {
|
||||||
|
if (!newPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = getEntryOrRoot(state, parentPath);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
const tree = parent.tree
|
||||||
|
// filter out old entry && new entry
|
||||||
|
.filter(({ key, path }) => key !== oldKey && path !== newPath)
|
||||||
|
// concat new entry
|
||||||
|
.concat(state.entries[newPath]);
|
||||||
|
|
||||||
|
parent.tree = sortTree(tree);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeFromParentTree = (state, oldKey, parentPath) => {
|
||||||
|
const parent = getEntryOrRoot(state, parentPath);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
parent.tree = sortTree(parent.tree.filter(({ key }) => key !== oldKey));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateFileCollections = (state, key, entryPath) => {
|
||||||
|
['openFiles', 'changedFiles', 'stagedFiles'].forEach(fileCollection => {
|
||||||
|
swapInStateArray(state, fileCollection, key, entryPath);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cleanTrailingSlash = path => path.replace(/\/$/, '');
|
||||||
|
|
||||||
|
export const pathsAreEqual = (a, b) => {
|
||||||
|
const cleanA = a ? cleanTrailingSlash(a) : '';
|
||||||
|
const cleanB = b ? cleanTrailingSlash(b) : '';
|
||||||
|
|
||||||
|
return cleanA === cleanB;
|
||||||
|
};
|
||||||
|
@ -70,7 +70,13 @@ export default {
|
|||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
showIcon() {
|
showIcon() {
|
||||||
return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted;
|
return (
|
||||||
|
this.file.changed ||
|
||||||
|
this.file.tempFile ||
|
||||||
|
this.file.staged ||
|
||||||
|
this.file.deleted ||
|
||||||
|
this.file.prevPath
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -131,7 +131,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="!file.moved">
|
<div>
|
||||||
<file-header v-if="file.isHeader" :path="file.path" />
|
<file-header v-if="file.isHeader" :path="file.path" />
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
|
@ -11,25 +11,10 @@
|
|||||||
@include webkit-prefix(animation-duration, 1s);
|
@include webkit-prefix(animation-duration, 1s);
|
||||||
@include webkit-prefix(animation-fill-mode, both);
|
@include webkit-prefix(animation-fill-mode, both);
|
||||||
|
|
||||||
&.infinite {
|
|
||||||
@include webkit-prefix(animation-iteration-count, infinite);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.once {
|
&.once {
|
||||||
@include webkit-prefix(animation-iteration-count, 1);
|
@include webkit-prefix(animation-iteration-count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hinge {
|
|
||||||
@include webkit-prefix(animation-duration, 2s);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.flipOutX,
|
|
||||||
&.flipOutY,
|
|
||||||
&.bounceIn,
|
|
||||||
&.bounceOut {
|
|
||||||
@include webkit-prefix(animation-duration, 0.75s);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.short {
|
&.short {
|
||||||
@include webkit-prefix(animation-duration, 321ms);
|
@include webkit-prefix(animation-duration, 321ms);
|
||||||
@include webkit-prefix(animation-fill-mode, none);
|
@include webkit-prefix(animation-fill-mode, none);
|
||||||
|
@ -35,7 +35,7 @@ module Resolvers
|
|||||||
description: 'Issues closed after this date'
|
description: 'Issues closed after this date'
|
||||||
argument :search, GraphQL::STRING_TYPE, # rubocop:disable Graphql/Descriptions
|
argument :search, GraphQL::STRING_TYPE, # rubocop:disable Graphql/Descriptions
|
||||||
required: false
|
required: false
|
||||||
argument :sort, Types::SortEnum,
|
argument :sort, Types::IssueSortEnum,
|
||||||
description: 'Sort issues by this criteria',
|
description: 'Sort issues by this criteria',
|
||||||
required: false,
|
required: false,
|
||||||
default_value: 'created_desc'
|
default_value: 'created_desc'
|
||||||
|
10
app/graphql/types/issuable_sort_enum.rb
Normal file
10
app/graphql/types/issuable_sort_enum.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Types
|
||||||
|
# rubocop: disable Graphql/AuthorizeTypes
|
||||||
|
class IssuableSortEnum < SortEnum
|
||||||
|
graphql_name 'IssuableSort'
|
||||||
|
description 'Values for sorting issuables'
|
||||||
|
end
|
||||||
|
# rubocop: enable Graphql/AuthorizeTypes
|
||||||
|
end
|
10
app/graphql/types/issue_sort_enum.rb
Normal file
10
app/graphql/types/issue_sort_enum.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Types
|
||||||
|
# rubocop: disable Graphql/AuthorizeTypes
|
||||||
|
class IssueSortEnum < IssuableSortEnum
|
||||||
|
graphql_name 'IssueSort'
|
||||||
|
description 'Values for sorting issues'
|
||||||
|
end
|
||||||
|
# rubocop: enable Graphql/AuthorizeTypes
|
||||||
|
end
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: Show 20 labels in dropdown instead of 5
|
||||||
|
merge_request: 17596
|
||||||
|
author:
|
||||||
|
type: fixed
|
@ -15,7 +15,10 @@ if (process.env.CI) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let testMatch = ['<rootDir>/spec/frontend/**/*_spec.js', '<rootDir>/ee/spec/frontend/**/*_spec.js'];
|
let testMatch = ['<rootDir>/spec/frontend/**/*_spec.js'];
|
||||||
|
if (IS_EE) {
|
||||||
|
testMatch.push('<rootDir>/ee/spec/frontend/**/*_spec.js');
|
||||||
|
}
|
||||||
|
|
||||||
// workaround for eslint-import-resolver-jest only resolving in test files
|
// workaround for eslint-import-resolver-jest only resolving in test files
|
||||||
// see https://github.com/JoinColony/eslint-import-resolver-jest#note
|
// see https://github.com/JoinColony/eslint-import-resolver-jest#note
|
||||||
|
@ -120,7 +120,7 @@ module Gitlab
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_feature_dependent_sub_relations(_relation_item)
|
def remove_feature_dependent_sub_relations!(_relation_item)
|
||||||
# no-op
|
# no-op
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ module Gitlab
|
|||||||
|
|
||||||
# Avoid keeping a possible heavy object in memory once we are done with it
|
# Avoid keeping a possible heavy object in memory once we are done with it
|
||||||
while relation_item = tree_array.shift
|
while relation_item = tree_array.shift
|
||||||
remove_feature_dependent_sub_relations(relation_item)
|
remove_feature_dependent_sub_relations!(relation_item)
|
||||||
|
|
||||||
# The transaction at this level is less speedy than one single transaction
|
# The transaction at this level is less speedy than one single transaction
|
||||||
# But we can't have it in the upper level or GC won't get rid of the AR objects
|
# But we can't have it in the upper level or GC won't get rid of the AR objects
|
||||||
|
@ -5,7 +5,11 @@ require 'set'
|
|||||||
|
|
||||||
module Gitlab
|
module Gitlab
|
||||||
module SidekiqConfig
|
module SidekiqConfig
|
||||||
QUEUE_CONFIG_PATHS = %w[app/workers/all_queues.yml ee/app/workers/all_queues.yml].freeze
|
QUEUE_CONFIG_PATHS = begin
|
||||||
|
result = %w[app/workers/all_queues.yml]
|
||||||
|
result << 'ee/app/workers/all_queues.yml' if Gitlab.ee?
|
||||||
|
result
|
||||||
|
end.freeze
|
||||||
|
|
||||||
# This method is called by `ee/bin/sidekiq-cluster` in EE, which runs outside
|
# This method is called by `ee/bin/sidekiq-cluster` in EE, which runs outside
|
||||||
# of bundler/Rails context, so we cannot use any gem or Rails methods.
|
# of bundler/Rails context, so we cannot use any gem or Rails methods.
|
||||||
@ -48,9 +52,11 @@ module Gitlab
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.workers
|
def self.workers
|
||||||
@workers ||=
|
@workers ||= begin
|
||||||
find_workers(Rails.root.join('app', 'workers')) +
|
result = find_workers(Rails.root.join('app', 'workers'))
|
||||||
find_workers(Rails.root.join('ee', 'app', 'workers'))
|
result.concat(find_workers(Rails.root.join('ee', 'app', 'workers'))) if Gitlab.ee?
|
||||||
|
result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_workers(root)
|
def self.find_workers(root)
|
||||||
|
@ -2,7 +2,10 @@ unless Rails.env.production?
|
|||||||
namespace :frontend do
|
namespace :frontend do
|
||||||
desc 'GitLab | Frontend | Generate fixtures for JavaScript tests'
|
desc 'GitLab | Frontend | Generate fixtures for JavaScript tests'
|
||||||
RSpec::Core::RakeTask.new(:fixtures, [:pattern]) do |t, args|
|
RSpec::Core::RakeTask.new(:fixtures, [:pattern]) do |t, args|
|
||||||
args.with_defaults(pattern: '{spec,ee/spec}/frontend/fixtures/*.rb')
|
directories = %w[spec]
|
||||||
|
directories << 'ee/spec' if Gitlab.ee?
|
||||||
|
directory_glob = "{#{directories.join(',')}}"
|
||||||
|
args.with_defaults(pattern: "#{directory_glob}/frontend/fixtures/*.rb")
|
||||||
ENV['NO_KNAPSACK'] = 'true'
|
ENV['NO_KNAPSACK'] = 'true'
|
||||||
t.pattern = args[:pattern]
|
t.pattern = args[:pattern]
|
||||||
t.rspec_opts = '--format documentation'
|
t.rspec_opts = '--format documentation'
|
||||||
|
@ -256,7 +256,7 @@ describe Projects::EnvironmentsController do
|
|||||||
it 'loads the terminals for the environment' do
|
it 'loads the terminals for the environment' do
|
||||||
# In EE we have to stub EE::Environment since it overwrites the
|
# In EE we have to stub EE::Environment since it overwrites the
|
||||||
# "terminals" method.
|
# "terminals" method.
|
||||||
expect_any_instance_of(defined?(EE) ? EE::Environment : Environment)
|
expect_any_instance_of(Gitlab.ee? ? EE::Environment : Environment)
|
||||||
.to receive(:terminals)
|
.to receive(:terminals)
|
||||||
|
|
||||||
get :terminal, params: environment_params
|
get :terminal, params: environment_params
|
||||||
@ -282,7 +282,7 @@ describe Projects::EnvironmentsController do
|
|||||||
it 'returns the first terminal for the environment' do
|
it 'returns the first terminal for the environment' do
|
||||||
# In EE we have to stub EE::Environment since it overwrites the
|
# In EE we have to stub EE::Environment since it overwrites the
|
||||||
# "terminals" method.
|
# "terminals" method.
|
||||||
expect_any_instance_of(defined?(EE) ? EE::Environment : Environment)
|
expect_any_instance_of(Gitlab.ee? ? EE::Environment : Environment)
|
||||||
.to receive(:terminals)
|
.to receive(:terminals)
|
||||||
.and_return([:fake_terminal])
|
.and_return([:fake_terminal])
|
||||||
|
|
||||||
|
@ -24,6 +24,10 @@ describe Projects::AutocompleteSourcesController, '(JavaScript fixtures)', type:
|
|||||||
|
|
||||||
create(:label, project: project, title: 'feature')
|
create(:label, project: project, title: 'feature')
|
||||||
create(:label, project: project, title: 'documentation')
|
create(:label, project: project, title: 'documentation')
|
||||||
|
create(:label, project: project, title: 'P1')
|
||||||
|
create(:label, project: project, title: 'P2')
|
||||||
|
create(:label, project: project, title: 'P3')
|
||||||
|
create(:label, project: project, title: 'P4')
|
||||||
|
|
||||||
get :labels,
|
get :labels,
|
||||||
format: :json,
|
format: :json,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
|
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
|
||||||
import { decorateFiles, splitParent, escapeFileUrl } from '~/ide/lib/files';
|
import { decorateFiles, splitParent } from '~/ide/lib/files';
|
||||||
import { decorateData } from '~/ide/stores/utils';
|
import { decorateData, escapeFileUrl } from '~/ide/stores/utils';
|
||||||
|
|
||||||
const TEST_BRANCH_ID = 'lorem-ipsum';
|
const TEST_BRANCH_ID = 'lorem-ipsum';
|
||||||
const TEST_PROJECT_ID = 10;
|
const TEST_PROJECT_ID = 10;
|
||||||
|
@ -2,12 +2,14 @@ import Vue from 'vue';
|
|||||||
import store from '~/ide/stores';
|
import store from '~/ide/stores';
|
||||||
import listItem from '~/ide/components/commit_sidebar/list_item.vue';
|
import listItem from '~/ide/components/commit_sidebar/list_item.vue';
|
||||||
import router from '~/ide/ide_router';
|
import router from '~/ide/ide_router';
|
||||||
|
import { trimText } from 'spec/helpers/text_helper';
|
||||||
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||||
import { file, resetStore } from '../../helpers';
|
import { file, resetStore } from '../../helpers';
|
||||||
|
|
||||||
describe('Multi-file editor commit sidebar list item', () => {
|
describe('Multi-file editor commit sidebar list item', () => {
|
||||||
let vm;
|
let vm;
|
||||||
let f;
|
let f;
|
||||||
|
let findPathEl;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const Component = Vue.extend(listItem);
|
const Component = Vue.extend(listItem);
|
||||||
@ -21,6 +23,8 @@ describe('Multi-file editor commit sidebar list item', () => {
|
|||||||
actionComponent: 'stage-button',
|
actionComponent: 'stage-button',
|
||||||
activeFileKey: `staged-${f.key}`,
|
activeFileKey: `staged-${f.key}`,
|
||||||
}).$mount();
|
}).$mount();
|
||||||
|
|
||||||
|
findPathEl = vm.$el.querySelector('.multi-file-commit-list-path');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -29,15 +33,39 @@ describe('Multi-file editor commit sidebar list item', () => {
|
|||||||
resetStore(store);
|
resetStore(store);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const findPathText = () => trimText(findPathEl.textContent);
|
||||||
|
|
||||||
it('renders file path', () => {
|
it('renders file path', () => {
|
||||||
expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent).toContain(f.path);
|
expect(findPathText()).toContain(f.path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly renders renamed entries', done => {
|
||||||
|
Vue.set(vm.file, 'prevName', 'Old name');
|
||||||
|
|
||||||
|
vm.$nextTick()
|
||||||
|
.then(() => {
|
||||||
|
expect(findPathText()).toEqual(`Old name → ${f.name}`);
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly renders entry, the name of which did not change after rename (as within a folder)', done => {
|
||||||
|
Vue.set(vm.file, 'prevName', f.name);
|
||||||
|
|
||||||
|
vm.$nextTick()
|
||||||
|
.then(() => {
|
||||||
|
expect(findPathText()).toEqual(f.name);
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens a closed file in the editor when clicking the file path', done => {
|
it('opens a closed file in the editor when clicking the file path', done => {
|
||||||
spyOn(vm, 'openPendingTab').and.callThrough();
|
spyOn(vm, 'openPendingTab').and.callThrough();
|
||||||
spyOn(router, 'push');
|
spyOn(router, 'push');
|
||||||
|
|
||||||
vm.$el.querySelector('.multi-file-commit-list-path').click();
|
findPathEl.click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(vm.openPendingTab).toHaveBeenCalled();
|
expect(vm.openPendingTab).toHaveBeenCalled();
|
||||||
@ -52,7 +80,7 @@ describe('Multi-file editor commit sidebar list item', () => {
|
|||||||
spyOn(vm, 'updateViewer').and.callThrough();
|
spyOn(vm, 'updateViewer').and.callThrough();
|
||||||
spyOn(router, 'push');
|
spyOn(router, 'push');
|
||||||
|
|
||||||
vm.$el.querySelector('.multi-file-commit-list-path').click();
|
findPathEl.click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(vm.updateViewer).toHaveBeenCalledWith('diff');
|
expect(vm.updateViewer).toHaveBeenCalledWith('diff');
|
||||||
|
@ -139,6 +139,27 @@ describe('IDE extra file row component', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows when file is renamed', done => {
|
||||||
|
vm.file.prevPath = 'original-file';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides when file is renamed', done => {
|
||||||
|
vm.file.prevPath = 'original-file';
|
||||||
|
vm.file.type = 'tree';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.querySelector('.file-changed-icon')).toBe(null);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('merge request icon', () => {
|
describe('merge request icon', () => {
|
||||||
|
@ -58,20 +58,6 @@ 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', () => {
|
||||||
|
@ -410,10 +410,23 @@ describe('RepoEditor', () => {
|
|||||||
|
|
||||||
describe('initEditor', () => {
|
describe('initEditor', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
vm.file.tempFile = false;
|
||||||
spyOn(vm.editor, 'createInstance');
|
spyOn(vm.editor, 'createInstance');
|
||||||
spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
|
spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not fetch file information for temp entries', done => {
|
||||||
|
vm.file.tempFile = true;
|
||||||
|
|
||||||
|
vm.initEditor();
|
||||||
|
vm.$nextTick()
|
||||||
|
.then(() => {
|
||||||
|
expect(vm.getFileData).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
it('is being initialised for files without content even if shouldHideEditor is `true`', done => {
|
it('is being initialised for files without content even if shouldHideEditor is `true`', done => {
|
||||||
vm.file.content = '';
|
vm.file.content = '';
|
||||||
vm.file.raw = '';
|
vm.file.raw = '';
|
||||||
@ -429,16 +442,13 @@ describe('RepoEditor', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not initialize editor for files already with content', done => {
|
it('does not initialize editor for files already with content', done => {
|
||||||
expect(vm.getFileData.calls.count()).toEqual(1);
|
|
||||||
expect(vm.getRawFileData.calls.count()).toEqual(1);
|
|
||||||
|
|
||||||
vm.file.content = 'foo';
|
vm.file.content = 'foo';
|
||||||
|
|
||||||
vm.initEditor();
|
vm.initEditor();
|
||||||
vm.$nextTick()
|
vm.$nextTick()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.getFileData.calls.count()).toEqual(1);
|
expect(vm.getFileData).not.toHaveBeenCalled();
|
||||||
expect(vm.getRawFileData.calls.count()).toEqual(1);
|
expect(vm.getRawFileData).not.toHaveBeenCalled();
|
||||||
expect(vm.editor.createInstance).not.toHaveBeenCalled();
|
expect(vm.editor.createInstance).not.toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done)
|
||||||
@ -446,23 +456,56 @@ describe('RepoEditor', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls removePendingTab when old file is pending', done => {
|
describe('updates on file changes', () => {
|
||||||
spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
|
beforeEach(() => {
|
||||||
spyOn(vm, 'removePendingTab');
|
spyOn(vm, 'initEditor');
|
||||||
|
});
|
||||||
|
|
||||||
vm.file.pending = true;
|
it('calls removePendingTab when old file is pending', done => {
|
||||||
|
spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true);
|
||||||
|
spyOn(vm, 'removePendingTab');
|
||||||
|
|
||||||
vm.$nextTick()
|
vm.file.pending = true;
|
||||||
.then(() => {
|
|
||||||
vm.file = file('testing');
|
|
||||||
vm.file.content = 'foo'; // need to prevent full cycle of initEditor
|
|
||||||
|
|
||||||
return vm.$nextTick();
|
vm.$nextTick()
|
||||||
})
|
.then(() => {
|
||||||
.then(() => {
|
vm.file = file('testing');
|
||||||
expect(vm.removePendingTab).toHaveBeenCalled();
|
vm.file.content = 'foo'; // need to prevent full cycle of initEditor
|
||||||
})
|
|
||||||
.then(done)
|
return vm.$nextTick();
|
||||||
.catch(done.fail);
|
})
|
||||||
|
.then(() => {
|
||||||
|
expect(vm.removePendingTab).toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not call initEditor if the file did not change', done => {
|
||||||
|
Vue.set(vm, 'file', vm.file);
|
||||||
|
|
||||||
|
vm.$nextTick()
|
||||||
|
.then(() => {
|
||||||
|
expect(vm.initEditor).not.toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls initEditor when file key is changed', done => {
|
||||||
|
expect(vm.initEditor).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
Vue.set(vm, 'file', {
|
||||||
|
...vm.file,
|
||||||
|
key: 'new',
|
||||||
|
});
|
||||||
|
|
||||||
|
vm.$nextTick()
|
||||||
|
.then(() => {
|
||||||
|
expect(vm.initEditor).toHaveBeenCalled();
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,8 +6,10 @@ import {
|
|||||||
createNewBranchFromDefault,
|
createNewBranchFromDefault,
|
||||||
showEmptyState,
|
showEmptyState,
|
||||||
openBranch,
|
openBranch,
|
||||||
|
loadFile,
|
||||||
|
loadBranch,
|
||||||
} from '~/ide/stores/actions';
|
} from '~/ide/stores/actions';
|
||||||
import store from '~/ide/stores';
|
import { createStore } from '~/ide/stores';
|
||||||
import service from '~/ide/services';
|
import service from '~/ide/services';
|
||||||
import api from '~/api';
|
import api from '~/api';
|
||||||
import router from '~/ide/ide_router';
|
import router from '~/ide/ide_router';
|
||||||
@ -16,8 +18,10 @@ import testAction from '../../../helpers/vuex_action_helper';
|
|||||||
|
|
||||||
describe('IDE store project actions', () => {
|
describe('IDE store project actions', () => {
|
||||||
let mock;
|
let mock;
|
||||||
|
let store;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
store = createStore();
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
|
|
||||||
store.state.projects['abc/def'] = {
|
store.state.projects['abc/def'] = {
|
||||||
@ -231,28 +235,139 @@ describe('IDE store project actions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('loadFile', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
Object.assign(store.state, {
|
||||||
|
entries: {
|
||||||
|
foo: { pending: false },
|
||||||
|
'foo/bar-pending': { pending: true },
|
||||||
|
'foo/bar': { pending: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
spyOn(store, 'dispatch');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does nothing, if basePath is not given', () => {
|
||||||
|
loadFile(store, { basePath: undefined });
|
||||||
|
|
||||||
|
expect(store.dispatch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles tree entry action, if basePath is given and the entry is not pending', () => {
|
||||||
|
loadFile(store, { basePath: 'foo/bar/' });
|
||||||
|
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith(
|
||||||
|
'handleTreeEntryAction',
|
||||||
|
store.state.entries['foo/bar'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not handle tree entry action, if entry is pending', () => {
|
||||||
|
loadFile(store, { basePath: 'foo/bar-pending/' });
|
||||||
|
|
||||||
|
expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a new temp file supplied via URL if the file does not exist yet', () => {
|
||||||
|
loadFile(store, { basePath: 'not-existent.md' });
|
||||||
|
|
||||||
|
expect(store.dispatch.calls.count()).toBe(1);
|
||||||
|
|
||||||
|
expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything());
|
||||||
|
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith('createTempEntry', {
|
||||||
|
name: 'not-existent.md',
|
||||||
|
type: 'blob',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadBranch', () => {
|
||||||
|
const projectId = 'abc/def';
|
||||||
|
const branchId = '123-lorem';
|
||||||
|
|
||||||
|
it('fetches branch data', done => {
|
||||||
|
spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
|
||||||
|
|
||||||
|
loadBranch(store, { projectId, branchId })
|
||||||
|
.then(() => {
|
||||||
|
expect(store.dispatch.calls.allArgs()).toEqual([
|
||||||
|
['getBranchData', { projectId, branchId }],
|
||||||
|
['getMergeRequestsForBranch', { projectId, branchId }],
|
||||||
|
['getFiles', { projectId, branchId }],
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows an error if branch can not be fetched', done => {
|
||||||
|
spyOn(store, 'dispatch').and.returnValue(Promise.reject());
|
||||||
|
|
||||||
|
loadBranch(store, { projectId, branchId })
|
||||||
|
.then(done.fail)
|
||||||
|
.catch(() => {
|
||||||
|
expect(store.dispatch.calls.allArgs()).toEqual([
|
||||||
|
['getBranchData', { projectId, branchId }],
|
||||||
|
['showBranchNotFoundError', branchId],
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('openBranch', () => {
|
describe('openBranch', () => {
|
||||||
|
const projectId = 'abc/def';
|
||||||
|
const branchId = '123-lorem';
|
||||||
|
|
||||||
const branch = {
|
const branch = {
|
||||||
projectId: 'abc/def',
|
projectId,
|
||||||
branchId: '123-lorem',
|
branchId,
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
store.state.entries = {
|
Object.assign(store.state, {
|
||||||
foo: { pending: false },
|
entries: {
|
||||||
'foo/bar-pending': { pending: true },
|
foo: { pending: false },
|
||||||
'foo/bar': { pending: false },
|
'foo/bar-pending': { pending: true },
|
||||||
};
|
'foo/bar': { pending: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('loads file right away if the branch has already been fetched', done => {
|
||||||
|
spyOn(store, 'dispatch');
|
||||||
|
|
||||||
|
Object.assign(store.state, {
|
||||||
|
projects: {
|
||||||
|
[projectId]: {
|
||||||
|
branches: {
|
||||||
|
[branchId]: { foo: 'bar' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
openBranch(store, branch)
|
||||||
|
.then(() => {
|
||||||
|
expect(store.dispatch.calls.allArgs()).toEqual([['loadFile', { basePath: undefined }]]);
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('empty repo', () => {
|
describe('empty repo', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
|
spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
|
||||||
|
|
||||||
store.state.currentProjectId = 'abc/def';
|
Object.assign(store.state, {
|
||||||
store.state.projects['abc/def'] = {
|
currentProjectId: 'abc/def',
|
||||||
empty_repo: true,
|
projects: {
|
||||||
};
|
'abc/def': {
|
||||||
|
empty_repo: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -262,10 +377,7 @@ describe('IDE store project actions', () => {
|
|||||||
it('dispatches showEmptyState action right away', done => {
|
it('dispatches showEmptyState action right away', done => {
|
||||||
openBranch(store, branch)
|
openBranch(store, branch)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(store.dispatch.calls.allArgs()).toEqual([
|
expect(store.dispatch.calls.allArgs()).toEqual([['showEmptyState', branch]]);
|
||||||
['setCurrentBranchId', branch.branchId],
|
|
||||||
['showEmptyState', branch],
|
|
||||||
]);
|
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
@ -281,56 +393,14 @@ describe('IDE store project actions', () => {
|
|||||||
openBranch(store, branch)
|
openBranch(store, branch)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(store.dispatch.calls.allArgs()).toEqual([
|
expect(store.dispatch.calls.allArgs()).toEqual([
|
||||||
['setCurrentBranchId', branch.branchId],
|
['setCurrentBranchId', branchId],
|
||||||
['getBranchData', branch],
|
['loadBranch', { projectId, branchId }],
|
||||||
['getMergeRequestsForBranch', branch],
|
['loadFile', { basePath: undefined }],
|
||||||
['getFiles', branch],
|
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done)
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles tree entry action, if basePath is given', done => {
|
|
||||||
openBranch(store, { ...branch, basePath: 'foo/bar/' })
|
|
||||||
.then(() => {
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith(
|
|
||||||
'handleTreeEntryAction',
|
|
||||||
store.state.entries['foo/bar'],
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(done)
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not handle tree entry action, if entry is pending', done => {
|
|
||||||
openBranch(store, { ...branch, basePath: 'foo/bar-pending' })
|
|
||||||
.then(() => {
|
|
||||||
expect(store.dispatch).not.toHaveBeenCalledWith(
|
|
||||||
'handleTreeEntryAction',
|
|
||||||
jasmine.anything(),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(done)
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates a new file supplied via URL if the file does not exist yet', done => {
|
|
||||||
openBranch(store, { ...branch, basePath: 'not-existent.md' })
|
|
||||||
.then(() => {
|
|
||||||
expect(store.dispatch).not.toHaveBeenCalledWith(
|
|
||||||
'handleTreeEntryAction',
|
|
||||||
jasmine.anything(),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(store.dispatch).toHaveBeenCalledWith('createTempEntry', {
|
|
||||||
name: 'not-existent.md',
|
|
||||||
type: 'blob',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(done)
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('non-existent branch', () => {
|
describe('non-existent branch', () => {
|
||||||
@ -342,9 +412,8 @@ describe('IDE store project actions', () => {
|
|||||||
openBranch(store, branch)
|
openBranch(store, branch)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(store.dispatch.calls.allArgs()).toEqual([
|
expect(store.dispatch.calls.allArgs()).toEqual([
|
||||||
['setCurrentBranchId', branch.branchId],
|
['setCurrentBranchId', branchId],
|
||||||
['getBranchData', branch],
|
['loadBranch', { projectId, branchId }],
|
||||||
['showBranchNotFoundError', branch.branchId],
|
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
.then(done)
|
.then(done)
|
||||||
|
@ -13,12 +13,15 @@ import actions, {
|
|||||||
createTempEntry,
|
createTempEntry,
|
||||||
} from '~/ide/stores/actions';
|
} from '~/ide/stores/actions';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import store from '~/ide/stores';
|
import { createStore } from '~/ide/stores';
|
||||||
import * as types from '~/ide/stores/mutation_types';
|
import * as types from '~/ide/stores/mutation_types';
|
||||||
import router from '~/ide/ide_router';
|
import router from '~/ide/ide_router';
|
||||||
import { resetStore, file } from '../helpers';
|
import { resetStore, file } from '../helpers';
|
||||||
import testAction from '../../helpers/vuex_action_helper';
|
import testAction from '../../helpers/vuex_action_helper';
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import eventHub from '~/ide/eventhub';
|
||||||
|
|
||||||
|
const store = createStore();
|
||||||
|
|
||||||
describe('Multi-file store actions', () => {
|
describe('Multi-file store actions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -451,6 +454,24 @@ describe('Multi-file store actions', () => {
|
|||||||
done,
|
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', () => {
|
describe('setCurrentBranchId', () => {
|
||||||
@ -540,82 +561,298 @@ describe('Multi-file store actions', () => {
|
|||||||
done,
|
done,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('if renamed, reverts the rename before deleting', () => {
|
||||||
|
const testEntry = {
|
||||||
|
path: 'test',
|
||||||
|
name: 'test',
|
||||||
|
prevPath: 'lorem/ipsum',
|
||||||
|
prevName: 'ipsum',
|
||||||
|
prevParentPath: 'lorem',
|
||||||
|
};
|
||||||
|
|
||||||
|
store.state.entries = { test: testEntry };
|
||||||
|
testAction(
|
||||||
|
deleteEntry,
|
||||||
|
testEntry.path,
|
||||||
|
store.state,
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'renameEntry',
|
||||||
|
payload: {
|
||||||
|
path: testEntry.path,
|
||||||
|
name: testEntry.prevName,
|
||||||
|
parentPath: testEntry.prevParentPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'deleteEntry',
|
||||||
|
payload: testEntry.prevPath,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('renameEntry', () => {
|
describe('renameEntry', () => {
|
||||||
it('renames entry', done => {
|
describe('purging of file model cache', () => {
|
||||||
store.state.entries.test = {
|
beforeEach(() => {
|
||||||
tree: [],
|
spyOn(eventHub, '$emit');
|
||||||
};
|
});
|
||||||
|
|
||||||
testAction(
|
it('does not purge model cache for temporary entries that got renamed', done => {
|
||||||
renameEntry,
|
Object.assign(store.state.entries, {
|
||||||
{ path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
|
test: {
|
||||||
store.state,
|
...file('test'),
|
||||||
[
|
key: 'foo-key',
|
||||||
{
|
type: 'blob',
|
||||||
type: types.RENAME_ENTRY,
|
tempFile: true,
|
||||||
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
type: types.TOGGLE_FILE_CHANGED,
|
|
||||||
payload: {
|
store
|
||||||
file: store.state.entries['parent-path/new-name'],
|
.dispatch('renameEntry', {
|
||||||
changed: true,
|
path: 'test',
|
||||||
},
|
name: 'new',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
expect(eventHub.$emit.calls.allArgs()).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,
|
||||||
},
|
},
|
||||||
],
|
});
|
||||||
[{ type: 'triggerFilesChange' }],
|
|
||||||
done,
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renames all entries in tree', done => {
|
describe('single entry', () => {
|
||||||
store.state.entries.test = {
|
let origEntry;
|
||||||
type: 'tree',
|
let renamedEntry;
|
||||||
tree: [
|
|
||||||
{
|
|
||||||
path: 'tree-1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'tree-2',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
testAction(
|
beforeEach(() => {
|
||||||
renameEntry,
|
// Need to insert both because `testAction` doesn't actually call the mutation
|
||||||
{ path: 'test', name: 'new-name', parentPath: 'parent-path' },
|
origEntry = file('orig', 'orig', 'blob');
|
||||||
store.state,
|
renamedEntry = {
|
||||||
[
|
...file('renamed', 'renamed', 'blob'),
|
||||||
{
|
prevKey: origEntry.key,
|
||||||
type: types.RENAME_ENTRY,
|
prevName: origEntry.name,
|
||||||
payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
|
prevPath: origEntry.path,
|
||||||
},
|
};
|
||||||
],
|
|
||||||
[
|
Object.assign(store.state.entries, {
|
||||||
{
|
orig: origEntry,
|
||||||
type: 'renameEntry',
|
renamed: renamedEntry,
|
||||||
payload: {
|
});
|
||||||
path: 'test',
|
});
|
||||||
name: 'new-name',
|
|
||||||
entryPath: 'tree-1',
|
afterEach(() => {
|
||||||
parentPath: 'parent-path/new-name',
|
resetStore(store);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('by default renames an entry and adds to changed', done => {
|
||||||
|
testAction(
|
||||||
|
renameEntry,
|
||||||
|
{ path: 'orig', name: 'renamed' },
|
||||||
|
store.state,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: types.RENAME_ENTRY,
|
||||||
|
payload: {
|
||||||
|
path: 'orig',
|
||||||
|
name: 'renamed',
|
||||||
|
parentPath: undefined,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
type: types.ADD_FILE_TO_CHANGED,
|
||||||
type: 'renameEntry',
|
payload: 'renamed',
|
||||||
payload: {
|
|
||||||
path: 'test',
|
|
||||||
name: 'new-name',
|
|
||||||
entryPath: 'tree-2',
|
|
||||||
parentPath: 'parent-path/new-name',
|
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
[{ type: 'triggerFilesChange' }],
|
||||||
|
done,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('if not changed, completely unstages 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,
|
||||||
|
[jasmine.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.calls.count()).toBe(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,
|
||||||
},
|
},
|
||||||
{ type: 'triggerFilesChange' },
|
});
|
||||||
],
|
|
||||||
done,
|
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(
|
||||||
|
jasmine.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(
|
||||||
|
jasmine.objectContaining({
|
||||||
|
path: 'old-folder/test',
|
||||||
|
name: 'test',
|
||||||
|
prevPath: undefined,
|
||||||
|
prevName: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import rootActions from '~/ide/stores/actions';
|
import rootActions from '~/ide/stores/actions';
|
||||||
import store from '~/ide/stores';
|
import { createStore } from '~/ide/stores';
|
||||||
import service from '~/ide/services';
|
import service from '~/ide/services';
|
||||||
import router from '~/ide/ide_router';
|
import router from '~/ide/ide_router';
|
||||||
import eventHub from '~/ide/eventhub';
|
import eventHub from '~/ide/eventhub';
|
||||||
@ -11,6 +11,7 @@ import { resetStore, file } from 'spec/ide/helpers';
|
|||||||
import testAction from '../../../../helpers/vuex_action_helper';
|
import testAction from '../../../../helpers/vuex_action_helper';
|
||||||
|
|
||||||
const TEST_COMMIT_SHA = '123456789';
|
const TEST_COMMIT_SHA = '123456789';
|
||||||
|
const store = createStore();
|
||||||
|
|
||||||
describe('IDE commit module actions', () => {
|
describe('IDE commit module actions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -59,7 +60,9 @@ describe('IDE commit module actions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sets shouldCreateMR to true if "Create new MR" option is visible', done => {
|
it('sets shouldCreateMR to true if "Create new MR" option is visible', done => {
|
||||||
store.state.shouldHideNewMrOption = false;
|
Object.assign(store.state, {
|
||||||
|
shouldHideNewMrOption: false,
|
||||||
|
});
|
||||||
|
|
||||||
testAction(
|
testAction(
|
||||||
actions.updateCommitAction,
|
actions.updateCommitAction,
|
||||||
@ -78,7 +81,9 @@ describe('IDE commit module actions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sets shouldCreateMR to false if "Create new MR" option is hidden', done => {
|
it('sets shouldCreateMR to false if "Create new MR" option is hidden', done => {
|
||||||
store.state.shouldHideNewMrOption = true;
|
Object.assign(store.state, {
|
||||||
|
shouldHideNewMrOption: true,
|
||||||
|
});
|
||||||
|
|
||||||
testAction(
|
testAction(
|
||||||
actions.updateCommitAction,
|
actions.updateCommitAction,
|
||||||
@ -172,24 +177,31 @@ describe('IDE commit module actions', () => {
|
|||||||
content: 'file content',
|
content: 'file content',
|
||||||
});
|
});
|
||||||
|
|
||||||
store.state.currentProjectId = 'abcproject';
|
Object.assign(store.state, {
|
||||||
store.state.currentBranchId = 'master';
|
currentProjectId: 'abcproject',
|
||||||
store.state.projects.abcproject = {
|
currentBranchId: 'master',
|
||||||
web_url: 'web_url',
|
projects: {
|
||||||
branches: {
|
abcproject: {
|
||||||
master: {
|
web_url: 'web_url',
|
||||||
workingReference: '',
|
branches: {
|
||||||
commit: {
|
master: {
|
||||||
short_id: TEST_COMMIT_SHA,
|
workingReference: '',
|
||||||
|
commit: {
|
||||||
|
short_id: TEST_COMMIT_SHA,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
stagedFiles: [
|
||||||
store.state.stagedFiles.push(f, {
|
f,
|
||||||
...file('changedFile2'),
|
{
|
||||||
changed: true,
|
...file('changedFile2'),
|
||||||
|
changed: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
openFiles: store.state.stagedFiles,
|
||||||
});
|
});
|
||||||
store.state.openFiles = store.state.stagedFiles;
|
|
||||||
|
|
||||||
store.state.stagedFiles.forEach(stagedFile => {
|
store.state.stagedFiles.forEach(stagedFile => {
|
||||||
store.state.entries[stagedFile.path] = stagedFile;
|
store.state.entries[stagedFile.path] = stagedFile;
|
||||||
@ -275,40 +287,40 @@ describe('IDE commit module actions', () => {
|
|||||||
|
|
||||||
document.body.innerHTML += '<div class="flash-container"></div>';
|
document.body.innerHTML += '<div class="flash-container"></div>';
|
||||||
|
|
||||||
store.state.currentProjectId = 'abcproject';
|
|
||||||
store.state.currentBranchId = 'master';
|
|
||||||
store.state.projects.abcproject = {
|
|
||||||
web_url: 'webUrl',
|
|
||||||
branches: {
|
|
||||||
master: {
|
|
||||||
workingReference: '1',
|
|
||||||
commit: {
|
|
||||||
id: TEST_COMMIT_SHA,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const f = {
|
const f = {
|
||||||
...file('changed'),
|
...file('changed'),
|
||||||
type: 'blob',
|
type: 'blob',
|
||||||
active: true,
|
active: true,
|
||||||
lastCommitSha: TEST_COMMIT_SHA,
|
lastCommitSha: TEST_COMMIT_SHA,
|
||||||
};
|
};
|
||||||
store.state.stagedFiles.push(f);
|
|
||||||
store.state.changedFiles = [
|
|
||||||
{
|
|
||||||
...f,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
store.state.openFiles = store.state.changedFiles;
|
|
||||||
|
|
||||||
store.state.openFiles.forEach(localF => {
|
Object.assign(store.state, {
|
||||||
store.state.entries[localF.path] = localF;
|
stagedFiles: [f],
|
||||||
|
changedFiles: [f],
|
||||||
|
openFiles: [f],
|
||||||
|
currentProjectId: 'abcproject',
|
||||||
|
currentBranchId: 'master',
|
||||||
|
projects: {
|
||||||
|
abcproject: {
|
||||||
|
web_url: 'webUrl',
|
||||||
|
branches: {
|
||||||
|
master: {
|
||||||
|
workingReference: '1',
|
||||||
|
commit: {
|
||||||
|
id: TEST_COMMIT_SHA,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
store.state.commit.commitAction = '2';
|
store.state.commit.commitAction = '2';
|
||||||
store.state.commit.commitMessage = 'testing 123';
|
store.state.commit.commitMessage = 'testing 123';
|
||||||
|
|
||||||
|
store.state.openFiles.forEach(localF => {
|
||||||
|
store.state.entries[localF.path] = localF;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -356,16 +356,16 @@ describe('IDE store file mutations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('STAGE_CHANGE', () => {
|
describe('STAGE_CHANGE', () => {
|
||||||
it('adds file into stagedFiles array', () => {
|
beforeEach(() => {
|
||||||
mutations.STAGE_CHANGE(localState, localFile.path);
|
mutations.STAGE_CHANGE(localState, localFile.path);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds file into stagedFiles array', () => {
|
||||||
expect(localState.stagedFiles.length).toBe(1);
|
expect(localState.stagedFiles.length).toBe(1);
|
||||||
expect(localState.stagedFiles[0]).toEqual(localFile);
|
expect(localState.stagedFiles[0]).toEqual(localFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates stagedFile if it is already staged', () => {
|
it('updates stagedFile if it is already staged', () => {
|
||||||
mutations.STAGE_CHANGE(localState, localFile.path);
|
|
||||||
|
|
||||||
localFile.raw = 'testing 123';
|
localFile.raw = 'testing 123';
|
||||||
|
|
||||||
mutations.STAGE_CHANGE(localState, localFile.path);
|
mutations.STAGE_CHANGE(localState, localFile.path);
|
||||||
@ -373,19 +373,6 @@ describe('IDE store file mutations', () => {
|
|||||||
expect(localState.stagedFiles.length).toBe(1);
|
expect(localState.stagedFiles.length).toBe(1);
|
||||||
expect(localState.stagedFiles[0].raw).toEqual('testing 123');
|
expect(localState.stagedFiles[0].raw).toEqual('testing 123');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds already-staged file to `replacedFiles`', () => {
|
|
||||||
localFile.raw = 'already-staged';
|
|
||||||
|
|
||||||
mutations.STAGE_CHANGE(localState, localFile.path);
|
|
||||||
|
|
||||||
localFile.raw = 'testing 123';
|
|
||||||
|
|
||||||
mutations.STAGE_CHANGE(localState, localFile.path);
|
|
||||||
|
|
||||||
expect(localState.replacedFiles.length).toBe(1);
|
|
||||||
expect(localState.replacedFiles[0].raw).toEqual('already-staged');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('UNSTAGE_CHANGE', () => {
|
describe('UNSTAGE_CHANGE', () => {
|
||||||
|
@ -79,16 +79,6 @@ describe('Multi-file store mutations', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('CLEAR_REPLACED_FILES', () => {
|
|
||||||
it('clears replacedFiles array', () => {
|
|
||||||
localState.replacedFiles.push('a');
|
|
||||||
|
|
||||||
mutations.CLEAR_REPLACED_FILES(localState);
|
|
||||||
|
|
||||||
expect(localState.replacedFiles.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('UPDATE_VIEWER', () => {
|
describe('UPDATE_VIEWER', () => {
|
||||||
it('sets viewer state', () => {
|
it('sets viewer state', () => {
|
||||||
mutations.UPDATE_VIEWER(localState, 'diff');
|
mutations.UPDATE_VIEWER(localState, 'diff');
|
||||||
@ -311,8 +301,7 @@ describe('Multi-file store mutations', () => {
|
|||||||
describe('UPDATE_FILE_AFTER_COMMIT', () => {
|
describe('UPDATE_FILE_AFTER_COMMIT', () => {
|
||||||
it('updates URLs if prevPath is set', () => {
|
it('updates URLs if prevPath is set', () => {
|
||||||
const f = {
|
const f = {
|
||||||
...file(),
|
...file('test'),
|
||||||
path: 'test',
|
|
||||||
prevPath: 'testing-123',
|
prevPath: 'testing-123',
|
||||||
rawPath: `${gl.TEST_HOST}/testing-123`,
|
rawPath: `${gl.TEST_HOST}/testing-123`,
|
||||||
permalink: `${gl.TEST_HOST}/testing-123`,
|
permalink: `${gl.TEST_HOST}/testing-123`,
|
||||||
@ -325,19 +314,26 @@ describe('Multi-file store mutations', () => {
|
|||||||
|
|
||||||
mutations.UPDATE_FILE_AFTER_COMMIT(localState, { file: f, lastCommit: { commit: {} } });
|
mutations.UPDATE_FILE_AFTER_COMMIT(localState, { file: f, lastCommit: { commit: {} } });
|
||||||
|
|
||||||
expect(f.rawPath).toBe(`${gl.TEST_HOST}/test`);
|
expect(f).toEqual(
|
||||||
expect(f.permalink).toBe(`${gl.TEST_HOST}/test`);
|
jasmine.objectContaining({
|
||||||
expect(f.commitsPath).toBe(`${gl.TEST_HOST}/test`);
|
rawPath: `${gl.TEST_HOST}/test`,
|
||||||
expect(f.blamePath).toBe(`${gl.TEST_HOST}/test`);
|
permalink: `${gl.TEST_HOST}/test`,
|
||||||
expect(f.replaces).toBe(false);
|
commitsPath: `${gl.TEST_HOST}/test`,
|
||||||
|
blamePath: `${gl.TEST_HOST}/test`,
|
||||||
|
replaces: false,
|
||||||
|
prevId: undefined,
|
||||||
|
prevPath: undefined,
|
||||||
|
prevName: undefined,
|
||||||
|
prevUrl: undefined,
|
||||||
|
prevKey: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('OPEN_NEW_ENTRY_MODAL', () => {
|
describe('OPEN_NEW_ENTRY_MODAL', () => {
|
||||||
it('sets entryModal', () => {
|
it('sets entryModal', () => {
|
||||||
localState.entries.testPath = {
|
localState.entries.testPath = file();
|
||||||
...file(),
|
|
||||||
};
|
|
||||||
|
|
||||||
mutations.OPEN_NEW_ENTRY_MODAL(localState, { type: 'test', path: 'testPath' });
|
mutations.OPEN_NEW_ENTRY_MODAL(localState, { type: 'test', path: 'testPath' });
|
||||||
|
|
||||||
@ -356,16 +352,119 @@ describe('Multi-file store mutations', () => {
|
|||||||
};
|
};
|
||||||
localState.currentProjectId = 'gitlab-ce';
|
localState.currentProjectId = 'gitlab-ce';
|
||||||
localState.currentBranchId = 'master';
|
localState.currentBranchId = 'master';
|
||||||
localState.entries.oldPath = {
|
localState.entries = {
|
||||||
...file(),
|
oldPath: file('oldPath', 'oldPath', 'blob'),
|
||||||
type: 'blob',
|
|
||||||
name: 'oldPath',
|
|
||||||
path: 'oldPath',
|
|
||||||
url: `${gl.TEST_HOST}/oldPath`,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates new renamed entry', () => {
|
it('updates existing entry without creating a new one', () => {
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'oldPath',
|
||||||
|
name: 'newPath',
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localState.entries).toEqual({
|
||||||
|
newPath: jasmine.objectContaining({
|
||||||
|
path: 'newPath',
|
||||||
|
prevPath: 'oldPath',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly handles consecutive renames for the same entry', () => {
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'oldPath',
|
||||||
|
name: 'newPath',
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'newPath',
|
||||||
|
name: 'newestPath',
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localState.entries).toEqual({
|
||||||
|
newestPath: jasmine.objectContaining({
|
||||||
|
path: 'newestPath',
|
||||||
|
prevPath: 'oldPath',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly handles the same entry within a consecutively renamed folder', () => {
|
||||||
|
const oldPath = file('root-folder/oldPath', 'root-folder/oldPath', 'blob');
|
||||||
|
localState.entries = {
|
||||||
|
'root-folder': {
|
||||||
|
...file('root-folder', 'root-folder', 'tree'),
|
||||||
|
tree: [oldPath],
|
||||||
|
},
|
||||||
|
'root-folder/oldPath': oldPath,
|
||||||
|
};
|
||||||
|
Object.assign(localState.entries['root-folder/oldPath'], {
|
||||||
|
parentPath: 'root-folder',
|
||||||
|
url: 'root-folder/oldPath-blob-root-folder/oldPath',
|
||||||
|
});
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'root-folder/oldPath',
|
||||||
|
name: 'renamed-folder/oldPath',
|
||||||
|
entryPath: null,
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'renamed-folder/oldPath',
|
||||||
|
name: 'simply-renamed/oldPath',
|
||||||
|
entryPath: null,
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localState.entries).toEqual({
|
||||||
|
'root-folder': jasmine.objectContaining({
|
||||||
|
path: 'root-folder',
|
||||||
|
}),
|
||||||
|
'simply-renamed/oldPath': jasmine.objectContaining({
|
||||||
|
path: 'simply-renamed/oldPath',
|
||||||
|
prevPath: 'root-folder/oldPath',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renames entry, preserving old parameters', () => {
|
||||||
|
Object.assign(localState.entries.oldPath, {
|
||||||
|
url: `project/-/oldPath`,
|
||||||
|
});
|
||||||
|
const oldPathData = localState.entries.oldPath;
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'oldPath',
|
||||||
|
name: 'newPath',
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localState.entries.newPath).toEqual({
|
||||||
|
...oldPathData,
|
||||||
|
id: 'newPath',
|
||||||
|
path: 'newPath',
|
||||||
|
name: 'newPath',
|
||||||
|
url: `project/-/newPath`,
|
||||||
|
key: jasmine.stringMatching('newPath'),
|
||||||
|
|
||||||
|
prevId: 'oldPath',
|
||||||
|
prevName: 'oldPath',
|
||||||
|
prevPath: 'oldPath',
|
||||||
|
prevUrl: `project/-/oldPath`,
|
||||||
|
prevKey: oldPathData.key,
|
||||||
|
prevParentPath: oldPathData.parentPath,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not store previous attributes on temp files', () => {
|
||||||
|
Object.assign(localState.entries.oldPath, {
|
||||||
|
tempFile: true,
|
||||||
|
});
|
||||||
mutations.RENAME_ENTRY(localState, {
|
mutations.RENAME_ENTRY(localState, {
|
||||||
path: 'oldPath',
|
path: 'oldPath',
|
||||||
name: 'newPath',
|
name: 'newPath',
|
||||||
@ -373,41 +472,58 @@ describe('Multi-file store mutations', () => {
|
|||||||
parentPath: '',
|
parentPath: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(localState.entries.newPath).toEqual({
|
expect(localState.entries.newPath).not.toEqual(
|
||||||
...localState.entries.oldPath,
|
jasmine.objectContaining({
|
||||||
id: 'newPath',
|
prevId: jasmine.anything(),
|
||||||
name: 'newPath',
|
prevName: jasmine.anything(),
|
||||||
key: 'newPath-blob-oldPath',
|
prevPath: jasmine.anything(),
|
||||||
path: 'newPath',
|
prevUrl: jasmine.anything(),
|
||||||
tempFile: true,
|
prevKey: jasmine.anything(),
|
||||||
prevPath: 'oldPath',
|
prevParentPath: jasmine.anything(),
|
||||||
tree: [],
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('properly handles files with spaces in name', () => {
|
||||||
|
const path = 'my fancy path';
|
||||||
|
const newPath = 'new path';
|
||||||
|
const oldEntry = {
|
||||||
|
...file(path, path, 'blob'),
|
||||||
|
url: `project/-/${encodeURI(path)}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
localState.entries[path] = oldEntry;
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path,
|
||||||
|
name: newPath,
|
||||||
|
entryPath: null,
|
||||||
parentPath: '',
|
parentPath: '',
|
||||||
url: `${gl.TEST_HOST}/newPath`,
|
});
|
||||||
moved: jasmine.anything(),
|
|
||||||
movedPath: jasmine.anything(),
|
expect(localState.entries[newPath]).toEqual({
|
||||||
opened: false,
|
...oldEntry,
|
||||||
|
id: newPath,
|
||||||
|
path: newPath,
|
||||||
|
name: newPath,
|
||||||
|
url: `project/-/new%20path`,
|
||||||
|
key: jasmine.stringMatching(newPath),
|
||||||
|
|
||||||
|
prevId: path,
|
||||||
|
prevName: path,
|
||||||
|
prevPath: path,
|
||||||
|
prevUrl: `project/-/my%20fancy%20path`,
|
||||||
|
prevKey: oldEntry.key,
|
||||||
|
prevParentPath: oldEntry.parentPath,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds new entry to changedFiles', () => {
|
it('adds to parent tree', () => {
|
||||||
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
const parentEntry = {
|
||||||
|
...file('parentPath', 'parentPath', 'tree'),
|
||||||
expect(localState.changedFiles.length).toBe(1);
|
tree: [localState.entries.oldPath],
|
||||||
expect(localState.changedFiles[0].path).toBe('newPath');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets oldEntry as moved', () => {
|
|
||||||
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
|
||||||
|
|
||||||
expect(localState.entries.oldPath.moved).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('adds to parents tree', () => {
|
|
||||||
localState.entries.oldPath.parentPath = 'parentPath';
|
|
||||||
localState.entries.parentPath = {
|
|
||||||
...file(),
|
|
||||||
};
|
};
|
||||||
|
localState.entries.parentPath = parentEntry;
|
||||||
|
|
||||||
mutations.RENAME_ENTRY(localState, {
|
mutations.RENAME_ENTRY(localState, {
|
||||||
path: 'oldPath',
|
path: 'oldPath',
|
||||||
@ -416,7 +532,180 @@ describe('Multi-file store mutations', () => {
|
|||||||
parentPath: 'parentPath',
|
parentPath: 'parentPath',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(localState.entries.parentPath.tree.length).toBe(1);
|
expect(parentEntry.tree.length).toBe(1);
|
||||||
|
expect(parentEntry.tree[0].name).toBe('newPath');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sorts tree after renaming an entry', () => {
|
||||||
|
const alpha = file('alpha', 'alpha', 'blob');
|
||||||
|
const beta = file('beta', 'beta', 'blob');
|
||||||
|
const gamma = file('gamma', 'gamma', 'blob');
|
||||||
|
localState.entries = { alpha, beta, gamma };
|
||||||
|
|
||||||
|
localState.trees['gitlab-ce/master'].tree = [alpha, beta, gamma];
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'alpha',
|
||||||
|
name: 'theta',
|
||||||
|
entryPath: null,
|
||||||
|
parentPath: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localState.trees['gitlab-ce/master'].tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'beta' }),
|
||||||
|
jasmine.objectContaining({ name: 'gamma' }),
|
||||||
|
jasmine.objectContaining({
|
||||||
|
path: 'theta',
|
||||||
|
name: 'theta',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates openFiles with the renamed one if the original one is open', () => {
|
||||||
|
Object.assign(localState.entries.oldPath, {
|
||||||
|
opened: true,
|
||||||
|
type: 'blob',
|
||||||
|
});
|
||||||
|
Object.assign(localState, {
|
||||||
|
openFiles: [localState.entries.oldPath],
|
||||||
|
});
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
||||||
|
|
||||||
|
expect(localState.openFiles.length).toBe(1);
|
||||||
|
expect(localState.openFiles[0].path).toBe('newPath');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add renamed entry to changedFiles', () => {
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
||||||
|
|
||||||
|
expect(localState.changedFiles.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates existing changedFiles entry with the renamed one', () => {
|
||||||
|
const origFile = {
|
||||||
|
...file('oldPath', 'oldPath', 'blob'),
|
||||||
|
content: 'Foo',
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(localState, {
|
||||||
|
changedFiles: [origFile],
|
||||||
|
});
|
||||||
|
Object.assign(localState.entries, {
|
||||||
|
oldPath: origFile,
|
||||||
|
});
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
||||||
|
|
||||||
|
expect(localState.changedFiles).toEqual([
|
||||||
|
jasmine.objectContaining({
|
||||||
|
path: 'newPath',
|
||||||
|
content: 'Foo',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly saves original values if an entry is renamed multiple times', () => {
|
||||||
|
const original = { ...localState.entries.oldPath };
|
||||||
|
const paramsToCheck = ['prevId', 'prevPath', 'prevName', 'prevUrl'];
|
||||||
|
const expectedObj = paramsToCheck.reduce(
|
||||||
|
(o, param) => ({ ...o, [param]: original[param.replace('prev', '').toLowerCase()] }),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
|
||||||
|
|
||||||
|
expect(localState.entries.newPath).toEqual(jasmine.objectContaining(expectedObj));
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'newPath', name: 'newer' });
|
||||||
|
|
||||||
|
expect(localState.entries.newer).toEqual(jasmine.objectContaining(expectedObj));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('renaming back to original', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const renamedEntry = {
|
||||||
|
...file('renamed', 'renamed', 'blob'),
|
||||||
|
prevId: 'lorem/orig',
|
||||||
|
prevPath: 'lorem/orig',
|
||||||
|
prevName: 'orig',
|
||||||
|
prevUrl: 'project/-/loren/orig',
|
||||||
|
prevKey: 'lorem/orig',
|
||||||
|
prevParentPath: 'lorem',
|
||||||
|
};
|
||||||
|
|
||||||
|
localState.entries = {
|
||||||
|
renamed: renamedEntry,
|
||||||
|
};
|
||||||
|
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'renamed', name: 'orig', parentPath: 'lorem' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renames entry and clears prev properties', () => {
|
||||||
|
expect(localState.entries).toEqual({
|
||||||
|
'lorem/orig': jasmine.objectContaining({
|
||||||
|
id: 'lorem/orig',
|
||||||
|
path: 'lorem/orig',
|
||||||
|
name: 'orig',
|
||||||
|
prevId: undefined,
|
||||||
|
prevPath: undefined,
|
||||||
|
prevName: undefined,
|
||||||
|
prevUrl: undefined,
|
||||||
|
prevKey: undefined,
|
||||||
|
prevParentPath: undefined,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('key updates', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const rootFolder = file('rootFolder', 'rootFolder', 'tree');
|
||||||
|
localState.entries = {
|
||||||
|
rootFolder,
|
||||||
|
oldPath: file('oldPath', 'oldPath', 'blob'),
|
||||||
|
'oldPath.txt': file('oldPath.txt', 'oldPath.txt', 'blob'),
|
||||||
|
'rootFolder/oldPath.md': file('oldPath.md', 'oldPath.md', 'blob', rootFolder),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets properly constucted key while preserving the original one', () => {
|
||||||
|
const key = 'oldPath.txt-blob-oldPath.txt';
|
||||||
|
localState.entries['oldPath.txt'].key = key;
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath.txt', name: 'newPath.md' });
|
||||||
|
|
||||||
|
expect(localState.entries['newPath.md'].key).toBe('newPath.md-blob-newPath.md');
|
||||||
|
expect(localState.entries['newPath.md'].prevKey).toBe(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly updates key for an entry without an extension', () => {
|
||||||
|
localState.entries.oldPath.key = 'oldPath-blob-oldPath';
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath.md' });
|
||||||
|
|
||||||
|
expect(localState.entries['newPath.md'].key).toBe('newPath.md-blob-newPath.md');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly updates key when new name does not have an extension', () => {
|
||||||
|
localState.entries['oldPath.txt'].key = 'oldPath.txt-blob-oldPath.txt';
|
||||||
|
mutations.RENAME_ENTRY(localState, { path: 'oldPath.txt', name: 'newPath' });
|
||||||
|
|
||||||
|
expect(localState.entries.newPath.key).toBe('newPath-blob-newPath');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly updates key when renaming an entry in a folder', () => {
|
||||||
|
localState.entries['rootFolder/oldPath.md'].key =
|
||||||
|
'rootFolder/oldPath.md-blob-rootFolder/oldPath.md';
|
||||||
|
mutations.RENAME_ENTRY(localState, {
|
||||||
|
path: 'rootFolder/oldPath.md',
|
||||||
|
name: 'newPath.md',
|
||||||
|
entryPath: null,
|
||||||
|
parentPath: 'rootFolder',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(localState.entries['rootFolder/newPath.md'].key).toBe(
|
||||||
|
'rootFolder/newPath.md-blob-rootFolder/newPath.md',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -237,31 +237,6 @@ describe('Multi-file store utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getCommitFiles', () => {
|
describe('getCommitFiles', () => {
|
||||||
it('returns list of files excluding moved files', () => {
|
|
||||||
const files = [
|
|
||||||
{
|
|
||||||
path: 'a',
|
|
||||||
type: 'blob',
|
|
||||||
deleted: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'c',
|
|
||||||
type: 'blob',
|
|
||||||
moved: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const flattendFiles = utils.getCommitFiles(files);
|
|
||||||
|
|
||||||
expect(flattendFiles).toEqual([
|
|
||||||
{
|
|
||||||
path: 'a',
|
|
||||||
type: 'blob',
|
|
||||||
deleted: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('filters out folders from the list', () => {
|
it('filters out folders from the list', () => {
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
@ -422,4 +397,204 @@ describe('Multi-file store utils', () => {
|
|||||||
expect(res[1].tree[0].opened).toEqual(true);
|
expect(res[1].tree[0].opened).toEqual(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('escapeFileUrl', () => {
|
||||||
|
it('encodes URL excluding the slashes', () => {
|
||||||
|
expect(utils.escapeFileUrl('/foo-bar/file.md')).toBe('/foo-bar/file.md');
|
||||||
|
expect(utils.escapeFileUrl('foo bar/file.md')).toBe('foo%20bar/file.md');
|
||||||
|
expect(utils.escapeFileUrl('foo/bar/file.md')).toBe('foo/bar/file.md');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapInStateArray', () => {
|
||||||
|
let localState;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localState = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('swaps existing entry with a new one', () => {
|
||||||
|
const file1 = {
|
||||||
|
...file('old'),
|
||||||
|
key: 'foo',
|
||||||
|
};
|
||||||
|
const file2 = file('new');
|
||||||
|
const arr = [file1];
|
||||||
|
|
||||||
|
Object.assign(localState, {
|
||||||
|
dummyArray: arr,
|
||||||
|
entries: {
|
||||||
|
new: file2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.swapInStateArray(localState, 'dummyArray', 'foo', 'new');
|
||||||
|
|
||||||
|
expect(localState.dummyArray.length).toBe(1);
|
||||||
|
expect(localState.dummyArray[0]).toBe(file2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add an item if it does not exist yet in array', () => {
|
||||||
|
const file1 = file('file');
|
||||||
|
Object.assign(localState, {
|
||||||
|
dummyArray: [],
|
||||||
|
entries: {
|
||||||
|
file: file1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
utils.swapInStateArray(localState, 'dummyArray', 'foo', 'file');
|
||||||
|
|
||||||
|
expect(localState.dummyArray.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapInParentTreeWithSorting', () => {
|
||||||
|
let localState;
|
||||||
|
let branchInfo;
|
||||||
|
const currentProjectId = '123-foo';
|
||||||
|
const currentBranchId = 'master';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localState = {
|
||||||
|
currentBranchId,
|
||||||
|
currentProjectId,
|
||||||
|
trees: {
|
||||||
|
[`${currentProjectId}/${currentBranchId}`]: {
|
||||||
|
tree: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
entries: {
|
||||||
|
oldPath: file('oldPath', 'oldPath', 'blob'),
|
||||||
|
newPath: file('newPath', 'newPath', 'blob'),
|
||||||
|
parentPath: file('parentPath', 'parentPath', 'tree'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
branchInfo = localState.trees[`${currentProjectId}/${currentBranchId}`];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not change tree if newPath is not supplied', () => {
|
||||||
|
branchInfo.tree = [localState.entries.oldPath];
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, 'oldPath', undefined, undefined);
|
||||||
|
|
||||||
|
expect(branchInfo.tree).toEqual([localState.entries.oldPath]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('oldPath to replace is not defined: simple addition to tree', () => {
|
||||||
|
it('adds to tree on the state if there is no parent for the entry', () => {
|
||||||
|
expect(branchInfo.tree.length).toBe(0);
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, undefined, 'oldPath', undefined);
|
||||||
|
|
||||||
|
expect(branchInfo.tree.length).toBe(1);
|
||||||
|
expect(branchInfo.tree[0].name).toBe('oldPath');
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, undefined, 'newPath', undefined);
|
||||||
|
|
||||||
|
expect(branchInfo.tree.length).toBe(2);
|
||||||
|
expect(branchInfo.tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'newPath' }),
|
||||||
|
jasmine.objectContaining({ name: 'oldPath' }),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds to parent tree if it is supplied', () => {
|
||||||
|
utils.swapInParentTreeWithSorting(localState, undefined, 'newPath', 'parentPath');
|
||||||
|
|
||||||
|
expect(localState.entries.parentPath.tree.length).toBe(1);
|
||||||
|
expect(localState.entries.parentPath.tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'newPath' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
localState.entries.parentPath.tree = [localState.entries.oldPath];
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, undefined, 'newPath', 'parentPath');
|
||||||
|
|
||||||
|
expect(localState.entries.parentPath.tree.length).toBe(2);
|
||||||
|
expect(localState.entries.parentPath.tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'newPath' }),
|
||||||
|
jasmine.objectContaining({ name: 'oldPath' }),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('swapping of the items', () => {
|
||||||
|
it('swaps entries if both paths are supplied', () => {
|
||||||
|
branchInfo.tree = [localState.entries.oldPath];
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, localState.entries.oldPath.key, 'newPath');
|
||||||
|
|
||||||
|
expect(branchInfo.tree).toEqual([jasmine.objectContaining({ name: 'newPath' })]);
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, localState.entries.newPath.key, 'oldPath');
|
||||||
|
|
||||||
|
expect(branchInfo.tree).toEqual([jasmine.objectContaining({ name: 'oldPath' })]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sorts tree after swapping the entries', () => {
|
||||||
|
const alpha = file('alpha', 'alpha', 'blob');
|
||||||
|
const beta = file('beta', 'beta', 'blob');
|
||||||
|
const gamma = file('gamma', 'gamma', 'blob');
|
||||||
|
const theta = file('theta', 'theta', 'blob');
|
||||||
|
localState.entries = { alpha, beta, gamma, theta };
|
||||||
|
|
||||||
|
branchInfo.tree = [alpha, beta, gamma];
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, alpha.key, 'theta');
|
||||||
|
|
||||||
|
expect(branchInfo.tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'beta' }),
|
||||||
|
jasmine.objectContaining({ name: 'gamma' }),
|
||||||
|
jasmine.objectContaining({ name: 'theta' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, gamma.key, 'alpha');
|
||||||
|
|
||||||
|
expect(branchInfo.tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'alpha' }),
|
||||||
|
jasmine.objectContaining({ name: 'beta' }),
|
||||||
|
jasmine.objectContaining({ name: 'theta' }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
utils.swapInParentTreeWithSorting(localState, beta.key, 'gamma');
|
||||||
|
|
||||||
|
expect(branchInfo.tree).toEqual([
|
||||||
|
jasmine.objectContaining({ name: 'alpha' }),
|
||||||
|
jasmine.objectContaining({ name: 'gamma' }),
|
||||||
|
jasmine.objectContaining({ name: 'theta' }),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cleanTrailingSlash', () => {
|
||||||
|
[
|
||||||
|
{ input: '', output: '' },
|
||||||
|
{ input: 'abc', output: 'abc' },
|
||||||
|
{ input: 'abc/', output: 'abc' },
|
||||||
|
{ input: 'abc/def', output: 'abc/def' },
|
||||||
|
{ input: 'abc/def/', output: 'abc/def' },
|
||||||
|
].forEach(({ input, output }) => {
|
||||||
|
it(`cleans trailing slash from string "${input}"`, () => {
|
||||||
|
expect(utils.cleanTrailingSlash(input)).toEqual(output);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('pathsAreEqual', () => {
|
||||||
|
[
|
||||||
|
{ args: ['abc', 'abc'], output: true },
|
||||||
|
{ args: ['abc', 'def'], output: false },
|
||||||
|
{ args: ['abc/', 'abc'], output: true },
|
||||||
|
{ args: ['abc/abc', 'abc'], output: false },
|
||||||
|
{ args: ['/', ''], output: true },
|
||||||
|
{ args: ['', '/'], output: true },
|
||||||
|
{ args: [false, '/'], output: true },
|
||||||
|
].forEach(({ args, output }) => {
|
||||||
|
it(`cleans and tests equality (${JSON.stringify(args)})`, () => {
|
||||||
|
expect(utils.pathsAreEqual(...args)).toEqual(output);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -62,7 +62,7 @@ describe('LazyLoader', function() {
|
|||||||
waitForAttributeChange(newImg, ['data-src', 'src']),
|
waitForAttributeChange(newImg, ['data-src', 'src']),
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).toHaveBeenCalled();
|
expect(LazyLoader.loadImage).toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg.getAttribute('src')).toBe(testPath);
|
expect(newImg.getAttribute('src')).toBe(testPath);
|
||||||
expect(newImg).toHaveClass('js-lazy-loaded');
|
expect(newImg).toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
@ -79,7 +79,7 @@ describe('LazyLoader', function() {
|
|||||||
scrollIntoViewPromise(newImg)
|
scrollIntoViewPromise(newImg)
|
||||||
.then(waitForPromises)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
|
expect(LazyLoader.loadImage).not.toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
@ -98,7 +98,7 @@ describe('LazyLoader', function() {
|
|||||||
scrollIntoViewPromise(newImg)
|
scrollIntoViewPromise(newImg)
|
||||||
.then(waitForPromises)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
|
expect(LazyLoader.loadImage).not.toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
@ -121,7 +121,7 @@ describe('LazyLoader', function() {
|
|||||||
])
|
])
|
||||||
.then(waitForPromises)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).toHaveBeenCalled();
|
expect(LazyLoader.loadImage).toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg).toHaveClass('js-lazy-loaded');
|
expect(newImg).toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
@ -156,7 +156,7 @@ describe('LazyLoader', function() {
|
|||||||
|
|
||||||
Promise.all([scrollIntoViewPromise(img), waitForAttributeChange(img, ['data-src', 'src'])])
|
Promise.all([scrollIntoViewPromise(img), waitForAttributeChange(img, ['data-src', 'src'])])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).toHaveBeenCalled();
|
expect(LazyLoader.loadImage).toHaveBeenCalledWith(img);
|
||||||
expect(img.getAttribute('src')).toBe(originalDataSrc);
|
expect(img.getAttribute('src')).toBe(originalDataSrc);
|
||||||
expect(img).toHaveClass('js-lazy-loaded');
|
expect(img).toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
@ -176,7 +176,7 @@ describe('LazyLoader', function() {
|
|||||||
waitForAttributeChange(newImg, ['data-src', 'src']),
|
waitForAttributeChange(newImg, ['data-src', 'src']),
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).toHaveBeenCalled();
|
expect(LazyLoader.loadImage).toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg.getAttribute('src')).toBe(testPath);
|
expect(newImg.getAttribute('src')).toBe(testPath);
|
||||||
expect(newImg).toHaveClass('js-lazy-loaded');
|
expect(newImg).toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
@ -193,7 +193,7 @@ describe('LazyLoader', function() {
|
|||||||
scrollIntoViewPromise(newImg)
|
scrollIntoViewPromise(newImg)
|
||||||
.then(waitForPromises)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
|
expect(LazyLoader.loadImage).not.toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
@ -212,7 +212,7 @@ describe('LazyLoader', function() {
|
|||||||
scrollIntoViewPromise(newImg)
|
scrollIntoViewPromise(newImg)
|
||||||
.then(waitForPromises)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).not.toHaveBeenCalled();
|
expect(LazyLoader.loadImage).not.toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
expect(newImg).not.toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
@ -234,7 +234,7 @@ describe('LazyLoader', function() {
|
|||||||
waitForAttributeChange(newImg, ['data-src', 'src']),
|
waitForAttributeChange(newImg, ['data-src', 'src']),
|
||||||
])
|
])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(LazyLoader.loadImage).toHaveBeenCalled();
|
expect(LazyLoader.loadImage).toHaveBeenCalledWith(newImg);
|
||||||
expect(newImg).toHaveClass('js-lazy-loaded');
|
expect(newImg).toHaveClass('js-lazy-loaded');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
|
@ -90,19 +90,6 @@ describe('File row component', () => {
|
|||||||
expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null);
|
expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is not rendered for `moved` entries in subfolders', () => {
|
|
||||||
createComponent({
|
|
||||||
file: {
|
|
||||||
path: 't5',
|
|
||||||
moved: true,
|
|
||||||
tree: [],
|
|
||||||
},
|
|
||||||
level: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(vm.$el.nodeType).not.toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('new dropdown', () => {
|
describe('new dropdown', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createComponent({
|
createComponent({
|
||||||
|
@ -150,6 +150,7 @@ describe Gitlab do
|
|||||||
|
|
||||||
describe '.ee?' do
|
describe '.ee?' do
|
||||||
before do
|
before do
|
||||||
|
stub_env('IS_GITLAB_EE', nil) # Make sure the ENV is clean
|
||||||
described_class.instance_variable_set(:@is_ee, nil)
|
described_class.instance_variable_set(:@is_ee, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user