From db06a89edf290c50a80ff1fa3f49dcc69cccae72 Mon Sep 17 00:00:00 2001 From: Vitaly Rtishchev Date: Sat, 6 Apr 2024 16:46:08 +0400 Subject: [PATCH] [@mantine/form] Add first part of uncontrolled form tests --- .../form/src/stories/Form.lists.story.tsx | 67 ++++++ .../form/src/tests/clearErrors.test.ts | 13 +- .../form/src/tests/clearFieldError.test.ts | 13 +- .../@mantine/form/src/tests/dirty.test.ts | 19 +- .../src/tests/enhanceGetInputProps.test.ts | 192 ++++++++---------- .../@mantine/form/src/tests/errors.test.ts | 19 +- .../form/src/tests/getInputProps.test.ts | 105 ++++++---- .../src/tests/getTransformedValues.test.ts | 15 +- .../form/src/tests/initialize.test.ts | 19 +- .../form/src/tests/insertListItem.test.ts | 31 ++- .../@mantine/form/src/tests/isValid.test.ts | 13 +- .../@mantine/form/src/tests/onReset.test.ts | 16 +- .../@mantine/form/src/tests/onSubmit.test.ts | 16 +- .../form/src/tests/removeListItem.test.ts | 24 ++- .../form/src/tests/reorderListItem.test.ts | 19 +- 15 files changed, 388 insertions(+), 193 deletions(-) diff --git a/packages/@mantine/form/src/stories/Form.lists.story.tsx b/packages/@mantine/form/src/stories/Form.lists.story.tsx index c7c255febf9..43115d2528b 100644 --- a/packages/@mantine/form/src/stories/Form.lists.story.tsx +++ b/packages/@mantine/form/src/stories/Form.lists.story.tsx @@ -71,3 +71,70 @@ export function Lists() { ); } + +export function ListsUncontrolled() { + const form = useForm({ + mode: 'uncontrolled', + initialValues: { + employees: [ + { name: '', active: false, key: randomId() }, + { name: '', active: false, key: randomId() }, + { name: '', active: false, key: randomId() }, + ], + }, + + validate: { + employees: { + name: (value) => (value.length < 2 ? 'Too short' : null), + }, + }, + }); + + const fields = form.values.employees.map((item, index) => ( + + + + form.removeListItem('employees', index)}> + $ + + + )); + + return ( + + {fields.length > 0 ? ( + + + Name + + + Status + + + ) : ( + + No one here... + + )} + + {fields} + + + + + + ); +} diff --git a/packages/@mantine/form/src/tests/clearErrors.test.ts b/packages/@mantine/form/src/tests/clearErrors.test.ts index 6a9b8d8238f..14fc1a522a9 100644 --- a/packages/@mantine/form/src/tests/clearErrors.test.ts +++ b/packages/@mantine/form/src/tests/clearErrors.test.ts @@ -1,11 +1,20 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/clearErrors', () => { +function tests(mode: FormMode) { it('clears errors when clearErrors handler is called', () => { - const hook = renderHook(() => useForm({ initialErrors: { a: 1, b: 2 } })); + const hook = renderHook(() => useForm({ mode, initialErrors: { a: 1, b: 2 } })); expect(hook.result.current.errors).toStrictEqual({ a: 1, b: 2 }); act(() => hook.result.current.clearErrors()); expect(hook.result.current.errors).toStrictEqual({}); }); +} + +describe('@mantine/form/clearErrors-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/clearErrors-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/clearFieldError.test.ts b/packages/@mantine/form/src/tests/clearFieldError.test.ts index be68759c9cf..276ed2bcde6 100644 --- a/packages/@mantine/form/src/tests/clearFieldError.test.ts +++ b/packages/@mantine/form/src/tests/clearFieldError.test.ts @@ -1,9 +1,10 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/clearFieldError', () => { +function tests(mode: FormMode) { it('clears error of given field with clearFieldError handler', () => { - const hook = renderHook(() => useForm({ initialErrors: { a: 1, b: 2 } })); + const hook = renderHook(() => useForm({ mode, initialErrors: { a: 1, b: 2 } })); expect(hook.result.current.errors).toStrictEqual({ a: 1, b: 2 }); act(() => hook.result.current.clearFieldError('a')); @@ -12,4 +13,12 @@ describe('@mantine/form/clearFieldError', () => { act(() => hook.result.current.clearFieldError('b')); expect(hook.result.current.errors).toStrictEqual({}); }); +} + +describe('@mantine/form/clearFieldError-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/clearFieldError-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/dirty.test.ts b/packages/@mantine/form/src/tests/dirty.test.ts index aa349887258..91203fab3ed 100644 --- a/packages/@mantine/form/src/tests/dirty.test.ts +++ b/packages/@mantine/form/src/tests/dirty.test.ts @@ -1,16 +1,17 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/dirty', () => { +function tests(mode: FormMode) { it('accepts initial dirty state', () => { - const hook = renderHook(() => useForm({ initialDirty: { a: true, b: false } })); + const hook = renderHook(() => useForm({ mode, initialDirty: { a: true, b: false } })); expect(hook.result.current.isDirty('a')).toBe(true); expect(hook.result.current.isDirty('b')).toBe(false); expect(hook.result.current.isDirty()).toBe(true); }); it('sets field as dirty if value changes', () => { - const hook = renderHook(() => useForm({ initialValues: { a: 1 } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: 1 } })); expect(hook.result.current.isDirty('a')).toBe(false); expect(hook.result.current.isDirty()).toBe(false); @@ -34,7 +35,7 @@ describe('@mantine/form/dirty', () => { }); it('resets status with resetDirty handler', () => { - const hook = renderHook(() => useForm({ initialDirty: { a: true } })); + const hook = renderHook(() => useForm({ mode, initialDirty: { a: true } })); expect(hook.result.current.isDirty()).toBe(true); act(() => hook.result.current.resetDirty()); @@ -42,7 +43,7 @@ describe('@mantine/form/dirty', () => { }); it('sets list field as dirty if list item changes', () => { - const hook = renderHook(() => useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }] } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }] } })); act(() => hook.result.current.setFieldValue('a.0', 3)); expect(hook.result.current.isDirty('a.0')).toBe(true); expect(hook.result.current.isDirty('a')).toBe(true); @@ -59,4 +60,12 @@ describe('@mantine/form/dirty', () => { expect(hook.result.current.isDirty('a.2')).toBe(false); expect(hook.result.current.isDirty('a')).toBe(false); }); +} + +describe('@mantine/form/dirty-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/dirty-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/enhanceGetInputProps.test.ts b/packages/@mantine/form/src/tests/enhanceGetInputProps.test.ts index f953eedcbf3..0a1eb1d31fd 100644 --- a/packages/@mantine/form/src/tests/enhanceGetInputProps.test.ts +++ b/packages/@mantine/form/src/tests/enhanceGetInputProps.test.ts @@ -1,14 +1,34 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/enhanceGetInputProps', () => { +function getInputProps(mode: FormMode, input: Record) { + const { value, error, ...others } = input; + const result = { + ...others, + [mode === 'controlled' ? 'value' : 'defaultValue']: value, + error, + onBlur: expect.any(Function), + onChange: expect.any(Function), + onFocus: expect.any(Function), + }; + + if (mode === 'uncontrolled') { + result.key = expect.any(String); + } + + return result; +} + +function tests(mode: FormMode) { it('allows overriding getInputProps properties', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { fruit: 'banana', vegetable: 'carrot' }, enhanceGetInputProps: ({ field }) => { if (field === 'fruit') { - return { value: 'apple' }; + return { test: 'apple' }; } return {}; @@ -16,53 +36,37 @@ describe('@mantine/form/enhanceGetInputProps', () => { }) ); - expect(hook.result.current.getInputProps('fruit')).toStrictEqual({ - value: 'apple', - error: undefined, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - expect(hook.result.current.getInputProps('vegetable')).toStrictEqual({ - value: 'carrot', - error: undefined, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'banana', test: 'apple' }) + ); + + expect(hook.result.current.getInputProps('vegetable')).toStrictEqual( + getInputProps(mode, { value: 'carrot' }) + ); }); it('allows adding new properties to getInputProps', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { fruit: 'banana', vegetable: 'carrot' }, enhanceGetInputProps: () => ({ readOnly: true }), }) ); - expect(hook.result.current.getInputProps('fruit')).toStrictEqual({ - value: 'banana', - error: undefined, - readOnly: true, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - expect(hook.result.current.getInputProps('vegetable')).toStrictEqual({ - value: 'carrot', - error: undefined, - readOnly: true, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'banana', readOnly: true }) + ); + + expect(hook.result.current.getInputProps('vegetable')).toStrictEqual( + getInputProps(mode, { value: 'carrot', readOnly: true }) + ); }); it('allows referencing form object in enhanceGetInputProps', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { fruit: 'banana', vegetable: 'carrot' }, enhanceGetInputProps: ({ form }) => ({ readOnly: !form.initialized, @@ -70,53 +74,29 @@ describe('@mantine/form/enhanceGetInputProps', () => { }) ); - expect(hook.result.current.getInputProps('fruit')).toStrictEqual({ - value: 'banana', - error: undefined, - readOnly: true, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - expect(hook.result.current.getInputProps('vegetable')).toStrictEqual({ - value: 'carrot', - error: undefined, - readOnly: true, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - act(() => { - hook.result.current.initialize({ - fruit: 'apple', - vegetable: 'carrot', - }); - }); - - expect(hook.result.current.getInputProps('fruit')).toStrictEqual({ - value: 'apple', - error: undefined, - readOnly: false, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - expect(hook.result.current.getInputProps('vegetable')).toStrictEqual({ - value: 'carrot', - error: undefined, - readOnly: false, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'banana', readOnly: true }) + ); + + expect(hook.result.current.getInputProps('vegetable')).toStrictEqual( + getInputProps(mode, { value: 'carrot', readOnly: true }) + ); + + act(() => hook.result.current.initialize({ fruit: 'apple', vegetable: 'carrot' })); + + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'apple', readOnly: false }) + ); + + expect(hook.result.current.getInputProps('vegetable')).toStrictEqual( + getInputProps(mode, { value: 'carrot', readOnly: false }) + ); }); it('allows referencing getInputProps options in enhanceGetInputProps', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { fruit: 'banana', vegetable: 'carrot' }, enhanceGetInputProps: ({ options }) => ({ readOnly: options.readOnly, @@ -124,51 +104,41 @@ describe('@mantine/form/enhanceGetInputProps', () => { }) ); - expect(hook.result.current.getInputProps('fruit')).toStrictEqual({ - value: 'banana', - error: undefined, - readOnly: undefined, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - expect(hook.result.current.getInputProps('vegetable', { readOnly: true })).toStrictEqual({ - value: 'carrot', - error: undefined, - readOnly: true, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'banana', readOnly: undefined }) + ); + + expect(hook.result.current.getInputProps('vegetable', { readOnly: true })).toStrictEqual( + getInputProps(mode, { value: 'carrot', readOnly: true }) + ); }); it('allows referencing inputProps in enhanceGetInputProps', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { fruit: 'banana', vegetable: 'carrot' }, enhanceGetInputProps: ({ inputProps }) => ({ - readOnly: inputProps.value === 'banana', + readOnly: + (inputProps as any)[mode === 'controlled' ? 'value' : 'defaultValue'] === 'banana', }), }) ); - expect(hook.result.current.getInputProps('fruit')).toStrictEqual({ - value: 'banana', - error: undefined, - readOnly: true, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); - - expect(hook.result.current.getInputProps('vegetable', { readOnly: true })).toStrictEqual({ - value: 'carrot', - error: undefined, - readOnly: false, - onBlur: expect.any(Function), - onChange: expect.any(Function), - onFocus: expect.any(Function), - }); + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'banana', readOnly: true }) + ); + + expect(hook.result.current.getInputProps('vegetable', { readOnly: true })).toStrictEqual( + getInputProps(mode, { value: 'carrot', readOnly: false }) + ); }); +} + +describe('@mantine/form/enhanceGetInputProps-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/enhanceGetInputProps-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/errors.test.ts b/packages/@mantine/form/src/tests/errors.test.ts index de817bccdf8..318cce665db 100644 --- a/packages/@mantine/form/src/tests/errors.test.ts +++ b/packages/@mantine/form/src/tests/errors.test.ts @@ -1,27 +1,36 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/errors', () => { +function tests(mode: FormMode) { it('initializes form with given initialErrors', () => { - const hook = renderHook(() => useForm({ initialErrors: { a: true, b: true } })); + const hook = renderHook(() => useForm({ mode, initialErrors: { a: true, b: true } })); expect(hook.result.current.errors).toStrictEqual({ a: true, b: true }); }); it('sets error to empty object if initialErrors value is not provided', () => { - const hook = renderHook(() => useForm()); + const hook = renderHook(() => useForm({ mode })); expect(hook.result.current.errors).toStrictEqual({}); }); it('allows to set errors object with setErrors handler', () => { - const hook = renderHook(() => useForm()); + const hook = renderHook(() => useForm({ mode })); act(() => hook.result.current.setErrors({ a: true, b: true })); expect(hook.result.current.errors).toStrictEqual({ a: true, b: true }); }); it('filters out initialErrors with null and undefined values', () => { const hook = renderHook(() => - useForm({ initialErrors: { a: 1, b: undefined, c: null, d: 2 } }) + useForm({ mode, initialErrors: { a: 1, b: undefined, c: null, d: 2 } }) ); expect(hook.result.current.errors).toStrictEqual({ a: 1, d: 2 }); }); +} + +describe('@mantine/form/errors-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/errors-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/getInputProps.test.ts b/packages/@mantine/form/src/tests/getInputProps.test.ts index e2ebc0db129..c517f80caee 100644 --- a/packages/@mantine/form/src/tests/getInputProps.test.ts +++ b/packages/@mantine/form/src/tests/getInputProps.test.ts @@ -1,95 +1,121 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/get-input-props', () => { +function getInputProps(mode: FormMode, input: Record) { + const { value, error, ...others } = input; + const result = { + ...others, + [mode === 'controlled' ? 'value' : 'defaultValue']: value, + error, + onBlur: expect.any(Function), + onChange: expect.any(Function), + onFocus: expect.any(Function), + }; + + if (mode === 'uncontrolled') { + result.key = expect.any(String); + } + + return result; +} + +function tests(mode: FormMode) { it('returns correct input props (root property)', () => { const hook = renderHook(() => - useForm({ initialValues: { fruit: 'banana' }, initialErrors: { fruit: 'invalid fruit' } }) + useForm({ + mode, + initialValues: { fruit: 'banana' }, + initialErrors: { fruit: 'invalid fruit' }, + }) ); - const props = hook.result.current.getInputProps('fruit'); - expect(props.value).toBe('banana'); - expect(props.error).toBe('invalid fruit'); - expect(typeof props.onChange).toBe('function'); + expect(hook.result.current.getInputProps('fruit')).toStrictEqual( + getInputProps(mode, { value: 'banana', error: 'invalid fruit' }) + ); }); it('returns correct input props (nested object property)', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { fruit: { name: 'banana' } }, initialErrors: { 'fruit.name': 'invalid fruit' }, }) ); - const props = hook.result.current.getInputProps('fruit.name'); - expect(props.value).toBe('banana'); - expect(props.error).toBe('invalid fruit'); - expect(typeof props.onChange).toBe('function'); + expect(hook.result.current.getInputProps('fruit.name')).toStrictEqual( + getInputProps(mode, { value: 'banana', error: 'invalid fruit' }) + ); }); it('returns correct input props (nested array property)', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] }, initialErrors: { 'a.1.b': 'error-b' }, }) ); - const props = hook.result.current.getInputProps('a.1.b'); - expect(props.value).toBe(2); - expect(props.error).toBe('error-b'); - expect(typeof props.onChange).toBe('function'); + expect(hook.result.current.getInputProps('a.1.b')).toStrictEqual( + getInputProps(mode, { value: 2, error: 'error-b' }) + ); }); - it('returns correct checkbox props', () => { + it('returns correct checked prop instead of value', () => { const hook = renderHook(() => - useForm({ initialValues: { fruit: false }, initialErrors: { fruit: 'invalid fruit' } }) + useForm({ mode, initialValues: { fruit: false }, initialErrors: { fruit: 'invalid fruit' } }) ); - const props = hook.result.current.getInputProps('fruit', { type: 'checkbox', withError: true }); - expect(props.checked).toBe(false); - expect(props.error).toBe('invalid fruit'); - expect(typeof props.onChange).toBe('function'); + const result = hook.result.current.getInputProps('fruit', { + type: 'checkbox', + withError: true, + }) as any; + + expect(result[mode === 'controlled' ? 'checked' : 'defaultChecked']).toBe(false); + expect(result[mode === 'controlled' ? 'value' : 'defaultValue']).toBe(undefined); }); it('does not return an error if withError is set to false', () => { const hook = renderHook(() => - useForm({ initialValues: { fruit: true }, initialErrors: { fruit: 'invalid fruit' } }) + useForm({ mode, initialValues: { fruit: true }, initialErrors: { fruit: 'invalid fruit' } }) ); - const props = hook.result.current.getInputProps('fruit', { + const result = hook.result.current.getInputProps('fruit', { type: 'checkbox', withError: false, - }); - expect(props.checked).toBe(true); - expect('error' in props).toBe(false); - expect(typeof props.onChange).toBe('function'); + }) as any; + expect(result[mode === 'controlled' ? 'checked' : 'defaultChecked']).toBe(true); + expect('error' in result).toBe(false); }); it('updates form value with returned onChange handler (root property)', () => { - const hook = renderHook(() => useForm({ initialValues: { fruit: true, vegetable: 'potato' } })); + const hook = renderHook(() => + useForm({ mode, initialValues: { fruit: true, vegetable: 'potato' } }) + ); act(() => hook.result.current.getInputProps('fruit', { type: 'checkbox' }).onChange(false)); - expect(hook.result.current.values).toStrictEqual({ fruit: false, vegetable: 'potato' }); + expect(hook.result.current.getValues()).toStrictEqual({ fruit: false, vegetable: 'potato' }); act(() => hook.result.current.getInputProps('vegetable').onChange('carrot')); - expect(hook.result.current.values).toStrictEqual({ fruit: false, vegetable: 'carrot' }); + expect(hook.result.current.getValues()).toStrictEqual({ fruit: false, vegetable: 'carrot' }); }); it('updates form value with returned onChange handler (nested object)', () => { const hook = renderHook(() => - useForm({ initialValues: { nested: { fruit: true, vegetable: 'potato' } } }) + useForm({ mode, initialValues: { nested: { fruit: true, vegetable: 'potato' } } }) ); act(() => hook.result.current.getInputProps('nested.fruit', { type: 'checkbox' }).onChange(false) ); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ nested: { fruit: false, vegetable: 'potato' }, }); act(() => hook.result.current.getInputProps('nested.vegetable').onChange('carrot')); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ nested: { fruit: false, vegetable: 'carrot' }, }); }); @@ -97,6 +123,7 @@ describe('@mantine/form/get-input-props', () => { it('updates form value with returned onChange handler (nested array)', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { nested: [ { fruit: true, vegetable: 'potato' }, @@ -109,7 +136,7 @@ describe('@mantine/form/get-input-props', () => { act(() => hook.result.current.getInputProps('nested.1.fruit', { type: 'checkbox' }).onChange(false) ); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ nested: [ { fruit: true, vegetable: 'potato' }, { fruit: false, vegetable: 'potato' }, @@ -117,7 +144,7 @@ describe('@mantine/form/get-input-props', () => { }); act(() => hook.result.current.getInputProps('nested.0.vegetable').onChange('carrot')); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ nested: [ { fruit: true, vegetable: 'carrot' }, { fruit: false, vegetable: 'potato' }, @@ -126,10 +153,18 @@ describe('@mantine/form/get-input-props', () => { }); it('returns onFocus if withFocus is true', () => { - const hook = renderHook(() => useForm({ initialValues: { a: 1 } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: 1 } })); expect(typeof hook.result.current.getInputProps('a').onFocus).toBe('function'); expect(typeof hook.result.current.getInputProps('a', { withFocus: false }).onFocus).toBe( 'undefined' ); }); +} + +describe('@mantine/form/get-input-props-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/get-input-props-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/getTransformedValues.test.ts b/packages/@mantine/form/src/tests/getTransformedValues.test.ts index 89eeba84b2e..fa14b25bfd9 100644 --- a/packages/@mantine/form/src/tests/getTransformedValues.test.ts +++ b/packages/@mantine/form/src/tests/getTransformedValues.test.ts @@ -1,10 +1,12 @@ import { renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/getTransformedValues', () => { +function tests(mode: FormMode) { it('transforms given values object', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: 1, b: '2' }, transformValues: (values) => ({ a: values.a.toString(), @@ -22,6 +24,7 @@ describe('@mantine/form/getTransformedValues', () => { it('transforms state values if input object is not provided', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: 1, b: '2' }, transformValues: (values) => ({ a: values.a.toString(), @@ -37,7 +40,7 @@ describe('@mantine/form/getTransformedValues', () => { }); it('returns provided values or form values if transformValues function is not set', () => { - const hook = renderHook(() => useForm({ initialValues: { a: 1, b: '2' } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: 1, b: '2' } })); expect(hook.result.current.getTransformedValues({ a: 2, b: '3' })).toStrictEqual({ a: 2, b: '3', @@ -47,4 +50,12 @@ describe('@mantine/form/getTransformedValues', () => { b: '2', }); }); +} + +describe('@mantine/form/getTransformedValues-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/getTransformedValues-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/initialize.test.ts b/packages/@mantine/form/src/tests/initialize.test.ts index 62fd1181566..10d7109a765 100644 --- a/packages/@mantine/form/src/tests/initialize.test.ts +++ b/packages/@mantine/form/src/tests/initialize.test.ts @@ -1,17 +1,26 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/initialize', () => { +function tests(mode: FormMode) { it('initializes form with given values with form.initialize', () => { - const hook = renderHook(() => useForm({ initialValues: { a: 1, b: 2 } })); - expect(hook.result.current.values).toStrictEqual({ a: 1, b: 2 }); + const hook = renderHook(() => useForm({ mode, initialValues: { a: 1, b: 2 } })); + expect(hook.result.current.getValues()).toStrictEqual({ a: 1, b: 2 }); act(() => hook.result.current.initialize({ a: 3, b: 4 })); - expect(hook.result.current.values).toStrictEqual({ a: 3, b: 4 }); + expect(hook.result.current.getValues()).toStrictEqual({ a: 3, b: 4 }); expect(hook.result.current.initialized).toBe(true); act(() => hook.result.current.setValues({ a: 1, b: 2 })); act(() => hook.result.current.initialize({ a: 5, b: 6 })); - expect(hook.result.current.values).toStrictEqual({ a: 1, b: 2 }); + expect(hook.result.current.getValues()).toStrictEqual({ a: 1, b: 2 }); }); +} + +describe('@mantine/form/initialize-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/initialize-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/insertListItem.test.ts b/packages/@mantine/form/src/tests/insertListItem.test.ts index 76eec87f174..a9a755072bc 100644 --- a/packages/@mantine/form/src/tests/insertListItem.test.ts +++ b/packages/@mantine/form/src/tests/insertListItem.test.ts @@ -1,39 +1,43 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/insertListItem', () => { +function tests(mode: FormMode) { it('appends list item to the end of the array if index is not specified', () => { - const hook = renderHook(() => useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }] } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }] } })); act(() => hook.result.current.insertListItem('a', { b: 3 })); - expect(hook.result.current.values).toStrictEqual({ a: [{ b: 1 }, { b: 2 }, { b: 3 }] }); + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: 1 }, { b: 2 }, { b: 3 }] }); }); it('inserts list item at given position when index is specified', () => { - const hook = renderHook(() => useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }] } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }] } })); act(() => hook.result.current.insertListItem('a', { b: 3 }, 1)); - expect(hook.result.current.values).toStrictEqual({ a: [{ b: 1 }, { b: 3 }, { b: 2 }] }); + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: 1 }, { b: 3 }, { b: 2 }] }); }); it('inserts item into nested list', () => { const hook = renderHook(() => - useForm({ initialValues: { a: [{ b: [{ c: 1 }, { c: 2 }] }, { b: [{ c: 1 }, { c: 2 }] }] } }) + useForm({ + mode, + initialValues: { a: [{ b: [{ c: 1 }, { c: 2 }] }, { b: [{ c: 1 }, { c: 2 }] }] }, + }) ); act(() => hook.result.current.insertListItem('a.1.b', { c: 3 }, 1)); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: [{ c: 1 }, { c: 2 }] }, { b: [{ c: 1 }, { c: 3 }, { c: 2 }] }], }); }); it('allows to insert multiple values with consecutive insetListItem calls', () => { - const hook = renderHook(() => useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }] } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }] } })); act(() => { hook.result.current.insertListItem('a', { b: 3 }); hook.result.current.insertListItem('a', { b: 4 }); hook.result.current.insertListItem('a', { b: 5 }); }); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: 1 }, { b: 2 }, { b: 3 }, { b: 4 }, { b: 5 }], }); }); @@ -41,6 +45,7 @@ describe('@mantine/form/insertListItem', () => { it('updates errors of associated fields when list item is inserted', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { name: '', a: [{ b: 1 }, { b: 2 }, { b: 3 }], @@ -89,4 +94,12 @@ describe('@mantine/form/insertListItem', () => { } ); }); +} + +describe('@mantine/form/insertListItem-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/insertListItem-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/isValid.test.ts b/packages/@mantine/form/src/tests/isValid.test.ts index d37472bec3a..73bdfe51af6 100644 --- a/packages/@mantine/form/src/tests/isValid.test.ts +++ b/packages/@mantine/form/src/tests/isValid.test.ts @@ -1,10 +1,12 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/isValid', () => { +function tests(mode: FormMode) { it('returns correct form validation state', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: 1 }, validate: { a: (value) => (value < 2 ? 'error' : null), @@ -22,6 +24,7 @@ describe('@mantine/form/isValid', () => { it('returns correct field validation state', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: 1, b: 2 }, validate: { a: (value) => (value < 2 ? 'error' : null), @@ -37,4 +40,12 @@ describe('@mantine/form/isValid', () => { expect(hook.result.current.isValid('a')).toBe(true); expect(hook.result.current.isValid('b')).toBe(true); }); +} + +describe('@mantine/form/isValid-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/isValid-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/onReset.test.ts b/packages/@mantine/form/src/tests/onReset.test.ts index 8b967173040..0fd4187d2ae 100644 --- a/packages/@mantine/form/src/tests/onReset.test.ts +++ b/packages/@mantine/form/src/tests/onReset.test.ts @@ -1,13 +1,15 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; const getFormEvent = () => ({ preventDefault: jest.fn() }) as any; -describe('@mantine/form/onReset', () => { +function tests(mode: FormMode) { it('resets form with onReset handler', () => { const event = getFormEvent(); const hook = renderHook(() => useForm({ + mode, clearInputErrorOnChange: false, initialValues: { a: 1, b: 2 }, initialErrors: { a: 'error-a', b: 'error-b' }, @@ -15,11 +17,19 @@ describe('@mantine/form/onReset', () => { ); act(() => hook.result.current.setValues({ a: 10, b: 20 })); - expect(hook.result.current.values).toStrictEqual({ a: 10, b: 20 }); + expect(hook.result.current.getValues()).toStrictEqual({ a: 10, b: 20 }); expect(hook.result.current.errors).toStrictEqual({ a: 'error-a', b: 'error-b' }); act(() => hook.result.current.onReset(event)); - expect(hook.result.current.values).toStrictEqual({ a: 1, b: 2 }); + expect(hook.result.current.getValues()).toStrictEqual({ a: 1, b: 2 }); expect(hook.result.current.errors).toStrictEqual({}); }); +} + +describe('@mantine/form/onReset-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/onReset-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/onSubmit.test.ts b/packages/@mantine/form/src/tests/onSubmit.test.ts index df503cd2862..7c4f0da8f40 100644 --- a/packages/@mantine/form/src/tests/onSubmit.test.ts +++ b/packages/@mantine/form/src/tests/onSubmit.test.ts @@ -1,12 +1,13 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; const getFormEvent = () => ({ preventDefault: jest.fn() }) as any; -describe('@mantine/form/onSubmit', () => { +function tests(mode: FormMode) { it('calls handleSubmit with values and event when all values are valid', () => { const hook = renderHook(() => - useForm({ initialValues: { banana: 'test banana', apple: 'test apple' } }) + useForm({ mode, initialValues: { banana: 'test banana', apple: 'test apple' } }) ); const event = getFormEvent(); @@ -27,6 +28,7 @@ describe('@mantine/form/onSubmit', () => { it('calls handleValidationFailure when values are not valid', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { banana: '', orange: '', @@ -70,9 +72,17 @@ describe('@mantine/form/onSubmit', () => { }); it('allows to call onSubmit without event', () => { - const hook = renderHook(() => useForm({ initialValues: { a: 1 } })); + const hook = renderHook(() => useForm({ mode, initialValues: { a: 1 } })); const handleSubmit = jest.fn(); act(() => hook.result.current.onSubmit(handleSubmit)()); expect(handleSubmit).toHaveBeenCalledWith({ a: 1 }, undefined); }); +} + +describe('@mantine/form/onSubmit-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/onSubmit-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/removeListItem.test.ts b/packages/@mantine/form/src/tests/removeListItem.test.ts index 443f7352739..300b6b1215d 100644 --- a/packages/@mantine/form/src/tests/removeListItem.test.ts +++ b/packages/@mantine/form/src/tests/removeListItem.test.ts @@ -1,28 +1,30 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/removeListItem', () => { +function tests(mode: FormMode) { it('removes list item with given index (root property)', () => { const hook = renderHook(() => - useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } }) + useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } }) ); act(() => hook.result.current.removeListItem('a', 1)); - expect(hook.result.current.values).toStrictEqual({ a: [{ b: 1 }, { b: 3 }] }); + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: 1 }, { b: 3 }] }); }); it('does not change values if given path does not exist', () => { const hook = renderHook(() => - useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } }) + useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } }) ); act(() => hook.result.current.removeListItem('does.not.exist', 1)); - expect(hook.result.current.values).toStrictEqual({ a: [{ b: 1 }, { b: 2 }, { b: 3 }] }); + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: 1 }, { b: 2 }, { b: 3 }] }); }); it('removes list item with given index (nested list)', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: [ { b: [{ c: [{ d: 1 }, { d: 2 }, { d: 3 }] }, { c: [{ d: 1 }, { d: 2 }, { d: 3 }] }] }, @@ -32,7 +34,7 @@ describe('@mantine/form/removeListItem', () => { ); act(() => hook.result.current.removeListItem('a.0.b.1.c', 1)); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: [{ c: [{ d: 1 }, { d: 2 }, { d: 3 }] }, { c: [{ d: 1 }, { d: 3 }] }] }], }); }); @@ -40,6 +42,7 @@ describe('@mantine/form/removeListItem', () => { it('updates errors of associated fields when list item is removed', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { name: '', a: [{ b: 1 }, { b: 2 }, { b: 3 }], @@ -72,6 +75,7 @@ describe('@mantine/form/removeListItem', () => { const spy = jest.fn(); const hook = renderHook(() => useForm({ + mode, onValuesChange: spy, initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }], @@ -85,4 +89,12 @@ describe('@mantine/form/removeListItem', () => { { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } ); }); +} + +describe('@mantine/form/removeListItem-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/removeListItem-uncontrolled', () => { + tests('uncontrolled'); }); diff --git a/packages/@mantine/form/src/tests/reorderListItem.test.ts b/packages/@mantine/form/src/tests/reorderListItem.test.ts index 2ea8cf3292f..c13487294cb 100644 --- a/packages/@mantine/form/src/tests/reorderListItem.test.ts +++ b/packages/@mantine/form/src/tests/reorderListItem.test.ts @@ -1,19 +1,21 @@ import { act, renderHook } from '@testing-library/react'; +import { FormMode } from '../types'; import { useForm } from '../use-form'; -describe('@mantine/form/reorderListItem', () => { +function tests(mode: FormMode) { it('reorders items at given list', () => { const hook = renderHook(() => - useForm({ initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } }) + useForm({ mode, initialValues: { a: [{ b: 1 }, { b: 2 }, { b: 3 }] } }) ); act(() => hook.result.current.reorderListItem('a', { from: 2, to: 0 })); - expect(hook.result.current.values).toStrictEqual({ a: [{ b: 3 }, { b: 1 }, { b: 2 }] }); + expect(hook.result.current.getValues()).toStrictEqual({ a: [{ b: 3 }, { b: 1 }, { b: 2 }] }); }); it('reorders items at given nested list', () => { const hook = renderHook(() => useForm({ + mode, initialValues: { a: [ { b: [{ c: 1 }, { c: 2 }, { c: 3 }] }, @@ -25,7 +27,7 @@ describe('@mantine/form/reorderListItem', () => { ); act(() => hook.result.current.reorderListItem('a.1.b', { from: 1, to: 0 })); - expect(hook.result.current.values).toStrictEqual({ + expect(hook.result.current.getValues()).toStrictEqual({ a: [ { b: [{ c: 1 }, { c: 2 }, { c: 3 }] }, { b: [{ c: 5 }, { c: 4 }, { c: 6 }] }, @@ -38,6 +40,7 @@ describe('@mantine/form/reorderListItem', () => { const spy = jest.fn(); const hook = renderHook(() => useForm({ + mode, onValuesChange: spy, initialValues: { a: [ @@ -67,4 +70,12 @@ describe('@mantine/form/reorderListItem', () => { } ); }); +} + +describe('@mantine/form/reorderListItem-controlled', () => { + tests('controlled'); +}); + +describe('@mantine/form/reorderListItem-uncontrolled', () => { + tests('uncontrolled'); });