Commit e02a6804 authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'ee-ide-close-changed-files' into 'master'

Added changed state to IDE

Closes #4540

See merge request gitlab-org/gitlab-ee!4429
parents 7a8194bc 5b2574d0
<script> <script>
import { mapActions } from 'vuex';
import icon from '../../../vue_shared/components/icon.vue'; import icon from '../../../vue_shared/components/icon.vue';
export default { export default {
...@@ -19,6 +20,11 @@ ...@@ -19,6 +20,11 @@
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`; return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
}, },
}, },
methods: {
...mapActions([
'discardFileChanges',
]),
},
}; };
</script> </script>
...@@ -32,5 +38,12 @@ ...@@ -32,5 +38,12 @@
<span class="multi-file-commit-list-path"> <span class="multi-file-commit-list-path">
{{ file.path }} {{ file.path }}
</span> </span>
<button
type="button"
class="btn btn-blank multi-file-discard-btn"
@click="discardFileChanges(file)"
>
Discard
</button>
</div> </div>
</template> </template>
...@@ -36,9 +36,9 @@ ...@@ -36,9 +36,9 @@
...mapState([ ...mapState([
'currentBlobView', 'currentBlobView',
'selectedFile', 'selectedFile',
'changedFiles',
]), ]),
...mapGetters([ ...mapGetters([
'changedFiles',
'activeFile', 'activeFile',
]), ]),
}, },
......
<script> <script>
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import repoCommitSection from './repo_commit_section.vue'; import repoCommitSection from './repo_commit_section.vue';
import icon from '../../vue_shared/components/icon.vue'; import icon from '../../vue_shared/components/icon.vue';
import panelResizer from '../../vue_shared/components/panel_resizer.vue'; import panelResizer from '../../vue_shared/components/panel_resizer.vue';
...@@ -28,8 +28,6 @@ ...@@ -28,8 +28,6 @@
computed: { computed: {
...mapState([ ...mapState([
'rightPanelCollapsed', 'rightPanelCollapsed',
]),
...mapGetters([
'changedFiles', 'changedFiles',
]), ]),
currentIcon() { currentIcon() {
......
<script> <script>
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue'; import icon from '../../vue_shared/components/icon.vue';
import modal from '../../vue_shared/components/modal.vue'; import modal from '../../vue_shared/components/modal.vue';
...@@ -38,8 +38,6 @@ export default { ...@@ -38,8 +38,6 @@ export default {
'currentBranchId', 'currentBranchId',
'rightPanelCollapsed', 'rightPanelCollapsed',
'lastCommitMsg', 'lastCommitMsg',
]),
...mapGetters([
'changedFiles', 'changedFiles',
]), ]),
commitButtonDisabled() { commitButtonDisabled() {
......
...@@ -9,7 +9,6 @@ export default { ...@@ -9,7 +9,6 @@ export default {
computed: { computed: {
...mapState([ ...mapState([
'editMode', 'editMode',
'discardPopupOpen',
]), ]),
...mapGetters([ ...mapGetters([
'canEditFile', 'canEditFile',
...@@ -21,7 +20,6 @@ export default { ...@@ -21,7 +20,6 @@ export default {
methods: { methods: {
...mapActions([ ...mapActions([
'toggleEditMode', 'toggleEditMode',
'closeDiscardPopup',
]), ]),
}, },
}; };
...@@ -43,15 +41,5 @@ export default { ...@@ -43,15 +41,5 @@ export default {
{{ buttonLabel }} {{ buttonLabel }}
</span> </span>
</button> </button>
<modal
v-if="discardPopupOpen"
class="text-left"
:primary-button-label="__('Discard changes')"
kind="warning"
:title="__('Are you sure?')"
:text="__('Are you sure you want to discard your changes?')"
@cancel="closeDiscardPopup"
@submit="toggleEditMode(true)"
/>
</div> </div>
</template> </template>
...@@ -19,6 +19,9 @@ export default { ...@@ -19,6 +19,9 @@ export default {
shouldHideEditor() { shouldHideEditor() {
return this.activeFile && this.activeFile.binary && !this.activeFile.raw; return this.activeFile && this.activeFile.binary && !this.activeFile.raw;
}, },
activeFileChanged() {
return this.activeFile && this.activeFile.changed;
},
}, },
watch: { watch: {
activeFile(oldVal, newVal) { activeFile(oldVal, newVal) {
...@@ -26,6 +29,13 @@ export default { ...@@ -26,6 +29,13 @@ export default {
this.initMonaco(); this.initMonaco();
} }
}, },
activeFileChanged(newVal) {
if (!this.editor) return;
if (!newVal && this.model) {
this.model.setValue(this.model.getOriginalModel().getValue());
}
},
leftPanelCollapsed() { leftPanelCollapsed() {
this.editor.updateDimensions(); this.editor.updateDimensions();
}, },
...@@ -78,11 +88,11 @@ export default { ...@@ -78,11 +88,11 @@ export default {
setupEditor() { setupEditor() {
if (!this.activeFile) return; if (!this.activeFile) return;
const model = this.editor.createModel(this.activeFile); this.model = this.editor.createModel(this.activeFile);
this.editor.attachModel(model); this.editor.attachModel(this.model);
model.onChange((m) => { this.model.onChange((m) => {
this.changeFileContent({ this.changeFileContent({
file: this.activeFile, file: this.activeFile,
content: m.getValue(), content: m.getValue(),
...@@ -104,12 +114,12 @@ export default { ...@@ -104,12 +114,12 @@ export default {
// Handle File Language // Handle File Language
this.setFileLanguage({ this.setFileLanguage({
fileLanguage: model.language, fileLanguage: this.model.language,
}); });
// Get File eol // Get File eol
this.setFileEOL({ this.setFileEOL({
eol: model.eol, eol: this.model.eol,
}); });
}, },
}, },
......
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import fileStatusIcon from './repo_file_status_icon.vue'; import fileStatusIcon from './repo_file_status_icon.vue';
import fileIcon from '../../vue_shared/components/file_icon.vue'; import fileIcon from '../../vue_shared/components/file_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
export default { export default {
components: { components: {
fileStatusIcon, fileStatusIcon,
fileIcon, fileIcon,
icon,
}, },
props: { props: {
tab: { tab: {
...@@ -14,6 +16,11 @@ ...@@ -14,6 +16,11 @@
required: true, required: true,
}, },
}, },
data() {
return {
tabMouseOver: false,
};
},
computed: { computed: {
closeLabel() { closeLabel() {
if (this.tab.changed || this.tab.tempFile) { if (this.tab.changed || this.tab.tempFile) {
...@@ -21,12 +28,14 @@ ...@@ -21,12 +28,14 @@
} }
return `Close ${this.tab.name}`; return `Close ${this.tab.name}`;
}, },
changedClass() { showChangedIcon() {
const tabChangedObj = { return this.tab.changed ? !this.tabMouseOver : false;
'fa-times close-icon': !this.tab.changed && !this.tab.tempFile, },
'fa-circle unsaved-icon': this.tab.changed || this.tab.tempFile, changedIcon() {
}; return this.tab.tempFile ? 'file-addition' : 'file-modified';
return tabChangedObj; },
changedIconClass() {
return this.tab.tempFile ? 'multi-file-addition' : 'multi-file-modified';
}, },
}, },
...@@ -37,28 +46,43 @@ ...@@ -37,28 +46,43 @@
clickFile(tab) { clickFile(tab) {
this.$router.push(`/project${tab.url}`); this.$router.push(`/project${tab.url}`);
}, },
mouseOverTab() {
if (this.tab.changed) {
this.tabMouseOver = true;
}
},
mouseOutTab() {
if (this.tab.changed) {
this.tabMouseOver = false;
}
},
}, },
}; };
</script> </script>
<template> <template>
<li @click="clickFile(tab)"> <li
@click="clickFile(tab)"
@mouseover="mouseOverTab"
@mouseout="mouseOutTab"
>
<button <button
type="button" type="button"
class="multi-file-tab-close" class="multi-file-tab-close"
@click.stop.prevent="closeFile({ file: tab })" @click.stop.prevent="closeFile(tab)"
:aria-label="closeLabel" :aria-label="closeLabel"
:class="{
'modified': tab.changed,
}"
:disabled="tab.changed"
> >
<i <icon
class="fa" v-if="!showChangedIcon"
:class="changedClass" name="close"
aria-hidden="true" :size="12"
> />
</i> <icon
v-else
:name="changedIcon"
:size="12"
:css-classes="changedIconClass"
/>
</button> </button>
<div <div
......
...@@ -48,6 +48,10 @@ export default class Model { ...@@ -48,6 +48,10 @@ export default class Model {
return this.originalModel; return this.originalModel;
} }
setValue(value) {
this.getModel().setValue(value);
}
onChange(cb) { onChange(cb) {
this.events.set( this.events.set(
this.path, this.path,
......
...@@ -10,42 +10,25 @@ export const redirectToUrl = (_, url) => visitUrl(url); ...@@ -10,42 +10,25 @@ export const redirectToUrl = (_, url) => visitUrl(url);
export const setInitialData = ({ commit }, data) => export const setInitialData = ({ commit }, data) =>
commit(types.SET_INITIAL_DATA, data); commit(types.SET_INITIAL_DATA, data);
export const closeDiscardPopup = ({ commit }) => export const discardAllChanges = ({ state, commit, dispatch }) => {
commit(types.TOGGLE_DISCARD_POPUP, false); state.changedFiles.forEach((file) => {
export const discardAllChanges = ({ commit, getters, dispatch }) => {
const changedFiles = getters.changedFiles;
changedFiles.forEach((file) => {
commit(types.DISCARD_FILE_CHANGES, file); commit(types.DISCARD_FILE_CHANGES, file);
if (file.tempFile) { if (file.tempFile) {
dispatch('closeFile', { file, force: true }); dispatch('closeFile', file);
} }
}); });
commit(types.REMOVE_ALL_CHANGES_FILES);
}; };
export const closeAllFiles = ({ state, dispatch }) => { export const closeAllFiles = ({ state, dispatch }) => {
state.openFiles.forEach(file => dispatch('closeFile', { file })); state.openFiles.forEach(file => dispatch('closeFile', file));
}; };
export const toggleEditMode = ( export const toggleEditMode = ({ commit, dispatch }) => {
{ state, commit, getters, dispatch }, commit(types.TOGGLE_EDIT_MODE);
force = false, dispatch('toggleBlobView');
) => {
const changedFiles = getters.changedFiles;
if (changedFiles.length && !force) {
commit(types.TOGGLE_DISCARD_POPUP, true);
} else {
commit(types.TOGGLE_EDIT_MODE);
commit(types.TOGGLE_DISCARD_POPUP, false);
dispatch('toggleBlobView');
if (!state.editMode) {
dispatch('discardAllChanges');
}
}
}; };
export const toggleBlobView = ({ commit, state }) => { export const toggleBlobView = ({ commit, state }) => {
...@@ -85,7 +68,7 @@ export const checkCommitStatus = ({ state }) => ...@@ -85,7 +68,7 @@ export const checkCommitStatus = ({ state }) =>
.catch(() => flash('Error checking branch data. Please try again.', 'alert', document, null, false, true)); .catch(() => flash('Error checking branch data. Please try again.', 'alert', document, null, false, true));
export const commitChanges = ( export const commitChanges = (
{ commit, state, dispatch, getters }, { commit, state, dispatch },
{ payload, newMr }, { payload, newMr },
) => ) =>
service service
...@@ -125,7 +108,7 @@ export const commitChanges = ( ...@@ -125,7 +108,7 @@ export const commitChanges = (
reference: data.id, reference: data.id,
}); });
getters.changedFiles.forEach((entry) => { state.changedFiles.forEach((entry) => {
commit(types.SET_LAST_COMMIT_DATA, { commit(types.SET_LAST_COMMIT_DATA, {
entry, entry,
lastCommit, lastCommit,
......
...@@ -10,9 +10,7 @@ import { ...@@ -10,9 +10,7 @@ import {
findIndexOfFile, findIndexOfFile,
} from '../utils'; } from '../utils';
export const closeFile = ({ commit, state, dispatch }, { file, force = false }) => { export const closeFile = ({ commit, state, dispatch }, file) => {
if ((file.changed || file.tempFile) && !force) return;
const indexOfClosedFile = findIndexOfFile(state.openFiles, file); const indexOfClosedFile = findIndexOfFile(state.openFiles, file);
const fileWasActive = file.active; const fileWasActive = file.active;
...@@ -79,8 +77,16 @@ export const getRawFileData = ({ commit, dispatch }, file) => service.getRawFile ...@@ -79,8 +77,16 @@ export const getRawFileData = ({ commit, dispatch }, file) => service.getRawFile
}) })
.catch(() => flash('Error loading file content. Please try again.', 'alert', document, null, false, true)); .catch(() => flash('Error loading file content. Please try again.', 'alert', document, null, false, true));
export const changeFileContent = ({ commit }, { file, content }) => { export const changeFileContent = ({ state, commit }, { file, content }) => {
commit(types.UPDATE_FILE_CONTENT, { file, content }); commit(types.UPDATE_FILE_CONTENT, { file, content });
const indexOfChangedFile = findIndexOfFile(state.changedFiles, file);
if (file.changed && indexOfChangedFile === -1) {
commit(types.ADD_FILE_TO_CHANGED, file);
} else if (!file.changed && indexOfChangedFile !== -1) {
commit(types.REMOVE_FILE_FROM_CHANGED, file);
}
}; };
export const setFileLanguage = ({ state, commit }, { fileLanguage }) => { export const setFileLanguage = ({ state, commit }, { fileLanguage }) => {
...@@ -125,6 +131,7 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI ...@@ -125,6 +131,7 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI
file, file,
}); });
commit(types.TOGGLE_FILE_OPEN, file); commit(types.TOGGLE_FILE_OPEN, file);
commit(types.ADD_FILE_TO_CHANGED, file);
dispatch('setFileActive', file); dispatch('setFileActive', file);
if (!state.editMode && !file.base64) { if (!state.editMode && !file.base64) {
...@@ -135,3 +142,12 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI ...@@ -135,3 +142,12 @@ export const createTempFile = ({ state, commit, dispatch }, { projectId, branchI
return Promise.resolve(file); return Promise.resolve(file);
}; };
export const discardFileChanges = ({ commit }, file) => {
commit(types.DISCARD_FILE_CHANGES, file);
commit(types.REMOVE_FILE_FROM_CHANGED, file);
if (file.tempFile && file.opened) {
commit(types.TOGGLE_FILE_OPEN, file);
}
};
export const changedFiles = state => state.openFiles.filter(file => file.changed);
export const activeFile = state => state.openFiles.find(file => file.active) || null; export const activeFile = state => state.openFiles.find(file => file.active) || null;
export const activeFileExtension = (state) => { export const activeFileExtension = (state) => {
...@@ -14,6 +12,6 @@ export const canEditFile = (state) => { ...@@ -14,6 +12,6 @@ export const canEditFile = (state) => {
(currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary);
}; };
export const addedFiles = state => changedFiles(state).filter(f => f.tempFile); export const addedFiles = state => state.changedFiles.filter(f => f.tempFile);
export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile); export const modifiedFiles = state => state.changedFiles.filter(f => !f.tempFile);
...@@ -24,6 +24,7 @@ export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; ...@@ -24,6 +24,7 @@ export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN';
export const CREATE_TMP_TREE = 'CREATE_TMP_TREE'; export const CREATE_TMP_TREE = 'CREATE_TMP_TREE';
export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL'; export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL';
export const CREATE_TREE = 'CREATE_TREE'; export const CREATE_TREE = 'CREATE_TREE';
export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES';
// File mutation types // File mutation types
export const SET_FILE_DATA = 'SET_FILE_DATA'; export const SET_FILE_DATA = 'SET_FILE_DATA';
...@@ -36,12 +37,12 @@ export const SET_FILE_POSITION = 'SET_FILE_POSITION'; ...@@ -36,12 +37,12 @@ export const SET_FILE_POSITION = 'SET_FILE_POSITION';
export const SET_FILE_EOL = 'SET_FILE_EOL'; export const SET_FILE_EOL = 'SET_FILE_EOL';
export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES'; export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES';
export const CREATE_TMP_FILE = 'CREATE_TMP_FILE'; export const CREATE_TMP_FILE = 'CREATE_TMP_FILE';
export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED';
export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED';
// Viewer mutation types // Viewer mutation types
export const SET_PREVIEW_MODE = 'SET_PREVIEW_MODE'; export const SET_PREVIEW_MODE = 'SET_PREVIEW_MODE';
export const SET_EDIT_MODE = 'SET_EDIT_MODE'; export const SET_EDIT_MODE = 'SET_EDIT_MODE';
export const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE'; export const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE';
export const TOGGLE_DISCARD_POPUP = 'TOGGLE_DISCARD_POPUP';
export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH'; export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH';
...@@ -28,11 +28,6 @@ export default { ...@@ -28,11 +28,6 @@ export default {
editMode: !state.editMode, editMode: !state.editMode,
}); });
}, },
[types.TOGGLE_DISCARD_POPUP](state, discardPopupOpen) {
Object.assign(state, {
discardPopupOpen,
});
},
[types.SET_ROOT](state, isRoot) { [types.SET_ROOT](state, isRoot) {
Object.assign(state, { Object.assign(state, {
isRoot, isRoot,
......
...@@ -71,4 +71,12 @@ export default { ...@@ -71,4 +71,12 @@ export default {
[types.CREATE_TMP_FILE](state, { file, parent }) { [types.CREATE_TMP_FILE](state, { file, parent }) {
parent.tree.push(file); parent.tree.push(file);
}, },
[types.ADD_FILE_TO_CHANGED](state, file) {
state.changedFiles.push(file);
},
[types.REMOVE_FILE_FROM_CHANGED](state, file) {
const indexOfChangedFile = findIndexOfFile(state.changedFiles, file);
state.changedFiles.splice(indexOfChangedFile, 1);
},
}; };
...@@ -33,4 +33,9 @@ export default { ...@@ -33,4 +33,9 @@ export default {
[types.CREATE_TMP_TREE](state, { parent, tmpEntry }) { [types.CREATE_TMP_TREE](state, { parent, tmpEntry }) {
parent.tree.push(tmpEntry); parent.tree.push(tmpEntry);
}, },
[types.REMOVE_ALL_CHANGES_FILES](state) {
Object.assign(state, {
changedFiles: [],
});
},
}; };
...@@ -3,7 +3,7 @@ export default () => ({ ...@@ -3,7 +3,7 @@ export default () => ({
currentProjectId: '', currentProjectId: '',
currentBranchId: '', currentBranchId: '',
currentBlobView: 'repo-editor', currentBlobView: 'repo-editor',
discardPopupOpen: false, changedFiles: [],
editMode: true, editMode: true,
endpoints: {}, endpoints: {},
isRoot: false, isRoot: false,
......
...@@ -172,20 +172,32 @@ table.table tr td.multi-file-table-name { ...@@ -172,20 +172,32 @@ table.table tr td.multi-file-table-name {
position: absolute; position: absolute;
right: 8px; right: 8px;
top: 50%; top: 50%;
width: 16px;
height: 16px;
padding: 0; padding: 0;
background: none; background: none;
border: 0; border: 0;
font-size: $gl-font-size; border-radius: $border-radius-default;
color: $gray-darkest; color: $theme-gray-900;
transform: translateY(-50%); transform: translateY(-50%);
&:not(.modified):hover, svg {
&:not(.modified):focus { position: relative;
color: $hint-color; top: -1px;
} }
&.modified { &:hover {
color: $indigo-700; background-color: $theme-gray-200;
}
&:focus {
background-color: $blue-500;
color: $white-light;
outline: 0;
svg {
fill: currentColor;
}
} }
} }
...@@ -412,7 +424,24 @@ table.table tr td.multi-file-table-name { ...@@ -412,7 +424,24 @@ table.table tr td.multi-file-table-name {
.multi-file-commit-list-item { .multi-file-commit-list-item {
display: flex; display: flex;
margin-bottom: $grid-size;
align-items: center; align-items: center;
> svg {
min-width: 16px;
}
.multi-file-discard-btn {
display: none;
margin-left: auto;
color: $gl-link-color;
}
&:hover {
.multi-file-discard-btn {
display: block;
}
}
} }
.multi-file-addition { .multi-file-addition {
......
...@@ -12,13 +12,8 @@ describe('Multi-file editor commit sidebar list collapsed', () => { ...@@ -12,13 +12,8 @@ describe('Multi-file editor commit sidebar list collapsed', () => {
vm = createComponentWithStore(Component, store); vm = createComponentWithStore(Component, store);
vm.$store.state.openFiles.push(file('file1'), file('file2')); vm.$store.state.changedFiles.push(file('file1'), file('file2'));
vm.$store.state.openFiles[0].tempFile = true; vm.$store.state.changedFiles[0].tempFile = true;
vm.$store.state.openFiles.forEach((f) => {
Object.assign(f, {
changed: true,
});
});
vm.$mount(); vm.$mount();
}); });
......
...@@ -25,6 +25,14 @@ describe('Multi-file editor commit sidebar list item', () => { ...@@ -25,6 +25,14 @@ describe('Multi-file editor commit sidebar list item', () => {
expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path); expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
}); });
it('calls discardFileChanges when clicking discard button', () => {
spyOn(vm, 'discardFileChanges');
vm.$el.querySelector('.multi-file-discard-btn').click();
expect(vm.discardFileChanges).toHaveBeenCalled();
});
describe('computed', () => { describe('computed', () => {
describe('iconName', () => { describe('iconName', () => {
it('returns modified when not a tempFile', () => { it('returns modified when not a tempFile', () => {
......
...@@ -31,8 +31,8 @@ describe('RepoCommitSection', () => { ...@@ -31,8 +31,8 @@ describe('RepoCommitSection', () => {
vm.$store.state.rightPanelCollapsed = false; vm.$store.state.rightPanelCollapsed = false;
vm.$store.state.currentBranch = 'master'; vm.$store.state.currentBranch = 'master';
vm.$store.state.openFiles = [file('file1'), file('file2')]; vm.$store.state.changedFiles = [file('file1'), file('file2')];
vm.$store.state.openFiles.forEach(f => Object.assign(f, { vm.$store.state.changedFiles.forEach(f => Object.assign(f, {
changed: true, changed: true,
content: 'testing', content: 'testing',
})); }));
...@@ -91,7 +91,7 @@ describe('RepoCommitSection', () => { ...@@ -91,7 +91,7 @@ describe('RepoCommitSection', () => {
expect(changedFileElements.length).toEqual(2); expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => { changedFileElements.forEach((changedFile, i) => {
expect(changedFile.textContent.trim()).toEqual(vm.$store.getters.changedFiles[i].path); expect(changedFile.textContent.trim()).toContain(vm.$store.state.changedFiles[i].path);
}); });
expect(submitCommit.disabled).toBeTruthy(); expect(submitCommit.disabled).toBeTruthy();
...@@ -103,7 +103,7 @@ describe('RepoCommitSection', () => { ...@@ -103,7 +103,7 @@ describe('RepoCommitSection', () => {
beforeEach(() => { beforeEach(() => {
vm.commitMessage = 'testing'; vm.commitMessage = 'testing';
changedFiles = JSON.parse(JSON.stringify(vm.$store.getters.changedFiles)); changedFiles = JSON.parse(JSON.stringify(vm.$store.state.changedFiles));
spyOn(service, 'commit').and.returnValue(Promise.resolve({ spyOn(service, 'commit').and.returnValue(Promise.resolve({
data: { data: {
......
...@@ -55,29 +55,4 @@ describe('RepoEditButton', () => { ...@@ -55,29 +55,4 @@ describe('RepoEditButton', () => {
done(); done();
}); });
}); });
describe('discardPopupOpen', () => {
beforeEach(() => {
vm.$store.state.discardPopupOpen = true;
vm.$store.state.editMode = true;
vm.$store.state.openFiles[0].changed = true;
vm.$mount();
});
it('renders popup', () => {
expect(vm.$el.querySelector('.modal')).not.toBeNull();
});
it('removes all changed files', (done) => {
vm.$el.querySelector('.btn-warning').click();
vm.$nextTick(() => {
expect(vm.$store.getters.changedFiles.length).toBe(0);
expect(vm.$el.querySelector('.modal')).toBeNull();
done();
});
});
});
}); });
...@@ -57,4 +57,18 @@ describe('RepoEditor', () => { ...@@ -57,4 +57,18 @@ describe('RepoEditor', () => {
expect(vm.$el.textContent).toContain('testing'); expect(vm.$el.textContent).toContain('testing');
}); });
}); });
describe('computed', () => {
describe('activeFileChanged', () => {
it('returns false when file has no changes', () => {
expect(vm.activeFileChanged).toBeFalsy();
});
it('returns true when file has changes', () => {
vm.$store.getters.activeFile.changed = true;
expect(vm.activeFileChanged).toBeTruthy();
});
});
});
}); });
...@@ -27,7 +27,7 @@ describe('RepoTab', () => { ...@@ -27,7 +27,7 @@ describe('RepoTab', () => {
const close = vm.$el.querySelector('.multi-file-tab-close'); const close = vm.$el.querySelector('.multi-file-tab-close');
const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`); const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`);
expect(close.querySelector('.fa-times')).toBeTruthy(); expect(close.innerHTML).toContain('#close');
expect(name.textContent.trim()).toEqual(vm.tab.name); expect(name.textContent.trim()).toEqual(vm.tab.name);
}); });
...@@ -52,17 +52,41 @@ describe('RepoTab', () => { ...@@ -52,17 +52,41 @@ describe('RepoTab', () => {
vm.$el.querySelector('.multi-file-tab-close').click(); vm.$el.querySelector('.multi-file-tab-close').click();
expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab }); expect(vm.closeFile).toHaveBeenCalledWith(vm.tab);
}); });
it('renders an fa-circle icon if tab is changed', () => { it('shows changed icon if tab is changed', () => {
const tab = file('changedFile'); const tab = file('changedFile');
tab.changed = true; tab.changed = true;
vm = createComponent({ vm = createComponent({
tab, tab,
}); });
expect(vm.$el.querySelector('.multi-file-tab-close .fa-circle')).not.toBeNull(); expect(vm.changedIcon).toBe('file-modified');
});
it('changes icon on hover', (done) => {
const tab = file();
tab.changed = true;
vm = createComponent({
tab,
});
vm.$el.dispatchEvent(new Event('mouseover'));
Vue.nextTick()
.then(() => {
expect(vm.$el.querySelector('.multi-file-modified')).toBeNull();
vm.$el.dispatchEvent(new Event('mouseout'));
})
.then(Vue.nextTick)
.then(() => {
expect(vm.$el.querySelector('.multi-file-modified')).not.toBeNull();
done();
})
.catch(done.fail);
}); });
describe('locked file', () => { describe('locked file', () => {
...@@ -97,20 +121,22 @@ describe('RepoTab', () => { ...@@ -97,20 +121,22 @@ describe('RepoTab', () => {
describe('methods', () => { describe('methods', () => {
describe('closeTab', () => { describe('closeTab', () => {
it('does not close tab if is changed', (done) => { it('closes tab if file has changed', (done) => {
const tab = file('closeFile'); const tab = file();
tab.changed = true; tab.changed = true;
tab.opened = true; tab.opened = true;
vm = createComponent({ vm = createComponent({
tab, tab,
}); });
vm.$store.state.openFiles.push(tab); vm.$store.state.openFiles.push(tab);
vm.$store.state.changedFiles.push(tab);
vm.$store.dispatch('setFileActive', tab); vm.$store.dispatch('setFileActive', tab);
vm.$el.querySelector('.multi-file-tab-close').click(); vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => { vm.$nextTick(() => {
expect(tab.opened).toBeTruthy(); expect(tab.opened).toBeFalsy();
expect(vm.$store.state.changedFiles.length).toBe(1);
done(); done();
}); });
......
...@@ -41,6 +41,14 @@ describe('Multi-file editor library model', () => { ...@@ -41,6 +41,14 @@ describe('Multi-file editor library model', () => {
}); });
}); });
describe('setValue', () => {
it('updates models value', () => {
model.setValue('testing 123');
expect(model.getModel().getValue()).toBe('testing 123');
});
});
describe('onChange', () => { describe('onChange', () => {
it('caches event by path', () => { it('caches event by path', () => {
model.onChange(() => {}); model.onChange(() => {});
......
...@@ -31,7 +31,7 @@ describe('Multi-file store file actions', () => { ...@@ -31,7 +31,7 @@ describe('Multi-file store file actions', () => {
}); });
it('closes open files', (done) => { it('closes open files', (done) => {
store.dispatch('closeFile', { file: localFile }) store.dispatch('closeFile', localFile)
.then(() => { .then(() => {
expect(localFile.opened).toBeFalsy(); expect(localFile.opened).toBeFalsy();
expect(localFile.active).toBeFalsy(); expect(localFile.active).toBeFalsy();
...@@ -41,43 +41,18 @@ describe('Multi-file store file actions', () => { ...@@ -41,43 +41,18 @@ describe('Multi-file store file actions', () => {
}).catch(done.fail); }).catch(done.fail);
}); });
it('does not close file if has changed', (done) => { it('closes file even if file has changes', (done) => {
localFile.changed = true; store.state.changedFiles.push(localFile);
store.dispatch('closeFile', { file: localFile }) store.dispatch('closeFile', localFile)
.then(() => { .then(Vue.nextTick)
expect(localFile.opened).toBeTruthy();
expect(localFile.active).toBeTruthy();
expect(store.state.openFiles.length).toBe(1);
done();
}).catch(done.fail);
});
it('does not close file if temp file', (done) => {
localFile.tempFile = true;
store.dispatch('closeFile', { file: localFile })
.then(() => {
expect(localFile.opened).toBeTruthy();
expect(localFile.active).toBeTruthy();
expect(store.state.openFiles.length).toBe(1);
done();
}).catch(done.fail);
});
it('force closes a changed file', (done) => {
localFile.changed = true;
store.dispatch('closeFile', { file: localFile, force: true })
.then(() => { .then(() => {
expect(localFile.opened).toBeFalsy();
expect(localFile.active).toBeFalsy();
expect(store.state.openFiles.length).toBe(0); expect(store.state.openFiles.length).toBe(0);
expect(store.state.changedFiles.length).toBe(1);
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('sets next file as active', (done) => { it('sets next file as active', (done) => {
...@@ -86,7 +61,7 @@ describe('Multi-file store file actions', () => { ...@@ -86,7 +61,7 @@ describe('Multi-file store file actions', () => {
expect(f.active).toBeFalsy(); expect(f.active).toBeFalsy();
store.dispatch('closeFile', { file: localFile }) store.dispatch('closeFile', localFile)
.then(() => { .then(() => {
expect(f.active).toBeTruthy(); expect(f.active).toBeTruthy();
...@@ -95,7 +70,7 @@ describe('Multi-file store file actions', () => { ...@@ -95,7 +70,7 @@ describe('Multi-file store file actions', () => {
}); });
it('calls getLastCommitData', (done) => { it('calls getLastCommitData', (done) => {
store.dispatch('closeFile', { file: localFile }) store.dispatch('closeFile', localFile)
.then(() => { .then(() => {
expect(getLastCommitDataSpy).toHaveBeenCalled(); expect(getLastCommitDataSpy).toHaveBeenCalled();
...@@ -194,7 +169,7 @@ describe('Multi-file store file actions', () => { ...@@ -194,7 +169,7 @@ describe('Multi-file store file actions', () => {
}), }),
})); }));
localFile = file('newCreate'); localFile = file(`newCreate-${Math.random()}`);
localFile.url = 'getFileDataURL'; localFile.url = 'getFileDataURL';
}); });
...@@ -315,6 +290,50 @@ describe('Multi-file store file actions', () => { ...@@ -315,6 +290,50 @@ describe('Multi-file store file actions', () => {
done(); done();
}).catch(done.fail); }).catch(done.fail);
}); });
it('adds file into changedFiles array', (done) => {
store.dispatch('changeFileContent', {
file: tmpFile,
content: 'content',
})
.then(() => {
expect(store.state.changedFiles.length).toBe(1);
done();
}).catch(done.fail);
});
it('adds file once into changedFiles array', (done) => {
store.dispatch('changeFileContent', {
file: tmpFile,
content: 'content',
})
.then(() => store.dispatch('changeFileContent', {
file: tmpFile,
content: 'content 123',
}))
.then(() => {
expect(store.state.changedFiles.length).toBe(1);
done();
}).catch(done.fail);
});
it('removes file from changedFiles array if not changed', (done) => {
store.dispatch('changeFileContent', {
file: tmpFile,
content: 'content',
})
.then(() => store.dispatch('changeFileContent', {
file: tmpFile,
content: '',
}))
.then(() => {
expect(store.state.changedFiles.length).toBe(0);
done();
}).catch(done.fail);
});
}); });
describe('createTempFile', () => { describe('createTempFile', () => {
...@@ -372,6 +391,20 @@ describe('Multi-file store file actions', () => { ...@@ -372,6 +391,20 @@ describe('Multi-file store file actions', () => {
}).catch(done.fail); }).catch(done.fail);
}); });
it('adds tmp file to changed files', (done) => {
store.dispatch('createTempFile', {
name: 'test',
projectId: 'abcproject',
branchId: 'mybranch',
parent: projectTree,
}).then((f) => {
expect(store.state.changedFiles.length).toBe(1);
expect(store.state.changedFiles[0].name).toBe(f.name);
done();
}).catch(done.fail);
});
it('sets tmp file as active', (done) => { it('sets tmp file as active', (done) => {
store.dispatch('createTempFile', { store.dispatch('createTempFile', {
name: 'test', name: 'test',
...@@ -428,4 +461,62 @@ describe('Multi-file store file actions', () => { ...@@ -428,4 +461,62 @@ describe('Multi-file store file actions', () => {
}).catch(done.fail); }).catch(done.fail);
}); });
}); });
describe('discardFileChanges', () => {
let tmpFile;
beforeEach(() => {
tmpFile = file();
tmpFile.content = 'testing';
store.state.changedFiles.push(tmpFile);
});
it('resets file content', (done) => {
store.dispatch('discardFileChanges', tmpFile)
.then(() => {
expect(tmpFile.content).not.toBe('testing');
done();
})
.catch(done.fail);
});
it('removes file from changedFiles array', (done) => {
store.dispatch('discardFileChanges', tmpFile)
.then(() => {
expect(store.state.changedFiles.length).toBe(0);
done();
})
.catch(done.fail);
});
it('closes temp file', (done) => {
tmpFile.tempFile = true;
tmpFile.opened = true;
store.dispatch('discardFileChanges', tmpFile)
.then(() => {
expect(tmpFile.opened).toBeFalsy();
done();
})
.catch(done.fail);
});
it('does not re-open a closed temp file', (done) => {
tmpFile.tempFile = true;
expect(tmpFile.opened).toBeFalsy();
store.dispatch('discardFileChanges', tmpFile)
.then(() => {
expect(tmpFile.opened).toBeFalsy();
done();
})
.catch(done.fail);
});
});
}); });
...@@ -34,22 +34,32 @@ describe('Multi-file store actions', () => { ...@@ -34,22 +34,32 @@ describe('Multi-file store actions', () => {
}); });
}); });
describe('closeDiscardPopup', () => { describe('discardAllChanges', () => {
it('closes the discard popup', (done) => { beforeEach(() => {
store.dispatch('closeDiscardPopup', false) const f = file('discardAll');
.then(() => { f.changed = true;
expect(store.state.discardPopupOpen).toBeFalsy();
done(); store.state.openFiles.push(f);
store.state.changedFiles.push(f);
});
it('discards changes in file', (done) => {
store.dispatch('discardAllChanges')
.then(() => {
expect(store.state.openFiles.changed).toBeFalsy();
}) })
.then(done)
.catch(done.fail); .catch(done.fail);
}); });
});
describe('discardAllChanges', () => { it('removes all files from changedFiles state', (done) => {
beforeEach(() => { store.dispatch('discardAllChanges')
store.state.openFiles.push(file('discardAll')); .then(() => {
store.state.openFiles[0].changed = true; expect(store.state.changedFiles.length).toBe(0);
expect(store.state.openFiles.length).toBe(1);
})
.then(done)
.catch(done.fail);
}); });
}); });
...@@ -94,49 +104,6 @@ describe('Multi-file store actions', () => { ...@@ -94,49 +104,6 @@ describe('Multi-file store actions', () => {
done(); done();
}).catch(done.fail); }).catch(done.fail);
}); });
it('opens discard popup if there are changed files', (done) => {
store.state.editMode = true;
store.state.openFiles.push(file('discardChanges'));
store.state.openFiles[0].changed = true;
store.dispatch('toggleEditMode')
.then(() => {
expect(store.state.discardPopupOpen).toBeTruthy();
done();
}).catch(done.fail);
});
it('can force closed if there are changed files', (done) => {
store.state.editMode = true;
store.state.openFiles.push(file('forceClose'));
store.state.openFiles[0].changed = true;
store.dispatch('toggleEditMode', true)
.then(() => {
expect(store.state.discardPopupOpen).toBeFalsy();
expect(store.state.editMode).toBeFalsy();
done();
}).catch(done.fail);
});
it('discards file changes', (done) => {
const f = file('discard');
store.state.editMode = true;
store.state.openFiles.push(f);
f.changed = true;
store.dispatch('toggleEditMode', true)
.then(Vue.nextTick)
.then(() => {
expect(f.changed).toBeFalsy();
done();
}).catch(done.fail);
});
}); });
describe('toggleBlobView', () => { describe('toggleBlobView', () => {
...@@ -290,16 +257,13 @@ describe('Multi-file store actions', () => { ...@@ -290,16 +257,13 @@ describe('Multi-file store actions', () => {
}); });
it('adds commit data to changed files', (done) => { it('adds commit data to changed files', (done) => {
const changedFile = file('changed'); const changedFile = file();
const f = file('newfile');
changedFile.changed = true;
store.state.openFiles.push(changedFile, f); store.state.changedFiles.push(changedFile);
store.dispatch('commitChanges', { payload, newMr: false }) store.dispatch('commitChanges', { payload, newMr: false })
.then(() => { .then(() => {
expect(changedFile.lastCommit.message).toBe('test message'); expect(changedFile.lastCommit.message).toBe('test message');
expect(f.lastCommit.message).not.toBe('test message');
done(); done();
}).catch(done.fail); }).catch(done.fail);
......
...@@ -9,19 +9,6 @@ describe('Multi-file store getters', () => { ...@@ -9,19 +9,6 @@ describe('Multi-file store getters', () => {
localState = state(); localState = state();
}); });
describe('changedFiles', () => {
it('returns a list of changed opened files', () => {
localState.openFiles.push(file());
localState.openFiles.push(file('changed'));
localState.openFiles[1].changed = true;
const changedFiles = getters.changedFiles(localState);
expect(changedFiles.length).toBe(1);
expect(changedFiles[0].name).toBe('changed');
});
});
describe('activeFile', () => { describe('activeFile', () => {
it('returns the current active file', () => { it('returns the current active file', () => {
localState.openFiles.push(file()); localState.openFiles.push(file());
...@@ -88,8 +75,8 @@ describe('Multi-file store getters', () => { ...@@ -88,8 +75,8 @@ describe('Multi-file store getters', () => {
describe('modifiedFiles', () => { describe('modifiedFiles', () => {
it('returns a list of modified files', () => { it('returns a list of modified files', () => {
localState.openFiles.push(file()); localState.openFiles.push(file());
localState.openFiles.push(file('changed')); localState.changedFiles.push(file('changed'));
localState.openFiles[1].changed = true; localState.changedFiles[0].changed = true;
const modifiedFiles = getters.modifiedFiles(localState); const modifiedFiles = getters.modifiedFiles(localState);
...@@ -101,9 +88,9 @@ describe('Multi-file store getters', () => { ...@@ -101,9 +88,9 @@ describe('Multi-file store getters', () => {
describe('addedFiles', () => { describe('addedFiles', () => {
it('returns a list of added files', () => { it('returns a list of added files', () => {
localState.openFiles.push(file()); localState.openFiles.push(file());
localState.openFiles.push(file('added')); localState.changedFiles.push(file('added'));
localState.openFiles[1].changed = true; localState.changedFiles[0].changed = true;
localState.openFiles[1].tempFile = true; localState.changedFiles[0].tempFile = true;
const modifiedFiles = getters.addedFiles(localState); const modifiedFiles = getters.addedFiles(localState);
......
...@@ -99,6 +99,17 @@ describe('Multi-file store file mutations', () => { ...@@ -99,6 +99,17 @@ describe('Multi-file store file mutations', () => {
expect(localFile.content).toBe('testing'); expect(localFile.content).toBe('testing');
expect(localFile.changed).toBeTruthy(); expect(localFile.changed).toBeTruthy();
}); });
it('sets changed if file is a temp file', () => {
localFile.tempFile = true;
mutations.UPDATE_FILE_CONTENT(localState, {
file: localFile,
content: '',
});
expect(localFile.changed).toBeTruthy();
});
}); });
describe('DISCARD_FILE_CHANGES', () => { describe('DISCARD_FILE_CHANGES', () => {
...@@ -128,4 +139,26 @@ describe('Multi-file store file mutations', () => { ...@@ -128,4 +139,26 @@ describe('Multi-file store file mutations', () => {
expect(localFile.tree[0].name).toBe(f.name); expect(localFile.tree[0].name).toBe(f.name);
}); });
}); });
describe('ADD_FILE_TO_CHANGED', () => {
it('adds file into changed files array', () => {
const f = file();
mutations.ADD_FILE_TO_CHANGED(localState, f);
expect(localState.changedFiles.length).toBe(1);
});
});
describe('REMOVE_FILE_FROM_CHANGED', () => {
it('removes files from changed files array', () => {
const f = file();
localState.changedFiles.push(f);
mutations.REMOVE_FILE_FROM_CHANGED(localState, f);
expect(localState.changedFiles.length).toBe(0);
});
});
}); });
...@@ -68,4 +68,14 @@ describe('Multi-file store tree mutations', () => { ...@@ -68,4 +68,14 @@ describe('Multi-file store tree mutations', () => {
expect(localTree.tree[0].name).toBe(tmpEntry.name); expect(localTree.tree[0].name).toBe(tmpEntry.name);
}); });
}); });
describe('REMOVE_ALL_CHANGES_FILES', () => {
it('removes all files from changedFiles state', () => {
localState.changedFiles.push(file('REMOVE_ALL_CHANGES_FILES'));
mutations.REMOVE_ALL_CHANGES_FILES(localState);
expect(localState.changedFiles.length).toBe(0);
});
});
}); });
...@@ -73,18 +73,6 @@ describe('Multi-file store mutations', () => { ...@@ -73,18 +73,6 @@ describe('Multi-file store mutations', () => {
}); });
}); });
describe('TOGGLE_DISCARD_POPUP', () => {
it('sets discardPopupOpen', () => {
mutations.TOGGLE_DISCARD_POPUP(localState, true);
expect(localState.discardPopupOpen).toBeTruthy();
mutations.TOGGLE_DISCARD_POPUP(localState, false);
expect(localState.discardPopupOpen).toBeFalsy();
});
});
describe('SET_ROOT', () => { describe('SET_ROOT', () => {
it('sets isRoot & initialRoot', () => { it('sets isRoot & initialRoot', () => {
mutations.SET_ROOT(localState, true); mutations.SET_ROOT(localState, true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment