2022-03-08 01:14:50 -05:00
|
|
|
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';
|
2022-06-13 05:09:24 -04:00
|
|
|
import InputWrapper from '~/pipeline_wizard/components/input_wrapper.vue';
|
2022-03-08 01:14:50 -05:00
|
|
|
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());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|