Files
MorphDoc/packages/markdown-echarts/tests/browser-render.test.ts
T
SkyJourney 92a5bfd016 feat: 扩展 ECharts 第二阶段图表支持
新增 tree、treemap、sunburst、graph、sankey、chord、funnel、gauge 和 pictorialBar 系列,并注册对应的 ECharts 按需模块。

完善层级与关系数据校验、节点和边数量限制、默认布局及错误占位规则,同时补充默认 Markdown 与 README 示例。

静态渲染全局关闭 ECharts 动画,修复 Treemap 标签冻结在透明动画帧的问题,并完成快速预览、精确 PDF、容器镜像和 147 项全项目测试验证。
2026-07-27 17:47:28 +08:00

195 lines
5.0 KiB
TypeScript

// @vitest-environment happy-dom
import MarkdownIt from "markdown-it";
import { describe, expect, it, vi } from "vitest";
import {
markdownItECharts
} from "../src/index.js";
import {
disposeEChartsBlocks,
renderEChartsBlocks,
type EChartsEngine,
type EChartsInstanceLike
} from "../src/browser.js";
function createDocumentWithChart() {
const markdown = new MarkdownIt().use(markdownItECharts);
document.body.innerHTML = markdown.render(`
\`\`\`echarts
version: 1
height: 80mm
caption: 测试图表
option:
xAxis:
type: category
data: [A, B]
yAxis:
type: value
series:
- type: bar
data: [1, 2]
\`\`\`
`);
}
function fakeEngine(
configureSvg?: (svg: SVGSVGElement) => void
) {
const dispose = vi.fn();
const resize = vi.fn();
const setOption = vi.fn();
const init = vi.fn(
(container: HTMLElement): EChartsInstanceLike => {
dispose.mockImplementation(() => container.replaceChildren());
const handlers = new Map<string, () => void>();
return {
setOption(option, setOptionOptions) {
setOption(option, setOptionOptions);
const svg = document.createElementNS(
"http://www.w3.org/2000/svg",
"svg"
);
svg.setAttribute("viewBox", "0 0 400 300");
const text = document.createElementNS(
"http://www.w3.org/2000/svg",
"text"
);
text.textContent = "测试图表";
svg.append(text);
configureSvg?.(svg);
container.replaceChildren(svg);
handlers.get("finished")?.();
},
on(eventName, handler) {
handlers.set(eventName, handler);
},
off(eventName) {
handlers.delete(eventName);
},
resize,
dispose
};
}
);
return {
engine: { init } satisfies EChartsEngine,
init,
dispose,
resize,
setOption
};
}
describe("浏览器 ECharts 渲染", () => {
it("生成安全的内联 SVG 并释放实例", async () => {
createDocumentWithChart();
const { engine, init, dispose } = fakeEngine();
const outcomes = await renderEChartsBlocks(document, {
engine,
outputMode: "inline-svg"
});
expect(outcomes).toHaveLength(1);
expect(outcomes[0]?.success).toBe(true);
expect(init).toHaveBeenCalledWith(
expect.any(HTMLElement),
undefined,
expect.objectContaining({ renderer: "svg" })
);
expect(document.querySelector(".md-echarts-host svg")).not.toBeNull();
expect(dispose).toHaveBeenCalledOnce();
expect(
document.querySelector(".md-echarts")?.getAttribute(
"data-echarts-pending"
)
).toBeNull();
});
it("全局及系列级动画始终关闭", async () => {
createDocumentWithChart();
const { engine, setOption } = fakeEngine();
await renderEChartsBlocks(document, {
engine,
outputMode: "interactive"
});
expect(setOption).toHaveBeenCalledWith(
expect.objectContaining({
animation: false,
animationDelay: 0,
animationDelayUpdate: 0,
animationDuration: 0,
animationDurationUpdate: 0,
series: [
expect.objectContaining({
animation: false,
animationDelay: 0,
animationDelayUpdate: 0,
animationDuration: 0,
animationDurationUpdate: 0
})
]
}),
{ notMerge: true }
);
});
it("生成 SVG Data URL 图片", async () => {
createDocumentWithChart();
const { engine } = fakeEngine();
const outcomes = await renderEChartsBlocks(document, {
engine,
outputMode: "svg-image"
});
expect(outcomes[0]?.success).toBe(true);
const image = document.querySelector<HTMLImageElement>(
".md-echarts-svg-image"
);
expect(image?.src).toMatch(/^data:image\/svg\+xml/u);
expect(image?.alt).toBe("测试图表");
});
it("交互模式保留实例并支持统一销毁", async () => {
createDocumentWithChart();
const { engine, dispose } = fakeEngine();
const outcomes = await renderEChartsBlocks(document, {
engine,
outputMode: "interactive"
});
expect(outcomes[0]?.instance).toBeDefined();
expect(dispose).not.toHaveBeenCalled();
disposeEChartsBlocks(document);
expect(dispose).toHaveBeenCalledOnce();
});
it("拒绝渲染器产生的外部 SVG 资源", async () => {
createDocumentWithChart();
const onError = vi.fn();
const { engine } = fakeEngine((svg) => {
const image = document.createElementNS(
"http://www.w3.org/2000/svg",
"image"
);
image.setAttribute("href", "https://example.com/chart.png");
svg.append(image);
});
const outcomes = await renderEChartsBlocks(document, {
engine,
outputMode: "inline-svg",
onError
});
expect(outcomes[0]?.success).toBe(false);
expect(onError).toHaveBeenCalledOnce();
expect(document.querySelector(".md-echarts-error")).not.toBeNull();
expect(document.body.textContent).toContain("外部 SVG 链接");
});
});