Commit bc8266dc authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Ensure the stage name is not reserved

Checks if the stage name specified
is one of the default names and displays
an error
parent 5943cfe2
...@@ -13,7 +13,7 @@ import { ...@@ -13,7 +13,7 @@ import {
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToSnakeCase } from '~/lib/utils/common_utils';
import LabelsSelector from './labels_selector.vue'; import LabelsSelector from './labels_selector.vue';
import { STAGE_ACTIONS } from '../constants'; import { STAGE_ACTIONS, DEFAULT_STAGE_NAMES } from '../constants';
import { import {
isStartEvent, isStartEvent,
isLabelEvent, isLabelEvent,
...@@ -127,8 +127,14 @@ export default { ...@@ -127,8 +127,14 @@ export default {
endEventRequiresLabel() { endEventRequiresLabel() {
return isLabelEvent(this.labelEvents, this.fields.endEventIdentifier); return isLabelEvent(this.labelEvents, this.fields.endEventIdentifier);
}, },
hasErrors() {
return (
this.eventMismatchError ||
Object.values(this.fieldErrors).some(errArray => errArray && errArray.length)
);
},
isComplete() { isComplete() {
if (this.eventMismatchError) { if (this.hasErrors) {
return false; return false;
} }
const { const {
...@@ -194,10 +200,14 @@ export default { ...@@ -194,10 +200,14 @@ export default {
}, },
methods: { methods: {
handleCancel() { handleCancel() {
this.fields = { const { initialFields = {}, errors = null } = this;
...defaultFields, const formData = initializeFormData({
...this.initialFields, emptyFieldState: defaultFields,
}; initialFields,
errors,
});
this.$set(this, 'fields', formData.fields);
this.$set(this, 'fieldErrors', formData.fieldErrors);
this.$emit('cancel'); this.$emit('cancel');
}, },
handleSave() { handleSave() {
...@@ -221,6 +231,15 @@ export default { ...@@ -221,6 +231,15 @@ export default {
fieldErrorMessage(key) { fieldErrorMessage(key) {
return this.fieldErrors[key]?.join('\n'); return this.fieldErrors[key]?.join('\n');
}, },
onUpdateNameField() {
if (DEFAULT_STAGE_NAMES.includes(this.fields.name.toLowerCase())) {
this.$set(this.fieldErrors, 'name', [
s__('CustomCycleAnalytics|Stage name already exists'),
]);
} else {
this.$set(this.fieldErrors, 'name', []);
}
},
onUpdateStartEventField() { onUpdateStartEventField() {
const initVal = this.initialFields?.endEventIdentifier const initVal = this.initialFields?.endEventIdentifier
? this.initialFields.endEventIdentifier ? this.initialFields.endEventIdentifier
...@@ -262,6 +281,7 @@ export default { ...@@ -262,6 +281,7 @@ export default {
:label="s__('CustomCycleAnalytics|Name')" :label="s__('CustomCycleAnalytics|Name')"
:state="!hasFieldErrors('name')" :state="!hasFieldErrors('name')"
:invalid-feedback="fieldErrorMessage('name')" :invalid-feedback="fieldErrorMessage('name')"
@change.native="onUpdateNameField"
> >
<gl-form-input <gl-form-input
v-model="fields.name" v-model="fields.name"
......
...@@ -30,6 +30,8 @@ export const EMPTY_STAGE_TEXT = { ...@@ -30,6 +30,8 @@ export const EMPTY_STAGE_TEXT = {
), ),
}; };
export const DEFAULT_STAGE_NAMES = [...Object.keys(EMPTY_STAGE_TEXT), 'total'];
export const TASKS_BY_TYPE_SUBJECT_ISSUE = 'Issue'; export const TASKS_BY_TYPE_SUBJECT_ISSUE = 'Issue';
export const TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST = 'MergeRequest'; export const TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST = 'MergeRequest';
......
...@@ -488,11 +488,10 @@ describe 'Group Value Stream Analytics', :js do ...@@ -488,11 +488,10 @@ describe 'Group Value Stream Analytics', :js do
end end
it 'with a default name' do it 'with a default name' do
name = 'issue' fill_in 'custom-stage-name', with: 'issue'
fill_in 'custom-stage-name', with: name
click_button 'Add stage' click_button 'Add stage'
expect(page.find('.flash-alert')).to have_text("'#{name}' stage already exists") expect(page).to have_button('Add stage', disabled: true)
end end
end end
...@@ -616,11 +615,10 @@ describe 'Group Value Stream Analytics', :js do ...@@ -616,11 +615,10 @@ describe 'Group Value Stream Analytics', :js do
end end
it 'with a default name' do it 'with a default name' do
name = 'issue' fill_in name_field, with: 'issue'
fill_in name_field, with: name
page.find(stage_save_button).click page.find(stage_save_button).click
expect(page.find('.flash-alert')).to have_text("'#{name}' stage already exists") expect(page.find(stage_form_class)).to have_text("Stage name already exists")
end end
end end
end end
......
...@@ -7,7 +7,7 @@ exports[`CustomStageForm Editing a custom stage isSavingCustomStage=true display ...@@ -7,7 +7,7 @@ exports[`CustomStageForm Editing a custom stage isSavingCustomStage=true display
`; `;
exports[`CustomStageForm 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__177\\"> "<select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__257\\">
<option value=\\"\\">Select start event</option> <option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option> <option value=\\"issue_created\\">Issue created</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option> <option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
...@@ -30,7 +30,7 @@ exports[`CustomStageForm Start event with events does not select events with can ...@@ -30,7 +30,7 @@ exports[`CustomStageForm Start event with events does not select events with can
`; `;
exports[`CustomStageForm 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__137\\"> "<select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__217\\">
<option value=\\"\\">Select start event</option> <option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option> <option value=\\"issue_created\\">Issue created</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option> <option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
......
...@@ -79,6 +79,15 @@ describe('CustomStageForm', () => { ...@@ -79,6 +79,15 @@ describe('CustomStageForm', () => {
}); });
} }
const findNameField = _wrapper => _wrapper.find({ ref: 'name' });
const findNameFieldInput = _wrapper => _wrapper.find(sel.name);
function setNameField(_wrapper, value = '') {
findNameFieldInput(_wrapper).setValue(value);
findNameFieldInput(_wrapper).trigger('change');
return _wrapper.vm.$nextTick();
}
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({}); wrapper = createComponent({});
}); });
...@@ -106,6 +115,25 @@ describe('CustomStageForm', () => { ...@@ -106,6 +115,25 @@ describe('CustomStageForm', () => {
}); });
}); });
describe('Name', () => {
describe('with a reserved name', () => {
beforeEach(() => {
wrapper = createComponent({});
return setNameField(wrapper, '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('Start event', () => {
describe('with events', () => { describe('with events', () => {
beforeEach(() => { beforeEach(() => {
...@@ -165,7 +193,6 @@ describe('CustomStageForm', () => { ...@@ -165,7 +193,6 @@ describe('CustomStageForm', () => {
expect(wrapper.vm.fields.startEventLabelId).toEqual(null); expect(wrapper.vm.fields.startEventLabelId).toEqual(null);
wrapper.find(sel.startEvent).setValue(labelStartEvent.identifier); wrapper.find(sel.startEvent).setValue(labelStartEvent.identifier);
// TODO: make func for setting single field
return Vue.nextTick() return Vue.nextTick()
.then(() => { .then(() => {
wrapper wrapper
......
...@@ -5848,6 +5848,9 @@ msgstr "" ...@@ -5848,6 +5848,9 @@ msgstr ""
msgid "CustomCycleAnalytics|Select stop event" msgid "CustomCycleAnalytics|Select stop event"
msgstr "" msgstr ""
msgid "CustomCycleAnalytics|Stage name already exists"
msgstr ""
msgid "CustomCycleAnalytics|Start event" msgid "CustomCycleAnalytics|Start event"
msgstr "" msgstr ""
......
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