Commit 43127930 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Vitaly Slobodin

Ensure restored stages have a transition key

parent 16cb01a6
...@@ -43,6 +43,12 @@ const initializeStages = (defaultStageConfig, selectedPreset = PRESET_OPTIONS_DE ...@@ -43,6 +43,12 @@ const initializeStages = (defaultStageConfig, selectedPreset = PRESET_OPTIONS_DE
return stages.map((stage) => ({ ...stage, transitionKey: uniqueId('stage-') })); return stages.map((stage) => ({ ...stage, transitionKey: uniqueId('stage-') }));
}; };
const initializeEditingStages = (stages = []) =>
filterStagesByHiddenStatus(cloneDeep(stages), false).map((stage) => ({
...stage,
transitionKey: uniqueId(`stage-${stage.name}-`),
}));
export default { export default {
name: 'ValueStreamForm', name: 'ValueStreamForm',
components: { components: {
...@@ -93,7 +99,7 @@ export default { ...@@ -93,7 +99,7 @@ export default {
const { name: nameError = [], stages: stageErrors = [{}] } = initialFormErrors; const { name: nameError = [], stages: stageErrors = [{}] } = initialFormErrors;
const additionalFields = { const additionalFields = {
stages: this.isEditing stages: this.isEditing
? filterStagesByHiddenStatus(cloneDeep(initialStages), false) ? initializeEditingStages(initialStages)
: initializeStages(defaultStageConfig, initialPreset), : initializeStages(defaultStageConfig, initialPreset),
stageErrors: stageErrors:
cloneDeep(stageErrors) || initializeStageErrors(defaultStageConfig, initialPreset), cloneDeep(stageErrors) || initializeStageErrors(defaultStageConfig, initialPreset),
...@@ -228,8 +234,8 @@ export default { ...@@ -228,8 +234,8 @@ export default {
handleMove({ index, direction }) { handleMove({ index, direction }) {
const newStages = this.moveItem(this.stages, index, direction); const newStages = this.moveItem(this.stages, index, direction);
const newErrors = this.moveItem(this.stageErrors, index, direction); const newErrors = this.moveItem(this.stageErrors, index, direction);
Vue.set(this, 'stageErrors', cloneDeep(newErrors));
Vue.set(this, 'stages', cloneDeep(newStages)); Vue.set(this, 'stages', cloneDeep(newStages));
Vue.set(this, 'stageErrors', cloneDeep(newErrors));
}, },
validateStageFields(index) { validateStageFields(index) {
Vue.set(this.stageErrors, index, validateStage(this.stages[index])); Vue.set(this.stageErrors, index, validateStage(this.stages[index]));
...@@ -253,7 +259,10 @@ export default { ...@@ -253,7 +259,10 @@ export default {
Vue.set(this, 'hiddenStages', [ Vue.set(this, 'hiddenStages', [
...this.hiddenStages.filter((_, i) => i !== hiddenStageIndex), ...this.hiddenStages.filter((_, i) => i !== hiddenStageIndex),
]); ]);
Vue.set(this, 'stages', [...this.stages, target]); Vue.set(this, 'stages', [
...this.stages,
{ ...target, transitionKey: uniqueId(`stage-${target.name}-`) },
]);
}, },
lastStage() { lastStage() {
const stages = this.$refs.formStages; const stages = this.$refs.formStages;
...@@ -282,6 +291,15 @@ export default { ...@@ -282,6 +291,15 @@ export default {
const updatedStage = { ...this.stages[activeStageIndex], [field]: value }; const updatedStage = { ...this.stages[activeStageIndex], [field]: value };
Vue.set(this.stages, activeStageIndex, updatedStage); Vue.set(this.stages, activeStageIndex, updatedStage);
}, },
resetAllFieldsToDefault() {
this.name = '';
Vue.set(this, 'stages', initializeStages(this.defaultStageConfig, this.selectedPreset));
Vue.set(
this,
'stageErrors',
initializeStageErrors(this.defaultStageConfig, this.selectedPreset),
);
},
handleResetDefaults() { handleResetDefaults() {
if (this.isEditing) { if (this.isEditing) {
const { const {
...@@ -289,30 +307,18 @@ export default { ...@@ -289,30 +307,18 @@ export default {
} = this; } = this;
Vue.set(this, 'name', initialName); Vue.set(this, 'name', initialName);
Vue.set(this, 'nameError', []); Vue.set(this, 'nameError', []);
Vue.set(this, 'stages', cloneDeep(initialStages)); Vue.set(this, 'stages', initializeStages(initialStages));
Vue.set(this, 'stageErrors', [{}]); Vue.set(this, 'stageErrors', [{}]);
} else { } else {
this.name = ''; this.resetAllFieldsToDefault();
this.defaultStageConfig.forEach((stage, index) => {
Vue.set(this.stages, index, { ...stage, hidden: false });
});
} }
}, },
handleResetBlank() {
this.name = '';
Vue.set(this, 'stages', initializeStages(this.defaultStageConfig, this.selectedPreset));
},
onSelectPreset() { onSelectPreset() {
if (this.selectedPreset === PRESET_OPTIONS_DEFAULT) { if (this.selectedPreset === PRESET_OPTIONS_DEFAULT) {
this.handleResetDefaults(); this.handleResetDefaults();
} else { } else {
this.handleResetBlank(); this.resetAllFieldsToDefault();
} }
Vue.set(
this,
'stageErrors',
initializeStageErrors(this.defaultStageConfig, this.selectedPreset),
);
}, },
restoreActionTestId(index) { restoreActionTestId(index) {
return `stage-action-restore-${index}`; return `stage-action-restore-${index}`;
......
...@@ -7,6 +7,8 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -7,6 +7,8 @@ RSpec.describe 'Multiple value streams', :js do
let_it_be(:group) { create(:group, name: 'CA-test-group') } let_it_be(:group) { create(:group, name: 'CA-test-group') }
let_it_be(:project) { create(:project, :repository, namespace: group, group: group, name: 'Cool fun project') } let_it_be(:project) { create(:project, :repository, namespace: group, group: group, name: 'Cool fun project') }
let_it_be(:sub_group) { create(:group, name: 'CA-sub-group', parent: group) } let_it_be(:sub_group) { create(:group, name: 'CA-sub-group', parent: group) }
let_it_be(:group_label1) { create(:group_label, group: group) }
let_it_be(:group_label2) { create(:group_label, group: group) }
let_it_be(:user) do let_it_be(:user) do
create(:user).tap do |u| create(:user).tap do |u|
group.add_owner(u) group.add_owner(u)
...@@ -70,6 +72,7 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -70,6 +72,7 @@ RSpec.describe 'Multiple value streams', :js do
it 'can create a value stream with a custom stage and hidden defaults' do it 'can create a value stream with a custom stage and hidden defaults' do
add_custom_stage_to_form add_custom_stage_to_form
add_custom_label_stage_to_form
# Hide some default stages # Hide some default stages
click_action_button('hide', 5) click_action_button('hide', 5)
...@@ -126,6 +129,7 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -126,6 +129,7 @@ RSpec.describe 'Multiple value streams', :js do
it 'can add and remove custom stages' do it 'can add and remove custom stages' do
add_custom_stage_to_form add_custom_stage_to_form
add_custom_label_stage_to_form
page.find_button(_('Save Value Stream')).click page.find_button(_('Save Value Stream')).click
wait_for_requests wait_for_requests
...@@ -138,6 +142,10 @@ RSpec.describe 'Multiple value streams', :js do ...@@ -138,6 +142,10 @@ RSpec.describe 'Multiple value streams', :js do
click_action_button('remove', 7) click_action_button('remove', 7)
click_action_button('remove', 6) click_action_button('remove', 6)
# re-order some stages
page.all("[data-testid*='stage-action-move-down-']").first.click
page.all("[data-testid*='stage-action-move-up-']").last.click
page.find_button(_('Save Value Stream')).click page.find_button(_('Save Value Stream')).click
wait_for_requests wait_for_requests
......
...@@ -103,6 +103,8 @@ describe('ValueStreamForm', () => { ...@@ -103,6 +103,8 @@ describe('ValueStreamForm', () => {
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 expectStageTransitionKeys = (stages) =>
stages.forEach((stage) => expect(stage.transitionKey).toContain('stage-'));
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -140,6 +142,16 @@ describe('ValueStreamForm', () => { ...@@ -140,6 +142,16 @@ describe('ValueStreamForm', () => {
}); });
}); });
it('each stage has a transition key when toggling', () => {
findPresetSelector().vm.$emit('input', PRESET_OPTIONS_BLANK);
expectStageTransitionKeys(wrapper.vm.stages);
findPresetSelector().vm.$emit('input', PRESET_OPTIONS_DEFAULT);
expectStageTransitionKeys(wrapper.vm.stages);
});
it('does not display any hidden stages', () => { it('does not display any hidden stages', () => {
expect(findHiddenStages().length).toBe(0); expect(findHiddenStages().length).toBe(0);
}); });
...@@ -176,6 +188,10 @@ describe('ValueStreamForm', () => { ...@@ -176,6 +188,10 @@ describe('ValueStreamForm', () => {
expect(wrapper.vm.nameError).toEqual(['Name is required']); expect(wrapper.vm.nameError).toEqual(['Name is required']);
}); });
it('each stage has a transition key', () => {
expectStageTransitionKeys(wrapper.vm.stages);
});
}); });
describe('form errors', () => { describe('form errors', () => {
...@@ -239,6 +255,10 @@ describe('ValueStreamForm', () => { ...@@ -239,6 +255,10 @@ describe('ValueStreamForm', () => {
expect(findHiddenStages().length).toBe(0); expect(findHiddenStages().length).toBe(0);
}); });
it('each stage has a transition key', () => {
expectStageTransitionKeys(wrapper.vm.stages);
});
describe('restore defaults button', () => { describe('restore defaults button', () => {
it('will clear the form fields', async () => { it('will clear the form fields', async () => {
expect(wrapper.vm.stages).toHaveLength(stageCount); expect(wrapper.vm.stages).toHaveLength(stageCount);
...@@ -280,6 +300,14 @@ describe('ValueStreamForm', () => { ...@@ -280,6 +300,14 @@ describe('ValueStreamForm', () => {
expect(findHiddenStages().length).toBe(hiddenStages.length - 1); expect(findHiddenStages().length).toBe(hiddenStages.length - 1);
expect(wrapper.vm.stages.length).toBe(stageCount + 1); expect(wrapper.vm.stages.length).toBe(stageCount + 1);
}); });
it('when a stage is restored it has a transition key', async () => {
await clickRestoreStageAtIndex(1);
expect(wrapper.vm.stages[stageCount].transitionKey).toContain(
`stage-${hiddenStages[1].name}-`,
);
});
}); });
describe('Add stage button', () => { describe('Add stage button', () => {
......
...@@ -23,12 +23,39 @@ module CycleAnalyticsHelpers ...@@ -23,12 +23,39 @@ module CycleAnalyticsHelpers
end end
end end
def select_event_label(sel)
page.within(sel) do
find('.dropdown-toggle').click
page.find(".dropdown-menu").all(".dropdown-item")[1].click
end
end
def fill_in_custom_label_stage_fields
index = page.all('[data-testid="value-stream-stage-fields"]').length
last_stage = page.all('[data-testid="value-stream-stage-fields"]').last
within last_stage do
find('[name*="custom-stage-name-"]').fill_in with: "Cool custom label stage - name #{index}"
select_dropdown_option_by_value "custom-stage-start-event-", :issue_label_added
select_dropdown_option_by_value "custom-stage-end-event-", :issue_label_removed
select_event_label("[data-testid*='custom-stage-start-event-label-']")
select_event_label("[data-testid*='custom-stage-end-event-label-']")
end
end
def add_custom_stage_to_form def add_custom_stage_to_form
page.find_button(s_('CreateValueStreamForm|Add another stage')).click page.find_button(s_('CreateValueStreamForm|Add another stage')).click
fill_in_custom_stage_fields fill_in_custom_stage_fields
end end
def add_custom_label_stage_to_form
page.find_button(s_('CreateValueStreamForm|Add another stage')).click
fill_in_custom_label_stage_fields
end
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
......
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