Fix browser tool workspace context
This commit is contained in:
@@ -1495,6 +1495,7 @@ describe("executeAutomationCommand", () => {
|
||||
it("sets workspace files on a file input ref through CDP", async () => {
|
||||
const debugCommands: Array<{ command: string; params?: Record<string, unknown> }> = [];
|
||||
const workspaceRoot = "/tmp/paseo-workspace-a";
|
||||
const workspaceOpaqueId = "wks_workspace_a";
|
||||
const tab = fakeTab({
|
||||
id: 25,
|
||||
executeJavaScript: async () =>
|
||||
@@ -1519,18 +1520,20 @@ describe("executeAutomationCommand", () => {
|
||||
},
|
||||
});
|
||||
const registry = createRegistry({
|
||||
getWorkspaceActiveTabContents: (workspaceId) =>
|
||||
workspaceId === workspaceRoot ? tab : null,
|
||||
getWorkspaceActiveBrowserId: (workspaceId) => (workspaceId === workspaceRoot ? "a" : null),
|
||||
getBrowserWorkspaceId: (id) => (id === "a" ? workspaceRoot : null),
|
||||
getWorkspaceActiveTabContents: (requestedWorkspaceId) =>
|
||||
requestedWorkspaceId === workspaceOpaqueId ? tab : null,
|
||||
getWorkspaceActiveBrowserId: (requestedWorkspaceId) =>
|
||||
requestedWorkspaceId === workspaceOpaqueId ? "a" : null,
|
||||
getBrowserWorkspaceId: (id) => (id === "a" ? workspaceOpaqueId : null),
|
||||
});
|
||||
const snapshotEngine = new BrowserSnapshotEngine();
|
||||
const snapshot = await executeAutomationCommand(
|
||||
{
|
||||
type: "browser.automation.execute.request",
|
||||
requestId: "r-snapshot-upload",
|
||||
workspaceId: workspaceRoot,
|
||||
command: { command: "snapshot", args: { workspaceId: workspaceRoot } },
|
||||
cwd: workspaceRoot,
|
||||
workspaceId: workspaceOpaqueId,
|
||||
command: { command: "snapshot", args: { workspaceId: workspaceOpaqueId } },
|
||||
},
|
||||
registry,
|
||||
{ snapshotEngine },
|
||||
@@ -1548,10 +1551,11 @@ describe("executeAutomationCommand", () => {
|
||||
{
|
||||
type: "browser.automation.execute.request",
|
||||
requestId: "r-upload",
|
||||
workspaceId: workspaceRoot,
|
||||
cwd: workspaceRoot,
|
||||
workspaceId: workspaceOpaqueId,
|
||||
command: {
|
||||
command: "upload",
|
||||
args: { workspaceId: workspaceRoot, ref, filePaths: ["uploads/file.txt"] },
|
||||
args: { workspaceId: workspaceOpaqueId, ref, filePaths: ["uploads/file.txt"] },
|
||||
},
|
||||
},
|
||||
registry,
|
||||
@@ -1576,6 +1580,7 @@ describe("executeAutomationCommand", () => {
|
||||
|
||||
it("rejects upload paths outside the workspace", async () => {
|
||||
const workspaceRoot = "/tmp/paseo-workspace-a";
|
||||
const workspaceOpaqueId = "wks_workspace_a";
|
||||
const debugCommands: Array<{ command: string; params?: Record<string, unknown> }> = [];
|
||||
const tab = fakeTab({
|
||||
id: 26,
|
||||
@@ -1601,18 +1606,20 @@ describe("executeAutomationCommand", () => {
|
||||
},
|
||||
});
|
||||
const registry = createRegistry({
|
||||
getWorkspaceActiveTabContents: (workspaceId) =>
|
||||
workspaceId === workspaceRoot ? tab : null,
|
||||
getWorkspaceActiveBrowserId: (workspaceId) => (workspaceId === workspaceRoot ? "a" : null),
|
||||
getBrowserWorkspaceId: (id) => (id === "a" ? workspaceRoot : null),
|
||||
getWorkspaceActiveTabContents: (requestedWorkspaceId) =>
|
||||
requestedWorkspaceId === workspaceOpaqueId ? tab : null,
|
||||
getWorkspaceActiveBrowserId: (requestedWorkspaceId) =>
|
||||
requestedWorkspaceId === workspaceOpaqueId ? "a" : null,
|
||||
getBrowserWorkspaceId: (id) => (id === "a" ? workspaceOpaqueId : null),
|
||||
});
|
||||
const snapshotEngine = new BrowserSnapshotEngine();
|
||||
const snapshot = await executeAutomationCommand(
|
||||
{
|
||||
type: "browser.automation.execute.request",
|
||||
requestId: "r-snapshot-upload-outside",
|
||||
workspaceId: workspaceRoot,
|
||||
command: { command: "snapshot", args: { workspaceId: workspaceRoot } },
|
||||
cwd: workspaceRoot,
|
||||
workspaceId: workspaceOpaqueId,
|
||||
command: { command: "snapshot", args: { workspaceId: workspaceOpaqueId } },
|
||||
},
|
||||
registry,
|
||||
{ snapshotEngine },
|
||||
@@ -1629,10 +1636,11 @@ describe("executeAutomationCommand", () => {
|
||||
{
|
||||
type: "browser.automation.execute.request",
|
||||
requestId: "r-upload-outside",
|
||||
workspaceId: workspaceRoot,
|
||||
cwd: workspaceRoot,
|
||||
workspaceId: workspaceOpaqueId,
|
||||
command: {
|
||||
command: "upload",
|
||||
args: { workspaceId: workspaceRoot, ref, filePaths: ["../secret.txt"] },
|
||||
args: { workspaceId: workspaceOpaqueId, ref, filePaths: ["../secret.txt"] },
|
||||
},
|
||||
},
|
||||
registry,
|
||||
@@ -1652,6 +1660,86 @@ describe("executeAutomationCommand", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects upload when the request has no cwd", async () => {
|
||||
const workspaceOpaqueId = "wks_workspace_a";
|
||||
const debugCommands: Array<{ command: string; params?: Record<string, unknown> }> = [];
|
||||
const tab = fakeTab({
|
||||
id: 28,
|
||||
executeJavaScript: async () =>
|
||||
JSON.stringify([
|
||||
{
|
||||
role: "textbox",
|
||||
tagName: "input",
|
||||
text: "",
|
||||
selector: "#file",
|
||||
attributes: { id: "file", type: "file" },
|
||||
},
|
||||
]),
|
||||
sendDebugCommand: async (command, params) => {
|
||||
debugCommands.push({ command, params });
|
||||
if (command === "DOM.getDocument") {
|
||||
return { root: { nodeId: 1 } };
|
||||
}
|
||||
if (command === "DOM.querySelector") {
|
||||
return { nodeId: 2 };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
});
|
||||
const registry = createRegistry({
|
||||
getWorkspaceActiveTabContents: (requestedWorkspaceId) =>
|
||||
requestedWorkspaceId === workspaceOpaqueId ? tab : null,
|
||||
getWorkspaceActiveBrowserId: (requestedWorkspaceId) =>
|
||||
requestedWorkspaceId === workspaceOpaqueId ? "a" : null,
|
||||
getBrowserWorkspaceId: (id) => (id === "a" ? workspaceOpaqueId : null),
|
||||
});
|
||||
const snapshotEngine = new BrowserSnapshotEngine();
|
||||
const snapshot = await executeAutomationCommand(
|
||||
{
|
||||
type: "browser.automation.execute.request",
|
||||
requestId: "r-snapshot-upload-missing-cwd",
|
||||
workspaceId: workspaceOpaqueId,
|
||||
command: { command: "snapshot", args: { workspaceId: workspaceOpaqueId } },
|
||||
},
|
||||
registry,
|
||||
{ snapshotEngine },
|
||||
);
|
||||
if (!snapshot.ok || snapshot.result.command !== "snapshot") {
|
||||
throw new Error("snapshot failed");
|
||||
}
|
||||
const ref = snapshot.result.elements[0]?.ref;
|
||||
if (!ref) {
|
||||
throw new Error("missing upload ref");
|
||||
}
|
||||
|
||||
const result = await executeAutomationCommand(
|
||||
{
|
||||
type: "browser.automation.execute.request",
|
||||
requestId: "r-upload-missing-cwd",
|
||||
workspaceId: workspaceOpaqueId,
|
||||
command: {
|
||||
command: "upload",
|
||||
args: { workspaceId: workspaceOpaqueId, ref, filePaths: ["uploads/file.txt"] },
|
||||
},
|
||||
},
|
||||
registry,
|
||||
{ snapshotEngine },
|
||||
);
|
||||
|
||||
expect(debugCommands).not.toContainEqual(
|
||||
expect.objectContaining({ command: "DOM.setFileInputFiles" }),
|
||||
);
|
||||
expect(result).toEqual({
|
||||
requestId: "r-upload-missing-cwd",
|
||||
ok: false,
|
||||
error: {
|
||||
code: "browser_unsupported",
|
||||
message: "browser_upload requires request cwd",
|
||||
retryable: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("navigation", () => {
|
||||
|
||||
@@ -319,6 +319,7 @@ const commandHandlers: Partial<Record<BrowserAutomationCommand["command"], Comma
|
||||
const uploadCommand = command as Extract<BrowserAutomationCommand, { command: "upload" }>;
|
||||
return executeUpload(
|
||||
requestId,
|
||||
request.cwd,
|
||||
workspaceId,
|
||||
uploadCommand.args.browserId ?? request.browserId,
|
||||
{ ref: uploadCommand.args.ref, filePaths: uploadCommand.args.filePaths },
|
||||
@@ -1273,6 +1274,7 @@ function isAllowedPageUrl(value: string): boolean {
|
||||
|
||||
async function executeUpload(
|
||||
requestId: string,
|
||||
cwd: string | undefined,
|
||||
workspaceId: string | undefined,
|
||||
browserId: string | undefined,
|
||||
input: { ref: string; filePaths: string[] },
|
||||
@@ -1309,9 +1311,9 @@ async function executeUpload(
|
||||
if (typeof queried.nodeId !== "number" || queried.nodeId <= 0) {
|
||||
return staleRefFailure(requestId, input.ref);
|
||||
}
|
||||
const workspaceRoot = resolveUploadWorkspaceRoot({ workspaceId, target, registry });
|
||||
const workspaceRoot = resolveUploadWorkspaceRoot(cwd);
|
||||
if (!workspaceRoot) {
|
||||
return fail(requestId, "browser_unsupported", "browser_upload requires a workspace target");
|
||||
return fail(requestId, "browser_unsupported", "browser_upload requires request cwd");
|
||||
}
|
||||
const filePaths = resolveWorkspaceFilePaths(input.filePaths, workspaceRoot);
|
||||
if (!filePaths) {
|
||||
@@ -1338,14 +1340,8 @@ async function executeUpload(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveUploadWorkspaceRoot(params: {
|
||||
workspaceId: string | undefined;
|
||||
target: ResolvedTabTarget;
|
||||
registry: BrowserRegistry;
|
||||
}): string | null {
|
||||
const workspaceRoot =
|
||||
params.workspaceId ?? params.registry.getBrowserWorkspaceId(params.target.browserId);
|
||||
return workspaceRoot ? resolvePath(workspaceRoot) : null;
|
||||
function resolveUploadWorkspaceRoot(cwd: string | undefined): string | null {
|
||||
return cwd ? resolvePath(cwd) : null;
|
||||
}
|
||||
|
||||
function resolveWorkspaceFilePaths(filePaths: string[], workspaceRoot: string): string[] | null {
|
||||
|
||||
Reference in New Issue
Block a user