import { act } from "@testing-library/react"; import { useReducedMotion } from "motion/react"; import { hydrateRoot, type Root } from "react-dom/client"; import { renderToString } from "react-dom/server"; import Hero from "./Hero"; jest.mock("motion/react", () => ({ ...jest.requireActual("motion/react"), useReducedMotion: jest.fn(), })); jest.mock("next/dynamic", () => () => () => null); jest.mock("./cinematic/ControlRoomGlobe", () => ({ ControlRoomGlobe: () => null, })); jest.mock("./cinematic/HeroStarfield", () => ({ HeroStarfield: () => null, })); afterEach(() => { jest.restoreAllMocks(); }); it.each([false, true])( "hydrates without attribute mismatches when reduced motion resolves to %s", async (reducedMotion) => { jest.spyOn(window, "matchMedia").mockImplementation((media) => ({ media, matches: media === "(prefers-reduced-motion: reduce)" && reducedMotion, onchange: null, addListener: jest.fn(), removeListener: jest.fn(), addEventListener: jest.fn(), removeEventListener: jest.fn(), dispatchEvent: jest.fn(), })); const preference = jest.mocked(useReducedMotion); preference.mockReturnValue(null); const container = document.createElement("div"); container.innerHTML = renderToString(); document.body.append(container); preference.mockReturnValue(reducedMotion); const consoleErrors = jest.spyOn(console, "error").mockImplementation(); const recoverableErrors: unknown[] = []; let root: Root | undefined; try { await act(async () => { root = hydrateRoot(container, , { onRecoverableError: (error) => recoverableErrors.push(error), }); }); expect(recoverableErrors).toEqual([]); expect(consoleErrors.mock.calls).toEqual([]); } finally { await act(async () => root?.unmount()); container.remove(); } }, );