diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 28a64f2fb37c1..852c1ef5b7e66 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -24,6 +24,7 @@ - `TimePicker`: Update unit tests to use `@testing-library/user-event` ([#41270](https://github.com/WordPress/gutenberg/pull/41270)). - `DateTimePicker`: Update `moment` to 2.26.0 and update `react-date` typings ([#41266](https://github.com/WordPress/gutenberg/pull/41266)). - `TextareaControl`: Convert to TypeScript ([#41215](https://github.com/WordPress/gutenberg/pull/41215)). +- `BoxControl`: Update unit tests to use `@testing-library/user-event` ([#41422](https://github.com/WordPress/gutenberg/pull/41422)). ### Experimental diff --git a/packages/components/src/box-control/test/index.js b/packages/components/src/box-control/test/index.js index 4515c4885c90f..c98dd3b48d43a 100644 --- a/packages/components/src/box-control/test/index.js +++ b/packages/components/src/box-control/test/index.js @@ -1,19 +1,29 @@ /** * External dependencies */ -import { render, fireEvent, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; /** * WordPress dependencies */ import { useState } from '@wordpress/element'; -import { ENTER } from '@wordpress/keycodes'; /** * Internal dependencies */ import BoxControl from '../'; +const setupUser = () => + userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); + +const getInput = () => + screen.getByLabelText( 'Box Control', { selector: 'input' } ); +const getSelect = () => screen.getByLabelText( 'Select unit' ); +const getReset = () => screen.getByText( /Reset/ ); + describe( 'BoxControl', () => { describe( 'Basic rendering', () => { it( 'should render', () => { @@ -23,42 +33,41 @@ describe( 'BoxControl', () => { expect( input ).toBeTruthy(); } ); - it( 'should update values when interacting with input', () => { - const { container } = render( ); - const input = container.querySelector( 'input' ); - const unitSelect = container.querySelector( 'select' ); + it( 'should update values when interacting with input', async () => { + const user = setupUser(); + render( ); + const input = getInput(); + const select = getSelect(); - input.focus(); - fireEvent.change( input, { target: { value: '100%' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100%' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( '%' ); + expect( input ).toHaveValue( '100' ); + expect( select ).toHaveValue( '%' ); } ); } ); describe( 'Reset', () => { - it( 'should reset values when clicking Reset', () => { - const { container, getByText } = render( ); - const input = container.querySelector( 'input' ); - const unitSelect = container.querySelector( 'select' ); - const reset = getByText( /Reset/ ); + it( 'should reset values when clicking Reset', async () => { + const user = setupUser(); + render( ); + const input = getInput(); + const select = getSelect(); + const reset = getReset(); - input.focus(); - fireEvent.change( input, { target: { value: '100px' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100px' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '100' ); + expect( select ).toHaveValue( 'px' ); - reset.focus(); - fireEvent.click( reset ); + await user.click( reset ); - expect( input.value ).toBe( '' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '' ); + expect( select ).toHaveValue( 'px' ); } ); - it( 'should reset values when clicking Reset, if controlled', () => { + it( 'should reset values when clicking Reset, if controlled', async () => { const Example = () => { const [ state, setState ] = useState(); @@ -69,26 +78,25 @@ describe( 'BoxControl', () => { /> ); }; - const { container, getByText } = render( ); - const input = container.querySelector( 'input' ); - const unitSelect = container.querySelector( 'select' ); - const reset = getByText( /Reset/ ); + const user = setupUser(); + render( ); + const input = getInput(); + const select = getSelect(); + const reset = getReset(); - input.focus(); - fireEvent.change( input, { target: { value: '100px' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100px' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '100' ); + expect( select ).toHaveValue( 'px' ); - reset.focus(); - fireEvent.click( reset ); + await user.click( reset ); - expect( input.value ).toBe( '' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '' ); + expect( select ).toHaveValue( 'px' ); } ); - it( 'should reset values when clicking Reset, if controlled <-> uncontrolled state changes', () => { + it( 'should reset values when clicking Reset, if controlled <-> uncontrolled state changes', async () => { const Example = () => { const [ state, setState ] = useState(); @@ -106,70 +114,78 @@ describe( 'BoxControl', () => { /> ); }; - const { container, getByText } = render( ); - const input = container.querySelector( 'input' ); - const unitSelect = container.querySelector( 'select' ); - const reset = getByText( /Reset/ ); + const user = setupUser(); + render( ); + const input = getInput(); + const select = getSelect(); + const reset = getReset(); - input.focus(); - fireEvent.change( input, { target: { value: '100px' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100px' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '100' ); + expect( select ).toHaveValue( 'px' ); - reset.focus(); - fireEvent.click( reset ); + await user.click( reset ); - expect( input.value ).toBe( '' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '' ); + expect( select ).toHaveValue( 'px' ); } ); - it( 'should persist cleared value when focus changes', () => { - render( ); + it( 'should persist cleared value when focus changes', async () => { + const user = setupUser(); + const spyChange = jest.fn(); + render( spyChange( v ) } /> ); const input = screen.getByLabelText( 'Box Control', { selector: 'input', } ); const unitSelect = screen.getByLabelText( 'Select unit' ); - input.focus(); - fireEvent.change( input, { target: { value: '100%' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100%' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( '%' ); + expect( input ).toHaveValue( '100' ); + expect( unitSelect ).toHaveValue( '%' ); - fireEvent.change( input, { target: { value: '' } } ); - fireEvent.blur( input ); + await user.clear( input ); + expect( input ).toHaveValue( '' ); + // Clicking document.body to trigger a blur event on the input. + await user.click( document.body ); - expect( input.value ).toBe( '' ); + expect( input ).toHaveValue( '' ); + expect( spyChange ).toHaveBeenLastCalledWith( { + top: undefined, + right: undefined, + bottom: undefined, + left: undefined, + } ); } ); } ); describe( 'Unlinked Sides', () => { - it( 'should update a single side value when unlinked', () => { + it( 'should update a single side value when unlinked', async () => { let state = {}; const setState = ( newState ) => ( state = newState ); - const { container, getByLabelText } = render( + const { getAllByLabelText, getByLabelText } = render( setState( next ) } /> ); - + const user = setupUser(); const unlink = getByLabelText( /Unlink Sides/ ); - fireEvent.click( unlink ); - const input = container.querySelector( 'input' ); - const unitSelect = container.querySelector( 'select' ); + await user.click( unlink ); + + const input = getByLabelText( /Top/ ); + const select = getAllByLabelText( /Select unit/ )[ 0 ]; - input.focus(); - fireEvent.change( input, { target: { value: '100px' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100px' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '100' ); + expect( select ).toHaveValue( 'px' ); expect( state ).toEqual( { top: '100px', @@ -179,30 +195,30 @@ describe( 'BoxControl', () => { } ); } ); - it( 'should update a whole axis when value is changed when unlinked', () => { + it( 'should update a whole axis when value is changed when unlinked', async () => { let state = {}; const setState = ( newState ) => ( state = newState ); - const { container, getByLabelText } = render( + const { getAllByLabelText, getByLabelText } = render( setState( next ) } splitOnAxis={ true } /> ); - + const user = setupUser(); const unlink = getByLabelText( /Unlink Sides/ ); - fireEvent.click( unlink ); - const input = container.querySelector( 'input' ); - const unitSelect = container.querySelector( 'select' ); + await user.click( unlink ); + + const input = getByLabelText( /Vertical/ ); + const select = getAllByLabelText( /Select unit/ )[ 0 ]; - input.focus(); - fireEvent.change( input, { target: { value: '100px' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '100px' ); + await user.keyboard( '{Enter}' ); - expect( input.value ).toBe( '100' ); - expect( unitSelect.value ).toBe( 'px' ); + expect( input ).toHaveValue( '100' ); + expect( select ).toHaveValue( 'px' ); expect( state ).toEqual( { top: '100px', @@ -214,36 +230,34 @@ describe( 'BoxControl', () => { } ); describe( 'Unit selections', () => { - it( 'should update unlinked controls unit selection based on all input control', () => { + it( 'should update unlinked controls unit selection based on all input control', async () => { // Render control. render( ); + const user = setupUser(); // Make unit selection on all input control. - const allUnitSelect = screen.getByLabelText( 'Select unit' ); - allUnitSelect.focus(); - fireEvent.change( allUnitSelect, { target: { value: 'em' } } ); + const allUnitSelect = getSelect(); + await user.selectOptions( allUnitSelect, [ 'em' ] ); // Unlink the controls. - const unlink = screen.getByLabelText( /Unlink Sides/ ); - fireEvent.click( unlink ); + await user.click( screen.getByLabelText( /Unlink Sides/ ) ); // Confirm that each individual control has the selected unit const unlinkedSelects = screen.getAllByDisplayValue( 'em' ); expect( unlinkedSelects.length ).toEqual( 4 ); } ); - it( 'should use individual side attribute unit when available', () => { + it( 'should use individual side attribute unit when available', async () => { // Render control. const { rerender } = render( ); + const user = setupUser(); // Make unit selection on all input control. - const allUnitSelect = screen.getByLabelText( 'Select unit' ); - allUnitSelect.focus(); - fireEvent.change( allUnitSelect, { target: { value: 'vw' } } ); + const allUnitSelect = getSelect(); + await user.selectOptions( allUnitSelect, [ 'vw' ] ); // Unlink the controls. - const unlink = screen.getByLabelText( /Unlink Sides/ ); - fireEvent.click( unlink ); + await user.click( screen.getByLabelText( /Unlink Sides/ ) ); // Confirm that each individual control has the selected unit const unlinkedSelects = screen.getAllByDisplayValue( 'vw' ); @@ -261,18 +275,15 @@ describe( 'BoxControl', () => { } ); describe( 'onChange updates', () => { - it( 'should call onChange when values contain more than just CSS units', () => { + it( 'should call onChange when values contain more than just CSS units', async () => { const setState = jest.fn(); render( ); + const user = setupUser(); + const input = getInput(); - const input = screen.getByLabelText( 'Box Control', { - selector: 'input', - } ); - - input.focus(); - fireEvent.change( input, { target: { value: '7.5rem' } } ); - fireEvent.keyDown( input, { keyCode: ENTER } ); + await user.type( input, '7.5rem' ); + await user.keyboard( '{Enter}' ); expect( setState ).toHaveBeenCalledWith( { top: '7.5rem', @@ -282,14 +293,14 @@ describe( 'BoxControl', () => { } ); } ); - it( 'should not pass invalid CSS unit only values to onChange', () => { + it( 'should not pass invalid CSS unit only values to onChange', async () => { const setState = jest.fn(); render( ); + const user = setupUser(); - const allUnitSelect = screen.getByLabelText( 'Select unit' ); - allUnitSelect.focus(); - fireEvent.change( allUnitSelect, { target: { value: 'rem' } } ); + const allUnitSelect = getSelect(); + await user.selectOptions( allUnitSelect, 'rem' ); expect( setState ).toHaveBeenCalledWith( { top: undefined,