From 71d2dc59a56a9977bcb88f5ce5bfd4d96febed27 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Wed, 21 Jun 2023 18:26:22 +0200 Subject: [PATCH] Mobile unit tests: remove custom waitFor implementation (#46735) * useNestedSettingsUpdate: don't rerender if blockListSettings value changes * Mobile unit tests: remove custom waitFor implementation * Upgrade @testing-library/react-native to 12.1.2, fixes effect flushing bugs * Remove references to legacy screen.container, prefer snapshots * Spacer block: use waitForModalVisible * Image test: remove unneeded workarounds * Queries exclude elements hidden from accessibility, find them explicitly --- .../react-native/integration-test-guide.md | 42 +- package-lock.json | 8 +- package.json | 2 +- .../test/__snapshots__/index.native.js.snap | 6 +- .../block-mover/test/index.native.js | 21 +- .../use-nested-settings-update.js | 67 ++- .../src/buttons/test/edit.native.js | 5 +- .../src/columns/test/edit.native.js | 68 +-- .../src/cover/test/edit.native.js | 76 +-- .../src/embed/test/index.native.js | 82 ++- .../src/gallery/test/index.native.js | 4 +- .../src/group/test/edit.native.js | 6 +- .../src/image/test/edit.native.js | 16 +- .../src/list/test/edit.native.js | 5 + .../test/__snapshots__/edit.native.js.snap | 39 ++ .../src/paragraph/test/edit.native.js | 6 +- .../src/preformatted/test/edit.native.js | 14 +- .../src/spacer/test/index.native.js | 62 +-- .../test/navigation-container.native.js | 45 +- .../test/__snapshots__/index.native.js.snap | 46 ++ .../html-text-input/test/index.native.js | 4 +- .../test/link-settings-navigation.native.js | 21 +- .../editor-help/test/index.native.js | 5 +- .../test/__snapshots__/modal.native.js.snap | 476 ++++++++++++++++++ .../src/link/test/index.native.js | 30 +- .../src/link/test/modal.native.js | 7 +- test/native/helpers.js | 4 - .../native/integration-test-helpers/README.md | 4 - .../integration-test-helpers/add-block.js | 9 +- test/native/integration-test-helpers/index.js | 1 - .../wait-for-modal-visible.js | 8 +- .../integration-test-helpers/wait-for.js | 50 -- 32 files changed, 842 insertions(+), 397 deletions(-) create mode 100644 packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap create mode 100644 packages/components/src/mobile/html-text-input/test/__snapshots__/index.native.js.snap create mode 100644 packages/format-library/src/link/test/__snapshots__/modal.native.js.snap delete mode 100644 test/native/integration-test-helpers/wait-for.js diff --git a/docs/contributors/code/react-native/integration-test-guide.md b/docs/contributors/code/react-native/integration-test-guide.md index af810a3a5dff2..d4da7bbd1d201 100644 --- a/docs/contributors/code/react-native/integration-test-guide.md +++ b/docs/contributors/code/react-native/integration-test-guide.md @@ -121,42 +121,32 @@ const radiusSlider = getByTestId( 'Slider Border Radius' ); Note that either a plain string or a regular expression can be passed into these queries. A regular expression is best for querying part of a string (e.g. any element whose accessibility label contains `Unsupported Block. Row 1`). Note that special characters such as `.` need to be escaped. -### Use of `waitFor` +### Use of `find` queries -After rendering the components or firing an event, side effects might happen due to potential state updates so the element we’re looking for might not be yet rendered. In this case, we would need to wait for the element to be available and for this purpose, we can use the `waitFor` function, which periodically executes the provided callback to determine whether the element appeared or not. +After rendering the components or firing an event, side effects might happen due to potential state updates so the element we’re looking for might not be yet rendered. In this case, we would need to wait for the element to be available and for this purpose, we can use the `find*` versions of query functions, which internally use `waitFor` and periodically check whether the element appeared or not. Here are some examples: ```js -const mediaLibraryButton = await waitFor( () => - getByText( 'WordPress Media Library' ) -); +const mediaLibraryButton = await findByText( 'WordPress Media Library' ); ``` ```js -const missingBlock = await waitFor( () => - getByLabelText( /Unsupported Block\. Row 1/ ) -); +const missingBlock = await findByLabelText( /Unsupported Block\. Row 1/ ); ``` ```js -const radiusSlider = await waitFor( () => - getByTestId( 'Slider Border Radius' ) -); +const radiusSlider = await findByTestId( 'Slider Border Radius' ); ``` -In most cases we’ll use the `waitFor` function, but it’s important to note that it should be restricted to those queries that actually require waiting for the element to be available. - -NOTE: The `react-native-testing-library` package provides the `query*` and `find*` functions for this purpose too, but we should avoid using them for now because there’s a [known issue](https://github.com/callstack/react-native-testing-library/issues/379) that would make the test fail. +In most cases we’ll use the `find*` functions, but it’s important to note that it should be restricted to those queries that actually require waiting for the element to be available. ### `within` queries It’s also possible to query elements contained in other elements via the `within` function, here is an example: ```js -const missingBlock = await waitFor( () => - getByLabelText( /Unsupported Block\. Row 1/ ) -); +const missingBlock = await findByLabelText( /Unsupported Block\. Row 1/ ); const translatedTableTitle = within( missingBlock ).getByText( 'Tabla' ); ``` @@ -236,7 +226,7 @@ Here is an example of how to insert a Paragraph block: ```js // Open the inserter menu -fireEvent.press( await waitFor( () => getByLabelText( 'Add block' ) ) ); +fireEvent.press( await findByLabelText( 'Add block' ) ); const blockList = getByTestId( 'InserterUI-Blocks' ); // onScroll event used to force the FlatList to render all items @@ -249,7 +239,7 @@ fireEvent.scroll( blockList, { } ); // Insert a Paragraph block -fireEvent.press( await waitFor( () => getByText( `Paragraph` ) ) ); +fireEvent.press( await findByText( `Paragraph` ) ); ``` ### Open block settings @@ -259,7 +249,7 @@ The block settings can be accessed by tapping the "Open Settings" button after s ```js fireEvent.press( block ); -const settingsButton = await waitFor( () => getByLabelText( 'Open Settings' ) ); +const settingsButton = await findByLabelText( 'Open Settings' ); fireEvent.press( settingsButton ); ``` @@ -301,9 +291,7 @@ fireEvent.scroll( blockList, { Sliders found in bottom sheets should be queried using their `testID`: ```js -const radiusSlider = await waitFor( () => - getByTestId( 'Slider Border Radius' ) -); +const radiusSlider = await findByTestId( 'Slider Border Radius' ); fireEvent( radiusSlider, 'valueChange', '30' ); ``` @@ -314,8 +302,8 @@ Note that a slider’s `testID` is "Slider " + label. So for a slider with a lab One caveat when adding blocks is that if they contain inner blocks, these inner blocks are not rendered. The following example shows how we can make a Buttons block render its inner Button blocks (assumes we’ve already obtained a reference to the Buttons block as `buttonsBlock`): ```js -const innerBlockListWrapper = await waitFor( () => - within( buttonsBlock ).getByTestId( 'block-list-wrapper' ) +const innerBlockListWrapper = await within( buttonsBlock ).findByTestId( + 'block-list-wrapper' ); fireEvent( innerBlockListWrapper, 'layout', { nativeEvent: { @@ -325,8 +313,8 @@ fireEvent( innerBlockListWrapper, 'layout', { }, } ); -const buttonInnerBlock = await waitFor( () => - within( buttonsBlock ).getByLabelText( /Button Block\. Row 1/ ) +const buttonInnerBlock = await within( buttonsBlock ).findByLabelText( + /Button Block\. Row 1/ ); fireEvent.press( buttonInnerBlock ); ``` diff --git a/package-lock.json b/package-lock.json index 52fb775cbe3f2..2b110762c27be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15784,12 +15784,12 @@ } }, "@testing-library/react-native": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-11.3.0.tgz", - "integrity": "sha512-wCbH7TJ2uVwtkQPRQTZbM3Y/mzwK5zxwkOPKfR2UdVdz/RtCC2UVyDnJMaBNwG/cNle5tc92sQDfp3Gn2oyftg==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@testing-library/react-native/-/react-native-12.1.2.tgz", + "integrity": "sha512-5rQMQpbSQAuJc4TxjTt1IoeT1a5eVXaa61hf/YcjV2QtGqCoyhuW8DvhTowrL7y2Ds4NzuYBmbVxjNPR6GIblw==", "dev": true, "requires": { - "pretty-format": "^29.0.3" + "pretty-format": "^29.0.0" } }, "@testing-library/user-event": { diff --git a/package.json b/package.json index ee825c7ff095d..0c4e3dd5f013e 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "@storybook/react": "6.5.7", "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "13.4.0", - "@testing-library/react-native": "11.3.0", + "@testing-library/react-native": "12.1.2", "@testing-library/user-event": "14.4.3", "@types/classnames": "2.3.1", "@types/eslint": "7.28.0", diff --git a/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap b/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap index 6f9cab69518b5..ea30939ef9d52 100644 --- a/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap +++ b/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap @@ -42,7 +42,7 @@ exports[`Block Mover Picker moving blocks moves blocks up and down 1`] = ` " `; -exports[`Block Mover Picker should match snapshot 1`] = ` +exports[`Block Mover Picker should render without crashing and match snapshot 1`] = ` [ { - it( 'renders without crashing', () => { - const props = { - isFirst: false, - isLast: true, - canMove: true, - numberOfBlocks: 2, - firstIndex: 1, - - onMoveDown: jest.fn(), - onMoveUp: jest.fn(), - onLongPress: jest.fn(), - - rootClientId: '', - isStackedHorizontally: true, - }; - const screen = render( ); - expect( screen.container ).toBeTruthy(); - } ); - - it( 'should match snapshot', () => { + it( 'should render without crashing and match snapshot', () => { const props = { isFirst: false, isLast: true, diff --git a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js index 49d2da85688c3..633d6b2701ed6 100644 --- a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js +++ b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js @@ -3,7 +3,6 @@ */ import { useLayoutEffect, useMemo } from '@wordpress/element'; import { useSelect, useDispatch, useRegistry } from '@wordpress/data'; -import isShallowEqual from '@wordpress/is-shallow-equal'; /** * Internal dependencies @@ -52,13 +51,11 @@ export default function useNestedSettingsUpdate( const { updateBlockListSettings } = useDispatch( blockEditorStore ); const registry = useRegistry(); - const { blockListSettings, parentLock } = useSelect( + const { parentLock } = useSelect( ( select ) => { const rootClientId = select( blockEditorStore ).getBlockRootClientId( clientId ); return { - blockListSettings: - select( blockEditorStore ).getBlockListSettings( clientId ), parentLock: select( blockEditorStore ).getTemplateLock( rootClientId ), }; @@ -83,14 +80,16 @@ export default function useNestedSettingsUpdate( prioritizedInserterBlocks ); + const _templateLock = + templateLock === undefined || parentLock === 'contentOnly' + ? parentLock + : templateLock; + useLayoutEffect( () => { const newSettings = { allowedBlocks: _allowedBlocks, prioritizedInserterBlocks: _prioritizedInserterBlocks, - templateLock: - templateLock === undefined || parentLock === 'contentOnly' - ? parentLock - : templateLock, + templateLock: _templateLock, }; // These values are not defined for RN, so only include them if they @@ -116,41 +115,37 @@ export default function useNestedSettingsUpdate( newSettings.__experimentalDirectInsert = __experimentalDirectInsert; } - if ( ! isShallowEqual( blockListSettings, newSettings ) ) { - // Batch updates to block list settings to avoid triggering cascading renders - // for each container block included in a tree and optimize initial render. - // To avoid triggering updateBlockListSettings for each container block - // causing X re-renderings for X container blocks, - // we batch all the updatedBlockListSettings in a single "data" batch - // which results in a single re-render. - if ( ! pendingSettingsUpdates.get( registry ) ) { - pendingSettingsUpdates.set( registry, [] ); - } - pendingSettingsUpdates - .get( registry ) - .push( [ clientId, newSettings ] ); - window.queueMicrotask( () => { - if ( pendingSettingsUpdates.get( registry )?.length ) { - registry.batch( () => { - pendingSettingsUpdates - .get( registry ) - .forEach( ( args ) => { - updateBlockListSettings( ...args ); - } ); - pendingSettingsUpdates.set( registry, [] ); - } ); - } - } ); + // Batch updates to block list settings to avoid triggering cascading renders + // for each container block included in a tree and optimize initial render. + // To avoid triggering updateBlockListSettings for each container block + // causing X re-renderings for X container blocks, + // we batch all the updatedBlockListSettings in a single "data" batch + // which results in a single re-render. + if ( ! pendingSettingsUpdates.get( registry ) ) { + pendingSettingsUpdates.set( registry, [] ); } + pendingSettingsUpdates + .get( registry ) + .push( [ clientId, newSettings ] ); + window.queueMicrotask( () => { + if ( pendingSettingsUpdates.get( registry )?.length ) { + registry.batch( () => { + pendingSettingsUpdates + .get( registry ) + .forEach( ( args ) => { + updateBlockListSettings( ...args ); + } ); + pendingSettingsUpdates.set( registry, [] ); + } ); + } + } ); }, [ clientId, - blockListSettings, _allowedBlocks, _prioritizedInserterBlocks, + _templateLock, __experimentalDefaultBlock, __experimentalDirectInsert, - templateLock, - parentLock, captureToolbars, orientation, updateBlockListSettings, diff --git a/packages/block-library/src/buttons/test/edit.native.js b/packages/block-library/src/buttons/test/edit.native.js index ade014330a1b2..c8e1415b7d1f7 100644 --- a/packages/block-library/src/buttons/test/edit.native.js +++ b/packages/block-library/src/buttons/test/edit.native.js @@ -81,7 +81,8 @@ describe( 'Buttons block', () => { ); const incrementButton = await within( radiusStepper ).findByTestId( - 'Increment' + 'Increment', + { hidden: true } ); fireEvent( incrementButton, 'onPressIn' ); @@ -181,7 +182,7 @@ describe( 'Buttons block', () => { expect( addBlockHerePlaceholders.length ).toBe( 0 ); // Add a new Button block - fireEvent.press( await screen.findByText( 'Button' ) ); + fireEvent.press( within( blockList ).getByText( 'Button' ) ); // Get new button const secondButtonBlock = await getBlock( screen, 'Button', { diff --git a/packages/block-library/src/columns/test/edit.native.js b/packages/block-library/src/columns/test/edit.native.js index 558041535aa57..9fe6a1b75a50d 100644 --- a/packages/block-library/src/columns/test/edit.native.js +++ b/packages/block-library/src/columns/test/edit.native.js @@ -9,8 +9,8 @@ import { openBlockSettings, within, getBlock, - waitFor, dismissModal, + waitForModalVisible, } from 'test/helpers'; /** @@ -142,19 +142,17 @@ describe( 'Columns block', () => { it( 'reaches the minimum limit of number of column blocks', async () => { const screen = await initializeEditor(); - const { getByLabelText, getByTestId } = screen; // Add block await addBlock( screen, 'Columns' ); // Wait for the variations modal to be visible - await waitFor( - () => getByTestId( 'block-variation-modal' ).props.isVisible + const blockVariationModal = await screen.findByTestId( + 'block-variation-modal' ); + await waitForModalVisible( blockVariationModal ); // Select a column variation - const blockVariationModal = getByTestId( 'block-variation-modal' ); - await waitFor( () => blockVariationModal.props.isVisible ); const threeColumnLayout = within( blockVariationModal ).getByLabelText( /33 \/ 33 \/ 33 block/ @@ -169,7 +167,7 @@ describe( 'Columns block', () => { await openBlockSettings( screen ); // Update the number of columns by adding one - const columnsControl = getByLabelText( /Number of columns/ ); + const columnsControl = screen.getByLabelText( /Number of columns/ ); fireEvent( columnsControl, 'accessibilityAction', { nativeEvent: { actionName: 'increment' }, } ); @@ -274,20 +272,19 @@ describe( 'Columns block', () => { const screen = await initializeEditor( { initialHtml: TWO_COLUMNS_BLOCK_HTML, } ); - const { getByLabelText } = screen; // Get block const columnsBlock = await getBlock( screen, 'Columns' ); fireEvent.press( columnsBlock ); // Open vertical alignment menu - const verticalAlignmentButton = getByLabelText( + const verticalAlignmentButton = screen.getByLabelText( /Change vertical alignment/ ); fireEvent.press( verticalAlignmentButton ); // Get Align top button - const verticalTopAlignmentButton = getByLabelText( /Align top/ ); + const verticalTopAlignmentButton = screen.getByLabelText( /Align top/ ); fireEvent.press( verticalTopAlignmentButton ); // Get the first column @@ -298,7 +295,8 @@ describe( 'Columns block', () => { fireEvent.press( verticalAlignmentButton ); // Get Align bottom button - const verticalBottomAlignmentButton = getByLabelText( /Align bottom/ ); + const verticalBottomAlignmentButton = + screen.getByLabelText( /Align bottom/ ); fireEvent.press( verticalBottomAlignmentButton ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -335,19 +333,17 @@ describe( 'Columns block', () => { describe( 'when using columns percentage mechanism', () => { it( "updates the slider's input value", async () => { const screen = await initializeEditor(); - const { getByLabelText, getByTestId } = screen; // Add block await addBlock( screen, 'Columns' ); // Wait for the variations modal to be visible - await waitFor( - () => getByTestId( 'block-variation-modal' ).props.isVisible + const blockVariationModal = await screen.findByTestId( + 'block-variation-modal' ); + await waitForModalVisible( blockVariationModal ); // Select a column variation - const blockVariationModal = getByTestId( 'block-variation-modal' ); - await waitFor( () => blockVariationModal.props.isVisible ); const threeColumnLayout = within( blockVariationModal ).getByLabelText( /33 \/ 33 \/ 33 block/ @@ -362,10 +358,14 @@ describe( 'Columns block', () => { await openBlockSettings( screen ); // Get width control - const widthControl = getByLabelText( /Width. Value is/ ); - fireEvent.press( within( widthControl ).getByText( '33.3' ) ); - const widthTextInput = - within( widthControl ).getByDisplayValue( '33.3' ); + const widthControl = screen.getByLabelText( /Width. Value is/ ); + fireEvent.press( + within( widthControl ).getByText( '33.3', { hidden: true } ) + ); + const widthTextInput = within( widthControl ).getByDisplayValue( + '33.3', + { hidden: true } + ); fireEvent.changeText( widthTextInput, '55.55555' ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -390,9 +390,13 @@ describe( 'Columns block', () => { // Set custom width value for the first column let widthControl = getByLabelText( /Width. Value is/ ); - fireEvent.press( within( widthControl ).getByText( '50' ) ); - let widthTextInput = - within( widthControl ).getByDisplayValue( '50' ); + fireEvent.press( + within( widthControl ).getByText( '50', { hidden: true } ) + ); + let widthTextInput = within( widthControl ).getByDisplayValue( + '50', + { hidden: true } + ); fireEvent.changeText( widthTextInput, '90' ); // Dismiss settings @@ -409,8 +413,12 @@ describe( 'Columns block', () => { // Set custom width value for the second column widthControl = getByLabelText( /Width. Value is/ ); - fireEvent.press( within( widthControl ).getByText( '50' ) ); - widthTextInput = within( widthControl ).getByDisplayValue( '50' ); + fireEvent.press( + within( widthControl ).getByText( '50', { hidden: true } ) + ); + widthTextInput = within( widthControl ).getByDisplayValue( '50', { + hidden: true, + } ); fireEvent.changeText( widthTextInput, '55.5' ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -431,21 +439,17 @@ describe( 'Columns block', () => { 'sets the predefined percentages for %s', async ( layout ) => { const screen = await initializeEditor(); - const { getByTestId } = screen; // Add block await addBlock( screen, 'Columns' ); // Wait for the variations modal to be visible - await waitFor( - () => getByTestId( 'block-variation-modal' ).props.isVisible + const blockVariationModal = await screen.findByTestId( + 'block-variation-modal' ); + await waitForModalVisible( blockVariationModal ); // Select a column variation - const blockVariationModal = getByTestId( - 'block-variation-modal' - ); - await waitFor( () => blockVariationModal.props.isVisible ); const columnLayout = within( blockVariationModal ).getByLabelText( layout ); fireEvent.press( columnLayout ); diff --git a/packages/block-library/src/cover/test/edit.native.js b/packages/block-library/src/cover/test/edit.native.js index 6a5bef376c18f..40f7620fd2ad2 100644 --- a/packages/block-library/src/cover/test/edit.native.js +++ b/packages/block-library/src/cover/test/edit.native.js @@ -7,7 +7,7 @@ import { initializeEditor, render, fireEvent, - waitFor, + waitForModalVisible, within, getBlock, openBlockSettings, @@ -185,14 +185,14 @@ describe( 'when an image is attached', () => { } ); it( 'toggles a fixed background', async () => { - const { getByText } = render( + const screen = render( ); - const fixedBackgroundButton = await waitFor( () => - getByText( 'Fixed background' ) + const fixedBackgroundButton = await screen.findByText( + 'Fixed background' ); fireEvent.press( fixedBackgroundButton ); @@ -215,7 +215,7 @@ describe( 'when an image is attached', () => { ); fireEvent.press( editFocalPointButton ); fireEvent( - screen.getByTestId( 'Slider Y-Axis Position' ), + screen.getByTestId( 'Slider Y-Axis Position', { hidden: true } ), 'valueChange', '52' ); @@ -240,10 +240,12 @@ describe( 'when an image is attached', () => { ); fireEvent.press( editFocalPointButton ); fireEvent.press( - screen.getByText( ( attributes.focalPoint.x * 100 ).toString() ) + screen.getByText( ( attributes.focalPoint.x * 100 ).toString(), { + hidden: true, + } ) ); fireEvent.changeText( - screen.getByLabelText( 'X-Axis Position' ), + screen.getByLabelText( 'X-Axis Position', { hidden: true } ), '99' ); fireEvent.press( screen.getByLabelText( 'Apply' ) ); @@ -256,21 +258,26 @@ describe( 'when an image is attached', () => { } ); it( 'discards canceled focal point changes', async () => { - const { getByText, getByLabelText } = render( + const screen = render( ); - const editFocalPointButton = await waitFor( () => - getByText( 'Edit focal point' ) + const editFocalPointButton = await screen.findByText( + 'Edit focal point' ); fireEvent.press( editFocalPointButton ); fireEvent.press( - getByText( ( attributes.focalPoint.x * 100 ).toString() ) + screen.getByText( ( attributes.focalPoint.x * 100 ).toString(), { + hidden: true, + } ) ); - fireEvent.changeText( getByLabelText( 'X-Axis Position' ), '80' ); - fireEvent.press( getByLabelText( 'Go back' ) ); + fireEvent.changeText( + screen.getByLabelText( 'X-Axis Position', { hidden: true } ), + '80' + ); + fireEvent.press( screen.getByLabelText( 'Go back' ) ); expect( setAttributes ).not.toHaveBeenCalledWith( expect.objectContaining( { @@ -314,9 +321,13 @@ describe( 'when an image is attached', () => { // Update Opacity attribute const opacityControl = getByLabelText( /Opacity/ ); - fireEvent.press( within( opacityControl ).getByText( '50' ) ); - const heightTextInput = - within( opacityControl ).getByDisplayValue( '50' ); + fireEvent.press( + within( opacityControl ).getByText( '50', { hidden: true } ) + ); + const heightTextInput = within( opacityControl ).getByDisplayValue( + '50', + { hidden: true } + ); fireEvent.changeText( heightTextInput, '20' ); // The decreasing button should be disabled @@ -356,7 +367,7 @@ describe( 'color settings', () => { // Wait for Block Settings to be visible. const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Open the overlay color settings. const colorOverlay = await screen.findByLabelText( 'Color. Empty' ); @@ -391,7 +402,7 @@ describe( 'color settings', () => { // Wait for Block Settings to be visible. const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Open the overlay color settings. const colorOverlay = await screen.findByLabelText( 'Color. Empty' ); @@ -447,7 +458,7 @@ describe( 'color settings', () => { // Wait for Block Settings to be visible. const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Open the overlay color settings. const colorOverlay = await screen.findByLabelText( 'Color. Empty' ); @@ -503,7 +514,7 @@ describe( 'color settings', () => { // Wait for Block Settings to be visible. const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Open the overlay color settings. const colorOverlay = await screen.findByLabelText( 'Color. Empty' ); @@ -536,12 +547,12 @@ describe( 'minimum height settings', () => { await openBlockSettings( screen ); // Set vw unit - fireEvent.press( getByText( 'px' ) ); - fireEvent.press( getByText( 'Viewport width (vw)' ) ); + fireEvent.press( getByText( 'px', { hidden: true } ) ); + fireEvent.press( getByText( 'Viewport width (vw)', { hidden: true } ) ); // Update height attribute - fireEvent.press( getByText( '300' ) ); - const heightTextInput = getByDisplayValue( '300' ); + fireEvent.press( getByText( '300', { hidden: true } ) ); + const heightTextInput = getByDisplayValue( '300', { hidden: true } ); fireEvent.changeText( heightTextInput, '20' ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -561,8 +572,8 @@ describe( 'minimum height settings', () => { await openBlockSettings( screen ); // Set the pixel unit - fireEvent.press( getByText( 'vw' ) ); - fireEvent.press( getByText( 'Pixels (px)' ) ); + fireEvent.press( getByText( 'vw', { hidden: true } ) ); + fireEvent.press( getByText( 'Pixels (px)', { hidden: true } ) ); expect( getEditorHtml() ).toMatchSnapshot(); } ); @@ -592,14 +603,17 @@ describe( 'minimum height settings', () => { await openBlockSettings( screen ); // Set the unit name - fireEvent.press( getByText( 'vw' ) ); - fireEvent.press( getByText( unitName ) ); + fireEvent.press( getByText( 'vw', { hidden: true } ) ); + fireEvent.press( getByText( unitName, { hidden: true } ) ); // Update height attribute const heightControl = getByLabelText( /Minimum height/ ); - fireEvent.press( within( heightControl ).getByText( value ) ); - const heightTextInput = - within( heightControl ).getByDisplayValue( value ); + fireEvent.press( + within( heightControl ).getByText( value, { hidden: true } ) + ); + const heightTextInput = within( + heightControl + ).getByDisplayValue( value, { hidden: true } ); fireEvent.changeText( heightTextInput, minValue ); // The decreasing button should be disabled diff --git a/packages/block-library/src/embed/test/index.native.js b/packages/block-library/src/embed/test/index.native.js index d4b7fa99ded83..6abdd2509f863 100644 --- a/packages/block-library/src/embed/test/index.native.js +++ b/packages/block-library/src/embed/test/index.native.js @@ -6,6 +6,7 @@ import { initializeEditor, fireEvent, waitFor, + waitForModalVisible, within, } from 'test/helpers'; import { Platform } from 'react-native'; @@ -238,7 +239,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Dismiss the edit URL modal. fireEvent( embedEditURLModal, 'backdropPress' ); @@ -256,7 +257,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Set an URL. const linkTextInput = editor.getByPlaceholderText( 'Add link' ); @@ -296,7 +297,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Get embed link with auto-pasted URL. const autopastedLinkField = await editor.findByText( clipboardURL ); @@ -335,7 +336,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Dismiss the edit URL modal. fireEvent( embedEditURLModal, 'backdropPress' ); @@ -356,7 +357,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Set an URL. const linkTextInput = editor.getByPlaceholderText( 'Add link' ); @@ -397,7 +398,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Get embed link. const embedLink = await editor.findByText( clipboardURL ); @@ -438,7 +439,7 @@ describe( 'Embed block', () => { const blockSettingsModal = editor.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -462,7 +463,7 @@ describe( 'Embed block', () => { const blockSettingsModal = editor.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Start editing link. fireEvent.press( @@ -507,7 +508,7 @@ describe( 'Embed block', () => { const blockSettingsModal = editor.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Start editing link. fireEvent.press( @@ -581,7 +582,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Dismiss the edit URL modal. fireEvent( embedEditURLModal, 'backdropPress' ); @@ -594,7 +595,7 @@ describe( 'Embed block', () => { fireEvent.press( editor.getByText( 'ADD LINK' ) ); // Wait for edit URL modal to be visible. - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Dismiss the edit URL modal. fireEvent( embedEditURLModal, 'backdropPress' ); @@ -604,11 +605,9 @@ describe( 'Embed block', () => { fireEvent.press( editor.getByText( 'ADD LINK' ) ); // Wait for edit URL modal to be visible. - const isVisibleThirdTime = await waitFor( - () => embedEditURLModal.props.isVisible - ); + await waitForModalVisible( embedEditURLModal ); - expect( isVisibleThirdTime ).toBeTruthy(); + expect( embedEditURLModal.props.isVisible ).toBe( true ); } ); // This test case covers the bug fixed in PR #35013. @@ -622,7 +621,7 @@ describe( 'Embed block', () => { const embedEditURLModal = editor.getByTestId( 'embed-edit-url-modal' ); - await waitFor( () => embedEditURLModal.props.isVisible ); + await waitForModalVisible( embedEditURLModal ); // Set an bad URL. let linkTextInput = editor.getByPlaceholderText( 'Add link' ); @@ -640,7 +639,7 @@ describe( 'Embed block', () => { const blockSettingsModal = editor.getByTestId( 'block-settings-modal' ); - await waitFor( () => blockSettingsModal.props.isVisible ); + await waitForModalVisible( blockSettingsModal ); // Start editing link. fireEvent.press( @@ -832,7 +831,7 @@ describe( 'Embed block', () => { // Wait for no preview modal to be visible. const noPreviewModal = getByTestId( 'embed-no-preview-modal' ); - await waitFor( () => noPreviewModal.props.isVisible ); + await waitForModalVisible( noPreviewModal ); // Preview post. fireEvent.press( getByText( 'Preview post' ) ); @@ -854,13 +853,15 @@ describe( 'Embed block', () => { // Wait for no preview modal to be visible. const noPreviewModal = getByTestId( 'embed-no-preview-modal' ); - await waitFor( () => noPreviewModal.props.isVisible ); + await waitForModalVisible( noPreviewModal ); // Dismiss modal. fireEvent.press( getByText( 'Dismiss' ) ); // Wait for no preview modal to be not visible. - await waitFor( () => ! noPreviewModal.props.isVisible ); + await waitFor( () => + expect( noPreviewModal.props.isVisible ).toBe( false ) + ); expect( requestPreview ).not.toHaveBeenCalled(); } ); @@ -893,7 +894,7 @@ describe( 'Embed block', () => { const embedHandlerPicker = editor.getByTestId( 'embed-handler-picker' ); - await waitFor( () => embedHandlerPicker.props.isVisible ); + await waitForModalVisible( embedHandlerPicker ); // Select create embed option. fireEvent.press( editor.getByText( 'Create embed' ) ); @@ -937,16 +938,14 @@ describe( 'Embed block', () => { const embedHandlerPicker = editor.getByTestId( 'embed-handler-picker' ); - await waitFor( () => embedHandlerPicker.props.isVisible ); + await waitForModalVisible( embedHandlerPicker ); // Select create link option. fireEvent.press( editor.getByText( 'Create link' ) ); // Get the link text. - const linkText = await waitFor( () => - editor.getByDisplayValue( - `

${ expectedURL }

` - ) + const linkText = await editor.findByDisplayValue( + `

${ expectedURL }

` ); expect( linkText ).toBeDefined(); @@ -1041,11 +1040,10 @@ describe( 'Embed block', () => { it( 'sets block caption', async () => { const expectedCaption = 'Caption'; - const { getByPlaceholderText, getByDisplayValue } = - await initializeWithEmbedBlock( RICH_TEXT_EMBED_HTML ); + const screen = await initializeWithEmbedBlock( RICH_TEXT_EMBED_HTML ); // Set a caption. - const captionField = getByPlaceholderText( 'Add caption' ); + const captionField = screen.getByPlaceholderText( 'Add caption' ); fireEvent( captionField, 'focus' ); fireEvent( captionField, 'onChange', { nativeEvent: { @@ -1056,8 +1054,8 @@ describe( 'Embed block', () => { } ); // Get current caption. - const caption = await waitFor( () => - getByDisplayValue( `

${ expectedCaption }

` ) + const caption = await screen.findByDisplayValue( + `

${ expectedCaption }

` ); expect( caption ).toBeDefined(); @@ -1086,36 +1084,32 @@ describe( 'Embed block', () => { describe( 'block settings', () => { it( 'toggles resize for smaller devices media settings', async () => { - const { getByLabelText, getByText } = - await initializeWithEmbedBlock( RICH_TEXT_EMBED_HTML ); + const screen = await initializeWithEmbedBlock( + RICH_TEXT_EMBED_HTML + ); // Open Block Settings. - fireEvent.press( - await waitFor( () => getByLabelText( 'Open Settings' ) ) - ); + fireEvent.press( await screen.findByLabelText( 'Open Settings' ) ); // Untoggle resize for smaller devices. fireEvent.press( - await waitFor( () => getByText( /Resize for smaller devices/ ) ) + await screen.findByText( /Resize for smaller devices/ ) ); expect( getEditorHtml() ).toMatchSnapshot(); } ); it( 'does not show media settings panel if responsive is not supported', async () => { - const { getByLabelText, getByText } = - await initializeWithEmbedBlock( WP_EMBED_HTML ); + const screen = await initializeWithEmbedBlock( WP_EMBED_HTML ); // Open Block Settings. - fireEvent.press( - await waitFor( () => getByLabelText( 'Open Settings' ) ) - ); + fireEvent.press( await screen.findByLabelText( 'Open Settings' ) ); // Wait for media settings panel. let mediaSettingsPanel; try { - mediaSettingsPanel = await waitFor( () => - getByText( 'Media settings' ) + mediaSettingsPanel = await screen.findByText( + 'Media settings' ); } catch ( e ) { // NOOP. diff --git a/packages/block-library/src/gallery/test/index.native.js b/packages/block-library/src/gallery/test/index.native.js index dabc6d6b1b30c..37c3349957aad 100644 --- a/packages/block-library/src/gallery/test/index.native.js +++ b/packages/block-library/src/gallery/test/index.native.js @@ -209,7 +209,9 @@ describe( 'Gallery block', () => { } ); // Check gallery item caption is not visible - const galleryItemCaption = getByLabelText( /Image caption. Empty/ ); + const galleryItemCaption = getByLabelText( /Image caption. Empty/, { + hidden: true, + } ); expect( galleryItemCaption ).not.toBeVisible(); // Set gallery caption diff --git a/packages/block-library/src/group/test/edit.native.js b/packages/block-library/src/group/test/edit.native.js index 5716d3380dc5b..1d02da768facf 100644 --- a/packages/block-library/src/group/test/edit.native.js +++ b/packages/block-library/src/group/test/edit.native.js @@ -8,7 +8,6 @@ import { initializeEditor, within, getBlock, - waitFor, } from 'test/helpers'; /** @@ -43,7 +42,6 @@ afterAll( () => { describe( 'Group block', () => { it( 'inserts block and adds a Heading block as an inner block', async () => { const screen = await initializeEditor(); - const { getByTestId, getByText } = screen; // Add block await addBlock( screen, 'Group' ); @@ -58,7 +56,7 @@ describe( 'Group block', () => { fireEvent.press( appenderButton ); // Look for a block in the inserter - const blockList = getByTestId( 'InserterUI-Blocks' ); + const blockList = screen.getByTestId( 'InserterUI-Blocks' ); // onScroll event used to force the FlatList to render all items fireEvent.scroll( blockList, { @@ -70,7 +68,7 @@ describe( 'Group block', () => { } ); // Add a block - fireEvent.press( await waitFor( () => getByText( 'Heading' ) ) ); + fireEvent.press( await screen.findByText( 'Heading' ) ); expect( getEditorHtml() ).toMatchSnapshot(); } ); diff --git a/packages/block-library/src/image/test/edit.native.js b/packages/block-library/src/image/test/edit.native.js index 1f1bdd6ae4eda..2e8bc4bf4a49b 100644 --- a/packages/block-library/src/image/test/edit.native.js +++ b/packages/block-library/src/image/test/edit.native.js @@ -7,7 +7,6 @@ import { initializeEditor, getEditorHtml, render, - waitFor, setupApiFetch, } from 'test/helpers'; import { Image } from 'react-native'; @@ -173,7 +172,7 @@ describe( 'Image Block', () => { 'wordpress.org' ); fireEvent.press( screen.getByLabelText( 'Apply' ) ); - await waitFor( + await act( () => new Promise( ( resolve ) => setTimeout( resolve, 100 ) ) ); @@ -196,25 +195,16 @@ describe( 'Image Block', () => { const [ imageBlock ] = screen.getAllByLabelText( /Image Block/ ); fireEvent.press( imageBlock ); - // Awaiting navigation event seemingly required due to React Navigation bug - // https://github.com/react-navigation/react-navigation/issues/9701 - await act( () => - fireEvent.press( screen.getByLabelText( 'Open Settings' ) ) - ); + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + fireEvent.press( screen.getByText( 'None' ) ); - fireEvent.press( screen.getByText( 'Media File' ) ); - await screen.findByText( 'Custom URL' ); fireEvent.press( screen.getByText( 'Custom URL' ) ); - // Await asynchronous fetch of clipboard - await act( () => clipboardPromise ); fireEvent.changeText( screen.getByPlaceholderText( 'Search or type URL' ), 'wordpress.org' ); fireEvent.press( screen.getByLabelText( 'Apply' ) ); fireEvent.press( await screen.findByText( 'Custom URL' ) ); - // Await asynchronous fetch of clipboard - await act( () => clipboardPromise ); fireEvent.press( screen.getByText( 'Media File' ) ); const expectedHtml = ` diff --git a/packages/block-library/src/list/test/edit.native.js b/packages/block-library/src/list/test/edit.native.js index 0083e4dd24575..6aefcf45b6552 100644 --- a/packages/block-library/src/list/test/edit.native.js +++ b/packages/block-library/src/list/test/edit.native.js @@ -172,6 +172,11 @@ describe( 'List block', () => { ); await triggerBlockListLayout( listItemBlock1 ); + // wait until inserter on the newly created indented block is enabled + // this is slightly delayed (by updating block list settings) and would + // trigger an "update not wrapped in act()" warning if not explicitly awaited. + screen.findByRole( 'button', { name: 'Add block', disabled: false } ); + expect( getEditorHtml() ).toMatchSnapshot(); } ); diff --git a/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap new file mode 100644 index 0000000000000..c1bcb2e853c8c --- /dev/null +++ b/packages/block-library/src/paragraph/test/__snapshots__/edit.native.js.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Paragraph block should render without crashing and match snapshot 1`] = ` + + + +`; diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index d3ff59c0e42c2..945b88ae997f5 100644 --- a/packages/block-library/src/paragraph/test/edit.native.js +++ b/packages/block-library/src/paragraph/test/edit.native.js @@ -40,9 +40,9 @@ const getTestComponentWithContent = ( content ) => { }; describe( 'Paragraph block', () => { - it( 'renders without crashing', () => { + it( 'should render without crashing and match snapshot', () => { const screen = getTestComponentWithContent( '' ); - expect( screen.container ).toBeTruthy(); + expect( screen.toJSON() ).toMatchSnapshot(); } ); it( 'should bold text', async () => { @@ -251,7 +251,7 @@ describe( 'Paragraph block', () => { 'wordpress.org' ); fireEvent.changeText( - screen.getByPlaceholderText( 'Add link text' ), + screen.getByPlaceholderText( 'Add link text', { hidden: true } ), 'WordPress' ); jest.useFakeTimers(); diff --git a/packages/block-library/src/preformatted/test/edit.native.js b/packages/block-library/src/preformatted/test/edit.native.js index 21286a7cdd67d..1fdb4532dacab 100644 --- a/packages/block-library/src/preformatted/test/edit.native.js +++ b/packages/block-library/src/preformatted/test/edit.native.js @@ -24,24 +24,12 @@ import PreformattedEdit from '../edit'; setupCoreBlocks(); describe( 'Preformatted', () => { - it( 'renders without crashing', () => { - const screen = render( - - ); - - expect( screen.container ).toBeDefined(); - } ); - it( 'should match snapshot when content is empty', () => { const screen = render( styles1 } + getStylesFromColorScheme={ jest.fn() } /> ); expect( screen.toJSON() ).toMatchSnapshot(); diff --git a/packages/block-library/src/spacer/test/index.native.js b/packages/block-library/src/spacer/test/index.native.js index ee052715b92c9..235b8bab78ee0 100644 --- a/packages/block-library/src/spacer/test/index.native.js +++ b/packages/block-library/src/spacer/test/index.native.js @@ -5,7 +5,7 @@ import { fireEvent, getEditorHtml, initializeEditor, - waitFor, + waitForModalVisible, } from 'test/helpers'; /** @@ -65,13 +65,14 @@ describe( 'Spacer block', () => { // Open block settings fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); - await waitFor( - () => screen.getByTestId( 'block-settings-modal' ).props.isVisible - ); + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitForModalVisible( blockSettingsModal ); // Update height attribute - fireEvent.press( screen.getByText( '100' ) ); - const heightTextInput = screen.getByDisplayValue( '100' ); + fireEvent.press( screen.getByText( '100', { hidden: true } ) ); + const heightTextInput = screen.getByDisplayValue( '100', { + hidden: true, + } ); fireEvent.changeText( heightTextInput, '50' ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -92,17 +93,20 @@ describe( 'Spacer block', () => { // Open block settings fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); - await waitFor( - () => screen.getByTestId( 'block-settings-modal' ).props.isVisible - ); + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitForModalVisible( blockSettingsModal ); // Set vh unit - fireEvent.press( screen.getByText( 'px' ) ); - fireEvent.press( screen.getByText( 'Viewport height (vh)' ) ); + fireEvent.press( screen.getByText( 'px', { hidden: true } ) ); + fireEvent.press( + screen.getByText( 'Viewport height (vh)', { hidden: true } ) + ); // Update height attribute - fireEvent.press( screen.getByText( '100' ) ); - const heightTextInput = screen.getByDisplayValue( '100' ); + fireEvent.press( screen.getByText( '100', { hidden: true } ) ); + const heightTextInput = screen.getByDisplayValue( '100', { + hidden: true, + } ); fireEvent.changeText( heightTextInput, '25' ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -123,9 +127,8 @@ describe( 'Spacer block', () => { // Open block settings fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); - await waitFor( - () => screen.getByTestId( 'block-settings-modal' ).props.isVisible - ); + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitForModalVisible( blockSettingsModal ); // Increment height fireEvent( @@ -154,9 +157,8 @@ describe( 'Spacer block', () => { // Open block settings fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); - await waitFor( - () => screen.getByTestId( 'block-settings-modal' ).props.isVisible - ); + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitForModalVisible( blockSettingsModal ); // Increment height fireEvent( @@ -212,13 +214,14 @@ describe( 'Spacer block', () => { // Open block settings fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); - await waitFor( - () => screen.getByTestId( 'block-settings-modal' ).props.isVisible - ); + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitForModalVisible( blockSettingsModal ); // Update height attribute - fireEvent.press( screen.getByText( '60' ) ); - const heightTextInput = screen.getByDisplayValue( '60' ); + fireEvent.press( screen.getByText( '60', { hidden: true } ) ); + const heightTextInput = screen.getByDisplayValue( '60', { + hidden: true, + } ); fireEvent.changeText( heightTextInput, '70' ); expect( getEditorHtml() ).toMatchSnapshot(); @@ -239,13 +242,14 @@ describe( 'Spacer block', () => { // Open block settings fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); - await waitFor( - () => screen.getByTestId( 'block-settings-modal' ).props.isVisible - ); + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitForModalVisible( blockSettingsModal ); // Update height attribute - fireEvent.press( screen.getByText( '100' ) ); - const heightTextInput = screen.getByDisplayValue( '100' ); + fireEvent.press( screen.getByText( '100', { hidden: true } ) ); + const heightTextInput = screen.getByDisplayValue( '100', { + hidden: true, + } ); fireEvent.changeText( heightTextInput, '120' ); expect( getEditorHtml() ).toMatchSnapshot(); diff --git a/packages/components/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js b/packages/components/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js index ced540e66d42a..de12428fed02e 100644 --- a/packages/components/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js +++ b/packages/components/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js @@ -2,7 +2,7 @@ * External dependencies */ import { Text } from 'react-native'; -import { render, fireEvent, waitFor, act } from 'test/helpers'; +import { render, fireEvent, act } from 'test/helpers'; import { useNavigation } from '@react-navigation/native'; /** @@ -49,8 +49,8 @@ it( 'animates height transitioning from non-full-screen to full-screen', async ( ); // Await navigation screen to allow async state updates to complete - const navigationScreen = await waitFor( () => - screen.getByTestId( 'navigation-screen-test-screen-1' ) + const navigationScreen = await screen.findByTestId( + 'navigation-screen-test-screen-1' ); // Trigger non-full-screen layout event act( () => { @@ -65,11 +65,9 @@ it( 'animates height transitioning from non-full-screen to full-screen', async ( jest.advanceTimersByTime( 10 ); } ); // Navigate to screen 2 - fireEvent.press( - await waitFor( () => screen.getByText( /test-screen-1/ ) ) - ); + fireEvent.press( await screen.findByText( /test-screen-1/ ) ); // Await navigation screen to allow async state updates to complete - await waitFor( () => screen.getByText( /test-screen-2/ ) ); + await screen.findByText( /test-screen-2/ ); expect( performLayoutAnimation ).toHaveBeenCalledTimes( 1 ); } ); @@ -87,8 +85,8 @@ it( 'animates height transitioning from full-screen to non-full-screen', async ( ); // Await navigation screen to allow async state updates to complete - const navigationScreen = await waitFor( () => - screen.getByTestId( 'navigation-screen-test-screen-1' ) + const navigationScreen = await screen.findByTestId( + 'navigation-screen-test-screen-1' ); // Trigger non-full-screen layout event act( () => { @@ -103,16 +101,11 @@ it( 'animates height transitioning from full-screen to non-full-screen', async ( jest.advanceTimersByTime( 10 ); } ); // Navigate to screen 2 - fireEvent.press( - await waitFor( () => screen.getByText( /test-screen-1/ ) ) - ); + fireEvent.press( await screen.findByText( /test-screen-1/ ) ); // Navigate to screen 1 - fireEvent.press( - // Use custom waitFor due to https://github.com/callstack/react-native-testing-library/issues/379 - await waitFor( () => screen.getByText( /test-screen-2/ ) ) - ); + fireEvent.press( await screen.findByText( /test-screen-2/ ) ); // Await navigation screen to allow async state updates to complete - await waitFor( () => screen.getByText( /test-screen-1/ ) ); + await screen.findByText( /test-screen-1/ ); expect( performLayoutAnimation ).toHaveBeenCalledTimes( 2 ); } ); @@ -135,8 +128,8 @@ it( 'does not animate height transitioning from full-screen to full-screen', asy ); // Await navigation screen to allow async state updates to complete - const navigationScreen = await waitFor( () => - screen.getByTestId( 'navigation-screen-test-screen-1' ) + const navigationScreen = await screen.findByTestId( + 'navigation-screen-test-screen-1' ); // Trigger non-full-screen layout event act( () => { @@ -151,19 +144,13 @@ it( 'does not animate height transitioning from full-screen to full-screen', asy jest.advanceTimersByTime( 10 ); } ); // Navigate to screen 2 - fireEvent.press( - await waitFor( () => screen.getByText( /test-screen-1/ ) ) - ); + fireEvent.press( await screen.findByText( /test-screen-1/ ) ); // Navigate to screen 3 - fireEvent.press( - await waitFor( () => screen.getByText( /test-screen-2/ ) ) - ); + fireEvent.press( await screen.findByText( /test-screen-2/ ) ); // Navigate to screen 2 - fireEvent.press( - await waitFor( () => screen.getByText( /test-screen-3/ ) ) - ); + fireEvent.press( await screen.findByText( /test-screen-3/ ) ); // Await navigation screen to allow async state updates to complete - await waitFor( () => screen.getByText( /test-screen-2/ ) ); + await screen.findByText( /test-screen-2/ ); expect( performLayoutAnimation ).toHaveBeenCalledTimes( 1 ); } ); diff --git a/packages/components/src/mobile/html-text-input/test/__snapshots__/index.native.js.snap b/packages/components/src/mobile/html-text-input/test/__snapshots__/index.native.js.snap new file mode 100644 index 0000000000000..ace4108a9968a --- /dev/null +++ b/packages/components/src/mobile/html-text-input/test/__snapshots__/index.native.js.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HTMLTextInput HTMLTextInput renders and matches snapshot 1`] = ` + + + + + + + + +`; diff --git a/packages/components/src/mobile/html-text-input/test/index.native.js b/packages/components/src/mobile/html-text-input/test/index.native.js index 5ddffe3d81d0d..1f900fc5ee1e9 100644 --- a/packages/components/src/mobile/html-text-input/test/index.native.js +++ b/packages/components/src/mobile/html-text-input/test/index.native.js @@ -23,13 +23,13 @@ const getStylesFromColorScheme = () => { }; describe( 'HTMLTextInput', () => { - it( 'HTMLTextInput renders', () => { + it( 'HTMLTextInput renders and matches snapshot', () => { const screen = render( ); - expect( screen.container ).toBeTruthy(); + expect( screen.toJSON() ).toMatchSnapshot(); } ); it( 'HTMLTextInput updates state on HTML text change', () => { diff --git a/packages/components/src/mobile/link-settings/test/link-settings-navigation.native.js b/packages/components/src/mobile/link-settings/test/link-settings-navigation.native.js index 1f861a9ba9332..9d8cf2a844e9b 100644 --- a/packages/components/src/mobile/link-settings/test/link-settings-navigation.native.js +++ b/packages/components/src/mobile/link-settings/test/link-settings-navigation.native.js @@ -2,7 +2,7 @@ * External dependencies */ import { Keyboard, Platform } from 'react-native'; -import { render, fireEvent, waitFor } from 'test/helpers'; +import { render, fireEvent } from 'test/helpers'; /** * Internal dependencies @@ -40,13 +40,8 @@ describe( 'Android', () => { it( 'improves back animation performance by dismissing keyboard beforehand', async () => { const screen = render( subject ); fireEvent.press( screen.getByText( 'Link to' ) ); - fireEvent.press( - screen.getByLabelText( 'Link to, Search or type URL' ) - ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => - screen.getByLabelText( 'Go back' ) - ); + const backButton = await screen.findByLabelText( 'Go back' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); @@ -57,9 +52,7 @@ describe( 'Android', () => { const screen = render( subject ); fireEvent.press( screen.getByText( 'Link to' ) ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => - screen.getByLabelText( 'Apply' ) - ); + const backButton = await screen.findByLabelText( 'Apply' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); @@ -81,9 +74,7 @@ describe( 'iOS', () => { const screen = render( subject ); fireEvent.press( screen.getByText( 'Link to' ) ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => - screen.getByLabelText( 'Go back' ) - ); + const backButton = await screen.findByLabelText( 'Go back' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); @@ -94,9 +85,7 @@ describe( 'iOS', () => { const screen = render( subject ); fireEvent.press( screen.getByText( 'Link to' ) ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => - screen.getByLabelText( 'Apply' ) - ); + const backButton = await screen.findByLabelText( 'Apply' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); diff --git a/packages/editor/src/components/editor-help/test/index.native.js b/packages/editor/src/components/editor-help/test/index.native.js index c300f3db8d7ba..1695590a769d3 100644 --- a/packages/editor/src/components/editor-help/test/index.native.js +++ b/packages/editor/src/components/editor-help/test/index.native.js @@ -35,10 +35,11 @@ it( 'navigates back from help topic detail screen', async () => { const backButton = screen.getAllByLabelText( 'Go back' ); fireEvent.press( backButton[ backButton.length - 1 ] ); - // Currently logs `act` warning due to https://github.com/callstack/react-native-testing-library/issues/379 const text = 'Each block has its own settings. To find them, tap on a block. Its settings will appear on the toolbar at the bottom of the screen.'; - await waitForElementToBeRemoved( () => screen.getByText( text ) ); + await waitForElementToBeRemoved( () => + screen.getByText( text, { hidden: true } ) + ); expect( screen.queryByText( text ) ).toBeNull(); } ); diff --git a/packages/format-library/src/link/test/__snapshots__/modal.native.js.snap b/packages/format-library/src/link/test/__snapshots__/modal.native.js.snap new file mode 100644 index 0000000000000..293b969b07773 --- /dev/null +++ b/packages/format-library/src/link/test/__snapshots__/modal.native.js.snap @@ -0,0 +1,476 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LinksUI LinksUI renders 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Path + + + + + Link to + + + + + Search or type URL + + + + Path + + + + + + + + + + + + Path + + + + + Link text + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; diff --git a/packages/format-library/src/link/test/index.native.js b/packages/format-library/src/link/test/index.native.js index 328ce17764dce..e55590fc4070e 100644 --- a/packages/format-library/src/link/test/index.native.js +++ b/packages/format-library/src/link/test/index.native.js @@ -2,7 +2,7 @@ * External dependencies */ import { Keyboard, Platform } from 'react-native'; -import { render, fireEvent, waitFor } from 'test/helpers'; +import { render, fireEvent } from 'test/helpers'; /** * WordPress dependencies @@ -53,9 +53,7 @@ describe( 'Android', () => { screen.getByLabelText( 'Link to, Search or type URL' ) ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => - screen.getByLabelText( 'Go back' ) - ); + const backButton = await screen.findByLabelText( 'Go back' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); @@ -63,7 +61,7 @@ describe( 'Android', () => { } ); it( 'improves apply animation performance by dismissing keyboard beforehand', async () => { - const { getByLabelText } = render( + const screen = render( {} } @@ -74,10 +72,12 @@ describe( 'Android', () => { } } /> ); - fireEvent.press( getByLabelText( 'Link' ) ); - fireEvent.press( getByLabelText( 'Link to, Search or type URL' ) ); + fireEvent.press( screen.getByLabelText( 'Link' ) ); + fireEvent.press( + screen.getByLabelText( 'Link to, Search or type URL' ) + ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => getByLabelText( 'Apply' ) ); + const backButton = await screen.findByLabelText( 'Apply' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); @@ -112,9 +112,7 @@ describe( 'iOS', () => { screen.getByLabelText( 'Link to, Search or type URL' ) ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => - screen.getByLabelText( 'Go back' ) - ); + const backButton = await screen.findByLabelText( 'Go back' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); @@ -122,7 +120,7 @@ describe( 'iOS', () => { } ); it( 'improves apply animation performance by dismissing keyboard beforehand', async () => { - const { getByLabelText } = render( + const screen = render( {} } @@ -133,10 +131,12 @@ describe( 'iOS', () => { } } /> ); - fireEvent.press( getByLabelText( 'Link' ) ); - fireEvent.press( getByLabelText( 'Link to, Search or type URL' ) ); + fireEvent.press( screen.getByLabelText( 'Link' ) ); + fireEvent.press( + screen.getByLabelText( 'Link to, Search or type URL' ) + ); // Await back button to allow async state updates to complete - const backButton = await waitFor( () => getByLabelText( 'Apply' ) ); + const backButton = await screen.findByLabelText( 'Apply' ); Keyboard.dismiss.mockClear(); fireEvent.press( backButton ); diff --git a/packages/format-library/src/link/test/modal.native.js b/packages/format-library/src/link/test/modal.native.js index 94d4499ef8906..36ba4480634de 100644 --- a/packages/format-library/src/link/test/modal.native.js +++ b/packages/format-library/src/link/test/modal.native.js @@ -9,7 +9,10 @@ import { render } from 'test/helpers'; describe( 'LinksUI', () => { it( 'LinksUI renders', () => { - const screen = render( ); - expect( screen.container ).toBeTruthy(); + const value = { text: '' }; // empty `RichTextValue` + const screen = render( + + ); + expect( screen.toJSON() ).toMatchSnapshot(); } ); } ); diff --git a/test/native/helpers.js b/test/native/helpers.js index 9a2bfda90c104..74c2fce2e4533 100644 --- a/test/native/helpers.js +++ b/test/native/helpers.js @@ -1,7 +1,3 @@ export * from '@testing-library/react-native'; export { measurePerformance } from 'reassure'; - export * from './integration-test-helpers'; - -// Override `waitFor` export with custom implementation -export { waitFor } from './integration-test-helpers'; diff --git a/test/native/integration-test-helpers/README.md b/test/native/integration-test-helpers/README.md index 49b63794ed8ff..b80370f79929c 100644 --- a/test/native/integration-test-helpers/README.md +++ b/test/native/integration-test-helpers/README.md @@ -94,10 +94,6 @@ Waits for a modal to be visible. Executes a function that triggers store resolvers and waits for them to be finished. -### [`waitFor`](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/wait-for.js) - -Custom implementation of the "waitFor" utility from `@testing-library/react-native` library. - ### [`withFakeTimers`](https://github.com/WordPress/gutenberg/blob/HEAD/test/native/integration-test-helpers/with-fake-timers.js) Set up fake timers for executing a function and restores them afterwards. diff --git a/test/native/integration-test-helpers/add-block.js b/test/native/integration-test-helpers/add-block.js index 197968be3edbf..800ce97d5228b 100644 --- a/test/native/integration-test-helpers/add-block.js +++ b/test/native/integration-test-helpers/add-block.js @@ -12,7 +12,6 @@ import { AccessibilityInfo } from 'react-native'; /** * Internal dependencies */ -import { waitFor } from './wait-for'; import { withFakeTimers } from './with-fake-timers'; /** @@ -28,13 +27,11 @@ export const addBlock = async ( blockName, { isPickerOpened } = {} ) => { - const { getByLabelText, getByTestId, getByText } = screen; - if ( ! isPickerOpened ) { - fireEvent.press( getByLabelText( 'Add block' ) ); + fireEvent.press( screen.getByLabelText( 'Add block' ) ); } - const blockList = getByTestId( 'InserterUI-Blocks' ); + const blockList = screen.getByTestId( 'InserterUI-Blocks' ); // onScroll event used to force the FlatList to render all items fireEvent.scroll( blockList, { nativeEvent: { @@ -44,7 +41,7 @@ export const addBlock = async ( }, } ); - fireEvent.press( await waitFor( () => getByText( blockName ) ) ); + fireEvent.press( await screen.findByText( blockName ) ); // On iOS the action for inserting a block is delayed (https://bit.ly/3AVALqH). // Hence, we need to wait for the different steps until the the block is inserted. diff --git a/test/native/integration-test-helpers/index.js b/test/native/integration-test-helpers/index.js index 13e74a69c4a66..5aca46049715c 100644 --- a/test/native/integration-test-helpers/index.js +++ b/test/native/integration-test-helpers/index.js @@ -24,6 +24,5 @@ export { transformBlock } from './transform-block'; export { triggerBlockListLayout } from './trigger-block-list-layout'; export { waitForModalVisible } from './wait-for-modal-visible'; export { waitForStoreResolvers } from './wait-for-store-resolvers'; -export { waitFor } from './wait-for'; export { withFakeTimers } from './with-fake-timers'; export { withReanimatedTimer } from './with-reanimated-timer'; diff --git a/test/native/integration-test-helpers/wait-for-modal-visible.js b/test/native/integration-test-helpers/wait-for-modal-visible.js index 1222fa5748bb9..045b50a60cdbb 100644 --- a/test/native/integration-test-helpers/wait-for-modal-visible.js +++ b/test/native/integration-test-helpers/wait-for-modal-visible.js @@ -1,7 +1,7 @@ /** - * Internal dependencies + * External dependencies */ -import { waitFor } from './wait-for'; +import { waitFor } from '@testing-library/react-native'; /** * Waits for a modal to be visible. @@ -9,5 +9,7 @@ import { waitFor } from './wait-for'; * @param {import('react-test-renderer').ReactTestInstance} modalInstance Modal test instance. */ export const waitForModalVisible = async ( modalInstance ) => { - return waitFor( () => modalInstance.props.isVisible ); + return waitFor( () => + expect( modalInstance.props.isVisible ).toBe( true ) + ); }; diff --git a/test/native/integration-test-helpers/wait-for.js b/test/native/integration-test-helpers/wait-for.js deleted file mode 100644 index 31afcdd1cdb5c..0000000000000 --- a/test/native/integration-test-helpers/wait-for.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * External dependencies - */ -import { act } from '@testing-library/react-native'; - -/** - * Custom implementation of the "waitFor" utility to - * prevent the issue: https://git.io/JYYGE - * - * @param {Function} cb Callback on which to wait. - * @param {Object} options Configuration options for waiting. - * @param {number} [options.timeout] Total time to wait for. If the execution exceeds this time, the call will be considered rejected. - * @param {number} [options.interval] Time to wait between calls. - * @return {*} Result of calling the callback function if it's not rejected. - */ -export function waitFor( - cb, - { timeout, interval } = { timeout: 1000, interval: 50 } -) { - let result; - let lastError; - const check = ( resolve, reject, time = 0 ) => { - try { - result = cb(); - } catch ( error ) { - lastError = error; - } - if ( ! result && time < timeout ) { - setTimeout( - () => check( resolve, reject, time + interval ), - interval - ); - return; - } - resolve( result ); - }; - return new Promise( ( resolve, reject ) => - act( - () => new Promise( ( internalResolve ) => check( internalResolve ) ) - ).then( () => { - if ( ! result ) { - reject( - `waitFor timed out after ${ timeout }ms for callback:\n${ cb }\n${ lastError.toString() }` - ); - return; - } - resolve( result ); - } ) - ); -}