From e0b8c1926ccd33849f96018979edf9d0bc02ffca Mon Sep 17 00:00:00 2001 From: Aymeric Giraudet Date: Tue, 7 Jan 2025 11:37:37 +0100 Subject: [PATCH 1/5] feat: support for React 19 --- examples/react/default-theme/package.json | 6 +- .../src/components/layout/Tabs.tsx | 4 +- .../components/ClearFiltersMobile.tsx | 2 +- examples/react/e-commerce/package.json | 6 +- examples/react/getting-started/package.json | 6 +- examples/react/next-app-router/package.json | 6 +- examples/react/next-routing/package.json | 6 +- examples/react/next/package.json | 6 +- examples/react/ssr/package.json | 4 +- package.json | 15 +-- .../src/components/Highlight.tsx | 3 +- .../src/components/Hits.tsx | 1 + .../src/types/Renderer.ts | 6 +- .../react-instantsearch-core/package.json | 2 +- .../src/components/DynamicWidgets.tsx | 4 +- .../__tests__/InstantSearch.test.tsx | 2 +- .../__tests__/useBreadcrumb.test.tsx | 70 +++++----- .../__tests__/useClearRefinements.test.tsx | 72 +++++------ .../__tests__/useConfigure.test.tsx | 25 ++-- .../__tests__/useCurrentRefinements.test.tsx | 104 ++++++++------- .../__tests__/useDynamicWidgets.test.tsx | 33 ++--- .../__tests__/useGeoSearch.test.tsx | 38 +++--- .../__tests__/useHierarchicalMenu.test.tsx | 32 +++-- .../src/connectors/__tests__/useHits.test.tsx | 28 ++-- .../__tests__/useHitsPerPage.test.tsx | 50 ++++---- .../__tests__/useInfiniteHits.test.tsx | 36 +++--- .../src/connectors/__tests__/useMenu.test.tsx | 39 +++--- .../__tests__/useNumericMenu.test.tsx | 70 +++++----- .../__tests__/usePagination.test.tsx | 41 +++--- .../__tests__/useQueryRules.test.tsx | 30 +++-- .../connectors/__tests__/useRange.test.tsx | 47 +++---- .../__tests__/useRefinementList.test.tsx | 38 +++--- .../__tests__/useRelatedProducts.test.tsx | 18 ++- .../__tests__/useSearchBox.test.tsx | 42 +++--- .../connectors/__tests__/useSortBy.test.tsx | 27 ++-- .../connectors/__tests__/useStats.test.tsx | 32 +++-- .../__tests__/useToggleRefinement.test.tsx | 44 ++++--- .../__tests__/useTrendingItems.test.tsx | 24 ++-- .../src/hooks/__tests__/useConnector.test.tsx | 74 +++++------ .../hooks/__tests__/useInstantSearch.test.tsx | 7 +- .../lib/__tests__/useSearchResults.test.tsx | 75 +++++------ .../src/lib/__tests__/useSearchState.test.tsx | 3 +- .../server/__tests__/getServerState.test.tsx | 4 +- .../__tests__/module/is-cjs-module.cjs | 4 + .../__tests__/module/is-es-module.mjs | 3 + .../src/InstantSearchNext.tsx | 2 +- .../src/useInstantSearchRouting.ts | 6 +- packages/react-instantsearch/package.json | 4 +- .../react-instantsearch/src/ui/SearchBox.tsx | 4 +- tests/umd.test.ts | 12 -- yarn.lock | 120 ++++++------------ 51 files changed, 678 insertions(+), 659 deletions(-) diff --git a/examples/react/default-theme/package.json b/examples/react/default-theme/package.json index e78e6b803f..493db2b52a 100644 --- a/examples/react/default-theme/package.json +++ b/examples/react/default-theme/package.json @@ -10,15 +10,15 @@ "algoliasearch": "5.1.1", "instantsearch.css": "8.5.1", "instantsearch.js": "4.75.7", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10" }, "devDependencies": { "@parcel/core": "2.10.0", "@parcel/packager-raw-url": "2.10.0", "@parcel/transformer-webmanifest": "2.10.0", - "@types/react": "^18.0.9", + "@types/react": "19.0.3", "parcel": "2.10.0", "typescript": "5.5.2" }, diff --git a/examples/react/default-theme/src/components/layout/Tabs.tsx b/examples/react/default-theme/src/components/layout/Tabs.tsx index faf72c151f..7dd0ae0487 100644 --- a/examples/react/default-theme/src/components/layout/Tabs.tsx +++ b/examples/react/default-theme/src/components/layout/Tabs.tsx @@ -50,7 +50,9 @@ export function Tabs({ children }: { children: React.ReactNode }) { id={getTabId(index, 'title')} tabIndex={isSelected ? 0 : -1} className={cx('Tabs-title', isSelected && 'Tabs-title--active')} - ref={(element) => (tabsRefs.current[index] = element!)} + ref={(element) => { + tabsRefs.current[index] = element!; + }} key={getTabId(index)} onClick={() => setCurrentTab(index)} onKeyDown={onKeyDown} diff --git a/examples/react/e-commerce/components/ClearFiltersMobile.tsx b/examples/react/e-commerce/components/ClearFiltersMobile.tsx index 849730d197..6fb501ecea 100644 --- a/examples/react/e-commerce/components/ClearFiltersMobile.tsx +++ b/examples/react/e-commerce/components/ClearFiltersMobile.tsx @@ -4,7 +4,7 @@ import { useClearRefinements } from 'react-instantsearch'; export function ClearFiltersMobile({ containerRef, }: { - containerRef: React.RefObject; + containerRef: React.RefObject; }) { const { refine } = useClearRefinements(); diff --git a/examples/react/e-commerce/package.json b/examples/react/e-commerce/package.json index b428201382..7afd189317 100644 --- a/examples/react/e-commerce/package.json +++ b/examples/react/e-commerce/package.json @@ -12,16 +12,16 @@ "algoliasearch": "5.1.1", "instantsearch.css": "8.5.1", "instantsearch.js": "4.75.7", - "react": "18.2.0", + "react": "19.0.0", "react-compound-slider": "3.4.0", - "react-dom": "18.2.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10" }, "devDependencies": { "@parcel/core": "2.10.0", "@parcel/packager-raw-url": "2.10.0", "@parcel/transformer-webmanifest": "2.10.0", - "@types/react": "^18.0.9", + "@types/react": "19.0.3", "parcel": "2.10.0", "typescript": "5.5.2" }, diff --git a/examples/react/getting-started/package.json b/examples/react/getting-started/package.json index 12986c9ba0..cdb18cb15c 100644 --- a/examples/react/getting-started/package.json +++ b/examples/react/getting-started/package.json @@ -10,15 +10,15 @@ "algoliasearch": "5.1.1", "instantsearch.css": "8.5.1", "instantsearch.js": "4.75.7", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10" }, "devDependencies": { "@parcel/core": "2.10.0", "@parcel/packager-raw-url": "2.10.0", "@parcel/transformer-webmanifest": "2.10.0", - "@types/react": "^18.0.9", + "@types/react": "19.0.3", "parcel": "2.10.0", "typescript": "5.5.2" }, diff --git a/examples/react/next-app-router/package.json b/examples/react/next-app-router/package.json index 5c9d0a73d2..c08688f698 100644 --- a/examples/react/next-app-router/package.json +++ b/examples/react/next-app-router/package.json @@ -12,14 +12,14 @@ "algoliasearch": "5.1.1", "instantsearch.css": "8.5.1", "next": "13.5.1", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10", "react-instantsearch-nextjs": "0.3.21" }, "devDependencies": { "@types/node": "17.0.40", - "@types/react": "18.0.12", + "@types/react": "19.0.3", "eslint": "8.4.0", "eslint-config-next": "12.0.7", "typescript": "5.5.2" diff --git a/examples/react/next-routing/package.json b/examples/react/next-routing/package.json index 9a48ad46e2..6b1d225fd7 100644 --- a/examples/react/next-routing/package.json +++ b/examples/react/next-routing/package.json @@ -12,14 +12,14 @@ "algoliasearch": "5.1.1", "instantsearch.css": "8.5.1", "next": "13.5.1", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10", "react-instantsearch-router-nextjs": "7.13.10" }, "devDependencies": { "@types/node": "17.0.40", - "@types/react": "18.0.12", + "@types/react": "19.0.3", "eslint": "8.4.0", "eslint-config-next": "12.0.7", "typescript": "5.5.2" diff --git a/examples/react/next/package.json b/examples/react/next/package.json index e7f8a609e9..ffb2915898 100644 --- a/examples/react/next/package.json +++ b/examples/react/next/package.json @@ -12,14 +12,14 @@ "algoliasearch": "5.1.1", "instantsearch.css": "8.5.1", "next": "13.5.1", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10", "react-instantsearch-router-nextjs": "7.13.10" }, "devDependencies": { "@types/node": "17.0.40", - "@types/react": "18.0.12", + "@types/react": "19.0.3", "eslint": "8.4.0", "eslint-config-next": "12.0.7", "typescript": "5.5.2" diff --git a/examples/react/ssr/package.json b/examples/react/ssr/package.json index 448ec73fb0..16e46081bf 100644 --- a/examples/react/ssr/package.json +++ b/examples/react/ssr/package.json @@ -24,8 +24,8 @@ "dependencies": { "algoliasearch": "5.1.1", "express": "4.17.1", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-instantsearch": "7.13.10" } } diff --git a/package.json b/package.json index 218db12963..1a448a8a47 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,7 @@ "@testing-library/dom": "8.18.1", "@testing-library/jest-dom": "5.16.5", "@testing-library/preact": "3.2.2", - "@testing-library/react": "13.2.0", - "@testing-library/react-hooks": "7.0.2", + "@testing-library/react": "16.1.0", "@testing-library/user-event": "13.5.0", "@types/enzyme": "^3.1.15", "@types/googlemaps": "^3.30.16", @@ -69,9 +68,9 @@ "@types/jest-diff": "^24.3.0", "@types/jsdom": "16.2.13", "@types/prop-types": "^15.5.8", - "@types/react": "^18.0.9", - "@types/react-dom": "^18.0.5", - "@types/react-test-renderer": "^18.0.0", + "@types/react": "19.0.3", + "@types/react-dom": "19.0.2", + "@types/react-test-renderer": "19.0.0", "@types/storybook__addon-actions": "3.4.2", "@types/storybook__addon-knobs": "^5.0.0", "@typescript-eslint/eslint-plugin": "5.38.1", @@ -126,9 +125,9 @@ "places.js": "1.17.1", "prettier": "^2.4.1", "prop-types": "15.6.2", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-test-renderer": "18.2.0", + "react": "19.0.0", + "react-dom": "19.0.0", + "react-test-renderer": "19.0.0", "rheostat": "2.2.0", "rollup": "1.29.1", "rollup-plugin-babel": "4.3.3", diff --git a/packages/instantsearch-ui-components/src/components/Highlight.tsx b/packages/instantsearch-ui-components/src/components/Highlight.tsx index d6172434c7..43e0aaf9b4 100644 --- a/packages/instantsearch-ui-components/src/components/Highlight.tsx +++ b/packages/instantsearch-ui-components/src/components/Highlight.tsx @@ -9,11 +9,12 @@ import type { } from '../types'; type HighlightPartProps = { - children: ComponentChildren; + children?: ComponentChildren; classNames: Partial; highlightedTagName: ElementType; nonHighlightedTagName: ElementType; isHighlighted: boolean; + key?: string | number; }; function createHighlightPartComponent({ createElement }: Renderer) { diff --git a/packages/instantsearch-ui-components/src/components/Hits.tsx b/packages/instantsearch-ui-components/src/components/Hits.tsx index dfeb42ad1c..546e16ccdc 100644 --- a/packages/instantsearch-ui-components/src/components/Hits.tsx +++ b/packages/instantsearch-ui-components/src/components/Hits.tsx @@ -56,6 +56,7 @@ export type HitsProps = ComponentProps<'div'> & { className: string; onClick: () => void; onAuxClick: () => void; + key?: string | number; }) => JSX.Element; sendEvent: SendEventForHits; classNames?: Partial; diff --git a/packages/instantsearch-ui-components/src/types/Renderer.ts b/packages/instantsearch-ui-components/src/types/Renderer.ts index 27ffef7874..11ad75dd5b 100644 --- a/packages/instantsearch-ui-components/src/types/Renderer.ts +++ b/packages/instantsearch-ui-components/src/types/Renderer.ts @@ -1,11 +1,13 @@ /* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-empty-interface */ +import type { JSX as ReactJSX } from 'react'; + // Ensure that the JSX namespace is available in this file and its dependents. declare global { namespace JSX { - interface Element {} - interface IntrinsicElements {} + interface Element extends ReactJSX.Element {} + interface IntrinsicElements extends ReactJSX.IntrinsicElements {} } } diff --git a/packages/react-instantsearch-core/package.json b/packages/react-instantsearch-core/package.json index 400a60aa45..55645fea69 100644 --- a/packages/react-instantsearch-core/package.json +++ b/packages/react-instantsearch-core/package.json @@ -59,6 +59,6 @@ }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6", - "react": ">= 16.8.0 < 19" + "react": ">= 16.8.0 < 20" } } diff --git a/packages/react-instantsearch-core/src/components/DynamicWidgets.tsx b/packages/react-instantsearch-core/src/components/DynamicWidgets.tsx index 666def0d9b..f49a5a5c31 100644 --- a/packages/react-instantsearch-core/src/components/DynamicWidgets.tsx +++ b/packages/react-instantsearch-core/src/components/DynamicWidgets.tsx @@ -66,7 +66,9 @@ export function DynamicWidgets({ ); } -function isReactElement(element: any): element is ReactElement { +function isReactElement( + element: any +): element is ReactElement> { return typeof element === 'object' && element.props; } diff --git a/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx b/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx index 7217852220..18ba8bf051 100644 --- a/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx +++ b/packages/react-instantsearch-core/src/components/__tests__/InstantSearch.test.tsx @@ -605,7 +605,7 @@ describe('InstantSearch', () => { }), }, ]); - expect(warn).toHaveBeenCalledTimes(4); + expect(warn).toHaveBeenCalledTimes(3); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useBreadcrumb.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useBreadcrumb.test.tsx index b6aa74c669..4a3e1e6a56 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useBreadcrumb.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useBreadcrumb.test.tsx @@ -1,10 +1,14 @@ +/** + * @jest-environment jsdom + */ + import { createSearchClient, createMultiSearchResponse, createSingleSearchResponse, } from '@instantsearch/mocks'; import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import React from 'react'; import { useBreadcrumb } from '../useBreadcrumb'; @@ -16,7 +20,7 @@ import type { MockSearchClient } from '@instantsearch/mocks'; describe('useBreadcrumb', () => { it('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useBreadcrumb({ attributes: [ @@ -38,14 +42,14 @@ describe('useBreadcrumb', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - createURL: expect.any(Function), - items: [], - refine: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + createURL: expect.any(Function), + items: [], + refine: expect.any(Function), + }); }); }); @@ -98,7 +102,7 @@ describe('useBreadcrumb', () => { ) as MockSearchClient['search'], }), }); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useBreadcrumb({ attributes: [ @@ -134,28 +138,28 @@ describe('useBreadcrumb', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: true, - createURL: expect.any(Function), - items: [ - { - label: 'Appliances', - value: 'Appliances > Small Kitchen Appliances', - }, - { - label: 'Small Kitchen Appliances', - value: - 'Appliances > Small Kitchen Appliances > Coffee, Tea & Espresso', - }, - { - label: 'Coffee, Tea & Espresso', - value: null, - }, - ], - refine: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: true, + createURL: expect.any(Function), + items: [ + { + label: 'Appliances', + value: 'Appliances > Small Kitchen Appliances', + }, + { + label: 'Small Kitchen Appliances', + value: + 'Appliances > Small Kitchen Appliances > Coffee, Tea & Espresso', + }, + { + label: 'Coffee, Tea & Espresso', + value: null, + }, + ], + refine: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useClearRefinements.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useClearRefinements.test.tsx index 7c7f93fb3c..e958f8029a 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useClearRefinements.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useClearRefinements.test.tsx @@ -1,5 +1,9 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import React from 'react'; import { useClearRefinements } from '../useClearRefinements'; @@ -10,12 +14,9 @@ import type { UseRefinementListProps } from '../useRefinementList'; describe('useClearRefinements', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => useClearRefinements(), - { - wrapper, - } - ); + const { result } = renderHook(() => useClearRefinements(), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -25,14 +26,14 @@ describe('useClearRefinements', () => { createURL: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - hasRefinements: false, - canRefine: false, - refine: expect.any(Function), - createURL: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + hasRefinements: false, + canRefine: false, + refine: expect.any(Function), + createURL: expect.any(Function), + }); }); }); @@ -47,20 +48,17 @@ describe('useClearRefinements', () => { }, }); - const { result, waitForNextUpdate } = renderHook( - () => useClearRefinements(), - { - wrapper: ({ children }: { children: React.ReactNode }) => - wrapper({ - children: ( - <> - - {children} - - ), - }), - } - ); + const { result } = renderHook(() => useClearRefinements(), { + wrapper: ({ children }: { children: React.ReactNode }) => + wrapper({ + children: ( + <> + + {children} + + ), + }), + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -70,14 +68,14 @@ describe('useClearRefinements', () => { createURL: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - hasRefinements: true, - canRefine: true, - refine: expect.any(Function), - createURL: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + hasRefinements: true, + canRefine: true, + refine: expect.any(Function), + createURL: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useConfigure.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useConfigure.test.tsx index 62ad89fa35..28ed00cab8 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useConfigure.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useConfigure.test.tsx @@ -1,28 +1,29 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useConfigure } from '../useConfigure'; describe('useConfigure', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => useConfigure({ hitsPerPage: 40 }), - { - wrapper, - } - ); + const { result } = renderHook(() => useConfigure({ hitsPerPage: 40 }), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ refine: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - refine: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + refine: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useCurrentRefinements.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useCurrentRefinements.test.tsx index b85d88b53c..1b70edb84f 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useCurrentRefinements.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useCurrentRefinements.test.tsx @@ -1,5 +1,9 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import React from 'react'; import { useCurrentRefinements } from '../useCurrentRefinements'; @@ -10,12 +14,9 @@ import type { UseRefinementListProps } from '../useRefinementList'; describe('useCurrentRefinements', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => useCurrentRefinements(), - { - wrapper, - } - ); + const { result } = renderHook(() => useCurrentRefinements(), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -25,14 +26,14 @@ describe('useCurrentRefinements', () => { createURL: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - items: [], - canRefine: false, - refine: expect.any(Function), - createURL: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + items: [], + canRefine: false, + refine: expect.any(Function), + createURL: expect.any(Function), + }); }); }); @@ -47,20 +48,17 @@ describe('useCurrentRefinements', () => { }, }); - const { result, waitForNextUpdate } = renderHook( - () => useCurrentRefinements(), - { - wrapper: ({ children }: { children: React.ReactNode }) => - wrapper({ - children: ( - <> - - {children} - - ), - }), - } - ); + const { result } = renderHook(() => useCurrentRefinements(), { + wrapper: ({ children }: { children: React.ReactNode }) => + wrapper({ + children: ( + <> + + {children} + + ), + }), + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -86,30 +84,30 @@ describe('useCurrentRefinements', () => { createURL: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - items: [ - { - attribute: 'brand', - indexName: 'indexName', - indexId: 'indexName', - label: 'brand', - refine: expect.any(Function), - refinements: [ - { - attribute: 'brand', - label: 'Apple', - type: 'disjunctive', - value: 'Apple', - }, - ], - }, - ], - canRefine: true, - refine: expect.any(Function), - createURL: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + items: [ + { + attribute: 'brand', + indexName: 'indexName', + indexId: 'indexName', + label: 'brand', + refine: expect.any(Function), + refinements: [ + { + attribute: 'brand', + label: 'Apple', + type: 'disjunctive', + value: 'Apple', + }, + ], + }, + ], + canRefine: true, + refine: expect.any(Function), + createURL: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useDynamicWidgets.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useDynamicWidgets.test.tsx index ccad5003d1..b75cfc6fbe 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useDynamicWidgets.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useDynamicWidgets.test.tsx @@ -1,3 +1,7 @@ +/** + * @jest-environment jsdom + */ + import { createMultiSearchResponse, createSearchClient, @@ -5,7 +9,7 @@ import { defaultRenderingContent, } from '@instantsearch/mocks'; import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useDynamicWidgets } from '../useDynamicWidgets'; @@ -26,27 +30,24 @@ describe('useDynamicWidgets', () => { }), }), }); - const { result, waitForNextUpdate } = renderHook( - () => useDynamicWidgets(), - { - wrapper, - } - ); + const { result } = renderHook(() => useDynamicWidgets(), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ attributesToRender: [], }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - attributesToRender: [ - 'brand', - 'hierarchicalCategories.lvl0', - 'categories', - ], + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + attributesToRender: [ + 'brand', + 'hierarchicalCategories.lvl0', + 'categories', + ], + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useGeoSearch.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useGeoSearch.test.tsx index bde145776e..947c3a9a72 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useGeoSearch.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useGeoSearch.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useGeoSearch } from '../useGeoSearch'; describe('useGeoSearch', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useGeoSearch(), { + const { result } = renderHook(() => useGeoSearch(), { wrapper, }); @@ -25,21 +29,21 @@ describe('useGeoSearch', () => { toggleRefineOnMapMove: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - clearMapRefinement: expect.any(Function), - currentRefinement: undefined, - hasMapMoveSinceLastRefine: expect.any(Function), - isRefinedWithMap: expect.any(Function), - isRefineOnMapMove: expect.any(Function), - items: [], - position: undefined, - refine: expect.any(Function), - sendEvent: expect.any(Function), - setMapMoveSinceLastRefine: expect.any(Function), - toggleRefineOnMapMove: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + clearMapRefinement: expect.any(Function), + currentRefinement: undefined, + hasMapMoveSinceLastRefine: expect.any(Function), + isRefinedWithMap: expect.any(Function), + isRefineOnMapMove: expect.any(Function), + items: [], + position: undefined, + refine: expect.any(Function), + sendEvent: expect.any(Function), + setMapMoveSinceLastRefine: expect.any(Function), + toggleRefineOnMapMove: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useHierarchicalMenu.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useHierarchicalMenu.test.tsx index 997c6f787e..93ac20bb59 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useHierarchicalMenu.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useHierarchicalMenu.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useHierarchicalMenu } from '../useHierarchicalMenu'; describe('useHierarchicalMenu', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useHierarchicalMenu({ attributes: ['attribute'] }), { wrapper, @@ -25,18 +29,18 @@ describe('useHierarchicalMenu', () => { toggleShowMore: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - canToggleShowMore: false, - createURL: expect.any(Function), - isShowingMore: false, - items: [], - refine: expect.any(Function), - sendEvent: expect.any(Function), - toggleShowMore: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + canToggleShowMore: false, + createURL: expect.any(Function), + isShowingMore: false, + items: [], + refine: expect.any(Function), + sendEvent: expect.any(Function), + toggleShowMore: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useHits.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useHits.test.tsx index cab803ea45..0d6a5165fe 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useHits.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useHits.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useHits } from '../useHits'; describe('useHits', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useHits(), { + const { result } = renderHook(() => useHits(), { wrapper, }); @@ -20,16 +24,16 @@ describe('useHits', () => { sendEvent: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - banner: undefined, - bindEvent: expect.any(Function), - hits: [], - items: [], - results: expect.objectContaining({ nbHits: 0 }), - sendEvent: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + banner: undefined, + bindEvent: expect.any(Function), + hits: [], + items: [], + results: expect.objectContaining({ nbHits: 0 }), + sendEvent: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useHitsPerPage.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useHitsPerPage.test.tsx index bf16f57ebe..242fe21dfb 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useHitsPerPage.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useHitsPerPage.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useHitsPerPage } from '../useHitsPerPage'; describe('useHitsPerPage', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useHitsPerPage({ items: [ @@ -40,27 +44,27 @@ describe('useHitsPerPage', () => { ], }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - refine: expect.any(Function), - createURL: expect.any(Function), - hasNoResults: true, - canRefine: false, - items: [ - { - default: true, - isRefined: true, - label: '4 hits per page', - value: 4, - }, - { - isRefined: false, - label: '8 hits per page', - value: 8, - }, - ], + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + refine: expect.any(Function), + createURL: expect.any(Function), + hasNoResults: true, + canRefine: false, + items: [ + { + default: true, + isRefined: true, + label: '4 hits per page', + value: 4, + }, + { + isRefined: false, + label: '8 hits per page', + value: 8, + }, + ], + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useInfiniteHits.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useInfiniteHits.test.tsx index ebc8275501..b34ccd8702 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useInfiniteHits.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useInfiniteHits.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useInfiniteHits } from '../useInfiniteHits'; describe('useInfiniteHits', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useInfiniteHits(), { + const { result } = renderHook(() => useInfiniteHits(), { wrapper, }); @@ -24,20 +28,20 @@ describe('useInfiniteHits', () => { showPrevious: undefined, }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - bindEvent: expect.any(Function), - hits: [], - items: [], - results: expect.objectContaining({ nbHits: 0 }), - sendEvent: expect.any(Function), - currentPageHits: [], - isFirstPage: true, - isLastPage: true, - showMore: expect.any(Function), - showPrevious: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + bindEvent: expect.any(Function), + hits: [], + items: [], + results: expect.objectContaining({ nbHits: 0 }), + sendEvent: expect.any(Function), + currentPageHits: [], + isFirstPage: true, + isLastPage: true, + showMore: expect.any(Function), + showPrevious: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useMenu.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useMenu.test.tsx index 3ab9038f05..34d16ef0dd 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useMenu.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useMenu.test.tsx @@ -1,17 +1,18 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useMenu } from '../useMenu'; describe('useMenu', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => useMenu({ attribute: 'attribute' }), - { - wrapper, - } - ); + const { result } = renderHook(() => useMenu({ attribute: 'attribute' }), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -25,18 +26,18 @@ describe('useMenu', () => { toggleShowMore: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - canToggleShowMore: false, - createURL: expect.any(Function), - isShowingMore: false, - items: [], - refine: expect.any(Function), - sendEvent: expect.any(Function), - toggleShowMore: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + canToggleShowMore: false, + createURL: expect.any(Function), + isShowingMore: false, + items: [], + refine: expect.any(Function), + sendEvent: expect.any(Function), + toggleShowMore: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useNumericMenu.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useNumericMenu.test.tsx index 2ee933954c..46c6a266ca 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useNumericMenu.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useNumericMenu.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useNumericMenu } from '../useNumericMenu'; describe('useNumericMenu', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useNumericMenu({ attribute: 'attribute', @@ -53,37 +57,37 @@ describe('useNumericMenu', () => { sendEvent: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - createURL: expect.any(Function), - hasNoResults: true, - canRefine: false, - items: [ - { - isRefined: true, - label: 'All', - value: '%7B%7D', - }, - { - isRefined: false, - label: 'Less than 500$', - value: '%7B%22end%22:500%7D', - }, - { - isRefined: false, - label: 'Between 500$ - 1000$', - value: '%7B%22start%22:500,%22end%22:1000%7D', - }, - { - isRefined: false, - label: 'More than 1000$', - value: '%7B%22start%22:1000%7D', - }, - ], - refine: expect.any(Function), - sendEvent: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + createURL: expect.any(Function), + hasNoResults: true, + canRefine: false, + items: [ + { + isRefined: true, + label: 'All', + value: '%7B%7D', + }, + { + isRefined: false, + label: 'Less than 500$', + value: '%7B%22end%22:500%7D', + }, + { + isRefined: false, + label: 'Between 500$ - 1000$', + value: '%7B%22start%22:500,%22end%22:1000%7D', + }, + { + isRefined: false, + label: 'More than 1000$', + value: '%7B%22start%22:1000%7D', + }, + ], + refine: expect.any(Function), + sendEvent: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/usePagination.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/usePagination.test.tsx index 8dbad37533..15597446d5 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/usePagination.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/usePagination.test.tsx @@ -1,17 +1,18 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { usePagination } from '../usePagination'; describe('usePagination', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => usePagination({ padding: 2 }), - { - wrapper, - } - ); + const { result } = renderHook(() => usePagination({ padding: 2 }), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -26,19 +27,19 @@ describe('usePagination', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - createURL: expect.any(Function), - currentRefinement: 0, - isFirstPage: true, - isLastPage: true, - nbHits: 0, - nbPages: 0, - pages: [0], - refine: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + createURL: expect.any(Function), + currentRefinement: 0, + isFirstPage: true, + isLastPage: true, + nbHits: 0, + nbPages: 0, + pages: [0], + refine: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useQueryRules.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useQueryRules.test.tsx index bfa10057e5..5f9778ec2d 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useQueryRules.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useQueryRules.test.tsx @@ -1,3 +1,7 @@ +/** + * @jest-environment jsdom + */ + import { createSearchClient, createMultiSearchResponse, @@ -5,7 +9,7 @@ import { defaultUserData, } from '@instantsearch/mocks'; import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useQueryRules } from '../useQueryRules'; @@ -46,7 +50,7 @@ describe('useQueryRules', () => { title: `${item.title} (transformed)`, })); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useQueryRules({ trackedFilters, @@ -63,17 +67,17 @@ describe('useQueryRules', () => { items: [], }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - items: [ - { - title: 'Banner title (transformed)', - banner: 'https://banner.jpg', - link: 'https://banner.com/link/', - }, - ], + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + items: [ + { + title: 'Banner title (transformed)', + banner: 'https://banner.jpg', + link: 'https://banner.com/link/', + }, + ], + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useRange.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useRange.test.tsx index cf6bdb8d2a..97783c081c 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useRange.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useRange.test.tsx @@ -1,17 +1,18 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useRange } from '../useRange'; describe('useRange', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => useRange({ attribute: 'attribute' }), - { - wrapper, - } - ); + const { result } = renderHook(() => useRange({ attribute: 'attribute' }), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ @@ -29,22 +30,22 @@ describe('useRange', () => { start: [-Infinity, Infinity], }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - format: { - from: expect.any(Function), - to: expect.any(Function), - }, - range: { - max: 0, - min: 0, - }, - refine: expect.any(Function), - sendEvent: expect.any(Function), - start: [-Infinity, Infinity], + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + format: { + from: expect.any(Function), + to: expect.any(Function), + }, + range: { + max: 0, + min: 0, + }, + refine: expect.any(Function), + sendEvent: expect.any(Function), + start: [-Infinity, Infinity], + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useRefinementList.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useRefinementList.test.tsx index fc79c78dc7..854554b621 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useRefinementList.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useRefinementList.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useRefinementList } from '../useRefinementList'; describe('useRefinementList', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useRefinementList({ attribute: 'attribute' }), { wrapper, @@ -28,21 +32,21 @@ describe('useRefinementList', () => { toggleShowMore: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - canToggleShowMore: false, - createURL: expect.any(Function), - hasExhaustiveItems: true, - isFromSearch: false, - isShowingMore: false, - items: [], - refine: expect.any(Function), - searchForItems: expect.any(Function), - sendEvent: expect.any(Function), - toggleShowMore: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + canToggleShowMore: false, + createURL: expect.any(Function), + hasExhaustiveItems: true, + isFromSearch: false, + isShowingMore: false, + items: [], + refine: expect.any(Function), + searchForItems: expect.any(Function), + sendEvent: expect.any(Function), + toggleShowMore: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useRelatedProducts.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useRelatedProducts.test.tsx index 9439da21e7..55868e7ae4 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useRelatedProducts.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useRelatedProducts.test.tsx @@ -1,6 +1,10 @@ +/** + * @jest-environment jsdom + */ + import { createRecommendSearchClient } from '@instantsearch/mocks/fixtures'; import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useRelatedProducts } from '../useRelatedProducts'; @@ -9,7 +13,7 @@ describe('useRelatedProducts', () => { const wrapper = createInstantSearchTestWrapper({ searchClient: createRecommendSearchClient({ minimal: true }), }); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useRelatedProducts({ objectIDs: ['1'] }), { wrapper, @@ -19,11 +23,11 @@ describe('useRelatedProducts', () => { // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ items: [] }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - items: expect.arrayContaining([{ objectID: '1' }, { objectID: '2' }]), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + items: expect.arrayContaining([{ objectID: '1' }, { objectID: '2' }]), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useSearchBox.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useSearchBox.test.tsx index fd730513d5..d92cd5574c 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useSearchBox.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useSearchBox.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useSearchBox } from '../useSearchBox'; describe('useSearchBox', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useSearchBox(), { + const { result } = renderHook(() => useSearchBox(), { wrapper, }); @@ -18,14 +22,14 @@ describe('useSearchBox', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - query: '', - isSearchStalled: false, - clear: expect.any(Function), - refine: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + query: '', + isSearchStalled: false, + clear: expect.any(Function), + refine: expect.any(Function), + }); }); }); @@ -37,7 +41,7 @@ describe('useSearchBox', () => { }, }, }); - const { result, waitForNextUpdate } = renderHook(() => useSearchBox(), { + const { result } = renderHook(() => useSearchBox(), { wrapper, }); @@ -49,14 +53,14 @@ describe('useSearchBox', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - query: 'testio', - isSearchStalled: false, - clear: expect.any(Function), - refine: expect.any(Function), + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + query: 'testio', + isSearchStalled: false, + clear: expect.any(Function), + refine: expect.any(Function), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useSortBy.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useSortBy.test.tsx index 2dec820032..f4981602b5 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useSortBy.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useSortBy.test.tsx @@ -1,5 +1,9 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useSortBy } from '../useSortBy'; @@ -12,10 +16,7 @@ const items = [ describe('useSortBy', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( - () => useSortBy({ items }), - { wrapper } - ); + const { result } = renderHook(() => useSortBy({ items }), { wrapper }); expect(result.current).toEqual({ currentRefinement: 'indexName', @@ -25,14 +26,14 @@ describe('useSortBy', () => { canRefine: expect.any(Boolean), }); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - currentRefinement: 'indexName', - options: items, - refine: expect.any(Function), - hasNoResults: expect.any(Boolean), - canRefine: expect.any(Boolean), + await waitFor(() => { + expect(result.current).toEqual({ + currentRefinement: 'indexName', + options: items, + refine: expect.any(Function), + hasNoResults: expect.any(Boolean), + canRefine: expect.any(Boolean), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useStats.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useStats.test.tsx index f72c55eb95..0ae9d97d20 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useStats.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useStats.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useStats } from '../useStats'; describe('useStats', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useStats(), { + const { result } = renderHook(() => useStats(), { wrapper, }); @@ -22,18 +26,18 @@ describe('useStats', () => { query: '', }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - areHitsSorted: false, - hitsPerPage: 20, - nbHits: 0, - nbPages: 0, - nbSortedHits: undefined, - page: 0, - processingTimeMS: 0, - query: '', + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + areHitsSorted: false, + hitsPerPage: 20, + nbHits: 0, + nbPages: 0, + nbSortedHits: undefined, + page: 0, + processingTimeMS: 0, + query: '', + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useToggleRefinement.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useToggleRefinement.test.tsx index 55bfa375cd..df12251083 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useToggleRefinement.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useToggleRefinement.test.tsx @@ -1,12 +1,16 @@ +/** + * @jest-environment jsdom + */ + import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useToggleRefinement } from '../useToggleRefinement'; describe('useToggleRefinement', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useToggleRefinement({ attribute: 'test' }), { wrapper, @@ -34,27 +38,27 @@ describe('useToggleRefinement', () => { }, }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - canRefine: false, - createURL: expect.any(Function), - refine: expect.any(Function), - sendEvent: expect.any(Function), - value: { - count: null, - isRefined: false, - name: 'test', - offFacetValue: { - count: 0, - isRefined: false, - }, - onFacetValue: { + await waitFor(() => { + // InstantSearch.js state from the `render` lifecycle step + expect(result.current).toEqual({ + canRefine: false, + createURL: expect.any(Function), + refine: expect.any(Function), + sendEvent: expect.any(Function), + value: { count: null, isRefined: false, + name: 'test', + offFacetValue: { + count: 0, + isRefined: false, + }, + onFacetValue: { + count: null, + isRefined: false, + }, }, - }, + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/useTrendingItems.test.tsx b/packages/react-instantsearch-core/src/connectors/__tests__/useTrendingItems.test.tsx index 7a53e1bcee..35972165c0 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/useTrendingItems.test.tsx +++ b/packages/react-instantsearch-core/src/connectors/__tests__/useTrendingItems.test.tsx @@ -1,6 +1,10 @@ +/** + * @jest-environment jsdom + */ + import { createRecommendSearchClient } from '@instantsearch/mocks/fixtures'; import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { useTrendingItems } from '../useTrendingItems'; @@ -9,21 +13,17 @@ describe('useTrendingItems', () => { const wrapper = createInstantSearchTestWrapper({ searchClient: createRecommendSearchClient({ minimal: true }), }); - const { result, waitForNextUpdate } = renderHook( - () => useTrendingItems({}), - { - wrapper, - } - ); + const { result } = renderHook(() => useTrendingItems({}), { + wrapper, + }); // Initial render state from manual `getWidgetRenderState` expect(result.current).toEqual({ items: [] }); - await waitForNextUpdate(); - - // InstantSearch.js state from the `render` lifecycle step - expect(result.current).toEqual({ - items: expect.arrayContaining([{ objectID: '1' }, { objectID: '2' }]), + await waitFor(() => { + expect(result.current).toEqual({ + items: expect.arrayContaining([{ objectID: '1' }, { objectID: '2' }]), + }); }); }); }); diff --git a/packages/react-instantsearch-core/src/hooks/__tests__/useConnector.test.tsx b/packages/react-instantsearch-core/src/hooks/__tests__/useConnector.test.tsx index 9c0463dc36..36dc9b7a3c 100644 --- a/packages/react-instantsearch-core/src/hooks/__tests__/useConnector.test.tsx +++ b/packages/react-instantsearch-core/src/hooks/__tests__/useConnector.test.tsx @@ -10,8 +10,7 @@ import { createInstantSearchTestWrapper, createInstantSearchSpy, } from '@instantsearch/testutils'; -import { render, waitFor } from '@testing-library/react'; -import { renderHook } from '@testing-library/react-hooks'; +import { render, waitFor, renderHook } from '@testing-library/react'; import { SearchParameters, SearchResults } from 'algoliasearch-helper'; import connectHits from 'instantsearch.js/es/connectors/hits/connectHits'; import React, { StrictMode, useState } from 'react'; @@ -244,7 +243,7 @@ describe('useConnector', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useConnector(connectCustomSearchBox, {}, {}), { wrapper } ); @@ -255,18 +254,18 @@ describe('useConnector', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // It should never be "query at init" because we skip the `init` step. - expect(result.current).not.toEqual({ - query: 'query at init', - refine: expect.any(Function), - }); - - // Render state provided by InstantSearch Core during `render`. - expect(result.current).toEqual({ - query: 'query', - refine: expect.any(Function), + await waitFor(() => { + // It should never be "query at init" because we skip the `init` step. + expect(result.current).not.toEqual({ + query: 'query at init', + refine: expect.any(Function), + }); + + // Render state provided by InstantSearch Core during `render`. + expect(result.current).toEqual({ + query: 'query', + refine: expect.any(Function), + }); }); }); @@ -281,7 +280,7 @@ describe('useConnector', () => { ); } - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useConnector(connectCustomSearchBox, {}, {}), { wrapper: Wrapper } ); @@ -292,41 +291,40 @@ describe('useConnector', () => { refine: expect.any(Function), }); - await waitForNextUpdate(); - - // It should never be "query at init" because we skip the `init` step. - expect(result.current).not.toEqual({ - query: 'query at init', - refine: expect.any(Function), - }); - - // Render state provided by InstantSearch Core during `render`. - expect(result.current).toEqual({ - query: 'query', - refine: expect.any(Function), + await waitFor(() => { + // It should never be "query at init" because we skip the `init` step. + expect(result.current).not.toEqual({ + query: 'query at init', + refine: expect.any(Function), + }); + + // Render state provided by InstantSearch Core during `render`. + expect(result.current).toEqual({ + query: 'query', + refine: expect.any(Function), + }); }); }); - test('returns empty connector initial render state without getWidgetRenderState', async () => { + test('returns empty connector initial render state without getWidgetRenderState', () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook( + const { result } = renderHook( () => useConnector(connectCustomSearchBoxWithoutRenderState, {}, {}), { wrapper } ); expect(result.current).toEqual({}); - - await waitForNextUpdate(); }); test('calls getWidgetRenderState with the InstantSearch render options and artificial results', () => { - const getWidgetRenderState = jest.fn(); + const getWidgetRenderState = jest.fn(() => ({})); const connectCustomSearchBoxMock: Connector< CustomSearchBoxWidgetDescription, - Record + Record > = (renderFn, unmountFn) => (widgetParams) => ({ ...connectCustomSearchBox(renderFn, unmountFn)(widgetParams), + // @ts-expect-error getWidgetRenderState, }); const searchClient = createSearchClient({}); @@ -367,7 +365,7 @@ describe('useConnector', () => { tagRefinements: [], }; - expect(getWidgetRenderState).toHaveBeenCalledTimes(1); + expect(getWidgetRenderState).toHaveBeenCalledTimes(2); expect(getWidgetRenderState).toHaveBeenCalledWith({ helper: expect.objectContaining({ state: helperState, @@ -399,7 +397,7 @@ describe('useConnector', () => { test('calls getWidgetRenderState with recommend results if available', () => { const result = createSingleSearchResponse(); - const getWidgetRenderState = jest.fn(); + const getWidgetRenderState = jest.fn(() => ({})); const searchClient = createSearchClient({}); const { InstantSearchSpy } = createInstantSearchSpy(); @@ -428,8 +426,12 @@ describe('useConnector', () => { () => () => ({ $$type: '', dependsOn: 'recommend', + init: jest.fn(), + render: jest.fn(), + dispose: jest.fn(), getWidgetParameters: jest.fn(), getRenderState: jest.fn(), + // @ts-expect-error getWidgetRenderState, }), {}, diff --git a/packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx b/packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx index eb129b4cd1..c3b4a58277 100644 --- a/packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx +++ b/packages/react-instantsearch-core/src/hooks/__tests__/useInstantSearch.test.tsx @@ -8,8 +8,7 @@ import { InstantSearchTestWrapper, wait, } from '@instantsearch/testutils'; -import { render, waitFor } from '@testing-library/react'; -import { renderHook } from '@testing-library/react-hooks'; +import { render, waitFor, renderHook } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { AlgoliaSearchHelper, SearchResults } from 'algoliasearch-helper'; import React, { useEffect } from 'react'; @@ -24,8 +23,8 @@ describe('useInstantSearch', () => { test('it errors when not nested in InstantSearch', () => { const { result } = renderHook(() => useInstantSearch()); - expect(result.error).toBeInstanceOf(Error); - expect(result.error?.message).toMatchInlineSnapshot(` + expect(result.current.error).toBeInstanceOf(Error); + expect(result.current.error?.message).toMatchInlineSnapshot(` "[InstantSearch] Hooks must be used inside the component. They are not compatible with the \`react-instantsearch-core@6.x\` and \`react-instantsearch-dom\` packages, so make sure to use the component from \`react-instantsearch-core@7.x\`." diff --git a/packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx index 2b758827d3..d3aec932f7 100644 --- a/packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx +++ b/packages/react-instantsearch-core/src/lib/__tests__/useSearchResults.test.tsx @@ -1,9 +1,13 @@ +/** + * @jest-environment jsdom + */ + import { createMultiSearchResponse, createSearchClient, } from '@instantsearch/mocks'; import { createInstantSearchTestWrapper } from '@instantsearch/testutils'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, waitFor } from '@testing-library/react'; import { AlgoliaSearchHelper, SearchResults } from 'algoliasearch-helper'; import React from 'react'; import { Index, SearchBox } from 'react-instantsearch'; @@ -13,7 +17,7 @@ import { useSearchResults } from '../useSearchResults'; describe('useSearchResults', () => { test('returns the connector render state', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useSearchResults(), { + const { result } = renderHook(() => useSearchResults(), { wrapper: ({ children }: { children: React.ReactNode }) => wrapper({ children: ( @@ -38,25 +42,25 @@ describe('useSearchResults', () => { }); expect(result.current.results.__isArtificial).toEqual(true); - await waitForNextUpdate(); - - // Update caused by - expect(result.current).toEqual({ - results: expect.any(SearchResults), - scopedResults: [ - expect.objectContaining({ - helper: expect.any(AlgoliaSearchHelper), - indexId: 'indexName', - results: expect.any(SearchResults), - }), - ], + await waitFor(() => { + // Update caused by + expect(result.current).toEqual({ + results: expect.any(SearchResults), + scopedResults: [ + expect.objectContaining({ + helper: expect.any(AlgoliaSearchHelper), + indexId: 'indexName', + results: expect.any(SearchResults), + }), + ], + }); + expect(result.current.results.__isArtificial).toBeUndefined(); }); - expect(result.current.results.__isArtificial).toBeUndefined(); }); test('returns scoped results', async () => { const wrapper = createInstantSearchTestWrapper(); - const { result, waitForNextUpdate } = renderHook(() => useSearchResults(), { + const { result } = renderHook(() => useSearchResults(), { wrapper: ({ children }: { children: React.ReactNode }) => wrapper({ children: ( @@ -70,21 +74,19 @@ describe('useSearchResults', () => { }), }); - await waitForNextUpdate(); - - expect(result.current.scopedResults).toHaveLength(3); - expect(result.current.scopedResults.map(({ indexId }) => indexId)).toEqual([ - 'indexName', - 'indexName1', - 'indexName2', - ]); + await waitFor(() => { + expect(result.current.scopedResults).toHaveLength(3); + expect( + result.current.scopedResults.map(({ indexId }) => indexId) + ).toEqual(['indexName', 'indexName1', 'indexName2']); + }); }); test('returns scoped results when the main index has no indexName set', async () => { const wrapper = createInstantSearchTestWrapper({ indexName: undefined, }); - const { result, waitForNextUpdate } = renderHook(() => useSearchResults(), { + const { result } = renderHook(() => useSearchResults(), { wrapper: ({ children }: { children: React.ReactNode }) => wrapper({ children: ( @@ -98,13 +100,12 @@ describe('useSearchResults', () => { }), }); - await waitForNextUpdate(); - - expect(result.current.scopedResults).toHaveLength(2); - expect(result.current.scopedResults.map(({ indexId }) => indexId)).toEqual([ - 'indexName1', - 'indexName2', - ]); + await waitFor(() => { + expect(result.current.scopedResults).toHaveLength(2); + expect( + result.current.scopedResults.map(({ indexId }) => indexId) + ).toEqual(['indexName1', 'indexName2']); + }); }); test('does not return `null` results when the first search is stalled', async () => { @@ -119,7 +120,7 @@ describe('useSearchResults', () => { }, }), }); - const { result, waitForNextUpdate } = renderHook(() => useSearchResults(), { + const { result } = renderHook(() => useSearchResults(), { wrapper: ({ children }: { children: React.ReactNode }) => wrapper({ children: ( @@ -131,9 +132,9 @@ describe('useSearchResults', () => { }), }); - await waitForNextUpdate(); - - // Update caused by - expect(result.current.results).not.toBeNull(); + await waitFor(() => { + // Update caused by + expect(result.current.results).not.toBeNull(); + }); }); }); diff --git a/packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx b/packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx index 894ac8bca3..23f7ea7556 100644 --- a/packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx +++ b/packages/react-instantsearch-core/src/lib/__tests__/useSearchState.test.tsx @@ -6,8 +6,7 @@ import { createInstantSearchTestWrapper, InstantSearchTestWrapper, } from '@instantsearch/testutils'; -import { render, waitFor } from '@testing-library/react'; -import { renderHook } from '@testing-library/react-hooks'; +import { render, waitFor, renderHook } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; diff --git a/packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx b/packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx index e98cdf0394..a7751c1fdd 100644 --- a/packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx +++ b/packages/react-instantsearch-core/src/server/__tests__/getServerState.test.tsx @@ -548,9 +548,9 @@ describe('getServerState', () => {