Commit e236bb09 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Move the custom value stream fields to a new component

Extracts the custom stage form fields into
a separate component and moves some of the related
logic into utils and constants
parent afb907e4
...@@ -11,7 +11,7 @@ import TypeOfWorkCharts from './type_of_work_charts.vue'; ...@@ -11,7 +11,7 @@ import TypeOfWorkCharts from './type_of_work_charts.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue'; import UrlSync from '~/vue_shared/components/url_sync.vue';
import { toYmd } from '../../shared/utils'; import { toYmd } from '../../shared/utils';
import StageTableNav from './stage_table_nav.vue'; import StageTableNav from './stage_table_nav.vue';
import CustomStageForm from './custom_stage_form.vue'; import CustomStageForm from './create_value_stream_form/custom_stage_form.vue';
import PathNavigation from './path_navigation.vue'; import PathNavigation from './path_navigation.vue';
import FilterBar from './filter_bar.vue'; import FilterBar from './filter_bar.vue';
import ValueStreamSelect from './value_stream_select.vue'; import ValueStreamSelect from './value_stream_select.vue';
......
import { s__ } from '~/locale';
export const I18N = {
SELECT_START_EVENT: s__('CustomCycleAnalytics|Select start event'),
SELECT_END_EVENT: s__('CustomCycleAnalytics|Select stop event'),
};
export 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 end event',
),
};
export const defaultErrors = {
id: [],
name: [],
startEventIdentifier: [],
startEventLabelId: [],
endEventIdentifier: [],
endEventLabelId: [],
};
export const defaultFields = {
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
};
<script>
import { GlFormGroup, GlFormInput, GlFormSelect } from '@gitlab/ui';
import { s__ } from '~/locale';
import LabelsSelector from '../labels_selector.vue';
import {
isStartEvent,
isLabelEvent,
getAllowedEndEvents,
eventToOption,
eventsByIdentifier,
} from '../../utils';
export default {
name: 'CustomStageFormFields',
components: {
GlFormGroup,
GlFormInput,
GlFormSelect,
LabelsSelector,
},
props: {
fields: {
type: Object,
required: true,
},
errors: {
type: Object,
required: false,
default: () => {},
},
events: {
type: Array,
required: true,
},
labelEvents: {
type: Array,
required: true,
},
},
computed: {
startEventOptions() {
return [
{ value: null, text: s__('CustomCycleAnalytics|Select start event') },
...this.events.filter(isStartEvent).map(eventToOption),
];
},
endEventOptions() {
const endEvents = getAllowedEndEvents(this.events, this.fields.startEventIdentifier);
return [
{ value: null, text: s__('CustomCycleAnalytics|Select end event') },
...eventsByIdentifier(this.events, endEvents).map(eventToOption),
];
},
hasStartEvent() {
return this.fields.startEventIdentifier;
},
startEventRequiresLabel() {
return isLabelEvent(this.labelEvents, this.fields.startEventIdentifier);
},
endEventRequiresLabel() {
return isLabelEvent(this.labelEvents, this.fields.endEventIdentifier);
},
},
methods: {
hasFieldErrors(key) {
return this.errors[key]?.length > 0;
},
fieldErrorMessage(key) {
return this.errors[key]?.join('\n');
},
handleUpdateField(field, value) {
this.$emit('update', field, value);
},
},
};
</script>
<template>
<div>
<gl-form-group
:label="s__('CustomCycleAnalytics|Name')"
label-for="custom-stage-name"
:state="!hasFieldErrors('name')"
:invalid-feedback="fieldErrorMessage('name')"
data-testid="custom-stage-name"
>
<gl-form-input
:value="fields.name"
class="form-control"
type="text"
name="custom-stage-name"
:placeholder="s__('CustomCycleAnalytics|Enter a name for the stage')"
required
@input="handleUpdateField('name', $event)"
/>
</gl-form-group>
<div class="d-flex" :class="{ 'justify-content-between': startEventRequiresLabel }">
<div :class="[startEventRequiresLabel ? 'w-50 mr-1' : 'w-100']">
<gl-form-group
data-testid="custom-stage-start-event-identifier"
:label="s__('CustomCycleAnalytics|Start event')"
label-for="custom-stage-start-event"
:state="!hasFieldErrors('startEventIdentifier')"
:invalid-feedback="fieldErrorMessage('startEventIdentifier')"
>
<gl-form-select
v-model="fields.startEventIdentifier"
name="custom-stage-start-event"
:required="true"
:options="startEventOptions"
@input="handleUpdateField('startEventIdentifier', $event)"
/>
</gl-form-group>
</div>
<div v-if="startEventRequiresLabel" class="w-50 ml-1">
<gl-form-group
data-testid="custom-stage-start-event-label-id"
:label="s__('CustomCycleAnalytics|Start event label')"
label-for="custom-stage-start-event-label"
:state="!hasFieldErrors('startEventLabelId')"
:invalid-feedback="fieldErrorMessage('startEventLabelId')"
>
<labels-selector
:selected-label-id="[fields.startEventLabelId]"
name="custom-stage-start-event-label"
@selectLabel="handleUpdateField('startEventLabelId', $event)"
/>
</gl-form-group>
</div>
</div>
<div class="d-flex" :class="{ 'justify-content-between': endEventRequiresLabel }">
<div :class="[endEventRequiresLabel ? 'w-50 mr-1' : 'w-100']">
<gl-form-group
data-testid="custom-stage-end-event-identifier"
:label="s__('CustomCycleAnalytics|End event')"
label-for="custom-stage-end-event"
:state="!hasFieldErrors('endEventIdentifier')"
:invalid-feedback="fieldErrorMessage('endEventIdentifier')"
>
<gl-form-select
v-model="fields.endEventIdentifier"
name="custom-stage-end-event"
:options="endEventOptions"
:required="true"
:disabled="!hasStartEvent"
@input="handleUpdateField('endEventIdentifier', $event)"
/>
</gl-form-group>
</div>
<div v-if="endEventRequiresLabel" class="w-50 ml-1">
<gl-form-group
data-testid="custom-stage-end-event-label-id"
:label="s__('CustomCycleAnalytics|End event label')"
label-for="custom-stage-end-event-label"
:state="!hasFieldErrors('endEventLabelId')"
:invalid-feedback="fieldErrorMessage('endEventLabelId')"
>
<labels-selector
:selected-label-id="[fields.endEventLabelId]"
name="custom-stage-end-event-label"
@selectLabel="handleUpdateField('endEventLabelId', $event)"
/>
</gl-form-group>
</div>
</div>
</div>
</template>
import { isStartEvent, getAllowedEndEvents, eventToOption, eventsByIdentifier } from '../../utils';
import { I18N, ERRORS, defaultErrors, defaultFields } from './constants';
import { DEFAULT_STAGE_NAMES } from '../../constants';
export const startEventOptions = eventsList => [
{ value: null, text: I18N.SELECT_START_EVENT },
...eventsList.filter(isStartEvent).map(eventToOption),
];
export const endEventOptions = (eventsList, startEventIdentifier) => {
const endEvents = getAllowedEndEvents(eventsList, startEventIdentifier);
return [
{ value: null, text: I18N.SELECT_END_EVENT },
...eventsByIdentifier(eventsList, endEvents).map(eventToOption),
];
};
export const initializeFormData = ({ fields, errors }) => {
const initErrors = fields?.endEventIdentifier
? defaultErrors
: {
...defaultErrors,
endEventIdentifier: !fields?.startEventIdentifier ? [ERRORS.START_EVENT_REQUIRED] : [],
};
return {
fields: {
...defaultFields,
...fields,
},
errors: {
...initErrors,
...errors,
},
};
};
export const validateFields = fields => {
const newErrors = {};
if (fields?.name) {
newErrors.name = DEFAULT_STAGE_NAMES.includes(fields?.name.toLowerCase())
? [ERRORS.STAGE_NAME_EXISTS]
: [];
}
if (fields?.startEventIdentifier) {
newErrors.endEventIdentifier = [];
} else {
newErrors.endEventIdentifier = [ERRORS.START_EVENT_REQUIRED];
}
if (fields?.endEventIdentifier) {
newErrors.endEventIdentifier = [];
}
return newErrors;
};
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { import {
GlFormGroup,
GlFormInput,
GlFormSelect,
GlLoadingIcon, GlLoadingIcon,
GlDropdown, GlDropdown,
GlDropdownSectionHeader, GlDropdownSectionHeader,
...@@ -14,74 +11,21 @@ import { ...@@ -14,74 +11,21 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
import LabelsSelector from './labels_selector.vue'; import CustomStageFormFields from './create_value_stream_form/custom_stage_fields.vue';
import { STAGE_ACTIONS, DEFAULT_STAGE_NAMES } from '../constants'; import { validateFields, initializeFormData } from './create_value_stream_form/utils';
import { import { defaultFields, ERRORS } from './create_value_stream_form/constants';
isStartEvent, import { STAGE_ACTIONS } from '../constants';
isLabelEvent, import { getAllowedEndEvents, getLabelEventsIdentifiers, isLabelEvent } from '../utils';
getAllowedEndEvents,
eventToOption,
eventsByIdentifier,
getLabelEventsIdentifiers,
} from '../utils';
const defaultFields = {
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: 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
: {
...defaultErrors,
endEventIdentifier: !fields?.startEventIdentifier ? [ERRORS.START_EVENT_REQUIRED] : [],
};
return {
fields: {
...emptyFieldState,
...fields,
},
errors: {
...initErrors,
...errors,
},
};
};
export default { export default {
components: { components: {
GlFormGroup,
GlFormInput,
GlFormSelect,
GlLoadingIcon, GlLoadingIcon,
LabelsSelector,
GlDropdown, GlDropdown,
GlDropdownSectionHeader, GlDropdownSectionHeader,
GlDropdownItem, GlDropdownItem,
GlSprintf, GlSprintf,
GlButton, GlButton,
CustomStageFormFields,
}, },
props: { props: {
events: { events: {
...@@ -93,7 +37,7 @@ export default { ...@@ -93,7 +37,7 @@ export default {
return { return {
labelEvents: getLabelEventsIdentifiers(this.events), labelEvents: getLabelEventsIdentifiers(this.events),
fields: {}, fields: {},
errors: [], errors: {},
}; };
}, },
computed: { computed: {
...@@ -105,21 +49,11 @@ export default { ...@@ -105,21 +49,11 @@ export default {
'formInitialData', 'formInitialData',
'formErrors', 'formErrors',
]), ]),
startEventOptions() {
return [ hasErrors() {
{ value: null, text: s__('CustomCycleAnalytics|Select start event') }, return (
...this.events.filter(isStartEvent).map(eventToOption), this.eventMismatchError || Object.values(this.errors).some(errArray => errArray?.length)
]; );
},
endEventOptions() {
const endEvents = getAllowedEndEvents(this.events, this.fields.startEventIdentifier);
return [
{ value: null, text: s__('CustomCycleAnalytics|Select stop event') },
...eventsByIdentifier(this.events, endEvents).map(eventToOption),
];
},
hasStartEvent() {
return this.fields.startEventIdentifier;
}, },
startEventRequiresLabel() { startEventRequiresLabel() {
return isLabelEvent(this.labelEvents, this.fields.startEventIdentifier); return isLabelEvent(this.labelEvents, this.fields.startEventIdentifier);
...@@ -127,11 +61,6 @@ export default { ...@@ -127,11 +61,6 @@ export default {
endEventRequiresLabel() { endEventRequiresLabel() {
return isLabelEvent(this.labelEvents, this.fields.endEventIdentifier); return isLabelEvent(this.labelEvents, this.fields.endEventIdentifier);
}, },
hasErrors() {
return (
this.eventMismatchError || Object.values(this.errors).some(errArray => errArray?.length)
);
},
isComplete() { isComplete() {
if (this.hasErrors) { if (this.hasErrors) {
return false; return false;
...@@ -222,33 +151,23 @@ export default { ...@@ -222,33 +151,23 @@ export default {
this.$emit(STAGE_ACTIONS.CREATE, data); this.$emit(STAGE_ACTIONS.CREATE, data);
} }
}, },
handleSelectLabel(key, labelId) {
this.fields[key] = labelId;
},
handleClearLabel(key) {
this.fields[key] = null;
},
hasFieldErrors(key) { hasFieldErrors(key) {
return this.errors[key]?.length > 0; return this.errors[key]?.length > 0;
}, },
fieldErrorMessage(key) { fieldErrorMessage(key) {
return this.errors[key]?.join('\n'); return this.errors[key]?.join('\n');
}, },
onUpdateNameField() {
this.errors.name = DEFAULT_STAGE_NAMES.includes(this.fields.name.toLowerCase())
? [ERRORS.STAGE_NAME_EXISTS]
: [];
},
onUpdateStartEventField() {
this.fields.endEventIdentifier = null;
this.errors.endEventIdentifier = [ERRORS.INVALID_EVENT_PAIRS];
},
onUpdateEndEventField() {
this.errors.endEventIdentifier = [];
},
handleRecoverStage(id) { handleRecoverStage(id) {
this.$emit(STAGE_ACTIONS.UPDATE, { id, hidden: false }); this.$emit(STAGE_ACTIONS.UPDATE, { id, hidden: false });
}, },
handleUpdateFields(field, value) {
this.fields = { ...this.fields, [field]: value };
const newErrors = validateFields(this.fields);
newErrors.endEventIdentifier = this.eventMismatchError
? [ERRORS.INVALID_EVENT_PAIRS]
: newErrors.endEventIdentifier;
this.errors = newErrors;
},
}, },
}; };
</script> </script>
...@@ -261,7 +180,7 @@ export default { ...@@ -261,7 +180,7 @@ export default {
<h4>{{ formTitle }}</h4> <h4>{{ formTitle }}</h4>
<gl-dropdown <gl-dropdown
:text="__('Recover hidden stage')" :text="__('Recover hidden stage')"
class="js-recover-hidden-stage-dropdown" data-testid="recover-hidden-stage-dropdown"
right right
> >
<gl-dropdown-section-header>{{ __('Default stages') }}</gl-dropdown-section-header> <gl-dropdown-section-header>{{ __('Default stages') }}</gl-dropdown-section-header>
...@@ -276,99 +195,18 @@ export default { ...@@ -276,99 +195,18 @@ export default {
<p v-else class="mx-3 my-2">{{ __('All default stages are currently visible') }}</p> <p v-else class="mx-3 my-2">{{ __('All default stages are currently visible') }}</p>
</gl-dropdown> </gl-dropdown>
</div> </div>
<gl-form-group <custom-stage-form-fields
ref="name" :fields="fields"
:label="s__('CustomCycleAnalytics|Name')" :label-events="labelEvents"
label-for="custom-stage-name" :errors="errors"
:state="!hasFieldErrors('name')" :events="events"
:invalid-feedback="fieldErrorMessage('name')" @update="handleUpdateFields"
> />
<gl-form-input
v-model="fields.name"
class="form-control"
type="text"
name="custom-stage-name"
:placeholder="s__('CustomCycleAnalytics|Enter a name for the stage')"
required
@change.native="onUpdateNameField"
/>
</gl-form-group>
<div class="d-flex" :class="{ 'justify-content-between': startEventRequiresLabel }">
<div :class="[startEventRequiresLabel ? 'w-50 mr-1' : 'w-100']">
<gl-form-group
ref="startEventIdentifier"
:label="s__('CustomCycleAnalytics|Start event')"
label-for="custom-stage-start-event"
:state="!hasFieldErrors('startEventIdentifier')"
:invalid-feedback="fieldErrorMessage('startEventIdentifier')"
>
<gl-form-select
v-model="fields.startEventIdentifier"
name="custom-stage-start-event"
:required="true"
:options="startEventOptions"
@change.native="onUpdateStartEventField"
/>
</gl-form-group>
</div>
<div v-if="startEventRequiresLabel" class="w-50 ml-1">
<gl-form-group
ref="startEventLabelId"
:label="s__('CustomCycleAnalytics|Start event label')"
label-for="custom-stage-start-event-label"
:state="!hasFieldErrors('startEventLabelId')"
:invalid-feedback="fieldErrorMessage('startEventLabelId')"
>
<labels-selector
:selected-label-id="[fields.startEventLabelId]"
name="custom-stage-start-event-label"
@selectLabel="handleSelectLabel('startEventLabelId', $event)"
@clearLabel="handleClearLabel('startEventLabelId')"
/>
</gl-form-group>
</div>
</div>
<div class="d-flex" :class="{ 'justify-content-between': endEventRequiresLabel }">
<div :class="[endEventRequiresLabel ? 'w-50 mr-1' : 'w-100']">
<gl-form-group
ref="endEventIdentifier"
:label="s__('CustomCycleAnalytics|Stop event')"
label-for="custom-stage-stop-event"
:state="!hasFieldErrors('endEventIdentifier')"
:invalid-feedback="fieldErrorMessage('endEventIdentifier')"
>
<gl-form-select
v-model="fields.endEventIdentifier"
name="custom-stage-stop-event"
:options="endEventOptions"
:required="true"
:disabled="!hasStartEvent"
@change.native="onUpdateEndEventField"
/>
</gl-form-group>
</div>
<div v-if="endEventRequiresLabel" class="w-50 ml-1">
<gl-form-group
ref="endEventLabelId"
:label="s__('CustomCycleAnalytics|Stop event label')"
label-for="custom-stage-stop-event-label"
:state="!hasFieldErrors('endEventLabelId')"
:invalid-feedback="fieldErrorMessage('endEventLabelId')"
>
<labels-selector
:selected-label-id="[fields.endEventLabelId]"
name="custom-stage-stop-event-label"
@selectLabel="handleSelectLabel('endEventLabelId', $event)"
@clearLabel="handleClearLabel('endEventLabelId')"
/>
</gl-form-group>
</div>
</div>
<div class="custom-stage-form-actions"> <div class="custom-stage-form-actions">
<gl-button <gl-button
:disabled="!isDirty" :disabled="!isDirty"
category="primary" category="primary"
class="js-save-stage-cancel" data-testid="cancel-custom-stage"
@click="handleCancel" @click="handleCancel"
> >
{{ __('Cancel') }} {{ __('Cancel') }}
...@@ -377,7 +215,7 @@ export default { ...@@ -377,7 +215,7 @@ export default {
:disabled="!isComplete || !isDirty" :disabled="!isComplete || !isDirty"
variant="success" variant="success"
category="primary" category="primary"
class="js-save-stage" data-testid="save-custom-stage"
@click="handleSave" @click="handleSave"
> >
<gl-loading-icon v-if="isSavingCustomStage" size="sm" inline /> <gl-loading-icon v-if="isSavingCustomStage" size="sm" inline />
......
...@@ -279,7 +279,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do ...@@ -279,7 +279,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
shared_examples 'can edit custom stages' do shared_examples 'can edit custom stages' do
context 'Edit stage form' do context 'Edit stage form' do
let(:stage_form_class) { '.custom-stage-form' } let(:stage_form_class) { '.custom-stage-form' }
let(:stage_save_button) { '.js-save-stage' } let(:stage_save_button) { '[data-testid="save-custom-stage"]' }
let(:name_field) { 'custom-stage-name' } let(:name_field) { 'custom-stage-name' }
let(:start_event_field) { 'custom-stage-start-event' } let(:start_event_field) { 'custom-stage-start-event' }
let(:end_event_field) { 'custom-stage-stop-event' } let(:end_event_field) { 'custom-stage-stop-event' }
...@@ -413,7 +413,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do ...@@ -413,7 +413,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
open_recover_stage_dropdown open_recover_stage_dropdown
expect(page.find('.js-recover-hidden-stage-dropdown')).to have_text(s_('CycleAnalyticsStage|Issue')) expect(page.find("[data-testid='recover-hidden-stage-dropdown']")).to have_text(s_('CycleAnalyticsStage|Issue'))
end end
end end
......
...@@ -5,7 +5,7 @@ import MockAdapter from 'axios-mock-adapter'; ...@@ -5,7 +5,7 @@ import MockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex'; import Vuex from 'vuex';
import AddStageButton from 'ee/analytics/cycle_analytics/components/add_stage_button.vue'; import AddStageButton from 'ee/analytics/cycle_analytics/components/add_stage_button.vue';
import Component from 'ee/analytics/cycle_analytics/components/base.vue'; import Component from 'ee/analytics/cycle_analytics/components/base.vue';
import CustomStageForm from 'ee/analytics/cycle_analytics/components/custom_stage_form.vue'; import CustomStageForm from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_form.vue';
import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue'; import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
import FilterBar from 'ee/analytics/cycle_analytics/components/filter_bar.vue'; import FilterBar from 'ee/analytics/cycle_analytics/components/filter_bar.vue';
import Metrics from 'ee/analytics/cycle_analytics/components/metrics.vue'; import Metrics from 'ee/analytics/cycle_analytics/components/metrics.vue';
......
export const findName = wrapper => wrapper.find('[data-testid="custom-stage-name"]');
export const findStartEvent = wrapper =>
wrapper.find('[data-testid="custom-stage-start-event-identifier"]');
export const findEndEvent = wrapper =>
wrapper.find('[data-testid="custom-stage-end-event-identifier"]');
export const findStartEventLabel = wrapper =>
wrapper.find('[data-testid="custom-stage-start-event-label-id"]');
export const findEndEventLabel = wrapper =>
wrapper.find('[data-testid="custom-stage-end-event-label-id"]');
export const formatStartEventOpts = events => [
{ text: 'Select start event', value: null },
...events
.filter(ev => ev.canBeStartEvent)
.map(({ name: text, identifier: value }) => ({ text, value })),
];
export const formatEndEventOpts = events => [
{ text: 'Select end event', value: null },
...events
.filter(ev => !ev.canBeStartEvent)
.map(({ name: text, identifier: value }) => ({ text, value })),
];
import { groupLabels, labelStartEvent, labelStopEvent } from '../../mock_data';
export const MERGE_REQUEST_CREATED = 'merge_request_created';
export const ISSUE_CREATED = 'issue_created';
export const MERGE_REQUEST_CLOSED = 'merge_request_closed';
export const ISSUE_CLOSED = 'issue_closed';
export const emptyState = {
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
};
export const emptyErrorsState = {
id: [],
name: [],
startEventIdentifier: [],
startEventLabelId: [],
endEventIdentifier: ['Please select a start event first'],
endEventLabelId: [],
};
export const firstLabel = groupLabels[0];
export const formInitialData = {
id: 74,
name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier,
startEventLabelId: firstLabel.id,
endEventIdentifier: labelStopEvent.identifier,
endEventLabelId: firstLabel.id,
};
export const minimumFields = {
name: 'Cool stage',
startEventIdentifier: MERGE_REQUEST_CREATED,
endEventIdentifier: MERGE_REQUEST_CLOSED,
};
import { initializeFormData } from 'ee/analytics/cycle_analytics/components/create_value_stream_form/utils';
import { emptyErrorsState, emptyState, formInitialData } from './mock_data';
describe('initializeFormData', () => {
const checkInitializedData = (
{ emptyFieldState = emptyState, fields = {}, errors = emptyErrorsState },
{ fields: resultFields = emptyState, errors: resultErrors = emptyErrorsState },
) => {
const res = initializeFormData({ emptyFieldState, fields, errors });
expect(res.fields).toEqual(resultFields);
expect(res.errors).toMatchObject(resultErrors);
};
describe('without a startEventIdentifier', () => {
it('with no errors', () => {
checkInitializedData(
{ fields: {} },
{ errors: { endEventIdentifier: ['Please select a start event first'] } },
);
});
it('with field errors', () => {
const data = { errors: { name: ['is reserved'] } };
const result = {
errors: {
endEventIdentifier: ['Please select a start event first'],
name: ['is reserved'],
},
};
checkInitializedData(data, result);
});
});
describe('with a startEventIdentifier', () => {
it('with no errors', () => {
const data = {
fields: { startEventIdentifier: 'start-event' },
errors: { ...emptyErrorsState, endEventIdentifier: [] },
};
const result = {
fields: { ...emptyState, startEventIdentifier: 'start-event' },
errors: { ...emptyErrorsState, endEventIdentifier: [] },
};
checkInitializedData(data, result);
});
it('with field errors', () => {
const data = {
fields: { startEventIdentifier: 'start-event' },
errors: { name: ['is reserved'] },
};
const result = {
fields: { ...emptyState, startEventIdentifier: 'start-event' },
errors: { endEventIdentifier: [], name: ['is reserved'] },
};
checkInitializedData(data, result);
});
});
describe('with all fields set', () => {
it('with no errors', () => {
const data = { fields: formInitialData };
const result = { fields: formInitialData };
checkInitializedData(data, result);
});
it('with field errors', () => {
const data = { fields: formInitialData, errors: { name: ['is reserved'] } };
const result = { fields: formInitialData, errors: { name: ['is reserved'] } };
checkInitializedData(data, result);
});
});
});
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