Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/pipeline_wizard/components/step_spec.js')
-rw-r--r--spec/frontend/pipeline_wizard/components/step_spec.js227
1 files changed, 227 insertions, 0 deletions
diff --git a/spec/frontend/pipeline_wizard/components/step_spec.js b/spec/frontend/pipeline_wizard/components/step_spec.js
new file mode 100644
index 00000000000..2289a349318
--- /dev/null
+++ b/spec/frontend/pipeline_wizard/components/step_spec.js
@@ -0,0 +1,227 @@
+import { parseDocument, Document } from 'yaml';
+import { omit } from 'lodash';
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import PipelineWizardStep from '~/pipeline_wizard/components/step.vue';
+import InputWrapper from '~/pipeline_wizard/components/input.vue';
+import StepNav from '~/pipeline_wizard/components/step_nav.vue';
+import {
+ stepInputs,
+ stepTemplate,
+ compiledYamlBeforeSetup,
+ compiledYamlAfterInitialLoad,
+ compiledYaml,
+} from '../mock/yaml';
+
+describe('Pipeline Wizard - Step Page', () => {
+ const inputs = parseDocument(stepInputs).toJS();
+ let wrapper;
+ let input1;
+ let input2;
+
+ const getInputWrappers = () => wrapper.findAllComponents(InputWrapper);
+ const forEachInputWrapper = (cb) => {
+ getInputWrappers().wrappers.forEach(cb);
+ };
+ const getStepNav = () => {
+ return wrapper.findComponent(StepNav);
+ };
+ const mockNextClick = () => {
+ getStepNav().vm.$emit('next');
+ };
+ const mockPrevClick = () => {
+ getStepNav().vm.$emit('back');
+ };
+ const expectFalsyAttributeValue = (testedWrapper, attributeName) => {
+ expect([false, null, undefined]).toContain(testedWrapper.attributes(attributeName));
+ };
+ const findInputWrappers = () => {
+ const inputWrappers = wrapper.findAllComponents(InputWrapper);
+ input1 = inputWrappers.at(0);
+ input2 = inputWrappers.at(1);
+ };
+
+ const createComponent = (props = {}) => {
+ const template = parseDocument(stepTemplate).get('template');
+ const defaultProps = {
+ inputs,
+ template,
+ };
+ wrapper = shallowMountExtended(PipelineWizardStep, {
+ propsData: {
+ ...defaultProps,
+ compiled: parseDocument(compiledYamlBeforeSetup),
+ ...props,
+ },
+ });
+ };
+
+ afterEach(async () => {
+ await wrapper.destroy();
+ });
+
+ describe('input children', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('mounts an inputWrapper for each input type', () => {
+ forEachInputWrapper((inputWrapper, i) =>
+ expect(inputWrapper.attributes('widget')).toBe(inputs[i].widget),
+ );
+ });
+
+ it('passes all unused props to the inputWrapper', () => {
+ const pickChildProperties = (from) => {
+ return omit(from, ['target', 'widget']);
+ };
+ forEachInputWrapper((inputWrapper, i) => {
+ const expectedProps = pickChildProperties(inputs[i]);
+ Object.entries(expectedProps).forEach(([key, value]) => {
+ expect(inputWrapper.attributes(key.toLowerCase())).toEqual(value.toString());
+ });
+ });
+ });
+ });
+
+ const yamlDocument = new Document({ foo: { bar: 'baz' } });
+ const yamlNode = yamlDocument.get('foo');
+
+ describe('prop validation', () => {
+ describe.each`
+ componentProp | required | valid | invalid
+ ${'inputs'} | ${true} | ${[inputs, []]} | ${[['invalid'], [null], [{}, {}]]}
+ ${'template'} | ${true} | ${[yamlNode]} | ${['invalid', null, { foo: 1 }, yamlDocument]}
+ ${'compiled'} | ${true} | ${[yamlDocument]} | ${['invalid', null, { foo: 1 }, yamlNode]}
+ `('testing `$componentProp` prop', ({ componentProp, required, valid, invalid }) => {
+ it('expects prop to be required', () => {
+ expect(PipelineWizardStep.props[componentProp].required).toEqual(required);
+ });
+
+ it('prop validators return false for invalid types', () => {
+ const validatorFunc = PipelineWizardStep.props[componentProp].validator;
+ invalid.forEach((invalidType) => {
+ expect(validatorFunc(invalidType)).toBe(false);
+ });
+ });
+
+ it('prop validators return true for valid types', () => {
+ const validatorFunc = PipelineWizardStep.props[componentProp].validator;
+ valid.forEach((validType) => {
+ expect(validatorFunc(validType)).toBe(true);
+ });
+ });
+ });
+ });
+
+ describe('navigation', () => {
+ it('shows the next button', () => {
+ createComponent();
+
+ expect(getStepNav().attributes('nextbuttonenabled')).toEqual('true');
+ });
+
+ it('does not show a back button if hasPreviousStep is false', () => {
+ createComponent({ hasPreviousStep: false });
+
+ expectFalsyAttributeValue(getStepNav(), 'showbackbutton');
+ });
+
+ it('shows a back button if hasPreviousStep is true', () => {
+ createComponent({ hasPreviousStep: true });
+
+ expect(getStepNav().attributes('showbackbutton')).toBe('true');
+ });
+
+ it('lets "back" event bubble upwards', async () => {
+ createComponent();
+
+ await mockPrevClick();
+ await nextTick();
+
+ expect(wrapper.emitted().back).toBeTruthy();
+ });
+
+ it('lets "next" event bubble upwards', async () => {
+ createComponent();
+
+ await mockNextClick();
+ await nextTick();
+
+ expect(wrapper.emitted().next).toBeTruthy();
+ });
+ });
+
+ describe('validation', () => {
+ beforeEach(() => {
+ createComponent({ hasNextPage: true });
+ findInputWrappers();
+ });
+
+ it('sets invalid once one input field has an invalid value', async () => {
+ input1.vm.$emit('update:valid', true);
+ input2.vm.$emit('update:valid', false);
+
+ await mockNextClick();
+
+ expectFalsyAttributeValue(getStepNav(), 'nextbuttonenabled');
+ });
+
+ it('returns to valid state once the invalid input is valid again', async () => {
+ input1.vm.$emit('update:valid', true);
+ input2.vm.$emit('update:valid', false);
+
+ await mockNextClick();
+
+ expectFalsyAttributeValue(getStepNav(), 'nextbuttonenabled');
+
+ input2.vm.$emit('update:valid', true);
+ await nextTick();
+
+ expect(getStepNav().attributes('nextbuttonenabled')).toBe('true');
+ });
+
+ it('passes validate state to all input wrapper children when next is clicked', async () => {
+ forEachInputWrapper((inputWrapper) => {
+ expectFalsyAttributeValue(inputWrapper, 'validate');
+ });
+
+ await mockNextClick();
+
+ expect(input1.attributes('validate')).toBe('true');
+ });
+
+ it('not emitting a valid state is considered valid', async () => {
+ // input1 does not emit a update:valid event
+ input2.vm.$emit('update:valid', true);
+
+ await mockNextClick();
+
+ expect(getStepNav().attributes('nextbuttonenabled')).toBe('true');
+ });
+ });
+
+ describe('template compilation', () => {
+ beforeEach(() => {
+ createComponent();
+ findInputWrappers();
+ });
+
+ it('injects the template when an input wrapper emits a beforeUpdate:compiled event', async () => {
+ input1.vm.$emit('beforeUpdate:compiled');
+
+ expect(wrapper.vm.compiled.toString()).toBe(compiledYamlAfterInitialLoad);
+ });
+
+ it('lets the "update:compiled" event bubble upwards', async () => {
+ const compiled = parseDocument(compiledYaml);
+
+ await input1.vm.$emit('update:compiled', compiled);
+
+ const updateEvents = wrapper.emitted()['update:compiled'];
+ const latestUpdateEvent = updateEvents[updateEvents.length - 1];
+
+ expect(latestUpdateEvent[0].toString()).toBe(compiled.toString());
+ });
+ });
+});