diff --git a/ui/src/App.test.tsx b/ui/src/App.test.tsx index ff00e41c09..920f192baf 100644 --- a/ui/src/App.test.tsx +++ b/ui/src/App.test.tsx @@ -43,17 +43,18 @@ vi.mock("@/lib/router", () => ({ useParams: () => ({}), })); -async function flushReact() { - await Promise.resolve(); - await new Promise((resolve) => window.setTimeout(resolve, 0)); -} - +/** + * Waits on the condition, not on a fixed number of turns. A hand-rolled retry + * loop is ample on an idle machine and not when the suite runs many workers in + * parallel: it gives up after N turns and reports a failure on behaviour that + * works. `vi.waitFor` retries against a time budget, so a loaded worker gets + * more turns instead. + * + * Same replacement as #11499 and #11521, which fixed the shorter-budget + * instances of this in the routing tests. + */ async function waitForText(container: HTMLElement, text: string) { - for (let attempt = 0; attempt < 20; attempt += 1) { - if (container.textContent?.includes(text)) return; - await flushReact(); - } - expect(container.textContent).toContain(text); + await vi.waitFor(() => expect(container.textContent).toContain(text)); } function renderGate(container: HTMLElement) { diff --git a/ui/src/components/WorkspaceFileBrowser.test.tsx b/ui/src/components/WorkspaceFileBrowser.test.tsx index 6277c65fd5..876794ace4 100644 --- a/ui/src/components/WorkspaceFileBrowser.test.tsx +++ b/ui/src/components/WorkspaceFileBrowser.test.tsx @@ -16,18 +16,21 @@ function act(callback: () => void | Promise) { return result; } +/** + * Waits on the condition, not on a fixed number of turns. A hand-rolled retry + * loop is ample on an idle machine and not when the suite runs many workers in + * parallel: it gives up after N turns and reports a failure on behaviour that + * works. `vi.waitFor` retries against a time budget, so a loaded worker gets + * more turns instead. + * + * Same replacement as #11499 and #11521, which fixed the shorter-budget + * instances of this in the routing tests. + * + * This was a reimplementation of `vi.waitFor` down to rethrowing the last + * error, differing only in bounding on turns rather than on time. + */ async function waitForExpectation(assertion: () => void) { - let lastError: unknown; - for (let attempt = 0; attempt < 20; attempt += 1) { - try { - assertion(); - return; - } catch (error) { - lastError = error; - await new Promise((resolve) => window.setTimeout(resolve, 0)); - } - } - throw lastError; + await vi.waitFor(assertion); } const useQueryMock = vi.fn(); diff --git a/ui/src/components/WorkspaceFileMarkdownBody.availability.test.tsx b/ui/src/components/WorkspaceFileMarkdownBody.availability.test.tsx index aa62337c5a..1d127c13cb 100644 --- a/ui/src/components/WorkspaceFileMarkdownBody.availability.test.tsx +++ b/ui/src/components/WorkspaceFileMarkdownBody.availability.test.tsx @@ -44,18 +44,21 @@ function act(callback: () => void) { flushSync(callback); } +/** + * Waits on the condition, not on a fixed number of turns. A hand-rolled retry + * loop is ample on an idle machine and not when the suite runs many workers in + * parallel: it gives up after N turns and reports a failure on behaviour that + * works. `vi.waitFor` retries against a time budget, so a loaded worker gets + * more turns instead. + * + * Same replacement as #11499 and #11521, which fixed the shorter-budget + * instances of this in the routing tests. + * + * This was a reimplementation of `vi.waitFor` down to rethrowing the last + * error, differing only in bounding on turns rather than on time. + */ async function waitForExpectation(assertion: () => void) { - let lastError: unknown; - for (let attempt = 0; attempt < 30; attempt += 1) { - try { - assertion(); - return; - } catch (error) { - lastError = error; - await new Promise((resolve) => window.setTimeout(resolve, 0)); - } - } - throw lastError; + await vi.waitFor(assertion); } function resource(overrides: Partial = {}): ResolvedWorkspaceResource { diff --git a/ui/src/pages/SkillStudio.test.tsx b/ui/src/pages/SkillStudio.test.tsx index a8afa4b62f..dd01d631a6 100644 --- a/ui/src/pages/SkillStudio.test.tsx +++ b/ui/src/pages/SkillStudio.test.tsx @@ -150,23 +150,21 @@ async function act(callback: () => void | Promise) { await result; } -async function flushReact() { - await Promise.resolve(); - await new Promise((resolve) => window.setTimeout(resolve, 0)); -} - +/** + * Waits on the condition, not on a fixed number of turns. A hand-rolled retry + * loop is ample on an idle machine and not when the suite runs many workers in + * parallel: it gives up after N turns and reports a failure on behaviour that + * works. `vi.waitFor` retries against a time budget, so a loaded worker gets + * more turns instead. + * + * Same replacement as #11499 and #11521, which fixed the shorter-budget + * instances of this in the routing tests. + * + * This was a reimplementation of `vi.waitFor` down to rethrowing the last + * error, differing only in bounding on turns rather than on time. + */ async function waitFor(assertion: () => void) { - let lastError: unknown; - for (let attempt = 0; attempt < 25; attempt += 1) { - try { - assertion(); - return; - } catch (error) { - lastError = error; - await flushReact(); - } - } - throw lastError; + await vi.waitFor(assertion); } async function renderStudio() {