diff --git a/apps/web/test/setupTests.ts b/apps/web/test/setupTests.ts index f514c10482..bcfede9805 100644 --- a/apps/web/test/setupTests.ts +++ b/apps/web/test/setupTests.ts @@ -22,6 +22,43 @@ declare global { globalThis.IS_REACT_ACT_ENVIRONMENT = true; +const REACT_USE_ID = /_r_[a-z0-9]+_/g; + +function normaliseReactUseIds(snapshot: string): string { + // React useId values can vary between runs and make snapshots flaky: + // https://github.com/element-hq/element-web/issues/31765 + // Avoid running the regex for DOM snapshots without React useId output. + if (!snapshot.includes("_r_")) return snapshot; + + const ids = new Map(); + let nextId = 1; + + return snapshot.replace(REACT_USE_ID, (id) => { + let replacement = ids.get(id); + if (!replacement) { + replacement = `react-use-id-${nextId++}`; + ids.set(id, replacement); + } + return replacement; + }); +} + +// Prevent this serializer from recursively matching the same DOM node when it calls serialize(). +let isSerializingDomSnapshot = false; + +expect.addSnapshotSerializer({ + test: (value: unknown): value is Element | DocumentFragment => + !isSerializingDomSnapshot && (value instanceof Element || value instanceof DocumentFragment), + print: (value: unknown, serialize: (value: unknown) => string): string => { + isSerializingDomSnapshot = true; + try { + return normaliseReactUseIds(serialize(value)); + } finally { + isSerializingDomSnapshot = false; + } + }, +}); + // Fake random strings to give a predictable snapshot for IDs jest.mock("matrix-js-sdk/src/randomstring"); beforeEach(() => { diff --git a/apps/web/test/unit-tests/components/structures/__snapshots__/FilePanel-test.tsx.snap b/apps/web/test/unit-tests/components/structures/__snapshots__/FilePanel-test.tsx.snap index 07c557e9d0..6a45b1d723 100644 --- a/apps/web/test/unit-tests/components/structures/__snapshots__/FilePanel-test.tsx.snap +++ b/apps/web/test/unit-tests/components/structures/__snapshots__/FilePanel-test.tsx.snap @@ -19,7 +19,7 @@ exports[`FilePanel renders empty state 1`] = `

@@ -13,7 +13,7 @@ exports[`ReactionsRowButtonTooltip > renders the tooltip with formatted senders exports[`ReactionsRowButtonTooltip > renders the tooltip with many senders 1`] = `
diff --git a/packages/shared-components/src/test/setupTests.ts b/packages/shared-components/src/test/setupTests.ts index 614dd85da3..f21999885c 100644 --- a/packages/shared-components/src/test/setupTests.ts +++ b/packages/shared-components/src/test/setupTests.ts @@ -7,11 +7,48 @@ Please see LICENSE files in the repository root for full details. import fetchMock from "@fetch-mock/vitest"; import { cleanup } from "@test-utils"; -import { afterEach } from "vitest"; +import { afterEach, expect } from "vitest"; import { setLanguage } from "../../src/core/i18n/i18n"; import en from "../i18n/strings/en_EN.json"; +const REACT_USE_ID = /_r_[a-z0-9]+_/g; + +function normaliseReactUseIds(snapshot: string): string { + // React useId values can vary between runs and make snapshots flaky: + // https://github.com/element-hq/element-web/issues/31765 + // Avoid running the regex for DOM snapshots without React useId output. + if (!snapshot.includes("_r_")) return snapshot; + + const ids = new Map(); + let nextId = 1; + + return snapshot.replace(REACT_USE_ID, (id) => { + let replacement = ids.get(id); + if (!replacement) { + replacement = `react-use-id-${nextId++}`; + ids.set(id, replacement); + } + return replacement; + }); +} + +// Prevent this serializer from recursively matching the same DOM node when it calls serialize(). +let isSerializingDomSnapshot = false; + +expect.addSnapshotSerializer({ + test: (value: unknown): value is Element | DocumentFragment => + !isSerializingDomSnapshot && (value instanceof Element || value instanceof DocumentFragment), + print: (value: unknown, serialize: (value: unknown) => string): string => { + isSerializingDomSnapshot = true; + try { + return normaliseReactUseIds(serialize(value)); + } finally { + isSerializingDomSnapshot = false; + } + }, +}); + function setupLanguageMock(): void { fetchMock .get("end:/i18n/languages.json", {