Files
Endoleap/tests/canvas-media.test.mjs
T

49 lines
3.5 KiB
JavaScript

/**
* 旧画布 CanvasElement 类体系的纯图片能力及图标搜索测试,与 headless component registry 的模型定义分开。
* 只操作节点数据、几何和序列化,不挂载 renderer 或读取图片字节。
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import { canvasReducer, decodeCanvas, emptyCanvas, makeCanvasNode } from "../src/lib/canvas.ts";
import { CanvasElement, CardElement, PlainImageElement } from "../src/lib/canvas-elements.ts";
import { resizeFromPointer } from "../src/lib/canvas-geometry.ts";
import { iconCatalog, iconsById, searchIcons } from "../src/components/canvas/icon-catalog.ts";
// PlainImageElement 继承通用变换但不继承 CardElement,比例缩放、名称和 src 可恢复,重命名可撤销。
// YQ== 只是单字节 a 的 data URL:用于来源字段往返,不是可解码图片;尺寸断言来自模型默认值。
test("plain images inherit transforms without card connections and preserve source/name on restore", () => {
const image = new PlainImageElement({ x: 0, y: 0 }, "", "data:image/png;base64,YQ==");
assert(image instanceof CanvasElement); assert.equal(image instanceof CardElement, false);
const node = image.toNode();
assert.equal(node.type, "plainImage"); assert.equal(PlainImageElement.capabilities.connectable, false);
const resized = resizeFromPointer(node, "bottom-right", { x: 320, y: 240 });
assert.equal(resized.width, 640); assert.equal(resized.height, 480);
const renamed = canvasReducer({ past: [], present: { ...emptyCanvas(), nodes: [node] }, future: [] }, { type: "rename-node", id: node.id, title: "参考照片" });
const restored = decodeCanvas(JSON.stringify(renamed.present));
assert.equal(restored.nodes[0].data.title, "参考照片"); assert.equal(restored.nodes[0].data.src, node.data.src);
assert.equal(canvasReducer(renamed, { type: "undo" }).present.nodes[0].data.title, "图片");
});
// emptyCanvas 的 schema 1 快照拒绝连向 plainImage 的旧卡片边;这不是 schema 2 的通用连线规则。
// 两次断言分别隔离非法连线和非法来源:先保持有效 src 测边,再清空 edges 测 javascript: src。
test("plain image sources are required and pure images cannot acquire card connections", () => {
const image = makeCanvasNode("plain-image", { x: 0, y: 0 }, "data:image/png;base64,YQ==");
const card = makeCanvasNode("text", { x: 300, y: 0 });
const document = { ...emptyCanvas(), nodes: [image, card], edges: [{ id: "edge", source: card.id, target: image.id }] };
assert.throws(() => decodeCanvas(JSON.stringify(document)));
image.data.src = "javascript:bad"; document.edges = [];
assert.throws(() => decodeCanvas(JSON.stringify(document)));
});
// 图标 ID 唯一并符合后端字符形状,中文类别与英文词可搜索;当前本地目录同时含 Lucide 和实验性 Tabler。
// 数量下限与两个固定 slug 是回归样本,不证明上游图标库的每个版本都已完整收录,也不请求后端搜索。
test("the complete Lucide catalog has unique backend-compatible identifiers and searchable names", () => {
assert(iconCatalog.length > 2000);
assert.equal(iconsById.size, iconCatalog.length);
for (const icon of iconCatalog) assert.match(icon.name, /^[A-Z][A-Za-z0-9]{0,63}$/);
assert(searchIcons("箭头").length > 45);
assert(searchIcons("arrow left").some((icon) => icon.name === "ArrowLeft"));
assert.equal(iconsById.get("Star").slug, "star");
assert.equal(iconsById.get("WandSparkles").slug, "wand-sparkles");
});