Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(native-validation): clear error #183

Merged
merged 1 commit into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions class-validator/src/__tests__/Form-native-validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,19 @@ test("form's native validation with Class Validator", async () => {
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(false);
expect(passwordField.validationMessage).toBe('password should not be empty');

await act(async () => {
user.type(screen.getByPlaceholderText(/username/i), 'joe');
user.type(screen.getByPlaceholderText(/password/i), 'password');
});

// username
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
expect(usernameField.validity.valid).toBe(true);
expect(usernameField.validationMessage).toBe('');

// password
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(true);
expect(passwordField.validationMessage).toBe('');
});
30 changes: 17 additions & 13 deletions class-validator/src/class-validator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FieldErrors } from 'react-hook-form';
import { toNestError } from '@hookform/resolvers';
import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
import { plainToClass } from 'class-transformer';
import { validate, validateSync, ValidationError } from 'class-validator';
import type { Resolver } from './types';
Expand Down Expand Up @@ -42,17 +42,21 @@ export const classValidatorResolver: Resolver =
? validateSync
: validate)(user, schemaOptions);

return rawErrors.length
? {
values: {},
errors: toNestError(
parseErrors(
rawErrors,
!options.shouldUseNativeValidation &&
options.criteriaMode === 'all',
),
options,
if (rawErrors.length) {
return {
values: {},
errors: toNestError(
parseErrors(
rawErrors,
!options.shouldUseNativeValidation &&
options.criteriaMode === 'all',
),
}
: { values, errors: {} };
options,
),
};
}

options.shouldUseNativeValidation && validateFieldsNatively({}, options);

return { values, errors: {} };
};
15 changes: 15 additions & 0 deletions computed-types/src/__tests__/Form-native-validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,19 @@ test("form's native validation with computed-types", async () => {
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(false);
expect(passwordField.validationMessage).toBe(PASSWORD_REQUIRED_MESSAGE);

await act(async () => {
user.type(screen.getByPlaceholderText(/username/i), 'joe');
user.type(screen.getByPlaceholderText(/password/i), 'password');
});

// username
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
expect(usernameField.validity.valid).toBe(true);
expect(usernameField.validationMessage).toBe('');

// password
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(true);
expect(passwordField.validationMessage).toBe('');
});
8 changes: 6 additions & 2 deletions computed-types/src/computed-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FieldErrors } from 'react-hook-form';
import { toNestError } from '@hookform/resolvers';
import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
import type { ValidationError } from 'computed-types';
import type { Resolver } from './types';

Expand All @@ -26,9 +26,13 @@ const parseErrorSchema = (
export const computedTypesResolver: Resolver =
(schema) => async (values, _, options) => {
try {
const data = await schema(values);

options.shouldUseNativeValidation && validateFieldsNatively({}, options);

return {
errors: {},
values: await schema(values),
values: data,
};
} catch (error) {
return {
Expand Down
15 changes: 15 additions & 0 deletions io-ts/src/__tests__/Form-native-validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,19 @@ test("form's native validation with io-ts", async () => {
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(false);
expect(passwordField.validationMessage).toBe(PASSWORD_REQUIRED_MESSAGE);

await act(async () => {
user.type(screen.getByPlaceholderText(/username/i), 'joe');
user.type(screen.getByPlaceholderText(/password/i), 'password');
});

// username
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
expect(usernameField.validity.valid).toBe(true);
expect(usernameField.validationMessage).toBe('');

// password
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(true);
expect(passwordField.validationMessage).toBe('');
});
15 changes: 10 additions & 5 deletions io-ts/src/io-ts.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Either from 'fp-ts/lib/Either';
import { pipe } from 'fp-ts/function';
import { toNestError } from '@hookform/resolvers';
import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
import errorsToRecord from './errorsToRecord';
import { Resolver } from './types';

Expand All @@ -19,9 +19,14 @@ export const ioTsResolver: Resolver = (codec) => (values, _context, options) =>
values: {},
errors,
}),
(values) => ({
values,
errors: {},
}),
(values) => {
options.shouldUseNativeValidation &&
validateFieldsNatively({}, options);

return {
values,
errors: {},
};
},
),
);
15 changes: 15 additions & 0 deletions joi/src/__tests__/Form-native-validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,19 @@ test("form's native validation with Joi", async () => {
expect(passwordField.validationMessage).toBe(
'"password" is not allowed to be empty',
);

await act(async () => {
user.type(screen.getByPlaceholderText(/username/i), 'joe');
user.type(screen.getByPlaceholderText(/password/i), 'password');
});

// username
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
expect(usernameField.validity.valid).toBe(true);
expect(usernameField.validationMessage).toBe('');

// password
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(true);
expect(passwordField.validationMessage).toBe('');
});
31 changes: 19 additions & 12 deletions joi/src/joi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { appendErrors, FieldError } from 'react-hook-form';
import { toNestError } from '@hookform/resolvers';
import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
import type { ValidationError } from 'joi';
import { Resolver } from './types';

Expand Down Expand Up @@ -58,17 +58,24 @@ export const joiResolver: Resolver =
}
}

if (result.error) {
return {
values: {},
errors: toNestError(
parseErrorSchema(
result.error,
!options.shouldUseNativeValidation &&
options.criteriaMode === 'all',
),
options,
),
};
}

options.shouldUseNativeValidation && validateFieldsNatively({}, options);

