Commit 5cf77d7f authored by Illya Klymov's avatar Illya Klymov

Merge branch '214893-update-custom-stages-form-specs' into 'master'

Clean up custom stage form specs

Closes #214893

See merge request gitlab-org/gitlab!30830
parents 7b4a0066 817f6d4d
......@@ -70,14 +70,9 @@ export default {
'endDate',
'medians',
]),
...mapState('customStages', [
'isSavingCustomStage',
'isCreatingCustomStage',
'isEditingCustomStage',
'formEvents',
'formErrors',
'formInitialData',
]),
// NOTE: formEvents are fetched in the same request as the list of stages (fetchGroupStagesAndEvents)
// so i think its ok to bind formEvents here even though its only used as a prop to the custom-stage-form
...mapState('customStages', ['isCreatingCustomStage', 'formEvents']),
...mapGetters([
'hasNoAccessError',
'currentGroupPath',
......@@ -106,9 +101,6 @@ export default {
isLoadingTypeOfWork() {
return this.isLoadingTasksByTypeChartTopLabels || this.isLoadingTasksByTypeChart;
},
isUpdatingCustomStage() {
return this.isEditingCustomStage && this.isSavingCustomStage;
},
hasDateRangeSet() {
return this.startDate && this.endDate;
},
......@@ -169,6 +161,7 @@ export default {
this.showCreateForm();
},
onShowEditStageForm(initData = {}) {
this.setSelectedStage(initData);
this.showEditForm(initData);
},
onCreateCustomStage(data) {
......@@ -299,7 +292,6 @@ export default {
:stages="activeStages"
:medians="medians"
:is-creating-custom-stage="isCreatingCustomStage"
:custom-stage-form-active="customStageFormActive"
:can-edit-stages="true"
:custom-ordering="enableCustomOrdering"
@reorderStage="onStageReorder"
......@@ -311,14 +303,8 @@ export default {
/>
</template>
<template v-if="customStageFormActive" #content>
<gl-loading-icon v-if="isUpdatingCustomStage" class="mt-4" size="md" />
<custom-stage-form
v-else
:events="formEvents"
:is-saving-custom-stage="isSavingCustomStage"
:initial-fields="formInitialData"
:is-editing-custom-stage="isEditingCustomStage"
:errors="formErrors"
@createStage="onCreateCustomStage"
@updateStage="onUpdateCustomStage"
@clearErrors="$emit('clearFormErrors')"
......
<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import { isEqual } from 'lodash';
import {
GlFormGroup,
......@@ -33,23 +33,37 @@ const defaultFields = {
endEventLabelId: null,
};
export const initializeFormData = ({ emptyFieldState, initialFields, errors }) => {
const defaultErrors = initialFields?.endEventIdentifier
? { ...emptyFieldState, endEventIdentifier: null }
const defaultErrors = {
id: [],
name: [],
startEventIdentifier: [],
startEventLabelId: [],
endEventIdentifier: [],
endEventLabelId: [],
};
const ERRORS = {
START_EVENT_REQUIRED: s__('CustomCycleAnalytics|Please select a start event first'),
STAGE_NAME_EXISTS: s__('CustomCycleAnalytics|Stage name already exists'),
INVALID_EVENT_PAIRS: s__(
'CustomCycleAnalytics|Start event changed, please select a valid stop event',
),
};
export const initializeFormData = ({ emptyFieldState = defaultFields, fields, errors }) => {
const initErrors = fields?.endEventIdentifier
? defaultErrors
: {
...emptyFieldState,
endEventIdentifier:
initialFields && !initialFields.startEventIdentifier
? [s__('CustomCycleAnalytics|Please select a start event first')]
: null,
...defaultErrors,
endEventIdentifier: !fields?.startEventIdentifier ? [ERRORS.START_EVENT_REQUIRED] : [],
};
return {
fields: {
...emptyFieldState,
...initialFields,
...fields,
},
fieldErrors: {
...defaultErrors,
errors: {
...initErrors,
...errors,
},
};
......@@ -72,42 +86,23 @@ export default {
type: Array,
required: true,
},
initialFields: {
type: Object,
required: false,
default: () => {},
},
isSavingCustomStage: {
type: Boolean,
required: false,
default: false,
},
isEditingCustomStage: {
type: Boolean,
required: false,
default: false,
},
errors: {
type: Object,
required: false,
default: null,
},
},
data() {
const { initialFields = {}, errors = null } = this;
const { fields, fieldErrors } = initializeFormData({
emptyFieldState: defaultFields,
initialFields,
errors,
});
return {
labelEvents: getLabelEventsIdentifiers(this.events),
fields,
fieldErrors,
fields: {},
errors: [],
};
},
computed: {
...mapGetters(['hiddenStages']),
...mapState('customStages', [
'isLoading',
'isSavingCustomStage',
'isEditingCustomStage',
'formInitialData',
'formErrors',
]),
startEventOptions() {
return [
{ value: null, text: s__('CustomCycleAnalytics|Select start event') },
......@@ -132,8 +127,7 @@ export default {
},
hasErrors() {
return (
this.eventMismatchError ||
Object.values(this.fieldErrors).some(errArray => errArray?.length)
this.eventMismatchError || Object.values(this.errors).some(errArray => errArray?.length)
);
},
isComplete() {
......@@ -162,7 +156,7 @@ export default {
);
},
isDirty() {
return !isEqual(this.initialFields, this.fields) && !isEqual(defaultFields, this.fields);
return !isEqual(this.fields, this.formInitialData || defaultFields);
},
eventMismatchError() {
const {
......@@ -188,35 +182,39 @@ export default {
},
},
watch: {
initialFields(newFields) {
formInitialData(newFields = {}) {
this.fields = {
...defaultFields,
...newFields,
};
},
errors(newErrors) {
this.fieldErrors = {
...defaultFields,
formErrors(newErrors = {}) {
this.errors = {
...newErrors,
};
},
},
mounted() {
this.resetFields();
},
methods: {
handleCancel() {
const { initialFields = {}, errors = null } = this;
const formData = initializeFormData({
emptyFieldState: defaultFields,
initialFields,
errors,
resetFields() {
const { formInitialData, formErrors } = this;
const { fields, errors } = initializeFormData({
fields: formInitialData,
errors: formErrors,
});
this.$set(this, 'fields', formData.fields);
this.$set(this, 'fieldErrors', formData.fieldErrors);
this.fields = { ...fields };
this.errors = { ...errors };
},
handleCancel() {
this.resetFields();
this.$emit('cancel');
},
handleSave() {
const data = convertObjectPropsToSnakeCase(this.fields);
if (this.isEditingCustomStage) {
const { id } = this.initialFields;
const { id } = this.fields;
this.$emit(STAGE_ACTIONS.UPDATE, { ...data, id });
} else {
this.$emit(STAGE_ACTIONS.CREATE, data);
......@@ -229,31 +227,22 @@ export default {
this.fields[key] = null;
},
hasFieldErrors(key) {
return this.fieldErrors[key]?.length > 0;
return this.errors[key]?.length > 0;
},
fieldErrorMessage(key) {
return this.fieldErrors[key]?.join('\n');
return this.errors[key]?.join('\n');
},
onUpdateNameField() {
if (DEFAULT_STAGE_NAMES.includes(this.fields.name.toLowerCase())) {
this.$set(this.fieldErrors, 'name', [
s__('CustomCycleAnalytics|Stage name already exists'),
]);
} else {
this.$set(this.fieldErrors, 'name', []);
}
this.errors.name = DEFAULT_STAGE_NAMES.includes(this.fields.name.toLowerCase())
? [ERRORS.STAGE_NAME_EXISTS]
: [];
},
onUpdateStartEventField() {
const initVal = this.initialFields?.endEventIdentifier
? this.initialFields.endEventIdentifier
: null;
this.$set(this.fields, 'endEventIdentifier', initVal);
this.$set(this.fieldErrors, 'endEventIdentifier', [
s__('CustomCycleAnalytics|Start event changed, please select a valid stop event'),
]);
this.fields.endEventIdentifier = null;
this.errors.endEventIdentifier = [ERRORS.INVALID_EVENT_PAIRS];
},
onUpdateEndEventField() {
this.$set(this.fieldErrors, 'endEventIdentifier', null);
this.errors.endEventIdentifier = [];
},
handleRecoverStage(id) {
this.$emit(STAGE_ACTIONS.UPDATE, { id, hidden: false });
......@@ -262,7 +251,10 @@ export default {
};
</script>
<template>
<form class="custom-stage-form m-4 mt-0">
<div v-if="isLoading">
<gl-loading-icon class="mt-4" size="md" />
</div>
<form v-else class="custom-stage-form m-4 mt-0">
<div class="mb-1 d-flex flex-row justify-content-between">
<h4>{{ formTitle }}</h4>
<gl-dropdown :text="__('Recover hidden stage')" class="js-recover-hidden-stage-dropdown">
......@@ -366,7 +358,6 @@ export default {
</gl-form-group>
</div>
</div>
<div class="custom-stage-form-actions">
<button
:disabled="!isDirty"
......@@ -386,7 +377,6 @@ export default {
{{ saveStageText }}
</button>
</div>
<div class="mt-2">
<gl-sprintf
:message="
......
......@@ -29,10 +29,6 @@ export default {
type: Boolean,
required: true,
},
customStageFormActive: {
type: Boolean,
required: true,
},
canEditStages: {
type: Boolean,
required: true,
......@@ -106,7 +102,7 @@ export default {
<add-stage-button
v-if="canEditStages"
:class="$options.noDragClass"
:active="customStageFormActive"
:active="isCreatingCustomStage"
@showform="$emit('showAddStageForm')"
/>
</ul>
......
......@@ -15,15 +15,18 @@ export const hideForm = ({ commit }) => {
};
export const showCreateForm = ({ commit }) => {
commit(types.SET_LOADING);
commit(types.SET_FORM_INITIAL_DATA);
commit(types.SHOW_CREATE_FORM);
removeFlash();
};
export const showEditForm = ({ commit, dispatch }, selectedStage = {}) => {
commit(types.SET_LOADING);
commit(types.SET_FORM_INITIAL_DATA, selectedStage);
commit(types.SHOW_EDIT_FORM);
dispatch('setSelectedStage', selectedStage, { root: true });
dispatch('clearSavingCustomStage');
commit(types.SHOW_EDIT_FORM);
removeFlash();
};
......
export const SET_LOADING = 'SET_LOADING';
export const SET_STAGE_EVENTS = 'SET_STAGE_EVENTS';
export const SET_STAGE_FORM_ERRORS = 'SET_STAGE_FORM_ERRORS';
export const SET_FORM_INITIAL_DATA = 'SET_FORM_INITIAL_DATA';
export const SET_SAVING_CUSTOM_STAGE = 'SET_SAVING_CUSTOM_STAGE';
export const CLEAR_SAVING_CUSTOM_STAGE = 'CLEAR_SAVING_CUSTOM_STAGE';
export const HIDE_FORM = 'SHOW_FORM';
export const HIDE_FORM = 'HIDE_FORM';
export const SHOW_CREATE_FORM = 'SHOW_CREATE_FORM';
export const SHOW_EDIT_FORM = 'SHOW_EDIT_FORM';
export const CLEAR_FORM_ERRORS = 'CLEAR_FORM_ERRORS';
......
......@@ -31,21 +31,26 @@ export default {
state.formErrors = convertObjectPropsToCamelCase(errors, { deep: true });
},
[types.SET_FORM_INITIAL_DATA](state, rawStageData = null) {
state.formInitialData = extractFormFields(rawStageData);
state.formInitialData = rawStageData ? extractFormFields(rawStageData) : null;
},
[types.SET_SAVING_CUSTOM_STAGE](state) {
state.isSavingCustomStage = true;
},
[types.SET_LOADING](state) {
state.isLoadingCustomStage = true;
},
[types.CLEAR_SAVING_CUSTOM_STAGE](state) {
state.isSavingCustomStage = false;
},
[types.SHOW_CREATE_FORM](state) {
state.isLoadingCustomStage = false;
state.isEditingCustomStage = false;
state.isCreatingCustomStage = true;
state.formInitialData = null;
state.formErrors = null;
},
[types.SHOW_EDIT_FORM](state) {
state.isLoadingCustomStage = false;
state.isCreatingCustomStage = false;
state.isEditingCustomStage = true;
state.formErrors = null;
......
export default () => ({
isLoadingCustomStage: false,
isSavingCustomStage: false,
isCreatingCustomStage: false,
isEditingCustomStage: false,
......
......@@ -130,8 +130,8 @@ export const rawCustomStage = {
export const medians = stageMedians;
const { events: rawCustomStageEvents } = customizableStagesAndEvents;
const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase);
export const rawCustomStageEvents = customizableStagesAndEvents.events;
export const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase);
export const customStageLabelEvents = camelCasedStageEvents.filter(ev => ev.type === 'label');
export const customStageStartEvents = camelCasedStageEvents.filter(ev => ev.canBeStartEvent);
......
......@@ -4,8 +4,6 @@ import testAction from 'helpers/vuex_action_helper';
import * as getters from 'ee/analytics/cycle_analytics/store/getters';
import * as actions from 'ee/analytics/cycle_analytics/store/actions';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import * as customStageActions from 'ee/analytics/cycle_analytics/store/modules/custom_stages/actions';
import * as customStageTypes from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutation_types';
import createFlash from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import {
......@@ -684,136 +682,6 @@ describe('Cycle analytics actions', () => {
});
});
describe('createStage', () => {
describe('with valid data', () => {
const customStageData = {
startEventIdentifier: 'start_event',
endEventIdentifier: 'end_event',
name: 'cool-new-stage',
};
beforeEach(() => {
state = { ...state, selectedGroup };
mock.onPost(endpoints.baseStagesEndpointstageData).reply(201, customStageData);
});
it(`dispatches the 'receiveCreateStageSuccess' action`, () =>
testAction(
customStageActions.createStage,
customStageData,
state,
[],
[
{ type: 'clearFormErrors' },
{ type: 'setSavingCustomStage' },
{
type: 'receiveCreateStageSuccess',
payload: { data: customStageData, status: 201 },
},
],
));
});
describe('with errors', () => {
const message = 'failed';
const errors = {
endEventIdentifier: ['Cant be blank'],
};
const customStageData = {
startEventIdentifier: 'start_event',
endEventIdentifier: '',
name: 'cool-new-stage',
};
beforeEach(() => {
state = { ...state, selectedGroup };
mock
.onPost(endpoints.baseStagesEndpointstageData)
.reply(httpStatusCodes.UNPROCESSABLE_ENTITY, {
message,
errors,
});
});
it(`dispatches the 'receiveCreateStageError' action`, () =>
testAction(
customStageActions.createStage,
customStageData,
state,
[],
[
{ type: 'clearFormErrors' },
{ type: 'setSavingCustomStage' },
{
type: 'receiveCreateStageError',
payload: {
data: customStageData,
errors,
message,
status: httpStatusCodes.UNPROCESSABLE_ENTITY,
},
},
],
));
});
});
describe('receiveCreateStageError', () => {
const response = {
data: { name: 'uh oh' },
};
beforeEach(() => {});
it('will commit the RECEIVE_CREATE_STAGE_ERROR mutation', () =>
testAction(
customStageActions.receiveCreateStageError,
response,
state,
[{ type: customStageTypes.RECEIVE_CREATE_STAGE_ERROR }],
[
{
type: 'setStageFormErrors',
payload: {},
},
],
));
it('will flash an error message', () => {
return customStageActions
.receiveCreateStageError(
{
dispatch: () => Promise.resolve(),
commit: () => {},
},
response,
)
.then(() => {
shouldFlashAMessage('There was a problem saving your custom stage, please try again');
});
});
describe('with a stage name error', () => {
it('will flash an error message', () => {
return customStageActions
.receiveCreateStageError(
{
dispatch: () => Promise.resolve(),
commit: () => {},
},
{
...response,
status: httpStatusCodes.UNPROCESSABLE_ENTITY,
errors: { name: ['is reserved'] },
},
)
.then(() => {
shouldFlashAMessage("'uh oh' stage already exists");
});
});
});
});
describe('initializeCycleAnalytics', () => {
let mockDispatch;
let mockCommit;
......@@ -873,38 +741,6 @@ describe('Cycle analytics actions', () => {
));
});
describe('receiveCreateStageSuccess', () => {
const response = {
data: {
title: 'COOL',
},
};
it('will dispatch fetchGroupStagesAndEvents', () =>
testAction(
customStageActions.receiveCreateStageSuccess,
response,
state,
[{ type: customStageTypes.RECEIVE_CREATE_STAGE_SUCCESS }],
[{ type: 'fetchGroupStagesAndEvents', payload: null }, { type: 'clearSavingCustomStage' }],
));
describe('with an error', () => {
it('will flash an error message', () =>
customStageActions
.receiveCreateStageSuccess(
{
dispatch: () => Promise.reject(),
commit: () => {},
},
response,
)
.then(() => {
shouldFlashAMessage('There was a problem refreshing the data, please try again');
}));
});
});
describe('reorderStage', () => {
const stageId = 'cool-stage';
const payload = { id: stageId, move_after_id: '2', move_before_id: '8' };
......
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from 'ee/analytics/cycle_analytics/store/modules/custom_stages/actions';
import * as types from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutation_types';
import createFlash from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import { selectedGroup, endpoints, rawCustomStage } from '../../../mock_data';
jest.mock('~/flash');
describe('Custom stage actions', () => {
let state;
let mock;
const selectedStage = rawCustomStage;
const shouldFlashAMessage = (msg, type = null) => {
const args = type ? [msg, type] : [msg];
expect(createFlash).toHaveBeenCalledWith(...args);
};
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
state = { selectedGroup: null };
});
describe('createStage', () => {
describe('with valid data', () => {
const customStageData = {
startEventIdentifier: 'start_event',
endEventIdentifier: 'end_event',
name: 'cool-new-stage',
};
beforeEach(() => {
state = { ...state, selectedGroup };
mock.onPost(endpoints.baseStagesEndpointstageData).reply(201, customStageData);
});
it(`dispatches the 'receiveCreateStageSuccess' action`, () =>
testAction(
actions.createStage,
customStageData,
state,
[],
[
{ type: 'clearFormErrors' },
{ type: 'setSavingCustomStage' },
{
type: 'receiveCreateStageSuccess',
payload: { data: customStageData, status: 201 },
},
],
));
});
describe('with errors', () => {
const message = 'failed';
const errors = {
endEventIdentifier: ['Cant be blank'],
};
const customStageData = {
startEventIdentifier: 'start_event',
endEventIdentifier: '',
name: 'cool-new-stage',
};
beforeEach(() => {
state = { ...state, selectedGroup };
mock
.onPost(endpoints.baseStagesEndpointstageData)
.reply(httpStatusCodes.UNPROCESSABLE_ENTITY, {
message,
errors,
});
});
it(`dispatches the 'receiveCreateStageError' action`, () =>
testAction(
actions.createStage,
customStageData,
state,
[],
[
{ type: 'clearFormErrors' },
{ type: 'setSavingCustomStage' },
{
type: 'receiveCreateStageError',
payload: {
data: customStageData,
errors,
message,
status: httpStatusCodes.UNPROCESSABLE_ENTITY,
},
},
],
));
});
});
describe('receiveCreateStageError', () => {
const response = {
data: { name: 'uh oh' },
};
beforeEach(() => {});
it('will commit the RECEIVE_CREATE_STAGE_ERROR mutation', () =>
testAction(
actions.receiveCreateStageError,
response,
state,
[{ type: types.RECEIVE_CREATE_STAGE_ERROR }],
[{ type: 'setStageFormErrors', payload: {} }],
));
it('will flash an error message', () => {
return actions
.receiveCreateStageError(
{
dispatch: () => Promise.resolve(),
commit: () => {},
},
response,
)
.then(() => {
shouldFlashAMessage('There was a problem saving your custom stage, please try again');
});
});
describe('with a stage name error', () => {
it('will flash an error message', () => {
return actions
.receiveCreateStageError(
{
dispatch: () => Promise.resolve(),
commit: () => {},
},
{
...response,
status: httpStatusCodes.UNPROCESSABLE_ENTITY,
errors: { name: ['is reserved'] },
},
)
.then(() => {
shouldFlashAMessage("'uh oh' stage already exists");
});
});
});
});
describe('receiveCreateStageSuccess', () => {
const response = {
data: {
title: 'COOL',
},
};
it('will dispatch fetchGroupStagesAndEvents', () =>
testAction(
actions.receiveCreateStageSuccess,
response,
state,
[{ type: types.RECEIVE_CREATE_STAGE_SUCCESS }],
[{ type: 'fetchGroupStagesAndEvents', payload: null }, { type: 'clearSavingCustomStage' }],
));
describe('with an error', () => {
it('will flash an error message', () =>
actions
.receiveCreateStageSuccess(
{
dispatch: () => Promise.reject(),
commit: () => {},
},
response,
)
.then(() => {
shouldFlashAMessage('There was a problem refreshing the data, please try again');
}));
});
});
describe('setStageFormErrors', () => {
it('commits the "SET_STAGE_FORM_ERRORS" mutation', () => {
return testAction(
actions.setStageFormErrors,
[],
state,
[{ type: types.SET_STAGE_FORM_ERRORS, payload: [] }],
[],
);
});
});
describe('clearFormErrors', () => {
it('commits the "CLEAR_FORM_ERRORS" mutation', () => {
return testAction(
actions.clearFormErrors,
[],
state,
[{ type: types.CLEAR_FORM_ERRORS }],
[],
);
});
});
describe('setStageEvents', () => {
it('commits the "SET_STAGE_EVENTS" mutation', () => {
return testAction(
actions.setStageEvents,
[],
state,
[{ type: types.SET_STAGE_EVENTS, payload: [] }],
[],
);
});
});
describe('hideForm', () => {
it('commits the "HIDE_FORM" mutation', () => {
return testAction(actions.hideForm, null, state, [{ type: types.HIDE_FORM }], []);
});
});
describe('showCreateForm', () => {
it('commits the "SHOW_CREATE_FORM" mutation', () => {
return testAction(
actions.showCreateForm,
null,
state,
[
{ type: types.SET_LOADING },
{ type: types.SET_FORM_INITIAL_DATA },
{ type: types.SHOW_CREATE_FORM },
],
[],
);
});
});
describe('showEditForm', () => {
it('commits the "SHOW_EDIT_FORM" mutation with initial data', () => {
return testAction(
actions.showEditForm,
selectedStage,
state,
[
{ type: types.SET_LOADING },
{ type: types.SET_FORM_INITIAL_DATA, payload: rawCustomStage },
{ type: types.SHOW_EDIT_FORM },
],
[{ type: 'setSelectedStage', payload: rawCustomStage }, { type: 'clearSavingCustomStage' }],
);
});
});
});
import * as getters from 'ee/analytics/cycle_analytics/store/modules/custom_stages/getters';
describe('Custom stages getters', () => {
describe.each`
state | result
${{ isCreatingCustomStage: true, isEditingCustomStage: true }} | ${true}
${{ isCreatingCustomStage: false, isEditingCustomStage: true }} | ${true}
${{ isCreatingCustomStage: true, isEditingCustomStage: false }} | ${true}
${{ isCreatingCustomStage: false, isEditingCustomStage: false }} | ${false}
`('customStageFormActive', ({ state, result }) => {
it(`with state ${state} returns ${result}`, () => {
expect(getters.customStageFormActive(state)).toEqual(result);
});
});
});
import mutations from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { rawCustomStageEvents, camelCasedStageEvents, rawCustomStage } from '../../../mock_data';
let state = null;
describe('Custom stage mutations', () => {
beforeEach(() => {
state = {};
});
afterEach(() => {
state = null;
});
it.each`
mutation | stateKey | value
${types.HIDE_FORM} | ${'isCreatingCustomStage'} | ${false}
${types.HIDE_FORM} | ${'isEditingCustomStage'} | ${false}
${types.HIDE_FORM} | ${'formErrors'} | ${null}
${types.HIDE_FORM} | ${'formInitialData'} | ${null}
${types.CLEAR_FORM_ERRORS} | ${'formErrors'} | ${null}
${types.SHOW_CREATE_FORM} | ${'isCreatingCustomStage'} | ${true}
${types.SHOW_CREATE_FORM} | ${'isEditingCustomStage'} | ${false}
${types.SHOW_CREATE_FORM} | ${'formErrors'} | ${null}
${types.SHOW_CREATE_FORM} | ${'formInitialData'} | ${null}
${types.SHOW_EDIT_FORM} | ${'isEditingCustomStage'} | ${true}
${types.SHOW_EDIT_FORM} | ${'isCreatingCustomStage'} | ${false}
${types.SHOW_EDIT_FORM} | ${'formErrors'} | ${null}
${types.RECEIVE_CREATE_STAGE_SUCCESS} | ${'formErrors'} | ${null}
${types.RECEIVE_CREATE_STAGE_SUCCESS} | ${'formInitialData'} | ${null}
${types.RECEIVE_CREATE_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false}
${types.SET_SAVING_CUSTOM_STAGE} | ${'isSavingCustomStage'} | ${true}
${types.CLEAR_SAVING_CUSTOM_STAGE} | ${'isSavingCustomStage'} | ${false}
${types.SET_LOADING} | ${'isLoadingCustomStage'} | ${true}
`('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => {
mutations[mutation](state);
expect(state[stateKey]).toEqual(value);
});
describe(`${types.SET_STAGE_EVENTS}`, () => {
it('will set formEvents', () => {
state = {};
mutations[types.SET_STAGE_EVENTS](state, rawCustomStageEvents);
expect(state.formEvents).toEqual(camelCasedStageEvents);
});
});
describe(`${types.SET_STAGE_FORM_ERRORS}`, () => {
const mockFormError = { start_identifier: ['Cant be blank'] };
it('will set formErrors', () => {
state = {};
mutations[types.SET_STAGE_FORM_ERRORS](state, mockFormError);
expect(state.formErrors).toEqual(convertObjectPropsToCamelCase(mockFormError));
});
});
describe(`${types.SET_FORM_INITIAL_DATA}`, () => {
const mockStage = {
endEventIdentifier: 'issue_first_added_to_board',
endEventLabelId: null,
id: 18,
name: 'Coolest beans stage',
startEventIdentifier: 'issue_first_mentioned_in_commit',
startEventLabelId: null,
};
it('will set formInitialData', () => {
state = {};
mutations[types.SET_FORM_INITIAL_DATA](state, rawCustomStage);
expect(state.formInitialData).toEqual(mockStage);
});
});
});
import mutations from 'ee/analytics/cycle_analytics/store/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import customStageMutations from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutations';
import * as customStageTypes from 'ee/analytics/cycle_analytics/store/modules/custom_stages/mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import {
issueStage,
......@@ -13,9 +10,8 @@ import {
totalStage,
startDate,
endDate,
customizableStagesAndEvents,
selectedProjects,
rawCustomStage,
customizableStagesAndEvents,
} from '../mock_data';
let state = null;
......@@ -70,65 +66,6 @@ describe('Cycle analytics mutations', () => {
},
);
describe('Custom stage mutations', () => {
beforeEach(() => {
state = {};
});
afterEach(() => {
state = null;
});
it.each`
mutation | stateKey | value
${customStageTypes.HIDE_FORM} | ${'isCreatingCustomStage'} | ${false}
${customStageTypes.HIDE_FORM} | ${'isEditingCustomStage'} | ${false}
${customStageTypes.HIDE_FORM} | ${'formErrors'} | ${null}
${customStageTypes.HIDE_FORM} | ${'formInitialData'} | ${null}
${customStageTypes.SHOW_CREATE_FORM} | ${'isCreatingCustomStage'} | ${true}
${customStageTypes.SHOW_CREATE_FORM} | ${'isEditingCustomStage'} | ${false}
${customStageTypes.SHOW_CREATE_FORM} | ${'formErrors'} | ${null}
${customStageTypes.SHOW_EDIT_FORM} | ${'isEditingCustomStage'} | ${true}
${customStageTypes.SHOW_EDIT_FORM} | ${'isCreatingCustomStage'} | ${false}
${customStageTypes.SHOW_EDIT_FORM} | ${'formErrors'} | ${null}
${customStageTypes.RECEIVE_CREATE_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false}
${customStageTypes.SET_SAVING_CUSTOM_STAGE} | ${'isSavingCustomStage'} | ${true}
${customStageTypes.CLEAR_SAVING_CUSTOM_STAGE} | ${'isSavingCustomStage'} | ${false}
`('$mutation will set $stateKey=$value', ({ mutation, stateKey, value }) => {
customStageMutations[mutation](state);
expect(state[stateKey]).toEqual(value);
});
describe(`${customStageTypes.SET_STAGE_FORM_ERRORS}`, () => {
const mockFormError = { start_identifier: ['Cant be blank'] };
it('will set formErrors', () => {
state = {};
customStageMutations[customStageTypes.SET_STAGE_FORM_ERRORS](state, mockFormError);
expect(state.formErrors).toEqual(convertObjectPropsToCamelCase(mockFormError));
});
});
describe(`${customStageTypes.SET_FORM_INITIAL_DATA}`, () => {
const mockStage = {
id: 18,
name: 'Coolest beans stage',
startEventIdentifier: 'issue_first_mentioned_in_commit',
startEventLabelId: null,
endEventIdentifier: 'issue_first_added_to_board',
endEventLabelId: null,
};
it('will set formInitialData', () => {
state = {};
customStageMutations[customStageTypes.SET_FORM_INITIAL_DATA](state, rawCustomStage);
expect(state.formInitialData).toEqual(mockStage);
});
});
});
describe(`${types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS}`, () => {
it('will set isLoading=false and errorCode=null', () => {
mutations[types.RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS](state, {
......
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