@financial-times/n-conversion-forms
Version:
Containing jsx components and styles for forms included on Accounts and Acqusition apps (next-signup, next-profile, next-retention, etc).
251 lines (200 loc) • 7.13 kB
JavaScript
jest.mock('@financial-times/o-forms');
jest.mock('@financial-times/o-forms/src/js/input');
const OForms = require('@financial-times/o-forms');
const Input = require('@financial-times/o-forms/src/js/input').default;
const ValidationUtil = require('./validation');
describe('Validation - Util', () => {
let validation;
const mockErrorSummaryMessage = 'mockError';
const mockUseBrowserValidation = false;
const mockPreventSubmit = true;
beforeEach(() => {
document.body.innerHTML = `
<!DOCTYPE html>
<html>
<head></head>
<body>
<form class="ncf"></form>
</body>
</html>`;
validation = new ValidationUtil({
formsOptions: {
errorSummaryMessage: mockErrorSummaryMessage,
useBrowserValidation: mockUseBrowserValidation,
preventSubmit: mockPreventSubmit,
},
});
jest.spyOn(validation, 'checkFormValidity');
validation.init();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('constructor', () => {
it('calls oForms to setup client side validation with expected arguments', () => {
const form = document.querySelector('form.ncf');
expect(OForms.default.init).toHaveBeenCalledWith(form, {
errorSummaryMessage: mockErrorSummaryMessage,
useBrowserValidation: mockUseBrowserValidation,
preventSubmit: mockPreventSubmit,
});
});
it('checks validation status on init', () => {
expect(validation.checkFormValidity).toHaveBeenCalled();
});
it('has a $form property exposing the form element', () => {
expect(validation.$form).toBeDefined();
});
});
describe('init', () => {
it('adds an event listener to required elements', () => {
expect(
validation.$requiredEls[0].input.addEventListener
).toHaveBeenCalled();
});
it('binds to onbeforeunload by default', () => {
expect(global.window.onbeforeunload).toBeDefined();
expect(global.window.onbeforeunload).toBeInstanceOf(Function);
});
it('does not bind to onbeforeunload if mutePromptBeforeLeaving = true', () => {
delete global.window.onbeforeunload;
const validationTest = new ValidationUtil({
mutePromptBeforeLeaving: true,
});
validationTest.init();
expect(global.window.onbeforeunload).not.toBeDefined();
});
});
describe('onbeforeunload', () => {
it('returns null by default', () => {
expect(global.window.onbeforeunload()).toBe(null);
});
it('returns true if the form has changed', () => {
validation.formChanged = true;
expect(global.window.onbeforeunload()).toBe(true);
});
});
describe('checkElementValidity', () => {
let $el;
beforeEach(() => {
$el = { foo: true };
});
it('does not call validateInput if custom validation fails', () => {
jest.spyOn(validation, 'checkCustomValidation').mockReturnValue(false);
validation.checkElementValidity($el);
expect(Input).not.toHaveBeenCalled();
});
it('calls input.validate for the element.', () => {
validation.checkElementValidity($el);
expect(Input).toHaveBeenCalledWith($el);
});
});
describe('checkFormValidity', () => {
it('sets the form as invalid if there are invalid elements.', () => {
validation.formValid = true;
validation.$requiredEls[0].input.checkValidity.mockReturnValue(false);
validation.checkFormValidity();
expect(validation.formValid).toBe(false);
});
it('sets the form as valid if there are no invalid elements.', () => {
validation.formValid = false;
validation.checkFormValidity();
expect(validation.formValid).toBe(true);
});
});
describe('Custom Validation', () => {
let field;
beforeEach(() => {
field = validation.oForms.formInputs.find(
(el) => el.input.name === 'foo'
);
});
describe('addCustomValidation', () => {
it('stores a custom validation function', () => {
validation.addCustomValidation({
errorMessage: 'Oops, something custom went wrong!',
field,
validator: jest.fn(),
});
expect(validation.customValidation.size).toBe(1);
});
it('throws if a custom validation function has already been specified for a particular field', () => {
validation.addCustomValidation({
errorMessage: 'Oops, something custom went wrong!',
field,
validator: jest.fn(),
});
expect(() => {
validation.addCustomValidation({
errorMessage: 'Oops, something else custom went wrong!',
field,
});
}).toThrow();
});
it('stores a custom validation function that will show a custom validation message when validation fails', async () => {
jest.spyOn(validation, 'showCustomFieldValidationError');
jest.spyOn(validation, 'clearCustomFieldValidationError');
validation.addCustomValidation({
errorMessage: 'Oops, something custom went wrong!',
field,
validator: jest.fn().mockReturnValue(false),
});
// Run the stored custom validation function
await validation.customValidation.get('foo')();
expect(validation.showCustomFieldValidationError).toHaveBeenCalled();
expect(
validation.clearCustomFieldValidationError
).not.toHaveBeenCalled();
});
it('stores a custom validation function that will clear a custom validation message when validation passes', async () => {
jest.spyOn(validation, 'showCustomFieldValidationError');
jest.spyOn(validation, 'clearCustomFieldValidationError');
validation.addCustomValidation({
errorMessage: 'Oops, something custom went wrong!',
field,
validator: jest.fn().mockReturnValue(true),
});
// Run the stored custom validation function
await validation.customValidation.get('foo')();
expect(validation.clearCustomFieldValidationError).toHaveBeenCalled();
});
});
describe('showCustomFieldValidationError', () => {
let messageStub = { foo: 'bar' };
it('adds the o-form--error class to the parent', () => {
jest.spyOn(field.input.parentNode.classList, 'add');
validation.showCustomFieldValidationError(field.input, messageStub);
expect(field.input.parentNode.classList.add).toHaveBeenCalledWith(
'o-forms-input--invalid'
);
});
it('adds the message to the parent', () => {
jest.spyOn(global.document, 'querySelector').mockReturnValue(null);
validation.showCustomFieldValidationError(field.input, messageStub);
expect(field.input.parentNode.insertBefore).toHaveBeenCalledWith(
messageStub,
null
);
});
});
describe('clearCustomFieldValidationError', () => {
beforeEach(() => {
validation.$form = document.createElement('form');
});
it('removes the message from the page', () => {
jest
.spyOn(validation.$form, 'querySelector')
.mockReturnValue(field.input);
validation.clearCustomFieldValidationError(field.input);
expect(field.parentNode.removeChild).toHaveBeenCalledWith(field.input);
});
it('re-checks the element validity for standard validation rules', () => {
jest.spyOn(validation, 'checkElementValidity');
validation.clearCustomFieldValidationError(field.input);
expect(validation.checkElementValidity).toHaveBeenCalledWith(
field.input
);
});
});
});
});