From fbb83f25af0d0498a71d0578b0dc2629f550b60e Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Fri, 31 Jul 2026 02:02:08 -0700 Subject: [PATCH] refactor(studio): share the flat-inspector test render harness across panel suites The eight flat property-panel suites each carried an identical copy of the React act-environment setup and a local renderInto() helper. Extract both into testRenderUtils so the harness has one definition to fix. No behavior change: setupReactActEnvironment() registers the same document.body cleanup each file registered inline, and renderInto() is the same createRoot + act wrapper. --- .../components/editor/PropertyPanel.test.ts | 4 +-- .../editor/PropertyPanelEmptyState.test.tsx | 22 +++------------- ...pertyPanelFlatColorGradingSection.test.tsx | 22 +++------------- .../propertyPanelFlatLayoutSection.test.tsx | 22 +++------------- .../propertyPanelFlatMotionSection.test.tsx | 17 +++--------- .../propertyPanelFlatPrimitives.test.tsx | 20 +++----------- .../propertyPanelFlatTextSection.test.tsx | 21 +++------------ .../editor/propertyPanelFlatToggle.test.tsx | 22 +++------------- .../src/components/editor/testRenderUtils.tsx | 26 +++++++++++++++++++ 9 files changed, 54 insertions(+), 122 deletions(-) create mode 100644 packages/studio/src/components/editor/testRenderUtils.tsx diff --git a/packages/studio/src/components/editor/PropertyPanel.test.ts b/packages/studio/src/components/editor/PropertyPanel.test.ts index cb22bd97af..9b470316e2 100644 --- a/packages/studio/src/components/editor/PropertyPanel.test.ts +++ b/packages/studio/src/components/editor/PropertyPanel.test.ts @@ -8,11 +8,11 @@ import { getCssFilterFunctionPx, inferBoxShadowPreset, inferClipPathPreset, + isSelectedElementHidden, normalizePanelPxValue, parseInsetClipPathSides, setCssFilterFunctionPx, -} from "./PropertyPanel"; -import { isSelectedElementHidden } from "./propertyPanelHelpers"; +} from "./propertyPanelHelpers"; describe("PropertyPanel style helpers", () => { it("normalizes bounded pixel values without accepting incompatible units", () => { diff --git a/packages/studio/src/components/editor/PropertyPanelEmptyState.test.tsx b/packages/studio/src/components/editor/PropertyPanelEmptyState.test.tsx index 397fb161cf..9804e6727b 100644 --- a/packages/studio/src/components/editor/PropertyPanelEmptyState.test.tsx +++ b/packages/studio/src/components/editor/PropertyPanelEmptyState.test.tsx @@ -1,26 +1,12 @@ // @vitest-environment happy-dom -import React, { act } from "react"; -import { createRoot } from "react-dom/client"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { act } from "react"; +import { describe, expect, it, vi } from "vitest"; import { PropertyPanelEmptyState } from "./PropertyPanelEmptyState"; import type { DomEditSelection } from "./domEditingTypes"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -afterEach(() => { - document.body.innerHTML = ""; -}); - -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} +setupReactActEnvironment(); describe("PropertyPanelEmptyState — flat empty", () => { it("shows the cursor glyph, headline, and the two shortcut rows", () => { diff --git a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx index 987bd4ee78..1e12ce41b9 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx @@ -1,8 +1,7 @@ // @vitest-environment happy-dom -import React, { act } from "react"; -import { createRoot } from "react-dom/client"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { act } from "react"; +import { describe, expect, it, vi } from "vitest"; import { FlatColorGradingAccessory, FlatColorGradingSection, @@ -11,22 +10,9 @@ import { normalizeHfColorGrading, type NormalizedHfColorGrading, } from "@hyperframes/core/color-grading"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -afterEach(() => { - document.body.innerHTML = ""; -}); - -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} +setupReactActEnvironment(); function neutralGrading() { const grading = normalizeHfColorGrading("neutral"); diff --git a/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx index ece488c962..176eff6122 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx @@ -1,8 +1,7 @@ // @vitest-environment happy-dom -import React, { act } from "react"; -import { createRoot } from "react-dom/client"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { act } from "react"; +import { describe, expect, it, vi } from "vitest"; import { FlatLayoutSection, LayoutFlexBlock, @@ -10,22 +9,9 @@ import { LayoutTransform3DBlock, LayoutZIndexRow, } from "./propertyPanelFlatLayoutSection"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -afterEach(() => { - document.body.innerHTML = ""; -}); - -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} +setupReactActEnvironment(); function getFlatRowInput(host: HTMLElement, label: string): HTMLInputElement { const rows = Array.from(host.querySelectorAll(".group")); diff --git a/packages/studio/src/components/editor/propertyPanelFlatMotionSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatMotionSection.test.tsx index c5f1055b13..e07200e6c1 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatMotionSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatMotionSection.test.tsx @@ -1,16 +1,15 @@ // @vitest-environment happy-dom -import React, { act } from "react"; -import { createRoot } from "react-dom/client"; +import { act } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { FlatMotionSection, FlatTimingRow } from "./propertyPanelFlatMotionSection"; import type { DomEditSelection } from "./domEditing"; import { usePlayerStore } from "../../player"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; +setupReactActEnvironment(); afterEach(() => { - document.body.innerHTML = ""; // The store is module-global, so a test that parks a focused ease segment // would otherwise leak it into every test that runs after it. usePlayerStore.getState().reset(); @@ -47,16 +46,6 @@ function baseElement(overrides: Partial = {}): DomEditSelectio } as DomEditSelection; } -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} - describe("FlatTimingRow", () => { it("renders Start, End, and Duration from the element's data attributes", () => { const { host, root } = renderInto( diff --git a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx index 9c6de843a1..afdb1c4c29 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx @@ -1,8 +1,7 @@ // @vitest-environment happy-dom import React, { act } from "react"; -import { createRoot } from "react-dom/client"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { FlatGroupHeader, FlatRow, @@ -10,22 +9,9 @@ import { FlatSelectRow, FlatSlider, } from "./propertyPanelFlatPrimitives"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -afterEach(() => { - document.body.innerHTML = ""; -}); - -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} +setupReactActEnvironment(); describe("FlatRow", () => { it("renders the default tier with no reset button", () => { diff --git a/packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx index 4dbe02cc0f..b2004725d0 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx @@ -1,28 +1,15 @@ // @vitest-environment happy-dom -import React, { act, useState } from "react"; +import { act, useState } from "react"; import { createRoot } from "react-dom/client"; import postcss from "postcss"; import tailwindcss from "tailwindcss"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { FlatTextLayerList, FlatTextSection } from "./propertyPanelFlatTextSection"; import type { DomEditSelection, DomEditTextField } from "./domEditingTypes"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -afterEach(() => { - document.body.innerHTML = ""; -}); - -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} +setupReactActEnvironment(); const FIELDS = [ { diff --git a/packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx index e169569375..ee4ff774d7 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx @@ -1,25 +1,11 @@ // @vitest-environment happy-dom -import React, { act } from "react"; -import { createRoot } from "react-dom/client"; -import { afterEach, describe, expect, it, vi } from "vitest"; +import { act } from "react"; +import { describe, expect, it, vi } from "vitest"; import { FlatToggle } from "./propertyPanelFlatToggle"; +import { renderInto, setupReactActEnvironment } from "./testRenderUtils"; -(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; - -afterEach(() => { - document.body.innerHTML = ""; -}); - -function renderInto(node: React.ReactElement) { - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(node); - }); - return { host, root }; -} +setupReactActEnvironment(); describe("FlatToggle", () => { it("renders the off state with a dim label and dim knob, and fires onChange(true) on click", () => { diff --git a/packages/studio/src/components/editor/testRenderUtils.tsx b/packages/studio/src/components/editor/testRenderUtils.tsx new file mode 100644 index 0000000000..bc06321626 --- /dev/null +++ b/packages/studio/src/components/editor/testRenderUtils.tsx @@ -0,0 +1,26 @@ +import React, { act } from "react"; +import { createRoot } from "react-dom/client"; +import { afterEach } from "vitest"; + +/** + * Marks the current environment as React-act-aware and registers the shared + * per-test DOM cleanup. Call once per test file, at module scope, before any + * `describe`/`it` blocks run. + */ +export function setupReactActEnvironment(): void { + (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + + afterEach(() => { + document.body.innerHTML = ""; + }); +} + +export function renderInto(node: React.ReactElement) { + const host = document.createElement("div"); + document.body.append(host); + const root = createRoot(host); + act(() => { + root.render(node); + }); + return { host, root }; +}