From 9ae8eb5f55fda28862faf261ea35885577b7f958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= <37746259+piotrpospiech@users.noreply.github.com> Date: Fri, 23 Feb 2024 12:12:53 +0100 Subject: [PATCH] Migrated `BaseForm` tests to @testing-library/react (#1303) --- packages/uniforms/__suites__/BaseForm.tsx | 8 +- packages/uniforms/__tests__/BaseForm.tsx | 440 ++++++++-------------- 2 files changed, 162 insertions(+), 286 deletions(-) diff --git a/packages/uniforms/__suites__/BaseForm.tsx b/packages/uniforms/__suites__/BaseForm.tsx index 1a2f49e9e..89c887748 100644 --- a/packages/uniforms/__suites__/BaseForm.tsx +++ b/packages/uniforms/__suites__/BaseForm.tsx @@ -4,10 +4,10 @@ import { ZodBridge } from 'uniforms-bridge-zod'; import z from 'zod'; export function testBaseForm(BaseForm: ComponentType) { + const schema = new ZodBridge({ schema: z.object({}) }); + test(' - renders', () => { - const schema = z.object({}); - const bridge = new ZodBridge({ schema }); - const screen = render(); - expect(screen.getByTestId('form')).toBeInTheDocument(); + const { container } = render(); + expect(container.getElementsByTagName('form')).toHaveLength(1); }); } diff --git a/packages/uniforms/__tests__/BaseForm.tsx b/packages/uniforms/__tests__/BaseForm.tsx index be29a930f..4ab9cdb62 100644 --- a/packages/uniforms/__tests__/BaseForm.tsx +++ b/packages/uniforms/__tests__/BaseForm.tsx @@ -1,182 +1,68 @@ -import { ReactWrapper } from 'enzyme'; -import React from 'react'; -import { BaseForm, Bridge, Context, useField } from 'uniforms'; - -import mount from './_mount'; - -jest.mock('meteor/aldeed:simple-schema'); -jest.mock('meteor/check'); +import { fireEvent, render, screen } from '@testing-library/react'; +import React, { useContext } from 'react'; +import { BaseForm, context } from 'uniforms'; +import { ZodBridge } from 'uniforms-bridge-zod'; +import { AutoField } from 'uniforms-unstyled'; +import z from 'zod'; describe('BaseForm', () => { - const error = new Error(); const model = { $: [1], _: 1 }; - const schema: Bridge = { - getError() {}, - getErrorMessage: () => '', - getErrorMessages: () => [], - getField: () => ({}), - getInitialValue() {}, - getProps: () => ({}), - getSubfields: () => [], - getType() {}, - getValidator: () => () => {}, - }; + const schema = new ZodBridge({ + schema: z.object({ a: z.string().optional() }), + }); const onChange = jest.fn(); const onSubmit = jest.fn(); - afterEach(() => { onChange.mockClear(); onSubmit.mockClear(); }); - it('have correct context', () => { - const wrapper = mount>( - , - ); - - const context = wrapper.instance().getContext(); - expect(context).toEqual>({ - changed: false, - changedMap: {}, - error, - model, - name: [], - onChange: expect.any(Function), - onSubmit: expect.any(Function), - randomId: expect.any(Function), - schema, - state: { - disabled: false, - readOnly: false, - showInlineError: false, - }, - submitted: false, - submitting: false, - validating: false, - formRef: expect.any(BaseForm), - }); - }); - describe('when rendered', () => { - const wrapper = mount>( - -
-
-
- , - ); - it('is
', () => { - expect(wrapper.find('form')).toHaveLength(1); - }); - - it('have correct props', () => { - expect(wrapper.props()).toHaveProperty('noValidate', true); + const { container } = render(); + expect(container.getElementsByTagName('form')).toHaveLength(1); }); it('have correct children', () => { - expect(wrapper).toContainEqual(expect.anything()); - expect(wrapper.find('div')).toHaveLength(3); - }); - - it('have correct `resetCount`', () => { - expect(wrapper.state('resetCount')).toBe(0); - }); - - it('have correct `state`', () => { - const context = wrapper.instance().getContext(); - expect(context.state).toEqual['state']>({ - disabled: true, - readOnly: false, - showInlineError: true, - }); - }); - - it('updates schema bridge', () => { - const schema2 = { ...(schema as Omit), getType: () => {} }; - - wrapper.setProps({ schema: schema2 }); - - const context = wrapper.instance().getContext(); - - expect(context).toHaveProperty('schema', schema2); - }); - - it('ignores changes made on first render', () => { - function Field() { - const [props] = useField('name', {}); - props.onChange(123); - return null; - } - - const wrapper = mount>( - - + const { container } = render( + +
+
+
, ); - const context = wrapper.instance().getContext(); - expect(context).toHaveProperty('changed', false); - expect(context).toHaveProperty('changedMap', {}); - - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange).toHaveBeenCalledWith('name', 123); + expect(container.getElementsByTagName('div')).toHaveLength(3); }); }); describe('when changed', () => { - type Form = BaseForm; - let wrapper: ReactWrapper; - - beforeEach(() => { - wrapper = mount( - , + it('autosaves correctly (`autosave` = false)', async () => { + render( + + + , ); - }); - - it('updates `changed` and `changedMap`', () => { - const context1 = wrapper.instance().getContext(); - expect(context1).toHaveProperty('changed', false); - expect(context1).toHaveProperty('changedMap', {}); - - wrapper.instance().getContext().onChange('$', [1, 2]); - const context2 = wrapper.instance().getContext(); - expect(context2).toHaveProperty('changed', true); - expect(context2).toHaveProperty('changedMap.$'); - expect(context2.changedMap.$).toBeTruthy(); - expect(context2).toHaveProperty('changedMap.$.1'); - expect(context2.changedMap.$?.[1]).toBeTruthy(); + const input = screen.getByLabelText('A'); + fireEvent.change(input, { target: { value: 'test' } }); - wrapper.instance().getContext().onChange('$', [1]); + await new Promise(resolve => setTimeout(resolve)); - const context3 = wrapper.instance().getContext(); - expect(context3).toHaveProperty('changed', true); - expect(context3).toHaveProperty('changedMap.$'); - expect(context3.changedMap.$).toBeTruthy(); - expect(context3).toHaveProperty('changedMap.$.1'); - expect(context3.changedMap.$?.[1]).toBeTruthy(); + expect(onSubmit).not.toBeCalled(); }); it('autosaves correctly (`autosave` = true)', async () => { - wrapper.setProps({ autosave: true }); - wrapper.instance().getContext().onChange('a', 1); - await new Promise(resolve => setTimeout(resolve)); - const context = wrapper.instance().getContext(); - expect(onSubmit).toHaveBeenCalledTimes(1); - expect(context.submitted).toBe(true); - expect(onSubmit).toHaveBeenLastCalledWith(model); - }); + render( + + + , + ); + + const input = screen.getByLabelText('A'); + fireEvent.change(input, { target: { value: 'test' } }); - it('autosaves are not delayed', async () => { - wrapper.setProps({ autosave: true }); - wrapper.instance().getContext().onChange('a', 1); await new Promise(resolve => setTimeout(resolve)); expect(onSubmit).toHaveBeenCalledTimes(1); @@ -184,10 +70,23 @@ describe('BaseForm', () => { }); it('autosaves can be delayed', async () => { - wrapper.setProps({ autosave: true, autosaveDelay: 25 }); - wrapper.instance().getContext().onChange('a', 1); - wrapper.instance().getContext().onChange('a', 2); - wrapper.instance().getContext().onChange('a', 3); + render( + + + , + ); + + const input = screen.getByLabelText('A'); + fireEvent.change(input, { target: { value: 'test 1' } }); + fireEvent.change(input, { target: { value: 'test 2' } }); + fireEvent.change(input, { target: { value: 'test 3' } }); + await new Promise(resolve => setTimeout(resolve)); expect(onSubmit).not.toHaveBeenCalled(); @@ -199,16 +98,28 @@ describe('BaseForm', () => { }); it('autosaves can be delayed (longer)', async () => { - wrapper.setProps({ autosave: true, autosaveDelay: 10 }); - wrapper.instance().getContext().onChange('a', 1); - wrapper.instance().getContext().onChange('a', 2); - wrapper.instance().getContext().onChange('a', 3); + render( + + + , + ); + + const input = screen.getByLabelText('A'); + fireEvent.change(input, { target: { value: 'test 1' } }); + fireEvent.change(input, { target: { value: 'test 2' } }); + fireEvent.change(input, { target: { value: 'test 3' } }); await new Promise(resolve => setTimeout(resolve, 25)); - wrapper.instance().getContext().onChange('a', 1); - wrapper.instance().getContext().onChange('a', 2); - wrapper.instance().getContext().onChange('a', 3); + fireEvent.change(input, { target: { value: 'test 1' } }); + fireEvent.change(input, { target: { value: 'test 2' } }); + fireEvent.change(input, { target: { value: 'test 3' } }); await new Promise(resolve => setTimeout(resolve, 25)); @@ -217,171 +128,136 @@ describe('BaseForm', () => { }); it('clears autosave correctly', () => { - wrapper.setProps({ autosave: true, autosaveDelay: 100 }); - wrapper.instance().getContext().onChange('a', 1); - wrapper.unmount(); + const { unmount } = render( + + + , + ); - expect(onSubmit).not.toBeCalled(); - }); + const input = screen.getByLabelText('A'); + fireEvent.change(input, { target: { value: 'test 1' } }); - it('autosaves correctly (`autosave` = false)', () => { - wrapper.setProps({ autosave: true }); - wrapper.setProps({ autosave: false }); - wrapper.instance().getContext().onChange('a', 1); + unmount(); expect(onSubmit).not.toBeCalled(); }); it('calls `onChange` with correct name and value', () => { - wrapper.instance().getContext().onChange('a', 1); - - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange).toHaveBeenLastCalledWith('a', 1); - }); - - it('cancels `onChange` event', () => { - wrapper.find('form').simulate('change'); - - expect(onChange).not.toBeCalled(); - }); - - it('does nothing without `onChange`', () => { - wrapper.setProps({ onChange: undefined }); - wrapper.instance().getContext().onChange('a', 1); - - expect(onChange).not.toBeCalled(); - }); - }); - - describe('when reset', () => { - const createWrapper = () => - mount>( - , + render( + + + , ); - it('increase `resetCount`', () => { - const wrapper = createWrapper(); - wrapper.instance().reset(); - - expect(wrapper.state('resetCount')).toBe(1); - }); + const input = screen.getByLabelText('A'); + fireEvent.change(input, { target: { value: 'test 1' } }); - it('sets submitted back to false', async () => { - const wrapper = createWrapper(); - const instance = wrapper.instance(); - expect(instance.getContext().submitted).toBe(false); - wrapper.find('form').simulate('submit'); - expect(instance.getContext().submitted).toBe(true); - instance.reset(); - expect(instance.getContext().submitted).toBe(false); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenLastCalledWith('a', 'test 1'); }); }); describe('when submitted', () => { - const createWrapper = () => - mount>( - , - ); - it('calls `onSubmit` once', () => { - const wrapper = createWrapper(); - wrapper.find('form').simulate('submit'); + render( + , + ); + fireEvent.submit(screen.getByTestId('form')); expect(onSubmit).toHaveBeenCalledTimes(1); }); it('calls `onSubmit` with correct model', () => { - const wrapper = createWrapper(); - wrapper.find('form').simulate('submit'); + render( + , + ); + fireEvent.submit(screen.getByTestId('form')); expect(onSubmit).toHaveBeenLastCalledWith(model); }); it('calls `onSubmit` with the correctly `modelTransform`ed model', () => { - const wrapper = createWrapper(); - wrapper.setProps({ - modelTransform(mode, model) { - return mode === 'submit' ? { submit: 1 } : model; - }, - }); - - wrapper.find('form').simulate('submit'); + render( + + mode === 'submit' ? { submit: 1 } : model + } + />, + ); + fireEvent.submit(screen.getByTestId('form')); expect(onSubmit).toHaveBeenLastCalledWith({ submit: 1 }); - - wrapper.setProps({ modelTransform: undefined }); }); it('sets `submitted` to true', async () => { - const wrapper = createWrapper(); - const instance = wrapper.instance(); - expect(instance.getContext().submitted).toBe(false); - wrapper.find('form').simulate('submit'); - expect(instance.getContext().submitted).toBe(true); - }); + let submitted: boolean | undefined; - it('sets `submitting` state while submitting', async () => { - const wrapper = createWrapper(); - // FIXME: It should say `() => void`. - let resolveSubmit: (...args: any[]) => void = () => {}; - wrapper.setProps({ - onSubmit: () => new Promise(resolve => (resolveSubmit = resolve)), - }); + function Field() { + const test = useContext(context); + submitted = test?.submitted; + return null; + } - const context1 = wrapper.instance().getContext(); - expect(context1).toHaveProperty('submitting', false); + render( + + + , + ); - wrapper.find('form').simulate('submit'); - await new Promise(resolve => process.nextTick(resolve)); + expect(submitted).toBe(false); + fireEvent.submit(screen.getByTestId('form')); + expect(submitted).toBe(true); + }); - const context2 = wrapper.instance().getContext(); - expect(context2).toHaveProperty('submitting', true); + it('sets `submitting` state while submitting', async () => { + let submitting: boolean | undefined; - resolveSubmit(); - await new Promise(resolve => process.nextTick(resolve)); + function Field() { + const test = useContext(context); + submitting = test?.submitting; + return null; + } - const context3 = wrapper.instance().getContext(); - expect(context3).toHaveProperty('submitting', false); - }); + let resolveSubmit: (...args: any[]) => void = () => {}; + const test = () => new Promise(resolve => (resolveSubmit = resolve)); - it('ignores synchronous errors', async () => { - const wrapper = createWrapper(); - const error = new Error(); - wrapper.setProps({ - onSubmit() { - throw error; - }, - }); - - try { - wrapper.instance().submit(); - throw new Error('Unreachable.'); - } catch (catched) { - expect(catched).toBe(error); - } - }); + render( + + + , + ); - it('returns asynchronous results', async () => { - const wrapper = createWrapper(); - const value = 42; - wrapper.setProps({ - async onSubmit() { - return value; - }, - }); + expect(submitting).toBe(false); - await expect(wrapper.instance().submit()).resolves.toBe(value); - }); + const form = screen.getByTestId('form'); + fireEvent.submit(form); - it('works when unmounted on submit', () => { - const spy = jest.spyOn(console, 'error'); - const wrapper = createWrapper(); - onSubmit.mockImplementationOnce(async () => wrapper.unmount()); - wrapper.find('form').simulate('submit'); + expect(submitting).toBe(true); - expect(spy).not.toHaveBeenCalled(); + resolveSubmit(); + await new Promise(resolve => process.nextTick(resolve)); - spy.mockRestore(); + expect(submitting).toBe(false); }); }); });