Commit 8b242314 authored by Simon Knox's avatar Simon Knox

Merge branch 'ek-minor-vsa-text-and-test-follow-up-fixes' into 'master'

Minor VSA i18n and create value stream form components refactor

See merge request gitlab-org/gitlab!75325
parents e26cfad6 e2d16634
...@@ -3,8 +3,8 @@ import { __, s__, sprintf } from '~/locale'; ...@@ -3,8 +3,8 @@ import { __, s__, sprintf } from '~/locale';
export const NAME_MAX_LENGTH = 100; export const NAME_MAX_LENGTH = 100;
export const i18n = { export const i18n = {
FORM_TITLE: s__('CreateValueStreamForm|Create Value Stream'), FORM_TITLE: s__('CreateValueStreamForm|Create value stream'),
EDIT_FORM_TITLE: s__('CreateValueStreamForm|Edit Value Stream'), EDIT_FORM_TITLE: s__('CreateValueStreamForm|Edit value stream'),
EDIT_FORM_ACTION: s__('CreateValueStreamForm|Save value stream'), EDIT_FORM_ACTION: s__('CreateValueStreamForm|Save value stream'),
FORM_CREATED: s__("CreateValueStreamForm|'%{name}' Value Stream created"), FORM_CREATED: s__("CreateValueStreamForm|'%{name}' Value Stream created"),
FORM_EDITED: s__("CreateValueStreamForm|'%{name}' Value Stream saved"), FORM_EDITED: s__("CreateValueStreamForm|'%{name}' Value Stream saved"),
......
<script>
import { GlFormGroup, GlDropdown, GlDropdownItem } from '@gitlab/ui';
export default {
name: 'CustomStageEventField',
components: {
GlFormGroup,
GlDropdown,
GlDropdownItem,
},
props: {
index: {
type: Number,
required: true,
},
eventType: {
type: String,
required: true,
},
eventsList: {
type: Array,
required: true,
},
fieldLabel: {
type: String,
required: true,
},
selectedEventName: {
type: String,
required: true,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
hasIdentifierError: {
type: Boolean,
required: false,
default: false,
},
identifierError: {
type: String,
required: false,
default: '',
},
},
computed: {
fieldName() {
const { eventType, index } = this;
return `custom-stage-${eventType}-${index}`;
},
},
};
</script>
<template>
<gl-form-group
class="gl-w-half gl-mr-2"
:data-testid="fieldName"
:label="fieldLabel"
:state="hasIdentifierError"
:invalid-feedback="identifierError"
>
<gl-dropdown
toggle-class="gl-mb-0"
:text="selectedEventName"
:name="fieldName"
:disabled="disabled"
menu-class="gl-overflow-hidden!"
block
>
<gl-dropdown-item
v-for="{ text, value } in eventsList"
:key="`${eventType}-${value}`"
:value="value"
@click="$emit('update-identifier', value)"
>{{ text }}</gl-dropdown-item
>
</gl-dropdown>
</gl-form-group>
</template>
<script>
import { GlFormGroup } from '@gitlab/ui';
import LabelsSelector from '../labels_selector.vue';
export default {
name: 'CustomStageEventLabelField',
components: {
GlFormGroup,
LabelsSelector,
},
props: {
index: {
type: Number,
required: true,
},
eventType: {
type: String,
required: true,
},
fieldLabel: {
type: String,
required: true,
},
requiresLabel: {
type: Boolean,
required: true,
},
initialGroupLabels: {
type: Array,
required: true,
},
hasLabelError: {
type: Boolean,
required: false,
default: false,
},
labelError: {
type: String,
required: false,
default: '',
},
selectedLabelIds: {
type: Array,
required: false,
default: () => [],
},
},
computed: {
fieldName() {
const { eventType, index } = this;
return `custom-stage-${eventType}-label-${index}`;
},
},
};
</script>
<template>
<div class="gl-w-half gl-ml-2">
<transition name="fade">
<gl-form-group
v-if="requiresLabel"
:data-testid="fieldName"
:label="fieldLabel"
:state="hasLabelError"
:invalid-feedback="labelError"
>
<labels-selector
:initial-data="initialGroupLabels"
:selected-label-ids="selectedLabelIds"
:name="fieldName"
@select-label="$emit('update-label', $event)"
/>
</gl-form-group>
</transition>
</div>
</template>
<script> <script>
import { GlFormGroup, GlFormInput, GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import { isLabelEvent, getLabelEventsIdentifiers, uniqById } from '../../utils'; import { isLabelEvent, getLabelEventsIdentifiers, uniqById } from '../../utils';
import LabelsSelector from '../labels_selector.vue';
import { i18n } from './constants'; import { i18n } from './constants';
import CustomStageEventField from './custom_stage_event_field.vue';
import CustomStageEventLabelField from './custom_stage_event_label_field.vue';
import StageFieldActions from './stage_field_actions.vue'; import StageFieldActions from './stage_field_actions.vue';
import { startEventOptions, endEventOptions } from './utils'; import { startEventOptions, endEventOptions } from './utils';
...@@ -11,9 +12,8 @@ export default { ...@@ -11,9 +12,8 @@ export default {
components: { components: {
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
GlDropdown, CustomStageEventField,
GlDropdownItem, CustomStageEventLabelField,
LabelsSelector,
StageFieldActions, StageFieldActions,
}, },
props: { props: {
...@@ -21,6 +21,10 @@ export default { ...@@ -21,6 +21,10 @@ export default {
type: Number, type: Number,
required: true, required: true,
}, },
stageLabel: {
type: String,
required: true,
},
totalStages: { totalStages: {
type: Number, type: Number,
required: true, required: true,
...@@ -107,6 +111,7 @@ export default { ...@@ -107,6 +111,7 @@ export default {
<div class="gl-display-flex"> <div class="gl-display-flex">
<gl-form-group <gl-form-group
class="gl-flex-grow-1" class="gl-flex-grow-1"
:label="stageLabel"
:state="hasFieldErrors('name')" :state="hasFieldErrors('name')"
:invalid-feedback="fieldErrorMessage('name')" :invalid-feedback="fieldErrorMessage('name')"
:data-testid="`custom-stage-name-${index}`" :data-testid="`custom-stage-name-${index}`"
...@@ -123,6 +128,7 @@ export default { ...@@ -123,6 +128,7 @@ export default {
</gl-form-group> </gl-form-group>
<stage-field-actions <stage-field-actions
v-if="hasMultipleStages" v-if="hasMultipleStages"
class="gl-mt-6"
:index="index" :index="index"
:stage-count="totalStages" :stage-count="totalStages"
:can-remove="true" :can-remove="true"
...@@ -131,91 +137,51 @@ export default { ...@@ -131,91 +137,51 @@ export default {
/> />
</div> </div>
<div class="gl-display-flex gl-justify-content-between"> <div class="gl-display-flex gl-justify-content-between">
<gl-form-group <custom-stage-event-field
:data-testid="`custom-stage-start-event-${index}`" event-type="start-event"
class="gl-w-half gl-mr-2" :index="index"
:label="$options.i18n.FORM_FIELD_START_EVENT" :field-label="$options.i18n.FORM_FIELD_START_EVENT"
:state="hasFieldErrors('startEventIdentifier')" :selected-event-name="selectedStartEventName"
:invalid-feedback="fieldErrorMessage('startEventIdentifier')" :events-list="startEvents"
> :identifier-error="fieldErrorMessage('startEventIdentifier')"
<gl-dropdown :has-identifier-error="hasFieldErrors('startEventIdentifier')"
toggle-class="gl-mb-0" @update-identifier="$emit('input', { field: 'startEventIdentifier', value: $event })"
:text="selectedStartEventName" />
:name="`custom-stage-start-id-${index}`" <custom-stage-event-label-field
menu-class="gl-overflow-hidden!" event-type="start-event"
block :index="index"
> :field-label="$options.i18n.FORM_FIELD_START_EVENT_LABEL"
<gl-dropdown-item :initial-group-labels="initialGroupLabels"
v-for="{ text, value } in startEvents" :requires-label="startEventRequiresLabel"
:key="`start-event-${value}`" :label-error="fieldErrorMessage('startEventLabelId')"
:value="value" :has-label-error="hasFieldErrors('startEventLabelId')"
@click="$emit('input', { field: 'startEventIdentifier', value })" :selected-label-ids="[stage.startEventLabelId]"
>{{ text }}</gl-dropdown-item @update-label="$emit('input', { field: 'startEventLabelId', value: $event })"
> />
</gl-dropdown>
</gl-form-group>
<div class="gl-w-half gl-ml-2">
<transition name="fade">
<gl-form-group
v-if="startEventRequiresLabel"
:data-testid="`custom-stage-start-event-label-${index}`"
:label="$options.i18n.FORM_FIELD_START_EVENT_LABEL"
:state="hasFieldErrors('startEventLabelId')"
:invalid-feedback="fieldErrorMessage('startEventLabelId')"
>
<labels-selector
:initial-data="initialGroupLabels"
:selected-label-ids="[stage.startEventLabelId]"
:name="`custom-stage-start-label-${index}`"
@select-label="$emit('input', { field: 'startEventLabelId', value: $event })"
/>
</gl-form-group>
</transition>
</div>
</div> </div>
<div class="gl-display-flex gl-justify-content-between"> <div class="gl-display-flex gl-justify-content-between">
<gl-form-group <custom-stage-event-field
:data-testid="`custom-stage-end-event-${index}`" event-type="end-event"
class="gl-w-half gl-mr-2" :index="index"
:label="$options.i18n.FORM_FIELD_END_EVENT" :disabled="!hasStartEvent"
:state="hasFieldErrors('endEventIdentifier')" :field-label="$options.i18n.FORM_FIELD_END_EVENT"
:invalid-feedback="fieldErrorMessage('endEventIdentifier')" :selected-event-name="selectedEndEventName"
> :events-list="endEvents"
<gl-dropdown :identifier-error="fieldErrorMessage('endEventIdentifier')"
toggle-class="gl-mb-0" :has-identifier-error="hasFieldErrors('endEventIdentifier')"
:text="selectedEndEventName" @update-identifier="$emit('input', { field: 'endEventIdentifier', value: $event })"
:name="`custom-stage-end-id-${index}`" />
:disabled="!hasStartEvent" <custom-stage-event-label-field
menu-class="gl-overflow-hidden!" event-type="end-event"
block :index="index"
> :field-label="$options.i18n.FORM_FIELD_END_EVENT_LABEL"
<gl-dropdown-item :initial-group-labels="initialGroupLabels"
v-for="{ text, value } in endEvents" :requires-label="endEventRequiresLabel"
:key="`end-event-${value}`" :label-error="fieldErrorMessage('endEventLabelId')"
:value="value" :has-label-error="hasFieldErrors('endEventLabelId')"
@click="$emit('input', { field: 'endEventIdentifier', value })" :selected-label-ids="[stage.endEventLabelId]"
>{{ text }}</gl-dropdown-item @update-label="$emit('input', { field: 'endEventLabelId', value: $event })"
> />
</gl-dropdown>
</gl-form-group>
<div class="gl-w-half gl-ml-2">
<transition name="fade">
<gl-form-group
v-if="endEventRequiresLabel"
:data-testid="`custom-stage-end-event-label-${index}`"
:label="$options.i18n.FORM_FIELD_END_EVENT_LABEL"
:state="hasFieldErrors('endEventLabelId')"
:invalid-feedback="fieldErrorMessage('endEventLabelId')"
>
<labels-selector
:initial-data="initialGroupLabels"
:selected-label-ids="[stage.endEventLabelId]"
:name="`custom-stage-end-label-${index}`"
@select-label="$emit('input', { field: 'endEventLabelId', value: $event })"
/>
</gl-form-group>
</transition>
</div>
</div> </div>
</div> </div>
</template> </template>
...@@ -26,6 +26,10 @@ export default { ...@@ -26,6 +26,10 @@ export default {
type: Number, type: Number,
required: true, required: true,
}, },
stageLabel: {
type: String,
required: true,
},
totalStages: { totalStages: {
type: Number, type: Number,
required: true, required: true,
...@@ -63,6 +67,7 @@ export default { ...@@ -63,6 +67,7 @@ export default {
<div class="gl-display-flex"> <div class="gl-display-flex">
<gl-form-group <gl-form-group
class="gl-flex-grow-1 gl-mb-0" class="gl-flex-grow-1 gl-mb-0"
:label="stageLabel"
:state="isValid('name')" :state="isValid('name')"
:invalid-feedback="renderError('name')" :invalid-feedback="renderError('name')"
:data-testid="`default-stage-name-${index}`" :data-testid="`default-stage-name-${index}`"
...@@ -79,6 +84,7 @@ export default { ...@@ -79,6 +84,7 @@ export default {
<!-- eslint-enable vue/no-mutating-props --> <!-- eslint-enable vue/no-mutating-props -->
</gl-form-group> </gl-form-group>
<stage-field-actions <stage-field-actions
class="gl-mt-6"
:index="index" :index="index"
:stage-count="totalStages" :stage-count="totalStages"
@move="$emit('move', $event)" @move="$emit('move', $event)"
......
...@@ -388,12 +388,9 @@ export default { ...@@ -388,12 +388,9 @@ export default {
:key="stage.id || stage.transitionKey" :key="stage.id || stage.transitionKey"
> >
<hr class="gl-my-3" /> <hr class="gl-my-3" />
<span
class="gl-display-flex gl-m-0 gl-vertical-align-middle gl-mr-2 gl-font-weight-bold gl-display-flex gl-pb-3"
>{{ stageGroupLabel(activeStageIndex) }}</span
>
<custom-stage-fields <custom-stage-fields
v-if="stage.custom" v-if="stage.custom"
:stage-label="stageGroupLabel(activeStageIndex)"
:stage="stage" :stage="stage"
:stage-events="formEvents" :stage-events="formEvents"
:index="activeStageIndex" :index="activeStageIndex"
...@@ -406,6 +403,7 @@ export default { ...@@ -406,6 +403,7 @@ export default {
/> />
<default-stage-fields <default-stage-fields
v-else v-else
:stage-label="stageGroupLabel(activeStageIndex)"
:stage="stage" :stage="stage"
:stage-events="formEvents" :stage-events="formEvents"
:index="activeStageIndex" :index="activeStageIndex"
......
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import {
i18n,
ERRORS,
} from 'ee/analytics/cycle_analytics/components/create_value_stream_form/constants';
import CustomStageEventField from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_event_field.vue';
import { customStageEvents as stageEvents } from '../../mock_data';
const formatStartEventOpts = (_events) => [
{ text: 'Select start event', value: null },
..._events
.filter((ev) => ev.canBeStartEvent)
.map(({ name: text, identifier: value }) => ({ text, value })),
];
const index = 0;
const eventType = 'stage-start-event';
const fieldLabel = i18n.FORM_FIELD_START_EVENT;
const selectedEventName = i18n.SELECT_START_EVENT;
const eventsList = formatStartEventOpts(stageEvents);
const selectedEventIndex = 1; // index `0` is the default select text
const firstEvent = eventsList[selectedEventIndex];
const identifierError = ERRORS.START_EVENT_REQUIRED;
const defaultProps = {
index,
eventType,
eventsList,
fieldLabel,
selectedEventName,
};
describe('CustomStageEventField', () => {
function createComponent(props = {}) {
return shallowMountExtended(CustomStageEventField, {
propsData: {
...defaultProps,
...props,
},
});
}
let wrapper = null;
const findEventField = () => wrapper.findByTestId(`custom-stage-${eventType}-${index}`);
const findEventDropdown = () => findEventField().findComponent(GlDropdown);
const findEventDropdownItems = () => findEventField().findAllComponents(GlDropdownItem);
const findEventDropdownItem = (itemIndex = 0) => findEventDropdownItems().at(itemIndex);
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('Event dropdown', () => {
it('renders the event dropdown', () => {
expect(findEventField().exists()).toBe(true);
expect(findEventField().attributes('label')).toBe(fieldLabel);
expect(findEventDropdown().attributes('disabled')).toBeUndefined();
expect(findEventDropdown().attributes('text')).toBe(selectedEventName);
});
it('renders each item in the event list', () => {
const ev = findEventDropdownItems().wrappers.map((d) => d.text());
const eventsListText = eventsList.map(({ text }) => text);
expect(eventsListText).toEqual(ev);
});
it('emits the `update-identifier` event when an event is selected', () => {
expect(wrapper.emitted('update-identifier')).toBeUndefined();
findEventDropdownItem(selectedEventIndex).vm.$emit('click');
expect(wrapper.emitted('update-identifier')[0]).toEqual([firstEvent.value]);
});
it('sets disables the dropdown when the disabled prop is set', async () => {
expect(findEventDropdown().attributes('disabled')).toBeUndefined();
wrapper = await createComponent({ disabled: true });
expect(findEventDropdown().attributes('disabled')).toBe('true');
});
});
describe('with an event field error', () => {
beforeEach(() => {
wrapper = createComponent({
hasIdentifierError: true,
identifierError,
});
});
it('sets the form group error state', () => {
expect(findEventField().attributes('state')).toBe('true');
expect(findEventField().attributes('invalid-feedback')).toBe(identifierError);
});
});
});
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import {
i18n,
ERRORS,
} from 'ee/analytics/cycle_analytics/components/create_value_stream_form/constants';
import CustomStageEventLabelField from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_event_label_field.vue';
import LabelsSelector from 'ee/analytics/cycle_analytics/components/labels_selector.vue';
import { groupLabels as initialGroupLabels } from '../../mock_data';
const index = 0;
const eventType = 'start-event';
const fieldLabel = i18n.FORM_FIELD_START_EVENT_LABEL;
const labelError = ERRORS.INVALID_EVENT_PAIRS;
const [selectedLabel, secondLabel] = initialGroupLabels;
const selectedLabelIds = [selectedLabel.id, secondLabel.id];
const defaultProps = {
index,
eventType,
fieldLabel,
requiresLabel: true,
initialGroupLabels,
labelError,
selectedLabelIds: [],
};
describe('CustomStageEventLabelField', () => {
function createComponent(props = {}) {
return shallowMountExtended(CustomStageEventLabelField, {
propsData: {
...defaultProps,
...props,
},
});
}
let wrapper = null;
const findEventLabelField = () =>
wrapper.findByTestId(`custom-stage-${eventType}-label-${index}`);
const findEventLabelDropdown = () => findEventLabelField().findComponent(LabelsSelector);
const findEventLabelDropdownProp = (prop) => findEventLabelDropdown().props(prop);
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('Label dropdown', () => {
it('renders the event dropdown', () => {
expect(findEventLabelField().exists()).toBe(true);
expect(findEventLabelField().attributes('label')).toBe(fieldLabel);
});
it('renders each item in the labels list', () => {
const lt = findEventLabelDropdownProp('initialData').map((d) => d.name);
const labelsText = initialGroupLabels.map(({ name }) => name);
expect(labelsText).toEqual(lt);
});
it('has no selected labels', () => {
const selected = findEventLabelDropdownProp('selectedLabelIds');
expect(selected).toEqual([]);
});
it('emits the `update-label` event when a label is selected', () => {
expect(wrapper.emitted('update-label')).toBeUndefined();
findEventLabelDropdown().vm.$emit('select-label', selectedLabel.id);
expect(wrapper.emitted('update-label')[0]).toEqual([selectedLabel.id]);
});
});
describe('with selected labels', () => {
beforeEach(() => {
wrapper = createComponent({ selectedLabelIds });
});
it('sets the selected labels', () => {
const selected = findEventLabelDropdownProp('selectedLabelIds');
expect(selected).toEqual(selectedLabelIds);
});
});
describe('with `requiresLabel=false`', () => {
beforeEach(() => {
wrapper = createComponent({ requiresLabel: false });
});
it('sets the form group error state', () => {
expect(findEventLabelField().exists()).toBe(false);
});
});
describe('with an event field error', () => {
beforeEach(() => {
wrapper = createComponent({
hasLabelError: true,
labelError,
});
});
it('sets the form group error state', () => {
expect(findEventLabelField().attributes('state')).toBe('true');
expect(findEventLabelField().attributes('invalid-feedback')).toBe(labelError);
});
});
});
import { GlDropdown, GlFormInput } from '@gitlab/ui'; import { GlFormInput } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import CustomStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_fields.vue'; import CustomStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_fields.vue';
import CustomStageEventField from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_event_field.vue';
import CustomStageEventLabelField from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_event_label_field.vue';
import StageFieldActions from 'ee/analytics/cycle_analytics/components/create_value_stream_form/stage_field_actions.vue'; import StageFieldActions from 'ee/analytics/cycle_analytics/components/create_value_stream_form/stage_field_actions.vue';
import LabelsSelector from 'ee/analytics/cycle_analytics/components/labels_selector.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { import {
customStageEvents as stageEvents, customStageEvents as stageEvents,
labelStartEvent, labelStartEvent,
labelEndEvent, labelEndEvent,
customStageEndEvents as endEvents, customStageEndEvents as endEvents,
groupLabels as defaultGroupLabels,
} from '../../mock_data'; } from '../../mock_data';
import { emptyState, emptyErrorsState, firstLabel } from './mock_data'; import { emptyState, emptyErrorsState, firstLabel } from './mock_data';
...@@ -39,11 +41,13 @@ describe('CustomStageFields', () => { ...@@ -39,11 +41,13 @@ describe('CustomStageFields', () => {
return extendedWrapper( return extendedWrapper(
shallowMount(CustomStageFields, { shallowMount(CustomStageFields, {
propsData: { propsData: {
defaultGroupLabels,
stage, stage,
errors, errors,
stageEvents, stageEvents,
index: 0, index: 0,
totalStages: 3, totalStages: 3,
stageLabel: 'Stage 1',
...props, ...props,
}, },
stubs: { stubs: {
...@@ -56,21 +60,17 @@ describe('CustomStageFields', () => { ...@@ -56,21 +60,17 @@ describe('CustomStageFields', () => {
let wrapper = null; let wrapper = null;
const getDropdown = (dropdownEl) => dropdownEl.findComponent(GlDropdown);
const getLabelSelect = (dropdownEl) => dropdownEl.findComponent(LabelsSelector);
const findName = (index = 0) => wrapper.findByTestId(`custom-stage-name-${index}`); const findName = (index = 0) => wrapper.findByTestId(`custom-stage-name-${index}`);
const findStartEvent = (index = 0) => wrapper.findByTestId(`custom-stage-start-event-${index}`);
const findEndEvent = (index = 0) => wrapper.findByTestId(`custom-stage-end-event-${index}`);
const findStartEventLabel = (index = 0) => const findStartEventLabel = (index = 0) =>
wrapper.findByTestId(`custom-stage-start-event-label-${index}`); wrapper.findByTestId(`custom-stage-start-event-label-${index}`);
const findEndEventLabel = (index = 0) => const findEndEventLabel = (index = 0) =>
wrapper.findByTestId(`custom-stage-end-event-label-${index}`); wrapper.findByTestId(`custom-stage-end-event-label-${index}`);
const findNameField = () => findName().findComponent(GlFormInput); const findNameField = () => findName().findComponent(GlFormInput);
const findStartEventField = () => getDropdown(findStartEvent()); const findStartEventField = () => wrapper.findAllComponents(CustomStageEventField).at(0);
const findEndEventField = () => getDropdown(findEndEvent()); const findEndEventField = () => wrapper.findAllComponents(CustomStageEventField).at(1);
const findStartEventLabelField = () => getLabelSelect(findStartEventLabel()); const findStartEventLabelField = () =>
const findEndEventLabelField = () => getLabelSelect(findEndEventLabel()); wrapper.findAllComponents(CustomStageEventLabelField).at(0);
const findEndEventLabelField = () => wrapper.findAllComponents(CustomStageEventLabelField).at(1);
const findStageFieldActions = () => wrapper.findComponent(StageFieldActions); const findStageFieldActions = () => wrapper.findComponent(StageFieldActions);
beforeEach(() => { beforeEach(() => {
...@@ -135,13 +135,13 @@ describe('CustomStageFields', () => { ...@@ -135,13 +135,13 @@ describe('CustomStageFields', () => {
}); });
it('will display the start event label field if a label event is selected', () => { it('will display the start event label field if a label event is selected', () => {
expect(findStartEventLabel().exists()).toEqual(true); expect(findStartEventLabelField().exists()).toEqual(true);
}); });
it('will emit the `input` event when the start event label field when selected', async () => { it('will emit the `input` event when the start event label field when selected', async () => {
expect(wrapper.emitted('input')).toBeUndefined(); expect(wrapper.emitted('input')).toBeUndefined();
findStartEventLabelField().vm.$emit('select-label', firstLabel.id); findStartEventLabelField().vm.$emit('update-label', firstLabel.id);
expect(wrapper.emitted('input')[0]).toEqual([ expect(wrapper.emitted('input')[0]).toEqual([
{ field: 'startEventLabelId', value: firstLabel.id }, { field: 'startEventLabelId', value: firstLabel.id },
...@@ -180,13 +180,13 @@ describe('CustomStageFields', () => { ...@@ -180,13 +180,13 @@ describe('CustomStageFields', () => {
}); });
it('will display the end event label field if a label event is selected', () => { it('will display the end event label field if a label event is selected', () => {
expect(findEndEventLabel().exists()).toEqual(true); expect(findEndEventLabelField().exists()).toEqual(true);
}); });
it('will emit the `input` event when the start event label field when selected', async () => { it('will emit the `input` event when the start event label field when selected', async () => {
expect(wrapper.emitted('input')).toBeUndefined(); expect(wrapper.emitted('input')).toBeUndefined();
findEndEventLabelField().vm.$emit('select-label', firstLabel.id); findEndEventLabelField().vm.$emit('update-label', firstLabel.id);
expect(wrapper.emitted('input')[0]).toEqual([ expect(wrapper.emitted('input')[0]).toEqual([
{ field: 'endEventLabelId', value: firstLabel.id }, { field: 'endEventLabelId', value: firstLabel.id },
......
...@@ -28,6 +28,7 @@ describe('DefaultStageFields', () => { ...@@ -28,6 +28,7 @@ describe('DefaultStageFields', () => {
stage, stage,
errors, errors,
stageEvents, stageEvents,
stageLabel: 'Stage 1',
}, },
stubs: { stubs: {
'labels-selector': false, 'labels-selector': false,
......
...@@ -3,10 +3,12 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -3,10 +3,12 @@ import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { import {
i18n,
PRESET_OPTIONS_BLANK, PRESET_OPTIONS_BLANK,
PRESET_OPTIONS_DEFAULT, PRESET_OPTIONS_DEFAULT,
} from 'ee/analytics/cycle_analytics/components/create_value_stream_form/constants'; } from 'ee/analytics/cycle_analytics/components/create_value_stream_form/constants';
import CustomStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_fields.vue'; import CustomStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_fields.vue';
import CustomStageEventField from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_event_field.vue';
import DefaultStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/default_stage_fields.vue'; import DefaultStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/default_stage_fields.vue';
import ValueStreamForm from 'ee/analytics/cycle_analytics/components/value_stream_form.vue'; import ValueStreamForm from 'ee/analytics/cycle_analytics/components/value_stream_form.vue';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
...@@ -15,7 +17,12 @@ import { ...@@ -15,7 +17,12 @@ import {
convertObjectPropsToCamelCase, convertObjectPropsToCamelCase,
convertObjectPropsToSnakeCase, convertObjectPropsToSnakeCase,
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import { customStageEvents as formEvents, defaultStageConfig, rawCustomStage } from '../mock_data'; import {
customStageEvents as formEvents,
defaultStageConfig,
rawCustomStage,
groupLabels as defaultGroupLabels,
} from '../mock_data';
const scrollIntoViewMock = jest.fn(); const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
...@@ -55,7 +62,7 @@ describe('ValueStreamForm', () => { ...@@ -55,7 +62,7 @@ describe('ValueStreamForm', () => {
state: { state: {
isCreatingValueStream: false, isCreatingValueStream: false,
formEvents, formEvents,
defaultGroupLabels: null, defaultGroupLabels,
...state, ...state,
}, },
actions: { actions: {
...@@ -96,12 +103,16 @@ describe('ValueStreamForm', () => { ...@@ -96,12 +103,16 @@ describe('ValueStreamForm', () => {
const findRestoreStageButton = (index) => wrapper.findByTestId(`stage-action-restore-${index}`); const findRestoreStageButton = (index) => wrapper.findByTestId(`stage-action-restore-${index}`);
const findHiddenStages = () => wrapper.findAllByTestId('vsa-hidden-stage').wrappers; const findHiddenStages = () => wrapper.findAllByTestId('vsa-hidden-stage').wrappers;
const findBtn = (btn) => findModal().props(btn); const findBtn = (btn) => findModal().props(btn);
const findCustomStageEventField = (index = 0) =>
wrapper.findAllComponents(CustomStageEventField).at(index);
const clickSubmit = () => findModal().vm.$emit('primary', mockEvent); const clickSubmit = () => findModal().vm.$emit('primary', mockEvent);
const clickAddStage = () => findModal().vm.$emit('secondary', mockEvent); const clickAddStage = () => findModal().vm.$emit('secondary', mockEvent);
const clickRestoreStageAtIndex = (index) => findRestoreStageButton(index).vm.$emit('click'); const clickRestoreStageAtIndex = (index) => findRestoreStageButton(index).vm.$emit('click');
const expectFieldError = (testId, error = '') => const expectFieldError = (testId, error = '') =>
expect(wrapper.findByTestId(testId).attributes('invalid-feedback')).toBe(error); expect(wrapper.findByTestId(testId).attributes('invalid-feedback')).toBe(error);
const expectCustomFieldError = (index, attr, error = '') =>
expect(findCustomStageEventField(index).attributes(attr)).toBe(error);
const expectStageTransitionKeys = (stages) => const expectStageTransitionKeys = (stages) =>
stages.forEach((stage) => expect(stage.transitionKey).toContain('stage-')); stages.forEach((stage) => expect(stage.transitionKey).toContain('stage-'));
...@@ -112,7 +123,7 @@ describe('ValueStreamForm', () => { ...@@ -112,7 +123,7 @@ describe('ValueStreamForm', () => {
describe('default state', () => { describe('default state', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent({ state: { defaultGroupLabels: null } });
}); });
it('has the extended fields', () => { it('has the extended fields', () => {
...@@ -120,7 +131,7 @@ describe('ValueStreamForm', () => { ...@@ -120,7 +131,7 @@ describe('ValueStreamForm', () => {
}); });
it('sets the submit action text to "Create Value Stream"', () => { it('sets the submit action text to "Create Value Stream"', () => {
expect(findBtn('actionPrimary').text).toBe('Create Value Stream'); expect(findBtn('actionPrimary').text).toBe(i18n.FORM_TITLE);
}); });
describe('Preset selector', () => { describe('Preset selector', () => {
...@@ -223,8 +234,9 @@ describe('ValueStreamForm', () => { ...@@ -223,8 +234,9 @@ describe('ValueStreamForm', () => {
}); });
expectFieldError('custom-stage-name-0', initialFormStageErrors.stages[0].name[0]); expectFieldError('custom-stage-name-0', initialFormStageErrors.stages[0].name[0]);
expectFieldError( expectCustomFieldError(
'custom-stage-end-event-0', 1,
'identifiererror',
initialFormStageErrors.stages[0].endEventIdentifier[0], initialFormStageErrors.stages[0].endEventIdentifier[0],
); );
}); });
...@@ -247,7 +259,7 @@ describe('ValueStreamForm', () => { ...@@ -247,7 +259,7 @@ describe('ValueStreamForm', () => {
}); });
it('sets the submit action text to "Save value stream"', () => { it('sets the submit action text to "Save value stream"', () => {
expect(findBtn('actionPrimary').text).toBe('Save value stream'); expect(findBtn('actionPrimary').text).toBe(i18n.EDIT_FORM_ACTION);
}); });
it('does not display any hidden stages', () => { it('does not display any hidden stages', () => {
...@@ -324,7 +336,7 @@ describe('ValueStreamForm', () => { ...@@ -324,7 +336,7 @@ describe('ValueStreamForm', () => {
}); });
it('has the add stage button', () => { it('has the add stage button', () => {
expect(findBtn('actionSecondary')).toMatchObject({ text: 'Add another stage' }); expect(findBtn('actionSecondary')).toMatchObject({ text: i18n.BTN_ADD_ANOTHER_STAGE });
}); });
it('adds a blank custom stage when clicked', async () => { it('adds a blank custom stage when clicked', async () => {
......
...@@ -10126,9 +10126,6 @@ msgstr "" ...@@ -10126,9 +10126,6 @@ msgstr ""
msgid "CreateValueStreamForm|Code stage start" msgid "CreateValueStreamForm|Code stage start"
msgstr "" msgstr ""
msgid "CreateValueStreamForm|Create Value Stream"
msgstr ""
msgid "CreateValueStreamForm|Create from default template" msgid "CreateValueStreamForm|Create from default template"
msgstr "" msgstr ""
...@@ -10138,13 +10135,16 @@ msgstr "" ...@@ -10138,13 +10135,16 @@ msgstr ""
msgid "CreateValueStreamForm|Create new Value Stream" msgid "CreateValueStreamForm|Create new Value Stream"
msgstr "" msgstr ""
msgid "CreateValueStreamForm|Create value stream"
msgstr ""
msgid "CreateValueStreamForm|Default stages" msgid "CreateValueStreamForm|Default stages"
msgstr "" msgstr ""
msgid "CreateValueStreamForm|Default stages can only be hidden or re-ordered" msgid "CreateValueStreamForm|Default stages can only be hidden or re-ordered"
msgstr "" msgstr ""
msgid "CreateValueStreamForm|Edit Value Stream" msgid "CreateValueStreamForm|Edit value stream"
msgstr "" msgstr ""
msgid "CreateValueStreamForm|Editing stage" msgid "CreateValueStreamForm|Editing stage"
......
...@@ -59,7 +59,7 @@ module CycleAnalyticsHelpers ...@@ -59,7 +59,7 @@ module CycleAnalyticsHelpers
def save_value_stream(custom_value_stream_name) def save_value_stream(custom_value_stream_name)
fill_in 'create-value-stream-name', with: custom_value_stream_name fill_in 'create-value-stream-name', with: custom_value_stream_name
page.find_button(s_('CreateValueStreamForm|Create Value Stream')).click page.find_button(s_('CreateValueStreamForm|Create value stream')).click
wait_for_requests wait_for_requests
end end
......
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