From 6fec75f15dae2848e5904f1d2a12edb6ac2cf28f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 27 Mar 2026 23:20:43 +0000 Subject: [PATCH] test(browser): isolate auth and download mocks --- .../browser/control-auth.auto-token.test.ts | 34 ++++- ...-core.waits-next-download-saves-it.test.ts | 126 ++++++++++++------ 2 files changed, 112 insertions(+), 48 deletions(-) diff --git a/extensions/browser/src/browser/control-auth.auto-token.test.ts b/extensions/browser/src/browser/control-auth.auto-token.test.ts index cc9521921db..f67ced76ee9 100644 --- a/extensions/browser/src/browser/control-auth.auto-token.test.ts +++ b/extensions/browser/src/browser/control-auth.auto-token.test.ts @@ -4,6 +4,25 @@ import type { OpenClawConfig } from "../config/config.js"; const mocks = vi.hoisted(() => ({ loadConfig: vi.fn<() => OpenClawConfig>(), + resolveGatewayAuth: vi.fn( + ({ + authConfig, + }: { + authConfig?: NonNullable["auth"]> | undefined; + }) => { + const token = + typeof authConfig?.token === "string" + ? authConfig.token + : typeof authConfig?.token === "object" + ? undefined + : undefined; + const password = typeof authConfig?.password === "string" ? authConfig.password : undefined; + return { + token, + password, + }; + }, + ), ensureGatewayStartupAuth: vi.fn(async ({ cfg }: { cfg: OpenClawConfig }) => ({ cfg: { ...cfg, @@ -25,18 +44,18 @@ const mocks = vi.hoisted(() => ({ })), })); -vi.mock("../config/config.js", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - loadConfig: mocks.loadConfig, - }; -}); +vi.mock("../config/config.js", () => ({ + loadConfig: mocks.loadConfig, +})); vi.mock("../gateway/startup-auth.js", () => ({ ensureGatewayStartupAuth: mocks.ensureGatewayStartupAuth, })); +vi.mock("../gateway/auth.js", () => ({ + resolveGatewayAuth: mocks.resolveGatewayAuth, +})); + let ensureBrowserControlAuth: typeof import("./control-auth.js").ensureBrowserControlAuth; describe("ensureBrowserControlAuth", () => { @@ -74,6 +93,7 @@ describe("ensureBrowserControlAuth", () => { ({ ensureBrowserControlAuth } = await import("./control-auth.js")); vi.restoreAllMocks(); mocks.loadConfig.mockClear(); + mocks.resolveGatewayAuth.mockClear(); mocks.ensureGatewayStartupAuth.mockClear(); }); diff --git a/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts b/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts index 26cb78ef6b6..bb89369a1d4 100644 --- a/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts +++ b/extensions/browser/src/browser/pw-tools-core.waits-next-download-saves-it.test.ts @@ -2,31 +2,102 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { - getPwToolsCoreSessionMocks, - installPwToolsCoreTestHooks, - setPwToolsCoreCurrentPage, - setPwToolsCoreCurrentRefLocator, -} from "./pw-tools-core.test-harness.js"; -installPwToolsCoreTestHooks(); -const sessionMocks = getPwToolsCoreSessionMocks(); +let currentPage: Record | null = null; +let currentRefLocator: Record | null = null; +let pageState: { + console: unknown[]; + armIdUpload: number; + armIdDialog: number; + armIdDownload: number; +} = { + console: [], + armIdUpload: 0, + armIdDialog: 0, + armIdDownload: 0, +}; + +const sessionMocks = vi.hoisted(() => ({ + getPageForTargetId: vi.fn(async () => { + if (!currentPage) { + throw new Error("missing page"); + } + return currentPage; + }), + ensurePageState: vi.fn(() => pageState), + forceDisconnectPlaywrightForTarget: vi.fn(async () => {}), + restoreRoleRefsForTarget: vi.fn(() => {}), + storeRoleRefsForTarget: vi.fn(() => {}), + refLocator: vi.fn(() => { + if (!currentRefLocator) { + throw new Error("missing locator"); + } + return currentRefLocator; + }), + rememberRoleRefsForTarget: vi.fn(() => {}), +})); const tmpDirMocks = vi.hoisted(() => ({ resolvePreferredOpenClawTmpDir: vi.fn(() => "/tmp/openclaw"), })); -vi.mock("../infra/tmp-openclaw-dir.js", () => tmpDirMocks); -let mod: typeof import("./pw-tools-core.js"); +const chromeMocks = vi.hoisted(() => ({ + getChromeWebSocketUrl: vi.fn(async () => "ws://127.0.0.1/devtools/browser/mock"), +})); +const clientFetchMocks = vi.hoisted(() => ({ + resolveBrowserRateLimitMessage: vi.fn(() => undefined), +})); +vi.mock("./pw-session.js", () => sessionMocks); +vi.mock("./chrome.js", () => chromeMocks); +vi.mock("./client-fetch.js", () => clientFetchMocks); +vi.mock("../infra/tmp-openclaw-dir.js", () => ({ + resolvePreferredOpenClawTmpDir: tmpDirMocks.resolvePreferredOpenClawTmpDir, +})); +let mod: Pick< + typeof import("./pw-tools-core.downloads.js"), + "downloadViaPlaywright" | "waitForDownloadViaPlaywright" +> & + Pick; describe("pw-tools-core", () => { beforeAll(async () => { vi.resetModules(); - mod = await import("./pw-tools-core.js"); + vi.doMock("./pw-session.js", () => sessionMocks); + vi.doMock("./chrome.js", () => chromeMocks); + vi.doMock("../infra/tmp-openclaw-dir.js", () => ({ + resolvePreferredOpenClawTmpDir: tmpDirMocks.resolvePreferredOpenClawTmpDir, + })); + const [downloads, responses] = await Promise.all([ + import("./pw-tools-core.downloads.js"), + import("./pw-tools-core.responses.js"), + ]); + mod = { + downloadViaPlaywright: downloads.downloadViaPlaywright, + waitForDownloadViaPlaywright: downloads.waitForDownloadViaPlaywright, + responseBodyViaPlaywright: responses.responseBodyViaPlaywright, + }; }); beforeEach(() => { + currentPage = null; + currentRefLocator = null; + pageState = { + console: [], + armIdUpload: 0, + armIdDialog: 0, + armIdDownload: 0, + }; + + for (const fn of Object.values(sessionMocks)) { + fn.mockClear(); + } for (const fn of Object.values(tmpDirMocks)) { fn.mockClear(); } + for (const fn of Object.values(chromeMocks)) { + fn.mockClear(); + } + for (const fn of Object.values(clientFetchMocks)) { + fn.mockClear(); + } tmpDirMocks.resolvePreferredOpenClawTmpDir.mockReturnValue("/tmp/openclaw"); }); @@ -72,7 +143,7 @@ describe("pw-tools-core", () => { } }); const off = vi.fn(); - setPwToolsCoreCurrentPage({ on, off }); + currentPage = { on, off }; return { trigger: (download: unknown) => { downloadHandler?.(download); @@ -137,7 +208,7 @@ describe("pw-tools-core", () => { const harness = createDownloadEventHarness(); const click = vi.fn(async () => {}); - setPwToolsCoreCurrentRefLocator({ click }); + currentRefLocator = { click }; const saveAs = vi.fn(async (outPath: string) => { await fs.writeFile(outPath, "report-content", "utf8"); @@ -244,7 +315,7 @@ describe("pw-tools-core", () => { } }); const off = vi.fn(); - setPwToolsCoreCurrentPage({ on, off }); + currentPage = { on, off }; const resp = { url: () => "https://example.com/api/data", @@ -271,31 +342,4 @@ describe("pw-tools-core", () => { expect(res.body).toBe('{"ok":true'); expect(res.truncated).toBe(true); }); - it("scrolls a ref into view (default timeout)", async () => { - const scrollIntoViewIfNeeded = vi.fn(async () => {}); - setPwToolsCoreCurrentRefLocator({ scrollIntoViewIfNeeded }); - const page = {}; - setPwToolsCoreCurrentPage(page); - - await mod.scrollIntoViewViaPlaywright({ - cdpUrl: "http://127.0.0.1:18792", - targetId: "T1", - ref: "1", - }); - - expect(sessionMocks.refLocator).toHaveBeenCalledWith(page, "1"); - expect(scrollIntoViewIfNeeded).toHaveBeenCalledWith({ timeout: 20_000 }); - }); - it("requires a ref for scrollIntoView", async () => { - setPwToolsCoreCurrentRefLocator({ scrollIntoViewIfNeeded: vi.fn(async () => {}) }); - setPwToolsCoreCurrentPage({}); - - await expect( - mod.scrollIntoViewViaPlaywright({ - cdpUrl: "http://127.0.0.1:18792", - targetId: "T1", - ref: " ", - }), - ).rejects.toThrow(/ref or selector is required/i); - }); });