
















@@ -0,0 +1,145 @@
1+import { afterEach, describe, expect, it, vi } from "vitest";
2+import { getFreePort } from "../browser/test-port.js";
3+import type { OpenClawConfig } from "../config/config.js";
4+5+const mocks = vi.hoisted(() => ({
6+runtimeConfig: {} as OpenClawConfig,
7+runtimeSourceConfig: null as OpenClawConfig | null,
8+ensureBrowserControlAuth: vi.fn(async () => ({ auth: {} })),
9+resolveBrowserControlAuth: vi.fn(() => ({})),
10+shouldAutoGenerateBrowserAuth: vi.fn(() => false),
11+ensureExtensionRelayForProfiles: vi.fn(async () => {}),
12+stopKnownBrowserProfiles: vi.fn(async () => {}),
13+isChromeReachable: vi.fn(async () => false),
14+isChromeCdpReady: vi.fn(async () => false),
15+}));
16+17+vi.mock("../config/config.js", async () => {
18+const actual = await vi.importActual<typeof import("../config/config.js")>("../config/config.js");
19+return {
20+ ...actual,
21+getRuntimeConfig: () => mocks.runtimeConfig,
22+getRuntimeConfigSourceSnapshot: () => mocks.runtimeSourceConfig,
23+loadConfig: () => mocks.runtimeConfig,
24+};
25+});
26+27+vi.mock("../browser/control-auth.js", () => ({
28+ensureBrowserControlAuth: mocks.ensureBrowserControlAuth,
29+resolveBrowserControlAuth: mocks.resolveBrowserControlAuth,
30+shouldAutoGenerateBrowserAuth: mocks.shouldAutoGenerateBrowserAuth,
31+}));
32+33+vi.mock("../browser/server-lifecycle.js", () => ({
34+ensureExtensionRelayForProfiles: mocks.ensureExtensionRelayForProfiles,
35+stopKnownBrowserProfiles: mocks.stopKnownBrowserProfiles,
36+}));
37+38+vi.mock("../browser/chrome.js", () => ({
39+diagnoseChromeCdp: vi.fn(async () => ({ ok: false })),
40+formatChromeCdpDiagnostic: vi.fn(() => "not reachable"),
41+isChromeCdpReady: mocks.isChromeCdpReady,
42+isChromeReachable: mocks.isChromeReachable,
43+launchOpenClawChrome: vi.fn(async () => {
44+throw new Error("launch should not be needed for status");
45+}),
46+resolveOpenClawUserDataDir: vi.fn(() => "/tmp/openclaw-browser"),
47+stopOpenClawChrome: vi.fn(async () => {}),
48+}));
49+50+vi.mock("../browser/pw-ai-state.js", () => ({
51+isPwAiLoaded: vi.fn(() => false),
52+}));
53+54+const { startBrowserControlServerFromConfig, stopBrowserControlServer } =
55+await import("../server.js");
56+const { stopBrowserControlService } = await import("../control-service.js");
57+const { browserHandlers } = await import("./browser-request.js");
58+59+function browserConfig(params: {
60+gatewayPort: number;
61+executablePath?: string;
62+headless?: boolean;
63+noSandbox?: boolean;
64+}): OpenClawConfig {
65+return {
66+gateway: {
67+port: params.gatewayPort,
68+},
69+browser: {
70+enabled: true,
71+defaultProfile: "openclaw",
72+ ...(params.executablePath ? { executablePath: params.executablePath } : {}),
73+ ...(typeof params.headless === "boolean" ? { headless: params.headless } : {}),
74+ ...(typeof params.noSandbox === "boolean" ? { noSandbox: params.noSandbox } : {}),
75+profiles: {
76+openclaw: {
77+cdpPort: params.gatewayPort + 11,
78+color: "#FF4500",
79+},
80+},
81+},
82+};
83+}
84+85+async function browserRequestStatus(): Promise<unknown> {
86+const respond = vi.fn();
87+await browserHandlers["browser.request"]({
88+params: {
89+method: "GET",
90+path: "/",
91+query: { profile: "openclaw" },
92+},
93+respond: respond as never,
94+context: {
95+nodeRegistry: {
96+listConnected: () => [],
97+},
98+} as never,
99+client: null,
100+req: { type: "req", id: "req-1", method: "browser.request" },
101+isWebchatConnect: () => false,
102+});
103+const call = respond.mock.calls[0];
104+expect(call?.[0]).toBe(true);
105+return call?.[1];
106+}
107+108+describe("browser.request local control state", () => {
109+afterEach(async () => {
110+await stopBrowserControlService();
111+await stopBrowserControlServer();
112+mocks.runtimeSourceConfig = null;
113+vi.clearAllMocks();
114+});
115+116+it("uses the same resolved browser config as the HTTP control service", async () => {
117+const controlPort = await getFreePort();
118+const gatewayPort = controlPort - 2;
119+120+mocks.runtimeConfig = browserConfig({
121+ gatewayPort,
122+executablePath: "/usr/bin/google-chrome",
123+headless: true,
124+noSandbox: true,
125+});
126+mocks.runtimeSourceConfig = mocks.runtimeConfig;
127+const httpState = await startBrowserControlServerFromConfig();
128+expect(httpState?.resolved.executablePath).toBe("/usr/bin/google-chrome");
129+expect(httpState?.resolved.noSandbox).toBe(true);
130+131+// The runtime snapshot can lag behind source config after gateway startup;
132+// browser.request must not fork a second stale control state from it.
133+mocks.runtimeConfig = browserConfig({
134+ gatewayPort,
135+headless: false,
136+noSandbox: false,
137+});
138+139+await expect(browserRequestStatus()).resolves.toMatchObject({
140+executablePath: "/usr/bin/google-chrome",
141+headless: true,
142+noSandbox: true,
143+});
144+});
145+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。