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

Support Yup ^0.32.0 #92

Merged
merged 10 commits into from
Dec 21, 2020
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
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
tsconfig: 'tsconfig.jest.json',
},
},
restoreMocks: true,
testMatch: ['**/?(*.)+(spec|test).ts?(x)'],
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$'],
moduleFileExtensions: ['ts', 'tsx', 'js'],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@
"rollup-plugin-typescript2": "^0.29.0",
"superstruct": "^0.13.1",
"ts-jest": "^26.4.4",
"yup": "^0.32.8",
"typescript": "^4.1.3",
"vest": "^2.2.3",
"yup": "^0.31.0",
"zod": "^1.11.11"
},
"peerDependencies": {
Expand Down
12 changes: 12 additions & 0 deletions src/__snapshots__/yup.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,15 @@ Object {
"values": Object {},
}
`;

exports[`yupResolver should pass down the yup context 1`] = `
Object {
"errors": Object {
"name": Object {
"message": "name must be at least 6 characters",
"type": "min",
},
},
"values": Object {},
}
`;
151 changes: 80 additions & 71 deletions src/yup.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable no-console */
/* eslint-disable no-console, @typescript-eslint/ban-ts-comment */
import * as yup from 'yup';
import { yupResolver } from './yup';

Expand Down Expand Up @@ -42,7 +42,7 @@ const errors = {
],
};

const schema = yup.object().shape({
const schema = yup.object({
Copy link
Member

@bluebill1049 bluebill1049 Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, shape is gone. 👍

name: yup.string().required(),
age: yup.number().required().positive().integer(),
email: yup.string().email(),
Expand Down Expand Up @@ -72,42 +72,43 @@ describe('yupResolver', () => {
it('should get values', async () => {
const data = {
name: 'jimmy',
age: '24',
age: 24,
email: 'jimmy@mail.com',
password: '[}tehk6Uor',
createdOn: '2014-09-23T19:25:25Z',
foo: [{ yup: true }],
website: 'https://react-hook-form.com/',
createdOn: new Date('2014-09-23T19:25:25Z'),
foo: [{ loose: true }],
};
expect(await yupResolver(schema)(data)).toEqual({
errors: {},
values: {
name: 'jimmy',
age: 24,
password: '[}tehk6Uor',
foo: [{ yup: true }],
createdOn: new Date('2014-09-23T19:25:25Z'),
},
values: data,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

});
});

it('should pass down the yup context', async () => {
const data = { name: 'eric' };
const context = { min: true };
const schemaWithContext = yup.object().shape({
const schemaWithContext = yup.object({
name: yup
.string()
.required()
.when('$min', (min: boolean, schema: yup.StringSchema) => {
return min ? schema.min(6) : schema;
}),
});
schemaWithContext.validate = jest.fn().mockResolvedValue({});
await yupResolver(schemaWithContext)(data, context);
expect(schemaWithContext.validate).toHaveBeenCalled();
expect(schemaWithContext.validate).toHaveBeenCalledWith(data, {
abortEarly: false,
context,
});
(schemaWithContext.validate as jest.Mock).mockClear();

const schemaSpyValidate = jest.spyOn(schemaWithContext, 'validate');

const output = await yupResolver(schemaWithContext)(data, context);
expect(schemaSpyValidate).toHaveBeenCalledTimes(1);
expect(schemaSpyValidate).toHaveBeenCalledWith(
data,
expect.objectContaining({
abortEarly: false,
context,
}),
);
expect(output).toMatchSnapshot();
});

describe('errors', () => {
Expand All @@ -119,26 +120,33 @@ describe('yupResolver', () => {
createdOn: null,
foo: [{ loose: null }],
};
const resolve = await yupResolver(schema)(data, {}, true);
expect(resolve).toMatchSnapshot();
expect(resolve.errors['foo'][0]['loose']).toBeDefined();
expect(resolve.errors['foo'][0]['loose'].types).toMatchInlineSnapshot(`

const output = await yupResolver(schema)(
// @ts-expect-error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor (don't have to fix it): Is this due to reuse the same schema? maybe worth to use a new schema instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is normal because yupResolver have a better type support now :)

The schema is good but passed data are intentionally false, that's why TS throw an error.

data,
{},
true,
);
expect(output).toMatchSnapshot();
expect(output.errors['foo']?.[0]?.['loose']).toBeDefined();
expect(output.errors['foo']?.[0]?.['loose']?.types)
.toMatchInlineSnapshot(`
Object {
"typeError": "foo[0].loose must be a \`boolean\` type, but the final value was: \`null\`.
If \\"null\\" is intended as an empty value be sure to mark the schema as \`.nullable()\`",
}
`);
expect(resolve.errors.age.types).toMatchInlineSnapshot(`
expect(output.errors.age?.types).toMatchInlineSnapshot(`
Object {
"typeError": "age must be a \`number\` type, but the final value was: \`NaN\` (cast from the value \`\\"test\\"\`).",
}
`);
expect(resolve.errors.createdOn.types).toMatchInlineSnapshot(`
expect(output.errors.createdOn?.types).toMatchInlineSnapshot(`
Object {
"typeError": "createdOn must be a \`date\` type, but the final value was: \`Invalid Date\`.",
}
`);
expect(resolve.errors.password.types).toMatchInlineSnapshot(`
expect(output.errors.password?.types).toMatchInlineSnapshot(`
Object {
"matches": Array [
"Lowercase",
Expand All @@ -159,12 +167,13 @@ describe('yupResolver', () => {
createdOn: null,
foo: [{ loose: null }],
};
const resolve = await yupResolver(schema)(data);
expect(await yupResolver(schema)(data)).toMatchSnapshot();
expect(resolve.errors['foo[0].loose']).toBeUndefined();
expect(resolve.errors.age.types).toBeUndefined();
expect(resolve.errors.createdOn.types).toBeUndefined();
expect(resolve.errors.password.types).toBeUndefined();

// @ts-expect-error
const output = await yupResolver(schema)(data);
expect(output).toMatchSnapshot();
expect(output.errors.age?.types).toBeUndefined();
expect(output.errors.createdOn?.types).toBeUndefined();
expect(output.errors.password?.types).toBeUndefined();
});

it('should get error if yup errors has no inner errors', async () => {
Expand All @@ -174,10 +183,12 @@ describe('yupResolver', () => {
createdOn: null,
foo: [{ loose: null }],
};
const resolve = await yupResolver(schema, {
const output = await yupResolver(schema, {
abortEarly: true,
})(data);
expect(resolve.errors).toMatchInlineSnapshot(`
// @ts-expect-error
})(data, undefined, true);

expect(output.errors).toMatchInlineSnapshot(`
Object {
"createdOn": Object {
"message": "createdOn must be a \`date\` type, but the final value was: \`Invalid Date\`.",
Expand All @@ -192,35 +203,35 @@ describe('yupResolver', () => {
const schemaWithContext = yup.object().shape({
name: yup.string().required(),
});
schemaWithContext.validate = jest.fn().mockRejectedValue({

jest.spyOn(schemaWithContext, 'validate').mockRejectedValueOnce({
inner: [{ path: '', message: 'error1', type: 'required' }],
} as yup.ValidationError);
const result = await yupResolver(schemaWithContext)(data);
expect(result).toMatchSnapshot();
});

// @ts-expect-error
const output = await yupResolver(schemaWithContext)(data);
expect(output).toMatchSnapshot();
});
});
});

describe('validateWithSchema', () => {
it('should return undefined when no error reported', async () => {
expect(
await yupResolver({
validate: () => {
throw errors;
},
} as any)({}),
).toMatchSnapshot();
const schema = yup.object();
jest.spyOn(schema, 'validate').mockRejectedValueOnce(errors);

expect(await yupResolver(schema)({})).toMatchSnapshot();
});

it('should return empty object when validate pass', async () => {
expect(
await yupResolver({
validate: () => new Promise((resolve) => resolve(undefined)),
} as any)({}),
).toEqual({
errors: {},
values: undefined,
});
const schema = yup.object();

expect(await yupResolver(schema)({})).toMatchInlineSnapshot(`
Object {
"errors": Object {},
"values": Object {},
}
`);
});

it('should return an error based on the user context', async () => {
Expand All @@ -233,6 +244,8 @@ describe('validateWithSchema', () => {
return min ? schema.min(6) : schema;
}),
});

// @ts-expect-error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assume this is the same issue above?

expect(await yupResolver(schemaWithContext)(data, { min: true }))
.toMatchInlineSnapshot(`
Object {
Expand All @@ -248,29 +261,23 @@ describe('validateWithSchema', () => {
});

it('should show a warning log if yup context is used instead only on dev environment', async () => {
console.warn = jest.fn();
jest.spyOn(console, 'warn').mockImplementation(jest.fn);
process.env.NODE_ENV = 'development';
await yupResolver(
{} as any,
{ context: { noContext: true } } as yup.ValidateOptions,
)({});

await yupResolver(yup.object(), { context: { noContext: true } })({});
expect(console.warn).toHaveBeenCalledWith(
"You should not used the yup options context. Please, use the 'useForm' context object instead",
);
process.env.NODE_ENV = 'test';
(console.warn as jest.Mock).mockClear();
});

it('should not show warning log if yup context is used instead only on production environment', async () => {
console.warn = jest.fn();
jest.spyOn(console, 'warn').mockImplementation(jest.fn);
process.env.NODE_ENV = 'production';
await yupResolver(
{} as any,
{ context: { noContext: true } } as yup.ValidateOptions,
)({});

await yupResolver(yup.object(), { context: { noContext: true } })({});
expect(console.warn).not.toHaveBeenCalled();
process.env.NODE_ENV = 'test';
(console.warn as jest.Mock).mockClear();
});

it('should return correct error message with using yup.test', async () => {
Expand All @@ -280,9 +287,11 @@ describe('validateWithSchema', () => {
name: yup.string(),
email: yup.string(),
})
.test('name', 'Email or name are required', function (value) {
return value && (value.name || value.email);
}),
.test(
'name',
'Email or name are required',
(value) => !!(value && (value.name || value.email)),
),
)({ name: '', email: '' });

expect(output).toEqual({
Expand Down
Loading