From de54b81d82fc21ec4cc6bc165883f778f59f14aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Wed, 14 Feb 2024 15:07:03 +0100 Subject: [PATCH 01/11] migrated connectField tests to RTL suites --- packages/uniforms/__suites__/connectField.tsx | 239 ++++++++++++++++++ packages/uniforms/__suites__/index.ts | 1 + packages/uniforms/__tests__/index.ts | 5 + 3 files changed, 245 insertions(+) create mode 100644 packages/uniforms/__suites__/connectField.tsx diff --git a/packages/uniforms/__suites__/connectField.tsx b/packages/uniforms/__suites__/connectField.tsx new file mode 100644 index 000000000..74f4f936a --- /dev/null +++ b/packages/uniforms/__suites__/connectField.tsx @@ -0,0 +1,239 @@ +import { fireEvent, screen } from '@testing-library/react'; +import { omit } from 'lodash'; +import React from 'react'; +import { + BaseForm, + Context, + OnChange, + UnknownObject, + connectField, + filterDOMProps, + randomIds, +} from 'uniforms'; + +import { render } from './render'; + +export function testConnectField() { + const onChange = jest.fn(); + + const schema = { + another: { type: String, optional: true }, + field: { type: Object, label: 'Field' }, + 'field.subfield': { type: String, label: 'Subfield' }, + }; + + 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 }) => { + return props.children ? ( + <> + props.onChange(event.target.value)} + /> + {props.children} + + ) : ( + props.onChange(event.target.value)} + /> + ); + }; + + beforeEach(() => { + onChange.mockClear(); + }); + + test('connectField - when called, creates component', () => { + const Field = connectField(Test); + + expect(Field).toBeInstanceOf(Function); + expect(Field.Component).toEqual(Test); + }); + + test('connectField - when called, does not set default value', () => { + const Field = connectField(Test); + + expect(Field.options).toEqual(undefined); + }); + + test('connectField - when called with `kind`, includes options object with `kind` value (leaf)', () => { + const Field = connectField(Test, { kind: 'leaf' }); + + expect(Field.options).toEqual({ kind: 'leaf' }); + }); + + test('connectField - when called with `kind`, includes options object with `kind` value (node)', () => { + const Field = connectField(Test, { kind: 'node' }); + + expect(Field.options).toEqual({ kind: 'node' }); + }); + + test('connectField - when called with `initialValue`, includes default value (true)', () => { + const Field = connectField(Test, { initialValue: true }); + + render(, schema, reactContext); + + expect(onChange).toBeCalledWith('field', {}); + }); + + test('connectField - when called with `initialValue`, does nothing (false)', () => { + const Field = connectField(Test, { initialValue: false }); + + render(, schema, reactContext); + + expect(onChange).not.toBeCalled(); + }); + + test('connectField - when called with `initialValue`, respects `required` (props)', () => { + const Field = connectField(Test, { initialValue: true }); + + render(, schema, reactContext); + + expect(onChange).not.toBeCalled(); + }); + + test('connectField - when called with `initialValue`, respects `required` (schema)', () => { + const Field = connectField(Test, { initialValue: true }); + + render(, schema, reactContext); + + expect(onChange).not.toBeCalled(); + }); + + test('connectField - when rendered with value, treats value as initial value', async () => { + const Field = connectField(Test); + + render( + , + schema, + reactContext, + ); + + await new Promise(resolve => setTimeout(resolve, 10)); + + expect(onChange).toBeCalledWith('field', 'initialValueExample'); + }); + + test('connectField - when rendered provides correct onChange, is defaults to field name', () => { + const Field = connectField(Test); + + render(, schema, reactContext); + + const value = 'some value'; + const input = screen.getByTestId('field'); + fireEvent.change(input, { + target: { value }, + }); + + expect(onChange).toBeCalledWith('another', value); + }); + + test('connectField - when rendered provides correct onChange, is able to set another 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); + }); + + test('connectField - works with nested labels', () => { + const Field = connectField(Test); + + render( + + + + + + + + + + + + + , + schema, + reactContext, + ); + + expect(screen.getByTestId('1')).toHaveAttribute('label', 'Field'); + expect(screen.getByTestId('2')).toHaveAttribute('label', ''); + expect(screen.getByTestId('3')).toHaveAttribute('label', 'Field'); + expect(screen.getByTestId('4')).toHaveAttribute('label', 'Other'); + expect(screen.getByTestId('5')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('6')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('7')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('8')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('9')).toHaveAttribute('label', ''); + }); + + test('connectField - when rendered with label', async () => { + const array = [ + ['Props', '', 'Props'], + ['Props', 'Schema', 'Props'], + ['Props', undefined, 'Props'], + ['', undefined, ''], + ['', 'Schema', ''], + ['', undefined, ''], + [undefined, '', ''], + [undefined, 'Schema', 'Schema'], + [undefined, undefined, ''], + ]; + + array.map(([propLabel, schemaLabel, resultLabel], index) => { + const schema = { + another: { type: String, optional: true }, + field: { type: Object, label: schemaLabel }, + 'field.subfield': { type: String, label: 'Subfield' }, + }; + + const Field = connectField(Test); + render( + , + schema, + reactContext, + ); + + expect(screen.getByTestId(index.toString())).toHaveAttribute( + 'label', + resultLabel, + ); + }); + }); +} diff --git a/packages/uniforms/__suites__/index.ts b/packages/uniforms/__suites__/index.ts index 20a088254..6b225d917 100644 --- a/packages/uniforms/__suites__/index.ts +++ b/packages/uniforms/__suites__/index.ts @@ -3,6 +3,7 @@ export * from './AutoFields'; export * from './AutoForm'; export * from './BaseForm'; export * from './BoolField'; +export * from './connectField'; export * from './DateField'; export * from './ErrorField'; export * from './ErrorsField'; diff --git a/packages/uniforms/__tests__/index.ts b/packages/uniforms/__tests__/index.ts index 263a65479..6ced7c075 100644 --- a/packages/uniforms/__tests__/index.ts +++ b/packages/uniforms/__tests__/index.ts @@ -1,4 +1,5 @@ import * as uniforms from 'uniforms'; +import * as suites from 'uniforms/__suites__'; it('exports everything', () => { expect(uniforms).toMatchObject({ @@ -18,3 +19,7 @@ it('exports everything', () => { useForm: expect.any(Function), }); }); + +describe('@RTL', () => { + suites.testConnectField(); +}); From fb92107aa914d667d2d6a7ccfa035ffff79264cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Wed, 14 Feb 2024 15:25:29 +0100 Subject: [PATCH 02/11] removed enzyme connectField tests --- packages/uniforms/__tests__/connectField.tsx | 218 ------------------- 1 file changed, 218 deletions(-) delete mode 100644 packages/uniforms/__tests__/connectField.tsx diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx deleted file mode 100644 index 4d44066e3..000000000 --- a/packages/uniforms/__tests__/connectField.tsx +++ /dev/null @@ -1,218 +0,0 @@ -import React, { ReactNode } from 'react'; -import SimpleSchema from 'simpl-schema'; -import { - BaseForm, - Context, - UnknownObject, - connectField, - randomIds, -} from 'uniforms'; -import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2'; - -import mount from './_mount'; - -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 Test = jest.fn(props => props.children || null); - - beforeEach(() => { - Test.mockClear(); - onChange.mockClear(); - }); - - describe('when called', () => { - it('creates component', () => { - const Field = connectField(Test); - - expect(Field).toBeInstanceOf(Function); - expect(Field.Component).toEqual(Test); - }); - }); - - describe('when called with `kind`', () => { - it('does not set default value', () => { - const Field = connectField(Test); - - expect(Field.options).toEqual(undefined); - }); - - it('includes options object with `kind` value (leaf)', () => { - const Field = connectField(Test, { kind: 'leaf' }); - - expect(Field.options).toEqual({ kind: 'leaf' }); - }); - - it('includes options object with `kind` value (node)', () => { - const Field = connectField(Test, { kind: 'node' }); - - expect(Field.options).toEqual({ kind: 'node' }); - }); - }); - - describe('when called with `initialValue`', () => { - it('includes default value (true)', () => { - const Field = connectField(Test, { initialValue: true }); - - mount(, reactContext); - - expect(onChange).toBeCalledWith('field', {}); - }); - - it('does nothing (false)', () => { - const Field = connectField(Test, { initialValue: false }); - - mount(, reactContext); - - expect(onChange).not.toBeCalled(); - }); - - it('respects `required` (props)', () => { - const Field = connectField(Test, { initialValue: true }); - - mount(, reactContext); - - expect(onChange).not.toBeCalled(); - }); - - it('respects `required` (schema)', () => { - const Field = connectField(Test, { initialValue: true }); - - mount(, reactContext); - - expect(onChange).not.toBeCalled(); - }); - }); - - describe('when rendered with value', () => { - it('treats value as initial value', async () => { - const Field = connectField(Test); - - mount(, reactContext); - await new Promise(resolve => setTimeout(resolve, 10)); - - expect(onChange).toBeCalledWith('field', 'initialValueExample'); - }); - }); - - describe('when rendered with label', () => { - const labelA = Error; - const labelB = OK; - - it.each([ - ['Props', '', 'Props'], - ['Props', 'Schema', 'Props'], - ['Props', labelB, 'Props'], - ['Props', undefined, 'Props'], - ['', undefined, ''], - ['', 'Schema', ''], - ['', labelB, ''], - ['', undefined, ''], - [labelA, '', labelA], - [labelA, 'Schema', labelA], - [labelA, labelB, labelA], - [labelA, undefined, labelA], - [undefined, '', ''], - [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); - }, - ); - }); - - describe('when rendered provides correct onChange', () => { - it('is defaults to field name', () => { - const Field = connectField(Test); - const value = 'some value'; - const wrapper = mount(, reactContext); - wrapper.find(Test).prop('onChange')(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); - }); - }); - - it('works with nested labels', () => { - const Field = connectField(Test); - const wrapper = mount( - - - - - - - - - - - - - , - 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(''); - }); -}); From f1a9e5deb20c1deace01e0c986da7d66c3f801e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Wed, 14 Feb 2024 15:28:28 +0100 Subject: [PATCH 03/11] refactor import --- packages/uniforms/__suites__/connectField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uniforms/__suites__/connectField.tsx b/packages/uniforms/__suites__/connectField.tsx index 74f4f936a..823d67c68 100644 --- a/packages/uniforms/__suites__/connectField.tsx +++ b/packages/uniforms/__suites__/connectField.tsx @@ -1,5 +1,5 @@ import { fireEvent, screen } from '@testing-library/react'; -import { omit } from 'lodash'; +import omit from 'lodash/omit'; import React from 'react'; import { BaseForm, From 9c42aa7d35bf2218856f6a09ee023a7493e3055f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Wed, 14 Feb 2024 15:32:40 +0100 Subject: [PATCH 04/11] refactor --- packages/uniforms/__suites__/connectField.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/uniforms/__suites__/connectField.tsx b/packages/uniforms/__suites__/connectField.tsx index 823d67c68..5c6eefcd8 100644 --- a/packages/uniforms/__suites__/connectField.tsx +++ b/packages/uniforms/__suites__/connectField.tsx @@ -143,9 +143,7 @@ export function testConnectField() { const value = 'some value'; const input = screen.getByTestId('field'); - fireEvent.change(input, { - target: { value }, - }); + fireEvent.change(input, { target: { value } }); expect(onChange).toBeCalledWith('another', value); }); @@ -164,9 +162,7 @@ export function testConnectField() { const input = screen.getByTestId('field'); const value = 'test'; - fireEvent.change(input, { - target: { value }, - }); + fireEvent.change(input, { target: { value } }); expect(onChange).toBeCalledWith('field.subfield', value); }); From b10039152b227b67338283797f5fcc7bd02f05a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Wed, 14 Feb 2024 15:35:35 +0100 Subject: [PATCH 05/11] refactor --- packages/uniforms/__suites__/connectField.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/uniforms/__suites__/connectField.tsx b/packages/uniforms/__suites__/connectField.tsx index 5c6eefcd8..39d0932c6 100644 --- a/packages/uniforms/__suites__/connectField.tsx +++ b/packages/uniforms/__suites__/connectField.tsx @@ -200,7 +200,7 @@ export function testConnectField() { }); test('connectField - when rendered with label', async () => { - const array = [ + const labelVariants = [ ['Props', '', 'Props'], ['Props', 'Schema', 'Props'], ['Props', undefined, 'Props'], @@ -212,7 +212,7 @@ export function testConnectField() { [undefined, undefined, ''], ]; - array.map(([propLabel, schemaLabel, resultLabel], index) => { + labelVariants.map(([propLabel, schemaLabel, resultLabel], index) => { const schema = { another: { type: String, optional: true }, field: { type: Object, label: schemaLabel }, From a54a3620945eb23a82209aa071c69bbf8d9e0e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Thu, 15 Feb 2024 09:09:24 +0100 Subject: [PATCH 06/11] refactor --- packages/uniforms/__suites__/connectField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/uniforms/__suites__/connectField.tsx b/packages/uniforms/__suites__/connectField.tsx index 39d0932c6..f3a02dec2 100644 --- a/packages/uniforms/__suites__/connectField.tsx +++ b/packages/uniforms/__suites__/connectField.tsx @@ -72,7 +72,7 @@ export function testConnectField() { expect(Field.Component).toEqual(Test); }); - test('connectField - when called, does not set default value', () => { + test('connectField - when called, does not set default options value', () => { const Field = connectField(Test); expect(Field.options).toEqual(undefined); From 7887a20d7c93897a08444c7a28429e96eab6d5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Fri, 16 Feb 2024 13:14:05 +0100 Subject: [PATCH 07/11] moved test from suites to tests --- packages/uniforms/__suites__/connectField.tsx | 235 ----------------- packages/uniforms/__suites__/index.ts | 1 - packages/uniforms/__tests__/connectField.tsx | 242 ++++++++++++++++++ packages/uniforms/__tests__/index.ts | 5 - 4 files changed, 242 insertions(+), 241 deletions(-) delete mode 100644 packages/uniforms/__suites__/connectField.tsx create mode 100644 packages/uniforms/__tests__/connectField.tsx diff --git a/packages/uniforms/__suites__/connectField.tsx b/packages/uniforms/__suites__/connectField.tsx deleted file mode 100644 index f3a02dec2..000000000 --- a/packages/uniforms/__suites__/connectField.tsx +++ /dev/null @@ -1,235 +0,0 @@ -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 { render } from './render'; - -export function testConnectField() { - const onChange = jest.fn(); - - const schema = { - another: { type: String, optional: true }, - field: { type: Object, label: 'Field' }, - 'field.subfield': { type: String, label: 'Subfield' }, - }; - - 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 }) => { - return props.children ? ( - <> - props.onChange(event.target.value)} - /> - {props.children} - - ) : ( - props.onChange(event.target.value)} - /> - ); - }; - - beforeEach(() => { - onChange.mockClear(); - }); - - test('connectField - when called, creates component', () => { - const Field = connectField(Test); - - expect(Field).toBeInstanceOf(Function); - expect(Field.Component).toEqual(Test); - }); - - test('connectField - when called, does not set default options value', () => { - const Field = connectField(Test); - - expect(Field.options).toEqual(undefined); - }); - - test('connectField - when called with `kind`, includes options object with `kind` value (leaf)', () => { - const Field = connectField(Test, { kind: 'leaf' }); - - expect(Field.options).toEqual({ kind: 'leaf' }); - }); - - test('connectField - when called with `kind`, includes options object with `kind` value (node)', () => { - const Field = connectField(Test, { kind: 'node' }); - - expect(Field.options).toEqual({ kind: 'node' }); - }); - - test('connectField - when called with `initialValue`, includes default value (true)', () => { - const Field = connectField(Test, { initialValue: true }); - - render(, schema, reactContext); - - expect(onChange).toBeCalledWith('field', {}); - }); - - test('connectField - when called with `initialValue`, does nothing (false)', () => { - const Field = connectField(Test, { initialValue: false }); - - render(, schema, reactContext); - - expect(onChange).not.toBeCalled(); - }); - - test('connectField - when called with `initialValue`, respects `required` (props)', () => { - const Field = connectField(Test, { initialValue: true }); - - render(, schema, reactContext); - - expect(onChange).not.toBeCalled(); - }); - - test('connectField - when called with `initialValue`, respects `required` (schema)', () => { - const Field = connectField(Test, { initialValue: true }); - - render(, schema, reactContext); - - expect(onChange).not.toBeCalled(); - }); - - test('connectField - when rendered with value, treats value as initial value', async () => { - const Field = connectField(Test); - - render( - , - schema, - reactContext, - ); - - await new Promise(resolve => setTimeout(resolve, 10)); - - expect(onChange).toBeCalledWith('field', 'initialValueExample'); - }); - - test('connectField - when rendered provides correct onChange, is defaults to field name', () => { - const Field = connectField(Test); - - render(, schema, reactContext); - - const value = 'some value'; - const input = screen.getByTestId('field'); - fireEvent.change(input, { target: { value } }); - - expect(onChange).toBeCalledWith('another', value); - }); - - test('connectField - when rendered provides correct onChange, is able to set another 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); - }); - - test('connectField - works with nested labels', () => { - const Field = connectField(Test); - - render( - - - - - - - - - - - - - , - schema, - reactContext, - ); - - expect(screen.getByTestId('1')).toHaveAttribute('label', 'Field'); - expect(screen.getByTestId('2')).toHaveAttribute('label', ''); - expect(screen.getByTestId('3')).toHaveAttribute('label', 'Field'); - expect(screen.getByTestId('4')).toHaveAttribute('label', 'Other'); - expect(screen.getByTestId('5')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('6')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('7')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('8')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('9')).toHaveAttribute('label', ''); - }); - - test('connectField - when rendered with label', async () => { - const labelVariants = [ - ['Props', '', 'Props'], - ['Props', 'Schema', 'Props'], - ['Props', undefined, 'Props'], - ['', undefined, ''], - ['', 'Schema', ''], - ['', undefined, ''], - [undefined, '', ''], - [undefined, 'Schema', 'Schema'], - [undefined, undefined, ''], - ]; - - labelVariants.map(([propLabel, schemaLabel, resultLabel], index) => { - const schema = { - another: { type: String, optional: true }, - field: { type: Object, label: schemaLabel }, - 'field.subfield': { type: String, label: 'Subfield' }, - }; - - const Field = connectField(Test); - render( - , - schema, - reactContext, - ); - - expect(screen.getByTestId(index.toString())).toHaveAttribute( - 'label', - resultLabel, - ); - }); - }); -} diff --git a/packages/uniforms/__suites__/index.ts b/packages/uniforms/__suites__/index.ts index 6b225d917..20a088254 100644 --- a/packages/uniforms/__suites__/index.ts +++ b/packages/uniforms/__suites__/index.ts @@ -3,7 +3,6 @@ export * from './AutoFields'; export * from './AutoForm'; export * from './BaseForm'; export * from './BoolField'; -export * from './connectField'; export * from './DateField'; export * from './ErrorField'; export * from './ErrorsField'; diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx new file mode 100644 index 000000000..7d59460b4 --- /dev/null +++ b/packages/uniforms/__tests__/connectField.tsx @@ -0,0 +1,242 @@ +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 { render } from '../__suites__/render'; + +describe('connectField', () => { + const onChange = jest.fn(); + + const schema = { + another: { type: String, optional: true }, + field: { type: Object, label: 'Field' }, + 'field.subfield': { type: String, label: 'Subfield' }, + }; + + 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 }) => { + return props.children ? ( + <> + props.onChange(event.target.value)} + /> + {props.children} + + ) : ( + props.onChange(event.target.value)} + /> + ); + }; + + afterEach(() => { + onChange.mockClear(); + }); + + describe('when called', () => { + it('creates component', () => { + const Field = connectField(Test); + + expect(Field).toBeInstanceOf(Function); + expect(Field.Component).toEqual(Test); + }); + }); + + describe('when called with `kind`', () => { + it('does not set default value', () => { + const Field = connectField(Test); + + expect(Field.options).toEqual(undefined); + }); + + it('includes options object with `kind` value (leaf)', () => { + const Field = connectField(Test, { kind: 'leaf' }); + + expect(Field.options).toEqual({ kind: 'leaf' }); + }); + + it('includes options object with `kind` value (node)', () => { + const Field = connectField(Test, { kind: 'node' }); + + expect(Field.options).toEqual({ kind: 'node' }); + }); + }); + + describe('when called with `initialValue`', () => { + it('includes default value (true)', () => { + const Field = connectField(Test, { initialValue: true }); + + render(, schema, reactContext); + + expect(onChange).toBeCalledWith('field', {}); + }); + + it('does nothing (false)', () => { + const Field = connectField(Test, { initialValue: false }); + + render(, schema, reactContext); + + expect(onChange).not.toBeCalled(); + }); + + it('respects `required` (props)', () => { + const Field = connectField(Test, { initialValue: true }); + + render(, schema, reactContext); + + expect(onChange).not.toBeCalled(); + }); + + it('respects `required` (schema)', () => { + const Field = connectField(Test, { initialValue: true }); + + render(, schema, reactContext); + + expect(onChange).not.toBeCalled(); + }); + }); + + describe('when rendered with value', () => { + it('treats value as initial value', async () => { + const Field = connectField(Test); + + render( + , + schema, + reactContext, + ); + + await new Promise(resolve => setTimeout(resolve, 10)); + + expect(onChange).toBeCalledWith('field', 'initialValueExample'); + }); + }); + + describe('when rendered with label', () => { + it.each([ + ['Props', '', 'Props'], + ['Props', 'Schema', 'Props'], + ['Props', undefined, 'Props'], + ['', undefined, ''], + ['', 'Schema', ''], + ['', undefined, ''], + [undefined, '', ''], + [undefined, 'Schema', 'Schema'], + [undefined, undefined, ''], + ])('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( + , + schema, + reactContext, + ); + + expect(screen.getByTestId('field')).toHaveAttribute('label', resultLabel); + }); + }); + + describe('when rendered provides correct onChange', () => { + it('is defaults to field name', () => { + const Field = connectField(Test); + + render(, schema, reactContext); + + const value = 'some 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((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); + + render( + + + + + + + + + + + + + , + schema, + reactContext, + ); + + expect(screen.getByTestId('1')).toHaveAttribute('label', 'Field'); + expect(screen.getByTestId('2')).toHaveAttribute('label', ''); + expect(screen.getByTestId('3')).toHaveAttribute('label', 'Field'); + expect(screen.getByTestId('4')).toHaveAttribute('label', 'Other'); + expect(screen.getByTestId('5')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('6')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('7')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('8')).toHaveAttribute('label', 'Subfield'); + expect(screen.getByTestId('9')).toHaveAttribute('label', ''); + }); +}); diff --git a/packages/uniforms/__tests__/index.ts b/packages/uniforms/__tests__/index.ts index 6ced7c075..263a65479 100644 --- a/packages/uniforms/__tests__/index.ts +++ b/packages/uniforms/__tests__/index.ts @@ -1,5 +1,4 @@ import * as uniforms from 'uniforms'; -import * as suites from 'uniforms/__suites__'; it('exports everything', () => { expect(uniforms).toMatchObject({ @@ -19,7 +18,3 @@ it('exports everything', () => { useForm: expect.any(Function), }); }); - -describe('@RTL', () => { - suites.testConnectField(); -}); From f96ddfa09fc01cb9f8ddb2a1abf91cb6f5ff8295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Thu, 22 Feb 2024 13:16:51 +0100 Subject: [PATCH 08/11] added missing label tests and refactored nested test --- packages/uniforms/__tests__/connectField.tsx | 76 +++++++++++++------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx index 7d59460b4..c5414083c 100644 --- a/packages/uniforms/__tests__/connectField.tsx +++ b/packages/uniforms/__tests__/connectField.tsx @@ -42,22 +42,31 @@ describe('connectField', () => { formRef: {} as BaseForm, } as Partial>; - const Test = (props: UnknownObject & { onChange: OnChange }) => { + const Test = ( + props: UnknownObject & { + onChange: OnChange; + label?: string | React.ReactNode; + }, + ) => { return props.children ? ( <> + {props.label ? {props.label} : null} props.onChange(event.target.value)} /> {props.children} ) : ( - props.onChange(event.target.value)} - /> + <> + {props.label ? {props.label} : null} + props.onChange(event.target.value)} + /> + ); }; @@ -145,6 +154,9 @@ describe('connectField', () => { }); describe('when rendered with label', () => { + const labelA = Label A; + const labelB = Label B; + it.each([ ['Props', '', 'Props'], ['Props', 'Schema', 'Props'], @@ -152,8 +164,13 @@ describe('connectField', () => { ['', undefined, ''], ['', 'Schema', ''], ['', undefined, ''], + [labelA, '', labelA], + [labelA, 'Schema', labelA], + [labelA, labelB, labelA], + [labelA, undefined, labelA], [undefined, '', ''], [undefined, 'Schema', 'Schema'], + [undefined, labelB, labelB], [undefined, undefined, ''], ])('resolves it correctly (%#)', (propLabel, schemaLabel, resultLabel) => { const schema = { @@ -165,11 +182,24 @@ describe('connectField', () => { const Field = connectField(Test); render( , + // @ts-expect-error schema, reactContext, ); - expect(screen.getByTestId('field')).toHaveAttribute('label', resultLabel); + 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); + } + } }); }); @@ -212,16 +242,16 @@ describe('connectField', () => { const Field = connectField(Test); render( - - - - - + + + + + - - - - + + + + , @@ -229,14 +259,8 @@ describe('connectField', () => { reactContext, ); - expect(screen.getByTestId('1')).toHaveAttribute('label', 'Field'); - expect(screen.getByTestId('2')).toHaveAttribute('label', ''); - expect(screen.getByTestId('3')).toHaveAttribute('label', 'Field'); - expect(screen.getByTestId('4')).toHaveAttribute('label', 'Other'); - expect(screen.getByTestId('5')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('6')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('7')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('8')).toHaveAttribute('label', 'Subfield'); - expect(screen.getByTestId('9')).toHaveAttribute('label', ''); + expect(screen.getAllByText('Field')).toHaveLength(2); + expect(screen.getAllByText('Subfield')).toHaveLength(4); + expect(screen.getAllByText('Other')).toHaveLength(1); }); }); From c3f1cf54a04f6868d0b4d33195db354e78e45cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Po=C5=9Bpiech?= Date: Thu, 22 Feb 2024 13:22:25 +0100 Subject: [PATCH 09/11] refactor --- packages/uniforms/__tests__/connectField.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx index c5414083c..35e82c294 100644 --- a/packages/uniforms/__tests__/connectField.tsx +++ b/packages/uniforms/__tests__/connectField.tsx @@ -50,7 +50,11 @@ describe('connectField', () => { ) => { return props.children ? ( <> - {props.label ? {props.label} : null} + {props.label ? ( + + ) : null} { ) : ( <> - {props.label ? {props.label} : null} + {props.label ? ( + + ) : null} Date: Thu, 22 Feb 2024 13:25:14 +0100 Subject: [PATCH 10/11] refactor --- packages/uniforms/__tests__/connectField.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx index 35e82c294..4ddcd7667 100644 --- a/packages/uniforms/__tests__/connectField.tsx +++ b/packages/uniforms/__tests__/connectField.tsx @@ -46,12 +46,13 @@ describe('connectField', () => { props: UnknownObject & { onChange: OnChange; label?: string | React.ReactNode; + id: string; }, ) => { return props.children ? ( <> {props.label ? ( -