Commit f9584a7e authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Replace callback tests with promises

Replaces the done() in the custom_stage_form_spec
with returned promises
parent 7256e544
......@@ -136,6 +136,7 @@ export default {
'setSelectedStage',
'hideCustomStageForm',
'showCustomStageForm',
'showEditCustomStageForm',
'setDateRange',
'fetchTasksByTypeData',
'updateSelectedDurationChartStages',
......@@ -143,7 +144,6 @@ export default {
'updateStage',
'removeStage',
'setFeatureFlags',
'editCustomStage',
'clearCustomStageFormErrors',
'updateStage',
'setTasksByTypeFilters',
......@@ -166,7 +166,7 @@ export default {
this.showCustomStageForm();
},
onShowEditStageForm(initData = {}) {
this.editCustomStage(initData);
this.showEditCustomStageForm(initData);
},
initDateRange() {
const endDate = new Date(Date.now());
......@@ -281,11 +281,11 @@ export default {
:current-stage-events="currentStageEvents"
:custom-stage-form-events="customStageFormEvents"
:custom-stage-form-errors="customStageFormErrors"
@clearCustomStageFormErrors="clearCustomStageFormErrors"
:labels="labels"
:no-data-svg-path="noDataSvgPath"
:no-access-svg-path="noAccessSvgPath"
:can-edit-stages="hasCustomizableCycleAnalytics"
@clearCustomStageFormErrors="clearCustomStageFormErrors"
@selectStage="onStageSelect"
@editStage="onShowEditStageForm"
@showAddStageForm="onShowAddStageForm"
......
......@@ -58,22 +58,19 @@ export default {
errors: {
type: Object,
required: false,
default: () => {},
default: null,
},
},
data() {
return {
labelEvents: getLabelEventsIdentifiers(this.events),
fields: {},
fields: {
...defaultFields,
...this.initialFields,
},
};
},
computed: {
defaultFieldData() {
return {
...defaultFields,
...this.initialFields,
};
},
startEventOptions() {
return [
{ value: null, text: s__('CustomCycleAnalytics|Select start event') },
......@@ -150,27 +147,20 @@ export default {
: s__('CustomCycleAnalytics|New stage');
},
},
mounted() {
this.resetFormFields();
watch: {
initialFields(newFields) {
this.fields = {
...defaultFields,
...newFields,
};
},
},
// updated() {
// this.resetFormFields();
// },
methods: {
resetFormFields() {
this.fields = this.defaultFieldData;
// console.log('this.fields', this.fields);
// console.log('this.initialFields', this.initialFields);
// console.log('defaultFields', defaultFields);
// Object.entries(this.defaultFieldData).
// for (let [key, value] of Object.entries(this.defaultFieldData)) {
// // console.log('setting', key, value);
// this.$set(this.fields, key, value);
// }
// console.log('this.fields', this.fields);
},
handleCancel() {
this.resetFormFields();
this.fields = {
...defaultFields,
...this.initialFields,
};
this.$emit('cancel');
},
handleSave() {
......@@ -194,9 +184,6 @@ export default {
fieldErrors(key) {
return !this.isValid(key) ? this.errors[key].join('\n') : null;
},
onUpdateFormField() {
if (this.errors) this.$emit('clearErrors');
},
},
};
</script>
......@@ -206,6 +193,7 @@ export default {
<h4>{{ formTitle }}</h4>
</div>
<gl-form-group
ref="name"
:label="s__('CustomCycleAnalytics|Name')"
:state="isValid('name')"
:invalid-feedback="fieldErrors('name')"
......@@ -217,12 +205,13 @@ export default {
name="custom-stage-name"
:placeholder="s__('CustomCycleAnalytics|Enter a name for the stage')"
required
@change="onUpdateFormField"
/>
<!-- @change="onUpdateFormField" -->
</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')"
:state="isValid('startEventIdentifier')"
:invalid-feedback="fieldErrors('startEventIdentifier')"
......@@ -232,12 +221,13 @@ export default {
name="custom-stage-start-event"
:required="true"
:options="startEventOptions"
@change="onUpdateFormField"
/>
<!-- @change="onUpdateFormField" -->
</gl-form-group>
</div>
<div v-if="startEventRequiresLabel" class="w-50 ml-1">
<gl-form-group
ref="startEventLabelId"
:label="s__('CustomCycleAnalytics|Start event label')"
:state="isValid('startEventLabelId')"
:invalid-feedback="fieldErrors('startEventLabelId')"
......@@ -248,14 +238,15 @@ export default {
name="custom-stage-start-event-label"
@selectLabel="handleSelectLabel('startEventLabelId', $event)"
@clearLabel="handleClearLabel('startEventLabelId')"
@change="onUpdateFormField"
/>
<!-- @change="onUpdateFormField" -->
</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')"
:description="
!hasStartEvent ? s__('CustomCycleAnalytics|Please select a start event first') : ''
......@@ -263,20 +254,19 @@ export default {
:state="isValid('endEventIdentifier')"
:invalid-feedback="fieldErrors('endEventIdentifier') || endEventError"
>
<!-- :state="hasValidStartAndEndEventPair"
:invalid-feedback="endEventError" -->
<gl-form-select
v-model="fields.endEventIdentifier"
name="custom-stage-stop-event"
:options="endEventOptions"
:required="true"
:disabled="!hasStartEvent"
@change="onUpdateFormField"
/>
<!-- @change="onUpdateFormField" -->
</gl-form-group>
</div>
<div v-if="endEventRequiresLabel" class="w-50 ml-1">
<gl-form-group
ref="endEventLabelId"
:label="s__('CustomCycleAnalytics|Stop event label')"
:state="isValid('endEventLabelId')"
:invalid-feedback="fieldErrors('endEventLabelId')"
......@@ -287,8 +277,8 @@ export default {
name="custom-stage-stop-event-label"
@selectLabel="handleSelectLabel('endEventLabelId', $event)"
@clearLabel="handleClearLabel('endEventLabelId')"
@change="onUpdateFormField"
/>
<!-- @change="onUpdateFormField" -->
</gl-form-group>
</div>
</div>
......
......@@ -138,8 +138,8 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => {
export const hideCustomStageForm = ({ commit }) => commit(types.HIDE_CUSTOM_STAGE_FORM);
export const showCustomStageForm = ({ commit }) => commit(types.SHOW_CUSTOM_STAGE_FORM);
export const editCustomStage = ({ commit, dispatch }, selectedStage = {}) => {
commit(types.EDIT_CUSTOM_STAGE);
export const showEditCustomStageForm = ({ commit, dispatch }, selectedStage = {}) => {
commit(types.SHOW_EDIT_CUSTOM_STAGE_FORM);
dispatch('setSelectedStage', selectedStage);
};
......
......@@ -21,7 +21,7 @@ export const RECEIVE_STAGE_MEDIANS_ERROR = 'RECEIVE_STAGE_MEDIANS_ERROR';
export const HIDE_CUSTOM_STAGE_FORM = 'HIDE_CUSTOM_STAGE_FORM';
export const SHOW_CUSTOM_STAGE_FORM = 'SHOW_CUSTOM_STAGE_FORM';
export const EDIT_CUSTOM_STAGE = 'EDIT_CUSTOM_STAGE';
export const SHOW_EDIT_CUSTOM_STAGE_FORM = 'SHOW_EDIT_CUSTOM_STAGE_FORM';
export const CLEAR_CUSTOM_STAGE_FORM_ERRORS = 'CLEAR_CUSTOM_STAGE_FORM_ERRORS';
export const REQUEST_GROUP_LABELS = 'REQUEST_GROUP_LABELS';
......
......@@ -98,7 +98,7 @@ export default {
state.isCreatingCustomStage = true;
state.isEditingCustomStage = false;
},
[types.EDIT_CUSTOM_STAGE](state) {
[types.SHOW_EDIT_CUSTOM_STAGE_FORM](state) {
state.isEditingCustomStage = true;
state.isCreatingCustomStage = false;
},
......
......@@ -6,7 +6,7 @@ exports[`CustomStageForm Editing a custom stage isSavingCustomStage=true display
</button>"
`;
exports[`CustomStageForm Empty form Start event with events does not select events with canBeStartEvent=false for the start events dropdown 1`] = `
exports[`CustomStageForm Start event with events does not select events with canBeStartEvent=false for the start events dropdown 1`] = `
"<select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__123\\">
<option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option>
......@@ -29,7 +29,7 @@ exports[`CustomStageForm Empty form Start event with events does not select even
</select>"
`;
exports[`CustomStageForm Empty form Start event with events selects events with canBeStartEvent=true for the start events dropdown 1`] = `
exports[`CustomStageForm Start event with events selects events with canBeStartEvent=true for the start events dropdown 1`] = `
"<select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__95\\">
<option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option>
......@@ -52,7 +52,49 @@ exports[`CustomStageForm Empty form Start event with events selects events with
</select>"
`;
exports[`CustomStageForm Empty form isSavingCustomStage=true displays a loading icon 1`] = `
exports[`CustomStageForm With errors renders the errors for the relevant fields 1`] = `
"<fieldset aria-invalid=\\"true\\" class=\\"form-group gl-form-group is-invalid\\" id=\\"__BVID__1372\\" aria-describedby=\\"__BVID__1372__BV_feedback_invalid_\\">
<legend tabindex=\\"-1\\" class=\\"col-form-label pt-0 col-form-label\\" id=\\"__BVID__1372__BV_label_\\">Name</legend>
<div tabindex=\\"-1\\" role=\\"group\\"><input name=\\"custom-stage-name\\" type=\\"text\\" placeholder=\\"Enter a name for the stage\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-input form-control form-control\\" id=\\"__BVID__1374\\">
<div tabindex=\\"-1\\" role=\\"alert\\" aria-live=\\"assertive\\" aria-atomic=\\"true\\" class=\\"invalid-feedback d-block\\" id=\\"__BVID__1372__BV_feedback_invalid_\\">is reserved
cant be blank</div>
<!---->
<!---->
</div>
</fieldset>"
`;
exports[`CustomStageForm With errors renders the errors for the relevant fields 2`] = `
"<fieldset aria-invalid=\\"true\\" class=\\"form-group gl-form-group is-invalid\\" id=\\"__BVID__1376\\" aria-describedby=\\"__BVID__1376__BV_feedback_invalid_\\">
<legend tabindex=\\"-1\\" class=\\"col-form-label pt-0 col-form-label\\" id=\\"__BVID__1376__BV_label_\\">Start event</legend>
<div tabindex=\\"-1\\" role=\\"group\\"><select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__1378\\">
<option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"merge_request_created\\">Merge request created</option>
<option value=\\"merge_request_first_deployed_to_production\\">Merge request first deployed to production</option>
<option value=\\"merge_request_last_build_finished\\">Merge request last build finish time</option>
<option value=\\"merge_request_last_build_started\\">Merge request last build start time</option>
<option value=\\"merge_request_merged\\">Merge request merged</option>
<option value=\\"code_stage_start\\">Issue first mentioned in a commit</option>
<option value=\\"plan_stage_start\\">Issue first associated with a milestone or issue first added to a board</option>
<option value=\\"issue_closed\\">Issue closed</option>
<option value=\\"issue_first_added_to_board\\">Issue first added to a board</option>
<option value=\\"issue_first_associated_with_milestone\\">Issue first associated with a milestone</option>
<option value=\\"issue_label_added\\">Issue label was added</option>
<option value=\\"issue_label_removed\\">Issue label was removed</option>
<option value=\\"merge_request_closed\\">Merge request closed</option>
<option value=\\"merge_request_label_added\\">Merge Request label was added</option>
<option value=\\"merge_request_label_removed\\">Merge Request label was removed</option>
</select>
<div tabindex=\\"-1\\" role=\\"alert\\" aria-live=\\"assertive\\" aria-atomic=\\"true\\" class=\\"invalid-feedback d-block\\" id=\\"__BVID__1376__BV_feedback_invalid_\\">cant be blank</div>
<!---->
<!---->
</div>
</fieldset>"
`;
exports[`CustomStageForm isSavingCustomStage=true displays a loading icon 1`] = `
"<button disabled=\\"disabled\\" type=\\"button\\" class=\\"js-save-stage btn btn-success\\"><span class=\\"gl-spinner-container\\"><span aria-label=\\"Loading\\" aria-hidden=\\"true\\" class=\\"gl-spinner gl-spinner-orange gl-spinner-sm\\"></span></span>
Add stage
</button>"
......
import Vue from 'vue';
import { shallowMount, mount } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import CustomStageForm from 'ee/analytics/cycle_analytics/components/custom_stage_form.vue';
import { STAGE_ACTIONS } from 'ee/analytics/cycle_analytics/constants';
import {
......@@ -9,6 +9,7 @@ import {
labelStopEvent,
customStageStartEvents as startEvents,
customStageStopEvents as stopEvents,
customStageFormErrors,
} from '../mock_data';
const initData = {
......@@ -21,14 +22,11 @@ const initData = {
};
describe('CustomStageForm', () => {
function createComponent(props, shallow = false) {
const func = shallow ? shallowMount : mount;
return func(CustomStageForm, {
function createComponent(props) {
return mount(CustomStageForm, {
propsData: {
events,
labels: groupLabels,
errors: {},
initialFields: {},
...props,
},
});
......@@ -59,491 +57,464 @@ describe('CustomStageForm', () => {
getDropdownOption(_wrapper, dropdown, index).setSelected();
}
describe('Empty form', () => {
beforeEach(() => {
wrapper = createComponent({}, false);
function setEventDropdowns({ startEventDropdownIndex = 1, stopEventDropdownIndex = 1 } = {}) {
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return Vue.nextTick().then(() => {
selectDropdownOption(wrapper, sel.endEvent, stopEventDropdownIndex);
});
}
afterEach(() => {
wrapper.destroy();
beforeEach(() => {
wrapper = createComponent({});
});
afterEach(() => {
wrapper.destroy();
});
describe.each([
['Name', sel.name, true],
['Start event', sel.startEvent, true],
['Stop 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.each([
['Name', sel.name, true],
['Start event', sel.startEvent, true],
['Stop event', sel.endEvent, false],
['Submit', sel.submit, false],
['Cancel', sel.cancel, false],
])('by default', (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('Start event', () => {
describe('with events', () => {
beforeEach(() => {
wrapper = createComponent({}, false);
});
describe('Start event', () => {
describe('with events', () => {
beforeEach(() => {
wrapper = createComponent({});
});
afterEach(() => {
wrapper.destroy();
});
afterEach(() => {
wrapper.destroy();
});
it('selects events with canBeStartEvent=true for the start events dropdown', () => {
const select = wrapper.find(sel.startEvent);
expect(select.html()).toMatchSnapshot();
});
it('selects events with canBeStartEvent=true for the start events dropdown', () => {
const select = wrapper.find(sel.startEvent);
expect(select.html()).toMatchSnapshot();
});
it('does not select events with canBeStartEvent=false for the start events dropdown', () => {
const select = wrapper.find(sel.startEvent);
expect(select.html()).toMatchSnapshot();
stopEvents
.filter(ev => !ev.canBeStartEvent)
.forEach(ev => {
expect(select.html()).not.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);
expect(select.html()).toMatchSnapshot();
stopEvents
.filter(ev => !ev.canBeStartEvent)
.forEach(ev => {
expect(select.html()).not.toHaveHtml(
`<option value="${ev.identifier}">${ev.name}</option>`,
);
});
});
});
describe('start event label', () => {
beforeEach(() => {
wrapper = createComponent({}, false);
});
describe('start event label', () => {
beforeEach(() => {
wrapper = createComponent({}, false);
});
afterEach(() => {
wrapper.destroy();
});
afterEach(() => {
wrapper.destroy();
});
it('is hidden by default', () => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(false);
});
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', done => {
wrapper.setData({
fields: {
startEventIdentifier: labelStartEvent.identifier,
},
});
it('will display the start event label field if a label event is selected', () => {
wrapper.setData({
fields: {
startEventIdentifier: labelStartEvent.identifier,
},
});
Vue.nextTick(() => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(true);
done();
});
return Vue.nextTick().then(() => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(true);
});
});
it('will set the "startEventLabelId" field when selected', done => {
const selectedLabelId = groupLabels[0].id;
expect(wrapper.vm.fields.startEventLabelId).toEqual(null);
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);
Vue.nextTick(() => {
wrapper.find(sel.startEvent).setValue(labelStartEvent.identifier);
// TODO: make func for setting single field
return Vue.nextTick()
.then(() => {
wrapper
.find(sel.startEventLabel)
.findAll('.dropdown-item')
.at(1) // item at index 0 is 'select a label'
.trigger('click');
Vue.nextTick(() => {
expect(wrapper.vm.fields.startEventLabelId).toEqual(selectedLabelId);
done();
});
return Vue.nextTick();
})
.then(() => {
expect(wrapper.vm.fields.startEventLabelId).toEqual(selectedLabelId);
});
});
});
});
});
describe('Stop event', () => {
const startEventArrayIndex = 2;
const startEventDropdownIndex = 1;
const currAllowed = startEvents[startEventArrayIndex].allowedEndEvents;
describe('Stop event', () => {
const startEventArrayIndex = 2;
const startEventDropdownIndex = 1;
const currAllowed = startEvents[startEventArrayIndex].allowedEndEvents;
beforeEach(() => {
wrapper = createComponent({}, false);
});
beforeEach(() => {
wrapper = createComponent({}, false);
});
it('notifies that a start event needs to be selected first', () => {
expect(wrapper.text()).toContain('Please select a start event first');
});
it('notifies that a start event needs to be selected first', () => {
expect(wrapper.text()).toContain('Please select a start event first');
});
it('clears notification when a start event is selected', done => {
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
Vue.nextTick(() => {
expect(wrapper.text()).not.toContain('Please select a start event first');
done();
});
it('clears notification when a start event is selected', () => {
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return Vue.nextTick().then(() => {
expect(wrapper.text()).not.toContain('Please select a start event first');
});
});
it('is enabled when a start event is selected', done => {
const el = wrapper.find(sel.endEvent);
expect(el.attributes('disabled')).toEqual('disabled');
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);
Vue.nextTick(() => {
expect(el.attributes('disabled')).toBeUndefined();
done();
});
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return Vue.nextTick().then(() => {
expect(el.attributes('disabled')).toBeUndefined();
});
});
it('will update the list of stop events when a start event is changed', done => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const selectedStartEvent = startEvents[startEventDropdownIndex];
expect(stopOptions.length).toEqual(1);
it('will update the list of stop events when a start event is changed', () => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const selectedStartEvent = startEvents[startEventDropdownIndex];
expect(stopOptions.length).toEqual(1);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.endEvent);
selectedStartEvent.allowedEndEvents.forEach(identifier => {
expect(stopOptions.html()).toContain(identifier);
});
done();
return Vue.nextTick().then(() => {
stopOptions = wrapper.find(sel.endEvent);
selectedStartEvent.allowedEndEvents.forEach(identifier => {
expect(stopOptions.html()).toContain(identifier);
});
});
});
it('will display all the valid stop events', done => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const possibleEndEvents = stopEvents.filter(ev => currAllowed.includes(ev.identifier));
it('will display all the valid stop 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 stop event</option>');
expect(stopOptions.at(0).html()).toEqual('<option value="">Select stop event</option>');
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.endEvent);
return Vue.nextTick().then(() => {
stopOptions = wrapper.find(sel.endEvent);
possibleEndEvents.forEach(({ name, identifier }) => {
expect(stopOptions.html()).toContain(`<option value="${identifier}">${name}</option>`);
});
done();
possibleEndEvents.forEach(({ name, identifier }) => {
expect(stopOptions.html()).toContain(`<option value="${identifier}">${name}</option>`);
});
});
});
it('will not display stop events that are not in the list of allowed stop events', done => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const excludedEndEvents = stopEvents.filter(ev => !currAllowed.includes(ev.identifier));
it('will not display stop events that are not in the list of allowed stop 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 stop event</option>');
expect(stopOptions.at(0).html()).toEqual('<option value="">Select stop event</option>');
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.endEvent);
return Vue.nextTick().then(() => {
stopOptions = wrapper.find(sel.endEvent);
excludedEndEvents.forEach(({ name, identifier }) => {
expect(wrapper.find(sel.endEvent).html()).not.toHaveHtml(
`<option value="${identifier}">${name}</option>`,
);
});
done();
excludedEndEvents.forEach(({ name, identifier }) => {
expect(wrapper.find(sel.endEvent).html()).not.toHaveHtml(
`<option value="${identifier}">${name}</option>`,
);
});
});
});
describe('with a stop event selected and a change to the start event', () => {
beforeEach(() => {
wrapper = createComponent({});
describe('with a stop event selected and a change to the start event', () => {
beforeEach(() => {
wrapper = createComponent({});
wrapper.setData({
fields: {
name: 'Cool stage',
startEventIdentifier: 'issue_created',
startEventLabelId: null,
endEventIdentifier: 'issue_stage_end',
endEventLabelId: null,
},
});
wrapper.setData({
fields: {
name: 'Cool stage',
startEventIdentifier: 'issue_created',
startEventLabelId: null,
endEventIdentifier: 'issue_stage_end',
endEventLabelId: null,
},
});
});
afterEach(() => {
wrapper.destroy();
});
afterEach(() => {
wrapper.destroy();
});
it('will notify if the current start and stop event pair is not valid', done => {
expect(wrapper.find(sel.invalidFeedback).exists()).toEqual(false);
it('will notify if the current start and stop event pair is not valid', () => {
expect(wrapper.find(sel.invalidFeedback).exists()).toEqual(false);
selectDropdownOption(wrapper, sel.startEvent, 2);
selectDropdownOption(wrapper, sel.startEvent, 2);
Vue.nextTick(() => {
expect(wrapper.find(sel.invalidFeedback).exists()).toEqual(true);
expect(wrapper.find(sel.invalidFeedback).text()).toContain(
'Start event changed, please select a valid stop event',
);
done();
});
return Vue.nextTick().then(() => {
expect(wrapper.find(sel.invalidFeedback).exists()).toEqual(true);
expect(wrapper.find(sel.invalidFeedback).text()).toContain(
'Start event changed, please select a valid stop event',
);
});
});
it('will update the list of stop events', done => {
const se = wrapper.vm.endEventOptions;
selectDropdownOption(wrapper, sel.startEvent, 2);
Vue.nextTick(() => {
expect(se[1].value).not.toEqual(wrapper.vm.endEventOptions[1].value);
done();
});
it('will update the list of stop events', () => {
const se = wrapper.vm.endEventOptions;
selectDropdownOption(wrapper, sel.startEvent, 2);
return Vue.nextTick().then(() => {
expect(se[1].value).not.toEqual(wrapper.vm.endEventOptions[1].value);
});
});
it('will disable the submit button until a valid endEvent is selected', done => {
selectDropdownOption(wrapper, sel.startEvent, 2);
Vue.nextTick(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
done();
});
it('will disable the submit button until a valid endEvent is selected', () => {
selectDropdownOption(wrapper, sel.startEvent, 2);
return Vue.nextTick().then(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
});
});
});
describe('Stop event label', () => {
beforeEach(() => {
wrapper = createComponent({});
});
describe('Stop event label', () => {
beforeEach(() => {
wrapper = createComponent({});
});
afterEach(() => {
wrapper.destroy();
});
afterEach(() => {
wrapper.destroy();
});
it('is hidden by default', () => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(false);
});
it('is hidden by default', () => {
expect(wrapper.find(sel.startEventLabel).exists()).toEqual(false);
});
it('will display the stop event label field if a label event is selected', done => {
expect(wrapper.find(sel.endEventLabel).exists()).toEqual(false);
it('will display the stop event label field if a label event is selected', () => {
expect(wrapper.find(sel.endEventLabel).exists()).toEqual(false);
wrapper.setData({
fields: {
endEventIdentifier: labelStopEvent.identifier,
startEventIdentifier: labelStartEvent.identifier,
},
});
wrapper.setData({
fields: {
endEventIdentifier: labelStopEvent.identifier,
startEventIdentifier: labelStartEvent.identifier,
},
});
Vue.nextTick(() => {
expect(wrapper.find(sel.endEventLabel).exists()).toEqual(true);
done();
});
return Vue.nextTick().then(() => {
expect(wrapper.find(sel.endEventLabel).exists()).toEqual(true);
});
});
it('will set the "endEventLabelId" field when selected', done => {
const selectedLabelId = groupLabels[1].id;
expect(wrapper.vm.fields.endEventLabelId).toEqual(null);
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,
},
});
wrapper.setData({
fields: {
startEventIdentifier: labelStartEvent.identifier,
endEventIdentifier: labelStopEvent.identifier,
},
});
Vue.nextTick(() => {
return Vue.nextTick()
.then(() => {
wrapper
.find(sel.endEventLabel)
.findAll('.dropdown-item')
.at(2) // item at index 0 is 'select a label'
.trigger('click');
Vue.nextTick(() => {
expect(wrapper.vm.fields.endEventLabelId).toEqual(selectedLabelId);
done();
});
return Vue.nextTick();
})
.then(() => {
expect(wrapper.vm.fields.endEventLabelId).toEqual(selectedLabelId);
});
});
});
});
});
describe.only('Add stage button', () => {
beforeEach(done => {
wrapper = createComponent({}, true);
describe('Add stage button', () => {
beforeEach(() => {
wrapper = createComponent({});
});
selectDropdownOption(wrapper, sel.startEvent, 1);
afterEach(() => {
wrapper.destroy();
});
wrapper.vm
.$nextTick()
.then(() => {
selectDropdownOption(wrapper, sel.endEvent, 1);
})
.then(wrapper.vm.$nextTick)
.then(done)
.catch(err => {
console.log('ERRRRRRR', err);
done.fail();
});
});
it('has text `Add stage`', () => {
expect(wrapper.find(sel.submit).text()).toEqual('Add stage');
});
afterEach(() => {
wrapper.destroy();
});
it('is enabled when all required fields are filled', () => {
const btn = wrapper.find(sel.submit);
it.only('has text `Add stage`', () => {
const txt = wrapper.find(sel.submit).text();
expect(txt).toEqual('Add stage');
expect(btn.attributes('disabled')).toEqual('disabled');
wrapper.find(sel.name).setValue('Cool stage');
return setEventDropdowns().then(() => {
expect(btn.attributes('disabled')).toBeUndefined();
});
});
it('is enabled when all required fields are filled', done => {
const btn = wrapper.find(sel.submit);
describe('with all fields set', () => {
const startEventDropdownIndex = 2;
const startEventArrayIndex = startEventDropdownIndex - 1;
const stopEventDropdownIndex = 1;
expect(btn.attributes('disabled')).toEqual('disabled');
beforeEach(() => {
wrapper = createComponent({});
wrapper.find(sel.name).setValue('Cool stage');
Vue.nextTick(() => {
expect(btn.attributes('disabled')).toBeUndefined();
done();
});
return Vue.nextTick().then(() =>
setEventDropdowns({ startEventDropdownIndex, stopEventDropdownIndex }),
);
});
describe('with all fields set', () => {
const startEventDropdownIndex = 2;
const startEventArrayIndex = startEventDropdownIndex - 1;
const stopEventIndex = 1;
beforeEach(() => {
wrapper = createComponent({});
afterEach(() => {
wrapper.destroy();
});
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
it(`emits a ${STAGE_ACTIONS.CREATE} event when clicked`, () => {
let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined();
return Vue.nextTick()
.then(() => {
selectDropdownOption(wrapper, sel.endEvent, stopEventIndex);
return Vue.nextTick();
})
.then(() => {
wrapper.find(sel.name).setValue('Cool stage');
return Vue.nextTick();
});
});
wrapper.find(sel.submit).trigger('click');
afterEach(() => {
wrapper.destroy();
return Vue.nextTick().then(() => {
event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeTruthy();
expect(event.length).toEqual(1);
});
});
it(`emits a ${STAGE_ACTIONS.CREATE} event when clicked`, done => {
let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined();
wrapper.find(sel.submit).trigger('click');
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();
Vue.nextTick(() => {
event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeTruthy();
expect(event.length).toEqual(1);
done();
});
});
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`, () => {
const startEv = startEvents[startEventArrayIndex];
const selectedStopEvent = getDropdownOption(wrapper, sel.endEvent, stopEventIndex);
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,
},
];
wrapper.find(sel.submit).trigger('click');
return Vue.nextTick().then(() => {
event = findEvent(STAGE_ACTIONS.CREATE);
expect(event[0]).toEqual(res);
});
wrapper.find(sel.submit).trigger('click');
return Vue.nextTick().then(() => {
event = findEvent(STAGE_ACTIONS.CREATE);
expect(event[0]).toEqual(res);
});
});
});
});
describe('Cancel button', () => {
beforeEach(() => {
wrapper = createComponent({});
});
describe('Cancel button', () => {
beforeEach(() => {
wrapper = createComponent({});
});
afterEach(() => {
wrapper.destroy();
});
afterEach(() => {
wrapper.destroy();
});
it('is enabled when the form is dirty', done => {
const btn = wrapper.find(sel.cancel);
it('is enabled when the form is dirty', () => {
const btn = wrapper.find(sel.cancel);
expect(btn.attributes('disabled')).toEqual('disabled');
wrapper.find(sel.name).setValue('Cool stage');
expect(btn.attributes('disabled')).toEqual('disabled');
wrapper.find(sel.name).setValue('Cool stage');
Vue.nextTick(() => {
expect(btn.attributes('disabled')).toBeUndefined();
done();
});
return Vue.nextTick().then(() => {
expect(btn.attributes('disabled')).toBeUndefined();
});
});
it('will reset the fields when clicked', done => {
wrapper.setData({
fields: {
name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier,
endEventIdentifier: labelStopEvent.identifier,
},
});
it('will reset the fields when clicked', () => {
wrapper.setData({
fields: {
name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier,
endEventIdentifier: labelStopEvent.identifier,
},
});
Vue.nextTick(() => {
return Vue.nextTick()
.then(() => {
wrapper.find(sel.cancel).trigger('click');
Vue.nextTick(() => {
expect(wrapper.vm.fields).toEqual({
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
});
done();
return Vue.nextTick();
})
.then(() => {
expect(wrapper.vm.fields).toEqual({
id: null,
name: null,
startEventIdentifier: null,
startEventLabelId: null,
endEventIdentifier: null,
endEventLabelId: null,
});
});
});
});
it('will emit the `cancel` event when clicked', done => {
let ev = findEvent('cancel');
expect(ev).toBeUndefined();
it('will emit the `cancel` event when clicked', () => {
let ev = findEvent('cancel');
expect(ev).toBeUndefined();
wrapper.setData({
fields: {
name: 'Cool stage pre',
},
});
wrapper.setData({
fields: {
name: 'Cool stage pre',
},
});
Vue.nextTick(() => {
return Vue.nextTick()
.then(() => {
wrapper.find(sel.cancel).trigger('click');
Vue.nextTick(() => {
ev = findEvent('cancel');
expect(ev).toBeTruthy();
expect(ev.length).toEqual(1);
done();
});
return Vue.nextTick();
})
.then(() => {
ev = findEvent('cancel');
expect(ev).toBeTruthy();
expect(ev.length).toEqual(1);
});
});
});
});
describe('isSavingCustomStage=true', () => {
beforeEach(() => {
wrapper = createComponent(
{
isSavingCustomStage: true,
},
false,
);
});
describe('isSavingCustomStage=true', () => {
beforeEach(() => {
wrapper = createComponent(
{
isSavingCustomStage: true,
},
false,
);
});
it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
});
it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
});
});
......@@ -573,7 +544,7 @@ describe('CustomStageForm', () => {
});
describe('Cancel button', () => {
it('will reset the fields to initial state when clicked', done => {
it('will reset the fields to initial state when clicked', () => {
wrapper.setData({
fields: {
name: 'Cool stage pre',
......@@ -582,16 +553,14 @@ describe('CustomStageForm', () => {
},
});
Vue.nextTick(() => {
wrapper.find(sel.cancel).trigger('click');
Vue.nextTick(() => {
expect(wrapper.vm.fields).toEqual({
...initData,
});
done();
return Vue.nextTick()
.then(() => {
wrapper.find(sel.cancel).trigger('click');
return Vue.nextTick();
})
.then(() => {
expect(wrapper.vm.fields).toEqual({ ...initData });
});
});
});
});
......@@ -604,33 +573,31 @@ describe('CustomStageForm', () => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
});
it('is enabled when a field is changed and fields are valid', done => {
it('is enabled when a field is changed and fields are valid', () => {
wrapper.setData({
fields: {
name: 'Cool updated form',
},
});
Vue.nextTick(() => {
return Vue.nextTick().then(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toBeUndefined();
done();
});
});
it('is disabled when a field is changed but fields are incomplete', done => {
it('is disabled when a field is changed but fields are incomplete', () => {
wrapper.setData({
fields: {
name: '',
},
});
Vue.nextTick(() => {
return Vue.nextTick().then(() => {
expect(wrapper.find(sel.submit).attributes('disabled')).toEqual('disabled');
done();
});
});
it(`emits a ${STAGE_ACTIONS.UPDATE} event when clicked`, done => {
it(`emits a ${STAGE_ACTIONS.UPDATE} event when clicked`, () => {
let ev = findEvent(STAGE_ACTIONS.UPDATE);
expect(ev).toBeUndefined();
......@@ -640,29 +607,31 @@ describe('CustomStageForm', () => {
},
});
Vue.nextTick(() => {
wrapper.find(sel.submit).trigger('click');
Vue.nextTick(() => {
return Vue.nextTick()
.then(() => {
wrapper.find(sel.submit).trigger('click');
return Vue.nextTick();
})
.then(() => {
ev = findEvent(STAGE_ACTIONS.UPDATE);
expect(ev).toBeTruthy();
expect(ev.length).toEqual(1);
done();
});
});
});
it('`submit` event receives the latest data', done => {
it('`submit` event receives the latest data', () => {
wrapper.setData({
fields: {
name: 'Cool updated form',
},
});
Vue.nextTick(() => {
wrapper.find(sel.submit).trigger('click');
Vue.nextTick(() => {
return Vue.nextTick()
.then(() => {
wrapper.find(sel.submit).trigger('click');
return Vue.nextTick();
})
.then(() => {
const submitted = findEvent(STAGE_ACTIONS.UPDATE)[0];
expect(submitted).not.toEqual([initData]);
expect(submitted).toEqual([
......@@ -675,30 +644,48 @@ describe('CustomStageForm', () => {
name: 'Cool updated form',
},
]);
done();
});
});
});
});
describe('isSavingCustomStage=true', () => {
beforeEach(() => {
wrapper = createComponent(
{
isEditingCustomStage: true,
initialFields: {
...initData,
},
isSavingCustomStage: true,
wrapper = createComponent({
isEditingCustomStage: true,
initialFields: {
...initData,
},
false,
);
isSavingCustomStage: true,
});
});
it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
});
});
});
describe('With errors', () => {
beforeEach(() => {
wrapper = createComponent({
initialFields: initData,
});
return Vue.nextTick();
});
afterEach(() => {
wrapper.destroy();
});
it('renders the errors for the relevant fields', () => {
console.log('wrapper', wrapper.html());
// const errorMessages = wrapper.findAll(sel.invalidFeedback);
// expect(errorMessages.length).toEqual(2);
wrapper.setProps({ errors: customStageFormErrors });
expect(wrapper.find({ ref: 'startEventIdentifier' }).html()).toContain('CAnt be beelebel');
expect(wrapper.find({ ref: 'name' }).html()).toContain('CAnt be beelebel');
});
});
});
......@@ -118,6 +118,13 @@ export const labelStopEvent = customStageLabelEvents.find(
ev => ev.identifier === labelStartEvent.allowedEndEvents[0],
);
export const rawCustomStageFormErrors = {
name: ['is reserved', 'cant be blank'],
start_event_identifier: ['cant be blank'],
};
export const customStageFormErrors = convertObjectPropsToCamelCase(rawCustomStageFormErrors);
const dateRange = getDatesInRange(startDate, endDate, toYmd);
export const tasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_by_type.json').map(
......
......@@ -38,7 +38,9 @@ describe('Cycle analytics mutations', () => {
mutation | stateKey | value
${types.HIDE_CUSTOM_STAGE_FORM} | ${'isCreatingCustomStage'} | ${false}
${types.SHOW_CUSTOM_STAGE_FORM} | ${'isCreatingCustomStage'} | ${true}
${types.EDIT_CUSTOM_STAGE} | ${'isEditingCustomStage'} | ${true}
${types.SHOW_CUSTOM_STAGE_FORM} | ${'isEditingCustomStage'} | ${false}
${types.SHOW_EDIT_CUSTOM_STAGE_FORM} | ${'isEditingCustomStage'} | ${true}
${types.SHOW_EDIT_CUSTOM_STAGE_FORM} | ${'isCreatingCustomStage'} | ${false}
${types.REQUEST_STAGE_DATA} | ${'isLoadingStage'} | ${true}
${types.RECEIVE_STAGE_DATA_ERROR} | ${'isEmptyStage'} | ${true}
${types.RECEIVE_STAGE_DATA_ERROR} | ${'isLoadingStage'} | ${false}
......
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