gitlab-org--gitlab-foss/spec/frontend/vue_shared/directives/validation_spec.js

404 lines
9.9 KiB
JavaScript

import { shallowMount } from '@vue/test-utils';
import validation, { initForm } from '~/vue_shared/directives/validation';
describe('validation directive', () => {
let wrapper;
const createComponentFactory = (options) => {
const {
inputAttributes = { type: 'text', required: true },
template,
data,
feedbackMap = {},
} = options;
const defaultTemplate = `
<form>
<input v-validation:[showValidation] name="exampleField" v-bind="attributes" />
</form>
`;
const component = {
directives: {
validation: validation(feedbackMap),
},
data() {
return {
attributes: inputAttributes,
...data,
};
},
template: template || defaultTemplate,
};
wrapper = shallowMount(component, { attachTo: document.body });
};
const createComponent = (options = {}) => {
const { inputAttributes, showValidation, template, feedbackMap } = options;
return createComponentFactory({
inputAttributes,
data: {
showValidation,
form: {
state: null,
fields: {
exampleField: {
state: null,
feedback: '',
},
},
},
},
template,
feedbackMap,
});
};
const createComponentWithInitForm = (options = {}) => {
const { inputAttributes, feedbackMap } = options;
return createComponentFactory({
inputAttributes,
data: {
form: initForm({
fields: {
exampleField: {
state: null,
value: 'lorem',
},
},
}),
},
template: `
<form>
<input v-validation:[form.showValidation] name="exampleField" v-bind="attributes" />
</form>
`,
feedbackMap,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const getFormData = () => wrapper.vm.form;
const findForm = () => wrapper.find('form');
const findInput = () => wrapper.find('input');
const setValueAndTriggerValidation = (value) => {
const input = findInput();
input.setValue(value);
input.trigger('blur');
};
describe.each([true, false])(
'with fields untouched and "showValidation" set to "%s"',
(showValidation) => {
beforeEach(() => {
createComponent({ showValidation });
});
it('sets the fields validity correctly', () => {
expect(getFormData().fields.exampleField).toEqual({
state: showValidation ? false : null,
feedback: showValidation ? expect.any(String) : '',
});
});
it('sets the form validity correctly', () => {
expect(getFormData().state).toBe(false);
});
},
);
describe.each`
inputAttributes | validValue | invalidValue
${{ required: true }} | ${'foo'} | ${''}
${{ type: 'url' }} | ${'http://foo.com'} | ${'foo'}
${{ type: 'number', min: 1, max: 5 }} | ${3} | ${0}
${{ type: 'number', min: 1, max: 5 }} | ${3} | ${6}
${{ pattern: 'foo|bar' }} | ${'bar'} | ${'quz'}
`(
'with input-attributes set to $inputAttributes',
({ inputAttributes, validValue, invalidValue }) => {
beforeEach(() => {
createComponent({ inputAttributes });
});
describe('with valid value', () => {
beforeEach(() => {
setValueAndTriggerValidation(validValue);
});
it('sets the field to be valid', () => {
expect(getFormData().fields.exampleField).toEqual({
state: true,
feedback: '',
});
});
it('sets the form to be valid', () => {
expect(getFormData().state).toBe(true);
});
});
describe('with invalid value', () => {
beforeEach(() => {
setValueAndTriggerValidation(invalidValue);
});
it('sets the field to be invalid', () => {
expect(getFormData().fields.exampleField).toEqual({
state: false,
feedback: expect.any(String),
});
expect(getFormData().fields.exampleField.feedback.length).toBeGreaterThan(0);
});
it('sets the form to be invalid', () => {
expect(getFormData().state).toBe(false);
});
it('sets focus on the first invalid input when the form is submitted', () => {
findForm().trigger('submit');
expect(findInput().element).toBe(document.activeElement);
});
});
},
);
describe('with group elements', () => {
const template = `
<form>
<div v-validation:[showValidation]>
<input name="exampleField" v-bind="attributes" />
</div>
</form>
`;
beforeEach(() => {
createComponent({
template,
inputAttributes: {
required: true,
},
});
});
describe('with invalid value', () => {
beforeEach(() => {
setValueAndTriggerValidation('');
});
it('should set correct field state', () => {
expect(getFormData().fields.exampleField).toEqual({
state: false,
feedback: expect.any(String),
});
});
it('should set correct feedback', () => {
expect(getFormData().fields.exampleField.feedback).toBe('Please fill out this field.');
});
});
describe('with valid value', () => {
beforeEach(() => {
setValueAndTriggerValidation('hello');
});
it('set the correct state', () => {
expect(getFormData().fields.exampleField).toEqual({
state: true,
feedback: '',
});
});
});
});
describe('with custom feedbackMap', () => {
const customMessage = 'Please fill out the name field.';
const template = `
<form>
<div v-validation:[showValidation]>
<input name="exampleField" v-bind="attributes" />
</div>
</form>
`;
beforeEach(() => {
const feedbackMap = {
valueMissing: {
isInvalid: (el) => el.validity?.valueMissing,
message: customMessage,
},
};
createComponent({
template,
inputAttributes: {
required: true,
},
feedbackMap,
});
});
describe('with invalid value', () => {
beforeEach(() => {
setValueAndTriggerValidation('');
});
it('should set correct field state', () => {
expect(getFormData().fields.exampleField).toEqual({
state: false,
feedback: customMessage,
});
});
});
describe('with valid value', () => {
beforeEach(() => {
setValueAndTriggerValidation('hello');
});
it('set the correct state', () => {
expect(getFormData().fields.exampleField).toEqual({
state: true,
feedback: '',
});
});
});
});
describe('with validation-message present on the element', () => {
const customMessage = 'The name field is required.';
const template = `
<form>
<div v-validation:[showValidation]>
<input name="exampleField" v-bind="attributes" validation-message="${customMessage}" />
</div>
</form>
`;
beforeEach(() => {
const feedbackMap = {
valueMissing: {
isInvalid: (el) => el.validity?.valueMissing,
},
};
createComponent({
template,
inputAttributes: {
required: true,
},
feedbackMap,
});
});
describe('with invalid value', () => {
beforeEach(() => {
setValueAndTriggerValidation('');
});
it('should set correct field state', () => {
expect(getFormData().fields.exampleField).toEqual({
state: false,
feedback: customMessage,
});
});
});
describe('with valid value', () => {
beforeEach(() => {
setValueAndTriggerValidation('hello');
});
it('set the correct state', () => {
expect(getFormData().fields.exampleField).toEqual({
state: true,
feedback: '',
});
});
});
});
describe('component using initForm', () => {
it('sets the form fields correctly', () => {
createComponentWithInitForm();
expect(getFormData().state).toBe(false);
expect(getFormData().showValidation).toBe(false);
expect(getFormData().fields.exampleField).toMatchObject({
value: 'lorem',
state: null,
required: true,
feedback: expect.any(String),
});
});
});
});
describe('initForm', () => {
const MOCK_FORM = {
fields: {
name: {
value: 'lorem',
},
description: {
value: 'ipsum',
required: false,
skipValidation: true,
},
},
};
const EXPECTED_FIELDS = {
name: { value: 'lorem', required: true, state: null, feedback: null },
description: { value: 'ipsum', required: false, state: true, feedback: null },
};
it('returns form object', () => {
expect(initForm(MOCK_FORM)).toMatchObject({
state: false,
showValidation: false,
fields: EXPECTED_FIELDS,
});
});
it('returns form object with additional parameters', () => {
const customFormObject = {
foo: {
bar: 'lorem',
},
};
const form = {
...MOCK_FORM,
...customFormObject,
};
expect(initForm(form)).toMatchObject({
state: false,
showValidation: false,
fields: EXPECTED_FIELDS,
...customFormObject,
});
});
it('can override existing state and showValidation values', () => {
const form = {
...MOCK_FORM,
state: true,
showValidation: true,
};
expect(initForm(form)).toMatchObject({
state: true,
showValidation: true,
fields: EXPECTED_FIELDS,
});
});
});