Commit df8a38b9 authored by Fatih Acet's avatar Fatih Acet

Merge branch 'ee-45687-web-ide-empty-state' into 'master'

Port of 45687-web-ide-empty-state to EE

See merge request gitlab-org/gitlab-ee!10445
parents cd8859d4 84be2e96
<script>
import Vue from 'vue';
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import FindFile from '~/vue_shared/components/file_finder/index.vue';
import NewModal from './new_dropdown/modal.vue';
......@@ -22,6 +23,8 @@ export default {
FindFile,
ErrorMessage,
CommitEditorHeader,
GlButton,
GlLoadingIcon,
},
props: {
rightPaneComponent: {
......@@ -47,13 +50,15 @@ export default {
'someUncommittedChanges',
'isCommitModeActive',
'allBlobs',
'emptyRepo',
'currentTree',
]),
},
mounted() {
window.onbeforeunload = e => this.onBeforeUnload(e);
},
methods: {
...mapActions(['toggleFileFinder']),
...mapActions(['toggleFileFinder', 'openNewEntryModal']),
onBeforeUnload(e = {}) {
const returnValue = __('Are you sure you want to lose unsaved changes?');
......@@ -98,17 +103,40 @@ export default {
<repo-editor :file="activeFile" class="multi-file-edit-pane-content" />
</template>
<template v-else>
<div v-once class="ide-empty-state">
<div class="ide-empty-state">
<div class="row js-empty-state">
<div class="col-12">
<div class="svg-content svg-250"><img :src="emptyStateSvgPath" /></div>
</div>
<div class="col-12">
<div class="text-content text-center">
<h4>Welcome to the GitLab IDE</h4>
<h4>
{{ __('Make and review changes in the browser with the Web IDE') }}
</h4>
<template v-if="emptyRepo">
<p>
Select a file from the left sidebar to begin editing. Afterwards, you'll be able
to commit your changes.
{{
__(
"Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes.",
)
}}
</p>
<gl-button
variant="success"
:title="__('New file')"
:aria-label="__('New file')"
@click="openNewEntryModal({ type: 'blob' })"
>
{{ __('New file') }}
</gl-button>
</template>
<gl-loading-icon v-else-if="!currentTree || currentTree.loading" size="md" />
<p v-else>
{{
__(
"Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes.",
)
}}
</p>
</div>
</div>
......
......@@ -54,6 +54,7 @@ export default {
<slot name="header"></slot>
</header>
<div class="ide-tree-body h-100">
<template v-if="currentTree.tree.length">
<file-row
v-for="file in currentTree.tree"
:key="file.key"
......@@ -62,6 +63,8 @@ export default {
:extra-component="$options.FileRowExtra"
@toggleTreeOpen="toggleTreeOpen"
/>
</template>
<div v-else class="file-row">{{ __('No files') }}</div>
</div>
</template>
</div>
......
import $ from 'jquery';
import Vue from 'vue';
import { __, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import flash from '~/flash';
import _ from 'underscore';
import * as types from './mutation_types';
import { decorateFiles } from '../lib/files';
import { stageKeys } from '../constants';
import service from '../services';
export const redirectToUrl = (_, url) => visitUrl(url);
export const redirectToUrl = (self, url) => visitUrl(url);
export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data);
......@@ -239,6 +242,53 @@ export const renameEntry = (
}
};
export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) =>
new Promise((resolve, reject) => {
const currentProject = state.projects[projectId];
if (!currentProject || !currentProject.branches[branchId] || force) {
service
.getBranchData(projectId, branchId)
.then(({ data }) => {
const { id } = data.commit;
commit(types.SET_BRANCH, {
projectPath: projectId,
branchName: branchId,
branch: data,
});
commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
resolve(data);
})
.catch(e => {
if (e.response.status === 404) {
reject(e);
} else {
flash(
__('Error loading branch data. Please try again.'),
'alert',
document,
null,
false,
true,
);
reject(
new Error(
sprintf(
__('Branch not loaded - %{branchId}'),
{
branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
},
false,
),
),
);
}
});
} else {
resolve(currentProject.branches[branchId]);
}
});
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
......
......@@ -35,48 +35,6 @@ export const getProjectData = ({ commit, state }, { namespace, projectId, force
}
});
export const getBranchData = (
{ commit, dispatch, state },
{ projectId, branchId, force = false } = {},
) =>
new Promise((resolve, reject) => {
if (
typeof state.projects[`${projectId}`] === 'undefined' ||
!state.projects[`${projectId}`].branches[branchId] ||
force
) {
service
.getBranchData(`${projectId}`, branchId)
.then(({ data }) => {
const { id } = data.commit;
commit(types.SET_BRANCH, {
projectPath: `${projectId}`,
branchName: branchId,
branch: data,
});
commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
resolve(data);
})
.catch(e => {
if (e.response.status === 404) {
dispatch('showBranchNotFoundError', branchId);
} else {
flash(
__('Error loading branch data. Please try again.'),
'alert',
document,
null,
false,
true,
);
}
reject(new Error(`Branch not loaded - ${projectId}/${branchId}`));
});
} else {
resolve(state.projects[`${projectId}`].branches[branchId]);
}
});
export const refreshLastCommitData = ({ commit }, { projectId, branchId } = {}) =>
service
.getBranchData(projectId, branchId)
......@@ -125,15 +83,31 @@ export const showBranchNotFoundError = ({ dispatch }, branchId) => {
});
};
export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath }) => {
export const showEmptyState = ({ commit, state }, { projectId, branchId }) => {
const treePath = `${projectId}/${branchId}`;
commit(types.CREATE_TREE, { treePath });
commit(types.TOGGLE_LOADING, {
entry: state.trees[treePath],
forceValue: false,
});
};
export const openBranch = ({ dispatch, state, getters }, { projectId, branchId, basePath }) => {
dispatch('setCurrentBranchId', branchId);
dispatch('getBranchData', {
if (getters.emptyRepo) {
return dispatch('showEmptyState', { projectId, branchId });
}
return dispatch('getBranchData', {
projectId,
branchId,
})
.then(() => {
dispatch('getMergeRequestsForBranch', {
projectId,
branchId,
});
return dispatch('getFiles', {
dispatch('getFiles', {
projectId,
branchId,
})
......@@ -155,10 +129,20 @@ export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath
}
}
})
.then(() => {
dispatch('getMergeRequestsForBranch', {
projectId,
branchId,
});
.catch(
() =>
new Error(
sprintf(
__('An error occurred whilst getting files for - %{branchId}'),
{
branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
},
false,
),
),
);
})
.catch(() => {
dispatch('showBranchNotFoundError', branchId);
});
};
......@@ -74,9 +74,6 @@ export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } =
resolve();
})
.catch(e => {
if (e.response.status === 404) {
dispatch('showBranchNotFoundError', branchId);
} else {
dispatch('setErrorMessage', {
text: __('An error occurred whilst loading all the files.'),
action: payload =>
......@@ -84,7 +81,6 @@ export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } =
actionText: __('Please try again'),
actionPayload: { projectId, branchId },
});
}
reject(e);
});
} else {
......
......@@ -36,6 +36,9 @@ export const currentMergeRequest = state => {
export const currentProject = state => state.projects[state.currentProjectId];
export const emptyRepo = state =>
state.projects[state.currentProjectId] && state.projects[state.currentProjectId].empty_repo;
export const currentTree = state =>
state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
......
......@@ -135,6 +135,17 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
return null;
}
if (!data.parent_ids.length) {
commit(
rootTypes.TOGGLE_EMPTY_STATE,
{
projectPath: rootState.currentProjectId,
value: false,
},
{ root: true },
);
}
dispatch('setLastCommitMessage', data);
dispatch('updateCommitMessage', '');
return dispatch('updateFilesAfterCommit', {
......
......@@ -12,6 +12,7 @@ export const SET_LINKS = 'SET_LINKS';
export const SET_PROJECT = 'SET_PROJECT';
export const SET_CURRENT_PROJECT = 'SET_CURRENT_PROJECT';
export const TOGGLE_PROJECT_OPEN = 'TOGGLE_PROJECT_OPEN';
export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
// Merge Request Mutation Types
export const SET_MERGE_REQUEST = 'SET_MERGE_REQUEST';
......
......@@ -19,6 +19,12 @@ export default {
});
},
[types.SET_BRANCH_WORKING_REFERENCE](state, { projectId, branchId, reference }) {
if (!state.projects[projectId].branches[branchId]) {
Object.assign(state.projects[projectId].branches, {
[branchId]: {},
});
}
Object.assign(state.projects[projectId].branches[branchId], {
workingReference: reference,
});
......
......@@ -21,4 +21,9 @@ export default {
}),
});
},
[types.TOGGLE_EMPTY_STATE](state, { projectPath, value }) {
Object.assign(state.projects[projectPath], {
empty_repo: value,
});
},
};
---
title: Empty project state for Web IDE
merge_request: 26556
author:
type: added
......@@ -239,6 +239,7 @@ module API
end
end
expose :empty_repo?, as: :empty_repo
expose :archived?, as: :archived
expose :visibility
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
......
......@@ -1211,6 +1211,9 @@ msgstr ""
msgid "An error occurred whilst fetching the latest pipeline."
msgstr ""
msgid "An error occurred whilst getting files for - %{branchId}"
msgstr ""
msgid "An error occurred whilst loading all the files."
msgstr ""
......@@ -1938,6 +1941,9 @@ msgstr ""
msgid "Branch name"
msgstr ""
msgid "Branch not loaded - %{branchId}"
msgstr ""
msgid "BranchSwitcherPlaceholder|Search branches"
msgstr ""
......@@ -3620,6 +3626,9 @@ msgstr ""
msgid "Create a new branch"
msgstr ""
msgid "Create a new file as there are no files yet. Afterwards, you'll be able to commit your changes."
msgstr ""
msgid "Create a new issue"
msgstr ""
......@@ -7667,6 +7676,9 @@ msgstr ""
msgid "MRDiff|Show full file"
msgstr ""
msgid "Make and review changes in the browser with the Web IDE"
msgstr ""
msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
......@@ -8500,6 +8512,9 @@ msgstr ""
msgid "No file selected"
msgstr ""
msgid "No files"
msgstr ""
msgid "No files found."
msgstr ""
......@@ -11252,6 +11267,9 @@ msgstr ""
msgid "Select Page"
msgstr ""
msgid "Select a file from the left sidebar to begin editing. Afterwards, you'll be able to commit your changes."
msgstr ""
msgid "Select a group to invite"
msgstr ""
......
......@@ -37,4 +37,39 @@ describe('Multi-file store branch mutations', () => {
expect(localState.projects.Example.branches.master.commit.title).toBe('Example commit');
});
});
describe('SET_BRANCH_WORKING_REFERENCE', () => {
beforeEach(() => {
localState.projects = {
Foo: {
branches: {
bar: {},
},
},
};
});
it('sets workingReference for existing branch', () => {
mutations.SET_BRANCH_WORKING_REFERENCE(localState, {
projectId: 'Foo',
branchId: 'bar',
reference: 'foo-bar-ref',
});
expect(localState.projects.Foo.branches.bar.workingReference).toBe('foo-bar-ref');
});
it('does not fail on non-existent just yet branch', () => {
expect(localState.projects.Foo.branches.unknown).toBeUndefined();
mutations.SET_BRANCH_WORKING_REFERENCE(localState, {
projectId: 'Foo',
branchId: 'unknown',
reference: 'fun-fun-ref',
});
expect(localState.projects.Foo.branches.unknown).not.toBeUndefined();
expect(localState.projects.Foo.branches.unknown.workingReference).toBe('fun-fun-ref');
});
});
});
import mutations from '~/ide/stores/mutations/project';
import state from '~/ide/stores/state';
describe('Multi-file store branch mutations', () => {
let localState;
beforeEach(() => {
localState = state();
localState.projects = { abcproject: { empty_repo: true } };
});
describe('TOGGLE_EMPTY_STATE', () => {
it('sets empty_repo for project to passed value', () => {
mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: 'abcproject', value: false });
expect(localState.projects.abcproject.empty_repo).toBe(false);
mutations.TOGGLE_EMPTY_STATE(localState, { projectPath: 'abcproject', value: true });
expect(localState.projects.abcproject.empty_repo).toBe(true);
});
});
});
......@@ -5,21 +5,31 @@ import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helpe
import { file, resetStore } from '../helpers';
import { projectData } from '../mock_data';
describe('ide component', () => {
let vm;
beforeEach(() => {
function bootstrap(projData) {
const Component = Vue.extend(ide);
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
store.state.projects.abcproject = Object.assign({}, projectData);
store.state.projects.abcproject = Object.assign({}, projData);
Vue.set(store.state.trees, 'abcproject/master', {
tree: [],
loading: false,
});
vm = createComponentWithStore(Component, store, {
return createComponentWithStore(Component, store, {
emptyStateSvgPath: 'svg',
noChangesStateSvgPath: 'svg',
committedStateSvgPath: 'svg',
}).$mount();
});
}
describe('ide component, empty repo', () => {
let vm;
beforeEach(() => {
const emptyProjData = Object.assign({}, projectData, { empty_repo: true, branches: {} });
vm = bootstrap(emptyProjData);
vm.$mount();
});
afterEach(() => {
......@@ -28,17 +38,37 @@ describe('ide component', () => {
resetStore(vm.$store);
});
it('does not render right when no files open', () => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
it('renders "New file" button in empty repo', done => {
vm.$nextTick(() => {
expect(vm.$el.querySelector('.ide-empty-state button[title="New file"]')).not.toBeNull();
done();
});
});
});
describe('ide component, non-empty repo', () => {
let vm;
beforeEach(() => {
vm = bootstrap(projectData);
vm.$mount();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
it('renders right panel when files are open', done => {
vm.$store.state.trees['abcproject/mybranch'] = {
tree: [file()],
it('shows error message when set', done => {
expect(vm.$el.querySelector('.flash-container')).toBe(null);
vm.$store.state.errorMessage = {
text: 'error',
};
Vue.nextTick(() => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
vm.$nextTick(() => {
expect(vm.$el.querySelector('.flash-container')).not.toBe(null);
done();
});
......@@ -71,17 +101,25 @@ describe('ide component', () => {
});
});
it('shows error message when set', done => {
expect(vm.$el.querySelector('.flash-container')).toBe(null);
describe('non-existent branch', () => {
it('does not render "New file" button for non-existent branch when repo is not empty', done => {
vm.$nextTick(() => {
expect(vm.$el.querySelector('.ide-empty-state button[title="New file"]')).toBeNull();
done();
});
});
});
vm.$store.state.errorMessage = {
text: 'error',
};
describe('branch with files', () => {
beforeEach(() => {
store.state.trees['abcproject/master'].tree = [file()];
});
it('does not render "New file" button', done => {
vm.$nextTick(() => {
expect(vm.$el.querySelector('.flash-container')).not.toBe(null);
expect(vm.$el.querySelector('.ide-empty-state button[title="New file"]')).toBeNull();
done();
});
});
});
});
......@@ -7,25 +7,23 @@ import { projectData } from '../mock_data';
describe('IDE tree list', () => {
const Component = Vue.extend(IdeTreeList);
const normalBranchTree = [file('fileName')];
const emptyBranchTree = [];
let vm;
beforeEach(() => {
const bootstrapWithTree = (tree = normalBranchTree) => {
store.state.currentProjectId = 'abcproject';
store.state.currentBranchId = 'master';
store.state.projects.abcproject = Object.assign({}, projectData);
Vue.set(store.state.trees, 'abcproject/master', {
tree: [file('fileName')],
tree,
loading: false,
});
vm = createComponentWithStore(Component, store, {
viewerType: 'edit',
});
spyOn(vm, 'updateViewer').and.callThrough();
vm.$mount();
});
};
afterEach(() => {
vm.$destroy();
......@@ -33,6 +31,15 @@ describe('IDE tree list', () => {
resetStore(vm.$store);
});
describe('normal branch', () => {
beforeEach(() => {
bootstrapWithTree();
spyOn(vm, 'updateViewer').and.callThrough();
vm.$mount();
});
it('updates viewer on mount', () => {
expect(vm.updateViewer).toHaveBeenCalledWith('edit');
});
......@@ -51,4 +58,20 @@ describe('IDE tree list', () => {
it('renders list of files', () => {
expect(vm.$el.textContent).toContain('fileName');
});
});
describe('empty-branch state', () => {
beforeEach(() => {
bootstrapWithTree(emptyBranchTree);
spyOn(vm, 'updateViewer').and.callThrough();
vm.$mount();
});
it('does not load files if the branch is empty', () => {
expect(vm.$el.textContent).not.toContain('fileName');
expect(vm.$el.textContent).toContain('No files');
});
});
});
......@@ -4,7 +4,7 @@ import {
refreshLastCommitData,
showBranchNotFoundError,
createNewBranchFromDefault,
getBranchData,
showEmptyState,
openBranch,
} from '~/ide/stores/actions';
import store from '~/ide/stores';
......@@ -196,39 +196,44 @@ describe('IDE store project actions', () => {
});
});
describe('getBranchData', () => {
describe('error', () => {
it('dispatches branch not found action when response is 404', done => {
const dispatch = jasmine.createSpy('dispatchSpy');
mock.onGet(/(.*)/).replyOnce(404);
getBranchData(
describe('showEmptyState', () => {
it('commits proper mutations when supplied error is 404', done => {
testAction(
showEmptyState,
{
commit() {},
dispatch,
state: store.state,
err: {
response: {
status: 404,
},
},
{
projectId: 'abc/def',
branchId: 'master-testing',
branchId: 'master',
},
)
.then(done.fail)
.catch(() => {
expect(dispatch.calls.argsFor(0)).toEqual([
'showBranchNotFoundError',
'master-testing',
]);
done();
});
});
store.state,
[
{
type: 'CREATE_TREE',
payload: {
treePath: 'abc/def/master',
},
},
{
type: 'TOGGLE_LOADING',
payload: {
entry: store.state.trees['abc/def/master'],
forceValue: false,
},
},
],
[],
done,
);
});
});
describe('openBranch', () => {
const branch = {
projectId: 'feature/lorem-ipsum',
projectId: 'abc/def',
branchId: '123-lorem',
};
......@@ -238,7 +243,37 @@ describe('IDE store project actions', () => {
'foo/bar-pending': { pending: true },
'foo/bar': { pending: false },
};
});
describe('empty repo', () => {
beforeEach(() => {
spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
store.state.currentProjectId = 'abc/def';
store.state.projects['abc/def'] = {
empty_repo: true,
};
});
afterEach(() => {
resetStore(store);
});
it('dispatches showEmptyState action right away', done => {
openBranch(store, branch)
.then(() => {
expect(store.dispatch.calls.allArgs()).toEqual([
['setCurrentBranchId', branch.branchId],
['showEmptyState', branch],
]);
done();
})
.catch(done.fail);
});
});
describe('existing branch', () => {
beforeEach(() => {
spyOn(store, 'dispatch').and.returnValue(Promise.resolve());
});
......@@ -248,8 +283,8 @@ describe('IDE store project actions', () => {
expect(store.dispatch.calls.allArgs()).toEqual([
['setCurrentBranchId', branch.branchId],
['getBranchData', branch],
['getFiles', branch],
['getMergeRequestsForBranch', branch],
['getFiles', branch],
]);
})
.then(done)
......@@ -297,4 +332,24 @@ describe('IDE store project actions', () => {
.catch(done.fail);
});
});
describe('non-existent branch', () => {
beforeEach(() => {
spyOn(store, 'dispatch').and.returnValue(Promise.reject());
});
it('dispatches correct branch actions', done => {
openBranch(store, branch)
.then(() => {
expect(store.dispatch.calls.allArgs()).toEqual([
['setCurrentBranchId', branch.branchId],
['getBranchData', branch],
['showBranchNotFoundError', branch.branchId],
]);
})
.then(done)
.catch(done.fail);
});
});
});
});
......@@ -93,38 +93,6 @@ describe('Multi-file store tree actions', () => {
});
describe('error', () => {
it('dispatches branch not found actions when response is 404', done => {
const dispatch = jasmine.createSpy('dispatchSpy');
store.state.projects = {
'abc/def': {
web_url: `${gl.TEST_HOST}/files`,
},
};
mock.onGet(/(.*)/).replyOnce(404);
getFiles(
{
commit() {},
dispatch,
state: store.state,
},
{
projectId: 'abc/def',
branchId: 'master-testing',
},
)
.then(done.fail)
.catch(() => {
expect(dispatch.calls.argsFor(0)).toEqual([
'showBranchNotFoundError',
'master-testing',
]);
done();
});
});
it('dispatches error action', done => {
const dispatch = jasmine.createSpy('dispatchSpy');
......
......@@ -9,12 +9,15 @@ import actions, {
setErrorMessage,
deleteEntry,
renameEntry,
getBranchData,
} from '~/ide/stores/actions';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
import * as types from '~/ide/stores/mutation_types';
import router from '~/ide/ide_router';
import { resetStore, file } from '../helpers';
import testAction from '../../helpers/vuex_action_helper';
import MockAdapter from 'axios-mock-adapter';
describe('Multi-file store actions', () => {
beforeEach(() => {
......@@ -560,4 +563,65 @@ describe('Multi-file store actions', () => {
);
});
});
describe('getBranchData', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('error', () => {
let dispatch;
const callParams = [
{
commit() {},
state: store.state,
},
{
projectId: 'abc/def',
branchId: 'master-testing',
},
];
beforeEach(() => {
dispatch = jasmine.createSpy('dispatchSpy');
document.body.innerHTML += '<div class="flash-container"></div>';
});
afterEach(() => {
document.querySelector('.flash-container').remove();
});
it('passes the error further unchanged without dispatching any action when response is 404', done => {
mock.onGet(/(.*)/).replyOnce(404);
getBranchData(...callParams)
.then(done.fail)
.catch(e => {
expect(dispatch.calls.count()).toEqual(0);
expect(e.response.status).toEqual(404);
expect(document.querySelector('.flash-alert')).toBeNull();
done();
});
});
it('does not pass the error further and flashes an alert if error is not 404', done => {
mock.onGet(/(.*)/).replyOnce(418);
getBranchData(...callParams)
.then(done.fail)
.catch(e => {
expect(dispatch.calls.count()).toEqual(0);
expect(e.response).toBeUndefined();
expect(document.querySelector('.flash-alert')).not.toBeNull();
done();
});
});
});
});
});
......@@ -272,6 +272,7 @@ describe('IDE commit module actions', () => {
short_id: '123',
message: 'test message',
committed_date: 'date',
parent_ids: '321',
stats: {
additions: '1',
deletions: '2',
......@@ -463,5 +464,63 @@ describe('IDE commit module actions', () => {
.catch(done.fail);
});
});
describe('first commit of a branch', () => {
const COMMIT_RESPONSE = {
id: '123456',
short_id: '123',
message: 'test message',
committed_date: 'date',
parent_ids: [],
stats: {
additions: '1',
deletions: '2',
},
};
it('commits TOGGLE_EMPTY_STATE mutation on empty repo', done => {
spyOn(service, 'commit').and.returnValue(
Promise.resolve({
data: COMMIT_RESPONSE,
}),
);
spyOn(store, 'commit').and.callThrough();
store
.dispatch('commit/commitChanges')
.then(() => {
expect(store.commit.calls.allArgs()).toEqual(
jasmine.arrayContaining([
['TOGGLE_EMPTY_STATE', jasmine.any(Object), jasmine.any(Object)],
]),
);
done();
})
.catch(done.fail);
});
it('does not commmit TOGGLE_EMPTY_STATE mutation on existing project', done => {
COMMIT_RESPONSE.parent_ids.push('1234');
spyOn(service, 'commit').and.returnValue(
Promise.resolve({
data: COMMIT_RESPONSE,
}),
);
spyOn(store, 'commit').and.callThrough();
store
.dispatch('commit/commitChanges')
.then(() => {
expect(store.commit.calls.allArgs()).not.toEqual(
jasmine.arrayContaining([
['TOGGLE_EMPTY_STATE', jasmine.any(Object), jasmine.any(Object)],
]),
);
done();
})
.catch(done.fail);
});
});
});
});
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