Commit 5c25ea48 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'ide-sidebar-commit-box' into '44846-improve-web-ide-left-panel-and-modes'

Updated commit section in IDE sidebar

See merge request gitlab-org/gitlab-ce!18616
parents 9f2f8679 8a21c31b
......@@ -8,7 +8,7 @@ export default {
Icon,
},
computed: {
...mapGetters(['currentProject']),
...mapGetters(['currentProject', 'hasChanges']),
...mapState(['currentActivityView']),
goBackUrl() {
return document.referrer || this.currentProject.web_url;
......@@ -65,7 +65,7 @@ export default {
/>
</button>
</li>
<li>
<li v-show="hasChanges">
<button
type="button"
class="ide-sidebar-link js-ide-commit-mode"
......
<script>
import { mapState } from 'vuex';
import { mapActions, mapState } from 'vuex';
import { sprintf, __ } from '~/locale';
import * as consts from '../../stores/modules/commit/constants';
import RadioGroup from './radio_group.vue';
......@@ -9,7 +9,7 @@ export default {
RadioGroup,
},
computed: {
...mapState(['currentBranchId']),
...mapState(['currentBranchId', 'changedFiles', 'stagedFiles']),
commitToCurrentBranchText() {
return sprintf(
__('Commit to %{branchName} branch'),
......@@ -17,6 +17,17 @@ export default {
false,
);
},
disableMergeRequestRadio() {
return this.changedFiles.length > 0 && this.stagedFiles.length > 0;
},
},
mounted() {
if (this.disableMergeRequestRadio) {
this.updateCommitAction(consts.COMMIT_TO_CURRENT_BRANCH);
}
},
methods: {
...mapActions('commit', ['updateCommitAction']),
},
commitToCurrentBranch: consts.COMMIT_TO_CURRENT_BRANCH,
commitToNewBranch: consts.COMMIT_TO_NEW_BRANCH,
......@@ -44,6 +55,7 @@ export default {
:value="$options.commitToNewBranchMR"
:label="__('Create a new branch and merge request')"
:show-input="true"
:disabled="disableMergeRequestRadio"
/>
</div>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { sprintf, __ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CommitMessageField from './message_field.vue';
import Actions from './actions.vue';
import SuccessMessage from './success_message.vue';
import { activityBarViews, MAX_WINDOW_HEIGHT_COMPACT, COMMIT_ITEM_PADDING } from '../../constants';
export default {
components: {
Actions,
LoadingButton,
CommitMessageField,
SuccessMessage,
},
data() {
return {
isCompact: true,
componentHeight: null,
};
},
computed: {
...mapState(['changedFiles', 'stagedFiles', 'currentActivityView', 'lastCommitMsg']),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters(['hasChanges']),
...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
overviewText() {
return sprintf(
__(
'<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes',
),
{
stagedFilesLength: this.stagedFiles.length,
changedFilesLength: this.changedFiles.length,
},
);
},
},
watch: {
currentActivityView() {
if (this.lastCommitMsg) {
this.isCompact = false;
} else {
this.isCompact = !(
this.currentActivityView === activityBarViews.commit &&
window.innerHeight >= MAX_WINDOW_HEIGHT_COMPACT
);
}
},
lastCommitMsg() {
this.isCompact =
this.currentActivityView !== activityBarViews.commit && this.lastCommitMsg === '';
},
},
methods: {
...mapActions(['updateActivityBarView']),
...mapActions('commit', ['updateCommitMessage', 'discardDraft', 'commitChanges']),
toggleIsSmall() {
this.updateActivityBarView(activityBarViews.commit)
.then(() => {
this.isCompact = !this.isCompact;
})
.catch(e => {
throw e;
});
},
beforeEnterTransition() {
const elHeight = this.isCompact
? this.$refs.formEl && this.$refs.formEl.offsetHeight
: this.$refs.compactEl && this.$refs.compactEl.offsetHeight;
this.componentHeight = elHeight + COMMIT_ITEM_PADDING;
},
enterTransition() {
this.$nextTick(() => {
const elHeight = this.isCompact
? this.$refs.compactEl && this.$refs.compactEl.offsetHeight
: this.$refs.formEl && this.$refs.formEl.offsetHeight;
this.componentHeight = elHeight + COMMIT_ITEM_PADDING;
});
},
afterEndTransition() {
this.componentHeight = null;
},
},
activityBarViews,
};
</script>
<template>
<div
class="multi-file-commit-form"
:class="{
'is-compact': isCompact,
'is-full': !isCompact
}"
:style="{
height: componentHeight ? `${componentHeight}px` : null,
}"
>
<transition
name="commit-form-slide-up"
@before-enter="beforeEnterTransition"
@enter="enterTransition"
@after-enter="afterEndTransition"
>
<div
v-if="isCompact"
class="commit-form-compact"
ref="compactEl"
>
<button
type="button"
:disabled="!hasChanges"
class="btn btn-primary btn-sm btn-block"
@click="toggleIsSmall"
>
{{ __('Commit') }}
</button>
<p
class="text-center"
v-html="overviewText"
></p>
</div>
<form
v-if="!isCompact"
class="form-horizontal"
@submit.prevent.stop="commitChanges"
ref="formEl"
>
<transition name="fade">
<success-message
v-show="lastCommitMsg"
/>
</transition>
<commit-message-field
:text="commitMessage"
@input="updateCommitMessage"
/>
<div class="clearfix prepend-top-15">
<actions />
<loading-button
:loading="submitCommitLoading"
:disabled="commitButtonDisabled"
container-class="btn btn-success btn-sm pull-left"
:label="__('Commit')"
@click="commitChanges"
/>
<button
v-if="!discardDraftButtonDisabled"
type="button"
class="btn btn-default btn-sm pull-right"
@click="discardDraft"
>
{{ __('Discard draft') }}
</button>
<button
v-else
type="button"
class="btn btn-default btn-sm pull-right"
@click="toggleIsSmall"
>
{{ __('Collapse') }}
</button>
</div>
</form>
</transition>
</div>
</template>
......@@ -44,6 +44,11 @@ export default {
default: false,
},
},
data() {
return {
showActionButton: false,
};
},
computed: {
titleText() {
return sprintf(__('%{title} changes'), {
......@@ -56,6 +61,9 @@ export default {
actionBtnClicked() {
this[this.action]();
},
setShowActionButton(show) {
this.showActionButton = show;
},
},
};
</script>
......@@ -76,7 +84,16 @@ export default {
:size="18"
/>
{{ titleText }}
<span
v-show="!showActionButton"
@mouseenter="setShowActionButton(true)"
class="ide-commit-file-count"
>
{{ fileList.length }}
</span>
<button
v-show="showActionButton"
@mouseleave="setShowActionButton(false)"
type="button"
class="btn btn-blank btn-link ide-staged-action-btn"
@click="actionBtnClicked"
......
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { __ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
......@@ -26,10 +27,20 @@ export default {
required: false,
default: false,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
...mapState('commit', ['commitAction']),
...mapGetters('commit', ['newBranchName']),
tooltipTitle() {
return this.disabled
? __('This option is disabled while you still have unstaged changes')
: '';
},
},
methods: {
...mapActions('commit', ['updateCommitAction', 'updateBranchName']),
......@@ -39,19 +50,28 @@ export default {
<template>
<fieldset>
<label>
<label
v-tooltip
:title="tooltipTitle"
:class="{
'is-disabled': disabled
}"
>
<input
type="radio"
name="commit-action"
:value="value"
@change="updateCommitAction($event.target.value)"
:checked="checked"
v-once
:checked="commitAction === value"
:disabled="disabled"
/>
<span class="prepend-left-10">
<template v-if="label">
<span
v-if="label"
class="ide-radio-label"
>
{{ label }}
</template>
</span>
<slot v-else></slot>
</span>
</label>
......
......@@ -10,7 +10,10 @@ import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue';
import CommitSection from './repo_commit_section.vue';
import CommitForm from './commit_sidebar/form.vue';
import IdeReview from './ide_review.vue';
import SuccessMessage from './commit_sidebar/success_message.vue';
import { activityBarViews } from '../constants';
export default {
directives: {
......@@ -26,7 +29,9 @@ export default {
Identicon,
CommitSection,
IdeTree,
CommitForm,
IdeReview,
SuccessMessage,
},
data() {
return {
......@@ -34,8 +39,21 @@ export default {
};
},
computed: {
...mapState(['loading', 'currentBranchId', 'currentActivityView']),
...mapGetters(['currentProject']),
...mapState([
'loading',
'currentBranchId',
'currentActivityView',
'changedFiles',
'stagedFiles',
'lastCommitMsg',
]),
...mapGetters(['currentProject', 'someUncommitedChanges']),
showSuccessMessage() {
return (
this.currentActivityView === activityBarViews.edit &&
(this.lastCommitMsg && !this.someUncommitedChanges)
);
},
branchTooltipTitle() {
return this.showTooltip ? this.currentBranchId : undefined;
},
......@@ -115,6 +133,7 @@ export default {
:is="currentActivityView"
/>
</div>
<commit-form />
</template>
</div>
</resizable-panel>
......
<script>
import { mapState, mapGetters } from 'vuex';
import { mapState, mapGetters, mapActions } from 'vuex';
import NewDropdown from './new_dropdown/index.vue';
import IdeTreeList from './ide_tree_list.vue';
......@@ -10,7 +10,17 @@ export default {
},
computed: {
...mapState(['currentBranchId']),
...mapGetters(['currentProject']),
...mapGetters(['currentProject', 'currentTree', 'activeFile']),
},
mounted() {
if (this.activeFile && this.activeFile.pending) {
this.$router.push(`/project${this.activeFile.url}`, () => {
this.updateViewer('editor');
});
}
},
methods: {
...mapActions(['updateViewer']),
},
};
</script>
......
......@@ -3,13 +3,10 @@ import { mapState, mapActions, mapGetters } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CommitFilesList from './commit_sidebar/list.vue';
import EmptyState from './commit_sidebar/empty_state.vue';
import CommitMessageField from './commit_sidebar/message_field.vue';
import SuccessMessage from './commit_sidebar/success_message.vue';
import * as consts from '../stores/modules/commit/constants';
import Actions from './commit_sidebar/actions.vue';
import { activityBarViews } from '../constants';
export default {
components: {
......@@ -17,21 +14,11 @@ export default {
Icon,
CommitFilesList,
EmptyState,
SuccessMessage,
Actions,
LoadingButton,
CommitMessageField,
},
directives: {
tooltip,
},
computed: {
showStageUnstageArea() {
return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal);
},
someUncommitedChanges() {
return !!(this.changedFiles.length || this.stagedFiles.length);
},
...mapState([
'changedFiles',
'stagedFiles',
......@@ -40,15 +27,37 @@ export default {
'unusedSeal',
]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges']),
...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
showStageUnstageArea() {
return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal);
},
},
watch: {
hasChanges() {
if (!this.hasChanges) {
this.updateActivityBarView(activityBarViews.edit);
}
},
},
mounted() {
if (this.lastOpenedFile) {
this.openPendingTab({
file: this.lastOpenedFile,
})
.then(changeViewer => {
if (changeViewer) {
this.updateViewer('diff');
}
})
.catch(e => {
throw e;
});
}
},
methods: {
...mapActions('commit', [
'updateCommitMessage',
'discardDraft',
'commitChanges',
'updateCommitAction',
]),
...mapActions(['openPendingTab', 'updateViewer', 'updateActivityBarView']),
...mapActions('commit', ['commitChanges', 'updateCommitAction']),
forceCreateNewBranch() {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges());
},
......@@ -76,6 +85,7 @@ export default {
v-if="showStageUnstageArea"
>
<commit-files-list
class="is-first"
icon-name="unstaged"
:title="__('Unstaged')"
:file-list="changedFiles"
......@@ -96,39 +106,5 @@ export default {
<empty-state
v-if="unusedSeal"
/>
<div
class="multi-file-commit-panel-bottom"
>
<form
class="form-horizontal multi-file-commit-form"
@submit.prevent.stop="commitChanges"
>
<success-message
v-if="lastCommitMsg && !someUncommitedChanges"
/>
<commit-message-field
:text="commitMessage"
@input="updateCommitMessage"
/>
<div class="clearfix prepend-top-15">
<actions />
<loading-button
:loading="submitCommitLoading"
:disabled="commitButtonDisabled"
container-class="btn btn-success btn-sm pull-left"
:label="__('Commit')"
@click="commitChanges"
/>
<button
v-if="!discardDraftButtonDisabled"
type="button"
class="btn btn-default btn-sm pull-right"
@click="discardDraft"
>
{{ __('Discard draft') }}
</button>
</div>
</form>
</div>
</div>
</template>
......@@ -32,6 +32,8 @@ export default {
return `Close ${this.tab.name}`;
},
showChangedIcon() {
if (this.tab.pending) return true;
return this.fileHasChanged ? !this.tabMouseOver : false;
},
fileHasChanged() {
......@@ -91,6 +93,7 @@ export default {
class="multi-file-tab-close"
@click.stop.prevent="closeFile(tab)"
:aria-label="closeLabel"
:disabled="tab.pending"
>
<icon
v-if="!showChangedIcon"
......
......@@ -3,6 +3,10 @@ export const MAX_FILE_FINDER_RESULTS = 40;
export const FILE_FINDER_ROW_HEIGHT = 55;
export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33;
export const MAX_WINDOW_HEIGHT_COMPACT = 750;
export const COMMIT_ITEM_PADDING = 32;
// Commit message textarea
export const MAX_TITLE_LENGTH = 50;
export const MAX_BODY_LENGTH = 72;
......
......@@ -195,13 +195,7 @@ export const unstageChange = ({ commit }, path) => {
};
export const openPendingTab = ({ commit, getters, dispatch, state }, { file, keyPrefix }) => {
if (
getters.activeFile &&
getters.activeFile.path === file.path &&
state.viewer === viewerTypes.diff
) {
return false;
}
state.openFiles.forEach(f => eventHub.$emit(`editor.update.model.dispose.${f.key}`));
commit(types.ADD_PENDING_TAB, { file, keyPrefix });
......
......@@ -56,10 +56,16 @@ export const allBlobs = state =>
export const getChangedFile = state => path => state.changedFiles.find(f => f.path === path);
export const getStagedFile = state => path => state.stagedFiles.find(f => f.path === path);
export const lastOpenedFile = state =>
[...state.changedFiles, ...state.stagedFiles].sort((a, b) => b.lastOpenedAt - a.lastOpenedAt)[0];
export const isEditModeActive = state => state.currentActivityView === activityBarViews.edit;
export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit;
export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review;
export const someUncommitedChanges = state =>
!!(state.changedFiles.length || state.stagedFiles.length);
export const getChangesInFolder = state => path => {
const changedFilesCount = state.changedFiles.filter(f => filePathMatches(f, path)).length;
const stagedFilesCount = state.stagedFiles.filter(
......
......@@ -8,6 +8,7 @@ import router from '../../../ide_router';
import service from '../../../services';
import * as types from './mutation_types';
import * as consts from './constants';
import { activityBarViews } from '../../../constants';
import eventHub from '../../../eventhub';
export const updateCommitMessage = ({ commit }, message) => {
......@@ -75,7 +76,7 @@ export const checkCommitStatus = ({ rootState }) =>
export const updateFilesAfterCommit = (
{ commit, dispatch, state, rootState, rootGetters },
{ data, branch },
{ data },
) => {
const selectedProject = rootState.projects[rootState.currentProjectId];
const lastCommit = {
......@@ -126,15 +127,9 @@ export const updateFilesAfterCommit = (
changed: !!changedFile,
});
});
if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH && rootGetters.activeFile) {
router.push(
`/project/${rootState.currentProjectId}/blob/${branch}/${rootGetters.activeFile.path}`,
);
}
};
export const commitChanges = ({ commit, state, getters, dispatch, rootState }) => {
export const commitChanges = ({ commit, state, getters, dispatch, rootState, rootGetters }) => {
const newBranch = state.commitAction !== consts.COMMIT_TO_CURRENT_BRANCH;
const payload = createCommitPayload(getters.branchName, newBranch, state, rootState);
const getCommitStatus = newBranch ? Promise.resolve(false) : dispatch('checkCommitStatus');
......@@ -187,6 +182,34 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState }) =
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
}, 5000);
})
.then(() => {
if (rootGetters.lastOpenedFile) {
dispatch(
'openPendingTab',
{
file: rootGetters.lastOpenedFile,
},
{ root: true },
)
.then(changeViewer => {
if (changeViewer) {
dispatch('updateViewer', 'diff', { root: true });
}
})
.catch(e => {
throw e;
});
} else {
dispatch('updateActivityBarView', activityBarViews.edit, { root: true });
dispatch('updateViewer', 'editor', { root: true });
router.push(
`/project/${rootState.currentProjectId}/blob/${getters.branchName}/${
rootGetters.activeFile.path
}`,
);
}
})
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH));
})
.catch(err => {
......
/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
export default {
......@@ -169,36 +170,24 @@ export default {
});
},
[types.ADD_PENDING_TAB](state, { file, keyPrefix = 'pending' }) {
const key = `${keyPrefix}-${file.key}`;
const pendingTab = state.openFiles.find(f => f.key === key && f.pending);
let openFiles = state.openFiles.map(f => Object.assign(f, { active: false, opened: false }));
if (!pendingTab) {
const openFile = openFiles.find(f => f.path === file.path);
openFiles = openFiles.concat(openFile ? null : file).reduce((acc, f) => {
if (!f) return acc;
if (f.path === file.path) {
return acc.concat({
...f,
content: file.content,
active: true,
pending: true,
opened: true,
key,
});
}
return acc.concat(f);
}, []);
} else {
openFiles = state.openFiles.map(f =>
Object.assign(f, { active: f.key === key, opened: f.key === key }),
);
}
Object.assign(state, { openFiles });
state.entries[file.path].opened = false;
state.entries[file.path].active = false;
state.entries[file.path].lastOpenedAt = new Date().getTime();
state.openFiles.forEach(f =>
Object.assign(f, {
opened: false,
active: false,
}),
);
state.openFiles = [
{
...file,
key: `${keyPrefix}-${file.key}`,
pending: true,
opened: true,
active: true,
},
];
},
[types.REMOVE_PENDING_TAB](state, file) {
Object.assign(state, {
......
......@@ -192,11 +192,11 @@
right: 3px;
}
&:hover {
&:not([disabled]):hover {
background-color: $theme-gray-200;
}
&:focus {
&:not([disabled]):focus {
background-color: $blue-500;
color: $white-light;
outline: 0;
......@@ -443,6 +443,7 @@
}
.multi-file-commit-panel-inner {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
......@@ -484,14 +485,13 @@
align-items: center;
margin-bottom: 0;
border-bottom: 1px solid $white-dark;
padding: $gl-btn-padding 0;
min-height: 56px;
padding: $gl-btn-padding $gl-padding;
}
.multi-file-commit-panel-header-title {
display: flex;
flex: 1;
padding-left: $grid-size;
align-items: center;
svg {
margin-right: $gl-btn-padding;
......@@ -507,7 +507,7 @@
.multi-file-commit-list {
flex: 1;
overflow: auto;
padding: $gl-padding 0;
padding: $gl-padding;
min-height: 60px;
}
......@@ -602,30 +602,24 @@
}
.multi-file-commit-form {
position: relative;
padding: $gl-padding;
background-color: $white-light;
border-top: 1px solid $white-dark;
border-left: 1px solid $white-dark;
transition: all 0.3s ease;
.btn {
font-size: $gl-font-size;
}
.multi-file-commit-panel-success-message {
top: 0;
}
}
.multi-file-commit-panel-bottom {
position: relative;
.multi-file-commit-panel-success-message {
position: absolute;
top: 1px;
left: 3px;
bottom: 0;
right: 0;
z-index: 10;
background: $gray-light;
overflow: auto;
display: flex;
flex-direction: column;
justify-content: center;
}
}
.dirty-diff {
......@@ -779,17 +773,36 @@
flex-direction: column;
width: 100%;
min-height: 140px;
padding: 0 16px;
&.is-first {
border-bottom: 1px solid $white-dark;
}
}
.ide-staged-action-btn {
margin-left: auto;
color: $gl-link-color;
line-height: 22px;
}
.ide-commit-file-count {
min-width: 22px;
margin-left: auto;
background-color: $gray-light;
border-radius: $border-radius-default;
border: 1px solid $white-dark;
line-height: 20px;
text-align: center;
}
.ide-commit-radios {
label {
font-weight: normal;
&.is-disabled {
.ide-radio-label {
text-decoration: line-through;
}
}
}
.help-block {
......@@ -853,6 +866,7 @@
.ide-activity-bar {
position: relative;
flex: 0 0 60px;
z-index: 1;
}
.ide-file-finder-overlay {
......@@ -969,6 +983,40 @@
}
}
.commit-form-compact {
.btn {
margin-bottom: 8px;
}
p {
margin-bottom: 0;
}
}
.commit-form-slide-up-enter-active,
.commit-form-slide-up-leave-active {
position: absolute;
top: 16px;
left: 16px;
right: 16px;
transition: all 0.3s ease;
}
.is-full .commit-form-slide-up-enter,
.is-compact .commit-form-slide-up-leave-to {
transform: translateY(100%);
}
.is-full .commit-form-slide-up-enter-to,
.is-compact .commit-form-slide-up-leave {
transform: translateY(0);
}
.commit-form-slide-up-enter,
.commit-form-slide-up-leave-to {
opacity: 0;
}
.ide-review-header {
flex-direction: column;
align-items: flex-start;
......@@ -996,6 +1044,20 @@
line-height: 34px;
}
.multi-file-commit-panel-success-message {
position: absolute;
top: 61px;
left: 1px;
bottom: 0;
right: 0;
z-index: 10;
background: $white-light;
overflow: auto;
display: flex;
flex-direction: column;
justify-content: center;
}
.ide-review-button-holder {
display: flex;
width: 100%;
......
......@@ -46,7 +46,8 @@ feature 'Multi-file editor new directory', :js do
find('.js-ide-commit-mode').click
click_button 'Stage all'
find('.multi-file-commit-list-item').hover
first('.multi-file-discard-btn .btn').click
fill_in('commit-message', with: 'commit message ide')
......
......@@ -36,7 +36,8 @@ feature 'Multi-file editor new file', :js do
find('.js-ide-commit-mode').click
click_button 'Stage all'
find('.multi-file-commit-list-item').hover
first('.multi-file-discard-btn .btn').click
fill_in('commit-message', with: 'commit message ide')
......
import Vue from 'vue';
import store from '~/ide/stores';
import CommitForm from '~/ide/components/commit_sidebar/form.vue';
import { activityBarViews } from '~/ide/constants';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import { resetStore } from '../../helpers';
describe('IDE commit form', () => {
const Component = Vue.extend(CommitForm);
let vm;
beforeEach(() => {
spyOnProperty(window, 'innerHeight').and.returnValue(800);
store.state.changedFiles.push('test');
vm = createComponentWithStore(Component, store).$mount();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('enables button when has changes', () => {
expect(vm.$el.querySelector('[disabled]')).toBe(null);
});
describe('compact', () => {
it('renders commit button in compact mode', () => {
expect(vm.$el.querySelector('.btn-primary')).not.toBeNull();
expect(vm.$el.querySelector('.btn-primary').textContent).toContain('Commit');
});
it('does not render form', () => {
expect(vm.$el.querySelector('form')).toBeNull();
});
it('renders overview text', done => {
vm.$store.state.stagedFiles.push('test');
vm.$nextTick(() => {
expect(vm.$el.querySelector('p').textContent).toContain('1 unstaged and 1 staged changes');
done();
});
});
it('shows form when clicking commit button', done => {
vm.$el.querySelector('.btn-primary').click();
vm.$nextTick(() => {
expect(vm.$el.querySelector('form')).not.toBeNull();
done();
});
});
it('toggles activity bar vie when clicking commit button', done => {
vm.$el.querySelector('.btn-primary').click();
vm.$nextTick(() => {
expect(store.state.currentActivityView).toBe(activityBarViews.commit);
done();
});
});
});
describe('full', () => {
beforeEach(done => {
vm.isCompact = false;
vm.$nextTick(done);
});
it('updates commitMessage in store on input', done => {
const textarea = vm.$el.querySelector('textarea');
textarea.value = 'testing commit message';
textarea.dispatchEvent(new Event('input'));
getSetTimeoutPromise()
.then(() => {
expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
})
.then(done)
.catch(done.fail);
});
it('updating currentActivityView not to commit view sets compact mode', done => {
store.state.currentActivityView = 'a';
vm.$nextTick(() => {
expect(vm.isCompact).toBe(true);
done();
});
});
describe('discard draft button', () => {
it('hidden when commitMessage is empty', () => {
expect(vm.$el.querySelector('.btn-default').textContent).toContain('Collapse');
});
it('resets commitMessage when clicking discard button', done => {
vm.$store.state.commit.commitMessage = 'testing commit message';
getSetTimeoutPromise()
.then(() => {
vm.$el.querySelector('.btn-default').click();
})
.then(Vue.nextTick)
.then(() => {
expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
})
.then(done)
.catch(done.fail);
});
});
describe('when submitting', () => {
beforeEach(() => {
spyOn(vm, 'commitChanges');
vm.$store.state.stagedFiles.push('test');
});
it('calls commitChanges', done => {
vm.$store.state.commit.commitMessage = 'testing commit message';
getSetTimeoutPromise()
.then(() => {
vm.$el.querySelector('.btn-success').click();
})
.then(Vue.nextTick)
.then(() => {
expect(vm.commitChanges).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
});
});
});
......@@ -49,16 +49,4 @@ describe('Multi-file editor commit sidebar list', () => {
expect(vm.$el.textContent).toContain('No changes');
});
});
describe('action button', () => {
beforeEach(() => {
spyOn(vm, 'stageAllChanges');
});
it('calls store action when clicked', () => {
vm.$el.querySelector('.ide-staged-action-btn').click();
expect(vm.stageAllChanges).toHaveBeenCalled();
});
});
});
import Vue from 'vue';
import store from '~/ide/stores';
import service from '~/ide/services';
import router from '~/ide/ide_router';
import repoCommitSection from '~/ide/components/repo_commit_section.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import { file, resetStore } from '../helpers';
describe('RepoCommitSection', () => {
......@@ -60,6 +60,8 @@ describe('RepoCommitSection', () => {
}
beforeEach(done => {
spyOn(router, 'push');
vm = createComponent();
spyOn(service, 'getTreeData').and.returnValue(
......@@ -105,30 +107,28 @@ describe('RepoCommitSection', () => {
it('renders a commit section', () => {
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
const submitCommit = vm.$el.querySelector('form .btn');
const allFiles = vm.$store.state.changedFiles.concat(vm.$store.state.stagedFiles);
expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
expect(changedFileElements.length).toEqual(4);
changedFileElements.forEach((changedFile, i) => {
expect(changedFile.textContent.trim()).toContain(allFiles[i].path);
});
expect(submitCommit.disabled).toBeTruthy();
expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
});
it('adds changed files into staged files', done => {
vm.$el.querySelector('.ide-staged-action-btn').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.ide-commit-list-container').textContent).toContain(
'No changes',
);
done();
});
vm.$el.querySelector('.multi-file-discard-btn .btn').click();
vm
.$nextTick()
.then(() => vm.$el.querySelector('.multi-file-discard-btn .btn').click())
.then(vm.$nextTick)
.then(() => {
expect(vm.$el.querySelector('.ide-commit-list-container').textContent).toContain(
'No changes',
);
})
.then(done)
.catch(done.fail);
});
it('stages a single file', done => {
......@@ -156,18 +156,6 @@ describe('RepoCommitSection', () => {
});
});
it('removes all staged files', done => {
vm.$el.querySelectorAll('.ide-staged-action-btn')[1].click();
Vue.nextTick(() => {
expect(vm.$el.querySelectorAll('.ide-commit-list-container')[1].textContent).toContain(
'No changes',
);
done();
});
});
it('unstages a single file', done => {
vm.$el
.querySelectorAll('.multi-file-discard-btn')[2]
......@@ -183,60 +171,10 @@ describe('RepoCommitSection', () => {
});
});
it('updates commitMessage in store on input', done => {
const textarea = vm.$el.querySelector('textarea');
textarea.value = 'testing commit message';
textarea.dispatchEvent(new Event('input'));
getSetTimeoutPromise()
.then(() => {
expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
})
.then(done)
.catch(done.fail);
});
describe('discard draft button', () => {
it('hidden when commitMessage is empty', () => {
expect(vm.$el.querySelector('.multi-file-commit-form .btn-default')).toBeNull();
});
it('resets commitMessage when clicking discard button', done => {
vm.$store.state.commit.commitMessage = 'testing commit message';
getSetTimeoutPromise()
.then(() => {
vm.$el.querySelector('.multi-file-commit-form .btn-default').click();
})
.then(Vue.nextTick)
.then(() => {
expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
})
.then(done)
.catch(done.fail);
});
});
describe('when submitting', () => {
beforeEach(() => {
spyOn(vm, 'commitChanges');
});
it('calls commitChanges', done => {
vm.$store.state.commit.commitMessage = 'testing commit message';
getSetTimeoutPromise()
.then(() => {
vm.$el.querySelector('.multi-file-commit-form .btn-success').click();
})
.then(Vue.nextTick)
.then(() => {
expect(vm.commitChanges).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
describe('mounted', () => {
it('opens last opened file', () => {
expect(store.state.openFiles.length).toBe(1);
expect(store.state.openFiles[0].pending).toBe(true);
});
});
});
......@@ -589,20 +589,6 @@ describe('IDE store file actions', () => {
.then(done)
.catch(done.fail);
});
it('returns false when passed in file is active & viewer is diff', done => {
f.active = true;
store.state.openFiles.push(f);
store.state.viewer = 'diff';
store
.dispatch('openPendingTab', { file: f, keyPrefix: 'pending' })
.then(added => {
expect(added).toBe(false);
})
.then(done)
.catch(done.fail);
});
});
describe('removePendingTab', () => {
......
......@@ -289,21 +289,6 @@ describe('IDE commit module actions', () => {
.then(done)
.catch(done.fail);
});
it('pushes route to new branch if commitAction is new branch', done => {
store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH;
store
.dispatch('commit/updateFilesAfterCommit', {
data,
branch,
})
.then(() => {
expect(router.push).toHaveBeenCalledWith(`/project/abcproject/blob/master/${f.path}`);
})
.then(done)
.catch(done.fail);
});
});
describe('commitChanges', () => {
......@@ -391,21 +376,6 @@ describe('IDE commit module actions', () => {
.catch(done.fail);
});
it('pushes router to new route', done => {
store
.dispatch('commit/commitChanges')
.then(() => {
expect(router.push).toHaveBeenCalledWith(
`/project/${store.state.currentProjectId}/blob/${
store.getters['commit/newBranchName']
}/changed`,
);
done();
})
.catch(done.fail);
});
it('sets last Commit Msg', done => {
store
.dispatch('commit/commitChanges')
......
......@@ -267,41 +267,23 @@ describe('IDE store file mutations', () => {
it('adds file into openFiles as pending', () => {
mutations.ADD_PENDING_TAB(localState, { file: localFile });
expect(localState.openFiles.length).toBe(2);
expect(localState.openFiles[1].pending).toBe(true);
expect(localState.openFiles[1].key).toBe(`pending-${localFile.key}`);
});
it('updates open file to pending', () => {
mutations.ADD_PENDING_TAB(localState, { file: localState.openFiles[0] });
expect(localState.openFiles.length).toBe(1);
expect(localState.openFiles[0].pending).toBe(true);
expect(localState.openFiles[0].key).toBe(`pending-${localFile.key}`);
});
it('updates pending open file to active', () => {
localState.openFiles.push({
...localFile,
pending: true,
});
it('only allows 1 open pending file', () => {
const newFile = file('test');
localState.entries[newFile.path] = newFile;
mutations.ADD_PENDING_TAB(localState, { file: localFile });
expect(localState.openFiles[1].pending).toBe(true);
expect(localState.openFiles[1].active).toBe(true);
});
it('sets all openFiles to not active', () => {
mutations.ADD_PENDING_TAB(localState, { file: localFile });
expect(localState.openFiles.length).toBe(1);
expect(localState.openFiles.length).toBe(2);
mutations.ADD_PENDING_TAB(localState, { file: file('test') });
localState.openFiles.forEach(f => {
if (f.pending) {
expect(f.active).toBe(true);
} else {
expect(f.active).toBe(false);
}
});
expect(localState.openFiles.length).toBe(1);
expect(localState.openFiles[0].name).toBe('test');
});
});
......
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