gitlab-org--gitlab-foss/spec/frontend/pipeline_wizard/components/step_spec.js

228 lines
6.9 KiB
JavaScript

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());
});
});
});