From 96497814e47adf7b623f4fc83a4a2ea2b66b8a7f Mon Sep 17 00:00:00 2001 From: Seghir Nadir Date: Wed, 11 Sep 2024 15:44:09 +0200 Subject: [PATCH] Revert update to createRoot in Checkout block. (#51289) * Revert "[React 18] Use `createRoot().render()` instead of `render()` (#48843)" This reverts commit 752273e6ce40db6e738800a815e99581d2be2736. * Add changefile(s) from automation for the following project(s): woocommerce-blocks, woocommerce --------- Co-authored-by: github-actions --- .../js/atomic/utils/render-parent-block.tsx | 5 +- .../cart-checkout/form/test/index.js | 13 +- .../assets/js/base/utils/render-frontend.tsx | 80 +--- .../js/blocks/attribute-filter/test/block.tsx | 29 +- .../checkout-pickup-options-block/block.tsx | 23 +- .../checkout-shipping-methods-block/block.tsx | 30 +- .../assets/js/blocks/mini-cart/block.tsx | 28 +- .../assets/js/blocks/mini-cart/test/block.js | 23 +- .../js/blocks/rating-filter/test/block.tsx | 404 ++++++++-------- .../js/blocks/stock-filter/test/block.tsx | 447 +++++++++--------- .../51289-revert-react-18-in-checkout-block | 4 + .../shopper/checkout-block-coupons.spec.js | 20 +- 12 files changed, 525 insertions(+), 581 deletions(-) create mode 100644 plugins/woocommerce/changelog/51289-revert-react-18-in-checkout-block diff --git a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx index 24b6c1bd296..913371d0983 100644 --- a/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx +++ b/plugins/woocommerce-blocks/assets/js/atomic/utils/render-parent-block.tsx @@ -15,7 +15,6 @@ import { hasInnerBlocks, } from '@woocommerce/blocks-checkout'; import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary'; -import type { ReactRootWithContainer } from '@woocommerce/base-utils'; /** * This file contains logic used on the frontend to convert DOM elements (saved by the block editor) to React @@ -295,7 +294,7 @@ export const renderParentBlock = ( { selector: string; // Function to generate the props object for the block. getProps: ( el: Element, i: number ) => Record< string, unknown >; -} ): ReactRootWithContainer[] => { +} ): void => { /** * In addition to getProps, we need to render and return the children. This adds children to props. */ @@ -311,7 +310,7 @@ export const renderParentBlock = ( { /** * The only difference between using renderParentBlock and renderFrontend is that here we provide children. */ - return renderFrontend( { + renderFrontend( { Block, selector, getProps: getPropsWithChildren, diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/test/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/test/index.js index 9151c6e39e7..3b802cdb8dd 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/test/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/test/index.js @@ -21,11 +21,16 @@ jest.mock( '@wordpress/element', () => { }; } ); -const renderInCheckoutProvider = ( ui, options = {} ) => { +const renderInCheckoutProvider = ( ui, options = { legacyRoot: true } ) => { const Wrapper = ( { children } ) => { return { children }; }; const result = render( ui, { wrapper: Wrapper, ...options } ); + // We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React + // rendering error will be shown. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot` + ); return result; }; @@ -124,7 +129,7 @@ describe( 'Form Component', () => { ); }; - test( 'updates context value when interacting with form elements', async () => { + it( 'updates context value when interacting with form elements', async () => { renderInCheckoutProvider( <> @@ -150,7 +155,7 @@ describe( 'Form Component', () => { ); } ); - test( 'input fields update when changing the country', async () => { + it( 'input fields update when changing the country', async () => { renderInCheckoutProvider( ); await act( async () => { @@ -177,7 +182,7 @@ describe( 'Form Component', () => { expect( screen.getByLabelText( /Postal code/ ) ).toBeInTheDocument(); } ); - test( 'input values are reset after changing the country', async () => { + it( 'input values are reset after changing the country', async () => { renderInCheckoutProvider( ); // First enter an address with no state, but fill the city. diff --git a/plugins/woocommerce-blocks/assets/js/base/utils/render-frontend.tsx b/plugins/woocommerce-blocks/assets/js/base/utils/render-frontend.tsx index 0a1739a5407..4867fd9815f 100644 --- a/plugins/woocommerce-blocks/assets/js/base/utils/render-frontend.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/utils/render-frontend.tsx @@ -1,9 +1,8 @@ /** * External dependencies */ -import { createRoot, useEffect, Suspense } from '@wordpress/element'; +import { render, Suspense } from '@wordpress/element'; import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary'; -import type { Root } from 'react-dom/client'; // Some blocks take care of rendering their inner blocks automatically. For // example, the empty cart. In those cases, we don't want to trigger the render @@ -28,11 +27,6 @@ export type GetPropsFn< TAttributes extends Record< string, unknown > > = ( el: HTMLElement, i: number ) => BlockProps< TProps, TAttributes >; -export type ReactRootWithContainer = { - container: HTMLElement; - root: Root; -}; - interface RenderBlockParams< TProps extends Record< string, unknown >, TAttributes extends Record< string, unknown > @@ -61,32 +55,20 @@ export const renderBlock = < attributes = {} as TAttributes, props = {} as BlockProps< TProps, TAttributes >, errorBoundaryProps = {}, -}: RenderBlockParams< TProps, TAttributes > ): Root => { - const BlockWrapper = () => { - useEffect( () => { +}: RenderBlockParams< TProps, TAttributes > ): void => { + render( + + }> + { Block && } + + , + container, + () => { if ( container.classList ) { container.classList.remove( 'is-loading' ); } - }, [] ); - - return ( - - Loading... - } - > - { Block && ( - - ) } - - - ); - }; - - const root = createRoot( container ); - root.render( ); - return root; + } + ); }; interface RenderBlockInContainersParams< @@ -117,14 +99,10 @@ const renderBlockInContainers = < containers, getProps = () => ( {} as BlockProps< TProps, TAttributes > ), getErrorBoundaryProps = () => ( {} ), -}: RenderBlockInContainersParams< - TProps, - TAttributes -> ): ReactRootWithContainer[] => { +}: RenderBlockInContainersParams< TProps, TAttributes > ): void => { if ( containers.length === 0 ) { - return []; + return; } - const roots: ReactRootWithContainer[] = []; // Use Array.forEach for IE11 compatibility. Array.prototype.forEach.call( containers, ( el, i ) => { @@ -136,19 +114,14 @@ const renderBlockInContainers = < ...( props.attributes || {} ), }; - roots.push( { + renderBlock( { + Block, container: el, - root: renderBlock( { - Block, - container: el, - props, - attributes, - errorBoundaryProps, - } ), + props, + attributes, + errorBoundaryProps, } ); } ); - - return roots; }; // Given an element and a list of wrappers, check if the element is inside at @@ -184,10 +157,7 @@ const renderBlockOutsideWrappers = < getErrorBoundaryProps, selector, wrappers, -}: RenderBlockOutsideWrappersParams< - TProps, - TAttributes -> ): ReactRootWithContainer[] => { +}: RenderBlockOutsideWrappersParams< TProps, TAttributes > ): void => { const containers = document.body.querySelectorAll( selector ); // Filter out blocks inside the wrappers. if ( wrappers && wrappers.length > 0 ) { @@ -195,8 +165,7 @@ const renderBlockOutsideWrappers = < return ! isElementInsideWrappers( el, wrappers ); } ); } - - return renderBlockInContainers( { + renderBlockInContainers( { Block, containers, getProps, @@ -265,21 +234,20 @@ export const renderFrontend = < props: | RenderBlockOutsideWrappersParams< TProps, TAttributes > | RenderBlockInsideWrapperParams< TProps, TAttributes > -): ReactRootWithContainer[] => { +): void => { const wrappersToSkipOnLoad = document.body.querySelectorAll( selectorsToSkipOnLoad.join( ',' ) ); const { Block, getProps, getErrorBoundaryProps, selector } = props; - const roots = renderBlockOutsideWrappers( { + renderBlockOutsideWrappers( { Block, getProps, getErrorBoundaryProps, selector, wrappers: wrappersToSkipOnLoad, } ); - // For each wrapper, add an event listener to render the inner blocks when // `wc-blocks_render_blocks_frontend` event is triggered. Array.prototype.forEach.call( wrappersToSkipOnLoad, ( wrapper ) => { @@ -287,8 +255,6 @@ export const renderFrontend = < renderBlockInsideWrapper( { ...props, wrapper } ); } ); } ); - - return roots; }; export default renderFrontend; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/attribute-filter/test/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/attribute-filter/test/block.tsx index 3b1b191e7ff..ecdc9b111ae 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/attribute-filter/test/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/attribute-filter/test/block.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { act, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import * as hooks from '@woocommerce/base-context/hooks'; import userEvent from '@testing-library/user-event'; @@ -106,8 +106,14 @@ const setup = ( params: SetupParams ) => { results: stubCollectionData(), isLoading: false, } ); - const utils = render( ); - + const utils = render( , { + legacyRoot: true, + } ); + // We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React + // rendering error will be shown. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot` + ); const applyButton = screen.getByRole( 'button', { name: /apply/i } ); const smallAttributeCheckbox = screen.getByRole( 'checkbox', { name: /small/i, @@ -158,10 +164,8 @@ describe( 'Filter by Attribute block', () => { test( 'should enable Apply button when filter attributes are changed', async () => { const { applyButton, smallAttributeCheckbox } = setupWithoutSelectedFilterAttributes(); + await userEvent.click( smallAttributeCheckbox ); - await act( async () => { - await userEvent.click( smallAttributeCheckbox ); - } ); expect( applyButton ).not.toBeDisabled(); } ); } ); @@ -176,25 +180,18 @@ describe( 'Filter by Attribute block', () => { test( 'should enable Apply button when filter attributes are changed', async () => { const { applyButton, smallAttributeCheckbox } = setupWithSelectedFilterAttributes(); + await userEvent.click( smallAttributeCheckbox ); - await act( async () => { - await userEvent.click( smallAttributeCheckbox ); - } ); expect( applyButton ).not.toBeDisabled(); } ); test( 'should disable Apply button when deselecting the same previously selected attribute', async () => { const { applyButton, smallAttributeCheckbox } = setupWithSelectedFilterAttributes( { filterSize: 'small' } ); - - await act( async () => { - await userEvent.click( smallAttributeCheckbox ); - } ); + await userEvent.click( smallAttributeCheckbox ); expect( applyButton ).not.toBeDisabled(); - await act( async () => { - await userEvent.click( smallAttributeCheckbox ); - } ); + await userEvent.click( smallAttributeCheckbox ); expect( applyButton ).toBeDisabled(); } ); } ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx index 9d6984b6984..916662798cf 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx @@ -6,7 +6,6 @@ import { useState, useEffect, useCallback, - useMemo, createInterpolateElement, } from '@wordpress/element'; import { useShippingData, useStoreCart } from '@woocommerce/base-context/hooks'; @@ -139,12 +138,10 @@ const renderPickupLocation = ( const Block = (): JSX.Element | null => { const { shippingRates, selectShippingRate } = useShippingData(); - // Memoize pickup locations to prevent re-rendering when the shipping rates change. - const pickupLocations = useMemo( () => { - return ( shippingRates[ 0 ]?.shipping_rates || [] ).filter( - isPackageRateCollectable - ); - }, [ shippingRates ] ); + // Get pickup locations from the first shipping package. + const pickupLocations = ( shippingRates[ 0 ]?.shipping_rates || [] ).filter( + isPackageRateCollectable + ); const [ selectedOption, setSelectedOption ] = useState< string >( () => pickupLocations.find( ( rate ) => rate.selected )?.rate_id || '' @@ -171,19 +168,13 @@ const Block = (): JSX.Element | null => { renderPickupLocation, }; + // Update the selected option if there is no rate selected on mount. useEffect( () => { - if ( - ! selectedOption && - pickupLocations[ 0 ] && - selectedOption !== pickupLocations[ 0 ].rate_id - ) { + if ( ! selectedOption && pickupLocations[ 0 ] ) { setSelectedOption( pickupLocations[ 0 ].rate_id ); onSelectRate( pickupLocations[ 0 ].rate_id ); } - // Removing onSelectRate as it lead to an infinite loop when only one pickup location is available. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ pickupLocations, selectedOption ] ); - + }, [ onSelectRate, pickupLocations, selectedOption ] ); const packageCount = getShippingRatesPackageCount( shippingRates ); return ( <> diff --git a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 80362a4b009..13633478b0c 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -26,7 +26,6 @@ import type { } from '@woocommerce/types'; import NoticeBanner from '@woocommerce/base-components/notice-banner'; import type { ReactElement } from 'react'; -import { useMemo } from '@wordpress/element'; /** * Renders a shipping rate control option. @@ -74,22 +73,19 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => { const { shippingAddress } = useCustomerData(); - const filteredShippingRates = useMemo( () => { - return isCollectable - ? shippingRates.map( ( shippingRatesPackage ) => { - return { - ...shippingRatesPackage, - shipping_rates: - shippingRatesPackage.shipping_rates.filter( - ( shippingRatesPackageRate ) => - ! hasCollectableRate( - shippingRatesPackageRate.method_id - ) - ), - }; - } ) - : shippingRates; - }, [ shippingRates, isCollectable ] ); + const filteredShippingRates = isCollectable + ? shippingRates.map( ( shippingRatesPackage ) => { + return { + ...shippingRatesPackage, + shipping_rates: shippingRatesPackage.shipping_rates.filter( + ( shippingRatesPackageRate ) => + ! hasCollectableRate( + shippingRatesPackageRate.method_id + ) + ), + }; + } ) + : shippingRates; if ( ! needsShipping ) { return null; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/block.tsx index 77b4097c39c..59e33ddcd74 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/block.tsx @@ -20,10 +20,15 @@ import { isCartResponseTotals, isNumber, } from '@woocommerce/types'; -import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; +import { + unmountComponentAtNode, + useCallback, + useEffect, + useRef, + useState, +} from '@wordpress/element'; import { sprintf, _n } from '@wordpress/i18n'; import clsx from 'clsx'; -import type { ReactRootWithContainer } from '@woocommerce/base-utils'; /** * Internal dependencies @@ -105,8 +110,6 @@ const MiniCartBlock = ( attributes: Props ): JSX.Element => { setContentsNode( node ); }, [] ); - const rootRef = useRef< ReactRootWithContainer[] | null >( null ); - useEffect( () => { const body = document.querySelector( 'body' ); if ( body ) { @@ -131,7 +134,7 @@ const MiniCartBlock = ( attributes: Props ): JSX.Element => { return; } if ( isOpen ) { - const renderedBlock = renderParentBlock( { + renderParentBlock( { Block: MiniCartContentsBlock, blockName, getProps: ( el: Element ) => { @@ -148,25 +151,16 @@ const MiniCartBlock = ( attributes: Props ): JSX.Element => { selector: '.wp-block-woocommerce-mini-cart-contents', blockMap: getRegisteredBlockComponents( blockName ), } ); - rootRef.current = renderedBlock; } } return () => { if ( contentsNode instanceof Element && isOpen ) { - const unmountingContainer = contentsNode.querySelector( + const container = contentsNode.querySelector( '.wp-block-woocommerce-mini-cart-contents' ); - - if ( unmountingContainer ) { - const foundRoot = rootRef?.current?.find( - ( { container } ) => unmountingContainer === container - ); - if ( typeof foundRoot?.root?.unmount === 'function' ) { - setTimeout( () => { - foundRoot.root.unmount(); - } ); - } + if ( container ) { + unmountComponentAtNode( container ); } } }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/test/block.js b/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/test/block.js index 94a9644b75b..313a7c3f3af 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/test/block.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/mini-cart/test/block.js @@ -111,6 +111,13 @@ describe( 'Testing Mini-Cart', () => { await waitFor( () => expect( screen.getByText( /your cart/i ) ).toBeInTheDocument() ); + + // The opening of the drawer uses deprecated ReactDOM.render. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot%s`, + // The stack trace + expect.any( String ) + ); } ); it( 'closes the drawer when clicking on the close button', async () => { @@ -125,11 +132,9 @@ describe( 'Testing Mini-Cart', () => { // Close drawer. let closeButton = null; - await waitFor( () => { closeButton = screen.getByLabelText( /close/i ); } ); - if ( closeButton ) { await act( async () => { await user.click( closeButton ); @@ -141,6 +146,13 @@ describe( 'Testing Mini-Cart', () => { screen.queryByText( /your cart/i ) ).not.toBeInTheDocument(); } ); + + // The opening of the drawer uses deprecated ReactDOM.render. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot%s`, + // The stack trace + expect.any( String ) + ); } ); it( 'renders empty cart if there are no items in the cart', async () => { @@ -155,6 +167,13 @@ describe( 'Testing Mini-Cart', () => { } ); expect( fetchMock ).toHaveBeenCalledTimes( 1 ); + + // The opening of the drawer uses deprecated ReactDOM.render. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot%s`, + // The stack trace + expect.any( String ) + ); } ); it( 'updates contents when removed from cart event is triggered', async () => { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/test/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/test/block.tsx index 2b1483dd00f..d442d9bc001 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/test/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/test/block.tsx @@ -2,14 +2,7 @@ * External dependencies */ import React from '@wordpress/element'; -import { - act, - cleanup, - render, - screen, - waitFor, - within, -} from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; import * as hooks from '@woocommerce/base-context/hooks'; import userEvent from '@testing-library/user-event'; @@ -66,7 +59,6 @@ const selectors = { }; const setup = ( params: SetupParams ) => { - cleanup(); const url = `http://woo.local/${ params.filterRating ? '?rating_filter=' + params.filterRating : '' }`; @@ -86,7 +78,14 @@ const setup = ( params: SetupParams ) => { } ); const { container, ...utils } = render( - + , + { legacyRoot: true } + ); + + // We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React + // rendering error will be shown. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot` ); const getList = () => container.querySelector( selectors.list ); @@ -204,81 +203,74 @@ describe( 'Filter by Rating block', () => { describe( 'Single choice Dropdown', () => { test( 'renders dropdown', () => { const { getDropdown, getList } = setupSingleChoiceDropdown(); - expect( getDropdown() ).toBeInTheDocument(); expect( getList() ).toBeNull(); } ); - test( 'renders chips based on URL params', async () => { - await waitFor( async () => { - const ratingParam = '2'; - const { getRating2Chips, getRating4Chips, getRating5Chips } = - setupSingleChoiceDropdown( ratingParam ); + test( 'renders chips based on URL params', () => { + const ratingParam = '2'; + const { getRating2Chips, getRating4Chips, getRating5Chips } = + setupSingleChoiceDropdown( ratingParam ); - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeNull(); - expect( getRating5Chips() ).toBeNull(); - } ); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeNull(); + expect( getRating5Chips() ).toBeNull(); } ); test( 'replaces chosen option when another one is clicked', async () => { - await waitFor( async () => { - const ratingParam = '2'; - const { - getDropdown, - getRating2Chips, - getRating4Chips, - getRating4Suggestion, - } = setupSingleChoiceDropdown( ratingParam ); + const ratingParam = '2'; + const { + getDropdown, + getRating2Chips, + getRating4Chips, + getRating4Suggestion, + } = setupSingleChoiceDropdown( ratingParam ); - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeNull(); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeNull(); - const dropdown = getDropdown(); + const dropdown = getDropdown(); - if ( dropdown ) { - await userEvent.click( dropdown ); - acceptErrorWithDuplicatedKeys(); - } + if ( dropdown ) { + await userEvent.click( dropdown ); + acceptErrorWithDuplicatedKeys(); + } - const rating4Suggestion = getRating4Suggestion(); + const rating4Suggestion = getRating4Suggestion(); - if ( rating4Suggestion ) { - await userEvent.click( rating4Suggestion ); - } + if ( rating4Suggestion ) { + await userEvent.click( rating4Suggestion ); + } - expect( getRating2Chips() ).toBeNull(); - expect( getRating4Chips() ).toBeInTheDocument(); - } ); + expect( getRating2Chips() ).toBeNull(); + expect( getRating4Chips() ).toBeInTheDocument(); } ); test( 'removes the option when the X button is clicked', async () => { - await waitFor( async () => { - const ratingParam = '4'; - const { - getRating2Chips, - getRating4Chips, - getRating5Chips, - getRemoveButtonFromChips, - } = setupMultipleChoiceDropdown( ratingParam ); + const ratingParam = '4'; + const { + getRating2Chips, + getRating4Chips, + getRating5Chips, + getRemoveButtonFromChips, + } = setupMultipleChoiceDropdown( ratingParam ); - expect( getRating2Chips() ).toBeNull(); - expect( getRating4Chips() ).toBeInTheDocument(); - expect( getRating5Chips() ).toBeNull(); + expect( getRating2Chips() ).toBeNull(); + expect( getRating4Chips() ).toBeInTheDocument(); + expect( getRating5Chips() ).toBeNull(); - const removeRating4Button = getRemoveButtonFromChips( - getRating4Chips() - ); + const removeRating4Button = getRemoveButtonFromChips( + getRating4Chips() + ); - if ( removeRating4Button ) { - await userEvent.click( removeRating4Button ); - acceptErrorWithDuplicatedKeys(); - } + if ( removeRating4Button ) { + await userEvent.click( removeRating4Button ); + acceptErrorWithDuplicatedKeys(); + } - expect( getRating2Chips() ).toBeNull(); - expect( getRating4Chips() ).toBeNull(); - expect( getRating5Chips() ).toBeNull(); - } ); + expect( getRating2Chips() ).toBeNull(); + expect( getRating4Chips() ).toBeNull(); + expect( getRating5Chips() ).toBeNull(); } ); } ); @@ -289,89 +281,83 @@ describe( 'Filter by Rating block', () => { expect( getList() ).toBeNull(); } ); - test( 'renders chips based on URL params', async () => { - await waitFor( async () => { - const ratingParam = '2,4'; - const { getRating2Chips, getRating4Chips, getRating5Chips } = - setupMultipleChoiceDropdown( ratingParam ); + test( 'renders chips based on URL params', () => { + const ratingParam = '2,4'; + const { getRating2Chips, getRating4Chips, getRating5Chips } = + setupMultipleChoiceDropdown( ratingParam ); - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeInTheDocument(); - expect( getRating5Chips() ).toBeNull(); - } ); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeInTheDocument(); + expect( getRating5Chips() ).toBeNull(); } ); test( 'adds chosen option to another one that is clicked', async () => { - await waitFor( async () => { - const ratingParam = '2'; - const { - getDropdown, - getRating2Chips, - getRating4Chips, - getRating5Chips, - getRating4Suggestion, - getRating5Suggestion, - } = setupMultipleChoiceDropdown( ratingParam ); + const ratingParam = '2'; + const { + getDropdown, + getRating2Chips, + getRating4Chips, + getRating5Chips, + getRating4Suggestion, + getRating5Suggestion, + } = setupMultipleChoiceDropdown( ratingParam ); - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeNull(); - expect( getRating5Chips() ).toBeNull(); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeNull(); + expect( getRating5Chips() ).toBeNull(); - const dropdown = getDropdown(); + const dropdown = getDropdown(); - if ( dropdown ) { - await userEvent.click( dropdown ); - acceptErrorWithDuplicatedKeys(); - } + if ( dropdown ) { + await userEvent.click( dropdown ); + acceptErrorWithDuplicatedKeys(); + } - const rating4Suggestion = getRating4Suggestion(); + const rating4Suggestion = getRating4Suggestion(); - if ( rating4Suggestion ) { - await userEvent.click( rating4Suggestion ); - } + if ( rating4Suggestion ) { + await userEvent.click( rating4Suggestion ); + } - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeInTheDocument(); - expect( getRating5Chips() ).toBeNull(); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeInTheDocument(); + expect( getRating5Chips() ).toBeNull(); - const rating5Suggestion = getRating5Suggestion(); + const rating5Suggestion = getRating5Suggestion(); - if ( rating5Suggestion ) { - await userEvent.click( rating5Suggestion ); - } + if ( rating5Suggestion ) { + await userEvent.click( rating5Suggestion ); + } - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeInTheDocument(); - expect( getRating5Chips() ).toBeInTheDocument(); - } ); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeInTheDocument(); + expect( getRating5Chips() ).toBeInTheDocument(); } ); test( 'removes the option when the X button is clicked', async () => { - await waitFor( async () => { - const ratingParam = '2,4,5'; - const { - getRating2Chips, - getRating4Chips, - getRating5Chips, - getRemoveButtonFromChips, - } = setupMultipleChoiceDropdown( ratingParam ); + const ratingParam = '2,4,5'; + const { + getRating2Chips, + getRating4Chips, + getRating5Chips, + getRemoveButtonFromChips, + } = setupMultipleChoiceDropdown( ratingParam ); - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeInTheDocument(); - expect( getRating5Chips() ).toBeInTheDocument(); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeInTheDocument(); + expect( getRating5Chips() ).toBeInTheDocument(); - const removeRating4Button = getRemoveButtonFromChips( - getRating4Chips() - ); + const removeRating4Button = getRemoveButtonFromChips( + getRating4Chips() + ); - if ( removeRating4Button ) { - await userEvent.click( removeRating4Button ); - } + if ( removeRating4Button ) { + await userEvent.click( removeRating4Button ); + } - expect( getRating2Chips() ).toBeInTheDocument(); - expect( getRating4Chips() ).toBeNull(); - expect( getRating5Chips() ).toBeInTheDocument(); - } ); + expect( getRating2Chips() ).toBeInTheDocument(); + expect( getRating4Chips() ).toBeNull(); + expect( getRating5Chips() ).toBeInTheDocument(); } ); } ); @@ -382,67 +368,61 @@ describe( 'Filter by Rating block', () => { expect( getList() ).toBeInTheDocument(); } ); - test( 'renders checked options based on URL params', async () => { - await waitFor( async () => { - const ratingParam = '4'; - const { - getRating2Checkbox, - getRating4Checkbox, - getRating5Checkbox, - } = setupSingleChoiceList( ratingParam ); + test( 'renders checked options based on URL params', () => { + const ratingParam = '4'; + const { + getRating2Checkbox, + getRating4Checkbox, + getRating5Checkbox, + } = setupSingleChoiceList( ratingParam ); - expect( getRating2Checkbox()?.checked ).toBeFalsy(); - expect( getRating4Checkbox()?.checked ).toBeTruthy(); - expect( getRating5Checkbox()?.checked ).toBeFalsy(); - } ); + expect( getRating2Checkbox()?.checked ).toBeFalsy(); + expect( getRating4Checkbox()?.checked ).toBeTruthy(); + expect( getRating5Checkbox()?.checked ).toBeFalsy(); } ); test( 'replaces chosen option when another one is clicked', async () => { - await waitFor( async () => { - const ratingParam = '2'; - const { - getRating2Checkbox, - getRating4Checkbox, - getRating5Checkbox, - } = setupSingleChoiceList( ratingParam ); + const ratingParam = '2'; + const { + getRating2Checkbox, + getRating4Checkbox, + getRating5Checkbox, + } = setupSingleChoiceList( ratingParam ); - expect( getRating2Checkbox()?.checked ).toBeTruthy(); - expect( getRating4Checkbox()?.checked ).toBeFalsy(); - expect( getRating5Checkbox()?.checked ).toBeFalsy(); + expect( getRating2Checkbox()?.checked ).toBeTruthy(); + expect( getRating4Checkbox()?.checked ).toBeFalsy(); + expect( getRating5Checkbox()?.checked ).toBeFalsy(); - const rating4checkbox = getRating4Checkbox(); + const rating4checkbox = getRating4Checkbox(); - if ( rating4checkbox ) { - await act( async () => { - await userEvent.click( rating4checkbox ); - } ); - } + if ( rating4checkbox ) { + await userEvent.click( rating4checkbox ); + } - expect( getRating2Checkbox()?.checked ).toBeFalsy(); - expect( getRating4Checkbox()?.checked ).toBeTruthy(); - expect( getRating5Checkbox()?.checked ).toBeFalsy(); - } ); + expect( getRating2Checkbox()?.checked ).toBeFalsy(); + expect( getRating4Checkbox()?.checked ).toBeTruthy(); + expect( getRating5Checkbox()?.checked ).toBeFalsy(); } ); test( 'removes the option when it is clicked again', async () => { - await waitFor( async () => { - const ratingParam = '4'; - const { - getRating2Checkbox, - getRating4Checkbox, - getRating5Checkbox, - } = setupMultipleChoiceList( ratingParam ); + const ratingParam = '4'; + const { + getRating2Checkbox, + getRating4Checkbox, + getRating5Checkbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getRating2Checkbox()?.checked ).toBeFalsy(); - expect( getRating4Checkbox()?.checked ).toBeTruthy(); - expect( getRating5Checkbox()?.checked ).toBeFalsy(); + expect( getRating2Checkbox()?.checked ).toBeFalsy(); + expect( getRating4Checkbox()?.checked ).toBeTruthy(); + expect( getRating5Checkbox()?.checked ).toBeFalsy(); - const rating4checkbox = getRating4Checkbox(); + const rating4checkbox = getRating4Checkbox(); - if ( rating4checkbox ) { - await userEvent.click( rating4checkbox ); - } + if ( rating4checkbox ) { + await userEvent.click( rating4checkbox ); + } + await waitFor( () => { expect( getRating2Checkbox()?.checked ).toBeFalsy(); expect( getRating4Checkbox()?.checked ).toBeFalsy(); expect( getRating5Checkbox()?.checked ).toBeFalsy(); @@ -457,40 +437,38 @@ describe( 'Filter by Rating block', () => { expect( getList() ).toBeInTheDocument(); } ); - test( 'renders chips based on URL params', async () => { - await waitFor( async () => { - const ratingParam = '4,5'; - const { - getRating2Checkbox, - getRating4Checkbox, - getRating5Checkbox, - } = setupMultipleChoiceList( ratingParam ); + test( 'renders chips based on URL params', () => { + const ratingParam = '4,5'; + const { + getRating2Checkbox, + getRating4Checkbox, + getRating5Checkbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getRating2Checkbox()?.checked ).toBeFalsy(); - expect( getRating4Checkbox()?.checked ).toBeTruthy(); - expect( getRating5Checkbox()?.checked ).toBeTruthy(); - } ); + expect( getRating2Checkbox()?.checked ).toBeFalsy(); + expect( getRating4Checkbox()?.checked ).toBeTruthy(); + expect( getRating5Checkbox()?.checked ).toBeTruthy(); } ); test( 'adds chosen option to another one that is clicked', async () => { - await waitFor( async () => { - const ratingParam = '2,4'; - const { - getRating2Checkbox, - getRating4Checkbox, - getRating5Checkbox, - } = setupMultipleChoiceList( ratingParam ); + const ratingParam = '2,4'; + const { + getRating2Checkbox, + getRating4Checkbox, + getRating5Checkbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getRating2Checkbox()?.checked ).toBeTruthy(); - expect( getRating4Checkbox()?.checked ).toBeTruthy(); - expect( getRating5Checkbox()?.checked ).toBeFalsy(); + expect( getRating2Checkbox()?.checked ).toBeTruthy(); + expect( getRating4Checkbox()?.checked ).toBeTruthy(); + expect( getRating5Checkbox()?.checked ).toBeFalsy(); - const rating5checkbox = getRating5Checkbox(); + const rating5checkbox = getRating5Checkbox(); - if ( rating5checkbox ) { - await userEvent.click( rating5checkbox ); - } + if ( rating5checkbox ) { + await userEvent.click( rating5checkbox ); + } + await waitFor( () => { expect( getRating2Checkbox()?.checked ).toBeTruthy(); expect( getRating4Checkbox()?.checked ).toBeTruthy(); expect( getRating5Checkbox()?.checked ).toBeTruthy(); @@ -498,24 +476,24 @@ describe( 'Filter by Rating block', () => { } ); test( 'removes the option when it is clicked again', async () => { - await waitFor( async () => { - const ratingParam = '2,4'; - const { - getRating2Checkbox, - getRating4Checkbox, - getRating5Checkbox, - } = setupMultipleChoiceList( ratingParam ); + const ratingParam = '2,4'; + const { + getRating2Checkbox, + getRating4Checkbox, + getRating5Checkbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getRating2Checkbox()?.checked ).toBeTruthy(); - expect( getRating4Checkbox()?.checked ).toBeTruthy(); - expect( getRating5Checkbox()?.checked ).toBeFalsy(); + expect( getRating2Checkbox()?.checked ).toBeTruthy(); + expect( getRating4Checkbox()?.checked ).toBeTruthy(); + expect( getRating5Checkbox()?.checked ).toBeFalsy(); - const rating2checkbox = getRating2Checkbox(); + const rating2checkbox = getRating2Checkbox(); - if ( rating2checkbox ) { - await userEvent.click( rating2checkbox ); - } + if ( rating2checkbox ) { + await userEvent.click( rating2checkbox ); + } + await waitFor( () => { expect( getRating2Checkbox()?.checked ).toBeFalsy(); expect( getRating4Checkbox()?.checked ).toBeTruthy(); expect( getRating5Checkbox()?.checked ).toBeFalsy(); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/test/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/test/block.tsx index e5f52059a5a..bc1b1c59c58 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/test/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/test/block.tsx @@ -2,14 +2,7 @@ * External dependencies */ import React from '@wordpress/element'; -import { - act, - cleanup, - render, - screen, - within, - waitFor, -} from '@testing-library/react'; +import { act, render, screen, within, waitFor } from '@testing-library/react'; import { default as fetchMock } from 'jest-fetch-mock'; import userEvent from '@testing-library/user-event'; @@ -75,7 +68,6 @@ const selectors = { }; const setup = ( params: SetupParams = {} ) => { - cleanup(); const url = `http://woo.local/${ params.filterStock ? '?filter_stock_status=' + params.filterStock : '' }`; @@ -95,7 +87,14 @@ const setup = ( params: SetupParams = {} ) => { }; const { container, ...utils } = render( - + , + { legacyRoot: true } + ); + + // We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React + // rendering error will be shown. + expect( console ).toHaveErroredWith( + `Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot` ); const getList = () => container.querySelector( selectors.list ); @@ -228,7 +227,7 @@ describe( 'Filter by Stock block', () => { fetchMock.resetMocks(); } ); - test( 'renders the stock filter block', async () => { + it( 'renders the stock filter block', async () => { const { container } = setup( { showFilterButton: false, showCounts: false, @@ -236,7 +235,7 @@ describe( 'Filter by Stock block', () => { expect( container ).toMatchSnapshot(); } ); - test( 'renders the stock filter block with the filter button', async () => { + it( 'renders the stock filter block with the filter button', async () => { const { container } = setup( { showFilterButton: true, showCounts: false, @@ -244,7 +243,7 @@ describe( 'Filter by Stock block', () => { expect( container ).toMatchSnapshot(); } ); - test( 'renders the stock filter block with the product counts', async () => { + it( 'renders the stock filter block with the product counts', async () => { const { container } = setup( { showFilterButton: false, showCounts: true, @@ -255,86 +254,80 @@ describe( 'Filter by Stock block', () => { describe( 'Single choice Dropdown', () => { test( 'renders dropdown', () => { const { getDropdown, getList } = setupSingleChoiceDropdown(); - expect( getDropdown() ).toBeInTheDocument(); expect( getList() ).toBeNull(); } ); - test( 'renders chips based on URL params', async () => { - await waitFor( async () => { - const ratingParam = 'instock'; - const { - getInStockChips, - getOutOfStockChips, - getOnBackorderChips, - } = setupSingleChoiceDropdown( ratingParam ); + test( 'renders chips based on URL params', () => { + const ratingParam = 'instock'; + const { getInStockChips, getOutOfStockChips, getOnBackorderChips } = + setupSingleChoiceDropdown( ratingParam ); - expect( getInStockChips() ).toBeInTheDocument(); - expect( getOutOfStockChips() ).toBeNull(); - expect( getOnBackorderChips() ).toBeNull(); - } ); + expect( getInStockChips() ).toBeInTheDocument(); + expect( getOutOfStockChips() ).toBeNull(); + expect( getOnBackorderChips() ).toBeNull(); } ); test( 'replaces chosen option when another one is clicked', async () => { - await waitFor( async () => { - const user = userEvent.setup(); - const ratingParam = 'instock'; - const { - getDropdown, - getInStockChips, - getOutOfStockChips, - getOutOfStockSuggestion, - } = setupSingleChoiceDropdown( ratingParam ); + const user = userEvent.setup(); + const ratingParam = 'instock'; + const { + getDropdown, + getInStockChips, + getOutOfStockChips, + getOutOfStockSuggestion, + } = setupSingleChoiceDropdown( ratingParam ); - expect( getInStockChips() ).toBeInTheDocument(); - expect( getOutOfStockChips() ).toBeNull(); + expect( getInStockChips() ).toBeInTheDocument(); + expect( getOutOfStockChips() ).toBeNull(); - const dropdown = getDropdown(); + const dropdown = getDropdown(); - if ( dropdown ) { - await act( async () => { - await user.click( dropdown ); - } ); - } + if ( dropdown ) { + await act( async () => { + await user.click( dropdown ); + } ); + } - const outOfStockSuggestion = getOutOfStockSuggestion(); + const outOfStockSuggestion = getOutOfStockSuggestion(); - if ( outOfStockSuggestion ) { - await act( async () => { - await user.click( outOfStockSuggestion ); - } ); - } + if ( outOfStockSuggestion ) { + await act( async () => { + await user.click( outOfStockSuggestion ); + } ); + } - expect( getInStockChips() ).toBeNull(); - expect( getOutOfStockChips() ).toBeInTheDocument(); - } ); + expect( getInStockChips() ).toBeNull(); + expect( getOutOfStockChips() ).toBeInTheDocument(); } ); test( 'removes the option when the X button is clicked', async () => { - await waitFor( async () => { - const user = userEvent.setup(); - const ratingParam = 'outofstock'; - const { - getInStockChips, - getOutOfStockChips, - getOnBackorderChips, - getRemoveButtonFromChips, - } = setupMultipleChoiceDropdown( ratingParam ); + const user = userEvent.setup(); + const ratingParam = 'outofstock'; + const { + getInStockChips, + getOutOfStockChips, + getOnBackorderChips, + getRemoveButtonFromChips, + } = setupMultipleChoiceDropdown( ratingParam ); + await waitFor( () => { expect( getInStockChips() ).toBeNull(); expect( getOutOfStockChips() ).toBeInTheDocument(); expect( getOnBackorderChips() ).toBeNull(); + } ); - const removeOutOfStockButton = getRemoveButtonFromChips( - getOutOfStockChips() - ); + const removeOutOfStockButton = getRemoveButtonFromChips( + getOutOfStockChips() + ); - if ( removeOutOfStockButton ) { - await act( async () => { - await user.click( removeOutOfStockButton ); - } ); - } + if ( removeOutOfStockButton ) { + act( async () => { + await user.click( removeOutOfStockButton ); + } ); + } + await waitFor( () => { expect( getInStockChips() ).toBeNull(); expect( getOutOfStockChips() ).toBeNull(); expect( getOnBackorderChips() ).toBeNull(); @@ -349,65 +342,67 @@ describe( 'Filter by Stock block', () => { expect( getList() ).toBeNull(); } ); - test( 'renders chips based on URL params', async () => { - await waitFor( async () => { - const ratingParam = 'instock,onbackorder'; - const { - getInStockChips, - getOutOfStockChips, - getOnBackorderChips, - } = setupMultipleChoiceDropdown( ratingParam ); + test( 'renders chips based on URL params', () => { + const ratingParam = 'instock,onbackorder'; + const { getInStockChips, getOutOfStockChips, getOnBackorderChips } = + setupMultipleChoiceDropdown( ratingParam ); - expect( getInStockChips() ).toBeInTheDocument(); - expect( getOutOfStockChips() ).toBeNull(); - expect( getOnBackorderChips() ).toBeInTheDocument(); - } ); + expect( getInStockChips() ).toBeInTheDocument(); + expect( getOutOfStockChips() ).toBeNull(); + expect( getOnBackorderChips() ).toBeInTheDocument(); } ); test( 'adds chosen option to another one that is clicked', async () => { - await waitFor( async () => { - const user = userEvent.setup(); - const ratingParam = 'onbackorder'; - const { - getDropdown, - getInStockChips, - getOutOfStockChips, - getOnBackorderChips, - getInStockSuggestion, - getOutOfStockSuggestion, - } = setupMultipleChoiceDropdown( ratingParam ); + const user = userEvent.setup(); + const ratingParam = 'onbackorder'; + const { + getDropdown, + getInStockChips, + getOutOfStockChips, + getOnBackorderChips, + getInStockSuggestion, + getOutOfStockSuggestion, + } = setupMultipleChoiceDropdown( ratingParam ); + await waitFor( () => { expect( getInStockChips() ).toBeNull(); expect( getOutOfStockChips() ).toBeNull(); expect( getOnBackorderChips() ).toBeInTheDocument(); + } ); + const dropdown = getDropdown(); - const dropdown = getDropdown(); - - if ( dropdown ) { + if ( dropdown ) { + await act( async () => { await user.click( dropdown ); - } + } ); + } - const inStockSuggestion = getInStockSuggestion(); + const inStockSuggestion = getInStockSuggestion(); - if ( inStockSuggestion ) { + if ( inStockSuggestion ) { + await act( async () => { await user.click( inStockSuggestion ); - } + } ); + } - expect( getInStockChips() ).toBeInTheDocument(); - expect( getOutOfStockChips() ).toBeNull(); - expect( getOnBackorderChips() ).toBeInTheDocument(); + expect( getInStockChips() ).toBeInTheDocument(); + expect( getOutOfStockChips() ).toBeNull(); + expect( getOnBackorderChips() ).toBeInTheDocument(); - const freshDropdown = getDropdown(); - if ( freshDropdown ) { + const freshDropdown = getDropdown(); + if ( freshDropdown ) { + await act( async () => { await user.click( freshDropdown ); - } + } ); + } - const outOfStockSuggestion = getOutOfStockSuggestion(); + const outOfStockSuggestion = getOutOfStockSuggestion(); - if ( outOfStockSuggestion ) { - await userEvent.click( outOfStockSuggestion ); - } + if ( outOfStockSuggestion ) { + userEvent.click( outOfStockSuggestion ); + } + await waitFor( () => { expect( getInStockChips() ).toBeInTheDocument(); expect( getOutOfStockChips() ).toBeInTheDocument(); expect( getOnBackorderChips() ).toBeInTheDocument(); @@ -415,30 +410,32 @@ describe( 'Filter by Stock block', () => { } ); test( 'removes the option when the X button is clicked', async () => { - await waitFor( async () => { - const user = userEvent.setup(); - const ratingParam = 'instock,outofstock,onbackorder'; - const { - getInStockChips, - getOutOfStockChips, - getOnBackorderChips, - getRemoveButtonFromChips, - } = setupMultipleChoiceDropdown( ratingParam ); + const user = userEvent.setup(); + const ratingParam = 'instock,outofstock,onbackorder'; + const { + getInStockChips, + getOutOfStockChips, + getOnBackorderChips, + getRemoveButtonFromChips, + } = setupMultipleChoiceDropdown( ratingParam ); + await waitFor( () => { expect( getInStockChips() ).toBeInTheDocument(); expect( getOutOfStockChips() ).toBeInTheDocument(); expect( getOnBackorderChips() ).toBeInTheDocument(); + } ); - const removeOutOfStockButton = getRemoveButtonFromChips( - getOutOfStockChips() - ); + const removeOutOfStockButton = getRemoveButtonFromChips( + getOutOfStockChips() + ); - if ( removeOutOfStockButton ) { - await act( async () => { - await user.click( removeOutOfStockButton ); - } ); - } + if ( removeOutOfStockButton ) { + act( async () => { + await user.click( removeOutOfStockButton ); + } ); + } + await waitFor( () => { expect( getInStockChips() ).toBeInTheDocument(); expect( getOutOfStockChips() ).toBeNull(); expect( getOnBackorderChips() ).toBeInTheDocument(); @@ -453,73 +450,67 @@ describe( 'Filter by Stock block', () => { expect( getList() ).toBeInTheDocument(); } ); - test( 'renders checked options based on URL params', async () => { - await waitFor( async () => { - const ratingParam = 'instock'; - const { - getInStockCheckbox, - getOutOfStockCheckbox, - getOnBackorderCheckbox, - } = setupSingleChoiceList( ratingParam ); + test( 'renders checked options based on URL params', () => { + const ratingParam = 'instock'; + const { + getInStockCheckbox, + getOutOfStockCheckbox, + getOnBackorderCheckbox, + } = setupSingleChoiceList( ratingParam ); - expect( getInStockCheckbox()?.checked ).toBeTruthy(); - expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); - expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); - } ); + expect( getInStockCheckbox()?.checked ).toBeTruthy(); + expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); + expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); } ); test( 'replaces chosen option when another one is clicked', async () => { - await waitFor( async () => { - const user = userEvent.setup(); - const ratingParam = 'outofstock'; - const { - getInStockCheckbox, - getOutOfStockCheckbox, - getOnBackorderCheckbox, - } = setupSingleChoiceList( ratingParam ); + const user = userEvent.setup(); + const ratingParam = 'outofstock'; + const { + getInStockCheckbox, + getOutOfStockCheckbox, + getOnBackorderCheckbox, + } = setupSingleChoiceList( ratingParam ); - expect( getInStockCheckbox()?.checked ).toBeFalsy(); - expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); - expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); + expect( getInStockCheckbox()?.checked ).toBeFalsy(); + expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); + expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); - const onBackorderCheckbox = getOnBackorderCheckbox(); + const onBackorderCheckbox = getOnBackorderCheckbox(); - if ( onBackorderCheckbox ) { - await act( async () => { - await user.click( onBackorderCheckbox ); - } ); - } + if ( onBackorderCheckbox ) { + await act( async () => { + await user.click( onBackorderCheckbox ); + } ); + } - expect( getInStockCheckbox()?.checked ).toBeFalsy(); - expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); - expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); - } ); + expect( getInStockCheckbox()?.checked ).toBeFalsy(); + expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); + expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); } ); test( 'removes the option when it is clicked again', async () => { - await waitFor( async () => { - const ratingParam = 'onbackorder'; - const { - getInStockCheckbox, - getOutOfStockCheckbox, - getOnBackorderCheckbox, - } = setupMultipleChoiceList( ratingParam ); + const ratingParam = 'onbackorder'; + const { + getInStockCheckbox, + getOutOfStockCheckbox, + getOnBackorderCheckbox, + } = setupMultipleChoiceList( ratingParam ); + expect( getInStockCheckbox()?.checked ).toBeFalsy(); + expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); + expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); + + const onBackorderCheckbox = getOnBackorderCheckbox(); + + if ( onBackorderCheckbox ) { + userEvent.click( onBackorderCheckbox ); + } + + await waitFor( () => { expect( getInStockCheckbox()?.checked ).toBeFalsy(); expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); - expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); - - const onBackorderCheckbox = getOnBackorderCheckbox(); - - if ( onBackorderCheckbox ) { - userEvent.click( onBackorderCheckbox ); - } - - await waitFor( () => { - expect( getInStockCheckbox()?.checked ).toBeFalsy(); - expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); - expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); - } ); + expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); } ); } ); } ); @@ -531,72 +522,66 @@ describe( 'Filter by Stock block', () => { expect( getList() ).toBeInTheDocument(); } ); - test( 'renders chips based on URL params', async () => { - await waitFor( async () => { - const ratingParam = 'instock,onbackorder'; - const { - getInStockCheckbox, - getOutOfStockCheckbox, - getOnBackorderCheckbox, - } = setupMultipleChoiceList( ratingParam ); + test( 'renders chips based on URL params', () => { + const ratingParam = 'instock,onbackorder'; + const { + getInStockCheckbox, + getOutOfStockCheckbox, + getOnBackorderCheckbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getInStockCheckbox()?.checked ).toBeTruthy(); - expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); - expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); - } ); + expect( getInStockCheckbox()?.checked ).toBeTruthy(); + expect( getOutOfStockCheckbox()?.checked ).toBeFalsy(); + expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); } ); test( 'adds chosen option to another one that is clicked', async () => { - await waitFor( async () => { - const ratingParam = 'outofstock,onbackorder'; - const { - getInStockCheckbox, - getOutOfStockCheckbox, - getOnBackorderCheckbox, - } = setupMultipleChoiceList( ratingParam ); + const ratingParam = 'outofstock,onbackorder'; + const { + getInStockCheckbox, + getOutOfStockCheckbox, + getOnBackorderCheckbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getInStockCheckbox()?.checked ).toBeFalsy(); + expect( getInStockCheckbox()?.checked ).toBeFalsy(); + expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); + expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); + + const inStockCheckbox = getInStockCheckbox(); + + if ( inStockCheckbox ) { + userEvent.click( inStockCheckbox ); + } + + await waitFor( () => { + expect( getInStockCheckbox()?.checked ).toBeTruthy(); expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); - - const inStockCheckbox = getInStockCheckbox(); - - if ( inStockCheckbox ) { - userEvent.click( inStockCheckbox ); - } - - await waitFor( () => { - expect( getInStockCheckbox()?.checked ).toBeTruthy(); - expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); - expect( getOnBackorderCheckbox()?.checked ).toBeTruthy(); - } ); } ); } ); test( 'removes the option when it is clicked again', async () => { - await waitFor( async () => { - const ratingParam = 'instock,outofstock'; - const { - getInStockCheckbox, - getOutOfStockCheckbox, - getOnBackorderCheckbox, - } = setupMultipleChoiceList( ratingParam ); + const ratingParam = 'instock,outofstock'; + const { + getInStockCheckbox, + getOutOfStockCheckbox, + getOnBackorderCheckbox, + } = setupMultipleChoiceList( ratingParam ); - expect( getInStockCheckbox()?.checked ).toBeTruthy(); + expect( getInStockCheckbox()?.checked ).toBeTruthy(); + expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); + expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); + + const inStockCheckbox = getInStockCheckbox(); + + if ( inStockCheckbox ) { + userEvent.click( inStockCheckbox ); + } + + await waitFor( () => { + expect( getInStockCheckbox()?.checked ).toBeFalsy(); expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); - - const inStockCheckbox = getInStockCheckbox(); - - if ( inStockCheckbox ) { - userEvent.click( inStockCheckbox ); - } - - await waitFor( () => { - expect( getInStockCheckbox()?.checked ).toBeFalsy(); - expect( getOutOfStockCheckbox()?.checked ).toBeTruthy(); - expect( getOnBackorderCheckbox()?.checked ).toBeFalsy(); - } ); } ); } ); } ); diff --git a/plugins/woocommerce/changelog/51289-revert-react-18-in-checkout-block b/plugins/woocommerce/changelog/51289-revert-react-18-in-checkout-block new file mode 100644 index 00000000000..f61f7fda144 --- /dev/null +++ b/plugins/woocommerce/changelog/51289-revert-react-18-in-checkout-block @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Revert update to React 18 in Checkout block. \ No newline at end of file diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block-coupons.spec.js index 6db1b13ff81..95bdb3f28f6 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block-coupons.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block-coupons.spec.js @@ -138,7 +138,9 @@ test.describe( await page .getByRole( 'button', { name: 'Add a coupon' } ) .click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code ); + await page + .locator( '#wc-block-components-totals-coupon__input-0' ) + .fill( coupons[ i ].code ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page @@ -181,7 +183,9 @@ test.describe( await page .getByRole( 'button', { name: 'Add a coupon' } ) .click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code ); + await page + .locator( '#wc-block-components-totals-coupon__input-0' ) + .fill( coupons[ i ].code ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page @@ -221,7 +225,9 @@ test.describe( } ) => { // try to add two same coupons and verify the error message await page.getByRole( 'button', { name: 'Add a coupon' } ).click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code ); + await page + .locator( '#wc-block-components-totals-coupon__input-0' ) + .fill( coupons[ 0 ].code ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page @@ -231,7 +237,9 @@ test.describe( ) ).toBeVisible(); await page.getByRole( 'button', { name: 'Add a coupon' } ).click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code ); + await page + .locator( '#wc-block-components-totals-coupon__input-0' ) + .fill( coupons[ 0 ].code ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page @@ -247,7 +255,9 @@ test.describe( } ) => { // add coupon with usage limit await page.getByRole( 'button', { name: 'Add a coupon' } ).click(); - await page.getByLabel( 'Enter code' ).fill( couponLimitedCode ); + await page + .locator( '#wc-block-components-totals-coupon__input-0' ) + .fill( couponLimitedCode ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page