Commit 96f3d43e authored by Janis Altherr's avatar Janis Altherr Committed by Natalia Tepluhina

Fix Pipeline Wizard losing state when returning to a step

parent eec06336
...@@ -92,6 +92,7 @@ export default { ...@@ -92,6 +92,7 @@ export default {
ref="widget" ref="widget"
:validate="validate" :validate="validate"
v-bind="$attrs" v-bind="$attrs"
:data-input-target="target"
@input="onModelChange" @input="onModelChange"
@update:valid="onValidationStateChange" @update:valid="onValidationStateChange"
/> />
......
<script> <script>
import { GlProgressBar } from '@gitlab/ui'; import { GlProgressBar } from '@gitlab/ui';
import { Document } from 'yaml'; import { Document } from 'yaml';
import { uniqueId } from 'lodash';
import { merge } from '~/lib/utils/yaml'; import { merge } from '~/lib/utils/yaml';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { isValidStepSeq } from '~/pipeline_wizard/validators'; import { isValidStepSeq } from '~/pipeline_wizard/validators';
...@@ -57,15 +58,6 @@ export default { ...@@ -57,15 +58,6 @@ export default {
}; };
}, },
computed: { computed: {
currentStepConfig() {
return this.steps.get(this.currentStepIndex);
},
currentStepInputs() {
return this.currentStepConfig.get('inputs').toJSON();
},
currentStepTemplate() {
return this.currentStepConfig.get('template', true);
},
currentStep() { currentStep() {
return this.currentStepIndex + 1; return this.currentStepIndex + 1;
}, },
...@@ -78,6 +70,13 @@ export default { ...@@ -78,6 +70,13 @@ export default {
isLastStep() { isLastStep() {
return this.currentStep === this.stepCount; return this.currentStep === this.stepCount;
}, },
stepList() {
return this.steps.items.map((_, i) => ({
id: uniqueId(),
inputs: this.steps.get(i).get('inputs').toJSON(),
template: this.steps.get(i).get('template', true),
}));
},
}, },
watch: { watch: {
isLastStep(value) { isLastStep(value) {
...@@ -85,6 +84,9 @@ export default { ...@@ -85,6 +84,9 @@ export default {
}, },
}, },
methods: { methods: {
getStep(index) {
return this.steps.get(index);
},
resetHighlight() { resetHighlight() {
this.highlightPath = null; this.highlightPath = null;
}, },
...@@ -119,8 +121,8 @@ export default { ...@@ -119,8 +121,8 @@ export default {
</header> </header>
<section class="gl-mb-4"> <section class="gl-mb-4">
<commit-step <commit-step
v-if="isLastStep" v-show="isLastStep"
ref="step" data-testid="step"
:default-branch="defaultBranch" :default-branch="defaultBranch"
:file-content="pipelineBlob" :file-content="pipelineBlob"
:filename="filename" :filename="filename"
...@@ -128,15 +130,16 @@ export default { ...@@ -128,15 +130,16 @@ export default {
@back="currentStepIndex--" @back="currentStepIndex--"
/> />
<wizard-step <wizard-step
v-else v-for="(step, i) in stepList"
:key="currentStepIndex" v-show="i === currentStepIndex"
ref="step" :key="step.id"
data-testid="step"
:compiled.sync="compiled" :compiled.sync="compiled"
:has-next-step="currentStepIndex < steps.items.length" :has-next-step="i < steps.items.length"
:has-previous-step="currentStepIndex > 0" :has-previous-step="i > 0"
:highlight.sync="highlightPath" :highlight.sync="highlightPath"
:inputs="currentStepInputs" :inputs="step.inputs"
:template="currentStepTemplate" :template="step.template"
@back="currentStepIndex--" @back="currentStepIndex--"
@next="currentStepIndex++" @next="currentStepIndex++"
@update:compiled="onUpdate" @update:compiled="onUpdate"
......
import { Document, parseDocument } from 'yaml'; import { Document, parseDocument } from 'yaml';
import { GlProgressBar } from '@gitlab/ui'; import { GlProgressBar } from '@gitlab/ui';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import PipelineWizardWrapper, { i18n } from '~/pipeline_wizard/components/wrapper.vue'; import PipelineWizardWrapper, { i18n } from '~/pipeline_wizard/components/wrapper.vue';
import WizardStep from '~/pipeline_wizard/components/step.vue'; import WizardStep from '~/pipeline_wizard/components/step.vue';
import CommitStep from '~/pipeline_wizard/components/commit.vue'; import CommitStep from '~/pipeline_wizard/components/commit.vue';
import YamlEditor from '~/pipeline_wizard/components/editor.vue'; import YamlEditor from '~/pipeline_wizard/components/editor.vue';
import { sprintf } from '~/locale'; import { sprintf } from '~/locale';
import { steps as stepsYaml } from '../mock/yaml'; import {
steps as stepsYaml,
compiledScenario1,
compiledScenario2,
compiledScenario3,
} from '../mock/yaml';
describe('Pipeline Wizard - wrapper.vue', () => { describe('Pipeline Wizard - wrapper.vue', () => {
let wrapper; let wrapper;
const steps = parseDocument(stepsYaml).toJS(); const steps = parseDocument(stepsYaml).toJS();
const getAsYamlNode = (value) => new Document(value).contents; const getAsYamlNode = (value) => new Document(value).contents;
const createComponent = (props = {}) => { const createComponent = (props = {}, mountFn = shallowMountExtended) => {
wrapper = shallowMountExtended(PipelineWizardWrapper, { wrapper = mountFn(PipelineWizardWrapper, {
propsData: { propsData: {
projectPath: '/user/repo', projectPath: '/user/repo',
defaultBranch: 'main', defaultBranch: 'main',
...@@ -23,13 +28,21 @@ describe('Pipeline Wizard - wrapper.vue', () => { ...@@ -23,13 +28,21 @@ describe('Pipeline Wizard - wrapper.vue', () => {
steps: getAsYamlNode(steps), steps: getAsYamlNode(steps),
...props, ...props,
}, },
stubs: {
CommitStep: true,
},
}); });
}; };
const getEditorContent = () => { const getEditorContent = () => {
return wrapper.getComponent(YamlEditor).attributes().doc.toString(); return wrapper.getComponent(YamlEditor).props().doc.toString();
}; };
const getStepWrapper = () => wrapper.getComponent(WizardStep); const getStepWrapper = () =>
wrapper.findAllComponents(WizardStep).wrappers.find((w) => w.isVisible());
const getGlProgressBarWrapper = () => wrapper.getComponent(GlProgressBar); const getGlProgressBarWrapper = () => wrapper.getComponent(GlProgressBar);
const findFirstVisibleStep = () =>
wrapper.findAllComponents('[data-testid="step"]').wrappers.find((w) => w.isVisible());
const findFirstInputFieldForTarget = (target) =>
wrapper.find(`[data-input-target="${target}"]`).find('input');
describe('display', () => { describe('display', () => {
afterEach(() => { afterEach(() => {
...@@ -118,8 +131,9 @@ describe('Pipeline Wizard - wrapper.vue', () => { ...@@ -118,8 +131,9 @@ describe('Pipeline Wizard - wrapper.vue', () => {
}) => { }) => {
beforeAll(async () => { beforeAll(async () => {
createComponent(); createComponent();
for (const emittedValue of navigationEventChain) { for (const emittedValue of navigationEventChain) {
wrapper.findComponent({ ref: 'step' }).vm.$emit(emittedValue); findFirstVisibleStep().vm.$emit(emittedValue);
// We have to wait for the next step to be mounted // We have to wait for the next step to be mounted
// before we can emit the next event, so we have to await // before we can emit the next event, so we have to await
// inside the loop. // inside the loop.
...@@ -134,11 +148,11 @@ describe('Pipeline Wizard - wrapper.vue', () => { ...@@ -134,11 +148,11 @@ describe('Pipeline Wizard - wrapper.vue', () => {
if (expectCommitStepShown) { if (expectCommitStepShown) {
it('does not show the step wrapper', async () => { it('does not show the step wrapper', async () => {
expect(wrapper.findComponent(WizardStep).exists()).toBe(false); expect(wrapper.findComponent(WizardStep).isVisible()).toBe(false);
}); });
it('shows the commit step page', () => { it('shows the commit step page', () => {
expect(wrapper.findComponent(CommitStep).exists()).toBe(true); expect(wrapper.findComponent(CommitStep).isVisible()).toBe(true);
}); });
} else { } else {
it('passes the correct step config to the step component', async () => { it('passes the correct step config to the step component', async () => {
...@@ -146,7 +160,7 @@ describe('Pipeline Wizard - wrapper.vue', () => { ...@@ -146,7 +160,7 @@ describe('Pipeline Wizard - wrapper.vue', () => {
}); });
it('does not show the commit step page', () => { it('does not show the commit step page', () => {
expect(wrapper.findComponent(CommitStep).exists()).toBe(false); expect(wrapper.findComponent(CommitStep).isVisible()).toBe(false);
}); });
} }
...@@ -247,4 +261,54 @@ describe('Pipeline Wizard - wrapper.vue', () => { ...@@ -247,4 +261,54 @@ describe('Pipeline Wizard - wrapper.vue', () => {
expect(wrapper.getComponent(YamlEditor).props('highlight')).toBe(null); expect(wrapper.getComponent(YamlEditor).props('highlight')).toBe(null);
}); });
}); });
describe('integration test', () => {
beforeAll(async () => {
createComponent({}, mountExtended);
});
it('updates the editor content after input on step 1', async () => {
findFirstInputFieldForTarget('$FOO').setValue('fooVal');
await nextTick();
expect(getEditorContent()).toBe(compiledScenario1);
});
it('updates the editor content after input on step 2', async () => {
findFirstVisibleStep().vm.$emit('next');
await nextTick();
findFirstInputFieldForTarget('$BAR').setValue('barVal');
await nextTick();
expect(getEditorContent()).toBe(compiledScenario2);
});
describe('navigating back', () => {
let inputField;
beforeAll(async () => {
findFirstVisibleStep().vm.$emit('back');
await nextTick();
inputField = findFirstInputFieldForTarget('$FOO');
});
afterAll(() => {
wrapper.destroy();
inputField = undefined;
});
it('still shows the input values from the former visit', () => {
expect(inputField.element.value).toBe('fooVal');
});
it('updates the editor content without modifying input that came from a later step', async () => {
inputField.setValue('newFooVal');
await nextTick();
expect(getEditorContent()).toBe(compiledScenario3);
});
});
});
}); });
...@@ -59,6 +59,17 @@ export const steps = ` ...@@ -59,6 +59,17 @@ export const steps = `
bar: $BAR bar: $BAR
`; `;
export const compiledScenario1 = `foo: fooVal
`;
export const compiledScenario2 = `foo: fooVal
bar: barVal
`;
export const compiledScenario3 = `foo: newFooVal
bar: barVal
`;
export const fullTemplate = ` export const fullTemplate = `
title: some title title: some title
description: some description description: some description
......
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