



























@@ -1,4 +1,5 @@
11import { describe, expect, it, vi } from "vitest";
2+import { withBrowserFetchPreconnect } from "../../test-fetch.js";
23import {
34installRemoteProfileTestLifecycle,
45loadRemoteProfileTestDeps,
@@ -127,4 +128,149 @@ describe("browser remote profile fallback and attachOnly behavior", () => {
127128expect(opened.targetId).toBe("T1");
128129expect(fetchMock).not.toHaveBeenCalled();
129130});
131+132+it("passes configured remote CDP timeouts when opening tabs through raw CDP", async () => {
133+vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue(null);
134+const createTargetViaCdp = vi
135+.spyOn(deps.cdpModule, "createTargetViaCdp")
136+.mockResolvedValue({ targetId: "T_REMOTE" });
137+const { state, remote } = deps.createRemoteRouteHarness(
138+vi.fn(
139+deps.createJsonListFetchMock([
140+{
141+id: "T_REMOTE",
142+title: "Remote Tab",
143+url: "https://example.com",
144+webSocketDebuggerUrl: "wss://browserless.example/devtools/page/T_REMOTE",
145+type: "page",
146+},
147+]),
148+),
149+);
150+state.resolved.remoteCdpTimeoutMs = 4321;
151+state.resolved.remoteCdpHandshakeTimeoutMs = 8765;
152+153+const opened = await remote.openTab("https://example.com");
154+155+expect(opened.targetId).toBe("T_REMOTE");
156+expect(createTargetViaCdp).toHaveBeenCalledWith(
157+expect.objectContaining({
158+cdpUrl: "https://1.1.1.1:9222/chrome?token=abc",
159+url: "https://example.com",
160+timeouts: {
161+httpTimeoutMs: 4321,
162+handshakeTimeoutMs: 8765,
163+},
164+}),
165+);
166+});
167+168+it("uses remote-class tab-open timeouts for attachOnly loopback CDP profiles", async () => {
169+vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue(null);
170+const createTargetViaCdp = vi
171+.spyOn(deps.cdpModule, "createTargetViaCdp")
172+.mockResolvedValue({ targetId: "T_ATTACH" });
173+const state = deps.makeState("openclaw");
174+state.resolved.remoteCdpTimeoutMs = 2345;
175+state.resolved.remoteCdpHandshakeTimeoutMs = 6789;
176+state.resolved.profiles.openclaw = {
177+cdpPort: 18800,
178+attachOnly: true,
179+color: "#FF4500",
180+};
181+const fetchMock = vi.fn(
182+deps.createJsonListFetchMock([
183+{
184+id: "T_ATTACH",
185+title: "Attach Tab",
186+url: "https://example.com",
187+webSocketDebuggerUrl: "ws://127.0.0.1:18800/devtools/page/T_ATTACH",
188+type: "page",
189+},
190+]),
191+);
192+global.fetch = withBrowserFetchPreconnect(fetchMock);
193+const ctx = deps.createBrowserRouteContext({ getState: () => state });
194+195+const opened = await ctx.forProfile("openclaw").openTab("https://example.com");
196+197+expect(opened.targetId).toBe("T_ATTACH");
198+expect(createTargetViaCdp).toHaveBeenCalledWith(
199+expect.objectContaining({
200+cdpUrl: "http://127.0.0.1:18800",
201+timeouts: {
202+httpTimeoutMs: 2345,
203+handshakeTimeoutMs: 6789,
204+},
205+}),
206+);
207+});
208+209+it("keeps managed loopback tab opens on local CDP defaults", async () => {
210+vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue(null);
211+const createTargetViaCdp = vi
212+.spyOn(deps.cdpModule, "createTargetViaCdp")
213+.mockResolvedValue({ targetId: "T_LOCAL" });
214+const state = deps.makeState("openclaw");
215+const fetchMock = vi.fn(
216+deps.createJsonListFetchMock([
217+{
218+id: "T_LOCAL",
219+title: "Local Tab",
220+url: "http://127.0.0.1:3000",
221+webSocketDebuggerUrl: "ws://127.0.0.1:18800/devtools/page/T_LOCAL",
222+type: "page",
223+},
224+]),
225+);
226+global.fetch = withBrowserFetchPreconnect(fetchMock);
227+const ctx = deps.createBrowserRouteContext({ getState: () => state });
228+229+await ctx.forProfile("openclaw").openTab("http://127.0.0.1:3000");
230+231+expect(createTargetViaCdp).toHaveBeenCalledWith({
232+cdpUrl: "http://127.0.0.1:18800",
233+url: "http://127.0.0.1:3000",
234+ssrfPolicy: undefined,
235+});
236+});
237+238+it("uses the remote HTTP timeout for /json/new fallback tab opens", async () => {
239+vi.spyOn(deps.pwAiModule, "getPwAiModule").mockResolvedValue(null);
240+vi.spyOn(deps.cdpModule, "createTargetViaCdp").mockRejectedValue(
241+new Error("Target.createTarget unavailable"),
242+);
243+const fetchMock = vi.fn(async (...args: unknown[]) => {
244+const url = String(args[0]);
245+if (url.includes("/json/new")) {
246+const init = args[1] as RequestInit | undefined;
247+expect(init?.method).toBe("PUT");
248+expect(init?.signal).toBeInstanceOf(AbortSignal);
249+return await new Promise<Response>((_resolve, reject) => {
250+init?.signal?.addEventListener(
251+"abort",
252+() => reject(new Error("aborted after remote timeout")),
253+{ once: true },
254+);
255+});
256+}
257+throw new Error(`unexpected fetch: ${url}`);
258+});
259+const { state, remote } = deps.createRemoteRouteHarness(fetchMock);
260+state.resolved.remoteCdpTimeoutMs = 25;
261+262+const startedAt = Date.now();
263+await expect(remote.openTab("https://example.com")).rejects.toThrow(
264+/aborted after remote timeout/,
265+);
266+267+expect(Date.now() - startedAt).toBeLessThan(700);
268+expect(fetchMock).toHaveBeenCalledWith(
269+expect.stringContaining("/json/new"),
270+expect.objectContaining({
271+method: "PUT",
272+signal: expect.any(AbortSignal),
273+}),
274+);
275+});
130276});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。