electron/desktop-workspace.node-test.mjs
Rama main · réplica de lectura del repositorio original.
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import test from "node:test";
const require = createRequire(import.meta.url);
const {
createDesktopWorkspaceManager,
desktopWorkspaceUrl,
normalizeDesktopWorkspaceBounds,
} = require("./desktop-workspace.cjs");
test("workspace URLs stay loopback and force the requested noVNC input mode", () => {
const watch = desktopWorkspaceUrl(
"http://127.0.0.1:6080/vnc.html#autoconnect=true&resize=scale&password=secret123",
);
assert.equal(watch.hostname, "127.0.0.1");
assert.equal(watch.hash.includes("autoconnect=true"), true);
assert.equal(watch.hash.includes("resize=scale"), true);
assert.equal(watch.hash.includes("password=secret123"), true);
assert.equal(watch.hash.includes("view_only=true"), true);
const interactive = desktopWorkspaceUrl(watch.toString(), true);
assert.equal(interactive.hash.includes("view_only=false"), true);
assert.equal(interactive.hash.includes("view_only=true"), false);
assert.doesNotThrow(() => desktopWorkspaceUrl("https://localhost:6080/vnc.html"));
assert.doesNotThrow(() => desktopWorkspaceUrl("http://[::1]:6080/vnc.html"));
assert.throws(() => desktopWorkspaceUrl("https://desktop.example/vnc.html"), /loopback/);
});
test("workspace URL errors never echo a secret-bearing input", () => {
const secret = "never-print-this";
assert.throws(
() => desktopWorkspaceUrl(`https://desktop.example/vnc.html#password=${secret}`),
(error) => error instanceof Error && !error.message.includes(secret),
);
});
test("workspace bounds reject malformed values and clamp to owner content", () => {
assert.deepEqual(
normalizeDesktopWorkspaceBounds({ x: 901, y: -5, width: 500, height: 900 }, [1000, 800]),
{ x: 901, y: 0, width: 99, height: 800 },
);
assert.throws(
() => normalizeDesktopWorkspaceBounds({ x: 0, y: 0, width: "20", height: 20 }, [1000, 800]),
/invalid/,
);
assert.throws(
() => normalizeDesktopWorkspaceBounds({ x: 0, y: 0, width: 0, height: 20 }, [1000, 800]),
/empty/,
);
});
function managerFixture() {
const notifications = [];
const views = [];
const children = [];
class FakeWebContents {
constructor() {
this.url = "";
this.closed = false;
this.handlers = new Map();
this.session = {
setPermissionCheckHandler: (handler) => { this.permissionCheck = handler; },
setPermissionRequestHandler: (handler) => { this.permissionRequest = handler; },
};
}
setWindowOpenHandler(handler) { this.windowOpenHandler = handler; }
on(name, handler) { this.handlers.set(name, handler); }
async loadURL(url) {
if (this.loadHook) await this.loadHook(url);
this.url = url;
}
getURL() { return this.url; }
isDestroyed() { return this.closed; }
close() { this.closed = true; }
}
class FakeView {
constructor(options) {
this.options = options;
this.webContents = new FakeWebContents();
this.visible = false;
this.bounds = null;
views.push(this);
}
setBounds(bounds) { this.bounds = bounds; }
setVisible(visible) { this.visible = visible; }
}
const owner = {
contentView: {
addChildView(view) { children.push(view); },
removeChildView(view) {
const index = children.indexOf(view);
if (index >= 0) children.splice(index, 1);
},
},
getContentSize: () => [1200, 800],
isDestroyed: () => false,
};
const manager = createDesktopWorkspaceManager({
owner,
createView: (options) => new FakeView(options),
notify: (state) => notifications.push(state),
partitionPrefix: "openmausbot-test",
});
const open = (contextId, port, bounds = { x: 10, y: 20, width: 500, height: 400 }) =>
manager.open({
contextId,
url: `http://127.0.0.1:${port}/vnc.html#autoconnect=true&password=secret-${port}`,
title: contextId,
bounds,
});
return { children, manager, notifications, open, views };
}
test("manager keeps two isolated watch-only views and rejects duplicates or a third", async () => {
const { children, manager, open, views } = managerFixture();
await open("left", 6080);
await open("right", 6081);
assert.equal(manager.size(), 2);
assert.equal(children.length, 2);
assert.notEqual(
views[0].options.webPreferences.partition,
views[1].options.webPreferences.partition,
);
assert.equal(views.every((view) => view.webContents.url.includes("view_only=true")), true);
assert.equal(views.every((view) => view.options.webPreferences.sandbox === true), true);
assert.equal(views.every((view) => view.options.webPreferences.contextIsolation === true), true);
assert.equal(views.every((view) => view.options.webPreferences.nodeIntegration === false), true);
assert.equal(views.every((view) => view.options.webPreferences.webSecurity === true), true);
assert.equal(
views.every((view) => view.options.webPreferences.allowRunningInsecureContent === false),
true,
);
assert.equal(views.every((view) => view.webContents.permissionCheck() === false), true);
assert.equal(views.every((view) => view.webContents.windowOpenHandler().action === "deny"), true);
assert.equal(
views.every((view) => !view.options.webPreferences.partition.startsWith("persist:")),
true,
);
let denied = null;
views[0].webContents.permissionRequest(null, "camera", (allowed) => { denied = allowed; });
assert.equal(denied, false);
let prevented = false;
views[0].webContents.handlers.get("will-navigate")(
{ preventDefault() { prevented = true; } },
"https://example.com/steal",
);
assert.equal(prevented, true);
prevented = false;
views[0].webContents.handlers.get("will-navigate")(
{ preventDefault() { prevented = true; } },
"http://127.0.0.1:6080/another-local-path",
);
assert.equal(prevented, false);
await assert.rejects(() => open("left", 6082), /already open/);
await assert.rejects(() => open("third", 6082), /Only two/);
manager.close("right");
await assert.rejects(() => open("third", 6080), /already open/);
});
test("manager lays out panes and demotes the old pane before promoting the new one", async () => {
const { manager, open, views } = managerFixture();
await open("left", 6080);
await open("right", 6081);
manager.layout([
{ contextId: "left", bounds: { x: 20, y: 60, width: 550, height: 600 }, visible: true },
{ contextId: "right", bounds: { x: 590, y: 60, width: 550, height: 600 }, visible: true },
]);
assert.equal(views[0].visible, true);
assert.deepEqual(views[1].bounds, { x: 590, y: 60, width: 550, height: 600 });
await manager.setInteractive("left");
assert.equal(views[0].webContents.url.includes("view_only=false"), true);
assert.equal(views[1].webContents.url.includes("view_only=true"), true);
await manager.setInteractive("right");
assert.equal(views[0].webContents.url.includes("view_only=true"), true);
assert.equal(views[1].webContents.url.includes("view_only=false"), true);
await manager.setInteractive(null);
assert.equal(views.every((view) => view.webContents.url.includes("view_only=true")), true);
});
test("manager serializes overlapping demotion and promotion calls", async () => {
const { manager, open, views } = managerFixture();
await open("left", 6080);
await open("right", 6081);
await manager.setInteractive("left");
let finishDemotion;
const demotionGate = new Promise((resolve) => { finishDemotion = resolve; });
let rightPromotionStarted = false;
views[0].webContents.loadHook = async (url) => {
if (url.includes("view_only=true")) await demotionGate;
};
views[1].webContents.loadHook = async (url) => {
if (url.includes("view_only=false")) rightPromotionStarted = true;
};
const demote = manager.setInteractive(null);
await new Promise((resolve) => setImmediate(resolve));
const promote = manager.setInteractive("right");
await new Promise((resolve) => setImmediate(resolve));
assert.equal(rightPromotionStarted, false);
assert.equal(views[0].webContents.url.includes("view_only=false"), true);
finishDemotion();
await Promise.all([demote, promote]);
assert.equal(views[0].webContents.url.includes("view_only=true"), true);
assert.equal(views[1].webContents.url.includes("view_only=false"), true);
});
test("manager preserves one controller across reverse-order queued switches", async () => {
const { manager, open, views } = managerFixture();
await open("left", 6080);
await open("right", 6081);
await manager.setInteractive("left");
let finishDemotion;
const demotionGate = new Promise((resolve) => { finishDemotion = resolve; });
views[0].webContents.loadHook = async (url) => {
if (url.includes("view_only=true")) await demotionGate;
};
const switchRight = manager.setInteractive("right");
await new Promise((resolve) => setImmediate(resolve));
const switchBackLeft = manager.setInteractive("left");
finishDemotion();
await Promise.all([switchRight, switchBackLeft]);
assert.equal(views[0].webContents.url.includes("view_only=false"), true);
assert.equal(views[1].webContents.url.includes("view_only=true"), true);
});
test("queued interaction cannot promote a replacement pane with a reused context id", async () => {
const { manager, open, views } = managerFixture();
await open("left", 6080);
await open("right", 6081);
await manager.setInteractive("left");
let finishDemotion;
const demotionGate = new Promise((resolve) => { finishDemotion = resolve; });
views[0].webContents.loadHook = async (url) => {
if (url.includes("view_only=true")) await demotionGate;
};
const demote = manager.setInteractive(null);
await new Promise((resolve) => setImmediate(resolve));
const stalePromotion = manager.setInteractive("right");
manager.close("right");
await open("right", 6082);
finishDemotion();
await demote;
await assert.rejects(stalePromotion, /not open/);
assert.equal(views[2].webContents.url.includes("view_only=true"), true);
});
test("manager fails closed when an interactive reload derives from an invalid URL", async () => {
const { manager, open, views } = managerFixture();
await open("left", 6080);
views[0].webContents.url = "https://desktop.example/vnc.html#password=never-print-this";
await assert.rejects(
manager.setInteractive("left"),
(error) => error instanceof Error && !error.message.includes("never-print-this"),
);
assert.equal(manager.size(), 0);
assert.equal(views[0].webContents.closed, true);
});
test("manager does not report a pane ready after it closes during open", async () => {
const { manager, notifications, open } = managerFixture();
const pending = open("left", 6080);
manager.close("left");
const state = await pending;
assert.deepEqual(state, {
contextId: "left",
open: false,
status: "closed",
interactive: false,
});
assert.equal(notifications.at(-1)?.status, "closed");
assert.equal(manager.size(), 0);
});
test("manager closes panes independently and emits no viewer URL", async () => {
const { children, manager, notifications, open, views } = managerFixture();
await open("left", 6080);
await open("right", 6081);
manager.close("left");
assert.equal(children.length, 1);
assert.equal(views[0].webContents.closed, true);
assert.equal(views[1].webContents.closed, false);
manager.closeAll();
assert.equal(children.length, 0);
assert.equal(JSON.stringify(notifications).includes("password="), false);
assert.equal(JSON.stringify(notifications).includes("127.0.0.1"), false);
});