diff --git a/packages/studio/src/components/editor/PropertyPanel.test.ts b/packages/studio/src/components/editor/PropertyPanel.test.ts index cb22bd97a..9b470316e 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 ed2059bfc..445f1838c 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 987bd4ee7..1e12ce41b 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 ece488c96..176eff612 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 ac24d4b37..dec9281e2 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 831c80467..54677f2d3 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 4dbe02cc0..4c52de210 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatTextSection.test.tsx @@ -1,28 +1,14 @@ // @vitest-environment happy-dom -import React, { act, useState } from "react"; -import { createRoot } from "react-dom/client"; +import { act, useState } from "react"; 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 = [ { @@ -365,12 +351,7 @@ describe("FlatTextSection — multi-field", () => { ); } - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(); - }); + const { host, root } = renderInto(); let rows = host.querySelectorAll('[data-flat-text-layer-row="true"]'); expect(rows).toHaveLength(2); @@ -461,12 +442,7 @@ describe("FlatTextSection — multi-field", () => { ); } - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render(); - }); + const { host, root } = renderInto(); const addButton = host.querySelector('[data-flat-text-layer-add="true"]'); // Wait for onAddTextField's promise to resolve (adds field "c" and makes it @@ -489,22 +465,17 @@ describe("FlatTextSection — multi-field", () => { const element = makeMultiFieldElement(); element.textFields[0].value = "First line\nSecond line\nThird line"; - const host = document.createElement("div"); - document.body.append(host); - const root = createRoot(host); - act(() => { - root.render( - , - ); - }); + const { host, root } = renderInto( + , + ); const contentTextarea = host.querySelector("textarea"); expect(contentTextarea?.value).toBe("First line\nSecond line\nThird line"); diff --git a/packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatToggle.test.tsx index e16956937..ee4ff774d 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 000000000..bc0632162 --- /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 }; +}