diff --git a/packages/x/tests/useConverterField.test.tsx b/packages/x/tests/useConverterField.test.tsx index 3a6bc86e..66854721 100644 --- a/packages/x/tests/useConverterField.test.tsx +++ b/packages/x/tests/useConverterField.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { ReactiveFormProvider, useForm } from '@reactive-forms/core'; -import { act, renderHook } from '@testing-library/react'; +import { act, renderHook, waitFor } from '@testing-library/react'; import { ConversionError, useConverterField } from '../src/useConverterField'; @@ -52,115 +52,83 @@ const renderUseConverterField = (config: Config = {}) => { }, ); - return { - formBag, - converterFieldBag, - }; + return [converterFieldBag, formBag] as const; }; describe('Converter field', () => { it('Should update field with valid value', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - } = renderUseConverterField(); + const [{ result: converterFieldBag }] = renderUseConverterField(); const { onTextChange } = converterFieldBag.current; expect(converterFieldBag.current.value).toBe(0); expect(converterFieldBag.current.text).toBe('0'); - await act(async () => { - await onTextChange('1'); + await act(() => { + onTextChange('1'); }); - expect(converterFieldBag.current.value).toBe(1); - expect(converterFieldBag.current.text).toBe('1'); - }); - - it('Should set an error if conversion fails', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - } = renderUseConverterField(); - const { onTextChange } = converterFieldBag.current; - - await act(async () => { - await onTextChange('a'); + await waitFor(() => { + expect(converterFieldBag.current.value).toBe(1); + expect(converterFieldBag.current.text).toBe('1'); }); - - expect(converterFieldBag.current.meta.error?.$error).toBe('hello'); - expect(converterFieldBag.current.value).toBe(0); - expect(converterFieldBag.current.text).toBe('a'); }); - it('Should clear conversion error', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - } = renderUseConverterField(); - + it('Should set an error if conversion fails', async () => { + const [{ result: converterFieldBag }] = renderUseConverterField(); const { onTextChange } = converterFieldBag.current; - await act(async () => { - await onTextChange('a'); + await act(() => { + onTextChange('a'); }); - expect(converterFieldBag.current.meta.error?.$error).toBe('hello'); - - await act(async () => { - await onTextChange('1'); + await waitFor(() => { + expect(converterFieldBag.current.meta.error?.$error).toBe('hello'); + expect(converterFieldBag.current.value).toBe(0); + expect(converterFieldBag.current.text).toBe('a'); }); - - expect(converterFieldBag.current.meta.error?.$error).toBeUndefined(); - expect(converterFieldBag.current.value).toBe(1); - expect(converterFieldBag.current.text).toBe('1'); }); - it('Should update text when form value changes (field is not focused)', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - formBag: { result: formBag }, - } = renderUseConverterField(); + it('Should update text when form value changes', async () => { + const [{ result: converterFieldBag }, { result: formBag }] = renderUseConverterField(); const { paths } = formBag.current; - await act(async () => { - await formBag.current.setFieldValue(paths.test, 1); + await act(() => { + formBag.current.setFieldValue(paths.test, 1); }); - expect(converterFieldBag.current.value).toBe(1); - expect(converterFieldBag.current.text).toBe('1'); + await waitFor(() => { + expect(converterFieldBag.current.value).toBe(1); + expect(converterFieldBag.current.text).toBe('1'); + }); }); - it('Should not update text when field is focused and set old value when field is blurred', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - formBag: { result: formBag }, - } = renderUseConverterField(); + it('Should clear conversion error', async () => { + const [{ result: converterFieldBag }] = renderUseConverterField(); - const { onFocus, onBlur } = converterFieldBag.current; - const { setFieldValue, paths } = formBag.current; + const { onTextChange } = converterFieldBag.current; - await act(async () => { - await onFocus(); + await act(() => { + onTextChange('a'); }); - await act(async () => { - await setFieldValue(paths.test, 1); + await waitFor(() => { + expect(converterFieldBag.current.meta.error?.$error).toBe('hello'); }); - expect(converterFieldBag.current.text).toBe('0'); - expect(converterFieldBag.current.value).toBe(1); - - await act(async () => { - await onBlur(); + await act(() => { + onTextChange('1'); }); - expect(converterFieldBag.current.text).toBe('0'); - expect(converterFieldBag.current.value).toBe(0); + await waitFor(() => { + expect(converterFieldBag.current.meta.error?.$error).toBeUndefined(); + expect(converterFieldBag.current.value).toBe(1); + expect(converterFieldBag.current.text).toBe('1'); + }); }); it('Should rethrow an error in case it is not ConversionError', () => { - const { - converterFieldBag: { result: converterFieldBag }, - } = renderUseConverterField({ + const [{ result: converterFieldBag }] = renderUseConverterField({ parse: () => { throw new Error('custom'); }, @@ -172,106 +140,113 @@ describe('Converter field', () => { }); it('Should not update text if there are some conversion errors', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - formBag: { result: formBag }, - } = renderUseConverterField(); + const [{ result: converterFieldBag }, { result: formBag }] = renderUseConverterField(); const { onTextChange } = converterFieldBag.current; const { setFieldValue, paths } = formBag.current; - await act(async () => { - await onTextChange('a'); + await act(() => { + onTextChange('a'); + setFieldValue(paths.test, 1); }); - await act(async () => { - await setFieldValue(paths.test, 1); + await waitFor(() => { + expect(converterFieldBag.current.value).toBe(1); + expect(converterFieldBag.current.text).toBe('a'); }); - - expect(converterFieldBag.current.value).toBe(1); - expect(converterFieldBag.current.text).toBe('a'); }); it('Should return error from validator', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - formBag: { result: formBag }, - } = renderUseConverterField(); + const [{ result: converterFieldBag }, { result: formBag }] = renderUseConverterField(); const { onTextChange } = converterFieldBag.current; const { validateForm, values } = formBag.current; - await act(async () => { - await onTextChange('a'); + await act(() => { + onTextChange('a'); }); const errors = await validateForm(values.getValues()); - expect(errors.test?.$error).toBe('hello'); }); + it('Should ignore new value when field is focused and set old value when field is blurred', async () => { + const [{ result: converterFieldBag }, { result: formBag }] = renderUseConverterField(); + + const { onFocus, onBlur } = converterFieldBag.current; + const { setFieldValue, paths } = formBag.current; + + await act(() => { + onFocus(); + setFieldValue(paths.test, 1); + }); + + await waitFor(() => { + expect(converterFieldBag.current.text).toBe('0'); + expect(converterFieldBag.current.value).toBe(1); + }); + + await act(() => { + onBlur(); + }); + + await waitFor(() => { + expect(converterFieldBag.current.text).toBe('0'); + expect(converterFieldBag.current.value).toBe(0); + }); + }); + it('Should set field touched=true on blur', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - } = renderUseConverterField(); + const [{ result: converterFieldBag }] = renderUseConverterField(); const { onBlur } = converterFieldBag.current; - await act(async () => { - await onBlur(); + await act(() => { + onBlur(); }); - expect(converterFieldBag.current.meta.touched?.$touched).toBe(true); + await waitFor(() => { + expect(converterFieldBag.current.meta.touched?.$touched).toBe(true); + }); }); - it('Should set value both in form state and local text state when focused if using `setValue` callback from hook bag', async () => { - const { - converterFieldBag: { result: converterFieldBag }, - } = renderUseConverterField(); + it('Should set value both in form state and local text state', async () => { + const [{ result: converterFieldBag }] = renderUseConverterField(); const { control: { setValue }, onFocus, } = converterFieldBag.current; - await act(async () => { - await onFocus(); + await act(() => { + onFocus(); + setValue(1); }); - await act(async () => { - await setValue(1); + await waitFor(() => { + expect(converterFieldBag.current.value).toBe(1); + expect(converterFieldBag.current.text).toBe('1'); }); - - expect(converterFieldBag.current.value).toBe(1); - expect(converterFieldBag.current.text).toBe('1'); }); it('Should reformat value when format function changes', () => { - const { converterFieldBag } = renderUseConverterField(); + const [converterFieldBag] = renderUseConverterField(); const format = jest.fn(() => 'test'); - expect(converterFieldBag.result.current.text).toBe('0'); - - act(() => { - converterFieldBag.rerender({ format, parse: defaultParse }); - }); + converterFieldBag.rerender({ format, parse: defaultParse }); - expect(format).toBeCalled(); expect(converterFieldBag.result.current.text).toBe('test'); }); - it('Should parse text again when parse function changes', () => { - const { converterFieldBag } = renderUseConverterField(); + it('Should parse text again when parse function changes', async () => { + const [converterFieldBag] = renderUseConverterField(); const parse = jest.fn(() => 1); - expect(converterFieldBag.result.current.value).toBe(0); - - act(() => { + await act(() => { converterFieldBag.rerender({ format: defaultFormat, parse }); }); - expect(parse).toBeCalled(); expect(converterFieldBag.result.current.value).toBe(1); }); });