



























@@ -91,6 +91,43 @@ async function callBasicRouteWithState(params: {
9191return response;
9292}
939394+async function callStartRoute(params: {
95+profile?: Record<string, unknown>;
96+query?: Record<string, unknown>;
97+}) {
98+const ensureBrowserAvailable = vi.fn(async () => {});
99+const profile = {
100+name: "openclaw",
101+driver: "openclaw",
102+cdpPort: 18800,
103+cdpUrl: "http://127.0.0.1:18800",
104+cdpHost: "127.0.0.1",
105+cdpIsLoopback: true,
106+userDataDir: "/tmp/openclaw-profile",
107+color: "#FF4500",
108+headless: false,
109+headlessSource: "default",
110+attachOnly: false,
111+ ...params.profile,
112+};
113+const { app, postHandlers } = createBrowserRouteApp();
114+registerBrowserBasicRoutes(app, {
115+state: () => ({ resolved: { enabled: true, headless: false }, profiles: new Map() }),
116+forProfile: () =>
117+({
118+ profile,
119+ ensureBrowserAvailable,
120+}) as never,
121+} as never);
122+123+const handler = postHandlers.get("/start");
124+expect(handler).toBeTypeOf("function");
125+126+const response = createBrowserRouteResponse();
127+await handler?.({ params: {}, query: params.query ?? {} }, response.res);
128+return { response, ensureBrowserAvailable };
129+}
130+94131describe("basic browser routes", () => {
95132it("reports Linux no-display headless fallback for local managed profiles", async () => {
96133const originalPlatform = process.platform;
@@ -126,6 +163,38 @@ describe("basic browser routes", () => {
126163}
127164});
128165166+it("reports request-local headless source for tracked local launches", async () => {
167+const state = createManagedProfileState();
168+const profile = (state.forProfile() as { profile: unknown }).profile as never;
169+state.profiles.set("openclaw", {
170+ profile,
171+running: {
172+pid: 222,
173+exe: { kind: "chromium", path: "/usr/bin/chromium" },
174+userDataDir: "/tmp/openclaw-profile",
175+cdpPort: 18800,
176+startedAt: Date.now(),
177+proc: {} as never,
178+headless: true,
179+headlessSource: "request",
180+},
181+});
182+183+const response = await callBasicRouteWithState({
184+query: { profile: "openclaw" },
185+ state,
186+});
187+188+expect(response.statusCode).toBe(200);
189+expect(response.body).toMatchObject({
190+profile: "openclaw",
191+pid: 222,
192+chosenBrowser: "chromium",
193+headless: true,
194+headlessSource: "request",
195+});
196+});
197+129198it("maps existing-session status failures to JSON browser errors", async () => {
130199const response = await callBasicRouteWithState({
131200state: createExistingSessionProfileState({
@@ -158,6 +227,50 @@ describe("basic browser routes", () => {
158227});
159228});
160229230+it("passes valid start headless override to local managed profiles", async () => {
231+const { response, ensureBrowserAvailable } = await callStartRoute({
232+query: { headless: "true" },
233+});
234+235+expect(response.statusCode).toBe(200);
236+expect(response.body).toEqual({ ok: true, profile: "openclaw" });
237+expect(ensureBrowserAvailable).toHaveBeenCalledWith({ headless: true });
238+});
239+240+it("rejects invalid start headless values", async () => {
241+const { response, ensureBrowserAvailable } = await callStartRoute({
242+query: { headless: "maybe" },
243+});
244+245+expect(response.statusCode).toBe(400);
246+expect(response.body).toMatchObject({
247+error: 'Invalid headless value. Use "true" or "false".',
248+});
249+expect(ensureBrowserAvailable).not.toHaveBeenCalled();
250+});
251+252+it("rejects start headless override for existing-session profiles", async () => {
253+const { response, ensureBrowserAvailable } = await callStartRoute({
254+profile: {
255+name: "chrome-live",
256+driver: "existing-session",
257+cdpPort: 0,
258+cdpUrl: "",
259+cdpHost: "",
260+cdpIsLoopback: true,
261+attachOnly: true,
262+},
263+query: { headless: "true" },
264+});
265+266+expect(response.statusCode).toBe(400);
267+expect(response.body).toMatchObject({
268+error:
269+'Headless start override is only supported for locally launched openclaw profiles. Profile "chrome-live" is attach-only, remote, or existing-session.',
270+});
271+expect(ensureBrowserAvailable).not.toHaveBeenCalled();
272+});
273+161274it("treats attach-only profiles as running when transport is available even if page reachability is false", async () => {
162275const response = await callBasicRouteWithState({
163276state: createExistingSessionProfileState({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。