This commit is contained in:
158
tests/hooks/useValidatedFields.test.tsx
Normal file
158
tests/hooks/useValidatedFields.test.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import { act } from 'react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { useValidatedFields } from '../../src/hooks/useValidatedFields';
|
||||
import { renderHook } from '../helpers/renderHook';
|
||||
|
||||
type FormValues = {
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
};
|
||||
|
||||
function validate(values: FormValues) {
|
||||
return {
|
||||
password: values.password.length < 3 ? 'Password too short' : undefined,
|
||||
confirmPassword:
|
||||
values.confirmPassword !== values.password ? 'Passwords do not match' : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
describe('useValidatedFields', () => {
|
||||
it('initializes values and keeps errors hidden until fields are touched', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: '', confirmPassword: '' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.values).toEqual({ password: '', confirmPassword: '' });
|
||||
expect(result.current.errors).toEqual({});
|
||||
expect(result.current.isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('setFieldValue touches and validates by default', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: '', confirmPassword: '' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setFieldValue('password', 'ab');
|
||||
});
|
||||
|
||||
expect(result.current.values.password).toBe('ab');
|
||||
expect(result.current.errors.password).toBe('Password too short');
|
||||
});
|
||||
|
||||
it('setFieldValue can skip touch and validation', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: '', confirmPassword: '' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setFieldValue('password', 'ab', { touch: false, validate: false });
|
||||
});
|
||||
|
||||
expect(result.current.values.password).toBe('ab');
|
||||
expect(result.current.errors).toEqual({});
|
||||
});
|
||||
|
||||
it('validateAll can avoid touching fields when requested', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: 'abcd', confirmPassword: 'abce' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
let validationErrors: ReturnType<typeof validate> | undefined;
|
||||
act(() => {
|
||||
validationErrors = result.current.validateAll({ touchAll: false });
|
||||
});
|
||||
|
||||
expect(validationErrors).toEqual({
|
||||
password: undefined,
|
||||
confirmPassword: 'Passwords do not match',
|
||||
});
|
||||
expect(result.current.errors).toEqual({});
|
||||
});
|
||||
|
||||
it('validateAll touches all fields by default', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: '', confirmPassword: '' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.validateAll();
|
||||
});
|
||||
|
||||
expect(result.current.errors.password).toBe('Password too short');
|
||||
expect(result.current.errors.confirmPassword).toBeUndefined();
|
||||
});
|
||||
|
||||
it('supports setFieldError, setErrors and clearErrors', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: 'abcd', confirmPassword: 'abcd' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setFieldError('password', 'Server-side password issue');
|
||||
});
|
||||
expect(result.current.errors.password).toBe('Server-side password issue');
|
||||
|
||||
act(() => {
|
||||
result.current.setErrors({
|
||||
password: undefined,
|
||||
confirmPassword: 'Still invalid',
|
||||
});
|
||||
});
|
||||
expect(result.current.errors.confirmPassword).toBe('Still invalid');
|
||||
|
||||
act(() => {
|
||||
result.current.clearErrors();
|
||||
});
|
||||
expect(result.current.errors).toEqual({});
|
||||
});
|
||||
|
||||
it('setValues with clearErrors resets touched state and revalidates', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useValidatedFields({
|
||||
initialValues: { password: '', confirmPassword: '' },
|
||||
validate,
|
||||
}),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setFieldValue('password', 'ab');
|
||||
});
|
||||
expect(result.current.errors.password).toBe('Password too short');
|
||||
|
||||
act(() => {
|
||||
result.current.setValues(
|
||||
{
|
||||
password: 'abcd',
|
||||
confirmPassword: 'abcd',
|
||||
},
|
||||
{ clearErrors: true },
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.values).toEqual({
|
||||
password: 'abcd',
|
||||
confirmPassword: 'abcd',
|
||||
});
|
||||
expect(result.current.errors).toEqual({});
|
||||
expect(result.current.isValid).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user