

























@@ -0,0 +1,133 @@
1+import { beforeEach, describe, expect, it, vi } from "vitest";
2+import { createBrowserRouteApp, createBrowserRouteResponse } from "./test-helpers.js";
3+4+const cdpMocks = vi.hoisted(() => ({
5+getChromeWebSocketUrl: vi.fn(async () => "ws://127.0.0.1:18800/devtools/browser/test"),
6+send: vi.fn(
7+async (
8+_method: string,
9+_params?: Record<string, unknown>,
10+): Promise<Record<string, unknown>> => ({}),
11+),
12+withCdpSocket: vi.fn(
13+async (
14+_wsUrl: string,
15+fn: (
16+send: (method: string, params?: Record<string, unknown>) => Promise<unknown>,
17+) => Promise<unknown>,
18+) => await fn(cdpMocks.send),
19+),
20+}));
21+22+vi.mock("../chrome.js", () => ({
23+getChromeWebSocketUrl: cdpMocks.getChromeWebSocketUrl,
24+}));
25+26+vi.mock("../cdp.helpers.js", () => ({
27+withCdpSocket: cdpMocks.withCdpSocket,
28+}));
29+30+const { registerBrowserPermissionRoutes } = await import("./permissions.js");
31+32+function createProfileContext() {
33+return {
34+profile: {
35+name: "openclaw",
36+cdpUrl: "http://127.0.0.1:18800",
37+},
38+ensureBrowserAvailable: vi.fn(async () => {}),
39+ensureTabAvailable: vi.fn(),
40+isHttpReachable: vi.fn(),
41+isTransportAvailable: vi.fn(),
42+isReachable: vi.fn(),
43+listTabs: vi.fn(),
44+openTab: vi.fn(),
45+labelTab: vi.fn(),
46+focusTab: vi.fn(),
47+closeTab: vi.fn(),
48+stopRunningBrowser: vi.fn(),
49+resetProfile: vi.fn(),
50+};
51+}
52+53+function createRouteContext(profileCtx: ReturnType<typeof createProfileContext>) {
54+return {
55+state: () => ({ resolved: { ssrfPolicy: { allowPrivateNetwork: false } } }),
56+forProfile: () => profileCtx,
57+listProfiles: vi.fn(async () => []),
58+mapTabError: vi.fn(() => null),
59+ ...profileCtx,
60+};
61+}
62+63+async function callGrant(body: Record<string, unknown>) {
64+const { app, postHandlers } = createBrowserRouteApp();
65+const profileCtx = createProfileContext();
66+registerBrowserPermissionRoutes(app, createRouteContext(profileCtx) as never);
67+const handler = postHandlers.get("/permissions/grant");
68+expect(handler).toBeTypeOf("function");
69+70+const response = createBrowserRouteResponse();
71+await handler?.({ params: {}, query: {}, body }, response.res);
72+return { response, profileCtx };
73+}
74+75+describe("browser permission routes", () => {
76+beforeEach(() => {
77+cdpMocks.getChromeWebSocketUrl.mockClear();
78+cdpMocks.send.mockReset().mockResolvedValue({});
79+cdpMocks.withCdpSocket.mockClear();
80+});
81+82+it("grants required and optional Chrome permissions for an origin", async () => {
83+const { response, profileCtx } = await callGrant({
84+origin: "https://meet.google.com/abc-defg-hij",
85+permissions: ["audioCapture", "videoCapture"],
86+optionalPermissions: ["speakerSelection"],
87+timeoutMs: 1234,
88+});
89+90+expect(response.statusCode).toBe(200);
91+expect(response.body).toMatchObject({
92+ok: true,
93+origin: "https://meet.google.com",
94+grantedPermissions: ["audioCapture", "videoCapture", "speakerSelection"],
95+unsupportedPermissions: [],
96+});
97+expect(profileCtx.ensureBrowserAvailable).toHaveBeenCalled();
98+expect(cdpMocks.getChromeWebSocketUrl).toHaveBeenCalledWith("http://127.0.0.1:18800", 1234, {
99+allowPrivateNetwork: false,
100+});
101+expect(cdpMocks.send).toHaveBeenCalledWith("Browser.grantPermissions", {
102+origin: "https://meet.google.com",
103+permissions: ["audioCapture", "videoCapture", "speakerSelection"],
104+});
105+});
106+107+it("keeps required permissions when an optional permission is unsupported", async () => {
108+cdpMocks.send.mockImplementation(async (_method: string, params?: Record<string, unknown>) => {
109+const permissions = Array.isArray(params?.permissions) ? params.permissions : [];
110+if (permissions.includes("speakerSelection")) {
111+throw new Error("Unknown permission type");
112+}
113+return {};
114+});
115+116+const { response } = await callGrant({
117+origin: "https://meet.google.com",
118+permissions: ["audioCapture", "videoCapture"],
119+optionalPermissions: ["speakerSelection"],
120+});
121+122+expect(response.statusCode).toBe(200);
123+expect(response.body).toMatchObject({
124+ok: true,
125+grantedPermissions: ["audioCapture", "videoCapture"],
126+unsupportedPermissions: ["speakerSelection"],
127+});
128+expect(cdpMocks.send).toHaveBeenNthCalledWith(2, "Browser.grantPermissions", {
129+origin: "https://meet.google.com",
130+permissions: ["audioCapture", "videoCapture"],
131+});
132+});
133+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。