Commit 2cffa02e authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'ide-file-templates' into 'master'

Added file templates to the Web IDE

Closes #47947

See merge request gitlab-org/gitlab-ce!21245
parents 63620ec7 21ed2da0
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import Dropdown from './dropdown.vue';
export default {
components: {
Dropdown,
},
computed: {
...mapGetters(['activeFile']),
...mapGetters('fileTemplates', ['templateTypes']),
...mapState('fileTemplates', ['selectedTemplateType', 'updateSuccess']),
showTemplatesDropdown() {
return Object.keys(this.selectedTemplateType).length > 0;
},
},
watch: {
activeFile: 'setInitialType',
},
mounted() {
this.setInitialType();
},
methods: {
...mapActions('fileTemplates', [
'setSelectedTemplateType',
'fetchTemplate',
'undoFileTemplate',
]),
setInitialType() {
const initialTemplateType = this.templateTypes.find(t => t.name === this.activeFile.name);
if (initialTemplateType) {
this.setSelectedTemplateType(initialTemplateType);
}
},
selectTemplateType(templateType) {
this.setSelectedTemplateType(templateType);
},
selectTemplate(template) {
this.fetchTemplate(template);
},
undo() {
this.undoFileTemplate();
},
},
};
</script>
<template>
<div class="d-flex align-items-center ide-file-templates">
<strong class="append-right-default">
{{ __('File templates') }}
</strong>
<dropdown
:data="templateTypes"
:label="selectedTemplateType.name || __('Choose a type...')"
class="mr-2"
@click="selectTemplateType"
/>
<dropdown
v-if="showTemplatesDropdown"
:label="__('Choose a template...')"
:is-async-data="true"
:searchable="true"
:title="__('File templates')"
class="mr-2"
@click="selectTemplate"
/>
<transition name="fade">
<button
v-show="updateSuccess"
type="button"
class="btn btn-default"
@click="undo"
>
{{ __('Undo') }}
</button>
</transition>
</div>
</template>
<script>
import $ from 'jquery';
import { mapActions, mapState } from 'vuex';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
export default {
components: {
DropdownButton,
LoadingIcon,
},
props: {
data: {
type: Array,
required: false,
default: () => [],
},
label: {
type: String,
required: true,
},
title: {
type: String,
required: false,
default: null,
},
isAsyncData: {
type: Boolean,
required: false,
default: false,
},
searchable: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
search: '',
};
},
computed: {
...mapState('fileTemplates', ['templates', 'isLoading']),
outputData() {
return (this.isAsyncData ? this.templates : this.data).filter(t => {
if (!this.searchable) return true;
return t.name.toLowerCase().indexOf(this.search.toLowerCase()) >= 0;
});
},
showLoading() {
return this.isAsyncData ? this.isLoading : false;
},
},
mounted() {
$(this.$el).on('show.bs.dropdown', this.fetchTemplatesIfAsync);
},
beforeDestroy() {
$(this.$el).off('show.bs.dropdown', this.fetchTemplatesIfAsync);
},
methods: {
...mapActions('fileTemplates', ['fetchTemplateTypes']),
fetchTemplatesIfAsync() {
if (this.isAsyncData) {
this.fetchTemplateTypes();
}
},
clickItem(item) {
this.$emit('click', item);
},
},
};
</script>
<template>
<div class="dropdown">
<dropdown-button
:toggle-text="label"
data-display="static"
/>
<div class="dropdown-menu pb-0">
<div
v-if="title"
class="dropdown-title ml-0 mr-0"
>
{{ title }}
</div>
<div
v-if="!showLoading && searchable"
class="dropdown-input"
>
<input
v-model="search"
:placeholder="__('Filter...')"
type="search"
class="dropdown-input-field"
/>
<i
aria-hidden="true"
class="fa fa-search dropdown-input-search"
></i>
</div>
<div class="dropdown-content">
<loading-icon
v-if="showLoading"
size="2"
/>
<ul v-else>
<li
v-for="(item, index) in outputData"
:key="index"
>
<button
type="button"
@click="clickItem(item)"
>
{{ item.name }}
</button>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import $ from 'jquery';
import { __ } from '~/locale';
import { mapActions, mapState } from 'vuex';
import { mapActions, mapState, mapGetters } from 'vuex';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { modalTypes } from '../../constants';
......@@ -15,6 +16,7 @@ export default {
},
computed: {
...mapState(['entryModal']),
...mapGetters('fileTemplates', ['templateTypes']),
entryName: {
get() {
if (this.entryModal.type === modalTypes.rename) {
......@@ -31,7 +33,9 @@ export default {
if (this.entryModal.type === modalTypes.tree) {
return __('Create new directory');
} else if (this.entryModal.type === modalTypes.rename) {
return this.entryModal.entry.type === modalTypes.tree ? __('Rename folder') : __('Rename file');
return this.entryModal.entry.type === modalTypes.tree
? __('Rename folder')
: __('Rename file');
}
return __('Create new file');
......@@ -40,11 +44,16 @@ export default {
if (this.entryModal.type === modalTypes.tree) {
return __('Create directory');
} else if (this.entryModal.type === modalTypes.rename) {
return this.entryModal.entry.type === modalTypes.tree ? __('Rename folder') : __('Rename file');
return this.entryModal.entry.type === modalTypes.tree
? __('Rename folder')
: __('Rename file');
}
return __('Create file');
},
isCreatingNew() {
return this.entryModal.type !== modalTypes.rename;
},
},
methods: {
...mapActions(['createTempEntry', 'renameEntry']),
......@@ -61,6 +70,14 @@ export default {
});
}
},
createFromTemplate(template) {
this.createTempEntry({
name: template.name,
type: this.entryModal.type,
});
$('#ide-new-entry').modal('toggle');
},
focusInput() {
this.$refs.fieldName.focus();
},
......@@ -77,6 +94,7 @@ export default {
:header-title-text="modalTitle"
:footer-primary-button-text="buttonLabel"
footer-primary-button-variant="success"
modal-size="lg"
@submit="submitForm"
@open="focusInput"
@closed="closedModal"
......@@ -84,16 +102,35 @@ export default {
<div
class="form-group row"
>
<label class="label-bold col-form-label col-sm-3">
<label class="label-bold col-form-label col-sm-2">
{{ __('Name') }}
</label>
<div class="col-sm-9">
<div class="col-sm-10">
<input
ref="fieldName"
v-model="entryName"
type="text"
class="form-control"
placeholder="/dir/file_name"
/>
<ul
v-if="isCreatingNew"
class="prepend-top-default list-inline"
>
<li
v-for="(template, index) in templateTypes"
:key="index"
class="list-inline-item"
>
<button
type="button"
class="btn btn-missing p-1 pr-2 pl-2"
@click="createFromTemplate(template)"
>
{{ template.name }}
</button>
</li>
</ul>
</div>
</div>
</gl-modal>
......
......@@ -6,12 +6,14 @@ import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
import FileTemplatesBar from './file_templates/bar.vue';
export default {
components: {
ContentViewer,
DiffViewer,
ExternalLink,
FileTemplatesBar,
},
props: {
file: {
......@@ -34,6 +36,7 @@ export default {
'isCommitModeActive',
'isReviewModeActive',
]),
...mapGetters('fileTemplates', ['showFileTemplatesBar']),
shouldHideEditor() {
return this.file && this.file.binary && !this.file.content;
},
......@@ -216,7 +219,7 @@ export default {
id="ide"
class="blob-viewer-container blob-editor-container"
>
<div class="ide-mode-tabs clearfix" >
<div class="ide-mode-tabs clearfix">
<ul
v-if="!shouldHideEditor && isEditModeActive"
class="nav-links float-left"
......@@ -249,6 +252,9 @@ export default {
:file="file"
/>
</div>
<file-templates-bar
v-if="showFileTemplatesBar(file.name)"
/>
<div
v-show="!shouldHideEditor && file.viewMode ==='editor'"
ref="editor"
......
......@@ -206,6 +206,7 @@ export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath = null }) => {
const entry = state.entries[entryPath || path];
commit(types.RENAME_ENTRY, { path, name, entryPath });
if (entry.type === 'tree') {
......@@ -214,7 +215,7 @@ export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath
);
}
if (!entryPath) {
if (!entryPath && !entry.tempFile) {
dispatch('deleteEntry', path);
}
};
......
......@@ -8,6 +8,7 @@ import commitModule from './modules/commit';
import pipelines from './modules/pipelines';
import mergeRequests from './modules/merge_requests';
import branches from './modules/branches';
import fileTemplates from './modules/file_templates';
Vue.use(Vuex);
......@@ -22,6 +23,7 @@ export const createStore = () =>
pipelines,
mergeRequests,
branches,
fileTemplates: fileTemplates(),
},
});
......
import Api from '~/api';
import { __ } from '~/locale';
import * as types from './mutation_types';
import eventHub from '../../../eventhub';
export const requestTemplateTypes = ({ commit }) => commit(types.REQUEST_TEMPLATE_TYPES);
export const receiveTemplateTypesError = ({ commit, dispatch }) => {
......@@ -31,9 +32,23 @@ export const fetchTemplateTypes = ({ dispatch, state }) => {
.catch(() => dispatch('receiveTemplateTypesError'));
};
export const setSelectedTemplateType = ({ commit }, type) =>
export const setSelectedTemplateType = ({ commit, dispatch, rootGetters }, type) => {
commit(types.SET_SELECTED_TEMPLATE_TYPE, type);
if (rootGetters.activeFile.prevPath === type.name) {
dispatch('discardFileChanges', rootGetters.activeFile.path, { root: true });
} else if (rootGetters.activeFile.name !== type.name) {
dispatch(
'renameEntry',
{
path: rootGetters.activeFile.path,
name: type.name,
},
{ root: true },
);
}
};
export const receiveTemplateError = ({ dispatch }, template) => {
dispatch(
'setErrorMessage',
......@@ -69,6 +84,7 @@ export const setFileTemplate = ({ dispatch, commit, rootGetters }, template) =>
{ root: true },
);
commit(types.SET_UPDATE_SUCCESS, true);
eventHub.$emit(`editor.update.model.new.content.${rootGetters.activeFile.key}`, template.content);
};
export const undoFileTemplate = ({ dispatch, commit, rootGetters }) => {
......@@ -76,6 +92,12 @@ export const undoFileTemplate = ({ dispatch, commit, rootGetters }) => {
dispatch('changeFileContent', { path: file.path, content: file.raw }, { root: true });
commit(types.SET_UPDATE_SUCCESS, false);
eventHub.$emit(`editor.update.model.new.content.${file.key}`, file.raw);
if (file.prevPath) {
dispatch('discardFileChanges', file.path, { root: true });
}
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
......
import { activityBarViews } from '../../../constants';
export const templateTypes = () => [
{
name: '.gitlab-ci.yml',
......@@ -17,7 +19,8 @@ export const templateTypes = () => [
},
];
export const showFileTemplatesBar = (_, getters) => name =>
getters.templateTypes.find(t => t.name === name);
export const showFileTemplatesBar = (_, getters, rootState) => name =>
getters.templateTypes.find(t => t.name === name) &&
rootState.currentActivityView === activityBarViews.edit;
export default () => {};
......@@ -3,10 +3,10 @@ import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
export default {
export default () => ({
namespaced: true,
actions,
state: createState(),
getters,
mutations,
};
});
import Vue from 'vue';
import * as types from './mutation_types';
import projectMutations from './mutations/project';
import mergeRequestMutation from './mutations/merge_request';
......@@ -226,7 +227,7 @@ export default {
path: newPath,
name: entryPath ? oldEntry.name : name,
tempFile: true,
prevPath: oldEntry.path,
prevPath: oldEntry.tempFile ? null : oldEntry.path,
url: oldEntry.url.replace(new RegExp(`${oldEntry.path}/?$`), newPath),
tree: [],
parentPath,
......@@ -245,6 +246,20 @@ export default {
if (newEntry.type === 'blob') {
state.changedFiles = state.changedFiles.concat(newEntry);
}
if (state.entries[newPath].opened) {
state.openFiles.push(state.entries[newPath]);
}
if (oldEntry.tempFile) {
const filterMethod = f => f.path !== oldEntry.path;
state.openFiles = state.openFiles.filter(filterMethod);
state.changedFiles = state.changedFiles.filter(filterMethod);
parent.tree = parent.tree.filter(filterMethod);
Vue.delete(state.entries, oldEntry.path);
}
},
...projectMutations,
...mergeRequestMutation,
......
......@@ -55,7 +55,7 @@ export default {
f => f.path === file.path && f.pending && !(f.tempFile && !f.prevPath),
);
if (file.tempFile) {
if (file.tempFile && file.content === '') {
Object.assign(state.entries[file.path], {
content: raw,
});
......
......@@ -1442,3 +1442,17 @@ $ide-tree-text-start: $ide-activity-bar-width + $ide-tree-padding;
top: 50%;
transform: translateY(-50%);
}
.ide-file-templates {
padding: $grid-size $gl-padding;
background-color: $gray-light;
border-bottom: 1px solid $white-dark;
.dropdown {
min-width: 180px;
}
.dropdown-content {
max-height: 222px;
}
}
---
title: Added file templates to the Web IDE
merge_request:
author:
type: added
......@@ -1170,6 +1170,12 @@ msgstr ""
msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
msgstr ""
msgid "Choose a template..."
msgstr ""
msgid "Choose a type..."
msgstr ""
msgid "Choose any color."
msgstr ""
......@@ -2694,6 +2700,9 @@ msgstr ""
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
msgid "File templates"
msgstr ""
msgid "Files"
msgstr ""
......@@ -2706,6 +2715,9 @@ msgstr ""
msgid "Filter by commit message"
msgstr ""
msgid "Filter..."
msgstr ""
msgid "Find by path"
msgstr ""
......@@ -6199,6 +6211,9 @@ msgstr ""
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Undo"
msgstr ""
msgid "Unlock"
msgstr ""
......
import Vue from 'vue';
import { createStore } from '~/ide/stores';
import Bar from '~/ide/components/file_templates/bar.vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore, file } from '../../helpers';
describe('IDE file templates bar component', () => {
let Component;
let vm;
beforeAll(() => {
Component = Vue.extend(Bar);
});
beforeEach(() => {
const store = createStore();
store.state.openFiles.push({
...file('file'),
opened: true,
active: true,
});
vm = mountComponentWithStore(Component, { store });
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
describe('template type dropdown', () => {
it('renders dropdown component', () => {
expect(vm.$el.querySelector('.dropdown').textContent).toContain('Choose a type');
});
it('calls setSelectedTemplateType when clicking item', () => {
spyOn(vm, 'setSelectedTemplateType').and.stub();
vm.$el.querySelector('.dropdown-content button').click();
expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({
name: '.gitlab-ci.yml',
key: 'gitlab_ci_ymls',
});
});
});
describe('template dropdown', () => {
beforeEach(done => {
vm.$store.state.fileTemplates.templates = [
{
name: 'test',
},
];
vm.$store.state.fileTemplates.selectedTemplateType = {
name: '.gitlab-ci.yml',
key: 'gitlab_ci_ymls',
};
vm.$nextTick(done);
});
it('renders dropdown component', () => {
expect(vm.$el.querySelectorAll('.dropdown')[1].textContent).toContain('Choose a template');
});
it('calls fetchTemplate on click', () => {
spyOn(vm, 'fetchTemplate').and.stub();
vm.$el
.querySelectorAll('.dropdown-content')[1]
.querySelector('button')
.click();
expect(vm.fetchTemplate).toHaveBeenCalledWith({
name: 'test',
});
});
});
it('shows undo button if updateSuccess is true', done => {
vm.$store.state.fileTemplates.updateSuccess = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.btn-default').style.display).not.toBe('none');
done();
});
});
it('calls undoFileTemplate when clicking undo button', () => {
spyOn(vm, 'undoFileTemplate').and.stub();
vm.$el.querySelector('.btn-default').click();
expect(vm.undoFileTemplate).toHaveBeenCalled();
});
it('calls setSelectedTemplateType if activeFile name matches a template', done => {
const fileName = '.gitlab-ci.yml';
spyOn(vm, 'setSelectedTemplateType');
vm.$store.state.openFiles[0].name = fileName;
vm.setInitialType();
vm.$nextTick(() => {
expect(vm.setSelectedTemplateType).toHaveBeenCalledWith({
name: fileName,
key: 'gitlab_ci_ymls',
});
done();
});
});
});
import $ from 'jquery';
import Vue from 'vue';
import { createStore } from '~/ide/stores';
import Dropdown from '~/ide/components/file_templates/dropdown.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../../helpers';
describe('IDE file templates dropdown component', () => {
let Component;
let vm;
beforeAll(() => {
Component = Vue.extend(Dropdown);
});
beforeEach(() => {
const store = createStore();
vm = createComponentWithStore(Component, store, {
label: 'Test',
}).$mount();
});
afterEach(() => {
vm.$destroy();
resetStore(vm.$store);
});
describe('async', () => {
beforeEach(() => {
vm.isAsyncData = true;
});
it('calls async store method on Bootstrap dropdown event', () => {
spyOn(vm, 'fetchTemplateTypes').and.stub();
$(vm.$el).trigger('show.bs.dropdown');
expect(vm.fetchTemplateTypes).toHaveBeenCalled();
});
it('renders templates when async', done => {
vm.$store.state.fileTemplates.templates = [
{
name: 'test',
},
];
vm.$nextTick(() => {
expect(vm.$el.querySelector('.dropdown-content').textContent).toContain('test');
done();
});
});
it('renders loading icon when isLoading is true', done => {
vm.$store.state.fileTemplates.isLoading = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
done();
});
});
it('searches template data', () => {
vm.$store.state.fileTemplates.templates = [
{
name: 'test',
},
];
vm.searchable = true;
vm.search = 'hello';
expect(vm.outputData).toEqual([]);
});
it('does not filter data is searchable is false', () => {
vm.$store.state.fileTemplates.templates = [
{
name: 'test',
},
];
vm.search = 'hello';
expect(vm.outputData).toEqual([
{
name: 'test',
},
]);
});
it('calls clickItem on click', done => {
spyOn(vm, 'clickItem').and.stub();
vm.$store.state.fileTemplates.templates = [
{
name: 'test',
},
];
vm.$nextTick(() => {
vm.$el.querySelector('.dropdown-content button').click();
expect(vm.clickItem).toHaveBeenCalledWith({
name: 'test',
});
done();
});
});
it('renders input when searchable is true', done => {
vm.searchable = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.dropdown-input')).not.toBe(null);
done();
});
});
it('does not render input when searchable is true & showLoading is true', done => {
vm.searchable = true;
vm.$store.state.fileTemplates.isLoading = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.dropdown-input')).toBe(null);
done();
});
});
});
describe('sync', () => {
beforeEach(done => {
vm.data = [
{
name: 'test sync',
},
];
vm.$nextTick(done);
});
it('renders props data', () => {
expect(vm.$el.querySelector('.dropdown-content').textContent).toContain('test sync');
});
it('renders input when searchable is true', done => {
vm.searchable = true;
vm.$nextTick(() => {
expect(vm.$el.querySelector('.dropdown-input')).not.toBe(null);
done();
});
});
it('calls clickItem on click', done => {
spyOn(vm, 'clickItem').and.stub();
vm.$nextTick(() => {
vm.$el.querySelector('.dropdown-content button').click();
expect(vm.clickItem).toHaveBeenCalledWith({
name: 'test sync',
});
done();
});
});
it('searches template data', () => {
vm.searchable = true;
vm.search = 'hello';
expect(vm.outputData).toEqual([]);
});
it('does not filter data is searchable is false', () => {
vm.search = 'hello';
expect(vm.outputData).toEqual([
{
name: 'test sync',
},
]);
});
it('renders dropdown title', done => {
vm.title = 'Test title';
vm.$nextTick(() => {
expect(vm.$el.querySelector('.dropdown-title').textContent).toContain('Test title');
done();
});
});
});
});
......@@ -5,6 +5,7 @@ import commitState from '~/ide/stores/modules/commit/state';
import mergeRequestsState from '~/ide/stores/modules/merge_requests/state';
import pipelinesState from '~/ide/stores/modules/pipelines/state';
import branchesState from '~/ide/stores/modules/branches/state';
import fileTemplatesState from '~/ide/stores/modules/file_templates/state';
export const resetStore = store => {
const newState = {
......@@ -13,6 +14,7 @@ export const resetStore = store => {
mergeRequests: mergeRequestsState(),
pipelines: pipelinesState(),
branches: branchesState(),
fileTemplates: fileTemplatesState(),
};
store.replaceState(newState);
};
......
......@@ -148,14 +148,66 @@ describe('IDE file templates actions', () => {
});
describe('setSelectedTemplateType', () => {
it('commits SET_SELECTED_TEMPLATE_TYPE', done => {
testAction(
actions.setSelectedTemplateType,
'test',
state,
[{ type: types.SET_SELECTED_TEMPLATE_TYPE, payload: 'test' }],
[],
done,
it('commits SET_SELECTED_TEMPLATE_TYPE', () => {
const commit = jasmine.createSpy('commit');
const options = {
commit,
dispatch() {},
rootGetters: {
activeFile: {
name: 'test',
prevPath: '',
},
},
};
actions.setSelectedTemplateType(options, { name: 'test' });
expect(commit).toHaveBeenCalledWith(types.SET_SELECTED_TEMPLATE_TYPE, { name: 'test' });
});
it('dispatches discardFileChanges if prevPath matches templates name', () => {
const dispatch = jasmine.createSpy('dispatch');
const options = {
commit() {},
dispatch,
rootGetters: {
activeFile: {
name: 'test',
path: 'test',
prevPath: 'test',
},
},
};
actions.setSelectedTemplateType(options, { name: 'test' });
expect(dispatch).toHaveBeenCalledWith('discardFileChanges', 'test', { root: true });
});
it('dispatches renameEntry if file name doesnt match', () => {
const dispatch = jasmine.createSpy('dispatch');
const options = {
commit() {},
dispatch,
rootGetters: {
activeFile: {
name: 'oldtest',
path: 'oldtest',
prevPath: '',
},
},
};
actions.setSelectedTemplateType(options, { name: 'test' });
expect(dispatch).toHaveBeenCalledWith(
'renameEntry',
{
path: 'oldtest',
name: 'test',
},
{ root: true },
);
});
});
......@@ -332,5 +384,20 @@ describe('IDE file templates actions', () => {
expect(commit).toHaveBeenCalledWith('SET_UPDATE_SUCCESS', false);
});
it('dispatches discardFileChanges if file has prevPath', () => {
const dispatch = jasmine.createSpy('dispatch');
const rootGetters = {
activeFile: { path: 'test', prevPath: 'newtest', raw: 'raw content' },
};
actions.undoFileTemplate({ dispatch, commit() {}, rootGetters });
expect(dispatch.calls.mostRecent().args).toEqual([
'discardFileChanges',
'test',
{ root: true },
]);
});
});
});
import createState from '~/ide/stores/state';
import { activityBarViews } from '~/ide/constants';
import * as getters from '~/ide/stores/modules/file_templates/getters';
describe('IDE file templates getters', () => {
......@@ -8,22 +10,49 @@ describe('IDE file templates getters', () => {
});
describe('showFileTemplatesBar', () => {
it('finds template type by name', () => {
let rootState;
beforeEach(() => {
rootState = createState();
});
it('returns true if template is found and currentActivityView is edit', () => {
rootState.currentActivityView = activityBarViews.edit;
expect(
getters.showFileTemplatesBar(null, {
getters.showFileTemplatesBar(
null,
{
templateTypes: getters.templateTypes(),
})('LICENSE'),
).toEqual({
name: 'LICENSE',
key: 'licenses',
},
rootState,
)('LICENSE'),
).toBe(true);
});
it('returns false if template is found and currentActivityView is not edit', () => {
rootState.currentActivityView = activityBarViews.commit;
expect(
getters.showFileTemplatesBar(
null,
{
templateTypes: getters.templateTypes(),
},
rootState,
)('LICENSE'),
).toBe(false);
});
it('returns undefined if not found', () => {
expect(
getters.showFileTemplatesBar(null, {
getters.showFileTemplatesBar(
null,
{
templateTypes: getters.templateTypes(),
})('test'),
},
rootState,
)('test'),
).toBe(undefined);
});
});
......
......@@ -339,5 +339,13 @@ describe('Multi-file store mutations', () => {
expect(localState.entries.parentPath.tree.length).toBe(1);
});
it('adds to openFiles if previously opened', () => {
localState.entries.oldPath.opened = true;
mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
expect(localState.openFiles).toEqual([localState.entries.newPath]);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment