Commit f2c10787 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '267536-vsa-move-custom-value-stream-fields-to-component' into 'master'

Refactor custom stage form specs

See merge request gitlab-org/gitlab!49651
parents 0e949d15 3e5bccea
......@@ -138,7 +138,7 @@ export default {
/>
</gl-form-group>
</div>
<div v-if="endEventRequiresLabel" class="w-50 ml-1">
<div v-if="endEventRequiresLabel" class="gl-w-half gl-ml-2">
<gl-form-group
data-testid="custom-stage-end-event-label"
:label="$options.I18N.FORM_FIELD_END_EVENT_LABEL"
......
......@@ -172,7 +172,7 @@ export default {
<div v-if="isLoading">
<gl-loading-icon class="mt-4" size="md" />
</div>
<form v-else class="custom-stage-form m-4 mt-0">
<form v-else class="custom-stage-form m-4 gl-mt-0">
<div class="gl-mb-1 gl-display-flex gl-justify-content-space-between gl-align-items-center">
<h4>{{ formTitle }}</h4>
<gl-dropdown
......@@ -191,7 +191,7 @@ export default {
>{{ stage.title }}</gl-dropdown-item
>
</template>
<p v-else class="mx-3 my-2">{{ $options.I18N.RECOVER_STAGES_VISIBLE }}</p>
<p v-else class="gl-mx-5 gl-my-3">{{ $options.I18N.RECOVER_STAGES_VISIBLE }}</p>
</gl-dropdown>
</div>
<custom-stage-form-fields
......@@ -201,7 +201,7 @@ export default {
:events="events"
@update="handleUpdateFields"
/>
<div class="custom-stage-form-actions">
<div>
<gl-button
:disabled="!isDirty"
category="primary"
......@@ -221,7 +221,7 @@ export default {
{{ saveStageText }}
</gl-button>
</div>
<div class="mt-2">
<div class="gl-mt-3">
<gl-sprintf
:message="
__(
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CustomStageForm Editing a custom stage isSavingCustomStage=true displays a loading icon 1`] = `
"<button data-testid=\\"save-custom-stage\\" type=\\"button\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\"><span class=\\"gl-spinner-container\\"><span aria-label=\\"Loading\\" class=\\"align-text-bottom gl-spinner gl-spinner-dark gl-spinner-sm\\"></span></span>
"<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" data-testid=\\"save-custom-stage\\">
<gl-loading-icon-stub label=\\"Loading\\" size=\\"sm\\" color=\\"dark\\" inline=\\"true\\"></gl-loading-icon-stub>
Update stage
</span></button>"
</gl-button-stub>"
`;
exports[`CustomStageForm isSavingCustomStage=true displays a loading icon 1`] = `
"<button data-testid=\\"save-custom-stage\\" type=\\"button\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\">
<!---->
<!----> <span class=\\"gl-button-text\\"><span class=\\"gl-spinner-container\\"><span aria-label=\\"Loading\\" class=\\"align-text-bottom gl-spinner gl-spinner-dark gl-spinner-sm\\"></span></span>
"<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" data-testid=\\"save-custom-stage\\">
<gl-loading-icon-stub label=\\"Loading\\" size=\\"sm\\" color=\\"dark\\" inline=\\"true\\"></gl-loading-icon-stub>
Add stage
</span></button>"
</gl-button-stub>"
`;
......@@ -91,7 +91,7 @@ describe('CustomStageFields', () => {
describe.each([
['Start event label', findStartEventLabel],
['End event label', findStartEventLabel],
['End event label', findEndEventLabel],
])('Default state', (field, finder) => {
it(`field '${field}' is hidden by default`, () => {
expect(finder(wrapper).exists()).toBe(false);
......@@ -172,7 +172,7 @@ describe('CustomStageFields', () => {
});
});
it('will display the start 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(wrapper).exists()).toEqual(true);
});
......
import { createLocalVue, mount } from '@vue/test-utils';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlSprintf, GlDropdownItem } from '@gitlab/ui';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex';
import CustomStageForm from 'ee/analytics/cycle_analytics/components/custom_stage_form.vue';
import { initializeFormData } from 'ee/analytics/cycle_analytics/components/create_value_stream_form/utils';
import CustomStageFields from 'ee/analytics/cycle_analytics/components/create_value_stream_form/custom_stage_fields.vue';
import { STAGE_ACTIONS } from 'ee/analytics/cycle_analytics/constants';
import customStagesStore from 'ee/analytics/cycle_analytics/store/modules/custom_stages';
import waitForPromises from 'helpers/wait_for_promises';
import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
import {
emptyState,
formInitialData,
minimumFields,
MERGE_REQUEST_CREATED,
MERGE_REQUEST_CLOSED,
ISSUE_CREATED,
ISSUE_CLOSED,
} from './create_value_stream_form/mock_data';
import {
endpoints,
groupLabels,
customStageEvents as events,
labelStartEvent,
labelStopEvent,
customStageStartEvents as startEvents,
customStageStopEvents as stopEvents,
customStageFormErrors,
} from '../mock_data';
const formInitialData = {
id: 74,
name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier,
startEventLabelId: groupLabels[0].id,
endEventIdentifier: labelStopEvent.identifier,
endEventLabelId: groupLabels[1].id,
};
const MERGE_REQUEST_CREATED = 'merge_request_created';
const MERGE_REQUEST_CLOSED = 'merge_request_closed';
const localVue = createLocalVue();
localVue.use(Vuex);
......@@ -56,9 +50,10 @@ describe('CustomStageForm', () => {
initialState = {},
initialRootGetters = {},
stubs = {},
props = {},
} = {}) {
return mount(CustomStageForm, {
return shallowMount(CustomStageForm, {
localVue,
store: fakeStore({ initialState, initialRootGetters }),
propsData: {
......@@ -66,7 +61,8 @@ describe('CustomStageForm', () => {
...props,
},
stubs: {
'labels-selector': false,
GlSprintf,
CustomStageFields,
...stubs,
},
});
......@@ -77,400 +73,112 @@ describe('CustomStageForm', () => {
const findEvent = ev => wrapper.emitted()[ev];
const sel = {
name: '[data-testid="custom-stage-name"] input',
startEvent: '[data-testid="custom-stage-start-event"] select',
startEventLabel: '[data-testid="custom-stage-start-event-label"]',
endEvent: '[data-testid="custom-stage-end-event"] select',
endEventLabel: '[data-testid="custom-stage-end-event-label"]',
submit: '[data-testid="save-custom-stage"]',
cancel: '[data-testid="cancel-custom-stage"]',
invalidFeedback: '.invalid-feedback',
recoverStageDropdown: '[data-testid="recover-hidden-stage-dropdown"]',
recoverStageDropdownTrigger: '[data-testid="recover-hidden-stage-dropdown"] .dropdown-toggle',
hiddenStageDropdownOption: '[data-testid="recover-hidden-stage-dropdown"] .dropdown-item',
};
function getDropdownOptions(_wrapper, dropdown) {
return _wrapper.find(dropdown).findAll('option');
}
function getDropdownOptionsArray(_wrapper, dropdown) {
return _wrapper
.find(dropdown)
.findAll('option')
.wrappers.map(w => w.attributes('value'));
}
const findSubmitButton = () => wrapper.find('[data-testid="save-custom-stage"]');
const findCancelButton = () => wrapper.find('[data-testid="cancel-custom-stage"]');
const findRecoverStageDropdown = () =>
wrapper.find('[data-testid="recover-hidden-stage-dropdown"]');
function getDropdownOption(_wrapper, dropdown, index) {
return getDropdownOptions(_wrapper, dropdown).at(index);
}
const findFieldErrors = field => wrapper.vm.errors[field];
function selectDropdownOption(_wrapper, dropdown, index) {
getDropdownOption(_wrapper, dropdown, index).setSelected();
}
// Valid start and end event pair: merge request created - merge request closed
const mergeRequestCreatedIndex = startEvents.findIndex(
e => e.identifier === MERGE_REQUEST_CREATED,
);
const mergeRequestCreatedDropdownIndex = mergeRequestCreatedIndex;
const mergeReqestCreatedEvent = startEvents[mergeRequestCreatedIndex];
const mergeRequestClosedDropdownIndex = mergeReqestCreatedEvent.allowedEndEvents.findIndex(
e => e === MERGE_REQUEST_CLOSED,
);
function setEventDropdowns({
startEventDropdownIndex = mergeRequestCreatedDropdownIndex,
stopEventDropdownIndex = mergeRequestClosedDropdownIndex,
} = {}) {
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return wrapper.vm.$nextTick().then(() => {
selectDropdownOption(wrapper, sel.endEvent, stopEventDropdownIndex);
const setFields = async (fields = minimumFields) => {
Object.entries(fields).forEach(([field, value]) => {
wrapper.find(CustomStageFields).vm.$emit('update', field, value);
});
}
await wrapper.vm.$nextTick();
};
const findNameField = _wrapper => _wrapper.find('[data-testid="custom-stage-name"]');
const findStartEventField = _wrapper => _wrapper.find('[data-testid="custom-stage-start-event"]');
const setNameField = (value = '') => setFields({ name: value });
function setNameField(_wrapper, value = '') {
wrapper.find(sel.name).setValue(value);
wrapper.find(sel.name).trigger('change');
return _wrapper.vm.$nextTick();
}
const setStartEvent = (value = MERGE_REQUEST_CREATED) =>
setFields({ startEventIdentifier: value });
const setEndEvent = (value = MERGE_REQUEST_CLOSED) => setFields({ endEventIdentifier: value });
const mockGroupLabelsRequest = () =>
new MockAdapter(axios).onGet(endpoints.groupLabels).reply(200, groupLabels);
beforeEach(() => {
beforeEach(async () => {
mock = mockGroupLabelsRequest();
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
mock.restore();
});
describe.each([
['Name', sel.name, true],
['Start event', sel.startEvent, true],
['End event', sel.endEvent, false],
['Submit', sel.submit, false],
['Cancel', sel.cancel, false],
])('Default state', (field, $sel, enabledState) => {
const state = enabledState ? 'enabled' : 'disabled';
it(`field '${field}' is ${state}`, () => {
const el = wrapper.find($sel);
expect(el.exists()).toEqual(true);
if (!enabledState) {
expect(el.attributes('disabled')).toEqual('disabled');
} else {
expect(el.attributes('disabled')).toBeUndefined();
}
describe('Default state', () => {
it('will set all fields to null', () => {
expect(wrapper.vm.fields).toMatchObject(emptyState);
});
});
describe('Helper text', () => {
it('displays the manual ordering helper text', () => {
expect(wrapper.text()).toContain(
'Note: Once a custom stage has been added you can re-order stages by dragging them into the desired position.',
expect(wrapper.html()).toContain(
'<strong>Note:</strong> Once a custom stage has been added you can re-order stages by dragging them into the desired position.',
);
});
});
describe('Name', () => {
describe('with a reserved name', () => {
beforeEach(() => {
beforeEach(async () => {
wrapper = createComponent();
return setNameField(wrapper, 'issue');
await setNameField('issue');
});
it('displays an error', () => {
expect(findNameField(wrapper).text()).toContain('Stage name already exists');
});
it('clears the error when the field changes', () => {
return setNameField(wrapper, 'not an issue').then(() => {
expect(findNameField(wrapper).text()).not.toContain('Stage name already exists');
});
});
});
});
describe('Start event', () => {
describe('with events', () => {
beforeEach(() => {
wrapper = createComponent();
expect(findFieldErrors('name')).toContain('Stage name already exists');
});
afterEach(() => {
wrapper.destroy();
});
it('selects events with canBeStartEvent=true for the start events dropdown', () => {
const select = wrapper.find(sel.startEvent);
it('clears the error when the field changes', async () => {
await setNameField('not an issue');
events
.filter(ev => ev.canBeStartEvent)
.forEach(ev => {
expect(select.html()).toHaveHtml(
`<option value="${ev.identifier}">${ev.name}</option>`,
);
});
});
it('does not select events with canBeStartEvent=false for the start events dropdown', () => {
const select = wrapper.find(sel.startEvent);
events
.filter(ev => !ev.canBeStartEvent)
.forEach(ev => {
expect(select.html()).not.toHaveHtml(
`<option value="${ev.identifier}">${ev.name}</option>`,
);
});
});
});
describe('start event label', () => {
beforeEach(() => {
mock = mockGroupLabelsRequest();
wrapper = createComponent();
return wrapper.vm.$nextTick();
});
afterEach(() => {
wrapper.destroy();
});
it('is hidden by default', () => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(false);
});
it('will display the start event label field if a label event is selected', () => {
wrapper.setData({
fields: {
startEventIdentifier: labelStartEvent.identifier,
},
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(true);
});
});
it('will set the "startEventLabelId" field when selected', () => {
const selectedLabelId = groupLabels[0].id;
expect(wrapper.vm.fields.startEventLabelId).toEqual(null);
wrapper.find(sel.startEvent).setValue(labelStartEvent.identifier);
return waitForPromises()
.then(() => {
wrapper
.find(sel.startEventLabel)
.findAll('.dropdown-item')
.at(0)
.trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
expect(wrapper.vm.fields.startEventLabelId).toEqual(selectedLabelId);
});
expect(findFieldErrors('name')).not.toContain('Stage name already exists');
});
});
});
describe('End event', () => {
const startEventArrayIndex = mergeRequestCreatedIndex;
const startEventDropdownIndex = startEventArrayIndex + 1;
const currAllowed = startEvents[startEventArrayIndex].allowedEndEvents;
beforeEach(() => {
wrapper = createComponent();
});
it('notifies that a start event needs to be selected first', () => {
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.text()).toContain('Please select a start event first');
});
});
it('clears notification when a start event is selected', () => {
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.text()).not.toContain('Please select a start event first');
});
});
it('is enabled when a start event is selected', () => {
const el = wrapper.find(sel.endEvent);
expect(el.attributes('disabled')).toEqual('disabled');
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return wrapper.vm.$nextTick().then(() => {
expect(el.attributes('disabled')).toBeUndefined();
});
});
it('will update the list of end events when a start event is changed', () => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const selectedStartEvent = startEvents[startEventDropdownIndex];
expect(stopOptions).toHaveLength(1);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return wrapper.vm.$nextTick().then(() => {
stopOptions = wrapper.find(sel.endEvent);
selectedStartEvent.allowedEndEvents.forEach(identifier => {
expect(stopOptions.html()).toContain(identifier);
});
});
it('sets an error if no start event is selected', () => {
expect(findFieldErrors('endEventIdentifier')).toContain('Please select a start event first');
});
it('will display all the valid end events', () => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const possibleEndEvents = stopEvents.filter(ev => currAllowed.includes(ev.identifier));
expect(stopOptions.at(0).html()).toEqual('<option value="">Select end event</option>');
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return wrapper.vm.$nextTick().then(() => {
stopOptions = wrapper.find(sel.endEvent);
possibleEndEvents.forEach(({ name, identifier }) => {
expect(stopOptions.html()).toContain(`<option value="${identifier}">${name}</option>`);
});
});
});
it('will not display end events that are not in the list of allowed end events', () => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const excludedEndEvents = stopEvents.filter(ev => !currAllowed.includes(ev.identifier));
expect(stopOptions.at(0).html()).toEqual('<option value="">Select end event</option>');
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
return wrapper.vm.$nextTick().then(() => {
stopOptions = wrapper.find(sel.endEvent);
excludedEndEvents.forEach(({ name, identifier }) => {
expect(wrapper.find(sel.endEvent).html()).not.toHaveHtml(
`<option value="${identifier}">${name}</option>`,
);
});
});
it('clears error when a start event is selected', async () => {
await setStartEvent();
expect(findFieldErrors('endEventIdentifier')).not.toContain(
'Please select a start event first',
);
});
describe('with a end event selected and a change to the start event', () => {
beforeEach(() => {
beforeEach(async () => {
wrapper = createComponent();
wrapper.setData({
fields: {
name: 'Cool stage',
startEventIdentifier: MERGE_REQUEST_CREATED,
startEventLabelId: null,
endEventIdentifier: MERGE_REQUEST_CLOSED,
endEventLabelId: null,
},
});
});
afterEach(() => {
wrapper.destroy();
});
it('notifies that a start event needs to be selected first', () => {
wrapper.setData({ fields: { startEventIdentifier: '' } });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.text()).toContain('Please select a start event first');
});
await setFields(minimumFields);
});
it('will notify if the current start and end event pair is not valid', () => {
selectDropdownOption(wrapper, sel.startEvent, 2);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.invalidFeedback).exists()).toEqual(true);
expect(wrapper.find(sel.invalidFeedback).text()).toContain(
'Start event changed, please select a valid end event',
);
});
});
it('will update the list of end events', () => {
const preEndEvents = getDropdownOptionsArray(wrapper, sel.endEvent);
selectDropdownOption(wrapper, sel.startEvent, 2);
return wrapper.vm.$nextTick().then(() => {
const opts = getDropdownOptionsArray(wrapper, sel.endEvent);
expect(preEndEvents).not.toEqual(opts);
});
});
it('will disable the submit button until a valid endEvent is selected', () => {
selectDropdownOption(wrapper, sel.startEvent, 2);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
});
});
});
describe('End event label', () => {
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('is hidden by default', () => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(false);
it('warns that the start event changed', async () => {
await setStartEvent('');
expect(findFieldErrors('endEventIdentifier')).toContain(
'Please select a start event first',
);
});
it('will display the end event label field if a label event is selected', () => {
expect(wrapper.find(sel.endEventLabel).exists()).toEqual(false);
it('warns if the current start and end event pair is not valid', async () => {
await setFields({ startEventIdentifier: 'fake_event_id' });
wrapper.setData({
fields: {
endEventIdentifier: labelStopEvent.identifier,
startEventIdentifier: labelStartEvent.identifier,
},
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.endEventLabel).exists()).toEqual(true);
});
expect(findFieldErrors('endEventIdentifier')).toContain(
'Start event changed, please select a valid end event',
);
});
it('will set the "endEventLabelId" field when selected', () => {
const selectedLabelId = groupLabels[1].id;
expect(wrapper.vm.fields.endEventLabelId).toEqual(null);
wrapper.setData({
fields: {
startEventIdentifier: labelStartEvent.identifier,
endEventIdentifier: labelStopEvent.identifier,
},
});
return waitForPromises()
.then(() => {
wrapper
.find(sel.endEventLabel)
.findAll('.dropdown-item')
.at(1)
.trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
expect(wrapper.vm.fields.endEventLabelId).toEqual(selectedLabelId);
});
it('will disable the submit button until a valid endEvent is selected', async () => {
expect(findSubmitButton().props('disabled')).toBe(false);
await setEndEvent('');
expect(findSubmitButton().props('disabled')).toBe(true);
});
});
});
......@@ -480,80 +188,43 @@ describe('CustomStageForm', () => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('has text `Add stage`', () => {
expect(wrapper.find(sel.submit).text()).toEqual('Add stage');
});
it('is enabled when all required fields are filled', () => {
const btn = wrapper.find(sel.submit);
expect(btn.attributes('disabled')).toEqual('disabled');
wrapper.find(sel.name).setValue('Cool stage');
return setEventDropdowns().then(() => {
expect(btn.attributes('disabled')).toBeUndefined();
});
expect(findSubmitButton().text()).toEqual('Add stage');
});
describe('with all fields set', () => {
const startEventDropdownIndex = 2;
const startEventArrayIndex = startEventDropdownIndex - 1;
const stopEventDropdownIndex = 1;
beforeEach(() => {
beforeEach(async () => {
wrapper = createComponent();
wrapper.find(sel.name).setValue('Cool stage');
return wrapper.vm.$nextTick().then(() =>
setEventDropdowns({
startEventDropdownIndex,
stopEventDropdownIndex,
}),
);
await setFields();
});
afterEach(() => {
wrapper.destroy();
it('is enabled', () => {
expect(findSubmitButton().props('disabled')).toBe(false);
});
it(`emits a ${STAGE_ACTIONS.CREATE} event when clicked`, () => {
let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined();
it('does not emit an event until the button is clicked', () => {
expect(findEvent(STAGE_ACTIONS.CREATE)).toBeUndefined();
});
wrapper.find(sel.submit).trigger('click');
it(`emits a ${STAGE_ACTIONS.CREATE} event when clicked`, async () => {
findSubmitButton().vm.$emit('click');
await wrapper.vm.$nextTick();
return wrapper.vm.$nextTick().then(() => {
event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeTruthy();
expect(event).toHaveLength(1);
});
expect(findEvent(STAGE_ACTIONS.CREATE)).toHaveLength(1);
});
it(`${STAGE_ACTIONS.CREATE} event receives the latest data`, () => {
const startEv = startEvents[startEventArrayIndex];
const selectedStopEvent = getDropdownOption(wrapper, sel.endEvent, stopEventDropdownIndex);
let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined();
const res = [
{
id: null,
name: 'Cool stage',
start_event_identifier: startEv.identifier,
start_event_label_id: null,
end_event_identifier: selectedStopEvent.attributes('value'),
end_event_label_id: null,
},
];
it(`${STAGE_ACTIONS.CREATE} event receives the latest data`, async () => {
const newData = {
name: 'Cool stage',
start_event_identifier: ISSUE_CREATED,
end_event_identifier: ISSUE_CLOSED,
};
setFields(newData);
wrapper.find(sel.submit).trigger('click');
return wrapper.vm.$nextTick().then(() => {
event = findEvent(STAGE_ACTIONS.CREATE);
expect(event[0]).toEqual(res);
});
findSubmitButton().vm.$emit('click');
await wrapper.vm.$nextTick();
expect(findEvent(STAGE_ACTIONS.CREATE)[0][0]).toMatchObject(newData);
});
});
});
......@@ -563,283 +234,168 @@ describe('CustomStageForm', () => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
it('is disabled by default', async () => {
expect(findCancelButton().props('disabled')).toBe(true);
});
it('is enabled when the form is dirty', () => {
const btn = wrapper.find(sel.cancel);
it('is enabled when the form is dirty', async () => {
await setNameField('Cool stage');
expect(findCancelButton().props('disabled')).toBe(false);
});
expect(btn.attributes('disabled')).toEqual('disabled');
wrapper.find(sel.name).setValue('Cool stage');
it('will reset the fields when clicked', async () => {
await setFields();
return wrapper.vm.$nextTick().then(() => {
expect(btn.attributes('disabled')).toBeUndefined();
});
});
findCancelButton().vm.$emit('click');
await wrapper.vm.$nextTick();
it('will reset the fields when clicked', () => {
wrapper.setData({
fields: {
name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier,
endEventIdentifier: labelStopEvent.identifier,
},
expect(wrapper.vm.fields).toMatchObject({
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
});
});
return wrapper.vm
.$nextTick()
.then(() => {
wrapper.find(sel.cancel).trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
expect(wrapper.vm.fields).toEqual({
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
});
});
it('does not emit an event until the button is clicked', () => {
expect(findEvent('cancel')).toBeUndefined();
});
it('will emit the `cancel` event when clicked', () => {
let ev = findEvent('cancel');
expect(ev).toBeUndefined();
it('will emit the `cancel` event when clicked', async () => {
await setFields();
wrapper.setData({
fields: {
name: 'Cool stage pre',
},
});
findCancelButton().vm.$emit('click');
await wrapper.vm.$nextTick();
return wrapper.vm
.$nextTick()
.then(() => {
wrapper.find(sel.cancel).trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
ev = findEvent('cancel');
expect(ev).toBeTruthy();
expect(ev).toHaveLength(1);
});
expect(findEvent('cancel')).toHaveLength(1);
});
});
describe('isSavingCustomStage=true', () => {
beforeEach(() => {
beforeEach(async () => {
wrapper = createComponent({
initialState: {
isSavingCustomStage: true,
},
});
return wrapper.vm.$nextTick();
await wrapper.vm.$nextTick();
});
it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
expect(findSubmitButton().html()).toMatchSnapshot();
});
});
describe('Editing a custom stage', () => {
beforeEach(() => {
beforeEach(async () => {
wrapper = createComponent({
initialState: {
isEditingCustomStage: true,
formInitialData,
},
});
return wrapper.vm.$nextTick();
});
afterEach(() => {
wrapper.destroy();
});
it('Cancel button will reset the fields to initial state when clicked', async () => {
await setFields(minimumFields);
describe('Cancel button', () => {
it('will reset the fields to initial state when clicked', () => {
wrapper.setData({
fields: {
name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier,
endEventIdentifier: labelStopEvent.identifier,
},
});
findCancelButton().vm.$emit('click');
await wrapper.vm.$nextTick();
return wrapper.vm
.$nextTick()
.then(() => {
wrapper.find(sel.cancel).trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
expect(wrapper.vm.fields).toEqual({
...formInitialData,
});
});
});
expect(wrapper.vm.fields).toEqual({ ...formInitialData });
});
describe('Update stage button', () => {
it('has text `Update stage`', () => {
expect(wrapper.find(sel.submit).text('value')).toEqual('Update stage');
expect(findSubmitButton().text('value')).toEqual('Update stage');
});
it('is disabled by default', () => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
expect(findSubmitButton().props('disabled')).toBe(true);
});
it('is enabled when a field is changed and fields are valid', () => {
wrapper.setData({
fields: {
name: 'Cool updated form',
},
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toBeUndefined();
});
it('is enabled when a field is changed and fields are valid', async () => {
await setFields(minimumFields);
expect(findSubmitButton().props('disabled')).toBe(false);
});
it('is disabled when a field is changed but fields are incomplete', () => {
wrapper.setData({
fields: {
name: '',
},
});
it('is disabled when a field is changed but fields are incomplete', async () => {
await setFields({ name: '' });
expect(findSubmitButton().props('disabled')).toBe(true);
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
});
it('does not emit an event until the button is clicked', () => {
expect(findEvent(STAGE_ACTIONS.UPDATE)).toBeUndefined();
});
it(`emits a ${STAGE_ACTIONS.UPDATE} event when clicked`, () => {
let ev = findEvent(STAGE_ACTIONS.UPDATE);
expect(ev).toBeUndefined();
it(`emits a ${STAGE_ACTIONS.UPDATE} event when clicked`, async () => {
await setFields({ name: 'Cool updated form' });
wrapper.setData({
fields: {
name: 'Cool updated form',
},
});
findSubmitButton().vm.$emit('click');
await wrapper.vm.$nextTick();
return wrapper.vm
.$nextTick()
.then(() => {
wrapper.find(sel.submit).trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
ev = findEvent(STAGE_ACTIONS.UPDATE);
expect(ev).toBeTruthy();
expect(ev).toHaveLength(1);
});
expect(findEvent(STAGE_ACTIONS.UPDATE)).toHaveLength(1);
});
it('`submit` event receives the latest data', () => {
wrapper.setData({
fields: {
name: 'Cool updated form',
},
});
it('`submit` event receives the latest data', async () => {
await setFields({ name: 'Cool updated form' });
return wrapper.vm
.$nextTick()
.then(() => {
wrapper.find(sel.submit).trigger('click');
return wrapper.vm.$nextTick();
})
.then(() => {
const submitted = findEvent(STAGE_ACTIONS.UPDATE)[0];
expect(submitted).not.toEqual([formInitialData]);
expect(submitted).toEqual([
{
id: formInitialData.id,
start_event_identifier: labelStartEvent.identifier,
start_event_label_id: groupLabels[0].id,
end_event_identifier: labelStopEvent.identifier,
end_event_label_id: groupLabels[1].id,
name: 'Cool updated form',
},
]);
});
findSubmitButton().vm.$emit('click');
await wrapper.vm.$nextTick();
const submitted = findEvent(STAGE_ACTIONS.UPDATE)[0];
expect(submitted).not.toEqual([formInitialData]);
expect(submitted).toEqual([
convertObjectPropsToSnakeCase({ ...formInitialData, name: 'Cool updated form' }),
]);
});
});
describe('isSavingCustomStage=true', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: {
isEditingCustomStage: true,
isSavingCustomStage: true,
},
initialState: { isEditingCustomStage: true, isSavingCustomStage: true },
});
});
it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
expect(findSubmitButton().html()).toMatchSnapshot();
});
});
});
describe('With errors', () => {
describe('With initial errors', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: {
formErrors: customStageFormErrors,
},
});
return wrapper.vm.$nextTick();
});
afterEach(() => {
wrapper.destroy();
});
it('renders the errors for the relevant fields', () => {
expect(findNameField(wrapper).html()).toContain('is reserved');
expect(findNameField(wrapper).html()).toContain('cant be blank');
expect(findStartEventField(wrapper).html()).toContain('cant be blank');
expect(findFieldErrors('name')).toEqual(['is reserved', 'cant be blank']);
expect(findFieldErrors('startEventIdentifier')).toEqual(['cant be blank']);
});
});
describe('recover stage dropdown', () => {
const formFieldStubs = {
'gl-form-group': true,
'gl-form-select': true,
'labels-selector': true,
};
beforeEach(() => {
wrapper = createComponent({
stubs: formFieldStubs,
});
});
describe('without hidden stages', () => {
it('has the recover stage dropdown', () => {
expect(wrapper.find(sel.recoverStageDropdown).exists()).toBe(true);
expect(findRecoverStageDropdown().exists()).toBe(true);
});
it('has no stages available to recover', () => {
wrapper.find(sel.recoverStageDropdownTrigger).trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(sel.recoverStageDropdown).text()).toContain(
'All default stages are currently visible',
);
});
it('has no stages available to recover', async () => {
expect(findRecoverStageDropdown().text()).toContain(
'All default stages are currently visible',
);
});
});
describe('with hidden stages', () => {
beforeEach(() => {
wrapper = createComponent({
stubs: formFieldStubs,
initialRootGetters: {
hiddenStages: () => [
{
......@@ -852,168 +408,20 @@ describe('CustomStageForm', () => {
});
});
it('has stages available to recover', () => {
wrapper.find(sel.recoverStageDropdownTrigger).trigger('click');
return wrapper.vm.$nextTick().then(() => {
const txt = wrapper.find(sel.recoverStageDropdown).text();
expect(txt).not.toContain('All default stages are currently visible');
expect(txt).toContain('My default stage');
});
it('has stages available to recover', async () => {
const txt = findRecoverStageDropdown().text();
expect(txt).not.toContain('All default stages are currently visible');
expect(txt).toContain('My default stage');
});
it(`emits the ${STAGE_ACTIONS.UPDATE} action when clicking on a stage to recover`, () => {
wrapper.find(sel.recoverStageDropdownTrigger).trigger('click');
return wrapper.vm.$nextTick().then(() => {
wrapper
.findAll(sel.hiddenStageDropdownOption)
.at(0)
.trigger('click');
expect(wrapper.emitted()).toEqual({
[STAGE_ACTIONS.UPDATE]: [[{ hidden: false, id: 'my-stage' }]],
});
});
});
});
});
it(`emits the ${STAGE_ACTIONS.UPDATE} action when clicking on a stage to recover`, async () => {
findRecoverStageDropdown()
.find(GlDropdownItem)
.vm.$emit('click');
await wrapper.vm.$nextTick();
describe('initializeFormData', () => {
const emptyFieldState = {
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
};
const emptyErrorsState = {
id: [],
name: [],
startEventIdentifier: [],
startEventLabelId: [],
endEventIdentifier: [],
endEventLabelId: [],
};
describe('without a startEventIdentifier', () => {
it('with no errors', () => {
const res = initializeFormData({
emptyFieldState,
fields: {},
});
expect(res.fields).toEqual(emptyFieldState);
expect(res.errors).toMatchObject({
endEventIdentifier: ['Please select a start event first'],
});
});
it('with field errors', () => {
const res = initializeFormData({
emptyFieldState,
fields: {},
errors: {
name: ['is reserved'],
},
});
expect(res.fields).toEqual(emptyFieldState);
expect(res.errors).toMatchObject({
endEventIdentifier: ['Please select a start event first'],
name: ['is reserved'],
});
});
});
describe('with a startEventIdentifier', () => {
it('with no errors', () => {
const res = initializeFormData({
emptyFieldState,
fields: {
startEventIdentifier: 'start-event',
},
errors: {},
});
expect(res.fields).toEqual({
...emptyFieldState,
startEventIdentifier: 'start-event',
});
expect(res.errors).toMatchObject({
endEventIdentifier: [],
});
});
it('with field errors', () => {
const res = initializeFormData({
emptyFieldState,
fields: {
startEventIdentifier: 'start-event',
},
errors: {
name: ['is reserved'],
},
});
expect(res.fields).toEqual({
...emptyFieldState,
startEventIdentifier: 'start-event',
});
expect(res.errors).toMatchObject({
endEventIdentifier: [],
name: ['is reserved'],
});
});
});
describe('with all fields set', () => {
it('with no errors', () => {
const res = initializeFormData({
emptyFieldState,
fields: {
id: 1,
name: 'cool-stage',
startEventIdentifier: 'start-event',
endEventIdentifier: 'end-event',
startEventLabelId: 10,
endEventLabelId: 20,
},
errors: {},
});
expect(res.fields).toEqual({
id: 1,
name: 'cool-stage',
startEventIdentifier: 'start-event',
endEventIdentifier: 'end-event',
startEventLabelId: 10,
endEventLabelId: 20,
});
expect(res.errors).toEqual(emptyErrorsState);
});
it('with field errors', () => {
const res = initializeFormData({
emptyFieldState,
fields: {
id: 1,
name: 'cool-stage',
startEventIdentifier: 'start-event',
endEventIdentifier: 'end-event',
startEventLabelId: 10,
endEventLabelId: 20,
},
errors: {
name: ['is reserved'],
},
});
expect(res.fields).toEqual({
id: 1,
name: 'cool-stage',
startEventIdentifier: 'start-event',
endEventIdentifier: 'end-event',
startEventLabelId: 10,
endEventLabelId: 20,
});
expect(res.errors).toMatchObject({
name: ['is reserved'],
expect(wrapper.emitted()).toEqual({
[STAGE_ACTIONS.UPDATE]: [[{ hidden: false, id: 'my-stage' }]],
});
});
});
......
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