feat(browser): add remote-capable profiles
Co-authored-by: James Groat <james@groat.com>
This commit is contained in:
154
src/browser/profiles-service.test.ts
Normal file
154
src/browser/profiles-service.test.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { resolveBrowserConfig } from "./config.js";
|
||||
import { createBrowserProfilesService } from "./profiles-service.js";
|
||||
import type {
|
||||
BrowserRouteContext,
|
||||
BrowserServerState,
|
||||
} from "./server-context.js";
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
loadConfig: vi.fn(),
|
||||
writeConfigFile: vi.fn(async () => {}),
|
||||
}));
|
||||
|
||||
vi.mock("./trash.js", () => ({
|
||||
movePathToTrash: vi.fn(async (targetPath: string) => targetPath),
|
||||
}));
|
||||
|
||||
vi.mock("./chrome.js", () => ({
|
||||
resolveClawdUserDataDir: vi.fn(() => "/tmp/clawd-test/clawd/user-data"),
|
||||
}));
|
||||
|
||||
import { loadConfig, writeConfigFile } from "../config/config.js";
|
||||
import { resolveClawdUserDataDir } from "./chrome.js";
|
||||
import { movePathToTrash } from "./trash.js";
|
||||
|
||||
function createCtx(resolved: BrowserServerState["resolved"]) {
|
||||
const state: BrowserServerState = {
|
||||
server: null as unknown as BrowserServerState["server"],
|
||||
port: 0,
|
||||
resolved,
|
||||
profiles: new Map(),
|
||||
};
|
||||
|
||||
const ctx = {
|
||||
state: () => state,
|
||||
listProfiles: vi.fn(async () => []),
|
||||
forProfile: vi.fn(() => ({
|
||||
stopRunningBrowser: vi.fn(async () => ({ stopped: true })),
|
||||
})),
|
||||
} as unknown as BrowserRouteContext;
|
||||
|
||||
return { state, ctx };
|
||||
}
|
||||
|
||||
describe("BrowserProfilesService", () => {
|
||||
it("allocates next local port for new profiles", async () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
controlUrl: "http://127.0.0.1:18791",
|
||||
});
|
||||
const { ctx, state } = createCtx(resolved);
|
||||
|
||||
vi.mocked(loadConfig).mockReturnValue({ browser: { profiles: {} } });
|
||||
|
||||
const service = createBrowserProfilesService(ctx);
|
||||
const result = await service.createProfile({ name: "work" });
|
||||
|
||||
expect(result.cdpPort).toBe(18801);
|
||||
expect(result.isRemote).toBe(false);
|
||||
expect(state.resolved.profiles.work?.cdpPort).toBe(18801);
|
||||
expect(writeConfigFile).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("accepts per-profile cdpUrl for remote Chrome", async () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
controlUrl: "http://127.0.0.1:18791",
|
||||
});
|
||||
const { ctx } = createCtx(resolved);
|
||||
|
||||
vi.mocked(loadConfig).mockReturnValue({ browser: { profiles: {} } });
|
||||
|
||||
const service = createBrowserProfilesService(ctx);
|
||||
const result = await service.createProfile({
|
||||
name: "remote",
|
||||
cdpUrl: "http://10.0.0.42:9222",
|
||||
});
|
||||
|
||||
expect(result.cdpUrl).toBe("http://10.0.0.42:9222");
|
||||
expect(result.cdpPort).toBe(9222);
|
||||
expect(result.isRemote).toBe(true);
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
browser: expect.objectContaining({
|
||||
profiles: expect.objectContaining({
|
||||
remote: expect.objectContaining({
|
||||
cdpUrl: "http://10.0.0.42:9222",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("deletes remote profiles without stopping or removing local data", async () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
controlUrl: "http://127.0.0.1:18791",
|
||||
profiles: {
|
||||
remote: { cdpUrl: "http://10.0.0.42:9222", color: "#0066CC" },
|
||||
},
|
||||
});
|
||||
const { ctx } = createCtx(resolved);
|
||||
|
||||
vi.mocked(loadConfig).mockReturnValue({
|
||||
browser: {
|
||||
defaultProfile: "clawd",
|
||||
profiles: {
|
||||
clawd: { cdpPort: 18800, color: "#FF4500" },
|
||||
remote: { cdpUrl: "http://10.0.0.42:9222", color: "#0066CC" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const service = createBrowserProfilesService(ctx);
|
||||
const result = await service.deleteProfile("remote");
|
||||
|
||||
expect(result.deleted).toBe(false);
|
||||
expect(ctx.forProfile).not.toHaveBeenCalled();
|
||||
expect(movePathToTrash).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("deletes local profiles and moves data to Trash", async () => {
|
||||
const resolved = resolveBrowserConfig({
|
||||
controlUrl: "http://127.0.0.1:18791",
|
||||
profiles: {
|
||||
work: { cdpPort: 18801, color: "#0066CC" },
|
||||
},
|
||||
});
|
||||
const { ctx } = createCtx(resolved);
|
||||
|
||||
vi.mocked(loadConfig).mockReturnValue({
|
||||
browser: {
|
||||
defaultProfile: "clawd",
|
||||
profiles: {
|
||||
clawd: { cdpPort: 18800, color: "#FF4500" },
|
||||
work: { cdpPort: 18801, color: "#0066CC" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tempDir = fs.mkdtempSync(path.join("/tmp", "clawd-profile-"));
|
||||
const userDataDir = path.join(tempDir, "work", "user-data");
|
||||
fs.mkdirSync(path.dirname(userDataDir), { recursive: true });
|
||||
vi.mocked(resolveClawdUserDataDir).mockReturnValue(userDataDir);
|
||||
|
||||
const service = createBrowserProfilesService(ctx);
|
||||
const result = await service.deleteProfile("work");
|
||||
|
||||
expect(result.deleted).toBe(true);
|
||||
expect(movePathToTrash).toHaveBeenCalledWith(path.dirname(userDataDir));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user