Files
openclaw/src/browser/server-context.loopback-direct-ws.test.ts

143 lines
4.9 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { withFetchPreconnect } from "../test-utils/fetch-mock.js";
import * as cdpModule from "./cdp.js";
import { createBrowserRouteContext } from "./server-context.js";
import { makeState, originalFetch } from "./server-context.remote-tab-ops.harness.js";
afterEach(() => {
globalThis.fetch = originalFetch;
vi.restoreAllMocks();
});
describe("browser server-context loopback direct WebSocket profiles", () => {
it("uses an HTTP /json/list base when opening tabs", async () => {
const createTargetViaCdp = vi
.spyOn(cdpModule, "createTargetViaCdp")
.mockResolvedValue({ targetId: "CREATED" });
const fetchMock = vi.fn(async (url: unknown) => {
const u = String(url);
expect(u).toBe("http://127.0.0.1:18800/json/list?token=abc");
return {
ok: true,
json: async () => [
{
id: "CREATED",
title: "New Tab",
url: "http://127.0.0.1:8080",
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/CREATED",
type: "page",
},
],
} as unknown as Response;
});
global.fetch = withFetchPreconnect(fetchMock);
const state = makeState("openclaw");
state.resolved.profiles.openclaw = {
cdpUrl: "ws://127.0.0.1:18800/devtools/browser/SESSION?token=abc",
color: "#FF4500",
};
const ctx = createBrowserRouteContext({ getState: () => state });
const openclaw = ctx.forProfile("openclaw");
const opened = await openclaw.openTab("http://127.0.0.1:8080");
expect(opened.targetId).toBe("CREATED");
expect(createTargetViaCdp).toHaveBeenCalledWith({
cdpUrl: "ws://127.0.0.1:18800/devtools/browser/SESSION?token=abc",
url: "http://127.0.0.1:8080",
ssrfPolicy: { allowPrivateNetwork: true },
});
});
it("uses an HTTP /json base for focus and close", async () => {
const fetchMock = vi.fn(async (url: unknown) => {
const u = String(url);
if (u === "http://127.0.0.1:18800/json/list?token=abc") {
return {
ok: true,
json: async () => [
{
id: "T1",
title: "Tab 1",
url: "https://example.com",
webSocketDebuggerUrl: "ws://127.0.0.1/devtools/page/T1",
type: "page",
},
],
} as unknown as Response;
}
if (u === "http://127.0.0.1:18800/json/activate/T1?token=abc") {
return { ok: true, json: async () => ({}) } as unknown as Response;
}
if (u === "http://127.0.0.1:18800/json/close/T1?token=abc") {
return { ok: true, json: async () => ({}) } as unknown as Response;
}
throw new Error(`unexpected fetch: ${u}`);
});
global.fetch = withFetchPreconnect(fetchMock);
const state = makeState("openclaw");
state.resolved.profiles.openclaw = {
cdpUrl: "ws://127.0.0.1:18800/devtools/browser/SESSION?token=abc",
color: "#FF4500",
};
const ctx = createBrowserRouteContext({ getState: () => state });
const openclaw = ctx.forProfile("openclaw");
await openclaw.focusTab("T1");
await openclaw.closeTab("T1");
expect(fetchMock).toHaveBeenCalledWith(
"http://127.0.0.1:18800/json/activate/T1?token=abc",
expect.any(Object),
);
expect(fetchMock).toHaveBeenCalledWith(
"http://127.0.0.1:18800/json/close/T1?token=abc",
expect.any(Object),
);
});
it("uses an HTTPS /json base for secure direct WebSocket profiles with a /cdp suffix", async () => {
const fetchMock = vi.fn(async (url: unknown) => {
const u = String(url);
if (u === "https://127.0.0.1:18800/json/list?token=abc") {
return {
ok: true,
json: async () => [
{
id: "T2",
title: "Secure Tab",
url: "https://example.com",
webSocketDebuggerUrl: "wss://127.0.0.1/devtools/page/T2",
type: "page",
},
],
} as unknown as Response;
}
if (u === "https://127.0.0.1:18800/json/activate/T2?token=abc") {
return { ok: true, json: async () => ({}) } as unknown as Response;
}
if (u === "https://127.0.0.1:18800/json/close/T2?token=abc") {
return { ok: true, json: async () => ({}) } as unknown as Response;
}
throw new Error(`unexpected fetch: ${u}`);
});
global.fetch = withFetchPreconnect(fetchMock);
const state = makeState("openclaw");
state.resolved.profiles.openclaw = {
cdpUrl: "wss://127.0.0.1:18800/cdp?token=abc",
color: "#FF4500",
};
const ctx = createBrowserRouteContext({ getState: () => state });
const openclaw = ctx.forProfile("openclaw");
const tabs = await openclaw.listTabs();
expect(tabs.map((tab) => tab.targetId)).toEqual(["T2"]);
await openclaw.focusTab("T2");
await openclaw.closeTab("T2");
});
});