Skip to content

Commit

Permalink
feat: provide root schema to validators and improve validator typing
Browse files Browse the repository at this point in the history
  • Loading branch information
alexgrozav committed Nov 11, 2023
1 parent 030c862 commit 29f0621
Show file tree
Hide file tree
Showing 26 changed files with 538 additions and 187 deletions.
7 changes: 5 additions & 2 deletions src/types/validation.ts
Expand Up @@ -17,9 +17,12 @@ export type ValidateOnEvent = 'blur' | 'change' | 'input' | 'submit';

export type FormValue = FormValue[] | object | string | number | boolean | null | undefined;

export type FormValidatorFn = (
export type FormValidatorFn<T = Record<string, any>, S extends Form = Form> = (
value: FormValue,
options?: Record<string, any>
options: T & {
schema?: ResolvedFormSchema<S> | FormSchema<S>;
path: string;
}
) => boolean | Promise<boolean>;

export interface FormValidator {
Expand Down
264 changes: 262 additions & 2 deletions src/validation/schema/__tests__/validateSchema.spec.ts
Expand Up @@ -4,7 +4,8 @@ import {
validateForm,
validateFormArray,
validateFormField,
validateFormFieldArray
validateFormFieldArray,
validators
} from '@inkline/inkline/validation';
import type { FormValidator, ResolvedFormField, ResolvedFormSchema } from '@inkline/inkline';
import { defaultValidationFieldValues, defaultValidationStateValues } from '@inkline/inkline';
Expand All @@ -31,7 +32,7 @@ describe('validation', () => {
...defaultValidationFieldValues,
...defaultValidationStateValues,
value: 'value',
validators: [{ name: 'required' }]
validators: ['required']
};

const resolvedSchema = await validateFormField(schema);
Expand Down Expand Up @@ -136,6 +137,32 @@ describe('validation', () => {
}
]);
});

describe('rootSchema', () => {
it('should should provide rootSchema to validator', async () => {
const schema: ResolvedFormField<string> = {
...defaultValidationFieldValues,
...defaultValidationStateValues,
value: '',
validators: ['required']
};
const rootSchema: ResolvedFormSchema<{ field: string }> = {
...defaultValidationFieldValues,
...defaultValidationStateValues,
field: schema
};
const path = 'field';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateFormField(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.value, {
name: schema.validators[0],
path,
schema: rootSchema
});
});
});
});

describe('validateFormFieldArray()', () => {
Expand Down Expand Up @@ -166,6 +193,41 @@ describe('validation', () => {
expect(resolvedSchema[1].valid).toEqual(true);
expect(resolvedSchema[1].invalid).toEqual(false);
});

describe('rootSchema', () => {
it('should should pass rootSchema to validateFormFieldSchema', async () => {
const schema = createFormArraySchema<string>([
{
value: '',
validators: ['required']
},
{
value: 'value',
validators: ['required']
}
]);
const rootSchema: ResolvedFormSchema<{ array: string[] }> = {
...defaultValidationFieldValues,
...defaultValidationStateValues,
array: schema
};
const path = 'array';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateFormFieldArray(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema[0].value, {
name: schema[0].validators[0],
path: `${path}.0`,
schema: rootSchema
});
expect(requiredValidatorSpy).toHaveBeenCalledWith(schema[1].value, {
name: schema[1].validators[0],
path: `${path}.1`,
schema: rootSchema
});
});
});
});

describe('validateFormArray()', () => {
Expand Down Expand Up @@ -200,6 +262,46 @@ describe('validation', () => {
expect(resolvedSchema[1].valid).toEqual(true);
expect(resolvedSchema[1].invalid).toEqual(false);
});

describe('rootSchema', () => {
it('should should pass rootSchema to validateFormSchema', async () => {
const schema = createFormArraySchema<{ field: string }>([
{
field: {
value: '',
validators: ['required']
}
},
{
field: {
value: 'value',
validators: ['required']
}
}
]) as ResolvedFormSchema<{ field: string }>[];

const rootSchema: ResolvedFormSchema<{ array: Array<{ field: string }> }> = {
...defaultValidationFieldValues,
...defaultValidationStateValues,
array: schema
};
const path = 'field';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateFormArray(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema[0].field.value, {
name: schema[0].field.validators[0],
path: `${path}.0.field`,
schema: rootSchema
});
expect(requiredValidatorSpy).toHaveBeenCalledWith(schema[1].field.value, {
name: schema[1].field.validators[0],
path: `${path}.1.field`,
schema: rootSchema
});
});
});
});

describe('validateForm()', () => {
Expand Down Expand Up @@ -396,5 +498,163 @@ describe('validation', () => {
expect(resolvedSchema.array[1].valid).toEqual(false);
expect(resolvedSchema.array[1].field.valid).toEqual(false);
});

describe('rootSchema', () => {
it('should pass root schema to validateFormField', async () => {
const schema = createSchema<{
field: string;
}>({
field: {
value: 'value',
validators: ['required']
}
});
const rootSchema: ResolvedFormSchema<{
group: {
field: string;
};
}> = {
...defaultValidationStateValues,
group: schema
};

const path = 'group';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateForm(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.field.value, {
name: schema.field.validators[0],
path: `${path}.field`,
schema: rootSchema
});
});

it('should pass root schema to validateForm', async () => {
const schema = createSchema<{
nested: {
field: string;
};
}>({
nested: {
field: {
value: 'value',
validators: ['required']
}
}
});
const rootSchema: ResolvedFormSchema<{
group: {
nested: {
field: string;
};
};
}> = {
...defaultValidationStateValues,
group: schema
};

const path = 'group';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateForm(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.nested.field.value, {
name: schema.nested.field.validators[0],
path: `${path}.nested.field`,
schema: rootSchema
});
});

it('should pass root schema to validateFormFieldArray', async () => {
const schema = createSchema<{
array: string[];
}>({
array: [
{
value: 'value',
validators: ['required']
},
{
value: '',
validators: ['required']
}
]
});
const rootSchema: ResolvedFormSchema<{
group: {
array: string[];
};
}> = {
...defaultValidationStateValues,
group: schema
};

const path = 'group';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateForm(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.array[0].value, {
name: schema.array[0].validators[0],
path: `${path}.array.0`,
schema: rootSchema
});
expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.array[1].value, {
name: schema.array[1].validators[0],
path: `${path}.array.1`,
schema: rootSchema
});
});

it('should pass root schema to validateFormArray', async () => {
const schema = createSchema<{
array: Array<{
field: string;
}>;
}>({
array: [
{
field: {
value: 'value',
validators: ['required']
}
},
{
field: {
value: '',
validators: ['required']
}
}
]
});
const rootSchema: ResolvedFormSchema<{
group: {
array: Array<{
field: string;
}>;
};
}> = {
...defaultValidationStateValues,
group: schema
};

const path = 'group';

const requiredValidatorSpy = vi.spyOn(validators, 'required');
await validateForm(schema, path, rootSchema);

expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.array[0].field.value, {
name: schema.array[0].field.validators[0],
path: `${path}.array.0.field`,
schema: rootSchema
});
expect(requiredValidatorSpy).toHaveBeenCalledWith(schema.array[1].field.value, {
name: schema.array[1].field.validators[0],
path: `${path}.array.1.field`,
schema: rootSchema
});
});
});
});
});

0 comments on commit 29f0621

Please sign in to comment.