diff --git a/.gitignore b/.gitignore index 451b010be..f67efa4fa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /.parcel-cache /coverage /lerna-debug.log +.DS_Store /node_modules /npm-debug.log diff --git a/packages/uniforms-antd/__tests__/index.ts b/packages/uniforms-antd/__tests__/index.ts index 0d5c364bc..f65bc471c 100644 --- a/packages/uniforms-antd/__tests__/index.ts +++ b/packages/uniforms-antd/__tests__/index.ts @@ -60,6 +60,8 @@ describe('@RTL', () => { suites.testRadioField(theme.RadioField, { skipHtmlAttributesTest: true }); // FIXME: AntD has problem with toHaveValue check suites.testSubmitField(theme.SubmitField, { skipValueTest: true }); + // FIXME: AntD select does not work with new RTL test implementation + suites.testSelectField(theme.SelectField, { theme: 'antd' }); suites.testTextField(theme.TextField); suites.testValidatedForm(theme.ValidatedForm); suites.testValidatedQuickForm(theme.ValidatedQuickForm); diff --git a/packages/uniforms-antd/src/SelectField.tsx b/packages/uniforms-antd/src/SelectField.tsx index ac3d77415..40197c0ed 100644 --- a/packages/uniforms-antd/src/SelectField.tsx +++ b/packages/uniforms-antd/src/SelectField.tsx @@ -41,30 +41,33 @@ export type SelectFieldProps = CheckboxesProps | SelectProps; function Select(props: SelectFieldProps) { const Group = props.fieldType === Array ? CheckboxGroup : RadioGroup; + const filteredDOMProps = filterDOMProps(props); return wrapField( props, props.checkboxes ? ( - // @ts-expect-error: Incorrect `value` type. - { - if (!props.readOnly) { - props.onChange( - // FIXME: Argument type depends on `props.fieldType`. - props.fieldType === Array - ? eventOrValue - : eventOrValue.target.value, - ); - } - }} - options={props.options?.map(option => ({ - ...option, - label: option.label ?? option.value, - }))} - value={props.value} - /> + + {/* @ts-expect-error: Incorrect `value` type. */} + { + if (!props.readOnly) { + props.onChange( + // FIXME: Argument type depends on `props.fieldType`. + props.fieldType === Array + ? eventOrValue + : eventOrValue.target.value, + ); + } + }} + options={props.options?.map(option => ({ + ...option, + label: option.label ?? option.value, + }))} + value={props.value} + /> + ) : ( allowClear={!props.required} @@ -86,13 +89,14 @@ function Select(props: SelectFieldProps) { : [] : props.value } - {...filterDOMProps(props)} + {...filteredDOMProps} > {props.options?.map(option => ( {option.label ?? option.value} diff --git a/packages/uniforms-bootstrap3/__tests__/SelectField.tsx b/packages/uniforms-bootstrap3/__tests__/SelectField.tsx deleted file mode 100644 index ad8a2e566..000000000 --- a/packages/uniforms-bootstrap3/__tests__/SelectField.tsx +++ /dev/null @@ -1,737 +0,0 @@ -import React from 'react'; -import { SelectField } from 'uniforms-bootstrap3'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - renders a select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); -}); - -test(' - renders a select with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('disabled')).toBe(true); -}); - -test(' - renders a select with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a select with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBeTruthy(); -}); - -test(' - renders a select with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBe('y'); -}); - -test(' - renders a select with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('name')).toBe('x'); -}); - -test(' - renders a select with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct options (transform)', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, label: '' } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'A'], - ['b', 'B'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (fallback)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], optional: true }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (implicit)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe(''); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a select which correctly reacts on change (empty)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: '' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', undefined); -}); - -test(' - renders a select which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').prop('children')).toBe('y'); - expect(wrapper.find('label').prop('htmlFor')).toBe( - wrapper.find('select').prop('id'), - ); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - disabled items (options)', () => { - const element = ( - - ); - const wrapper = mount(element, createContext({ x: { type: String } })); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option[value="a"]').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('option[value="b"]').at(0).prop('disabled')).toBe(false); -}); - -test(' - renders a set of checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); -}); - -test(' - renders a set of inline checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('.checkbox-inline')).toHaveLength(2); -}); - -test(' - renders a set of checkboxes with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a set of checkboxes with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBeTruthy(); - expect(wrapper.find('input').at(1).prop('id')).toBeTruthy(); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual([]); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { model: { x: ['b'] } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select which correctly reacts on change (first value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a']); -}); - -test(' - renders a select which correctly reacts on change (next value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a', 'b']); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by value', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by selectedIndex', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper - .find('select') - .simulate('change', { target: { selectedIndex: -1 } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBe('y-YQ'); - expect(wrapper.find('input').at(1).prop('id')).toBe('y-Yg'); -}); - -test(' - renders a set of checkboxes with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('name')).toBe('x'); - expect(wrapper.find('input').at(1).prop('name')).toBe('x'); -}); - -test(' - renders a set of checkboxes with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('label')).toHaveLength(2); - expect(wrapper.find('label').at(0).text()).toBe('a'); - expect(wrapper.find('label').at(1).text()).toBe('b'); -}); - -test(' - renders a set of checkboxes with correct options (transform)', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, label: '' } }), - ); - - expect(wrapper.find('label')).toHaveLength(2); - expect(wrapper.find('label').at(0).text()).toBe('A'); - expect(wrapper.find('label').at(1).text()).toBe('B'); -}); - -test(' - renders a set of checkboxes with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(false); -}); - -test(' - renders a set of checkboxes with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array check)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['b']); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array uncheck)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'a'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(3); - expect(wrapper.find('label').at(0).text()).toBe('y'); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - disabled items (checkboxes)', () => { - const allowedValues = ['a', 'b']; - - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(false); -}); diff --git a/packages/uniforms-bootstrap3/__tests__/index.ts b/packages/uniforms-bootstrap3/__tests__/index.ts index 17c4da346..73501bc3f 100644 --- a/packages/uniforms-bootstrap3/__tests__/index.ts +++ b/packages/uniforms-bootstrap3/__tests__/index.ts @@ -55,6 +55,10 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + suites.testSelectField(theme.SelectField, { + getCheckboxInlineOption: screen => + screen.getByLabelText('a').closest('.checkbox-inline'), + }); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField, { testWrapClassName: true }); suites.testValidatedForm(theme.ValidatedForm); diff --git a/packages/uniforms-bootstrap4/__tests__/SelectField.tsx b/packages/uniforms-bootstrap4/__tests__/SelectField.tsx deleted file mode 100644 index fbe3504a6..000000000 --- a/packages/uniforms-bootstrap4/__tests__/SelectField.tsx +++ /dev/null @@ -1,778 +0,0 @@ -import React from 'react'; -import { SelectField } from 'uniforms-bootstrap4'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - renders a select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); -}); - -test(' - renders a select with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('disabled')).toBe(true); -}); - -test(' - renders a select with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a select with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBeTruthy(); -}); - -test(' - renders a select with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBe('y'); -}); - -test(' - renders a select with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('name')).toBe('x'); -}); - -test(' - renders a select with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct options', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, label: '' } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'A'], - ['b', 'B'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (fallback)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], optional: true }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (implicit)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe(''); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a select which correctly reacts on change (empty)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: '' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', undefined); -}); - -test(' - renders a select which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').prop('children')).toBe('y'); - expect(wrapper.find('label').prop('htmlFor')).toBe( - wrapper.find('select').prop('id'), - ); -}); - -test(' - renders a select with class "bg-red" beside "form-group"', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('.bg-red.form-group')).toHaveLength(1); -}); - -test(' - renders a disabled select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('select').prop('disabled')).toEqual(true); -}); - -test(' - renders a required select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('.form-group.required')).toHaveLength(1); -}); - -test(' - renders am error massge in select', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('.form-text.text-danger')).toHaveLength(1); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - disabled items (options)', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, label: '' } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option[value="a"]').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('option[value="b"]').at(0).prop('disabled')).toBe(false); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual([]); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { model: { x: ['b'] } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select which correctly reacts on change (first value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a']); -}); - -test(' - renders a select which correctly reacts on change (next value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a', 'b']); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by value', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by selectedIndex', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper - .find('select') - .simulate('change', { target: { selectedIndex: -1 } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); -}); - -test(' - renders a set of inline checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('.checkbox-inline')).toHaveLength(2); -}); - -test(' - renders a set of checkboxes with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a set of checkboxes with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBeTruthy(); - expect(wrapper.find('input').at(1).prop('id')).toBeTruthy(); -}); - -test(' - renders a set of checkboxes with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBe('y-YQ'); - expect(wrapper.find('input').at(1).prop('id')).toBe('y-Yg'); -}); - -test(' - renders a set of checkboxes with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('name')).toBe('x'); - expect(wrapper.find('input').at(1).prop('name')).toBe('x'); -}); - -test(' - renders a set of checkboxes with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('label')).toHaveLength(2); - expect(wrapper.find('label').at(0).text()).toBe('a'); - expect(wrapper.find('label').at(1).text()).toBe('b'); -}); - -test(' - renders a set of checkboxes with correct options (transform)', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, label: '' } }), - ); - - expect(wrapper.find('label')).toHaveLength(2); - expect(wrapper.find('label').at(0).text()).toBe('A'); - expect(wrapper.find('label').at(1).text()).toBe('B'); -}); - -test(' - renders a set of checkboxes with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(false); -}); - -test(' - renders a set of checkboxes with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array check)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['b']); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array uncheck)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'a'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(3); - expect(wrapper.find('label').at(0).text()).toBe('y'); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - disabled items (checkboxes)', () => { - const element = ( - - ); - const wrapper = mount(element, createContext({ x: { type: String } })); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(false); -}); diff --git a/packages/uniforms-bootstrap4/__tests__/index.ts b/packages/uniforms-bootstrap4/__tests__/index.ts index 6d828563a..74eb7b3de 100644 --- a/packages/uniforms-bootstrap4/__tests__/index.ts +++ b/packages/uniforms-bootstrap4/__tests__/index.ts @@ -55,6 +55,10 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + suites.testSelectField(theme.SelectField, { + getCheckboxInlineOption: screen => + screen.getByLabelText('a').closest('.checkbox-inline'), + }); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField, { testWrapClassName: true }); suites.testValidatedForm(theme.ValidatedForm); diff --git a/packages/uniforms-bootstrap5/__tests__/SelectField.tsx b/packages/uniforms-bootstrap5/__tests__/SelectField.tsx deleted file mode 100644 index 9a66984e8..000000000 --- a/packages/uniforms-bootstrap5/__tests__/SelectField.tsx +++ /dev/null @@ -1,755 +0,0 @@ -import React from 'react'; -import { SelectField } from 'uniforms-bootstrap5'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' - renders a select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); -}); - -test(' - renders a select with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('disabled')).toBe(true); -}); - -test(' - renders a select with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a select with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBeTruthy(); -}); - -test(' - renders a select with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBe('y'); -}); - -test(' - renders a select with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('name')).toBe('x'); -}); - -test(' - renders a select with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct options (transform)', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, label: '' } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'A'], - ['b', 'B'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (fallback)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], optional: true }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (implicit)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe(''); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a select which correctly reacts on change (empty)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: '' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', undefined); -}); - -test(' - renders a select which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').prop('children')).toBe('y'); - expect(wrapper.find('label').prop('htmlFor')).toBe( - wrapper.find('select').prop('id'), - ); -}); - -test(' - renders a select with class "bg-red" beside "mb-3"', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('.bg-red.mb-3')).toHaveLength(1); -}); - -test(' - renders a disabled select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('select').prop('disabled')).toEqual(true); -}); - -test(' - renders a required select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('.mb-3.required')).toHaveLength(1); -}); - -test(' - renders am error massge in select', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - expect(wrapper.find('.form-text.text-danger')).toHaveLength(1); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - disabled items (options)', () => { - const element = ( - - ); - const wrapper = mount(element, createContext({ x: { type: String } })); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option[value="a"]').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('option[value="b"]').at(0).prop('disabled')).toBe(false); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual([]); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { model: { x: ['b'] } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select which correctly reacts on change (first value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a']); -}); - -test(' - renders a select which correctly reacts on change (next value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a', 'b']); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by value', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by selectedIndex', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper - .find('select') - .simulate('change', { target: { selectedIndex: -1 } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); -}); - -test(' - renders a set of inline checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('.form-check')).toHaveLength(2); - expect(wrapper.find('.form-check-inline')).toHaveLength(2); -}); - -test(' - renders a set of checkboxes with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a set of checkboxes with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBeTruthy(); - expect(wrapper.find('input').at(1).prop('id')).toBeTruthy(); -}); - -test(' - renders a set of checkboxes with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBe('y-YQ'); - expect(wrapper.find('input').at(1).prop('id')).toBe('y-Yg'); -}); - -test(' - renders a set of checkboxes with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('name')).toBe('x'); - expect(wrapper.find('input').at(1).prop('name')).toBe('x'); -}); - -test(' - renders a set of checkboxes with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('label')).toHaveLength(2); - expect(wrapper.find('label').at(0).text()).toBe('a'); - expect(wrapper.find('label').at(1).text()).toBe('b'); -}); - -test(' - renders a set of checkboxes with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(false); -}); - -test(' - renders a set of checkboxes with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array check)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['b']); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array uncheck)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'a'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(3); - expect(wrapper.find('label').at(0).text()).toBe('y'); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - disabled items (checkboxes)', () => { - const element = ( - - ); - const wrapper = mount(element, createContext({ x: { type: String } })); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(false); -}); diff --git a/packages/uniforms-bootstrap5/__tests__/index.ts b/packages/uniforms-bootstrap5/__tests__/index.ts index 851845fe8..374958d94 100644 --- a/packages/uniforms-bootstrap5/__tests__/index.ts +++ b/packages/uniforms-bootstrap5/__tests__/index.ts @@ -57,6 +57,10 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + suites.testSelectField(theme.SelectField, { + getCheckboxInlineOption: screen => + screen.getByLabelText('a').closest('.form-check-inline'), + }); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField, { testWrapClassName: true, diff --git a/packages/uniforms-material/__tests__/index.tsx b/packages/uniforms-material/__tests__/index.tsx index 4376e8005..647461ce5 100644 --- a/packages/uniforms-material/__tests__/index.tsx +++ b/packages/uniforms-material/__tests__/index.tsx @@ -75,6 +75,8 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + // FIXME: MUI select does not work with new RTL test implementation + // suites.testSelectField(theme.SelectField); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField, { testShowInlineError: true, diff --git a/packages/uniforms-mui/__tests__/index.ts b/packages/uniforms-mui/__tests__/index.ts index db660ef77..634b6f46f 100644 --- a/packages/uniforms-mui/__tests__/index.ts +++ b/packages/uniforms-mui/__tests__/index.ts @@ -54,6 +54,8 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + // FIXME: MUI select does not work with new RTL test implementation + // suites.testSelectField(theme.SelectField); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField); suites.testValidatedForm(theme.ValidatedForm); diff --git a/packages/uniforms-semantic/__tests__/index.ts b/packages/uniforms-semantic/__tests__/index.ts index 0b1d6cd21..87c1de6f0 100644 --- a/packages/uniforms-semantic/__tests__/index.ts +++ b/packages/uniforms-semantic/__tests__/index.ts @@ -53,6 +53,7 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + suites.testSelectField(theme.SelectField); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField, { testWrapClassName: true, diff --git a/packages/uniforms-unstyled/__tests__/SelectField.tsx b/packages/uniforms-unstyled/__tests__/SelectField.tsx deleted file mode 100644 index 5ffddda31..000000000 --- a/packages/uniforms-unstyled/__tests__/SelectField.tsx +++ /dev/null @@ -1,708 +0,0 @@ -import React from 'react'; -import { SelectField } from 'uniforms-unstyled'; - -import createContext from './_createContext'; -import mount from './_mount'; - -test(' { - const element = ( - - ); - const wrapper = mount(element, createContext({ x: { type: String } })); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); -}); - -test(' { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, required: true, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); -}); - -test(' - renders a select', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); -}); - -test(' - renders a select with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('disabled')).toBe(true); -}); - -test(' - renders a select with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a select with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBeTruthy(); -}); - -test(' - renders a select with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('id')).toBe('y'); -}); - -test(' - renders a select with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('name')).toBe('x'); -}); - -test(' - renders a select with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', ''], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (fallback)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], optional: true }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct placeholder (implicit)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('option')).toHaveLength(3); - [ - ['', 'y'], - ['a', 'a'], - ['b', 'b'], - ].forEach(([value, text], index) => { - const option = wrapper.find('option').at(index); - expect(option.prop('value')).toBe(value); - expect(option.text()).toBe(text); - }); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe(''); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toBe('b'); -}); - -test(' - renders a select which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a select which correctly reacts on change (empty)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: '' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', undefined); -}); - -test(' - renders a select which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'b' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(1); - expect(wrapper.find('label').prop('children')).toBe('y'); - expect(wrapper.find('label').prop('htmlFor')).toBe( - wrapper.find('select').prop('id'), - ); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - renders a set of checkboxes', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); -}); - -test(' - renders a set of checkboxes with correct disabled state', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBe(true); - expect(wrapper.find('input').at(1).prop('disabled')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct readOnly state', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).not.toHaveBeenCalled(); -}); - -test(' - renders a set of checkboxes with correct id (inherited)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBeTruthy(); - expect(wrapper.find('input').at(1).prop('id')).toBeTruthy(); -}); - -test(' - renders a set of checkboxes with correct id (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('id')).toBe('y-YQ'); - expect(wrapper.find('input').at(1).prop('id')).toBe('y-Yg'); -}); - -test(' - renders a set of checkboxes with correct name', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('name')).toBe('x'); - expect(wrapper.find('input').at(1).prop('name')).toBe('x'); -}); - -test(' - renders a select with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual([]); -}); - -test(' - renders a multiselect with disabled options', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual([]); - expect(wrapper.find('option').at(0).prop('disabled')).toBeFalsy(); - expect(wrapper.find('option').at(1).prop('disabled')).toBeTruthy(); -}); - -test(' - renders a select with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { model: { x: ['b'] } }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect(wrapper.find('select').prop('value')).toStrictEqual(['b']); -}); - -test(' - renders a select which correctly reacts on change (first value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a']); -}); - -test(' - renders a select which correctly reacts on change (next value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['a', 'b']); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by value', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper.find('select').simulate('change', { target: { value: 'a' } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a select which correctly reacts on change (uncheck) by selectedIndex', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('select')).toHaveLength(1); - expect( - wrapper - .find('select') - .simulate('change', { target: { selectedIndex: -1 } }), - ).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes with correct options', () => { - const element = ; - const wrapper = mount( - element, - createContext({ - x: { type: String, allowedValues: ['a', 'b'], label: '' }, - }), - ); - - expect(wrapper.find('label')).toHaveLength(2); - expect(wrapper.find('label').at(0).text()).toBe('a'); - expect(wrapper.find('label').at(1).text()).toBe('b'); -}); - -test(' - renders a set of checkboxes with correct value (default)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(false); -}); - -test(' - renders a set of checkboxes with correct value (model)', () => { - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' } }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes with correct value (specified)', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('checked')).toBe(false); - expect(wrapper.find('input').at(1).prop('checked')).toBe(true); -}); - -test(' - renders a set of checkboxes which correctly reacts on change', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'b'); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array check)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', ['b']); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (array uncheck)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { - x: { type: Array }, - 'x.$': { type: String, allowedValues: ['a', 'b'] }, - }, - { onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(1).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', []); -}); - -test(' - renders a set of checkboxes which correctly reacts on change (same value)', () => { - const onChange = jest.fn(); - - const element = ; - const wrapper = mount( - element, - createContext( - { x: { type: String, allowedValues: ['a', 'b'] } }, - { model: { x: 'b' }, onChange }, - ), - ); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).simulate('change')).toBeTruthy(); - expect(onChange).toHaveBeenLastCalledWith('x', 'a'); -}); - -test(' - renders a label', () => { - const element = ; - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('label')).toHaveLength(3); - expect(wrapper.find('label').at(0).text()).toBe('y'); -}); - -test(' - renders a wrapper with unknown props', () => { - const element = ( - - ); - const wrapper = mount( - element, - createContext({ x: { type: String, allowedValues: ['a', 'b'] } }), - ); - - expect(wrapper.find('div').at(0).prop('data-x')).toBe('x'); - expect(wrapper.find('div').at(0).prop('data-y')).toBe('y'); - expect(wrapper.find('div').at(0).prop('data-z')).toBe('z'); -}); - -test(' - works with special characters', () => { - mount( - , - createContext({ x: { type: String, allowedValues: ['ă', 'ș'] } }), - ); -}); - -test(' - renders a set of checkboxes with per-item props', () => { - const element = ( - - ); - const wrapper = mount(element, createContext({ x: { type: String } })); - - expect(wrapper.find('input')).toHaveLength(2); - expect(wrapper.find('input').at(0).prop('disabled')).toBeTruthy(); - expect(wrapper.find('input').at(1).prop('disabled')).toBeFalsy(); -}); diff --git a/packages/uniforms-unstyled/__tests__/index.ts b/packages/uniforms-unstyled/__tests__/index.ts index 323a5f617..097876b9d 100644 --- a/packages/uniforms-unstyled/__tests__/index.ts +++ b/packages/uniforms-unstyled/__tests__/index.ts @@ -58,6 +58,7 @@ describe('@RTL', () => { suites.testNumField(theme.NumField); suites.testQuickForm(theme.QuickForm); suites.testRadioField(theme.RadioField); + suites.testSelectField(theme.SelectField); suites.testSubmitField(theme.SubmitField); suites.testTextField(theme.TextField, { testShowInlineError: false }); suites.testValidatedForm(theme.ValidatedForm); diff --git a/packages/uniforms/__suites__/SelectField.tsx b/packages/uniforms/__suites__/SelectField.tsx new file mode 100644 index 000000000..7cb413876 --- /dev/null +++ b/packages/uniforms/__suites__/SelectField.tsx @@ -0,0 +1,620 @@ +import { fireEvent, screen, Screen } from '@testing-library/react'; +import React, { ComponentType } from 'react'; +import z from 'zod'; + +import { renderWithZod } from './render-zod'; +import { skipTestIf } from './skipTestIf'; + +export function testSelectField( + SelectField: ComponentType, + options?: { + theme?: 'antd'; + getCheckboxInlineOption?: (screen: Screen) => Element | null; + }, +) { + test(' - renders a select', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByTestId('select-field')).toBeInTheDocument(); + }); + + test(' - renders a select with correct disabled state', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + expect(select).toBeDisabled(); + }); + + test(' - renders a select with correct readOnly state', () => { + const onChange = jest.fn(); + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const option = screen.getByTestId('select-field').querySelector('option'); + option?.click(); + expect(onChange).not.toHaveBeenCalled(); + }); + + skipTestIf(options?.theme === 'antd')( + ' - ignores selection with readOnly state ', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByTestId('select-field').querySelector('select'); + fireEvent.change(select!, { target: { value: 'b' } }); + expect(onChange).not.toHaveBeenCalled(); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - (multiple) renders a select which correctly reacts on change (uncheck) by value', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('listbox'); + fireEvent.change(select, { target: { value: '' } }); + expect(onChange).toHaveBeenCalledWith([]); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - (multiple) renders a select which correctly reacts on change (uncheck) by selectedIndex', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('listbox'); + fireEvent.change(select, { target: { selectedIndex: -1 } }); + expect(onChange).toHaveBeenCalledWith([]); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - (multiple) renders a select which correctly reacts on change (checked) by selectedIndex', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('listbox'); + fireEvent.change(select, { target: { selectedIndex: 0 } }); + expect(onChange).toHaveBeenCalledWith(['a']); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - renders a select which correctly reacts on change (uncheck) by value', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + fireEvent.change(select, { target: { value: '' } }); + expect(onChange).toHaveBeenCalledWith(undefined); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - renders a select which correctly reacts on change (uncheck) by selectedIndex', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + fireEvent.change(select, { target: { selectedIndex: -1 } }); + expect(onChange).toHaveBeenCalledWith(undefined); + }, + ); + + test(' - renders a select with correct id (inherited)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByTestId('select-field').querySelector('[id]'); + expect(select?.getAttribute('id')).toBeTruthy(); + }); + + test(' - renders a select with correct id (specified)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByTestId('select-field').querySelector('[id="y"]'); + expect(select?.getAttribute('id')).toBe('y'); + }); + + test(' - renders a select with correct name', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByTestId('select-field'); + const elementWithAttribute = select.querySelector('[name="x"]') || select; + expect(elementWithAttribute?.getAttribute('name')).toBe('x'); + }); + + test(' - renders a select with correct options', () => { + const selectOptions = ['a', 'b'] as const; + renderWithZod({ + element: , + schema: z.object({ x: z.enum(selectOptions) }), + }); + const combobox = screen.getByRole('combobox'); + fireEvent.mouseDown(combobox); + selectOptions.forEach(option => { + expect(screen.getByRole('option', { name: option })).not.toBeNull(); + }); + }); + + test(' - renders a select with correct options (transform)', () => { + const selectOptions = ['a', 'b'] as const; + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(selectOptions) }), + }); + const combobox = screen.getByRole('combobox'); + fireEvent.mouseDown(combobox); + selectOptions.forEach(option => { + expect( + screen.getByRole('option', { name: option.toUpperCase() }), + ).toBeInTheDocument(); + }); + }); + + test(' - renders a select with correct placeholder (fallback)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByText('y')).toBeInTheDocument(); + }); + + skipTestIf(options?.theme === 'antd')( + ' - renders a select with correct placeholder (implicit)', + () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByText('y')).toBeInTheDocument(); + }, + ); + + test(' - renders a select with correct value (default)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + if (options?.theme === 'antd') { + expect(screen.getByText('a')).toBeInTheDocument(); + expect(screen.queryByText('b')).not.toBeInTheDocument(); + } else { + expect(select).toHaveValue('a'); + } + }); + + test(' - renders a select with missing value (model)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + expect(select).toHaveValue(''); + }); + + test(' - renders a select with correct value (model)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + model: { x: 'b' }, + }); + const select = screen.getByRole('combobox'); + if (options?.theme === 'antd') { + expect(screen.getByText('b')).toBeInTheDocument(); + expect(screen.queryByText('a')).not.toBeInTheDocument(); + } else { + expect(select).toHaveValue('b'); + } + }); + + test(' - renders a select with correct value (specified)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + if (options?.theme === 'antd') { + expect(screen.getByText('b')).toBeInTheDocument(); + expect(screen.queryByText('a')).not.toBeInTheDocument(); + } else { + expect(select).toHaveValue('b'); + } + }); + + skipTestIf(options?.theme === 'antd')( + ' - renders a select which correctly reacts on change', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + fireEvent.change(select, { target: { value: 'b' } }); + expect(onChange).toHaveBeenCalledWith('b'); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - renders a select which correctly reacts on change (empty)', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const select = screen.getByRole('combobox'); + fireEvent.change(select, { target: { value: '' } }); + expect(onChange).toHaveBeenLastCalledWith(undefined); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - renders a select which correctly reacts on change (same value)', + () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + model: { x: 'b' }, + }); + const select = screen.getByRole('combobox'); + fireEvent.change(select, { target: { value: 'b' } }); + expect(onChange).toHaveBeenCalledWith('b'); + }, + ); + + test(' - renders a wrapper with unknown props', () => { + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const field = screen.getByTestId('select-field'); + expect(field).toHaveAttribute('data-x', 'x'); + expect(field).toHaveAttribute('data-z', 'z'); + expect(field).toHaveAttribute('data-y', 'y'); + }); + + test(' - works with special characters', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['ă', 'ś']) }), + }); + const combobox = screen.getByRole('combobox'); + fireEvent.mouseDown(combobox); + expect(screen.getAllByText('ă')[0]).toBeInTheDocument(); + expect(screen.getAllByText('ś')[0]).toBeInTheDocument(); + }); + + skipTestIf(options?.theme === 'antd')( + ' - disabled items (options)', + () => { + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByText('A')).toBeDisabled(); + expect(screen.getByText('B')).not.toBeDisabled(); + }, + ); + + test(' - renders a set of checkboxes', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getAllByRole(/checkbox|radio/)).toHaveLength(2); + }); + + test(' - renders a set of checkboxes with correct disabled state', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).toBeDisabled(); + expect(checkboxes?.[1]).toBeDisabled(); + }); + + test(' - renders a set of checkboxes with correct readOnly state', () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + fireEvent.click(checkboxes?.[0]); + expect(onChange).not.toHaveBeenCalled(); + }); + + skipTestIf(!options?.getCheckboxInlineOption)( + ' - renders a set of inline checkboxes', + () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(options?.getCheckboxInlineOption?.(screen)).toBeInTheDocument(); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - renders a set of checkboxes with correct id (inherited)', + () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).toHaveAttribute('id'); + }, + ); + + skipTestIf(options?.theme === 'antd')( + ' - renders a set of checkboxes with correct id (specified)', + () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).toHaveAttribute('id', 'y-YQ'); + expect(checkboxes?.[1]).toHaveAttribute('id', 'y-Yg'); + }, + ); + + test(' - renders a set of checkboxes with correct name', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).toHaveAttribute('name', 'x'); + expect(checkboxes?.[1]).toHaveAttribute('name', 'x'); + }); + + test(' - renders a set of checkboxes with correct options', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByText('a')).toBeInTheDocument(); + expect(screen.getByText('b')).toBeInTheDocument(); + }); + + test(' - renders a set of checkboxes with correct options (transform)', () => { + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByText('A')).toBeInTheDocument(); + expect(screen.getByText('B')).toBeInTheDocument(); + }); + + test(' - renders a set of checkboxes with correct value (default)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).toBeChecked(); + expect(checkboxes?.[1]).not.toBeChecked(); + }); + + test(' - renders a set of checkboxes with correct value (model)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + model: { x: 'b' }, + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).not.toBeChecked(); + expect(checkboxes?.[1]).toBeChecked(); + }); + + test(' - renders a set of checkboxes with correct value (specified)', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).not.toBeChecked(); + expect(checkboxes?.[1]).toBeChecked(); + }); + + test(' - renders a set of checkboxes which correctly reacts on change', async () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + fireEvent.click(checkboxes?.[1]); + expect(onChange).toHaveBeenCalledWith('b'); + }); + + test(' - renders a set of checkboxes which correctly reacts on change (same value)', () => { + const onChange = jest.fn(); + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + model: { x: 'a' }, + }); + const checkbox = screen.getByRole(/checkbox|radio/, { name: 'b' }); + fireEvent.click(checkbox); + expect(onChange).toHaveBeenCalledWith('b'); + }); + + test(' - (multiple) renders a set of checkboxes which correctly reacts on change (array check)', () => { + const onChange = jest.fn(); + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + fireEvent.click(checkboxes?.[1]); + expect(onChange).toHaveBeenCalledWith(['b']); + fireEvent.click(checkboxes?.[0]); + expect(onChange).toHaveBeenCalledWith(['a']); + }); + + test(' - (multiple) renders a set of checkboxes which correctly reacts on change (array uncheck)', () => { + const onChange = jest.fn(); + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + fireEvent.click(checkboxes?.[0]); + expect(onChange).toHaveBeenCalledWith([]); + }); + + test(' - renders a label', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + expect(screen.getByText('y')).toBeInTheDocument(); + }); + + test(' - renders a wrapper with unknown props', () => { + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const field = screen.getByTestId('select-field'); + expect(field).toHaveAttribute('data-x', 'x'); + expect(field).toHaveAttribute('data-z', 'z'); + expect(field).toHaveAttribute('data-y', 'y'); + }); + + test(' - works with special characters', () => { + renderWithZod({ + element: , + schema: z.object({ x: z.enum(['ă', 'ș']) }), + }); + expect(screen.getByText('ă')).toBeInTheDocument(); + expect(screen.getByText('ș')).toBeInTheDocument(); + }); + + test(' - disabled items (checkboxes)', () => { + renderWithZod({ + element: ( + + ), + schema: z.object({ x: z.enum(['a', 'b']) }), + }); + const checkboxes = screen.getAllByRole(/checkbox|radio/); + expect(checkboxes?.[0]).toBeDisabled(); + expect(checkboxes?.[1]).not.toBeDisabled(); + }); +} diff --git a/packages/uniforms/__suites__/index.ts b/packages/uniforms/__suites__/index.ts index 04d72395f..e63c78281 100644 --- a/packages/uniforms/__suites__/index.ts +++ b/packages/uniforms/__suites__/index.ts @@ -16,6 +16,7 @@ export * from './NestField'; export * from './NumField'; export * from './QuickForm'; export * from './RadioField'; +export * from './SelectField'; export * from './SubmitField'; export * from './TextField'; export * from './ValidatedForm'; diff --git a/packages/uniforms/__suites__/render-zod.tsx b/packages/uniforms/__suites__/render-zod.tsx index 8dde3b026..ac2490fd0 100644 --- a/packages/uniforms/__suites__/render-zod.tsx +++ b/packages/uniforms/__suites__/render-zod.tsx @@ -14,10 +14,6 @@ export function renderWithZod>({ } & Omit>>, 'schema'>) { return renderOnScreen(element, { wrapper({ children }) { - if (!schema) { - return <>{children}; - } - const value = { changed: false, changedMap: Object.create(null),