Files
moteva/frontend/tests/canvasNodeClipboard.test.mjs
T

117 lines
4.4 KiB
JavaScript
Raw Normal View History

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import {
canvasNodeClipboardMime,
canvasNodeFromClipboardData,
canvasNodeFromClipboardItems,
createPastedCanvasNode,
parseCanvasNodeClipboard,
serializeCanvasNodeClipboard,
serializeCanvasNodeClipboardSvg
} from "../src/ui/pages/CanvasWorkspace/canvas/canvasNodeClipboard.ts";
const pathNode = {
id: "path-1",
type: "frame",
title: "Circle",
x: 100,
y: 200,
width: 300,
height: 180,
content: JSON.stringify({ kind: "path", pathData: [1, 0, 0.5, 5, 0.25, 0, 0.75, 1, 1, 0.5, 11] }),
tone: "shape",
status: "draft",
layerRole: "shape",
fillColor: "transparent",
strokeColor: "#111827",
strokeWidth: 2
};
test("round-trips editable path nodes through the internal clipboard format", () => {
const parsed = parseCanvasNodeClipboard(serializeCanvasNodeClipboard(pathNode));
assert.deepEqual(parsed, pathNode);
assert.equal(JSON.parse(parsed.content).kind, "path");
});
test("creates a pasted path node at the clipboard anchor without rasterizing it", () => {
const pasted = createPastedCanvasNode(pathNode, "path-copy", { x: 800, y: 600 });
assert.equal(pasted.id, "path-copy");
assert.equal(pasted.type, "frame");
assert.equal(pasted.layerRole, "shape");
assert.equal(pasted.title, pathNode.title);
assert.equal(pasted.status, pathNode.status);
assert.equal(JSON.parse(pasted.content).kind, "path");
assert.equal(pasted.x, 650);
assert.equal(pasted.y, 510);
});
test("prefers internal path data when the clipboard also contains a PNG preview", async () => {
const serialized = serializeCanvasNodeClipboard(pathNode);
const dataTransfer = {
files: [new File(["png"], "preview.png", { type: "image/png" })],
getData: (type) => (type === canvasNodeClipboardMime ? serialized : "")
};
const clipboardItem = {
types: ["image/png", canvasNodeClipboardMime],
getType: async (type) => new Blob([type === canvasNodeClipboardMime ? serialized : "png"], { type })
};
assert.deepEqual(canvasNodeFromClipboardData(dataTransfer), pathNode);
assert.deepEqual(await canvasNodeFromClipboardItems([clipboardItem]), pathNode);
});
test("restores path data from the standard SVG clipboard fallback", () => {
const svg = serializeCanvasNodeClipboardSvg(pathNode, '<svg xmlns="http://www.w3.org/2000/svg"><path d="M0 0"/></svg>');
const dataTransfer = {
getData: (type) => (type === "text/plain" ? svg : "")
};
assert.match(svg, /^<svg data-fluxo-canvas-node=/);
assert.deepEqual(canvasNodeFromClipboardData(dataTransfer), pathNode);
});
test("preserves raster source formats and node properties when pasted", () => {
for (const extension of ["png", "webp"]) {
const source = {
...pathNode,
id: `${extension}-1`,
type: "image",
title: `source.${extension}`,
content: `https://assets.example/source.${extension}`,
renderContent: `https://assets.example/render.${extension}`,
layerRole: "image",
imageAdjustments: JSON.stringify({ brightness: 12 })
};
const pasted = createPastedCanvasNode(parseCanvasNodeClipboard(serializeCanvasNodeClipboard(source)), `${extension}-copy`, { x: 500, y: 400 });
assert.deepEqual(pasted, {
...source,
id: `${extension}-copy`,
x: 350,
y: 310
});
}
});
test("rejects unrelated or malformed clipboard text", () => {
assert.equal(parseCanvasNodeClipboard("hello"), null);
assert.equal(parseCanvasNodeClipboard('{"version":1}'), null);
});
test("routes context-menu and keyboard copy-paste through the lossless node clipboard", async () => {
const source = await readFile(new URL("../src/ui/pages/CanvasWorkspace/index.tsx", import.meta.url), "utf8");
const dataTransferPaste = source.slice(source.indexOf("const pasteFromClipboardData"), source.indexOf("const pasteFromSystemClipboard"));
assert.ok(dataTransferPaste.indexOf("canvasNodeFromClipboardData") < dataTransferPaste.indexOf("latestDataTransferImageFile"));
assert.match(source, /copy: \(\) => copyNode\(node\)/);
assert.match(source, /paste: \(\) => void pasteFromSystemClipboard\(\)/);
assert.match(source, /key === "c"\) return void run\(selectedHandlers\?\.copy\)/);
assert.match(source, /pasteFromClipboardData\(event\.clipboardData\)/);
assert.match(source, /writeClipboardBlob\(blob, node\)/);
assert.match(source, /writeClipboardTextNode\(node\)/);
});