Commit 29810a89eea for woocommerce

commit 29810a89eeaf759e9672571075cb7486bef356c1
Author: Marin Atanasov <8436925+tyxla@users.noreply.github.com>
Date:   Thu Jul 9 16:21:36 2026 +0300

    Migrate tests off react-test-renderer to @testing-library (#66436)

    * Migrate tests off react-test-renderer to @testing-library

    * Add changelog entry for react-test-renderer removal

diff --git a/.syncpackrc b/.syncpackrc
index ad044294492..860beab38c4 100644
--- a/.syncpackrc
+++ b/.syncpackrc
@@ -119,7 +119,6 @@
 			"dependencies": [
 				"react",
 				"react-dom",
-				"react-test-renderer",
 				"@types/react-dom",
 				"@types/react"
 			],
diff --git a/plugins/woocommerce/changelog/dev-remove-react-test-renderer b/plugins/woocommerce/changelog/dev-remove-react-test-renderer
new file mode 100644
index 00000000000..66271b83a89
--- /dev/null
+++ b/plugins/woocommerce/changelog/dev-remove-react-test-renderer
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Migrate unit tests from react-test-renderer to the @testing-library family and drop the react-test-renderer dependency.
diff --git a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/use-import-status.test.ts b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/use-import-status.test.ts
index c9353b43d56..aef33dc20f4 100644
--- a/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/use-import-status.test.ts
+++ b/plugins/woocommerce/client/admin/client/analytics/components/import-status-bar/test/use-import-status.test.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks/dom';
 import apiFetch from '@wordpress/api-fetch';

 /**
diff --git a/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginAvailability.test.ts b/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginAvailability.test.ts
index a6f81d4472c..d745fca55ce 100644
--- a/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginAvailability.test.ts
+++ b/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginAvailability.test.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks/dom';
 import apiFetch from '@wordpress/api-fetch';

 /**
diff --git a/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginToken.test.ts b/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginToken.test.ts
index cfaaf23e6f2..ca5757a7fd4 100644
--- a/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginToken.test.ts
+++ b/plugins/woocommerce/client/admin/client/homescreen/mobile-app-modal/components/test/useQRLoginToken.test.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks/dom';
 import apiFetch from '@wordpress/api-fetch';

 /**
diff --git a/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts b/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts
index a932f9c0199..f112e298fa2 100644
--- a/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/test/use-endpoint-dismiss.test.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks/dom';
 import apiFetch from '@wordpress/api-fetch';

 /**
diff --git a/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts b/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts
index d1a1062c239..59338abe850 100644
--- a/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/test/use-option-dismiss.test.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks/dom';
 import { useDispatch, useSelect } from '@wordpress/data';

 /**
diff --git a/plugins/woocommerce/client/admin/client/hooks/test/use-tasklists-state.test.ts b/plugins/woocommerce/client/admin/client/hooks/test/use-tasklists-state.test.ts
index a0f26152d57..a3db1ca7a9d 100644
--- a/plugins/woocommerce/client/admin/client/hooks/test/use-tasklists-state.test.ts
+++ b/plugins/woocommerce/client/admin/client/hooks/test/use-tasklists-state.test.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook } from '@testing-library/react-hooks';
+import { renderHook } from '@testing-library/react-hooks/dom';
 import { useSelect } from '@wordpress/data';
 import { TaskType } from '@woocommerce/data';

diff --git a/plugins/woocommerce/client/admin/client/utils/react-hooks/test/use-network-status.test.tsx b/plugins/woocommerce/client/admin/client/utils/react-hooks/test/use-network-status.test.tsx
index 324386b30c1..2d42a7600fd 100644
--- a/plugins/woocommerce/client/admin/client/utils/react-hooks/test/use-network-status.test.tsx
+++ b/plugins/woocommerce/client/admin/client/utils/react-hooks/test/use-network-status.test.tsx
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { renderHook, act } from '@testing-library/react-hooks';
+import { renderHook, act } from '@testing-library/react-hooks/dom';

 /**
  * Internal dependencies
diff --git a/plugins/woocommerce/client/admin/package.json b/plugins/woocommerce/client/admin/package.json
index 3c033df7386..4fb15dab12f 100644
--- a/plugins/woocommerce/client/admin/package.json
+++ b/plugins/woocommerce/client/admin/package.json
@@ -206,7 +206,6 @@
 		"prop-types": "^15.8.1",
 		"raw-loader": "4.0.x",
 		"react-refresh": "^0.14.0",
-		"react-test-renderer": "18.3.x",
 		"readline-sync": "^1.4.10",
 		"replace": "^1.2.2",
 		"rimraf": "5.0.5",
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/__snapshots__/index.js.snap b/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/__snapshots__/index.js.snap
index 4181ab5b446..5a8b4636777 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/__snapshots__/index.js.snap
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/__snapshots__/index.js.snap
@@ -1,48 +1,44 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP

 exports[`ProductName should merge classes and props 1`] = `
-<a
-  className="wc-block-components-product-name lorem-ipsum"
-  dangerouslySetInnerHTML={
-    {
-      "__html": "Test product",
-    }
-  }
-  href="/"
-/>
+<div>
+  <a
+    class="wc-block-components-product-name lorem-ipsum"
+    href="/"
+  >
+    Test product
+  </a>
+</div>
 `;

 exports[`ProductName should not render a link if disabled is true 1`] = `
-<span
-  className="wc-block-components-product-name"
-  dangerouslySetInnerHTML={
-    {
-      "__html": "Test product",
-    }
-  }
-/>
+<div>
+  <span
+    class="wc-block-components-product-name"
+  >
+    Test product
+  </span>
+</div>
 `;

 exports[`ProductName should render a link if disabled is false 1`] = `
-<a
-  className="wc-block-components-product-name"
-  dangerouslySetInnerHTML={
-    {
-      "__html": "Test product",
-    }
-  }
-  href="/"
-/>
+<div>
+  <a
+    class="wc-block-components-product-name"
+    href="/"
+  >
+    Test product
+  </a>
+</div>
 `;

 exports[`ProductName should render a link if disabled is not defined 1`] = `
-<a
-  className="wc-block-components-product-name"
-  dangerouslySetInnerHTML={
-    {
-      "__html": "Test product",
-    }
-  }
-  href="/"
-/>
+<div>
+  <a
+    class="wc-block-components-product-name"
+    href="/"
+  >
+    Test product
+  </a>
+</div>
 `;
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/index.js b/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/index.js
index 890a7aafbe9..bf1d501d0ac 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/product-name/test/index.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -10,31 +10,31 @@ import ProductName from '..';

 describe( 'ProductName', () => {
 	test( 'should not render a link if disabled is true', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<ProductName disabled={ true } name="Test product" permalink="/" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render a link if disabled is false', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<ProductName disabled={ false } name="Test product" permalink="/" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render a link if disabled is not defined', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<ProductName name="Test product" permalink="/" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should merge classes and props', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<ProductName
 				className="lorem-ipsum"
 				name="Test product"
@@ -43,6 +43,6 @@ describe( 'ProductName', () => {
 			/>
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/__snapshots__/index.js.snap b/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/__snapshots__/index.js.snap
index 27d4a1312fa..983dcc38272 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/__snapshots__/index.js.snap
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/__snapshots__/index.js.snap
@@ -1,61 +1,65 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP

 exports[`ProductPrice should apply the format if one is provided 1`] = `
-<span
-  className="price wc-block-components-product-price"
->
-  pre price
+<div>
   <span
-    className="screen-reader-text"
+    class="price wc-block-components-product-price"
   >
-    Previous price:
+    pre price
+    <span
+      class="screen-reader-text"
+    >
+      Previous price:
+    </span>
+    <del
+      class="wc-block-components-product-price__regular"
+      translate="no"
+    >
+      £1.00
+    </del>
+    <span
+      class="screen-reader-text"
+    >
+      Discounted price:
+    </span>
+    <ins
+      class="wc-block-components-product-price__value is-discounted"
+      translate="no"
+    >
+      £0.50
+    </ins>
+     Test format
   </span>
-  <del
-    className="wc-block-components-product-price__regular"
-    translate="no"
-  >
-    £1.00
-  </del>
-  <span
-    className="screen-reader-text"
-  >
-    Discounted price:
-  </span>
-  <ins
-    className="wc-block-components-product-price__value is-discounted"
-    translate="no"
-  >
-    £0.50
-  </ins>
-   Test format
-</span>
+</div>
 `;

 exports[`ProductPrice should use default price if no format is provided 1`] = `
-<span
-  className="price wc-block-components-product-price"
->
+<div>
   <span
-    className="screen-reader-text"
+    class="price wc-block-components-product-price"
   >
-    Previous price:
+    <span
+      class="screen-reader-text"
+    >
+      Previous price:
+    </span>
+    <del
+      class="wc-block-components-product-price__regular"
+      translate="no"
+    >
+      £1.00
+    </del>
+    <span
+      class="screen-reader-text"
+    >
+      Discounted price:
+    </span>
+    <ins
+      class="wc-block-components-product-price__value is-discounted"
+      translate="no"
+    >
+      £0.50
+    </ins>
   </span>
-  <del
-    className="wc-block-components-product-price__regular"
-    translate="no"
-  >
-    £1.00
-  </del>
-  <span
-    className="screen-reader-text"
-  >
-    Discounted price:
-  </span>
-  <ins
-    className="wc-block-components-product-price__value is-discounted"
-    translate="no"
-  >
-    £0.50
-  </ins>
-</span>
+</div>
 `;
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/index.js b/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/index.js
index 7a75da69e13..316cd29e0dd 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/product-price/test/index.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -37,7 +37,7 @@ describe( 'ProductPrice', () => {
 	};

 	test( 'should use default price if no format is provided', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<ProductPrice
 				price={ 50 }
 				regularPrice={ 100 }
@@ -45,11 +45,11 @@ describe( 'ProductPrice', () => {
 			/>
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should apply the format if one is provided', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<ProductPrice
 				price={ 50 }
 				regularPrice={ 100 }
@@ -58,6 +58,6 @@ describe( 'ProductPrice', () => {
 			/>
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js
index 5d49d2b6bf3..89b4e7c109d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart-item-quantity.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook, act } from '@testing-library/react';
 import { createRegistry, RegistryProvider } from '@wordpress/data';
 import { CART_STORE_KEY, CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

@@ -27,18 +27,14 @@ jest.mock( 'use-debounce', () => ( {
 } ) );

 describe( 'useStoreCartItemQuantity', () => {
-	let registry, renderer;
+	let registry;

-	const getWrappedComponents = ( Component ) => (
-		<RegistryProvider value={ registry }>
-			<Component />
-		</RegistryProvider>
+	const wrapper = ( { children } ) => (
+		<RegistryProvider value={ registry }>{ children }</RegistryProvider>
 	);

-	const getTestComponent = ( options ) => () => {
-		const props = useStoreCartItemQuantity( options );
-		return <div { ...props } />;
-	};
+	const renderStoreCartItemQuantityHook = ( options ) =>
+		renderHook( () => useStoreCartItemQuantity( options ), { wrapper } );

 	let mockRemoveItemFromCart;
 	let mockChangeCartItemQuantity;
@@ -73,7 +69,6 @@ describe( 'useStoreCartItemQuantity', () => {

 	beforeEach( () => {
 		registry = createRegistry();
-		renderer = null;
 	} );

 	afterEach( () => {
@@ -90,69 +85,41 @@ describe( 'useStoreCartItemQuantity', () => {
 		} );

 		it( 'update quantity value should happen instantly', () => {
-			const TestComponent = getTestComponent( {
+			const { result } = renderStoreCartItemQuantityHook( {
 				key: '123',
 				quantity: 1,
 			} );

-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent )
-				);
-			} );
-
-			const { setItemQuantity, quantity } =
-				renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-
-			expect( quantity ).toBe( 1 );
+			expect( result.current.quantity ).toBe( 1 );

 			act( () => {
-				setItemQuantity( 2 );
+				result.current.setItemQuantity( 2 );
 			} );

-			const { quantity: newQuantity } =
-				renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-
-			expect( newQuantity ).toBe( 2 );
+			expect( result.current.quantity ).toBe( 2 );
 		} );

 		it( 'removeItem should call the dispatch action', () => {
-			const TestComponent = getTestComponent( {
+			const { result } = renderStoreCartItemQuantityHook( {
 				key: '123',
 				quantity: 1,
 			} );

 			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent )
-				);
-			} );
-
-			const { removeItem } = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-
-			act( () => {
-				removeItem();
+				result.current.removeItem();
 			} );

 			expect( mockRemoveItemFromCart ).toHaveBeenCalledWith( '123' );
 		} );

 		it( 'setItemQuantity should call the dispatch action', () => {
-			const TestComponent = getTestComponent( {
+			const { result } = renderStoreCartItemQuantityHook( {
 				key: '123',
 				quantity: 1,
 			} );

 			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent )
-				);
-			} );
-
-			const { setItemQuantity } = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-
-			act( () => {
-				setItemQuantity( 2 );
+				result.current.setItemQuantity( 2 );
 			} );

 			expect( mockChangeCartItemQuantity.mock.calls ).toEqual( [
@@ -171,21 +138,14 @@ describe( 'useStoreCartItemQuantity', () => {
 			cartErrors: mockCartErrors,
 		} );

-		const TestComponent = getTestComponent( {
+		const { result } = renderStoreCartItemQuantityHook( {
 			key: '123',
 			quantity: 1,
 		} );

-		act( () => {
-			renderer = TestRenderer.create(
-				getWrappedComponents( TestComponent )
-			);
-		} );
-
-		const { cartItemQuantityErrors } =
-			renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-
-		expect( cartItemQuantityErrors ).toEqual( mockCartErrors );
+		expect( result.current.cartItemQuantityErrors ).toEqual(
+			mockCartErrors
+		);
 	} );

 	it( 'isPendingDelete should depend on the value provided by the store', () => {
@@ -197,19 +157,11 @@ describe( 'useStoreCartItemQuantity', () => {
 			cartErrors: {},
 		} );

-		const TestComponent = getTestComponent( {
+		const { result } = renderStoreCartItemQuantityHook( {
 			key: '123',
 			quantity: 1,
 		} );

-		act( () => {
-			renderer = TestRenderer.create(
-				getWrappedComponents( TestComponent )
-			);
-		} );
-
-		const { isPendingDelete } = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-
-		expect( isPendingDelete ).toBe( true );
+		expect( result.current.isPendingDelete ).toBe( true );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx
index 4882c9ad672..778b04fde5d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/cart/test/use-store-cart.jsx
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';
 import { createRegistry, RegistryProvider } from '@wordpress/data';
 import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';

@@ -21,7 +21,7 @@ jest.mock( '@woocommerce/block-data', () => ( {
 } ) );

 describe( 'useStoreCart', () => {
-	let registry, renderer;
+	let registry;

 	const mockCartItems = [ { key: '1', id: 1, name: 'Lorem Ipsum' } ];
 	const mockShippingAddress = {
@@ -78,23 +78,12 @@ describe( 'useStoreCart', () => {
 		hasPendingItemsOperations: false,
 	};

-	const getWrappedComponents = ( Component ) => (
-		<RegistryProvider value={ registry }>
-			<Component />
-		</RegistryProvider>
+	const wrapper = ( { children } ) => (
+		<RegistryProvider value={ registry }>{ children }</RegistryProvider>
 	);

-	const getTestComponent = ( options ) => () => {
-		const { receiveCart, receiveCartContents, ...results } =
-			useStoreCart( options );
-		return (
-			<div
-				data-results={ results }
-				data-receiveCart={ receiveCart }
-				data-receiveCartContents={ receiveCartContents }
-			/>
-		);
-	};
+	const renderStoreCartHook = ( options ) =>
+		renderHook( () => useStoreCart( options ), { wrapper } );

 	const setUpMocks = () => {
 		const mocks = {
@@ -120,7 +109,6 @@ describe( 'useStoreCart', () => {

 	beforeEach( () => {
 		registry = createRegistry();
-		renderer = null;
 		setUpMocks();
 	} );

@@ -136,20 +124,12 @@ describe( 'useStoreCart', () => {
 		} );

 		it( 'return default data when shouldSelect is false', () => {
-			const TestComponent = getTestComponent( {
+			const { result } = renderStoreCartHook( {
 				shouldSelect: false,
 			} );

-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent )
-				);
-			} );
-
-			const props = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-			const results = props[ 'data-results' ];
-			const receiveCart = props[ 'data-receiveCart' ];
-			const receiveCartContents = props[ 'data-receiveCartContents' ];
+			const { receiveCart, receiveCartContents, ...results } =
+				result.current;

 			const {
 				receiveCart: defaultReceiveCart,
@@ -162,20 +142,12 @@ describe( 'useStoreCart', () => {
 		} );

 		it( 'return store data when shouldSelect is true', () => {
-			const TestComponent = getTestComponent( {
+			const { result } = renderStoreCartHook( {
 				shouldSelect: true,
 			} );

-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent )
-				);
-			} );
-
-			const props = renderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-			const results = props[ 'data-results' ];
-			const receiveCart = props[ 'data-receiveCart' ];
-			const receiveCartContents = props[ 'data-receiveCartContents' ];
+			const { receiveCart, receiveCartContents, ...results } =
+				result.current;

 			expect( results ).toEqual( mockStoreCartData );
 			expect( receiveCart ).toBeUndefined();
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx
index f325d91df77..4d1eaa34c8b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/collections/test/use-collection.jsx
@@ -1,9 +1,8 @@
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';
 import { createRegistry, RegistryProvider } from '@wordpress/data';
-import { Component as ReactComponent } from '@wordpress/element';
 import { COLLECTIONS_STORE_KEY as storeKey } from '@woocommerce/block-data';

 /**
@@ -16,50 +15,30 @@ jest.mock( '@woocommerce/block-data', () => ( {
 	COLLECTIONS_STORE_KEY: 'test/store',
 } ) );

-class TestErrorBoundary extends ReactComponent {
-	constructor( props ) {
-		super( props );
-		this.state = { hasError: false, error: {} };
-	}
-	static getDerivedStateFromError( error ) {
-		// Update state so the next render will show the fallback UI.
-		return { hasError: true, error };
-	}
-
-	render() {
-		if ( this.state.hasError ) {
-			return <div data-error={ this.state.error } />;
-		}
-
-		return this.props.children;
-	}
-}
-
 describe( 'useCollection', () => {
-	let registry, mocks, renderer;
-	const getProps = ( testRenderer ) => {
-		const { results, isLoading } =
-			testRenderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-		return {
-			results,
-			isLoading,
-		};
-	};
+	let registry, mocks;

-	const getWrappedComponents = ( Component, props ) => (
-		<RegistryProvider value={ registry }>
-			<TestErrorBoundary>
-				<Component { ...props } />
-			</TestErrorBoundary>
-		</RegistryProvider>
+	const wrapper = ( { children } ) => (
+		<RegistryProvider value={ registry }>{ children }</RegistryProvider>
 	);

-	const getTestComponent =
-		() =>
-		( { options } ) => {
-			const items = useCollection( options );
-			return <div { ...items } />;
-		};
+	const renderUseCollection = ( options ) =>
+		renderHook( ( props ) => useCollection( props.options ), {
+			initialProps: { options },
+			wrapper,
+		} );
+
+	// useCollection throws during render for invalid options or store errors;
+	// renderHook rethrows that error, so we capture it here.
+	const renderUseCollectionError = ( options ) => {
+		let error;
+		try {
+			renderUseCollection( options );
+		} catch ( caught ) {
+			error = caught;
+		}
+		return error;
+	};

 	const setUpMocks = () => {
 		// Memoize the fixture by selector args so wp-data's SCRIPT_DEBUG
@@ -92,161 +71,108 @@ describe( 'useCollection', () => {
 	beforeEach( () => {
 		registry = createRegistry();
 		mocks = {};
-		renderer = null;
 		setUpMocks();
 	} );
 	it(
 		'should throw an error if an options object is provided without ' +
 			'a namespace property',
 		() => {
-			const TestComponent = getTestComponent();
-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent, {
-						options: {
-							resourceName: 'products',
-							query: { bar: 'foo' },
-						},
-					} )
-				);
+			const error = renderUseCollectionError( {
+				resourceName: 'products',
+				query: { bar: 'foo' },
 			} );
-			//eslint-disable-next-line testing-library/await-async-query
-			const props = renderer.root.findByType( 'div' ).props;
-			expect( props[ 'data-error' ].message ).toMatch( /options object/ );
+			expect( error.message ).toMatch( /options object/ );
 			expect( console ).toHaveErrored( /your React components:/ );
-			renderer.unmount();
 		}
 	);
 	it(
 		'should throw an error if an options object is provided without ' +
 			'a resourceName property',
 		() => {
-			const TestComponent = getTestComponent();
-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							query: { bar: 'foo' },
-						},
-					} )
-				);
+			const error = renderUseCollectionError( {
+				namespace: 'test/store',
+				query: { bar: 'foo' },
 			} );
-			//eslint-disable-next-line testing-library/await-async-query
-			const props = renderer.root.findByType( 'div' ).props;
-			expect( props[ 'data-error' ].message ).toMatch( /options object/ );
+			expect( error.message ).toMatch( /options object/ );
 			expect( console ).toHaveErrored( /your React components:/ );
-			renderer.unmount();
 		}
 	);
 	it(
 		'should return expected behaviour for equivalent query on props ' +
 			'across renders',
 		() => {
-			const TestComponent = getTestComponent();
-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							resourceName: 'products',
-							query: { bar: 'foo' },
-						},
-					} )
-				);
+			const { result, rerender } = renderUseCollection( {
+				namespace: 'test/store',
+				resourceName: 'products',
+				query: { bar: 'foo' },
 			} );
-			const { results } = getProps( renderer );
+			const { results } = result.current;
 			// rerender
-			act( () => {
-				renderer.update(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							resourceName: 'products',
-							query: { bar: 'foo' },
-						},
-					} )
-				);
+			rerender( {
+				options: {
+					namespace: 'test/store',
+					resourceName: 'products',
+					query: { bar: 'foo' },
+				},
 			} );
 			// re-render should result in same products object because although
 			// query-state is a different instance, it's still equivalent.
-			const { results: newResults } = getProps( renderer );
+			const { results: newResults } = result.current;
 			expect( newResults ).toBe( results );
 			// now let's change the query passed through to verify new object
 			// is created.
 			// remember this won't actually change the results because the mock
 			// selector is returning an equivalent object when it is called,
 			// however it SHOULD be a new object instance.
-			act( () => {
-				renderer.update(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							resourceName: 'products',
-							query: { foo: 'bar' },
-						},
-					} )
-				);
+			rerender( {
+				options: {
+					namespace: 'test/store',
+					resourceName: 'products',
+					query: { foo: 'bar' },
+				},
 			} );
-			const { results: resultsVerification } = getProps( renderer );
+			const { results: resultsVerification } = result.current;
 			expect( resultsVerification ).not.toBe( results );
 			expect( resultsVerification ).toEqual( results );
-			renderer.unmount();
 		}
 	);
 	it(
 		'should return expected behaviour for equivalent resourceValues on' +
 			' props across renders',
 		() => {
-			const TestComponent = getTestComponent();
-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							resourceName: 'products',
-							resourceValues: [ 10, 20 ],
-						},
-					} )
-				);
+			const { result, rerender } = renderUseCollection( {
+				namespace: 'test/store',
+				resourceName: 'products',
+				resourceValues: [ 10, 20 ],
 			} );
-			const { results } = getProps( renderer );
+			const { results } = result.current;
 			// rerender
-			act( () => {
-				renderer.update(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							resourceName: 'products',
-							resourceValues: [ 10, 20 ],
-						},
-					} )
-				);
+			rerender( {
+				options: {
+					namespace: 'test/store',
+					resourceName: 'products',
+					resourceValues: [ 10, 20 ],
+				},
 			} );
 			// re-render should result in same products object because although
 			// query-state is a different instance, it's still equivalent.
-			const { results: newResults } = getProps( renderer );
+			const { results: newResults } = result.current;
 			expect( newResults ).toBe( results );
 			// now let's change the query passed through to verify new object
 			// is created.
 			// remember this won't actually change the results because the mock
 			// selector is returning an equivalent object when it is called,
 			// however it SHOULD be a new object instance.
-			act( () => {
-				renderer.update(
-					getWrappedComponents( TestComponent, {
-						options: {
-							namespace: 'test/store',
-							resourceName: 'products',
-							resourceValues: [ 20, 10 ],
-						},
-					} )
-				);
+			rerender( {
+				options: {
+					namespace: 'test/store',
+					resourceName: 'products',
+					resourceValues: [ 20, 10 ],
+				},
 			} );
-			const { results: resultsVerification } = getProps( renderer );
+			const { results: resultsVerification } = result.current;
 			expect( resultsVerification ).not.toBe( results );
 			expect( resultsVerification ).toEqual( results );
-			renderer.unmount();
 		}
 	);
 	it( 'should return previous query results if `shouldSelect` is false', () => {
@@ -264,19 +190,12 @@ describe( 'useCollection', () => {
 				return cache.get( key );
 			}
 		);
-		const TestComponent = getTestComponent();
-		act( () => {
-			renderer = TestRenderer.create(
-				getWrappedComponents( TestComponent, {
-					options: {
-						namespace: 'test/store',
-						resourceName: 'products',
-						resourceValues: [ 10, 20 ],
-					},
-				} )
-			);
+		const { result, rerender } = renderUseCollection( {
+			namespace: 'test/store',
+			resourceName: 'products',
+			resourceValues: [ 10, 20 ],
 		} );
-		const { results } = getProps( renderer );
+		const { results } = result.current;
 		// Capture the call count after the first render so the next assertion
 		// measures whether the rerender caused additional invocations rather
 		// than the absolute total (wp-data's SCRIPT_DEBUG unstable-reference
@@ -285,19 +204,15 @@ describe( 'useCollection', () => {
 		const callsAfterFirstRender =
 			mocks.selectors.getCollection.mock.calls.length;
 		// rerender but with shouldSelect to false
-		act( () => {
-			renderer.update(
-				getWrappedComponents( TestComponent, {
-					options: {
-						namespace: 'test/store',
-						resourceName: 'productsb',
-						resourceValues: [ 10, 30 ],
-						shouldSelect: false,
-					},
-				} )
-			);
+		rerender( {
+			options: {
+				namespace: 'test/store',
+				resourceName: 'productsb',
+				resourceValues: [ 10, 30 ],
+				shouldSelect: false,
+			},
 		} );
-		const { results: results2 } = getProps( renderer );
+		const { results: results2 } = result.current;
 		expect( results2 ).toBe( results );
 		// `shouldSelect: false` should not have triggered any new selector
 		// invocations; the cached previous results should be returned.
@@ -307,19 +222,15 @@ describe( 'useCollection', () => {

 		// rerender again but set shouldSelect to true again and we should see
 		// new results
-		act( () => {
-			renderer.update(
-				getWrappedComponents( TestComponent, {
-					options: {
-						namespace: 'test/store',
-						resourceName: 'productsb',
-						resourceValues: [ 10, 30 ],
-						shouldSelect: true,
-					},
-				} )
-			);
+		rerender( {
+			options: {
+				namespace: 'test/store',
+				resourceName: 'productsb',
+				resourceValues: [ 10, 30 ],
+				shouldSelect: true,
+			},
 		} );
-		const { results: results3 } = getProps( renderer );
+		const { results: results3 } = result.current;
 		expect( results3 ).not.toEqual( results );
 		expect( results3 ).toEqual( [
 			'test/store',
@@ -330,54 +241,41 @@ describe( 'useCollection', () => {
 	} );
 	const renderWithStoreError = ( errorValue ) => {
 		mocks.selectors.getCollectionError.mockReturnValue( errorValue );
-		const TestComponent = getTestComponent();
-		act( () => {
-			renderer = TestRenderer.create(
-				getWrappedComponents( TestComponent, {
-					options: {
-						namespace: 'test/store',
-						resourceName: 'products',
-						query: { bar: 'foo' },
-					},
-				} )
-			);
+		return renderUseCollectionError( {
+			namespace: 'test/store',
+			resourceName: 'products',
+			query: { bar: 'foo' },
 		} );
-		//eslint-disable-next-line testing-library/await-async-query
-		return renderer.root.findByType( 'div' ).props;
 	};

 	it( 'should propagate an Error instance from the store via the error boundary', () => {
 		const error = new Error( 'A real error' );
-		const props = renderWithStoreError( error );
-		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
-		expect( props[ 'data-error' ].message ).toBe( 'A real error' );
+		const caught = renderWithStoreError( error );
+		expect( caught ).toBeInstanceOf( Error );
+		expect( caught.message ).toBe( 'A real error' );
 		expect( console ).toHaveErrored( /your React components:/ );
-		renderer.unmount();
 	} );
 	it( 'should convert a non-Error object with a message into an Error instance', () => {
 		const error = { code: 'rest_no_route', message: 'No route found.' };
-		const props = renderWithStoreError( error );
-		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
-		expect( props[ 'data-error' ].message ).toBe( 'No route found.' );
+		const caught = renderWithStoreError( error );
+		expect( caught ).toBeInstanceOf( Error );
+		expect( caught.message ).toBe( 'No route found.' );
 		expect( console ).toHaveErrored( /your React components:/ );
-		renderer.unmount();
 	} );
 	it( 'should use a fallback message when a non-Error object has no message', () => {
-		const props = renderWithStoreError( { code: 500 } );
-		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
-		expect( props[ 'data-error' ].message ).toBe(
+		const caught = renderWithStoreError( { code: 500 } );
+		expect( caught ).toBeInstanceOf( Error );
+		expect( caught.message ).toBe(
 			'Something went wrong while loading data.'
 		);
 		expect( console ).toHaveErrored( /your React components:/ );
-		renderer.unmount();
 	} );
 	it( 'should use a fallback message when a primitive value is returned from the store', () => {
-		const props = renderWithStoreError( 'oops' );
-		expect( props[ 'data-error' ] ).toBeInstanceOf( Error );
-		expect( props[ 'data-error' ].message ).toBe(
+		const caught = renderWithStoreError( 'oops' );
+		expect( caught ).toBeInstanceOf( Error );
+		expect( caught.message ).toBe(
 			'Something went wrong while loading data.'
 		);
 		expect( console ).toHaveErrored( /your React components:/ );
-		renderer.unmount();
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-checkout-submit.js b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-checkout-submit.js
index 10e3114a249..13a89e20187 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-checkout-submit.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-checkout-submit.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';
 import { createRegistry, RegistryProvider } from '@wordpress/data';

 /**
@@ -30,38 +30,25 @@ jest.mock( '../../providers/cart-checkout/checkout-events', () => {
 } );

 describe( 'useCheckoutSubmit', () => {
-	let registry, renderer;
+	let registry;

-	const getWrappedComponents = ( Component ) => (
-		<RegistryProvider value={ registry }>
-			<Component />
-		</RegistryProvider>
+	const wrapper = ( { children } ) => (
+		<RegistryProvider value={ registry }>{ children }</RegistryProvider>
 	);

-	const getTestComponent = () => () => {
-		const data = useCheckoutSubmit();
-		return <div { ...data } />;
-	};
-
 	beforeEach( () => {
 		registry = createRegistry( {
 			[ CHECKOUT_STORE_KEY ]: checkoutStoreConfig,
 			[ PAYMENT_STORE_KEY ]: paymentDataStoreConfig,
 		} );
-		renderer = null;
 	} );

 	it( 'onSubmit calls the correct action in the checkout events context', () => {
-		const TestComponent = getTestComponent();
-
-		act( () => {
-			renderer = TestRenderer.create(
-				getWrappedComponents( TestComponent )
-			);
+		const { result } = renderHook( () => useCheckoutSubmit(), {
+			wrapper,
 		} );

-		//eslint-disable-next-line testing-library/await-async-query
-		const { onSubmit } = renderer.root.findByType( 'div' ).props;
+		const { onSubmit } = result.current;

 		onSubmit();

diff --git a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-store-products.js b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-store-products.js
index ab5cefb8299..418d4989baf 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-store-products.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/context/hooks/test/use-store-products.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';
 import { createRegistry, RegistryProvider } from '@wordpress/data';
 import { COLLECTIONS_STORE_KEY as storeKey } from '@woocommerce/block-data';

@@ -16,30 +16,12 @@ jest.mock( '@woocommerce/block-data', () => ( {
 } ) );

 describe( 'useStoreProducts', () => {
-	let registry, mocks, renderer;
-	const getProps = ( testRenderer ) => {
-		const { products, totalProducts, productsLoading } =
-			testRenderer.root.findByType( 'div' ).props; //eslint-disable-line testing-library/await-async-query
-		return {
-			products,
-			totalProducts,
-			productsLoading,
-		};
-	};
+	let registry, mocks;

-	const getWrappedComponents = ( Component, props ) => (
-		<RegistryProvider value={ registry }>
-			<Component { ...props } />
-		</RegistryProvider>
+	const wrapper = ( { children } ) => (
+		<RegistryProvider value={ registry }>{ children }</RegistryProvider>
 	);

-	const getTestComponent =
-		() =>
-		( { query } ) => {
-			const items = useStoreProducts( query );
-			return <div { ...items } />;
-		};
-
 	const setUpMocks = () => {
 		// Memoize the fixture by selector args so wp-data's SCRIPT_DEBUG
 		// unstable-reference check (which double-invokes the selector with
@@ -72,50 +54,33 @@ describe( 'useStoreProducts', () => {
 	beforeEach( () => {
 		registry = createRegistry();
 		mocks = {};
-		renderer = null;
 		setUpMocks();
 	} );
 	it(
 		'should return expected behaviour for equivalent query on props ' +
 			'across renders',
 		() => {
-			const TestComponent = getTestComponent();
-			act( () => {
-				renderer = TestRenderer.create(
-					getWrappedComponents( TestComponent, {
-						query: { bar: 'foo' },
-					} )
-				);
-			} );
-			const { products } = getProps( renderer );
+			const { result, rerender, unmount } = renderHook(
+				( { query } ) => useStoreProducts( query ),
+				{ initialProps: { query: { bar: 'foo' } }, wrapper }
+			);
+			const { products } = result.current;
 			// rerender
-			act( () => {
-				renderer.update(
-					getWrappedComponents( TestComponent, {
-						query: { bar: 'foo' },
-					} )
-				);
-			} );
+			rerender( { query: { bar: 'foo' } } );
 			// re-render should result in same products object because although
 			// query-state is a different instance, it's still equivalent.
-			const { products: newProducts } = getProps( renderer );
+			const { products: newProducts } = result.current;
 			expect( newProducts ).toBe( products );
 			// now let's change the query passed through to verify new object
 			// is created.
 			// remember this won't actually change the results because the mock
 			// selector is returning an equivalent object when it is called,
 			// however it SHOULD be a new object instance.
-			act( () => {
-				renderer.update(
-					getWrappedComponents( TestComponent, {
-						query: { foo: 'bar' },
-					} )
-				);
-			} );
-			const { products: productsVerification } = getProps( renderer );
+			rerender( { query: { foo: 'bar' } } );
+			const { products: productsVerification } = result.current;
 			expect( productsVerification ).not.toBe( products );
 			expect( productsVerification ).toEqual( products );
-			renderer.unmount();
+			unmount();
 		}
 	);
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/hocs/test/with-reviews.jsx b/plugins/woocommerce/client/blocks/assets/js/base/hocs/test/with-reviews.jsx
index 3ceb37f1148..736918f6dd4 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/hocs/test/with-reviews.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/hocs/test/with-reviews.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render, act } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -37,39 +34,48 @@ const defaultArgs = {
 	per_page: 2,
 	product_id: 1,
 };
-const TestComponent = withReviews( ( props ) => {
-	return (
-		<div
-			data-error={ props.error }
-			data-getReviews={ props.getReviews }
-			data-appendReviews={ props.appendReviews }
-			data-onChangeArgs={ props.onChangeArgs }
-			data-isLoading={ props.isLoading }
-			data-reviews={ props.reviews }
-			data-totalReviews={ props.totalReviews }
-		/>
-	);
+
+// Capture the props the HOC injects into the wrapped component.
+let lastProps;
+const CapturedComponent = jest.fn( ( props ) => {
+	lastProps = props;
+	return null;
 } );
-const render = () => {
-	return TestRenderer.create(
-		<TestComponent
-			attributes={ {} }
-			order="desc"
-			orderby="date_gmt"
-			productId={ 1 }
-			reviewsToDisplay={ 2 }
-		/>
-	);
+const TestComponent = withReviews( CapturedComponent );
+
+// Run an interaction and flush the async state updates it triggers inside
+// `act`, so React's async fetch-then-setState work does not leak past the test.
+const settle = async ( fn ) => {
+	await act( async () => {
+		fn();
+		await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
+	} );
 };

 describe( 'withReviews Component', () => {
-	let renderer;
+	let renderResult;
+	const renderComponent = ( props ) =>
+		settle( () => {
+			renderResult = render(
+				<TestComponent
+					attributes={ {} }
+					order="desc"
+					orderby="date_gmt"
+					productId={ 1 }
+					reviewsToDisplay={ 2 }
+					{ ...props }
+				/>
+			);
+		} );
+
 	afterEach( () => {
 		mockUtils.getReviews.mockReset();
+		CapturedComponent.mockClear();
+		lastProps = undefined;
 	} );

 	describe( 'lifecycle events', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getReviews
 				.mockImplementationOnce( () =>
 					Promise.resolve( {
@@ -83,7 +89,7 @@ describe( 'withReviews Component', () => {
 						totalReviews: mockReviews.length,
 					} )
 				);
-			renderer = render();
+			await renderComponent();
 		} );

 		it( 'getReviews is called on mount with default args', () => {
@@ -93,15 +99,17 @@ describe( 'withReviews Component', () => {
 			expect( getReviews ).toHaveBeenCalledTimes( 1 );
 		} );

-		it( 'getReviews is called on component update', () => {
+		it( 'getReviews is called on component update', async () => {
 			const { getReviews } = mockUtils;
-			renderer.update(
-				<TestComponent
-					order="desc"
-					orderby="date_gmt"
-					productId={ 1 }
-					reviewsToDisplay={ 3 }
-				/>
+			await settle( () =>
+				renderResult.rerender(
+					<TestComponent
+						order="desc"
+						orderby="date_gmt"
+						productId={ 1 }
+						reviewsToDisplay={ 3 }
+					/>
+				)
 			);

 			expect( getReviews ).toHaveBeenNthCalledWith( 2, {
@@ -114,54 +122,46 @@ describe( 'withReviews Component', () => {
 	} );

 	describe( 'when the API returns product data', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getReviews.mockImplementation( () =>
 				Promise.resolve( {
 					reviews: mockReviews.slice( 0, 2 ),
 					totalReviews: mockReviews.length,
 				} )
 			);
-			renderer = render();
+			await renderComponent();
 		} );

 		it( 'sets reviews based on API response', () => {
-			const props = renderer.root.findByType( 'div' ).props;
-
-			expect( props[ 'data-error' ] ).toBeNull();
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-reviews' ] ).toEqual(
-				mockReviews.slice( 0, 2 )
-			);
-			expect( props[ 'data-totalReviews' ] ).toEqual(
-				mockReviews.length
-			);
+			expect( lastProps.error ).toBeNull();
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.reviews ).toEqual( mockReviews.slice( 0, 2 ) );
+			expect( lastProps.totalReviews ).toEqual( mockReviews.length );
 		} );
 	} );

 	describe( 'when the API returns an error', () => {
 		const error = { message: 'There was an error.' };
-		const getReviewsPromise = Promise.reject( error );
 		const formattedError = { message: 'There was an error.', type: 'api' };

-		beforeEach( () => {
-			mockUtils.getReviews.mockImplementation( () => getReviewsPromise );
+		beforeEach( async () => {
+			mockUtils.getReviews.mockImplementation( () =>
+				Promise.reject( error )
+			);
 			mockBaseUtils.formatError.mockImplementation(
 				() => formattedError
 			);
-			renderer = render();
+			await renderComponent();
 		} );

-		test( 'sets the error prop', async () => {
-			await expect( () => getReviewsPromise() ).toThrow();
-
+		test( 'sets the error prop', () => {
 			const { formatError } = mockBaseUtils;
-			const props = renderer.root.findByType( 'div' ).props;

 			expect( formatError ).toHaveBeenCalledWith( error );
 			expect( formatError ).toHaveBeenCalledTimes( 1 );
-			expect( props[ 'data-error' ] ).toEqual( formattedError );
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-reviews' ] ).toEqual( [] );
+			expect( lastProps.error ).toEqual( formattedError );
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.reviews ).toEqual( [] );
 		} );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/hocs/with-scroll-to-top/test/index.js b/plugins/woocommerce/client/blocks/assets/js/base/hocs/with-scroll-to-top/test/index.js
index 6687d83ea1c..1ceabcb59a6 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/hocs/with-scroll-to-top/test/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/hocs/with-scroll-to-top/test/index.js
@@ -1,55 +1,35 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render, screen } from '@testing-library/react';

 /**
  * Internal dependencies
  */
 import withScrollToTop from '../index';

-const TestComponent = withScrollToTop( ( props ) => (
-	<span { ...props }>
-		<button />
-	</span>
-) );
-
 const focusedMock = jest.fn();
 const scrollIntoViewMock = jest.fn();

-const mockedButton = {
-	focus: focusedMock,
-	scrollIntoView: scrollIntoViewMock,
-};
-const render = ( { inView } ) => {
-	const getBoundingClientRect = () => ( {
+let scrollToTop;
+const TestComponent = withScrollToTop( ( props ) => {
+	scrollToTop = props.scrollToTop;
+	return (
+		<span>
+			<button />
+		</span>
+	);
+} );
+
+const setup = ( { inView } ) => {
+	render( <TestComponent /> );
+	const button = screen.getByRole( 'button' );
+	button.focus = focusedMock;
+	button.scrollIntoView = scrollIntoViewMock;
+	button.getBoundingClientRect = () => ( {
 		bottom: inView ? 0 : -10,
 	} );
-	return TestRenderer.create( <TestComponent />, {
-		createNodeMock: ( element ) => {
-			if ( element.type === 'button' ) {
-				return {
-					...mockedButton,
-					getBoundingClientRect,
-				};
-			}
-			if ( element.type === 'div' ) {
-				return {
-					getBoundingClientRect,
-					parentElement: {
-						querySelectorAll: () => [
-							{ ...mockedButton, getBoundingClientRect },
-						],
-					},
-					scrollIntoView: scrollIntoViewMock,
-				};
-			}
-			return null;
-		},
-	} );
+	scrollToTop( { focusableSelector: 'button' } );
 };

 describe( 'withScrollToTop Component', () => {
@@ -60,11 +40,7 @@ describe( 'withScrollToTop Component', () => {

 	describe( 'if component is not in view', () => {
 		beforeEach( () => {
-			const renderer = render( { inView: false } );
-			const props = renderer.root.findByType( 'span' ).props;
-			props.scrollToTop( {
-				focusableSelector: 'button',
-			} );
+			setup( { inView: false } );
 		} );

 		it( 'scrolls to top of the component when scrollToTop is called', () => {
@@ -78,11 +54,7 @@ describe( 'withScrollToTop Component', () => {

 	describe( 'if component is in view', () => {
 		beforeEach( () => {
-			const renderer = render( { inView: true } );
-			const props = renderer.root.findByType( 'span' ).props;
-			props.scrollToTop( {
-				focusableSelector: 'button',
-			} );
+			setup( { inView: true } );
 		} );

 		it( "doesn't scroll to top of the component when scrollToTop is called", () => {
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-previous.jsx b/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-previous.jsx
index c1db5f40599..dd4f4a232cd 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-previous.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-previous.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -12,87 +9,35 @@ import TestRenderer, { act } from 'react-test-renderer';
 import { usePrevious } from '../use-previous';

 describe( 'usePrevious', () => {
-	const TestComponent = ( { testValue, validation } ) => {
-		const previousValue = usePrevious( testValue, validation );
-		return (
-			<div
-				data-testValue={ testValue }
-				data-previousValue={ previousValue }
-			/>
+	const renderUsePrevious = ( testValue, validation ) =>
+		renderHook(
+			( props ) => usePrevious( props.testValue, props.validation ),
+			{ initialProps: { testValue, validation } }
 		);
-	};
-
-	let renderer;
-	beforeEach( () => ( renderer = null ) );

 	it( 'should be undefined at first pass', () => {
-		act( () => {
-			renderer = TestRenderer.create( <TestComponent testValue={ 1 } /> );
-		} );
-		const testValue =
-			renderer.root.findByType( 'div' ).props[ 'data-testValue' ];
-		const testPreviousValue =
-			renderer.root.findByType( 'div' ).props[ 'data-previousValue' ];
+		const { result } = renderUsePrevious( 1 );

-		expect( testValue ).toBe( 1 );
-		expect( testPreviousValue ).toBe( undefined );
+		expect( result.current ).toBe( undefined );
 	} );

 	it( 'test new and previous value', () => {
-		let testValue;
-		let testPreviousValue;
-		act( () => {
-			renderer = TestRenderer.create( <TestComponent testValue={ 1 } /> );
-		} );
+		const { result, rerender } = renderUsePrevious( 1 );

-		act( () => {
-			renderer.update( <TestComponent testValue={ 2 } /> );
-		} );
-		testValue = renderer.root.findByType( 'div' ).props[ 'data-testValue' ];
-		testPreviousValue =
-			renderer.root.findByType( 'div' ).props[ 'data-previousValue' ];
-		expect( testValue ).toBe( 2 );
-		expect( testPreviousValue ).toBe( 1 );
+		rerender( { testValue: 2 } );
+		expect( result.current ).toBe( 1 );

-		act( () => {
-			renderer.update( <TestComponent testValue={ 3 } /> );
-		} );
-		testValue = renderer.root.findByType( 'div' ).props[ 'data-testValue' ];
-		testPreviousValue =
-			renderer.root.findByType( 'div' ).props[ 'data-previousValue' ];
-		expect( testValue ).toBe( 3 );
-		expect( testPreviousValue ).toBe( 2 );
+		rerender( { testValue: 3 } );
+		expect( result.current ).toBe( 2 );
 	} );

 	it( 'should not update value if validation fails', () => {
-		let testValue;
-		let testPreviousValue;
-		act( () => {
-			renderer = TestRenderer.create(
-				<TestComponent testValue={ 1 } validation={ Number.isFinite } />
-			);
-		} );
+		const { result, rerender } = renderUsePrevious( 1, Number.isFinite );

-		act( () => {
-			renderer.update(
-				<TestComponent testValue="abc" validation={ Number.isFinite } />
-			);
-		} );
-		testValue = renderer.root.findByType( 'div' ).props[ 'data-testValue' ];
-		testPreviousValue =
-			renderer.root.findByType( 'div' ).props[ 'data-previousValue' ];
-		expect( testValue ).toBe( 'abc' );
-		expect( testPreviousValue ).toBe( 1 );
+		rerender( { testValue: 'abc', validation: Number.isFinite } );
+		expect( result.current ).toBe( 1 );

-		act( () => {
-			renderer.update(
-				<TestComponent testValue={ 3 } validation={ Number.isFinite } />
-			);
-		} );
-		testValue = renderer.root.findByType( 'div' ).props[ 'data-testValue' ];
-		testPreviousValue =
-			renderer.root.findByType( 'div' ).props[ 'data-previousValue' ];
-		expect( testValue ).toBe( 3 );
-		expect( testPreviousValue ).toBe( 1 );
+		rerender( { testValue: 3, validation: Number.isFinite } );
+		expect( result.current ).toBe( 1 );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-shallow-equal.jsx b/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-shallow-equal.jsx
index 687b6b1c6b5..64c40d0e41f 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-shallow-equal.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-shallow-equal.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -12,12 +9,11 @@ import TestRenderer, { act } from 'react-test-renderer';
 import { useShallowEqual } from '../use-shallow-equal';

 describe( 'useShallowEqual', () => {
-	const TestComponent = ( { testValue } ) => {
-		const newValue = useShallowEqual( testValue );
-		return <div data-newValue={ newValue } />;
-	};
-	let renderer;
-	beforeEach( () => ( renderer = null ) );
+	const renderShallowEqual = ( testValue ) =>
+		renderHook( ( props ) => useShallowEqual( props.testValue ), {
+			initialProps: { testValue },
+		} );
+
 	it.each`
 		testValueA                  | aType         | testValueB                  | bType
 		${ { a: 'b', foo: 'bar' } } | ${ 'object' } | ${ { foo: 'bar', a: 'b' } } | ${ 'object' }
@@ -28,22 +24,11 @@ describe( 'useShallowEqual', () => {
 	`(
 		'$testValueA ($aType) and $testValueB ($bType) are expected to be equal',
 		( { testValueA, testValueB } ) => {
-			let testPropValue;
-			act( () => {
-				renderer = TestRenderer.create(
-					<TestComponent testValue={ testValueA } />
-				);
-			} );
-			testPropValue =
-				renderer.root.findByType( 'div' ).props[ 'data-newValue' ];
-			expect( testPropValue ).toBe( testValueA );
+			const { result, rerender } = renderShallowEqual( testValueA );
+			expect( result.current ).toBe( testValueA );
 			// do update
-			act( () => {
-				renderer.update( <TestComponent testValue={ testValueB } /> );
-			} );
-			testPropValue =
-				renderer.root.findByType( 'div' ).props[ 'data-newValue' ];
-			expect( testPropValue ).toBe( testValueA );
+			rerender( { testValue: testValueB } );
+			expect( result.current ).toBe( testValueA );
 		}
 	);

@@ -58,22 +43,11 @@ describe( 'useShallowEqual', () => {
 	`(
 		'$testValueA ($aType) and $testValueB ($bType) are expected to not be equal',
 		( { testValueA, testValueB } ) => {
-			let testPropValue;
-			act( () => {
-				renderer = TestRenderer.create(
-					<TestComponent testValue={ testValueA } />
-				);
-			} );
-			testPropValue =
-				renderer.root.findByType( 'div' ).props[ 'data-newValue' ];
-			expect( testPropValue ).toBe( testValueA );
+			const { result, rerender } = renderShallowEqual( testValueA );
+			expect( result.current ).toBe( testValueA );
 			// do update
-			act( () => {
-				renderer.update( <TestComponent testValue={ testValueB } /> );
-			} );
-			testPropValue =
-				renderer.root.findByType( 'div' ).props[ 'data-newValue' ];
-			expect( testPropValue ).toBe( testValueB );
+			rerender( { testValue: testValueB } );
+			expect( result.current ).toBe( testValueB );
 		}
 	);
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/price-filter/test/use-price-constraints.jsx b/plugins/woocommerce/client/blocks/assets/js/blocks/price-filter/test/use-price-constraints.jsx
index f5d2b10965a..6fd3083cb58 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/price-filter/test/use-price-constraints.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/price-filter/test/use-price-constraints.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { renderHook } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -13,77 +10,62 @@ import { usePriceConstraint } from '../use-price-constraints';
 import { ROUND_UP, ROUND_DOWN } from '../constants';

 describe( 'usePriceConstraints', () => {
-	const TestComponent = ( { price } ) => {
-		const maxPriceConstraint = usePriceConstraint( price, 2, ROUND_UP );
-		const minPriceConstraint = usePriceConstraint( price, 2, ROUND_DOWN );
-		return (
-			<div
-				data-minPriceConstraint={ minPriceConstraint }
-				data-maxPriceConstraint={ maxPriceConstraint }
-			/>
+	const renderPriceConstraints = ( price ) =>
+		renderHook(
+			( { price: currentPrice } ) => ( {
+				max: usePriceConstraint( currentPrice, 2, ROUND_UP ),
+				min: usePriceConstraint( currentPrice, 2, ROUND_DOWN ),
+			} ),
+			{ initialProps: { price } }
 		);
-	};

 	it( 'max price constraint should be updated when new price is set', () => {
-		const renderer = TestRenderer.create(
-			<TestComponent price={ 1000 } />
-		);
-		const container = renderer.root.findByType( 'div' );
+		const { result, rerender } = renderPriceConstraints( 1000 );

-		expect( container.props[ 'data-maxPriceConstraint' ] ).toBe( 1000 );
+		expect( result.current.max ).toBe( 1000 );

-		renderer.update( <TestComponent price={ 2000 } /> );
+		rerender( { price: 2000 } );

-		expect( container.props[ 'data-maxPriceConstraint' ] ).toBe( 2000 );
+		expect( result.current.max ).toBe( 2000 );
 	} );

 	it( 'min price constraint should be updated when new price is set', () => {
-		const renderer = TestRenderer.create(
-			<TestComponent price={ 1000 } />
-		);
-		const container = renderer.root.findByType( 'div' );
+		const { result, rerender } = renderPriceConstraints( 1000 );

-		expect( container.props[ 'data-minPriceConstraint' ] ).toBe( 1000 );
+		expect( result.current.min ).toBe( 1000 );

-		renderer.update( <TestComponent price={ 2000 } /> );
+		rerender( { price: 2000 } );

-		expect( container.props[ 'data-minPriceConstraint' ] ).toBe( 2000 );
+		expect( result.current.min ).toBe( 2000 );
 	} );

 	it( 'previous price constraint should be preserved when new price is not an infinite number', () => {
-		const renderer = TestRenderer.create(
-			<TestComponent price={ 1000 } />
-		);
-		const container = renderer.root.findByType( 'div' );
+		const { result, rerender } = renderPriceConstraints( 1000 );

-		expect( container.props[ 'data-maxPriceConstraint' ] ).toBe( 1000 );
+		expect( result.current.max ).toBe( 1000 );

-		renderer.update( <TestComponent price={ Infinity } /> );
+		rerender( { price: Infinity } );

-		expect( container.props[ 'data-maxPriceConstraint' ] ).toBe( 1000 );
+		expect( result.current.max ).toBe( 1000 );
 	} );

 	it( 'max price constraint should be higher if the price is decimal', () => {
-		const renderer = TestRenderer.create(
-			<TestComponent price={ 1099 } />
-		);
-		const container = renderer.root.findByType( 'div' );
+		const { result, rerender } = renderPriceConstraints( 1099 );

-		expect( container.props[ 'data-maxPriceConstraint' ] ).toBe( 1100 );
+		expect( result.current.max ).toBe( 1100 );

-		renderer.update( <TestComponent price={ 1999 } /> );
+		rerender( { price: 1999 } );

-		expect( container.props[ 'data-maxPriceConstraint' ] ).toBe( 2000 );
+		expect( result.current.max ).toBe( 2000 );
 	} );

 	it( 'min price constraint should be lower if the price is decimal', () => {
-		const renderer = TestRenderer.create( <TestComponent price={ 999 } /> );
-		const container = renderer.root.findByType( 'div' );
+		const { result, rerender } = renderPriceConstraints( 999 );

-		expect( container.props[ 'data-minPriceConstraint' ] ).toBe( 900 );
+		expect( result.current.min ).toBe( 900 );

-		renderer.update( <TestComponent price={ 1999 } /> );
+		rerender( { price: 1999 } );

-		expect( container.props[ 'data-minPriceConstraint' ] ).toBe( 1900 );
+		expect( result.current.min ).toBe( 1900 );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-category.jsx b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-category.jsx
index ca85d49be66..dd0720dcf8b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-category.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-category.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render, act } from '@testing-library/react';
 import * as mockUtils from '@woocommerce/editor-components/utils';

 /**
@@ -23,30 +20,41 @@ jest.mock( '../../base/utils/errors', () => ( {

 const mockCategory = { name: 'Clothing' };
 const attributes = { categoryId: 1 };
-const TestComponent = withCategory( ( props ) => {
-	return (
-		<div
-			data-error={ props.error }
-			data-getCategory={ props.getCategory }
-			data-isLoading={ props.isLoading }
-			data-category={ props.category }
-		/>
-	);
+
+// Capture the props the HOC injects into the wrapped component.
+let lastProps;
+const CapturedComponent = jest.fn( ( props ) => {
+	lastProps = props;
+	return null;
 } );
-const render = () => {
-	return TestRenderer.create( <TestComponent attributes={ attributes } /> );
+const TestComponent = withCategory( CapturedComponent );
+
+// Run an interaction and flush the async state updates it triggers inside
+// `act`, so React's async fetch-then-setState work does not leak past the test.
+const settle = async ( fn ) => {
+	await act( async () => {
+		fn();
+		await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
+	} );
 };

 describe( 'withCategory Component', () => {
-	let renderer;
+	let renderResult;
+	const renderComponent = ( props = { attributes } ) =>
+		settle( () => {
+			renderResult = render( <TestComponent { ...props } /> );
+		} );
+
 	afterEach( () => {
 		mockUtils.getCategory.mockReset();
+		CapturedComponent.mockClear();
+		lastProps = undefined;
 	} );

 	describe( 'lifecycle events', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getCategory.mockImplementation( () => Promise.resolve() );
-			renderer = render();
+			await renderComponent();
 		} );

 		it( 'getCategory is called on mount with passed in category id', () => {
@@ -56,10 +64,14 @@ describe( 'withCategory Component', () => {
 			expect( getCategory ).toHaveBeenCalledTimes( 1 );
 		} );

-		it( 'getCategory is called on component update', () => {
+		it( 'getCategory is called on component update', async () => {
 			const { getCategory } = mockUtils;
 			const newAttributes = { ...attributes, categoryId: 2 };
-			renderer.update( <TestComponent attributes={ newAttributes } /> );
+			await settle( () =>
+				renderResult.rerender(
+					<TestComponent attributes={ newAttributes } />
+				)
+			);

 			expect( getCategory ).toHaveBeenNthCalledWith(
 				2,
@@ -68,31 +80,28 @@ describe( 'withCategory Component', () => {
 			expect( getCategory ).toHaveBeenCalledTimes( 2 );
 		} );

-		it( 'getCategory is hooked to the prop', () => {
+		it( 'getCategory is hooked to the prop', async () => {
 			const { getCategory } = mockUtils;
-			const props = renderer.root.findByType( 'div' ).props;

-			props[ 'data-getCategory' ]();
+			await settle( () => lastProps.getCategory() );

 			expect( getCategory ).toHaveBeenCalledTimes( 2 );
 		} );
 	} );

 	describe( 'when the API returns category data', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getCategory.mockImplementation( ( categoryId ) =>
 				Promise.resolve( { ...mockCategory, id: categoryId } )
 			);
-			renderer = render();
+			await renderComponent();
 		} );

 		it( 'sets the category props', () => {
-			const props = renderer.root.findByType( 'div' ).props;
-
-			expect( props[ 'data-error' ] ).toBeNull();
-			expect( typeof props[ 'data-getCategory' ] ).toBe( 'function' );
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-category' ] ).toEqual( {
+			expect( lastProps.error ).toBeNull();
+			expect( typeof lastProps.getCategory ).toBe( 'function' );
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.category ).toEqual( {
 				...mockCategory,
 				id: attributes.categoryId,
 			} );
@@ -101,31 +110,25 @@ describe( 'withCategory Component', () => {

 	describe( 'when the API returns an error', () => {
 		const error = { message: 'There was an error.' };
-		const getCategoryPromise = Promise.reject( error );
 		const formattedError = { message: 'There was an error.', type: 'api' };

-		beforeEach( () => {
-			mockUtils.getCategory.mockImplementation(
-				() => getCategoryPromise
+		beforeEach( async () => {
+			mockUtils.getCategory.mockImplementation( () =>
+				Promise.reject( error )
 			);
-			mockBaseUtils.formatError.mockImplementation(
-				() => formattedError
-			);
-			renderer = render();
+			mockBaseUtils.formatError.mockImplementation( () => formattedError );
+			await renderComponent();
 		} );

-		test( 'sets the error prop', async () => {
-			await expect( () => getCategoryPromise() ).toThrow();
-
+		test( 'sets the error prop', () => {
 			const { formatError } = mockBaseUtils;
-			const props = renderer.root.findByType( 'div' ).props;

 			expect( formatError ).toHaveBeenCalledWith( error );
 			expect( formatError ).toHaveBeenCalledTimes( 1 );
-			expect( props[ 'data-error' ] ).toEqual( formattedError );
-			expect( typeof props[ 'data-getCategory' ] ).toBe( 'function' );
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-category' ] ).toBeNull();
+			expect( lastProps.error ).toEqual( formattedError );
+			expect( typeof lastProps.getCategory ).toBe( 'function' );
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.category ).toBeNull();
 		} );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product-variations.jsx b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product-variations.jsx
index 0ba3fd4a868..14d5473b622 100644
--- a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product-variations.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product-variations.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render, act } from '@testing-library/react';
 import * as mockUtils from '@woocommerce/editor-components/utils';

 /**
@@ -29,35 +26,44 @@ const mockVariations = [
 	{ id: 3, name: 'Blue' },
 	{ id: 4, name: 'Red' },
 ];
-const TestComponent = withProductVariations( ( props ) => {
-	return (
-		<div
-			data-error={ props.error }
-			data-expandedProduct={ props.expandedProduct }
-			data-isLoading={ props.isLoading }
-			data-variations={ props.variations }
-			data-variationsLoading={ props.variationsLoading }
-			data-onLoadMoreVariations={ props.onLoadMoreVariations }
-			data-totalVariations={ props.totalVariations }
-		/>
-	);
+
+// Capture the props the HOC injects into the wrapped component.
+let lastProps;
+const CapturedComponent = jest.fn( ( props ) => {
+	lastProps = props;
+	return null;
 } );
-const render = () => {
-	return TestRenderer.create(
-		<TestComponent
-			error={ null }
-			isLoading={ false }
-			products={ mockProducts }
-			selected={ [ 1 ] }
-			showVariations={ true }
-		/>
-	);
+const TestComponent = withProductVariations( CapturedComponent );
+
+// Run an interaction and flush the async state updates it triggers inside
+// `act`, so React's async fetch-then-setState work does not leak past the test.
+const settle = async ( fn ) => {
+	await act( async () => {
+		fn();
+		await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
+	} );
 };

 describe( 'withProductVariations Component', () => {
-	let renderer;
+	let renderResult;
+	const renderComponent = ( props ) =>
+		settle( () => {
+			renderResult = render(
+				<TestComponent
+					error={ null }
+					isLoading={ false }
+					products={ mockProducts }
+					selected={ [ 1 ] }
+					showVariations={ true }
+					{ ...props }
+				/>
+			);
+		} );
+
 	afterEach( () => {
 		mockUtils.getProductVariationsWithTotal.mockReset();
+		CapturedComponent.mockClear();
+		lastProps = undefined;
 	} );

 	describe( 'lifecycle events', () => {
@@ -70,8 +76,8 @@ describe( 'withProductVariations Component', () => {
 			);
 		} );

-		it( 'getProductVariationsWithTotal is called on mount', () => {
-			renderer = render();
+		it( 'getProductVariationsWithTotal is called on mount', async () => {
+			await renderComponent();
 			const { getProductVariationsWithTotal } = mockUtils;

 			expect( getProductVariationsWithTotal ).toHaveBeenCalledWith( 1, {
@@ -80,26 +86,25 @@ describe( 'withProductVariations Component', () => {
 			expect( getProductVariationsWithTotal ).toHaveBeenCalledTimes( 1 );
 		} );

-		it( 'getProductVariationsWithTotal is called on component update', () => {
-			renderer = TestRenderer.create(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ mockProducts }
-				/>
-			);
+		it( 'getProductVariationsWithTotal is called on component update', async () => {
+			await renderComponent( {
+				selected: undefined,
+				showVariations: undefined,
+			} );
 			const { getProductVariationsWithTotal } = mockUtils;

 			expect( getProductVariationsWithTotal ).toHaveBeenCalledTimes( 0 );

-			renderer.update(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ mockProducts }
-					selected={ [ 1 ] }
-					showVariations={ true }
-				/>
+			await settle( () =>
+				renderResult.rerender(
+					<TestComponent
+						error={ null }
+						isLoading={ false }
+						products={ mockProducts }
+						selected={ [ 1 ] }
+						showVariations={ true }
+					/>
+				)
 			);

 			expect( getProductVariationsWithTotal ).toHaveBeenCalledWith( 1, {
@@ -108,31 +113,15 @@ describe( 'withProductVariations Component', () => {
 			expect( getProductVariationsWithTotal ).toHaveBeenCalledTimes( 1 );
 		} );

-		it( 'getProductVariationsWithTotal is not called if selected product has no variations', () => {
-			TestRenderer.create(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ mockProducts }
-					selected={ [ 2 ] }
-					showVariations={ true }
-				/>
-			);
+		it( 'getProductVariationsWithTotal is not called if selected product has no variations', async () => {
+			await renderComponent( { selected: [ 2 ] } );
 			const { getProductVariationsWithTotal } = mockUtils;

 			expect( getProductVariationsWithTotal ).toHaveBeenCalledTimes( 0 );
 		} );

-		it( 'getProductVariationsWithTotal is called if selected product is a variation', () => {
-			TestRenderer.create(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ mockProducts }
-					selected={ [ 3 ] }
-					showVariations={ true }
-				/>
-			);
+		it( 'getProductVariationsWithTotal is called if selected product is a variation', async () => {
+			await renderComponent( { selected: [ 3 ] } );
 			const { getProductVariationsWithTotal } = mockUtils;

 			expect( getProductVariationsWithTotal ).toHaveBeenCalledWith( 1, {
@@ -143,18 +132,17 @@ describe( 'withProductVariations Component', () => {
 	} );

 	describe( 'when the API returns variations data', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getProductVariationsWithTotal.mockImplementation( () =>
 				Promise.resolve( {
 					variations: mockVariations,
 					total: mockVariations.length,
 				} )
 			);
-			renderer = render();
+			await renderComponent();
 		} );

-		it( 'sets the variations props', async () => {
-			const props = renderer.root.findByType( 'div' ).props;
+		it( 'sets the variations props', () => {
 			const expectedVariations = {
 				1: [
 					{ id: 3, name: 'Blue', parent: 1 },
@@ -162,40 +150,34 @@ describe( 'withProductVariations Component', () => {
 				],
 			};

-			expect( props[ 'data-error' ] ).toBeNull();
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-variations' ] ).toEqual( expectedVariations );
+			expect( lastProps.error ).toBeNull();
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.variations ).toEqual( expectedVariations );
 		} );
 	} );

 	describe( 'when the API returns an error', () => {
 		const error = { message: 'There was an error.' };
-		const getProductVariationsWithTotalPromise = Promise.reject( error );
 		const formattedError = { message: 'There was an error.', type: 'api' };

-		beforeEach( () => {
-			mockUtils.getProductVariationsWithTotal.mockImplementation(
-				() => getProductVariationsWithTotalPromise
+		beforeEach( async () => {
+			mockUtils.getProductVariationsWithTotal.mockImplementation( () =>
+				Promise.reject( error )
 			);
 			mockBaseUtils.formatError.mockImplementation(
 				() => formattedError
 			);
-			renderer = render();
+			await renderComponent();
 		} );

-		test( 'sets the error prop', async () => {
-			await TestRenderer.act( async () => {
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
-
+		test( 'sets the error prop', () => {
 			const { formatError } = mockBaseUtils;
-			const props = renderer.root.findByType( 'div' ).props;

 			expect( formatError ).toHaveBeenCalledWith( error );
 			expect( formatError ).toHaveBeenCalledTimes( 1 );
-			expect( props[ 'data-error' ] ).toEqual( formattedError );
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-variations' ] ).toEqual( { 1: null } );
+			expect( lastProps.error ).toEqual( formattedError );
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.variations ).toEqual( { 1: null } );
 		} );
 	} );

@@ -233,21 +215,8 @@ describe( 'withProductVariations Component', () => {
 		} );

 		it( 'loads the first 25 variations by default and provides onLoadMoreVariations', async () => {
-			renderer = TestRenderer.create(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ productWithManyVariations }
-					selected={ [ 1 ] }
-					showVariations={ true }
-				/>
-			);
+			await renderComponent( { products: productWithManyVariations } );

-			await TestRenderer.act( async () => {
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
-
-			const props = renderer.root.findByType( 'div' ).props;
 			const { getProductVariationsWithTotal } = mockUtils;

 			// Should have been called once with offset 0
@@ -257,57 +226,34 @@ describe( 'withProductVariations Component', () => {
 			expect( getProductVariationsWithTotal ).toHaveBeenCalledTimes( 1 );

 			// Should have first 25 variations
-			expect( props[ 'data-variations' ][ 1 ] ).toHaveLength( 25 );
-			expect( props[ 'data-variations' ][ 1 ][ 0 ] ).toEqual( {
+			expect( lastProps.variations[ 1 ] ).toHaveLength( 25 );
+			expect( lastProps.variations[ 1 ][ 0 ] ).toEqual( {
 				id: 1,
 				name: 'Variation 1',
 				parent: 1,
 			} );
-			expect( props[ 'data-variations' ][ 1 ][ 24 ] ).toEqual( {
+			expect( lastProps.variations[ 1 ][ 24 ] ).toEqual( {
 				id: 25,
 				name: 'Variation 25',
 				parent: 1,
 			} );

 			// Should have total variations count
-			expect( props[ 'data-totalVariations' ][ 1 ] ).toBe(
-				totalVariations
-			);
+			expect( lastProps.totalVariations[ 1 ] ).toBe( totalVariations );

 			// Should provide onLoadMoreVariations function
-			expect( typeof props[ 'data-onLoadMoreVariations' ] ).toBe(
-				'function'
-			);
+			expect( typeof lastProps.onLoadMoreVariations ).toBe( 'function' );
 		} );

 		it( 'loads the next 25 variations when onLoadMoreVariations is called', async () => {
-			renderer = TestRenderer.create(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ productWithManyVariations }
-					selected={ [ 1 ] }
-					showVariations={ true }
-				/>
-			);
-
-			// Wait for initial load
-			await TestRenderer.act( async () => {
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
-
-			let props = renderer.root.findByType( 'div' ).props;
+			await renderComponent( { products: productWithManyVariations } );

 			// Verify initial 25 variations are loaded
-			expect( props[ 'data-variations' ][ 1 ] ).toHaveLength( 25 );
+			expect( lastProps.variations[ 1 ] ).toHaveLength( 25 );

 			// Call onLoadMoreVariations to load next batch
-			await TestRenderer.act( async () => {
-				props[ 'data-onLoadMoreVariations' ]();
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
+			await settle( () => lastProps.onLoadMoreVariations() );

-			props = renderer.root.findByType( 'div' ).props;
 			const { getProductVariationsWithTotal } = mockUtils;

 			// Should have been called again with offset 25
@@ -317,13 +263,13 @@ describe( 'withProductVariations Component', () => {
 			expect( getProductVariationsWithTotal ).toHaveBeenCalledTimes( 2 );

 			// Should now have 50 variations (25 + 25)
-			expect( props[ 'data-variations' ][ 1 ] ).toHaveLength( 50 );
-			expect( props[ 'data-variations' ][ 1 ][ 25 ] ).toEqual( {
+			expect( lastProps.variations[ 1 ] ).toHaveLength( 50 );
+			expect( lastProps.variations[ 1 ][ 25 ] ).toEqual( {
 				id: 26,
 				name: 'Variation 26',
 				parent: 1,
 			} );
-			expect( props[ 'data-variations' ][ 1 ][ 49 ] ).toEqual( {
+			expect( lastProps.variations[ 1 ][ 49 ] ).toEqual( {
 				id: 50,
 				name: 'Variation 50',
 				parent: 1,
@@ -331,38 +277,14 @@ describe( 'withProductVariations Component', () => {
 		} );

 		it( 'loads all variations when onLoadMoreVariations is called multiple times', async () => {
-			renderer = TestRenderer.create(
-				<TestComponent
-					error={ null }
-					isLoading={ false }
-					products={ productWithManyVariations }
-					selected={ [ 1 ] }
-					showVariations={ true }
-				/>
-			);
-
-			// Wait for initial load
-			await TestRenderer.act( async () => {
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
-
-			let props = renderer.root.findByType( 'div' ).props;
+			await renderComponent( { products: productWithManyVariations } );

 			// Load second batch
-			await TestRenderer.act( async () => {
-				props[ 'data-onLoadMoreVariations' ]();
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
-
-			props = renderer.root.findByType( 'div' ).props;
+			await settle( () => lastProps.onLoadMoreVariations() );

 			// Load third batch (final 10 variations)
-			await TestRenderer.act( async () => {
-				props[ 'data-onLoadMoreVariations' ]();
-				await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
-			} );
+			await settle( () => lastProps.onLoadMoreVariations() );

-			props = renderer.root.findByType( 'div' ).props;
 			const { getProductVariationsWithTotal } = mockUtils;

 			// Should have been called 3 times total
@@ -376,8 +298,8 @@ describe( 'withProductVariations Component', () => {
 			);

 			// Should now have all 60 variations
-			expect( props[ 'data-variations' ][ 1 ] ).toHaveLength( 60 );
-			expect( props[ 'data-variations' ][ 1 ][ 59 ] ).toEqual( {
+			expect( lastProps.variations[ 1 ] ).toHaveLength( 60 );
+			expect( lastProps.variations[ 1 ][ 59 ] ).toEqual( {
 				id: 60,
 				name: 'Variation 60',
 				parent: 1,
diff --git a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product.jsx b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product.jsx
index 4319cb15013..02bbfb7e254 100644
--- a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-product.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render, act } from '@testing-library/react';
 import * as mockUtils from '@woocommerce/editor-components/utils';

 /**
@@ -23,30 +20,41 @@ jest.mock( '../../base/utils/errors', () => ( {

 const mockProduct = { name: 'T-Shirt' };
 const attributes = { productId: 1 };
-const TestComponent = withProduct( ( props ) => {
-	return (
-		<div
-			data-error={ props.error }
-			data-getProduct={ props.getProduct }
-			data-isLoading={ props.isLoading }
-			data-product={ props.product }
-		/>
-	);
+
+// Capture the props the HOC injects into the wrapped component.
+let lastProps;
+const CapturedComponent = jest.fn( ( props ) => {
+	lastProps = props;
+	return null;
 } );
-const render = () => {
-	return TestRenderer.create( <TestComponent attributes={ attributes } /> );
+const TestComponent = withProduct( CapturedComponent );
+
+// Run an interaction and flush the async state updates it triggers inside
+// `act`, so React's async fetch-then-setState work does not leak past the test.
+const settle = async ( fn ) => {
+	await act( async () => {
+		fn();
+		await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
+	} );
 };

 describe( 'withProduct Component', () => {
-	let renderer;
+	let renderResult;
+	const renderComponent = ( props = { attributes } ) =>
+		settle( () => {
+			renderResult = render( <TestComponent { ...props } /> );
+		} );
+
 	afterEach( () => {
 		mockUtils.getProduct.mockReset();
+		CapturedComponent.mockClear();
+		lastProps = undefined;
 	} );

 	describe( 'lifecycle events', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getProduct.mockImplementation( () => Promise.resolve() );
-			renderer = render();
+			await renderComponent();
 		} );

 		it( 'getProduct is called on mount with passed in product id', () => {
@@ -56,10 +64,14 @@ describe( 'withProduct Component', () => {
 			expect( getProduct ).toHaveBeenCalledTimes( 1 );
 		} );

-		it( 'getProduct is called on component update', () => {
+		it( 'getProduct is called on component update', async () => {
 			const { getProduct } = mockUtils;
 			const newAttributes = { ...attributes, productId: 2 };
-			renderer.update( <TestComponent attributes={ newAttributes } /> );
+			await settle( () =>
+				renderResult.rerender(
+					<TestComponent attributes={ newAttributes } />
+				)
+			);

 			expect( getProduct ).toHaveBeenNthCalledWith(
 				2,
@@ -68,31 +80,28 @@ describe( 'withProduct Component', () => {
 			expect( getProduct ).toHaveBeenCalledTimes( 2 );
 		} );

-		it( 'getProduct is hooked to the prop', () => {
+		it( 'getProduct is hooked to the prop', async () => {
 			const { getProduct } = mockUtils;
-			const props = renderer.root.findByType( 'div' ).props;

-			props[ 'data-getProduct' ]();
+			await settle( () => lastProps.getProduct() );

 			expect( getProduct ).toHaveBeenCalledTimes( 2 );
 		} );
 	} );

 	describe( 'when the API returns product data', () => {
-		beforeEach( () => {
+		beforeEach( async () => {
 			mockUtils.getProduct.mockImplementation( ( productId ) =>
 				Promise.resolve( { ...mockProduct, id: productId } )
 			);
-			renderer = render();
+			await renderComponent();
 		} );

 		it( 'sets the product props', () => {
-			const props = renderer.root.findByType( 'div' ).props;
-
-			expect( props[ 'data-error' ] ).toBeNull();
-			expect( typeof props[ 'data-getProduct' ] ).toBe( 'function' );
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-product' ] ).toEqual( {
+			expect( lastProps.error ).toBeNull();
+			expect( typeof lastProps.getProduct ).toBe( 'function' );
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.product ).toEqual( {
 				...mockProduct,
 				id: attributes.productId,
 			} );
@@ -101,29 +110,25 @@ describe( 'withProduct Component', () => {

 	describe( 'when the API returns an error', () => {
 		const error = { message: 'There was an error.' };
-		const getProductPromise = Promise.reject( error );
 		const formattedError = { message: 'There was an error.', type: 'api' };

-		beforeEach( () => {
-			mockUtils.getProduct.mockImplementation( () => getProductPromise );
-			mockBaseUtils.formatError.mockImplementation(
-				() => formattedError
+		beforeEach( async () => {
+			mockUtils.getProduct.mockImplementation( () =>
+				Promise.reject( error )
 			);
-			renderer = render();
+			mockBaseUtils.formatError.mockImplementation( () => formattedError );
+			await renderComponent();
 		} );

-		test( 'sets the error prop', async () => {
-			await expect( () => getProductPromise() ).toThrow();
-
+		test( 'sets the error prop', () => {
 			const { formatError } = mockBaseUtils;
-			const props = renderer.root.findByType( 'div' ).props;

 			expect( formatError ).toHaveBeenCalledWith( error );
 			expect( formatError ).toHaveBeenCalledTimes( 1 );
-			expect( props[ 'data-error' ] ).toEqual( formattedError );
-			expect( typeof props[ 'data-getProduct' ] ).toBe( 'function' );
-			expect( props[ 'data-isLoading' ] ).toBe( false );
-			expect( props[ 'data-product' ] ).toBeNull();
+			expect( lastProps.error ).toEqual( formattedError );
+			expect( typeof lastProps.getProduct ).toBe( 'function' );
+			expect( lastProps.isLoading ).toBe( false );
+			expect( lastProps.product ).toBeNull();
 		} );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-categories.jsx b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-categories.jsx
index 37efc9f4a98..ee20dc52c3a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-categories.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-categories.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { render, act } from '@testing-library/react';
 import * as mockUtils from '@woocommerce/editor-components/utils';

 /**
@@ -20,36 +17,42 @@ mockUtils.getCategories = jest.fn().mockImplementation( () =>
 	] )
 );

+// Capture the props the HOC injects into the wrapped component.
+let lastProps;
+const CapturedComponent = jest.fn( ( props ) => {
+	lastProps = props;
+	return null;
+} );
+const TestComponent = withSearchedCategories( CapturedComponent );
+
+// Run an interaction and flush the async state updates it triggers inside
+// `act`, so React's async fetch-then-setState work does not leak past the test.
+const settle = async ( fn ) => {
+	await act( async () => {
+		fn();
+		await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
+	} );
+};
+
 describe( 'withSearchedCategories Component', () => {
 	afterEach( () => {
 		mockUtils.getCategories.mockClear();
+		CapturedComponent.mockClear();
+		lastProps = undefined;
 	} );
-	const TestComponent = withSearchedCategories(
-		( { selected, categories, isLoading, onSearch } ) => {
-			return (
-				<div
-					data-categories={ categories }
-					data-selected={ selected }
-					data-isLoading={ isLoading }
-					data-onSearch={ onSearch }
-				/>
-			);
-		}
-	);
+
 	describe( 'lifecycle tests', () => {
 		const selected = [ 10 ];
-		let props, renderer;

-		act( () => {
-			renderer = TestRenderer.create(
-				<TestComponent selected={ selected } />
+		beforeEach( async () => {
+			await settle( () =>
+				render( <TestComponent selected={ selected } /> )
 			);
 		} );

 		it( 'has expected values for props', () => {
-			props = renderer.root.findByType( 'div' ).props;
-			expect( props[ 'data-selected' ] ).toEqual( selected );
-			expect( props[ 'data-categories' ] ).toEqual( [
+			expect( lastProps.selected ).toEqual( selected );
+			expect( lastProps.categories ).toEqual( [
 				{ id: 1, name: 'Clothing' },
 				{ id: 2, name: 'Food' },
 			] );
diff --git a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-products.jsx b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-products.jsx
index a8b156fd3ff..0cd382a97af 100644
--- a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-products.jsx
+++ b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-searched-products.jsx
@@ -1,10 +1,7 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer, { act } from 'react-test-renderer';
+import { render, act } from '@testing-library/react';
 import * as mockUtils from '@woocommerce/editor-components/utils';
 import { useDebouncedCallback } from 'use-debounce';

@@ -39,49 +36,55 @@ mockUtils.getProducts = jest.fn().mockImplementation( () =>
 	] )
 );

+// Capture the props the HOC injects into the wrapped component.
+let lastProps;
+const CapturedComponent = jest.fn( ( props ) => {
+	lastProps = props;
+	return null;
+} );
+const TestComponent = withSearchedProducts( CapturedComponent );
+
+// Run an interaction and flush the async state updates it triggers inside
+// `act`, so React's async fetch-then-setState work does not leak past the test.
+const settle = async ( fn ) => {
+	await act( async () => {
+		fn();
+		await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
+	} );
+};
+
 describe( 'withSearchedProducts Component', () => {
 	const { getProducts } = mockUtils;
 	afterEach( () => {
 		useDebouncedCallback.mockClear();
 		mockUtils.getProducts.mockClear();
+		CapturedComponent.mockClear();
+		lastProps = undefined;
 	} );
-	const TestComponent = withSearchedProducts(
-		( { selected, products, isLoading, onSearch } ) => {
-			return (
-				<div
-					data-products={ products }
-					data-selected={ selected }
-					data-isLoading={ isLoading }
-					data-onSearch={ onSearch }
-				/>
-			);
-		}
-	);
+
 	describe( 'lifecycle tests', () => {
 		const selected = [ 10 ];
-		let props, renderer;

-		act( () => {
-			renderer = TestRenderer.create(
-				<TestComponent selected={ selected } />
+		beforeEach( async () => {
+			await settle( () =>
+				render( <TestComponent selected={ selected } /> )
 			);
 		} );

 		it( 'has expected values for props', () => {
-			props = renderer.root.findByType( 'div' ).props;
-			expect( props[ 'data-selected' ] ).toEqual( selected );
-			expect( props[ 'data-products' ] ).toEqual( [
+			expect( lastProps.selected ).toEqual( selected );
+			expect( lastProps.products ).toEqual( [
 				{ id: 10, name: 'foo', parent: 0 },
 				{ id: 20, name: 'bar', parent: 0 },
 			] );
 		} );

 		it( 'debounce and getProducts is called on search event', async () => {
-			props = renderer.root.findByType( 'div' ).props;
+			// Ignore the getProducts call triggered on mount so the assertion
+			// measures only the call made in response to the search event.
+			getProducts.mockClear();

-			act( () => {
-				props[ 'data-onSearch' ]();
-			} );
+			await settle( () => lastProps.onSearch() );

 			expect( useDebouncedCallback ).toHaveBeenCalled();
 			expect( getProducts ).toHaveBeenCalledTimes( 1 );
diff --git a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-transform-single-select-to-multiple-select.js b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-transform-single-select-to-multiple-select.js
index 8f571477dc3..64d96aaa0ae 100644
--- a/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-transform-single-select-to-multiple-select.js
+++ b/plugins/woocommerce/client/blocks/assets/js/hocs/test/with-transform-single-select-to-multiple-select.js
@@ -1,37 +1,35 @@
-// We need to disable the following eslint check as it's only applicable
-// to testing-library/react not `react-test-renderer` used here
-/* eslint-disable testing-library/await-async-query */
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render } from '@testing-library/react';

 /**
  * Internal dependencies
  */
 import withTransformSingleSelectToMultipleSelect from '../with-transform-single-select-to-multiple-select';

-const TestComponent = withTransformSingleSelectToMultipleSelect( ( props ) => {
-	return <div selected={ props.selected } />;
-} );
+const InnerComponent = jest.fn( () => null );
+const TestComponent =
+	withTransformSingleSelectToMultipleSelect( InnerComponent );
+
+const getReceivedProps = () =>
+	InnerComponent.mock.calls[ InnerComponent.mock.calls.length - 1 ][ 0 ];

 describe( 'withTransformSingleSelectToMultipleSelect Component', () => {
+	beforeEach( () => {
+		InnerComponent.mockClear();
+	} );
+
 	describe( 'when the API returns an error', () => {
 		it( 'converts the selected value into an array', () => {
 			const selected = 123;
-			const renderer = TestRenderer.create(
-				<TestComponent selected={ selected } />
-			);
-			const props = renderer.root.findByType( 'div' ).props;
-			expect( props.selected ).toEqual( [ selected ] );
+			render( <TestComponent selected={ selected } /> );
+			expect( getReceivedProps().selected ).toEqual( [ selected ] );
 		} );

 		it( 'passes an empty array as the selected prop if selected was null', () => {
-			const renderer = TestRenderer.create(
-				<TestComponent selected={ null } />
-			);
-			const props = renderer.root.findByType( 'div' ).props;
-			expect( props.selected ).toEqual( [] );
+			render( <TestComponent selected={ null } /> );
+			expect( getReceivedProps().selected ).toEqual( [] );
 		} );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/package.json b/plugins/woocommerce/client/blocks/package.json
index 87be32ac489..9a1798e41b4 100644
--- a/plugins/woocommerce/client/blocks/package.json
+++ b/plugins/woocommerce/client/blocks/package.json
@@ -210,7 +210,6 @@
 		"progress-bar-webpack-plugin": "2.1.x",
 		"react-docgen": "5.4.3",
 		"react-docgen-typescript-plugin": "^1.0.5",
-		"react-test-renderer": "18.3.x",
 		"redux": "4.2.1",
 		"request-promise": "4.2.6",
 		"rimraf": "5.0.5",
diff --git a/plugins/woocommerce/client/blocks/packages/components/chip/test/__snapshots__/index.js.snap b/plugins/woocommerce/client/blocks/packages/components/chip/test/__snapshots__/index.js.snap
index a2336c981e2..d761af527bd 100644
--- a/plugins/woocommerce/client/blocks/packages/components/chip/test/__snapshots__/index.js.snap
+++ b/plugins/woocommerce/client/blocks/packages/components/chip/test/__snapshots__/index.js.snap
@@ -1,312 +1,319 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP

 exports[`Chip should render children nodes 1`] = `
-<li
-  className="wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
+<div>
+  <li
+    class="wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    Test
-  </span>
-  Lorem Ipsum
-</li>
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
+    >
+      Test
+    </span>
+    Lorem Ipsum
+  </li>
+</div>
 `;

 exports[`Chip should render defined radius 1`] = `
-<li
-  className="wc-block-components-chip wc-block-components-chip--radius-large"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
+<div>
+  <li
+    class="wc-block-components-chip wc-block-components-chip--radius-large"
   >
-    Test
-  </span>
-</li>
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
+    >
+      Test
+    </span>
+  </li>
+</div>
 `;

 exports[`Chip should render nodes as the text 1`] = `
-<li
-  className="wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
+<div>
+  <li
+    class="wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    <h1>
-      Test
-    </h1>
-  </span>
-</li>
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
+    >
+      <h1>
+        Test
+      </h1>
+    </span>
+  </li>
+</div>
 `;

 exports[`Chip should render screen reader text 1`] = `
-<li
-  className="wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={true}
-    className="wc-block-components-chip__text"
-  >
-    Test
-  </span>
-  <span
-    className="screen-reader-text"
+<div>
+  <li
+    class="wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    Test 2
-  </span>
-</li>
+    <span
+      aria-hidden="true"
+      class="wc-block-components-chip__text"
+    >
+      Test
+    </span>
+    <span
+      class="screen-reader-text"
+    >
+      Test 2
+    </span>
+  </li>
+</div>
 `;

 exports[`Chip should render text 1`] = `
-<li
-  className="wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
+<div>
+  <li
+    class="wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    Test
-  </span>
-</li>
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
+    >
+      Test
+    </span>
+  </li>
+</div>
 `;

 exports[`Chip with custom wrapper should render a chip made up of a div instead of a li 1`] = `
-<div
-  className="wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
+<div>
+  <div
+    class="wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    Test
-  </span>
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
+    >
+      Test
+    </span>
+  </div>
 </div>
 `;

 exports[`RemovableChip should render custom aria label 1`] = `
-<li
-  className="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
-  >
-    <h1>
-      Test
-    </h1>
-  </span>
-  <button
-    aria-label="Aria test"
-    className="wc-block-components-chip__remove"
-    disabled={false}
-    onClick={[Function]}
-    onKeyDown={[Function]}
+<div>
+  <li
+    class="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    <svg
-      aria-hidden={true}
-      className="wc-block-components-chip__remove-icon"
-      focusable={false}
-      height={16}
-      role="img"
-      viewBox="0 0 24 24"
-      width={16}
-      xmlns="http://www.w3.org/2000/svg"
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
     >
-      <path
-        d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
-      />
-    </svg>
-  </button>
-</li>
+      <h1>
+        Test
+      </h1>
+    </span>
+    <button
+      aria-label="Aria test"
+      class="wc-block-components-chip__remove"
+    >
+      <svg
+        aria-hidden="true"
+        class="wc-block-components-chip__remove-icon"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 24 24"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
+        />
+      </svg>
+    </button>
+  </li>
+</div>
 `;

 exports[`RemovableChip should render default aria label if text is a node 1`] = `
-<li
-  className="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={true}
-    className="wc-block-components-chip__text"
-  >
-    <h1>
-      Test
-    </h1>
-  </span>
-  <span
-    className="screen-reader-text"
+<div>
+  <li
+    class="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    Test 2
-  </span>
-  <button
-    aria-label="Remove "Test 2""
-    className="wc-block-components-chip__remove"
-    disabled={false}
-    onClick={[Function]}
-    onKeyDown={[Function]}
-  >
-    <svg
-      aria-hidden={true}
-      className="wc-block-components-chip__remove-icon"
-      focusable={false}
-      height={16}
-      role="img"
-      viewBox="0 0 24 24"
-      width={16}
-      xmlns="http://www.w3.org/2000/svg"
+    <span
+      aria-hidden="true"
+      class="wc-block-components-chip__text"
     >
-      <path
-        d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
-      />
-    </svg>
-  </button>
-</li>
+      <h1>
+        Test
+      </h1>
+    </span>
+    <span
+      class="screen-reader-text"
+    >
+      Test 2
+    </span>
+    <button
+      aria-label="Remove "Test 2""
+      class="wc-block-components-chip__remove"
+    >
+      <svg
+        aria-hidden="true"
+        class="wc-block-components-chip__remove-icon"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 24 24"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
+        />
+      </svg>
+    </button>
+  </li>
+</div>
 `;

 exports[`RemovableChip should render screen reader text aria label 1`] = `
-<li
-  className="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={true}
-    className="wc-block-components-chip__text"
-  >
-    Test
-  </span>
-  <span
-    className="screen-reader-text"
-  >
-    Test 2
-  </span>
-  <button
-    aria-label="Remove "Test 2""
-    className="wc-block-components-chip__remove"
-    disabled={false}
-    onClick={[Function]}
-    onKeyDown={[Function]}
+<div>
+  <li
+    class="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    <svg
-      aria-hidden={true}
-      className="wc-block-components-chip__remove-icon"
-      focusable={false}
-      height={16}
-      role="img"
-      viewBox="0 0 24 24"
-      width={16}
-      xmlns="http://www.w3.org/2000/svg"
+    <span
+      aria-hidden="true"
+      class="wc-block-components-chip__text"
     >
-      <path
-        d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
-      />
-    </svg>
-  </button>
-</li>
+      Test
+    </span>
+    <span
+      class="screen-reader-text"
+    >
+      Test 2
+    </span>
+    <button
+      aria-label="Remove "Test 2""
+      class="wc-block-components-chip__remove"
+    >
+      <svg
+        aria-hidden="true"
+        class="wc-block-components-chip__remove-icon"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 24 24"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
+        />
+      </svg>
+    </button>
+  </li>
+</div>
 `;

 exports[`RemovableChip should render text and the remove button 1`] = `
-<li
-  className="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
-  >
-    Test
-  </span>
-  <button
-    aria-label="Remove "Test""
-    className="wc-block-components-chip__remove"
-    disabled={false}
-    onClick={[Function]}
-    onKeyDown={[Function]}
+<div>
+  <li
+    class="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    <svg
-      aria-hidden={true}
-      className="wc-block-components-chip__remove-icon"
-      focusable={false}
-      height={16}
-      role="img"
-      viewBox="0 0 24 24"
-      width={16}
-      xmlns="http://www.w3.org/2000/svg"
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
     >
-      <path
-        d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
-      />
-    </svg>
-  </button>
-</li>
+      Test
+    </span>
+    <button
+      aria-label="Remove "Test""
+      class="wc-block-components-chip__remove"
+    >
+      <svg
+        aria-hidden="true"
+        class="wc-block-components-chip__remove-icon"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 24 24"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
+        />
+      </svg>
+    </button>
+  </li>
+</div>
 `;

 exports[`RemovableChip should render with disabled remove button 1`] = `
-<li
-  className="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
-  >
-    Test
-  </span>
-  <button
-    aria-label="Remove "Test""
-    className="wc-block-components-chip__remove"
-    disabled={true}
-    onClick={[Function]}
-    onKeyDown={[Function]}
+<div>
+  <li
+    class="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    <svg
-      aria-hidden={true}
-      className="wc-block-components-chip__remove-icon"
-      focusable={false}
-      height={16}
-      role="img"
-      viewBox="0 0 24 24"
-      width={16}
-      xmlns="http://www.w3.org/2000/svg"
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
     >
-      <path
-        d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
-      />
-    </svg>
-  </button>
-</li>
+      Test
+    </span>
+    <button
+      aria-label="Remove "Test""
+      class="wc-block-components-chip__remove"
+      disabled=""
+    >
+      <svg
+        aria-hidden="true"
+        class="wc-block-components-chip__remove-icon"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 24 24"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
+        />
+      </svg>
+    </button>
+  </li>
+</div>
 `;

 exports[`RemovableChip with removeOnAnyClick should be a button when removeOnAnyClick is set to true 1`] = `
-<button
-  aria-label="Remove "Test""
-  className="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
-  disabled={false}
-  onClick={[Function]}
-  onKeyDown={[Function]}
->
-  <span
-    aria-hidden={false}
-    className="wc-block-components-chip__text"
-  >
-    Test
-  </span>
-  <span
-    aria-hidden={true}
-    className="wc-block-components-chip__remove"
+<div>
+  <button
+    aria-label="Remove "Test""
+    class="is-removable wc-block-components-chip wc-block-components-chip--radius-small"
   >
-    <svg
-      aria-hidden={true}
-      className="wc-block-components-chip__remove-icon"
-      focusable={false}
-      height={16}
-      role="img"
-      viewBox="0 0 24 24"
-      width={16}
-      xmlns="http://www.w3.org/2000/svg"
+    <span
+      aria-hidden="false"
+      class="wc-block-components-chip__text"
     >
-      <path
-        d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
-      />
-    </svg>
-  </span>
-</button>
+      Test
+    </span>
+    <span
+      aria-hidden="true"
+      class="wc-block-components-chip__remove"
+    >
+      <svg
+        aria-hidden="true"
+        class="wc-block-components-chip__remove-icon"
+        focusable="false"
+        height="16"
+        role="img"
+        viewBox="0 0 24 24"
+        width="16"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"
+        />
+      </svg>
+    </span>
+  </button>
+</div>
 `;
diff --git a/plugins/woocommerce/client/blocks/packages/components/chip/test/index.js b/plugins/woocommerce/client/blocks/packages/components/chip/test/index.js
index f666b1ab1ff..f95c8ff347d 100644
--- a/plugins/woocommerce/client/blocks/packages/components/chip/test/index.js
+++ b/plugins/woocommerce/client/blocks/packages/components/chip/test/index.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -10,100 +10,92 @@ import { Chip, RemovableChip } from '..';

 describe( 'Chip', () => {
 	test( 'should render text', () => {
-		const component = TestRenderer.create( <Chip text="Test" /> );
+		const { container } = render( <Chip text="Test" /> );

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render nodes as the text', () => {
-		const component = TestRenderer.create(
-			<Chip text={ <h1>Test</h1> } />
-		);
+		const { container } = render( <Chip text={ <h1>Test</h1> } /> );

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render defined radius', () => {
-		const component = TestRenderer.create(
-			<Chip text="Test" radius="large" />
-		);
+		const { container } = render( <Chip text="Test" radius="large" /> );

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render screen reader text', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<Chip text="Test" screenReaderText="Test 2" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render children nodes', () => {
-		const component = TestRenderer.create(
-			<Chip text="Test">Lorem Ipsum</Chip>
-		);
+		const { container } = render( <Chip text="Test">Lorem Ipsum</Chip> );

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	describe( 'with custom wrapper', () => {
 		test( 'should render a chip made up of a div instead of a li', () => {
-			const component = TestRenderer.create(
-				<Chip text="Test" element="div" />
-			);
+			const { container } = render( <Chip text="Test" element="div" /> );

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );
 	} );
 } );

 describe( 'RemovableChip', () => {
 	test( 'should render text and the remove button', () => {
-		const component = TestRenderer.create( <RemovableChip text="Test" /> );
+		const { container } = render( <RemovableChip text="Test" /> );

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render with disabled remove button', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<RemovableChip text="Test" disabled={ true } />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render custom aria label', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<RemovableChip text={ <h1>Test</h1> } ariaLabel="Aria test" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render default aria label if text is a node', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<RemovableChip text={ <h1>Test</h1> } screenReaderText="Test 2" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	test( 'should render screen reader text aria label', () => {
-		const component = TestRenderer.create(
+		const { container } = render(
 			<RemovableChip text="Test" screenReaderText="Test 2" />
 		);

-		expect( component.toJSON() ).toMatchSnapshot();
+		expect( container ).toMatchSnapshot();
 	} );

 	describe( 'with removeOnAnyClick', () => {
 		test( 'should be a button when removeOnAnyClick is set to true', () => {
-			const component = TestRenderer.create(
+			const { container } = render(
 				<RemovableChip text="Test" removeOnAnyClick={ true } />
 			);

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );
 	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/packages/components/label/test/__snapshots__/index.js.snap b/plugins/woocommerce/client/blocks/packages/components/label/test/__snapshots__/index.js.snap
index 48dccc73894..d3e50b43aa7 100644
--- a/plugins/woocommerce/client/blocks/packages/components/label/test/__snapshots__/index.js.snap
+++ b/plugins/woocommerce/client/blocks/packages/components/label/test/__snapshots__/index.js.snap
@@ -1,62 +1,74 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP

 exports[`Label with wrapperElement should render both label and screen reader label 1`] = `
-<label
-  className="foo-bar"
-  data-foo="bar"
->
-  <span
-    aria-hidden="true"
-  >
-    Lorem
-  </span>
-  <span
-    className="screen-reader-text"
+<div>
+  <label
+    class="foo-bar"
+    data-foo="bar"
   >
-    Ipsum
-  </span>
-</label>
+    <span
+      aria-hidden="true"
+    >
+      Lorem
+    </span>
+    <span
+      class="screen-reader-text"
+    >
+      Ipsum
+    </span>
+  </label>
+</div>
 `;

 exports[`Label with wrapperElement should render only the label 1`] = `
-<label
-  className="foo-bar"
-  data-foo="bar"
->
-  Lorem
-</label>
+<div>
+  <label
+    class="foo-bar"
+    data-foo="bar"
+  >
+    Lorem
+  </label>
+</div>
 `;

 exports[`Label with wrapperElement should render only the screen reader label 1`] = `
-<label
-  className="foo-bar screen-reader-text"
-  data-foo="bar"
->
-  Ipsum
-</label>
+<div>
+  <label
+    class="foo-bar screen-reader-text"
+    data-foo="bar"
+  >
+    Ipsum
+  </label>
+</div>
 `;

 exports[`Label without wrapperElement should render both label and screen reader label 1`] = `
-[
+<div>
   <span
     aria-hidden="true"
   >
     Lorem
-  </span>,
+  </span>
   <span
-    className="screen-reader-text"
+    class="screen-reader-text"
   >
     Ipsum
-  </span>,
-]
+  </span>
+</div>
 `;

-exports[`Label without wrapperElement should render only the label 1`] = `"Lorem"`;
+exports[`Label without wrapperElement should render only the label 1`] = `
+<div>
+  Lorem
+</div>
+`;

 exports[`Label without wrapperElement should render only the screen reader label 1`] = `
-<span
-  className="screen-reader-text"
->
-  Ipsum
-</span>
+<div>
+  <span
+    class="screen-reader-text"
+  >
+    Ipsum
+  </span>
+</div>
 `;
diff --git a/plugins/woocommerce/client/blocks/packages/components/label/test/index.js b/plugins/woocommerce/client/blocks/packages/components/label/test/index.js
index 1ce67de5fa4..56e8b651997 100644
--- a/plugins/woocommerce/client/blocks/packages/components/label/test/index.js
+++ b/plugins/woocommerce/client/blocks/packages/components/label/test/index.js
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import TestRenderer from 'react-test-renderer';
+import { render } from '@testing-library/react';

 /**
  * Internal dependencies
@@ -11,31 +11,29 @@ import Label from '../';
 describe( 'Label', () => {
 	describe( 'without wrapperElement', () => {
 		test( 'should render both label and screen reader label', () => {
-			const component = TestRenderer.create(
+			const { container } = render(
 				<Label label="Lorem" screenReaderLabel="Ipsum" />
 			);

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );

 		test( 'should render only the label', () => {
-			const component = TestRenderer.create( <Label label="Lorem" /> );
+			const { container } = render( <Label label="Lorem" /> );

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );

 		test( 'should render only the screen reader label', () => {
-			const component = TestRenderer.create(
-				<Label screenReaderLabel="Ipsum" />
-			);
+			const { container } = render( <Label screenReaderLabel="Ipsum" /> );

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );
 	} );

 	describe( 'with wrapperElement', () => {
 		test( 'should render both label and screen reader label', () => {
-			const component = TestRenderer.create(
+			const { container } = render(
 				<Label
 					label="Lorem"
 					screenReaderLabel="Ipsum"
@@ -47,11 +45,11 @@ describe( 'Label', () => {
 				/>
 			);

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );

 		test( 'should render only the label', () => {
-			const component = TestRenderer.create(
+			const { container } = render(
 				<Label
 					label="Lorem"
 					wrapperElement="label"
@@ -62,11 +60,11 @@ describe( 'Label', () => {
 				/>
 			);

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );

 		test( 'should render only the screen reader label', () => {
-			const component = TestRenderer.create(
+			const { container } = render(
 				<Label
 					screenReaderLabel="Ipsum"
 					wrapperElement="label"
@@ -77,7 +75,7 @@ describe( 'Label', () => {
 				/>
 			);

-			expect( component.toJSON() ).toMatchSnapshot();
+			expect( container ).toMatchSnapshot();
 		} );
 	} );
 } );
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2c4ce65320c..9c3d6a4b3ce 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3285,9 +3285,6 @@ importers:
       react-refresh:
         specifier: ^0.14.0
         version: 0.14.2
-      react-test-renderer:
-        specifier: 18.3.x
-        version: 18.3.1(react@18.3.1)
       readline-sync:
         specifier: ^1.4.10
         version: 1.4.10
@@ -3853,9 +3850,6 @@ importers:
       react-docgen-typescript-plugin:
         specifier: ^1.0.5
         version: 1.0.8(typescript@5.7.3)(webpack@5.97.1)
-      react-test-renderer:
-        specifier: 18.3.x
-        version: 18.3.1(react@18.3.1)
       redux:
         specifier: 4.2.1
         version: 4.2.1