- 新增可复用的 markdown-echarts 工作区,支持安全的 YAML 围栏、默认值、语义校验、错误占位和 SVG 渲染 - 将 ECharts 接入统一 Markdown、快速预览、精确预览与 PDF 链路,并复用 Mermaid 的不可跨页和超高图单页适配策略 - 默认示例加入 ECharts,增加源文本折叠、顶部文档状态、页码输入跳转与滚动同步 - 快速预览改用外层容器滚动,使滚动条与精确预览统一贴在预览区域右侧,并兼容显示缩放 - 完善 Docker 工作区复制、可配置 Compose 镜像、测试覆盖和进度文档
182 lines
4.7 KiB
TypeScript
182 lines
4.7 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { defaultExportConfig } from "@md-to-pdf/core";
|
|
import {
|
|
createPdfExportCacheKey,
|
|
getCachedPdfExport,
|
|
getOrRequestPdfExport,
|
|
parseContentDispositionFileName,
|
|
requestPdfExport,
|
|
type CachedPdfExport,
|
|
type PdfExportRequest
|
|
} from "../src/pdf-export";
|
|
|
|
describe("PDF 导出客户端", () => {
|
|
it("仅复用与当前文档和导出配置完全匹配的 PDF", () => {
|
|
const request: PdfExportRequest = {
|
|
markdown: "# 测试",
|
|
fileName: "测试.md",
|
|
language: "zh-CN",
|
|
exportConfig: defaultExportConfig
|
|
};
|
|
const key = createPdfExportCacheKey(request);
|
|
const result = {
|
|
blob: new Blob(["%PDF-test"]),
|
|
fileName: "测试.pdf",
|
|
pageCount: 1,
|
|
echartsErrorCount: 0,
|
|
mermaidErrorCount: 0
|
|
};
|
|
const cache: CachedPdfExport = { key, result };
|
|
|
|
expect(getCachedPdfExport(cache, key)).toBe(result);
|
|
expect(
|
|
getCachedPdfExport(
|
|
cache,
|
|
createPdfExportCacheKey({
|
|
...request,
|
|
markdown: "# 已修改"
|
|
})
|
|
)
|
|
).toBeUndefined();
|
|
expect(
|
|
getCachedPdfExport(
|
|
cache,
|
|
createPdfExportCacheKey({
|
|
...request,
|
|
exportConfig: {
|
|
...defaultExportConfig,
|
|
paper: {
|
|
...defaultExportConfig.paper,
|
|
orientation: "landscape"
|
|
}
|
|
}
|
|
})
|
|
)
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("命中缓存时复用同一个 PDF Blob 且不重复请求", async () => {
|
|
const request: PdfExportRequest = {
|
|
markdown: "# 测试",
|
|
fileName: "测试.md",
|
|
language: "zh-CN",
|
|
exportConfig: defaultExportConfig
|
|
};
|
|
const result = {
|
|
blob: new Blob(["%PDF-cached"]),
|
|
fileName: "测试.pdf",
|
|
pageCount: 1,
|
|
echartsErrorCount: 0,
|
|
mermaidErrorCount: 0
|
|
};
|
|
const cache: CachedPdfExport = {
|
|
key: createPdfExportCacheKey(request),
|
|
result
|
|
};
|
|
const requester = vi.fn();
|
|
|
|
const resolved = await getOrRequestPdfExport(
|
|
request,
|
|
cache,
|
|
requester
|
|
);
|
|
|
|
expect(requester).not.toHaveBeenCalled();
|
|
expect(resolved).toEqual(cache);
|
|
expect(resolved.result.blob).toBe(result.blob);
|
|
});
|
|
|
|
it("缓存失效时请求并保存当前 PDF", async () => {
|
|
const request: PdfExportRequest = {
|
|
markdown: "# 新内容",
|
|
fileName: "测试.md",
|
|
language: "zh-CN",
|
|
exportConfig: defaultExportConfig
|
|
};
|
|
const result = {
|
|
blob: new Blob(["%PDF-new"]),
|
|
fileName: "测试.pdf",
|
|
pageCount: 2,
|
|
echartsErrorCount: 0,
|
|
mermaidErrorCount: 0
|
|
};
|
|
const requester = vi.fn(async () => result);
|
|
|
|
const resolved = await getOrRequestPdfExport(
|
|
request,
|
|
undefined,
|
|
requester
|
|
);
|
|
|
|
expect(requester).toHaveBeenCalledOnce();
|
|
expect(resolved.key).toBe(createPdfExportCacheKey(request));
|
|
expect(resolved.result).toBe(result);
|
|
});
|
|
|
|
it("优先解析 UTF-8 下载文件名", () => {
|
|
expect(
|
|
parseContentDispositionFileName(
|
|
"attachment; filename=\"document.pdf\"; " +
|
|
"filename*=UTF-8''%E6%B5%8B%E8%AF%95.pdf"
|
|
)
|
|
).toBe("测试.pdf");
|
|
});
|
|
|
|
it("请求 PDF 并读取生成信息", async () => {
|
|
const fetcher = vi.fn(async () => {
|
|
return new Response(new Blob(["%PDF-test"]), {
|
|
status: 200,
|
|
headers: {
|
|
"content-type": "application/pdf",
|
|
"content-disposition":
|
|
"attachment; filename=\"document.pdf\"",
|
|
"x-pdf-page-count": "4",
|
|
"x-echarts-error-count": "2",
|
|
"x-mermaid-error-count": "1"
|
|
}
|
|
});
|
|
});
|
|
|
|
const result = await requestPdfExport(
|
|
{
|
|
markdown: "# 测试",
|
|
fileName: "测试.md",
|
|
language: "zh-CN",
|
|
exportConfig: defaultExportConfig
|
|
},
|
|
fetcher
|
|
);
|
|
|
|
expect(fetcher).toHaveBeenCalledWith(
|
|
"/api/pdf",
|
|
expect.objectContaining({ method: "POST" })
|
|
);
|
|
expect(result.fileName).toBe("document.pdf");
|
|
expect(result.pageCount).toBe(4);
|
|
expect(result.echartsErrorCount).toBe(2);
|
|
expect(result.mermaidErrorCount).toBe(1);
|
|
expect(await result.blob.text()).toBe("%PDF-test");
|
|
});
|
|
|
|
it("显示服务端返回的错误信息", async () => {
|
|
const fetcher = vi.fn(async () => {
|
|
return Response.json(
|
|
{ message: "PDF 生成队列已满" },
|
|
{ status: 503 }
|
|
);
|
|
});
|
|
|
|
await expect(
|
|
requestPdfExport(
|
|
{
|
|
markdown: "# 测试",
|
|
fileName: "测试.md",
|
|
language: "zh-CN",
|
|
exportConfig: defaultExportConfig
|
|
},
|
|
fetcher
|
|
)
|
|
).rejects.toThrow("PDF 生成队列已满");
|
|
});
|
|
});
|