mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-31 20:01:36 +00:00
refactor: remove core browser test duplicates
This commit is contained in:
@@ -1,340 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const controlServiceMocks = vi.hoisted(() => ({
|
||||
createBrowserControlContext: vi.fn(() => ({ control: true })),
|
||||
startBrowserControlServiceFromConfig: vi.fn(async () => true),
|
||||
}));
|
||||
|
||||
const dispatcherMocks = vi.hoisted(() => ({
|
||||
dispatch: vi.fn(),
|
||||
createBrowserRouteDispatcher: vi.fn(() => ({
|
||||
dispatch: dispatcherMocks.dispatch,
|
||||
})),
|
||||
}));
|
||||
|
||||
const configMocks = vi.hoisted(() => ({
|
||||
loadConfig: vi.fn(() => ({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: [] as string[] } },
|
||||
})),
|
||||
}));
|
||||
|
||||
const browserConfigMocks = vi.hoisted(() => ({
|
||||
resolveBrowserConfig: vi.fn(() => ({
|
||||
enabled: true,
|
||||
defaultProfile: "openclaw",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../../extensions/browser/src/core-api.js", async () => ({
|
||||
...(await vi.importActual<object>("../../extensions/browser/src/core-api.js")),
|
||||
createBrowserControlContext: controlServiceMocks.createBrowserControlContext,
|
||||
createBrowserRouteDispatcher: dispatcherMocks.createBrowserRouteDispatcher,
|
||||
detectMime: vi.fn(async () => "image/png"),
|
||||
loadConfig: configMocks.loadConfig,
|
||||
resolveBrowserConfig: browserConfigMocks.resolveBrowserConfig,
|
||||
startBrowserControlServiceFromConfig: controlServiceMocks.startBrowserControlServiceFromConfig,
|
||||
}));
|
||||
|
||||
let runBrowserProxyCommand: typeof import("../../extensions/browser/src/node-host/invoke-browser.js").runBrowserProxyCommand;
|
||||
|
||||
describe("runBrowserProxyCommand", () => {
|
||||
beforeEach(async () => {
|
||||
// No-isolate runs can reuse a cached invoke-browser module that was loaded
|
||||
// via node-host entrypoints before this file's mocks were declared.
|
||||
vi.useRealTimers();
|
||||
vi.resetModules();
|
||||
dispatcherMocks.dispatch.mockReset();
|
||||
dispatcherMocks.createBrowserRouteDispatcher.mockReset().mockImplementation(() => ({
|
||||
dispatch: dispatcherMocks.dispatch,
|
||||
}));
|
||||
controlServiceMocks.createBrowserControlContext.mockReset().mockReturnValue({ control: true });
|
||||
controlServiceMocks.startBrowserControlServiceFromConfig.mockReset().mockResolvedValue(true);
|
||||
configMocks.loadConfig.mockReset().mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: [] as string[] } },
|
||||
});
|
||||
browserConfigMocks.resolveBrowserConfig.mockReset().mockReturnValue({
|
||||
enabled: true,
|
||||
defaultProfile: "openclaw",
|
||||
});
|
||||
({ runBrowserProxyCommand } =
|
||||
await import("../../extensions/browser/src/node-host/invoke-browser.js"));
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: [] as string[] } },
|
||||
});
|
||||
browserConfigMocks.resolveBrowserConfig.mockReturnValue({
|
||||
enabled: true,
|
||||
defaultProfile: "openclaw",
|
||||
});
|
||||
controlServiceMocks.startBrowserControlServiceFromConfig.mockResolvedValue(true);
|
||||
});
|
||||
|
||||
it("adds profile and browser status details on ws-backed timeouts", async () => {
|
||||
vi.useFakeTimers();
|
||||
dispatcherMocks.dispatch
|
||||
.mockImplementationOnce(async () => {
|
||||
await new Promise(() => {});
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
body: {
|
||||
running: true,
|
||||
cdpHttp: true,
|
||||
cdpReady: false,
|
||||
cdpUrl: "http://127.0.0.1:18792",
|
||||
},
|
||||
});
|
||||
|
||||
const result = expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "GET",
|
||||
path: "/snapshot",
|
||||
profile: "openclaw",
|
||||
timeoutMs: 5,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow(
|
||||
/browser proxy timed out for GET \/snapshot after 5ms; ws-backed browser action; profile=openclaw; status\(running=true, cdpHttp=true, cdpReady=false, cdpUrl=http:\/\/127\.0\.0\.1:18792\)/,
|
||||
);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await result;
|
||||
});
|
||||
|
||||
it("includes chrome-mcp transport in timeout diagnostics when no CDP URL exists", async () => {
|
||||
vi.useFakeTimers();
|
||||
dispatcherMocks.dispatch
|
||||
.mockImplementationOnce(async () => {
|
||||
await new Promise(() => {});
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
body: {
|
||||
running: true,
|
||||
transport: "chrome-mcp",
|
||||
cdpHttp: true,
|
||||
cdpReady: false,
|
||||
cdpUrl: null,
|
||||
},
|
||||
});
|
||||
|
||||
const result = expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "GET",
|
||||
path: "/snapshot",
|
||||
profile: "user",
|
||||
timeoutMs: 5,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow(
|
||||
/browser proxy timed out for GET \/snapshot after 5ms; ws-backed browser action; profile=user; status\(running=true, cdpHttp=true, cdpReady=false, transport=chrome-mcp\)/,
|
||||
);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await result;
|
||||
});
|
||||
|
||||
it("redacts sensitive cdpUrl details in timeout diagnostics", async () => {
|
||||
vi.useFakeTimers();
|
||||
dispatcherMocks.dispatch
|
||||
.mockImplementationOnce(async () => {
|
||||
await new Promise(() => {});
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
status: 200,
|
||||
body: {
|
||||
running: true,
|
||||
cdpHttp: true,
|
||||
cdpReady: false,
|
||||
cdpUrl:
|
||||
"https://alice:supersecretpasswordvalue1234@example.com/chrome?token=supersecrettokenvalue1234567890",
|
||||
},
|
||||
});
|
||||
|
||||
const result = expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "GET",
|
||||
path: "/snapshot",
|
||||
profile: "remote",
|
||||
timeoutMs: 5,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow(
|
||||
/status\(running=true, cdpHttp=true, cdpReady=false, cdpUrl=https:\/\/example\.com\/chrome\?token=supers…7890\)/,
|
||||
);
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await result;
|
||||
});
|
||||
|
||||
it("keeps non-timeout browser errors intact", async () => {
|
||||
dispatcherMocks.dispatch.mockResolvedValue({
|
||||
status: 500,
|
||||
body: { error: "tab not found" },
|
||||
});
|
||||
|
||||
await expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "POST",
|
||||
path: "/act",
|
||||
profile: "openclaw",
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow("tab not found");
|
||||
});
|
||||
|
||||
it("rejects unauthorized query.profile when allowProfiles is configured", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: ["openclaw"] } },
|
||||
});
|
||||
|
||||
await expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "GET",
|
||||
path: "/snapshot",
|
||||
query: { profile: "user" },
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow("INVALID_REQUEST: browser profile not allowed");
|
||||
expect(dispatcherMocks.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects unauthorized body.profile when allowProfiles is configured", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: ["openclaw"] } },
|
||||
});
|
||||
|
||||
await expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "POST",
|
||||
path: "/stop",
|
||||
body: { profile: "user" },
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow("INVALID_REQUEST: browser profile not allowed");
|
||||
expect(dispatcherMocks.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects persistent profile creation when allowProfiles is configured", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: ["openclaw"] } },
|
||||
});
|
||||
|
||||
await expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "POST",
|
||||
path: "/profiles/create",
|
||||
body: { name: "poc", cdpUrl: "http://127.0.0.1:9222" },
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow(
|
||||
"INVALID_REQUEST: browser.proxy cannot mutate persistent browser profiles when allowProfiles is configured",
|
||||
);
|
||||
expect(dispatcherMocks.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects persistent profile deletion when allowProfiles is configured", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: ["openclaw"] } },
|
||||
});
|
||||
|
||||
await expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "DELETE",
|
||||
path: "/profiles/poc",
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow(
|
||||
"INVALID_REQUEST: browser.proxy cannot mutate persistent browser profiles when allowProfiles is configured",
|
||||
);
|
||||
expect(dispatcherMocks.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rejects persistent profile reset when allowProfiles is configured", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: ["openclaw"] } },
|
||||
});
|
||||
|
||||
await expect(
|
||||
runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "POST",
|
||||
path: "/reset-profile",
|
||||
body: { profile: "openclaw", name: "openclaw" },
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow(
|
||||
"INVALID_REQUEST: browser.proxy cannot mutate persistent browser profiles when allowProfiles is configured",
|
||||
);
|
||||
expect(dispatcherMocks.dispatch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("canonicalizes an allowlisted body profile into the dispatched query", async () => {
|
||||
configMocks.loadConfig.mockReturnValue({
|
||||
browser: {},
|
||||
nodeHost: { browserProxy: { enabled: true, allowProfiles: ["openclaw"] } },
|
||||
});
|
||||
dispatcherMocks.dispatch.mockResolvedValue({
|
||||
status: 200,
|
||||
body: { ok: true },
|
||||
});
|
||||
|
||||
await runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "POST",
|
||||
path: "/stop",
|
||||
body: { profile: "openclaw" },
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(dispatcherMocks.dispatch).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: "/stop",
|
||||
query: { profile: "openclaw" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves legacy proxy behavior when allowProfiles is empty", async () => {
|
||||
dispatcherMocks.dispatch.mockResolvedValue({
|
||||
status: 200,
|
||||
body: { ok: true },
|
||||
});
|
||||
|
||||
await runBrowserProxyCommand(
|
||||
JSON.stringify({
|
||||
method: "POST",
|
||||
path: "/profiles/create",
|
||||
body: { name: "poc", cdpUrl: "http://127.0.0.1:9222" },
|
||||
timeoutMs: 50,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(dispatcherMocks.dispatch).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
path: "/profiles/create",
|
||||
body: { name: "poc", cdpUrl: "http://127.0.0.1:9222" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user