Skip to content

Commit

Permalink
feat(form-builder/validation): allow async validation (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
hpierre74 authored Jan 10, 2022
1 parent 1c29c33 commit 9f7e11b
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 57 deletions.
4 changes: 3 additions & 1 deletion libs/form-builder/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,7 @@ export interface Dictionary {
}

export interface ExtraValidation {
[FieldId: string]: (value?: any) => (input?: any) => boolean;
[FieldId: string]:
| ((value?: any) => (input?: any) => Promise<boolean | string>)
| ((value?: any) => (input?: any) => boolean | string);
}
52 changes: 0 additions & 52 deletions libs/form-builder/src/lib/utils/__tests__/validation.utils.spec.js

This file was deleted.

73 changes: 73 additions & 0 deletions libs/form-builder/src/lib/utils/__tests__/validation.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { getFieldRules, handleValidateErrorMessage } from '../validation.utils';

describe('handleValidateErrorMessage', () => {
const validate = jest.fn();
const message = 'error label';

it('should return validate result if truthy', async () => {
validate.mockResolvedValue(true);
await expect(
handleValidateErrorMessage(validate, message)()
).resolves.toEqual(true);
});

it('should return the message if falsy', async () => {
validate.mockResolvedValue(false);
await expect(
handleValidateErrorMessage(validate, message)()
).resolves.toEqual('error label');
});
});

describe('getFieldRules', () => {
describe('extraValidation', () => {
const extraValidation = {
func1: jest.fn(() => () => Promise.resolve(true)),
func2: jest.fn(() => () => Promise.resolve(false))
};

it('should return hook rules when we provide default rule names', () => {
const validation = {
func1: { key: 'required', value: true, message: 'required error' },
func2: { key: 'minLength', value: 8, message: 'minLength error' }
};

const resultHook = {
required: { value: true, message: 'required error' }
};

expect(getFieldRules({ validation, extraValidation })).toEqual(
resultHook
);
});

it('should complete validate if we provide existing extraValidation', async () => {
const validation = {
func1: { key: 'func1', value: '12', message: 'error label' },
func2: { key: 'func2', value: '12', message: 'error label' }
};

const rules = getFieldRules({ validation, extraValidation });
expect(rules.validate).toEqual({
func1: expect.any(Function),
func2: expect.any(Function)
});
await expect(rules?.validate?.func1()).resolves.toEqual(true);
await expect(rules?.validate?.func2()).resolves.toBe('error label');
});

it('should not complete validate if we provide missing extraValidation function', () => {
const validation = {
func1: { key: 'func1', value: '', message: 'func1 error' },
badFunc: { key: 'badFunc', value: '', message: 'badFunc error' }
};

const rules = getFieldRules({ validation, extraValidation });

expect(JSON.stringify(rules.validate)).toEqual(
JSON.stringify({ func1: extraValidation.func1() })
);
expect(rules?.validate?.func2).toBeUndefined();
});
});
});
27 changes: 23 additions & 4 deletions libs/form-builder/src/lib/utils/validation.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ import _ from 'lodash';
import { DEFAULT_RULES_NAMES } from '../constants';
import { ExtraValidation, Validations } from '../types';

export const handleValidateErrorMessage =
(validate: (...args: any[]) => boolean | undefined, message: string) =>
async (...args: any[]) => {
const result = await validate(...args);

return result || message;
};

export interface GetFieldRulesArgs {
validation?: Validations;
extraValidation?: ExtraValidation;
}

export interface FieldRules extends RegisterOptions {
validate?: { [key: string]: (value?: any) => Promise<boolean> | boolean };
}

export const getFieldRules = ({
validation,
extraValidation
}: GetFieldRulesArgs): RegisterOptions => {
}: GetFieldRulesArgs): FieldRules => {
const hookFormRules = _.reduce(
validation,
(acc, { key, ...rest }) =>
Expand All @@ -22,10 +34,17 @@ export const getFieldRules = ({

const extraRules = _.reduce(
validation,
(acc, { key, value }) =>
_.includes(DEFAULT_RULES_NAMES, key) || !_.get(extraValidation, key)
(acc, { key, value, message }) =>
_.includes(DEFAULT_RULES_NAMES, key) ||
(extraValidation && !extraValidation[key])
? acc
: { ...acc, [key]: _.invoke(extraValidation, key, value) },
: {
...acc,
[key]: handleValidateErrorMessage(
_.invoke(extraValidation, key, value),
message
)
},
{}
);
const hasExtraRules = !!Object.keys(extraRules).length;
Expand Down

0 comments on commit 9f7e11b

Please sign in to comment.