mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-13 03:46:05 +00:00
175 lines
5.5 KiB
TypeScript
175 lines
5.5 KiB
TypeScript
// Canvas tests cover cli plugin behavior.
|
|
import { Command } from "commander";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { registerNodesCanvasCommands, type CanvasCliDependencies } from "./cli.js";
|
|
|
|
function createCanvasCliDeps() {
|
|
const writtenFiles: Array<{ filePath: string; base64: string }> = [];
|
|
const runtime = {
|
|
log: vi.fn(),
|
|
error: vi.fn(),
|
|
exit: vi.fn((code: number) => {
|
|
throw new Error(`exit ${code}`);
|
|
}),
|
|
writeJson: vi.fn(),
|
|
};
|
|
const deps: CanvasCliDependencies = {
|
|
defaultRuntime: runtime,
|
|
nodesCallOpts: (cmd) =>
|
|
cmd
|
|
.option("--url <url>", "Gateway WebSocket URL")
|
|
.option("--token <token>", "Gateway token")
|
|
.option("--timeout <ms>", "Timeout in ms", "10000")
|
|
.option("--json", "Output JSON", false),
|
|
runNodesCommand: async (_label, action) => {
|
|
await action();
|
|
},
|
|
getNodesTheme: () => ({ ok: (value) => value }),
|
|
parseTimeoutMs: (raw) => (typeof raw === "string" ? Number.parseInt(raw, 10) : undefined),
|
|
resolveNodeId: async (opts) => opts.node ?? "ios-node",
|
|
buildNodeInvokeParams: ({ nodeId, command, params, timeoutMs }) => ({
|
|
nodeId,
|
|
command,
|
|
params,
|
|
...(typeof timeoutMs === "number" ? { timeoutMs } : {}),
|
|
}),
|
|
callGatewayCli: vi.fn(async () => ({
|
|
payload: {
|
|
format: "png",
|
|
base64: "aGk=",
|
|
},
|
|
})),
|
|
writeBase64ToFile: async (filePath, base64) => {
|
|
writtenFiles.push({ filePath, base64 });
|
|
},
|
|
shortenHomePath: (filePath) => filePath,
|
|
};
|
|
return { deps, runtime, writtenFiles };
|
|
}
|
|
|
|
describe("canvas CLI", () => {
|
|
it("registers under nodes and captures a snapshot media path", async () => {
|
|
const program = new Command();
|
|
program.exitOverride();
|
|
const nodes = program.command("nodes");
|
|
const { deps, runtime, writtenFiles } = createCanvasCliDeps();
|
|
|
|
registerNodesCanvasCommands(nodes, deps);
|
|
await program.parseAsync(["nodes", "canvas", "snapshot", "--node", "ios-node"], {
|
|
from: "user",
|
|
});
|
|
|
|
expect(deps.callGatewayCli).toHaveBeenCalledTimes(1);
|
|
expect(deps.callGatewayCli).toHaveBeenCalledWith(
|
|
"node.invoke",
|
|
{
|
|
node: "ios-node",
|
|
format: "jpg",
|
|
timeout: "10000",
|
|
json: false,
|
|
invokeTimeout: "20000",
|
|
},
|
|
{
|
|
nodeId: "ios-node",
|
|
command: "canvas.snapshot",
|
|
params: {
|
|
format: "jpeg",
|
|
maxWidth: undefined,
|
|
quality: undefined,
|
|
},
|
|
timeoutMs: 20000,
|
|
},
|
|
);
|
|
expect(writtenFiles).toHaveLength(1);
|
|
const [writtenFile] = writtenFiles;
|
|
if (!writtenFile) {
|
|
throw new Error("Expected canvas snapshot file");
|
|
}
|
|
expect(writtenFile.filePath).toMatch(/openclaw-canvas-snapshot-.*\.png$/);
|
|
expect(writtenFile.base64).toBe("aGk=");
|
|
expect(runtime.log).toHaveBeenCalledTimes(1);
|
|
const savedPath = runtime.log.mock.calls[0]?.[0];
|
|
expect(savedPath?.startsWith("MEDIA:")).toBe(false);
|
|
expect(savedPath?.endsWith(".png")).toBe(true);
|
|
});
|
|
|
|
it("rejects node-controlled snapshot formats before writing", async () => {
|
|
const program = new Command();
|
|
program.exitOverride();
|
|
const nodes = program.command("nodes");
|
|
const { deps, writtenFiles } = createCanvasCliDeps();
|
|
vi.mocked(deps.callGatewayCli).mockResolvedValueOnce({
|
|
payload: {
|
|
format: "/../../target.sh",
|
|
base64: "aGk=",
|
|
},
|
|
});
|
|
|
|
registerNodesCanvasCommands(nodes, deps);
|
|
|
|
await expect(
|
|
program.parseAsync(["nodes", "canvas", "snapshot", "--node", "ios-node"], {
|
|
from: "user",
|
|
}),
|
|
).rejects.toThrow(/invalid canvas\.snapshot payload/i);
|
|
expect(writtenFiles).toHaveLength(0);
|
|
});
|
|
|
|
it("rejects unsupported snapshot formats before invoking the node", async () => {
|
|
const program = new Command();
|
|
program.exitOverride();
|
|
const nodes = program.command("nodes");
|
|
const { deps, writtenFiles } = createCanvasCliDeps();
|
|
|
|
registerNodesCanvasCommands(nodes, deps);
|
|
|
|
await expect(
|
|
program.parseAsync(["nodes", "canvas", "snapshot", "--node", "ios-node", "--format", "gif"], {
|
|
from: "user",
|
|
}),
|
|
).rejects.toThrow(/invalid format: gif/i);
|
|
expect(deps.callGatewayCli).not.toHaveBeenCalled();
|
|
expect(writtenFiles).toHaveLength(0);
|
|
});
|
|
|
|
it.each([
|
|
["--max-width", "640px", "--max-width must be a positive integer."],
|
|
["--quality", "0.8x", "--quality must be a number."],
|
|
])("rejects partial numeric snapshot %s values", async (flag, value, message) => {
|
|
const program = new Command();
|
|
program.exitOverride();
|
|
const nodes = program.command("nodes");
|
|
const { deps } = createCanvasCliDeps();
|
|
|
|
registerNodesCanvasCommands(nodes, deps);
|
|
|
|
await expect(
|
|
program.parseAsync(["nodes", "canvas", "snapshot", "--node", "ios-node", flag, value], {
|
|
from: "user",
|
|
}),
|
|
).rejects.toThrow(message);
|
|
expect(deps.callGatewayCli).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it.each([
|
|
["--x", "1x"],
|
|
["--y", "2px"],
|
|
["--width", "800wide"],
|
|
["--height", "600tall"],
|
|
])("rejects partial numeric present %s values", async (flag, value) => {
|
|
const program = new Command();
|
|
program.exitOverride();
|
|
const nodes = program.command("nodes");
|
|
const { deps } = createCanvasCliDeps();
|
|
|
|
registerNodesCanvasCommands(nodes, deps);
|
|
|
|
await expect(
|
|
program.parseAsync(["nodes", "canvas", "present", "--node", "ios-node", flag, value], {
|
|
from: "user",
|
|
}),
|
|
).rejects.toThrow(`${flag} must be a number.`);
|
|
expect(deps.callGatewayCli).not.toHaveBeenCalled();
|
|
});
|
|
});
|