Merge branch 'ide-file-templates-store' into 'master'
Added store for file templates in the Web IDE See merge request gitlab-org/gitlab-ce!21272
This commit is contained in:
commit
726f5cdc2b
|
@ -15,6 +15,7 @@ const Api = {
|
|||
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
|
||||
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
|
||||
groupLabelsPath: '/groups/:namespace_path/-/labels',
|
||||
templatesPath: '/api/:version/templates/:key',
|
||||
licensePath: '/api/:version/templates/licenses/:key',
|
||||
gitignorePath: '/api/:version/templates/gitignores/:key',
|
||||
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
|
||||
|
@ -265,6 +266,12 @@ const Api = {
|
|||
});
|
||||
},
|
||||
|
||||
templates(key, params = {}) {
|
||||
const url = Api.buildUrl(this.templatesPath).replace(':key', key);
|
||||
|
||||
return axios.get(url, { params });
|
||||
},
|
||||
|
||||
buildUrl(url) {
|
||||
let urlRoot = '';
|
||||
if (gon.relative_url_root != null) {
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import Api from '~/api';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export const requestTemplateTypes = ({ commit }) => commit(types.REQUEST_TEMPLATE_TYPES);
|
||||
export const receiveTemplateTypesError = ({ commit, dispatch }) => {
|
||||
commit(types.RECEIVE_TEMPLATE_TYPES_ERROR);
|
||||
dispatch(
|
||||
'setErrorMessage',
|
||||
{
|
||||
text: __('Error loading template types.'),
|
||||
action: () =>
|
||||
dispatch('fetchTemplateTypes').then(() =>
|
||||
dispatch('setErrorMessage', null, { root: true }),
|
||||
),
|
||||
actionText: __('Please try again'),
|
||||
},
|
||||
{ root: true },
|
||||
);
|
||||
};
|
||||
export const receiveTemplateTypesSuccess = ({ commit }, templates) =>
|
||||
commit(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, templates);
|
||||
|
||||
export const fetchTemplateTypes = ({ dispatch, state }) => {
|
||||
if (!Object.keys(state.selectedTemplateType).length) return Promise.reject();
|
||||
|
||||
dispatch('requestTemplateTypes');
|
||||
|
||||
return Api.templates(state.selectedTemplateType.key)
|
||||
.then(({ data }) => dispatch('receiveTemplateTypesSuccess', data))
|
||||
.catch(() => dispatch('receiveTemplateTypesError'));
|
||||
};
|
||||
|
||||
export const setSelectedTemplateType = ({ commit }, type) =>
|
||||
commit(types.SET_SELECTED_TEMPLATE_TYPE, type);
|
||||
|
||||
export const receiveTemplateError = ({ dispatch }, template) => {
|
||||
dispatch(
|
||||
'setErrorMessage',
|
||||
{
|
||||
text: __('Error loading template.'),
|
||||
action: payload =>
|
||||
dispatch('fetchTemplateTypes', payload).then(() =>
|
||||
dispatch('setErrorMessage', null, { root: true }),
|
||||
),
|
||||
actionText: __('Please try again'),
|
||||
actionPayload: template,
|
||||
},
|
||||
{ root: true },
|
||||
);
|
||||
};
|
||||
|
||||
export const fetchTemplate = ({ dispatch, state }, template) => {
|
||||
if (template.content) {
|
||||
return dispatch('setFileTemplate', template);
|
||||
}
|
||||
|
||||
return Api.templates(`${state.selectedTemplateType.key}/${template.key || template.name}`)
|
||||
.then(({ data }) => {
|
||||
dispatch('setFileTemplate', data);
|
||||
})
|
||||
.catch(() => dispatch('receiveTemplateError', template));
|
||||
};
|
||||
|
||||
export const setFileTemplate = ({ dispatch, commit, rootGetters }, template) => {
|
||||
dispatch(
|
||||
'changeFileContent',
|
||||
{ path: rootGetters.activeFile.path, content: template.content },
|
||||
{ root: true },
|
||||
);
|
||||
commit(types.SET_UPDATE_SUCCESS, true);
|
||||
};
|
||||
|
||||
export const undoFileTemplate = ({ dispatch, commit, rootGetters }) => {
|
||||
const file = rootGetters.activeFile;
|
||||
|
||||
dispatch('changeFileContent', { path: file.path, content: file.raw }, { root: true });
|
||||
commit(types.SET_UPDATE_SUCCESS, false);
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
|
@ -0,0 +1,23 @@
|
|||
export const templateTypes = () => [
|
||||
{
|
||||
name: '.gitlab-ci.yml',
|
||||
key: 'gitlab_ci_ymls',
|
||||
},
|
||||
{
|
||||
name: '.gitignore',
|
||||
key: 'gitignores',
|
||||
},
|
||||
{
|
||||
name: 'LICENSE',
|
||||
key: 'licenses',
|
||||
},
|
||||
{
|
||||
name: 'Dockerfile',
|
||||
key: 'dockerfiles',
|
||||
},
|
||||
];
|
||||
|
||||
export const showFileTemplatesBar = (_, getters) => name =>
|
||||
getters.templateTypes.find(t => t.name === name);
|
||||
|
||||
export default () => {};
|
|
@ -0,0 +1,12 @@
|
|||
import createState from './state';
|
||||
import * as actions from './actions';
|
||||
import * as getters from './getters';
|
||||
import mutations from './mutations';
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
actions,
|
||||
state: createState(),
|
||||
getters,
|
||||
mutations,
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export const REQUEST_TEMPLATE_TYPES = 'REQUEST_TEMPLATE_TYPES';
|
||||
export const RECEIVE_TEMPLATE_TYPES_ERROR = 'RECEIVE_TEMPLATE_TYPES_ERROR';
|
||||
export const RECEIVE_TEMPLATE_TYPES_SUCCESS = 'RECEIVE_TEMPLATE_TYPES_SUCCESS';
|
||||
|
||||
export const SET_SELECTED_TEMPLATE_TYPE = 'SET_SELECTED_TEMPLATE_TYPE';
|
||||
|
||||
export const SET_UPDATE_SUCCESS = 'SET_UPDATE_SUCCESS';
|
|
@ -0,0 +1,21 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.REQUEST_TEMPLATE_TYPES](state) {
|
||||
state.isLoading = true;
|
||||
},
|
||||
[types.RECEIVE_TEMPLATE_TYPES_ERROR](state) {
|
||||
state.isLoading = false;
|
||||
},
|
||||
[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, templates) {
|
||||
state.isLoading = false;
|
||||
state.templates = templates;
|
||||
},
|
||||
[types.SET_SELECTED_TEMPLATE_TYPE](state, type) {
|
||||
state.selectedTemplateType = type;
|
||||
},
|
||||
[types.SET_UPDATE_SUCCESS](state, success) {
|
||||
state.updateSuccess = success;
|
||||
},
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
export default () => ({
|
||||
isLoading: false,
|
||||
templates: [],
|
||||
selectedTemplateType: {},
|
||||
updateSuccess: false,
|
||||
});
|
|
@ -2508,6 +2508,12 @@ msgstr ""
|
|||
msgid "Error loading project data. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Error loading template types."
|
||||
msgstr ""
|
||||
|
||||
msgid "Error loading template."
|
||||
msgstr ""
|
||||
|
||||
msgid "Error occurred when toggling the notification subscription"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import createState from '~/ide/stores/modules/file_templates/state';
|
||||
import * as actions from '~/ide/stores/modules/file_templates/actions';
|
||||
import * as types from '~/ide/stores/modules/file_templates/mutation_types';
|
||||
import testAction from 'spec/helpers/vuex_action_helper';
|
||||
|
||||
describe('IDE file templates actions', () => {
|
||||
let state;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = createState();
|
||||
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
describe('requestTemplateTypes', () => {
|
||||
it('commits REQUEST_TEMPLATE_TYPES', done => {
|
||||
testAction(
|
||||
actions.requestTemplateTypes,
|
||||
null,
|
||||
state,
|
||||
[{ type: types.REQUEST_TEMPLATE_TYPES }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveTemplateTypesError', () => {
|
||||
it('commits RECEIVE_TEMPLATE_TYPES_ERROR and dispatches setErrorMessage', done => {
|
||||
testAction(
|
||||
actions.receiveTemplateTypesError,
|
||||
null,
|
||||
state,
|
||||
[{ type: types.RECEIVE_TEMPLATE_TYPES_ERROR }],
|
||||
[
|
||||
{
|
||||
type: 'setErrorMessage',
|
||||
payload: {
|
||||
action: jasmine.any(Function),
|
||||
actionText: 'Please try again',
|
||||
text: 'Error loading template types.',
|
||||
},
|
||||
},
|
||||
],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveTemplateTypesSuccess', () => {
|
||||
it('commits RECEIVE_TEMPLATE_TYPES_SUCCESS', done => {
|
||||
testAction(
|
||||
actions.receiveTemplateTypesSuccess,
|
||||
'test',
|
||||
state,
|
||||
[{ type: types.RECEIVE_TEMPLATE_TYPES_SUCCESS, payload: 'test' }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchTemplateTypes', () => {
|
||||
describe('success', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(/api\/(.*)\/templates\/licenses/).replyOnce(200, [
|
||||
{
|
||||
name: 'MIT',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects if selectedTemplateType is empty', done => {
|
||||
const dispatch = jasmine.createSpy('dispatch');
|
||||
|
||||
actions
|
||||
.fetchTemplateTypes({ dispatch, state })
|
||||
.then(done.fail)
|
||||
.catch(() => {
|
||||
expect(dispatch).not.toHaveBeenCalled();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('dispatches actions', done => {
|
||||
state.selectedTemplateType = {
|
||||
key: 'licenses',
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.fetchTemplateTypes,
|
||||
null,
|
||||
state,
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'requestTemplateTypes',
|
||||
},
|
||||
{
|
||||
type: 'receiveTemplateTypesSuccess',
|
||||
payload: [
|
||||
{
|
||||
name: 'MIT',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(/api\/(.*)\/templates\/licenses/).replyOnce(500);
|
||||
});
|
||||
|
||||
it('dispatches actions', done => {
|
||||
state.selectedTemplateType = {
|
||||
key: 'licenses',
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.fetchTemplateTypes,
|
||||
null,
|
||||
state,
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'requestTemplateTypes',
|
||||
},
|
||||
{
|
||||
type: 'receiveTemplateTypesError',
|
||||
},
|
||||
],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSelectedTemplateType', () => {
|
||||
it('commits SET_SELECTED_TEMPLATE_TYPE', done => {
|
||||
testAction(
|
||||
actions.setSelectedTemplateType,
|
||||
'test',
|
||||
state,
|
||||
[{ type: types.SET_SELECTED_TEMPLATE_TYPE, payload: 'test' }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('receiveTemplateError', () => {
|
||||
it('dispatches setErrorMessage', done => {
|
||||
testAction(
|
||||
actions.receiveTemplateError,
|
||||
'test',
|
||||
state,
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'setErrorMessage',
|
||||
payload: {
|
||||
action: jasmine.any(Function),
|
||||
actionText: 'Please try again',
|
||||
text: 'Error loading template.',
|
||||
actionPayload: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchTemplate', () => {
|
||||
describe('success', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(/api\/(.*)\/templates\/licenses\/mit/).replyOnce(200, {
|
||||
content: 'MIT content',
|
||||
});
|
||||
mock.onGet(/api\/(.*)\/templates\/licenses\/testing/).replyOnce(200, {
|
||||
content: 'testing content',
|
||||
});
|
||||
});
|
||||
|
||||
it('dispatches setFileTemplate if template already has content', done => {
|
||||
const template = {
|
||||
content: 'already has content',
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.fetchTemplate,
|
||||
template,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'setFileTemplate', payload: template }],
|
||||
done,
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatches success', done => {
|
||||
const template = {
|
||||
key: 'mit',
|
||||
};
|
||||
|
||||
state.selectedTemplateType = {
|
||||
key: 'licenses',
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.fetchTemplate,
|
||||
template,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'setFileTemplate', payload: { content: 'MIT content' } }],
|
||||
done,
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatches success and uses name key for API call', done => {
|
||||
const template = {
|
||||
name: 'testing',
|
||||
};
|
||||
|
||||
state.selectedTemplateType = {
|
||||
key: 'licenses',
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.fetchTemplate,
|
||||
template,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'setFileTemplate', payload: { content: 'testing content' } }],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(/api\/(.*)\/templates\/licenses\/mit/).replyOnce(500);
|
||||
});
|
||||
|
||||
it('dispatches error', done => {
|
||||
const template = {
|
||||
name: 'testing',
|
||||
};
|
||||
|
||||
state.selectedTemplateType = {
|
||||
key: 'licenses',
|
||||
};
|
||||
|
||||
testAction(
|
||||
actions.fetchTemplate,
|
||||
template,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'receiveTemplateError', payload: template }],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setFileTemplate', () => {
|
||||
it('dispatches changeFileContent', () => {
|
||||
const dispatch = jasmine.createSpy('dispatch');
|
||||
const commit = jasmine.createSpy('commit');
|
||||
const rootGetters = {
|
||||
activeFile: { path: 'test' },
|
||||
};
|
||||
|
||||
actions.setFileTemplate({ dispatch, commit, rootGetters }, { content: 'content' });
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(
|
||||
'changeFileContent',
|
||||
{ path: 'test', content: 'content' },
|
||||
{ root: true },
|
||||
);
|
||||
});
|
||||
|
||||
it('commits SET_UPDATE_SUCCESS', () => {
|
||||
const dispatch = jasmine.createSpy('dispatch');
|
||||
const commit = jasmine.createSpy('commit');
|
||||
const rootGetters = {
|
||||
activeFile: { path: 'test' },
|
||||
};
|
||||
|
||||
actions.setFileTemplate({ dispatch, commit, rootGetters }, { content: 'content' });
|
||||
|
||||
expect(commit).toHaveBeenCalledWith('SET_UPDATE_SUCCESS', true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('undoFileTemplate', () => {
|
||||
it('dispatches changeFileContent', () => {
|
||||
const dispatch = jasmine.createSpy('dispatch');
|
||||
const commit = jasmine.createSpy('commit');
|
||||
const rootGetters = {
|
||||
activeFile: { path: 'test', raw: 'raw content' },
|
||||
};
|
||||
|
||||
actions.undoFileTemplate({ dispatch, commit, rootGetters });
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(
|
||||
'changeFileContent',
|
||||
{ path: 'test', content: 'raw content' },
|
||||
{ root: true },
|
||||
);
|
||||
});
|
||||
|
||||
it('commits SET_UPDATE_SUCCESS', () => {
|
||||
const dispatch = jasmine.createSpy('dispatch');
|
||||
const commit = jasmine.createSpy('commit');
|
||||
const rootGetters = {
|
||||
activeFile: { path: 'test', raw: 'raw content' },
|
||||
};
|
||||
|
||||
actions.undoFileTemplate({ dispatch, commit, rootGetters });
|
||||
|
||||
expect(commit).toHaveBeenCalledWith('SET_UPDATE_SUCCESS', false);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
import * as getters from '~/ide/stores/modules/file_templates/getters';
|
||||
|
||||
describe('IDE file templates getters', () => {
|
||||
describe('templateTypes', () => {
|
||||
it('returns list of template types', () => {
|
||||
expect(getters.templateTypes().length).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('showFileTemplatesBar', () => {
|
||||
it('finds template type by name', () => {
|
||||
expect(
|
||||
getters.showFileTemplatesBar(null, {
|
||||
templateTypes: getters.templateTypes(),
|
||||
})('LICENSE'),
|
||||
).toEqual({
|
||||
name: 'LICENSE',
|
||||
key: 'licenses',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns undefined if not found', () => {
|
||||
expect(
|
||||
getters.showFileTemplatesBar(null, {
|
||||
templateTypes: getters.templateTypes(),
|
||||
})('test'),
|
||||
).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
import createState from '~/ide/stores/modules/file_templates/state';
|
||||
import * as types from '~/ide/stores/modules/file_templates/mutation_types';
|
||||
import mutations from '~/ide/stores/modules/file_templates/mutations';
|
||||
|
||||
describe('IDE file templates mutations', () => {
|
||||
let state;
|
||||
|
||||
beforeEach(() => {
|
||||
state = createState();
|
||||
});
|
||||
|
||||
describe(types.REQUEST_TEMPLATE_TYPES, () => {
|
||||
it('sets isLoading', () => {
|
||||
mutations[types.REQUEST_TEMPLATE_TYPES](state);
|
||||
|
||||
expect(state.isLoading).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(types.RECEIVE_TEMPLATE_TYPES_ERROR, () => {
|
||||
it('sets isLoading', () => {
|
||||
state.isLoading = true;
|
||||
|
||||
mutations[types.RECEIVE_TEMPLATE_TYPES_ERROR](state);
|
||||
|
||||
expect(state.isLoading).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, () => {
|
||||
it('sets isLoading to false', () => {
|
||||
state.isLoading = true;
|
||||
|
||||
mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, []);
|
||||
|
||||
expect(state.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
it('sets templates', () => {
|
||||
mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, ['test']);
|
||||
|
||||
expect(state.templates).toEqual(['test']);
|
||||
});
|
||||
});
|
||||
|
||||
describe(types.SET_SELECTED_TEMPLATE_TYPE, () => {
|
||||
it('sets selectedTemplateType', () => {
|
||||
mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type');
|
||||
|
||||
expect(state.selectedTemplateType).toBe('type');
|
||||
});
|
||||
});
|
||||
|
||||
describe(types.SET_UPDATE_SUCCESS, () => {
|
||||
it('sets updateSuccess', () => {
|
||||
mutations[types.SET_UPDATE_SUCCESS](state, true);
|
||||
|
||||
expect(state.updateSuccess).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue