Files
Jyotisha/frontend/tests/personal-report-longform-md.test.ts
T
Jesse_ChenandCursor cfcd369d4f feat(report): render longform Markdown as the report and close gaps2 holes
New reports skip the writer, persist pl9 Markdown as the body, and settle zero-token usage on the catalog model. Planned longform sections now emit blocked rows instead of vanishing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 21:46:16 +08:00

148 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { PersonalReportMarkdownView } from "../src/components/personal-report/personal-report-markdown-view.tsx";
import { buildLongformCoverDocument } from "../src/lib/personal-report-longform-cover.ts";
import {
PERSONAL_REPORT_EXPORT_LABEL,
PERSONAL_REPORT_GENERATING_COPY,
PERSONAL_REPORT_LEGACY_PLACEHOLDER,
} from "../src/lib/personal-report-longform-copy.ts";
import {
buildLongformOutline,
extractLongformSummary,
personalReportMarkdownFilename,
} from "../src/lib/personal-report-longform-outline.ts";
import { PERSONAL_REPORT_WRITER_ENABLED } from "../src/lib/personal-report-writer-flag.ts";
Object.assign(globalThis, { React });
const SAMPLE_MARKDOWN = [
"# 个人长报告",
"",
"## 成品阅读导航",
"",
"导航正文 blocked",
"",
"## 摘要",
"",
"事业方向保持观察,不承诺日期。parameter_sensitive",
"",
"## 力量章",
"",
"### 子节",
"",
"后续章节",
"",
"## 质量验收矩阵",
"",
"| 项 | 状态 |",
"| --- | --- |",
"| MEVG | blocked |",
].join("\n");
const UNSAFE_MARKDOWN = [
SAMPLE_MARKDOWN,
"",
"## 摘要",
"",
"事业方向保持观察。",
"",
"<script>alert(1)</script>",
"<img src=\"https://evil.example/x.png\">",
"[bad](javascript:alert(1))",
"",
"| 月 | KP |",
"| --- | --- |",
"| 2026-01 | blocked |",
].join("\n");
test("report centre cards read the stored Markdown excerpt, not a writer summary field name", () => {
const listRoute = readFileSync(new URL("../src/app/api/reports/route.ts", import.meta.url), "utf8");
assert.match(listRoute, /card_summary:report_document->executiveSummary->>summary/);
const coverSource = readFileSync(
new URL("../src/lib/personal-report-longform-cover.ts", import.meta.url),
"utf8",
);
assert.match(coverSource, /extractLongformSummary/);
});
test("writer pipeline stays in the tree but is feature-off", () => {
assert.equal(PERSONAL_REPORT_WRITER_ENABLED, false);
assert.equal(PERSONAL_REPORT_EXPORT_LABEL, "导出报告(.md");
assert.equal(PERSONAL_REPORT_LEGACY_PLACEHOLDER, "旧版本报告,请重新生成");
assert.match(PERSONAL_REPORT_GENERATING_COPY, /1030 秒/);
});
test("outline lifts navigation and summary to the first screen", () => {
const outline = buildLongformOutline(SAMPLE_MARKDOWN);
assert.ok(outline.headings.some((heading) => heading.title === "成品阅读导航"));
assert.ok(outline.headings.some((heading) => heading.title === "摘要"));
assert.match(outline.leadMarkdown, /成品阅读导航/);
assert.match(outline.leadMarkdown, /事业方向保持观察/);
assert.equal(outline.sections.some((section) => section.eager && section.title === "成品阅读导航"), true);
assert.equal(outline.sections.some((section) => section.eager && section.title === "摘要"), true);
});
test("card excerpt comes from the Markdown 摘要 section", () => {
const excerpt = extractLongformSummary(SAMPLE_MARKDOWN, 80);
assert.match(excerpt, /事业方向保持观察/);
assert.doesNotMatch(excerpt, /executiveSummary/);
assert.equal(personalReportMarkdownFilename("2026-09-06T08:00:00.000Z"), "个人报告-2026-09-06");
});
test("cover document parses and stores the Markdown excerpt", () => {
const document = buildLongformCoverDocument({
report: {
id: "123e4567-e89b-12d3-a456-426614174000",
reportType: "personal_full",
presentationMode: "default",
depth: "standard",
requestedThemes: ["career", "wealth"],
skillName: "jyotish-vedic-astrology",
skillVersion: "6.9.14",
skillSourceCommit: null,
skillSnapshotSha256: "ab".repeat(32),
},
subject: {
displayName: "测试",
birthTimeStatus: "accepted",
birthPlaceLabel: "测试地点",
},
markdown: SAMPLE_MARKDOWN,
generatedAt: "2026-09-06T00:00:00.000Z",
});
assert.equal(document.schemaVersion, "report_document.v2");
assert.match(document.executiveSummary.summary, /事业方向保持观察/);
assert.equal(document.blockedConflictDisclosure.length, 2);
assert.equal(document.thematicNarrative.length, 0);
});
test("markdown view escapes HTML, skips images, and wraps wide tables", () => {
const started = Date.now();
const markup = renderToStaticMarkup(React.createElement(PersonalReportMarkdownView, { markdown: UNSAFE_MARKDOWN }));
const elapsed = Date.now() - started;
assert.ok(elapsed < 1500, `first paint ${elapsed}ms`);
assert.match(markup, /personal-report-toc/);
assert.match(markup, /成品阅读导航/);
assert.match(markup, /parameter_sensitive/);
assert.match(markup, /blocked/);
assert.match(markup, /markdown-table personal-report-table-wrap/);
assert.doesNotMatch(markup, /<script/i);
assert.doesNotMatch(markup, /<img/i);
assert.doesNotMatch(markup, /javascript:/i);
assert.doesNotMatch(markup, /evil\.example/);
const viewSource = readFileSync(
new URL("../src/components/personal-report/personal-report-markdown-view.tsx", import.meta.url),
"utf8",
);
assert.match(viewSource, /skipHtml/);
assert.match(viewSource, /remarkGfm/);
assert.doesNotMatch(viewSource, /rehype-raw/);
assert.doesNotMatch(viewSource, /dangerouslySetInnerHTML/);
});