/**
* Renderer 一致性测试(对应设计文档 §15 的
* "HTML、SVG、PDF Renderer 的公共布局结果一致性测试")。
*
* 核心断言:同一份 LayoutDocument 经 HTML / SVG 渲染后,
* 分页结构一致(页数相同、节点都能找到)。
*/
import { describe, expect, it } from "vitest"
import { createDocument, createTableElement, type ReportDocument } from "@report/core"
import { createEstimateMeasurer, createPassthroughFontResolver } from "@report/layout"
import { layoutDocument } from "@report/layout"
import { renderHtml, renderSvg, createPdfRenderer } from "../src/index.js"
import type { RenderContext } from "@report/layout"
const measurer = createEstimateMeasurer()
const fontResolver = createPassthroughFontResolver()
async function layout(doc: ReportDocument, context: RenderContext) {
return layoutDocument(doc, context, { textMeasurer: measurer, fontResolver })
}
/** 构造一份含页眉、文本、明细表、页脚的报表文档。 */
function sampleDoc(): ReportDocument {
const doc = createDocument({ name: "渲染测试" })
doc.datasets = { sales: { id: "sales" } }
const header = doc.sections.find((s) => s.kind === "page-header")!
header.children.push({
id: "h1",
type: "text",
frame: { x: 0, y: 0, width: 100, height: 8 },
props: { text: "销售单" },
})
const detail = doc.sections.find((s) => s.kind === "detail")!
detail.children.push(
createTableElement("tbl1", { x: 0, y: 0, width: 180, height: 20 }, "sales", [
{ id: "c1", width: 90, header: "商品", binding: { field: "name" } },
{ id: "c2", width: 90, header: "金额", binding: { field: "amount" }, align: "right" },
]),
)
const footer = doc.sections.find((s) => s.kind === "page-footer")!
footer.children.push({
id: "f1",
type: "text",
frame: { x: 0, y: 0, width: 100, height: 6 },
binding: { expression: "'第 ' + page.pageNumber + ' 页'" },
})
return doc
}
const ROWS = [
{ name: "苹果", amount: 100 },
{ name: "香蕉", amount: 200 },
{ name: "橙子", amount: 300 },
]
describe("HTML / SVG 分页一致性", () => {
it("同一文档渲染为 HTML 与 SVG 时页数一致", async () => {
const doc = sampleDoc()
const result = await layout(doc, { data: { sales: ROWS }, parameters: {} })
const html = renderHtml(result)
const svg = renderSvg(result)
expect(html.target).toBe("html")
expect(svg.target).toBe("svg")
expect(html.pageCount).toBe(result.pageCount)
expect(svg.pageCount).toBe(result.pageCount)
expect(html.pageCount).toBe(svg.pageCount)
})
it("HTML 输出包含与页数一致的 .rd-page 容器", async () => {
const doc = sampleDoc()
const result = await layout(doc, { data: { sales: ROWS }, parameters: {} })
const html = renderHtml(result)
const pageCount = (html.content.match(/class="rd-page"/g) ?? []).length
expect(pageCount).toBe(result.pageCount)
})
it("SVG 输出包含与页数一致的