return {
values: result.error ? {} : result.value,
errors: result.error
? toNestError(
parseErrorSchema(
result.error,
!options.shouldUseNativeValidation &&
options.criteriaMode === 'all',
),
options,
)
: {},
errors: {},
values: result.value,
};
};
15 changes: 15 additions & 0 deletions nope/src/__tests__/Form-native-validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,19 @@ test("form's native validation with Nope", async () => {
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(false);
expect(passwordField.validationMessage).toBe(PASSWORD_REQUIRED_MESSAGE);

await act(async () => {
user.type(screen.getByPlaceholderText(/username/i), 'joe');
user.type(screen.getByPlaceholderText(/password/i), 'password');
});

// username
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
expect(usernameField.validity.valid).toBe(true);
expect(usernameField.validationMessage).toBe('');

// password
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
expect(passwordField.validity.valid).toBe(true);
expect(passwordField.validationMessage).toBe('');
});
12 changes: 8 additions & 4 deletions nope/src/nope.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FieldErrors } from 'react-hook-form';
import { toNestError } from '@hookform/resolvers';
import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
import type { ShapeErrors } from 'nope-validator/lib/cjs/types';
import type { Resolver } from './types';

Expand Down Expand Up @@ -36,7 +36,11 @@ export const nopeResolver: Resolver =
| ShapeErrors
| undefined;

return result
? { values: {}, errors: toNestError(parseErrors(result), options) }
: { values, errors: {} };
if (result) {
return { values: {}, errors: toNestError(parseErrors(result), options) };
}

options.shouldUseNativeValidation && validateFieldsNatively({}, options);

return { values, errors: {} };
};
42 changes: 0 additions & 42 deletions src/__tests__/__snapshots__/toNestObject.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@

exports[`transforms flat object to nested object 1`] = `
Object {
"n": Object {
"test": Object {
"message": "third message",
"ref": Object {
"reportValidity": [MockFunction],
"setCustomValidity": [MockFunction],
},
"type": "rd",
},
},
"name": Object {
"message": "first message",
"ref": Object {
Expand All @@ -34,38 +24,6 @@ Object {

exports[`transforms flat object to nested object and shouldUseNativeValidation: true 1`] = `
Object {
"n": Object {
"test": Object {
"message": "third message",
"ref": Object {
"reportValidity": [MockFunction] {
"calls": Array [
Array [],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
},
"setCustomValidity": [MockFunction] {
"calls": Array [
Array [
"third message",
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
},
},
"type": "rd",
},
},
"name": Object {
"message": "first message",
"ref": Object {
Expand Down
19 changes: 0 additions & 19 deletions src/__tests__/toNestObject.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Field, FieldError, InternalFieldName } from 'react-hook-form';
import { toNestError } from '../toNestError';

const flatObject: Record<string, FieldError> = {
name: { type: 'st', message: 'first message' },
'test.0.name': { type: 'nd', message: 'second message' },
'n.test': { type: 'rd', message: 'third message' },
};

const fields = {
Expand All @@ -15,14 +13,6 @@ const fields = {
setCustomValidity: jest.fn(),
},
},
n: {
test: {
ref: {
reportValidity: jest.fn(),
setCustomValidity: jest.fn(),
},
},
},
unused: {
ref: { name: 'unusedRef' },
},
Expand All @@ -47,13 +37,4 @@ test('transforms flat object to nested object and shouldUseNativeValidation: tru
expect(
(fields.name.ref as HTMLInputElement).setCustomValidity,
).toHaveBeenCalledWith(flatObject.name.message);

// @ts-expect-error
expect(fields.n?.test.ref.reportValidity).toHaveBeenCalledTimes(1);
// @ts-expect-error
expect(fields.n.test.ref.setCustomValidity).toHaveBeenCalledTimes(1);
// @ts-expect-error
expect(fields.n.test.ref.setCustomValidity).toHaveBeenCalledWith(
flatObject['n.test'].message,
);
});
42 changes: 42 additions & 0 deletions src/__tests__/validateFieldsNatively.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Field, FieldError, InternalFieldName } from 'react-hook-form';
import { validateFieldsNatively } from '../validateFieldsNatively';

const flatObject: Record<string, FieldError> = {
name: { type: 'st', message: 'first message' },
};

const fields = {
name: {
ref: {
reportValidity: jest.fn(),
setCustomValidity: jest.fn(),
},
},
nd: {
jorisre marked this conversation as resolved.
Show resolved Hide resolved
ref: {
reportValidity: jest.fn(),
setCustomValidity: jest.fn(),
},
},
} as any as Record<InternalFieldName, Field['_f']>;

test('validates natively fields', () => {
validateFieldsNatively(flatObject, {
fields,
shouldUseNativeValidation: true,
});

expect(
(fields.name.ref as HTMLInputElement).setCustomValidity,
).toHaveBeenCalledWith(flatObject.name.message);
expect(
(fields.name.ref as HTMLInputElement).reportValidity,
).toHaveBeenCalledTimes(1);

expect(
(fields.nd.ref as HTMLInputElement).setCustomValidity,
).toHaveBeenCalledWith('');
expect(
(fields.nd.ref as HTMLInputElement).reportValidity,
).toHaveBeenCalledTimes(1);
});
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './toNestError';
export * from './validateFieldsNatively';
Loading