From 834f33c31b2734aa6d2d86c7a7d7e1973353a4c1 Mon Sep 17 00:00:00 2001 From: Priya Raman Date: Sat, 5 Sep 2026 08:07:54 +0000 Subject: [PATCH] test(ui): flush passive effects with React act in CompanySkills tests The local act helper wrapped flushSync, which flushes render work but not passive effects. The install dialog seeds its state in a useEffect, so tests added a setTimeout(0) hop to give it a chance to run. A setTimeout callback and React's own scheduler task queue in a different order each run, so the hop sometimes lost the race and the test read stale state. Delegate the helper to React's own act, which flushes passive effects before it resolves, and remove the now-unneeded timing hops. Co-authored-by: Paperclip --- ui/src/pages/CompanySkills.test.tsx | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/ui/src/pages/CompanySkills.test.tsx b/ui/src/pages/CompanySkills.test.tsx index 11147e97e7..56c365708d 100644 --- a/ui/src/pages/CompanySkills.test.tsx +++ b/ui/src/pages/CompanySkills.test.tsx @@ -1,7 +1,6 @@ // @vitest-environment jsdom -import type { ComponentProps, ReactNode } from "react"; -import { flushSync } from "react-dom"; +import { act as reactAct, type ComponentProps, type ReactNode } from "react"; import { createRoot, type Root } from "react-dom/client"; import type { CatalogSkill, CompanySkillDetail, CompanySkillListItem, CompanySkillVersion, FolderListResult } from "@paperclipai/shared"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -104,11 +103,9 @@ let root: Root | null = null; let container: HTMLDivElement | null = null; async function act(callback: () => void | Promise) { - let result: void | Promise = undefined; - flushSync(() => { - result = callback(); + await reactAct(async () => { + await callback(); }); - await result; } afterEach(() => { @@ -1043,11 +1040,6 @@ describe("install-time agent enablement", () => { />, ); }); - // The dialog seeds its slug/agent state in passive effects; give them a - // macrotask to flush before interacting. - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 0)); - }); const node = container as ParentNode; expect(node.textContent).toContain("Enable for agents"); @@ -1089,15 +1081,9 @@ describe("install-time agent enablement", () => { // Dialog opens before the agents query resolves: nothing to select yet. await act(async () => renderDialog([])); - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 0)); - }); // The agents arrive later; the untouched selection must pick them up. await act(async () => renderDialog(agentOptions)); - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 0)); - }); await click(buttonsNamed(container as ParentNode, "Install skill")[0] as HTMLButtonElement); @@ -1129,9 +1115,6 @@ describe("install-time agent enablement", () => { />, ); }); - await act(async () => { - await new Promise((resolve) => setTimeout(resolve, 0)); - }); const node = container as ParentNode; expect(node.textContent).not.toContain("Enable for agents");