From 33c01384c5125495bcceb970ed95089055507280 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:08:37 +0100 Subject: [PATCH 1/5] Migrated `ListField` tests to @testing-library/react (#1299) --- .../uniforms-antd/__tests__/ListField.tsx | 183 ------------------ packages/uniforms-antd/__tests__/index.ts | 2 + .../__tests__/ListField.tsx | 175 ----------------- .../uniforms-bootstrap3/__tests__/index.ts | 1 + .../__tests__/ListField.tsx | 175 ----------------- .../uniforms-bootstrap4/__tests__/index.ts | 1 + .../__tests__/ListField.tsx | 175 ----------------- .../uniforms-bootstrap5/__tests__/index.ts | 1 + .../uniforms-material/__tests__/ListField.tsx | 138 ------------- packages/uniforms-material/__tests__/index.ts | 1 + packages/uniforms-mui/__tests__/ListField.tsx | 138 ------------- packages/uniforms-mui/__tests__/index.ts | 1 + .../uniforms-semantic/__tests__/ListField.tsx | 167 ---------------- packages/uniforms-semantic/__tests__/index.ts | 1 + .../uniforms-unstyled/__tests__/ListField.tsx | 136 ------------- packages/uniforms-unstyled/__tests__/index.ts | 1 + packages/uniforms/__suites__/ListField.tsx | 119 +++++++++++- 17 files changed, 125 insertions(+), 1290 deletions(-) delete mode 100644 packages/uniforms-antd/__tests__/ListField.tsx delete mode 100644 packages/uniforms-bootstrap3/__tests__/ListField.tsx delete mode 100644 packages/uniforms-bootstrap4/__tests__/ListField.tsx delete mode 100644 packages/uniforms-bootstrap5/__tests__/ListField.tsx delete mode 100644 packages/uniforms-material/__tests__/ListField.tsx delete mode 100644 packages/uniforms-mui/__tests__/ListField.tsx delete mode 100644 packages/uniforms-semantic/__tests__/ListField.tsx delete mode 100644 packages/uniforms-unstyled/__tests__/ListField.tsx diff --git a/packages/uniforms-antd/__tests__/ListField.tsx b/packages/uniforms-antd/__tests__/ListField.tsx deleted file mode 100644 index 0d91e4c9d..000000000 --- a/packages/uniforms-antd/__tests__/ListField.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import Tooltip from 'antd/lib/tooltip'; -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-antd'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label and info (specified)', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(Tooltip)).toHaveLength(1); - expect(wrapper.find(Tooltip).prop('title')).toBe('ListFieldInfo'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('div > div').at(0).text()).toEqual( - expect.stringContaining('ListFieldLabel'), - ); -}); - -test(' - renders correct numer of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find('input')).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders correct error text (specified)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array, label: '' }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('div > div').at(0).text()).toBe('Error'); -}); - -test(' - renders correct error style', () => { - const error = new Error(); - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('div').at(0).prop('style')).toHaveProperty( - 'borderColor', - 'rgb(255, 85, 0)', - ); -}); - -test(' - renders correct error style (with specified style prop)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('div').at(0).prop('style')).toHaveProperty( - 'marginLeft', - '8px', - ); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - - expect(wrapper.find(ListAddField).simulate('click')).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-antd/__tests__/index.ts b/packages/uniforms-antd/__tests__/index.ts index 8f0fbdb8b..205f5d766 100644 --- a/packages/uniforms-antd/__tests__/index.ts +++ b/packages/uniforms-antd/__tests__/index.ts @@ -47,6 +47,8 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByRole('img', { name: 'plus-square' }), + testTooltip: true, + testStyle: true, }); suites.testLongTextField(theme.LongTextField); // FIXME: AntD number input doesn't work with new RTL test implementation diff --git a/packages/uniforms-bootstrap3/__tests__/ListField.tsx b/packages/uniforms-bootstrap3/__tests__/ListField.tsx deleted file mode 100644 index 81e67ced7..000000000 --- a/packages/uniforms-bootstrap3/__tests__/ListField.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-bootstrap3'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').text()).toEqual( - expect.stringContaining('ListFieldLabel'), - ); -}); - -test(' - renders correct numer of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find('input')).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders correct error text (specified)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('.help-block').text()).toBe('Error'); -}); - -test(' - renders correct error text (showInlineError=false)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('.help-block')).toHaveLength(0); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - - expect(wrapper.find(ListAddField).simulate('click')).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-bootstrap3/__tests__/index.ts b/packages/uniforms-bootstrap3/__tests__/index.ts index 83ab14a6d..94fa7abc6 100644 --- a/packages/uniforms-bootstrap3/__tests__/index.ts +++ b/packages/uniforms-bootstrap3/__tests__/index.ts @@ -47,6 +47,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByRole('button'), + disableInlineError: true, }); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField); diff --git a/packages/uniforms-bootstrap4/__tests__/ListField.tsx b/packages/uniforms-bootstrap4/__tests__/ListField.tsx deleted file mode 100644 index 4f6215222..000000000 --- a/packages/uniforms-bootstrap4/__tests__/ListField.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-bootstrap4'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').text()).toEqual( - expect.stringContaining('ListFieldLabel'), - ); -}); - -test(' - renders correct numer of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find('input')).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders correct error text (specified)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('.text-danger').text()).toBe('Error'); -}); - -test(' - renders correct error text (showInlineError=false)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('.text-danger')).toHaveLength(0); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - - expect(wrapper.find(ListAddField).simulate('click')).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-bootstrap4/__tests__/index.ts b/packages/uniforms-bootstrap4/__tests__/index.ts index 0d7ded864..84249a4e7 100644 --- a/packages/uniforms-bootstrap4/__tests__/index.ts +++ b/packages/uniforms-bootstrap4/__tests__/index.ts @@ -47,6 +47,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByRole('button'), + disableInlineError: true, }); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField); diff --git a/packages/uniforms-bootstrap5/__tests__/ListField.tsx b/packages/uniforms-bootstrap5/__tests__/ListField.tsx deleted file mode 100644 index 72efa14fc..000000000 --- a/packages/uniforms-bootstrap5/__tests__/ListField.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-bootstrap5'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').text()).toEqual( - expect.stringContaining('ListFieldLabel'), - ); -}); - -test(' - renders correct numer of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find('input')).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders correct error text (specified)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('.text-danger').text()).toBe('Error'); -}); - -test(' - renders correct error text (showInlineError=false)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('.text-danger')).toHaveLength(0); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - - expect(wrapper.find(ListAddField).simulate('click')).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-bootstrap5/__tests__/index.ts b/packages/uniforms-bootstrap5/__tests__/index.ts index bd1096e57..5248b5b72 100644 --- a/packages/uniforms-bootstrap5/__tests__/index.ts +++ b/packages/uniforms-bootstrap5/__tests__/index.ts @@ -47,6 +47,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByRole('button'), + disableInlineError: true, }); suites.testLongTextField(theme.LongTextField, { testMinMaxLength: true, diff --git a/packages/uniforms-material/__tests__/ListField.tsx b/packages/uniforms-material/__tests__/ListField.tsx deleted file mode 100644 index 046235db6..000000000 --- a/packages/uniforms-material/__tests__/ListField.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import IconButton from '@material-ui/core/IconButton'; -import ListSubheader from '@material-ui/core/ListSubheader'; -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-material'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListSubheader)).toHaveLength(1); - expect(wrapper.find(ListSubheader).text()).toBe('ListFieldLabel'); -}); - -test(' - renders correct number of items with model', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - expect( - wrapper.find(ListAddField).find(IconButton).simulate('click'), - ).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-material/__tests__/index.ts b/packages/uniforms-material/__tests__/index.ts index 5d81b6d44..c3e9643e9 100644 --- a/packages/uniforms-material/__tests__/index.ts +++ b/packages/uniforms-material/__tests__/index.ts @@ -49,6 +49,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByText(/\+/), + testError: false, }); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField, { skipInMuiTests: true }); diff --git a/packages/uniforms-mui/__tests__/ListField.tsx b/packages/uniforms-mui/__tests__/ListField.tsx deleted file mode 100644 index 4c10074d0..000000000 --- a/packages/uniforms-mui/__tests__/ListField.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import IconButton from '@mui/material/IconButton'; -import ListSubheader from '@mui/material/ListSubheader'; -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-mui'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListSubheader)).toHaveLength(1); - expect(wrapper.find(ListSubheader).text()).toBe('ListFieldLabel'); -}); - -test(' - renders correct number of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -// Strange enzyme behavior -// TypeError: Cannot read properties of null (reading '__reactFiber$my72orhzzz9') -test.skip(' - renders proper number of optional values after add new value (with initialCount)', async () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - ), - ); - - expect( - wrapper.find(ListAddField).find(IconButton).simulate('click'), - ).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-mui/__tests__/index.ts b/packages/uniforms-mui/__tests__/index.ts index bb7fdb5b8..a1c7dfdae 100644 --- a/packages/uniforms-mui/__tests__/index.ts +++ b/packages/uniforms-mui/__tests__/index.ts @@ -46,6 +46,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByText(/\+/), + testError: false, }); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField, { skipInMuiTests: true }); diff --git a/packages/uniforms-semantic/__tests__/ListField.tsx b/packages/uniforms-semantic/__tests__/ListField.tsx deleted file mode 100644 index 2efb22d0f..000000000 --- a/packages/uniforms-semantic/__tests__/ListField.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-semantic'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').text()).toEqual( - expect.stringContaining('ListFieldLabel'), - ); -}); - -test(' - renders correct number of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find('input')).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders correct error text (specified)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array, label: '' }, 'x.$': { type: String } }), - ); - - expect(wrapper.children().first().text()).toBe('Error'); -}); - -test(' - renders correct error text (showInlineError=false)', () => { - const error = new Error(); - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.children().first().text()).not.toBe('Error'); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - expect(wrapper.find(ListAddField).simulate('click')).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-semantic/__tests__/index.ts b/packages/uniforms-semantic/__tests__/index.ts index 1c5817f1a..0a4d2734d 100644 --- a/packages/uniforms-semantic/__tests__/index.ts +++ b/packages/uniforms-semantic/__tests__/index.ts @@ -45,6 +45,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByRole('button'), + disableInlineError: true, }); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField); diff --git a/packages/uniforms-unstyled/__tests__/ListField.tsx b/packages/uniforms-unstyled/__tests__/ListField.tsx deleted file mode 100644 index 7c8dd726a..000000000 --- a/packages/uniforms-unstyled/__tests__/ListField.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React from 'react'; -import { ListAddField, ListField, ListItemField } from 'uniforms-unstyled'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListField)).toHaveLength(1); -}); - -test(' - renders ListAddField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListAddField)).toHaveLength(1); - expect(wrapper.find(ListAddField).prop('name')).toBe('$'); -}); - -test(' - renders correct label (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').text()).toEqual( - expect.stringContaining('ListFieldLabel'), - ); -}); - -test(' - renders correct numer of items with model (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined, undefined], - }), - ); - - expect(wrapper.find('input')).toHaveLength(3); -}); - -test(' - passes itemProps to its children', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined], - }), - ); - - expect(wrapper.find(ListItemField).first().prop('data-xyz')).toBe(1); -}); - -test(' - renders children (specified)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - PlainText - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(Child).toHaveBeenCalledTimes(2); -}); - -test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(Child).at(0).prop('name')).toBe('0'); - expect(wrapper.find(Child).at(1).prop('name')).toBe('1'); -}); - -test(' - renders children with correct name (value)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }, undefined, { - x: [undefined, undefined], - }), - ); - - expect(wrapper.find(ListItemField).at(0).prop('name')).toBe('0'); - expect(wrapper.find(ListItemField).at(1).prop('name')).toBe('1'); -}); - -test(' - renders proper number of optional values after add new value', () => { - const element = ; - const onChange = jest.fn(); - const wrapper = mount( - element, - createContext( - { x: { type: Array, optional: true }, 'x.$': { type: String } }, - { onChange }, - { - x: [undefined, undefined, undefined], - }, - ), - ); - expect(wrapper.find(ListAddField).simulate('click')).toBeTruthy(); - expect(onChange).toHaveBeenNthCalledWith(1, 'x', [ - undefined, - undefined, - undefined, - undefined, - ]); -}); diff --git a/packages/uniforms-unstyled/__tests__/index.ts b/packages/uniforms-unstyled/__tests__/index.ts index 98de11a8e..a420e2333 100644 --- a/packages/uniforms-unstyled/__tests__/index.ts +++ b/packages/uniforms-unstyled/__tests__/index.ts @@ -45,6 +45,7 @@ describe('@RTL', () => { suites.testListDelField(theme.ListDelField); suites.testListField(theme.ListField, { getListAddField: screen => screen.getByRole('button'), + testError: false, }); suites.testLongTextField(theme.LongTextField, { skipShowInlineErrorTests: true, diff --git a/packages/uniforms/__suites__/ListField.tsx b/packages/uniforms/__suites__/ListField.tsx index 1ff7fd040..3c534102d 100644 --- a/packages/uniforms/__suites__/ListField.tsx +++ b/packages/uniforms/__suites__/ListField.tsx @@ -6,7 +6,19 @@ import { render } from './render'; export function testListField( ListField: ComponentType, - { getListAddField }: { getListAddField: (screen: Screen) => HTMLElement }, + { + getListAddField, + disableInlineError, + testError = true, + testStyle, + testTooltip, + }: { + getListAddField: (screen: Screen) => HTMLElement; + disableInlineError?: boolean; + testError?: boolean; + testStyle?: boolean; + testTooltip?: boolean; + }, ) { test(' - renders ListAddField', () => { render(, { @@ -27,7 +39,66 @@ export function testListField( expect(screen.getByText(/ListFieldLabel.*/)).toBeInTheDocument(); }); - test(' - renders correct numer of items with model (specified)', () => { + if (testStyle) { + test(' - renders correct error style', () => { + const error = new Error(); + + render( + , + { + x: Array, + 'x.$': String, + }, + ); + + expect(screen.getByTestId('field')).toHaveStyle( + 'borderColor: rgb(255, 85, 0)', + ); + }); + + test(' - renders correct error style (with specified style prop)', () => { + const error = new Error(); + + render( + , + { + x: Array, + 'x.$': String, + }, + ); + + expect(screen.getByTestId('field')).toHaveStyle('marginLeft: 8px'); + }); + } + + if (testTooltip) { + test(' - renders correct info (specified)', () => { + const { container } = render( + , + { + x: Array, + 'x.$': String, + }, + ); + + expect( + container.getElementsByClassName('anticon-question-circle').length, + ).toBe(1); + }); + } + + test(' - renders correct number of items with model (specified)', () => { render( , { @@ -74,7 +145,7 @@ export function testListField( }); test(' - renders children with correct name (children)', () => { - const Child = jest.fn(() =>
) as FC; + const Child = jest.fn(() =>
) as FC; render( @@ -116,4 +187,46 @@ export function testListField( expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenLastCalledWith('x', [undefined]); }); + + if (testError) { + test(' - renders correct error text (specified)', async () => { + const error = new Error(); + + render( + , + { + x: Array, + 'x.$': String, + }, + ); + + expect(screen.getByText(/^Error$/)).toBeInTheDocument(); + }); + } + + if (disableInlineError) { + test(' - renders correct error text (showInlineError=false)', async () => { + const error = new Error(); + + render( + , + { + x: Array, + 'x.$': String, + }, + ); + + expect(screen.queryByText(/^Error$/)).not.toBeInTheDocument(); + }); + } } From 9d7cde00ee11eefbda51844ab3317bfb9d3b2a55 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:08:55 +0100 Subject: [PATCH 2/5] Migrated `connectField` tests to @testing-library/react (#1301) --- packages/uniforms/__tests__/connectField.tsx | 219 ++++++++++++------- 1 file changed, 138 insertions(+), 81 deletions(-) diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx index 4d44066e3..6865b8fff 100644 --- a/packages/uniforms/__tests__/connectField.tsx +++ b/packages/uniforms/__tests__/connectField.tsx @@ -1,54 +1,85 @@ -import React, { ReactNode } from 'react'; -import SimpleSchema from 'simpl-schema'; +import { fireEvent, screen } from '@testing-library/react'; +import omit from 'lodash/omit'; +import React from 'react'; import { BaseForm, Context, + OnChange, UnknownObject, connectField, + filterDOMProps, randomIds, } from 'uniforms'; -import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2'; -import mount from './_mount'; +import { render } from '../__suites__/render'; describe('connectField', () => { const onChange = jest.fn(); - const schema = new SimpleSchema2Bridge({ - schema: new SimpleSchema({ - another: { type: String, optional: true }, - field: { type: Object, label: 'Field' }, - 'field.subfield': { type: Number, label: 'Subfield' }, - }), - }); - const reactContext = { - context: { - changed: false, - changedMap: {}, - error: undefined, - model: {}, - name: [], - onChange, - onSubmit() {}, - randomId: randomIds(), - schema, - state: { - disabled: false, - placeholder: false, - readOnly: false, - showInlineError: true, - }, - submitted: false, - submitting: false, - validating: false, - formRef: {} as BaseForm, - } as Context, + const schema = { + another: { type: String, optional: true }, + field: { type: Object, label: 'Field' }, + 'field.subfield': { type: String, label: 'Subfield' }, }; - const Test = jest.fn(props => props.children || null); + const reactContext = { + changed: false, + changedMap: {}, + error: undefined, + model: {}, + name: [], + onChange, + onSubmit() {}, + randomId: randomIds(), + state: { + disabled: false, + readOnly: false, + showInlineError: true, + }, + submitted: false, + submitting: false, + validating: false, + formRef: {} as BaseForm, + } as Partial>; + + const Test = ( + props: UnknownObject & { + onChange: OnChange; + label?: string | React.ReactNode; + id: string; + }, + ) => { + return props.children ? ( + <> + {props.label && ( + + )} + props.onChange(event.target.value)} + /> + {props.children} + + ) : ( + <> + {props.label ? ( + + ) : null} + props.onChange(event.target.value)} + /> + + ); + }; - beforeEach(() => { - Test.mockClear(); + afterEach(() => { onChange.mockClear(); }); @@ -85,7 +116,7 @@ describe('connectField', () => { it('includes default value (true)', () => { const Field = connectField(Test, { initialValue: true }); - mount(, reactContext); + render(, schema, reactContext); expect(onChange).toBeCalledWith('field', {}); }); @@ -93,7 +124,7 @@ describe('connectField', () => { it('does nothing (false)', () => { const Field = connectField(Test, { initialValue: false }); - mount(, reactContext); + render(, schema, reactContext); expect(onChange).not.toBeCalled(); }); @@ -101,7 +132,7 @@ describe('connectField', () => { it('respects `required` (props)', () => { const Field = connectField(Test, { initialValue: true }); - mount(, reactContext); + render(, schema, reactContext); expect(onChange).not.toBeCalled(); }); @@ -109,7 +140,7 @@ describe('connectField', () => { it('respects `required` (schema)', () => { const Field = connectField(Test, { initialValue: true }); - mount(, reactContext); + render(, schema, reactContext); expect(onChange).not.toBeCalled(); }); @@ -119,7 +150,12 @@ describe('connectField', () => { it('treats value as initial value', async () => { const Field = connectField(Test); - mount(, reactContext); + render( + , + schema, + reactContext, + ); + await new Promise(resolve => setTimeout(resolve, 10)); expect(onChange).toBeCalledWith('field', 'initialValueExample'); @@ -127,17 +163,15 @@ describe('connectField', () => { }); describe('when rendered with label', () => { - const labelA = Error; - const labelB = OK; + const labelA = Label A; + const labelB = Label B; it.each([ ['Props', '', 'Props'], ['Props', 'Schema', 'Props'], - ['Props', labelB, 'Props'], ['Props', undefined, 'Props'], ['', undefined, ''], ['', 'Schema', ''], - ['', labelB, ''], ['', undefined, ''], [labelA, '', labelA], [labelA, 'Schema', labelA], @@ -147,48 +181,76 @@ describe('connectField', () => { [undefined, 'Schema', 'Schema'], [undefined, labelB, labelB], [undefined, undefined, ''], - ] as [ReactNode, ReactNode, ReactNode][])( - 'resolves it correctly (%#)', - (prop, schema, result) => { - const context: typeof reactContext = { - context: { - ...reactContext.context, - state: { ...reactContext.context.state }, - }, - }; - - jest - .spyOn(context.context.schema, 'getProps') - .mockReturnValueOnce({ label: schema }); - - const Field = connectField(Test); - const wrapper = mount(, context); - expect(wrapper.find(Test).prop('label')).toBe(result); - }, - ); + ])('resolves it correctly (%#)', (propLabel, schemaLabel, resultLabel) => { + const schema = { + another: { type: String, optional: true }, + field: { type: Object, label: schemaLabel }, + 'field.subfield': { type: String, label: 'Subfield' }, + }; + + const Field = connectField(Test); + render( + , + // @ts-expect-error + schema, + reactContext, + ); + + if (resultLabel === labelA) { + expect(screen.getByText('Label A')).toBeInTheDocument(); + } else if (resultLabel === labelB) { + expect(screen.getByText('Label B')).toBeInTheDocument(); + } else { + const result = resultLabel as string; + + if (result) { + expect(screen.getByText(result)).toBeInTheDocument(); + } else { + expect(screen.queryByTestId('label')).toBe(null); + } + } + }); }); describe('when rendered provides correct onChange', () => { it('is defaults to field name', () => { const Field = connectField(Test); + + render(, schema, reactContext); + const value = 'some value'; - const wrapper = mount(, reactContext); - wrapper.find(Test).prop('onChange')(value); + const input = screen.getByTestId('field'); + fireEvent.change(input, { target: { value } }); + expect(onChange).toBeCalledWith('another', value); }); it('is able to set another field value', () => { - const Field = connectField(Test); - const value = { subfield: 123 }; - const wrapper = mount(, reactContext); - wrapper.find(Test).prop('onChange')(value, 'field'); - expect(onChange).toBeCalledWith('field', value); + const Field = connectField((props: any) => ( + + props.onChange(event.target.value, 'field.subfield') + } + /> + )); + + render(, schema, reactContext); + + const input = screen.getByTestId('field'); + const value = 'test'; + + fireEvent.change(input, { target: { value } }); + + expect(onChange).toBeCalledWith('field.subfield', value); }); }); it('works with nested labels', () => { const Field = connectField(Test); - const wrapper = mount( + + render( @@ -202,17 +264,12 @@ describe('connectField', () => { , + schema, reactContext, ); - expect(wrapper.find(Test).at(0).prop('label')).toBe('Field'); - expect(wrapper.find(Test).at(1).prop('label')).toBe(''); - expect(wrapper.find(Test).at(2).prop('label')).toBe('Field'); - expect(wrapper.find(Test).at(3).prop('label')).toBe('Other'); - expect(wrapper.find(Test).at(4).prop('label')).toBe('Subfield'); - expect(wrapper.find(Test).at(5).prop('label')).toBe('Subfield'); - expect(wrapper.find(Test).at(6).prop('label')).toBe('Subfield'); - expect(wrapper.find(Test).at(7).prop('label')).toBe('Subfield'); - expect(wrapper.find(Test).at(8).prop('label')).toBe(''); + expect(screen.getAllByText('Field')).toHaveLength(2); + expect(screen.getAllByText('Subfield')).toHaveLength(4); + expect(screen.getAllByText('Other')).toHaveLength(1); }); }); From e79b6eb8d8244b8202886fbcf78a48a6f1cc1c46 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:09:09 +0100 Subject: [PATCH 3/5] Migrated `QuickForm` tests to @testing-library/react (#1302) --- packages/uniforms/__suites__/QuickForm.tsx | 8 +-- packages/uniforms/__tests__/QuickForm.tsx | 82 +++++++++++++--------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/packages/uniforms/__suites__/QuickForm.tsx b/packages/uniforms/__suites__/QuickForm.tsx index a13b0a6ef..c0f17e639 100644 --- a/packages/uniforms/__suites__/QuickForm.tsx +++ b/packages/uniforms/__suites__/QuickForm.tsx @@ -4,10 +4,10 @@ import { ZodBridge } from 'uniforms-bridge-zod'; import z from 'zod'; export function testQuickForm(QuickForm: ComponentType) { + const bridge = 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__/QuickForm.tsx b/packages/uniforms/__tests__/QuickForm.tsx index b1a0cb6cd..f0b9ff76d 100644 --- a/packages/uniforms/__tests__/QuickForm.tsx +++ b/packages/uniforms/__tests__/QuickForm.tsx @@ -1,82 +1,96 @@ +import { render } from '@testing-library/react'; import React from 'react'; -import SimpleSchema from 'simpl-schema'; import { QuickForm } from 'uniforms'; -import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2'; - -import mount from './_mount'; +import { ZodBridge } from 'uniforms-bridge-zod'; +import { z } from 'zod'; describe('QuickForm', () => { + const AutoField = jest.fn(() => null) as any; + const ErrorsField = jest.fn(() => null) as any; + const SubmitField = jest.fn(() => null) as any; + // @ts-expect-error QuickForm is not a valid Component. class TestForm extends QuickForm { // eslint-disable-next-line react/display-name - getAutoField = () => () => ; + getAutoField = () => () => ; // eslint-disable-next-line react/display-name - getErrorsField = () => () => ; + getErrorsField = () => () => ; // eslint-disable-next-line react/display-name - getSubmitField = () => () => ; + getSubmitField = () => () => ; } - const schema = new SimpleSchema2Bridge({ - schema: new SimpleSchema({ - a: String, - b: String, - c: String, - }), + const schema = z.object({ + a: z.string(), + b: z.string(), + c: z.string(), + }); + const bridge = new ZodBridge({ schema }); + + afterEach(() => { + AutoField.mockClear(); + ErrorsField.mockClear(); + SubmitField.mockClear(); }); describe('when rendered with custom fields', () => { it('renders `AutoField` for each field', () => { - const wrapper = mount(); + render(); - expect(wrapper.find('.auto').length).toBeGreaterThan(0); + expect(AutoField).toHaveBeenCalledTimes(3); }); it('renders `ErrorsField`', () => { - const wrapper = mount(); + render(); - expect(wrapper.find('.errors').length).toBeGreaterThan(0); + expect(ErrorsField).toHaveBeenCalledTimes(1); }); it('renders `SubmitField`', () => { - const wrapper = mount(); + render(); - expect(wrapper.find('.submit').length).toBeGreaterThan(0); + expect(SubmitField).toHaveBeenCalledTimes(1); }); }); describe('when rendered with custom fields in `props`', () => { it('renders `ErrorsField`', () => { - const wrapper = mount( + const ErrorsOverrideField = jest.fn(() =>
) as React.FC; + + render( } + schema={bridge} + errorsField={() => } />, ); - expect(wrapper.find('.errorsOverride').length).toBeGreaterThan(0); + expect(ErrorsOverrideField).toHaveBeenCalledTimes(1); }); it('renders `SubmitField`', () => { - const wrapper = mount( + const SubmitOverrideField = jest.fn(() =>
) as React.FC; + + render( } + schema={bridge} + submitField={() => } />, ); - expect(wrapper.find('.submitOverride').length).toBeGreaterThan(0); + expect(SubmitOverrideField).toHaveBeenCalledTimes(1); }); }); describe('when rendered with children', () => { - const wrapper = mount( - -
- , - ); - it('renders children', () => { - expect(wrapper.find('div')).toHaveLength(1); + const Child = jest.fn(() =>
) as React.FC; + + render( + + + , + ); + + expect(Child).toHaveBeenCalledTimes(1); }); }); }); 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 4/5] 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); }); }); }); From eed59c6e6b67b10a7ba2e996bb4467d349b43792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zendran?= <37419008+zendranm@users.noreply.github.com> Date: Fri, 23 Feb 2024 12:15:13 +0100 Subject: [PATCH 5/5] Migrated `ListItemField` tests to @testing-library/react (#1304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr Pośpiech --- .../uniforms-antd/__tests__/ListItemField.tsx | 52 ------------------- packages/uniforms-antd/__tests__/index.ts | 1 + .../__tests__/ListItemField.tsx | 52 ------------------- .../uniforms-bootstrap3/__tests__/index.ts | 1 + .../__tests__/ListItemField.tsx | 52 ------------------- .../uniforms-bootstrap4/__tests__/index.ts | 1 + .../__tests__/ListItemField.tsx | 52 ------------------- .../uniforms-bootstrap5/__tests__/index.ts | 1 + .../__tests__/ListItemField.tsx | 52 ------------------- packages/uniforms-material/__tests__/index.ts | 1 + .../uniforms-mui/__tests__/ListItemField.tsx | 52 ------------------- packages/uniforms-mui/__tests__/index.ts | 1 + .../__tests__/ListItemField.tsx | 52 ------------------- packages/uniforms-semantic/__tests__/index.ts | 1 + .../__tests__/ListItemField.tsx | 52 ------------------- packages/uniforms-unstyled/__tests__/index.ts | 1 + .../uniforms/__suites__/ListItemField.tsx | 40 ++++++++++++++ packages/uniforms/__suites__/index.ts | 1 + 18 files changed, 49 insertions(+), 416 deletions(-) delete mode 100644 packages/uniforms-antd/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-bootstrap3/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-bootstrap4/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-bootstrap5/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-material/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-mui/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-semantic/__tests__/ListItemField.tsx delete mode 100644 packages/uniforms-unstyled/__tests__/ListItemField.tsx create mode 100644 packages/uniforms/__suites__/ListItemField.tsx diff --git a/packages/uniforms-antd/__tests__/ListItemField.tsx b/packages/uniforms-antd/__tests__/ListItemField.tsx deleted file mode 100644 index 1a68cea18..000000000 --- a/packages/uniforms-antd/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-antd'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-antd/__tests__/index.ts b/packages/uniforms-antd/__tests__/index.ts index 205f5d766..0d5c364bc 100644 --- a/packages/uniforms-antd/__tests__/index.ts +++ b/packages/uniforms-antd/__tests__/index.ts @@ -50,6 +50,7 @@ describe('@RTL', () => { testTooltip: true, testStyle: true, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField); // FIXME: AntD number input doesn't work with new RTL test implementation // suites.testNumField(antd.NumField); diff --git a/packages/uniforms-bootstrap3/__tests__/ListItemField.tsx b/packages/uniforms-bootstrap3/__tests__/ListItemField.tsx deleted file mode 100644 index b8f55cafb..000000000 --- a/packages/uniforms-bootstrap3/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-bootstrap3'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-bootstrap3/__tests__/index.ts b/packages/uniforms-bootstrap3/__tests__/index.ts index 94fa7abc6..fb7d76594 100644 --- a/packages/uniforms-bootstrap3/__tests__/index.ts +++ b/packages/uniforms-bootstrap3/__tests__/index.ts @@ -49,6 +49,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByRole('button'), disableInlineError: true, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField); suites.testNumField(theme.NumField); diff --git a/packages/uniforms-bootstrap4/__tests__/ListItemField.tsx b/packages/uniforms-bootstrap4/__tests__/ListItemField.tsx deleted file mode 100644 index 3bc76866e..000000000 --- a/packages/uniforms-bootstrap4/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-bootstrap4'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-bootstrap4/__tests__/index.ts b/packages/uniforms-bootstrap4/__tests__/index.ts index 84249a4e7..31e0a2b56 100644 --- a/packages/uniforms-bootstrap4/__tests__/index.ts +++ b/packages/uniforms-bootstrap4/__tests__/index.ts @@ -49,6 +49,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByRole('button'), disableInlineError: true, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField); suites.testNumField(theme.NumField); diff --git a/packages/uniforms-bootstrap5/__tests__/ListItemField.tsx b/packages/uniforms-bootstrap5/__tests__/ListItemField.tsx deleted file mode 100644 index c105a0ba2..000000000 --- a/packages/uniforms-bootstrap5/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-bootstrap5'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-bootstrap5/__tests__/index.ts b/packages/uniforms-bootstrap5/__tests__/index.ts index 5248b5b72..6e036c834 100644 --- a/packages/uniforms-bootstrap5/__tests__/index.ts +++ b/packages/uniforms-bootstrap5/__tests__/index.ts @@ -49,6 +49,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByRole('button'), disableInlineError: true, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField, { testMinMaxLength: true, }); diff --git a/packages/uniforms-material/__tests__/ListItemField.tsx b/packages/uniforms-material/__tests__/ListItemField.tsx deleted file mode 100644 index 996d39dbb..000000000 --- a/packages/uniforms-material/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-material'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-material/__tests__/index.ts b/packages/uniforms-material/__tests__/index.ts index c3e9643e9..a74821372 100644 --- a/packages/uniforms-material/__tests__/index.ts +++ b/packages/uniforms-material/__tests__/index.ts @@ -51,6 +51,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByText(/\+/), testError: false, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField, { skipInMuiTests: true }); suites.testNumField(theme.NumField); diff --git a/packages/uniforms-mui/__tests__/ListItemField.tsx b/packages/uniforms-mui/__tests__/ListItemField.tsx deleted file mode 100644 index 25de0fd8e..000000000 --- a/packages/uniforms-mui/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-mui'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-mui/__tests__/index.ts b/packages/uniforms-mui/__tests__/index.ts index a1c7dfdae..db660ef77 100644 --- a/packages/uniforms-mui/__tests__/index.ts +++ b/packages/uniforms-mui/__tests__/index.ts @@ -48,6 +48,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByText(/\+/), testError: false, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField, { skipInMuiTests: true }); suites.testNumField(theme.NumField); diff --git a/packages/uniforms-semantic/__tests__/ListItemField.tsx b/packages/uniforms-semantic/__tests__/ListItemField.tsx deleted file mode 100644 index 2c4224268..000000000 --- a/packages/uniforms-semantic/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-semantic'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child = jest.fn(() =>
) as React.FC; - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-semantic/__tests__/index.ts b/packages/uniforms-semantic/__tests__/index.ts index 0a4d2734d..0713cec2f 100644 --- a/packages/uniforms-semantic/__tests__/index.ts +++ b/packages/uniforms-semantic/__tests__/index.ts @@ -47,6 +47,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByRole('button'), disableInlineError: true, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField); suites.testNestField(theme.NestField); suites.testNumField(theme.NumField); diff --git a/packages/uniforms-unstyled/__tests__/ListItemField.tsx b/packages/uniforms-unstyled/__tests__/ListItemField.tsx deleted file mode 100644 index 6e9970e9b..000000000 --- a/packages/uniforms-unstyled/__tests__/ListItemField.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { AutoField, ListDelField, ListItemField } from 'uniforms-unstyled'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - works', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListItemField)).toHaveLength(1); -}); - -test(' - renders ListDelField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(ListDelField)).toHaveLength(1); - expect(wrapper.find(ListDelField).childAt(0).prop('name')).toBe('x.1'); -}); - -test(' - renders AutoField', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(wrapper.find(AutoField)).toHaveLength(1); -}); - -test(' - renders children if specified', () => { - const Child: () => null = jest.fn(() => null); - - const element = ( - - - - ); - mount( - element, - createContext({ x: { type: Array }, 'x.$': { type: String } }), - ); - - expect(Child).toHaveBeenCalledTimes(1); -}); diff --git a/packages/uniforms-unstyled/__tests__/index.ts b/packages/uniforms-unstyled/__tests__/index.ts index a420e2333..7df73ce41 100644 --- a/packages/uniforms-unstyled/__tests__/index.ts +++ b/packages/uniforms-unstyled/__tests__/index.ts @@ -47,6 +47,7 @@ describe('@RTL', () => { getListAddField: screen => screen.getByRole('button'), testError: false, }); + suites.testListItemField(theme.ListItemField); suites.testLongTextField(theme.LongTextField, { skipShowInlineErrorTests: true, }); diff --git a/packages/uniforms/__suites__/ListItemField.tsx b/packages/uniforms/__suites__/ListItemField.tsx new file mode 100644 index 000000000..14eb4b34a --- /dev/null +++ b/packages/uniforms/__suites__/ListItemField.tsx @@ -0,0 +1,40 @@ +import { screen } from '@testing-library/react'; +import React, { ComponentType } from 'react'; +import z from 'zod'; + +import { renderWithZod } from './render-zod'; + +export function testListItemField(ListItemField: ComponentType) { + test(' - works', () => { + renderWithZod({ + element: , + schema: z.object({ field: z.string().optional() }), + }); + + expect(screen.getByLabelText('Field')).toBeInTheDocument(); + }); + + test(' - renders ListDelField', () => { + renderWithZod({ + element: , + schema: z.object({ field: z.string() }), + }); + + expect(screen.getByRole('button')).toBeInTheDocument(); + }); + + test(' - renders children if specified', () => { + const Child = jest.fn(() =>
) as React.FC; + + renderWithZod({ + element: ( + + + + ), + schema: z.object({ field: z.string() }), + }); + + expect(Child).toHaveBeenCalledTimes(1); + }); +} diff --git a/packages/uniforms/__suites__/index.ts b/packages/uniforms/__suites__/index.ts index 20a088254..04d72395f 100644 --- a/packages/uniforms/__suites__/index.ts +++ b/packages/uniforms/__suites__/index.ts @@ -10,6 +10,7 @@ export * from './HiddenField'; export * from './ListAddField'; export * from './ListDelField'; export * from './ListField'; +export * from './ListItemField'; export * from './LongTextField'; export * from './NestField'; export * from './NumField';