Homepage no longer waits on /api/onboarding for six starter questions. Natal answers show the spoken layer first; the Level 2 skeleton sits in a collapsed 完整分析 block. Wide tables scroll sideways on a phone. Form inputs are 16px so iOS does not zoom on focus.
204 lines
6.6 KiB
TypeScript
204 lines
6.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
answerDetailCaption,
|
|
shortenAnswerHeading,
|
|
splitSpokenAndReport,
|
|
} from "../src/lib/chat-answer-split.ts";
|
|
|
|
const NATAL = [
|
|
"你更适合把已经积累的专业做成长期事业,而不是频繁换赛道。",
|
|
"",
|
|
"现有工作里还有没做完的深耕。",
|
|
"今明两年更像内部推进,而不是另起一摊。",
|
|
"下一步把你最想推进的那件事说具体。",
|
|
"",
|
|
"## 统一参数与原始结构",
|
|
"岁差与宫位。",
|
|
"",
|
|
"## 事业",
|
|
"第十宫判断。",
|
|
"",
|
|
"## 技法审计表",
|
|
"| 技法 | 状态 | 说明 |",
|
|
"",
|
|
"## 现代生活",
|
|
"先把职责做稳。",
|
|
].join("\n");
|
|
|
|
test("answers without an H2 stay fully spoken", () => {
|
|
const text = "今晚先把待确认的条款列成清单。";
|
|
assert.deepEqual(splitSpokenAndReport(text), { spoken: text, report: "", headings: [] });
|
|
});
|
|
|
|
test("empty text stays empty and unfolded", () => {
|
|
assert.deepEqual(splitSpokenAndReport(""), { spoken: "", report: "", headings: [] });
|
|
});
|
|
|
|
test("the first ATX H2 is the cut", () => {
|
|
const split = splitSpokenAndReport(NATAL);
|
|
assert.match(split.spoken, /做成长期事业/);
|
|
assert.doesNotMatch(split.spoken, /## /);
|
|
assert.equal(split.report.startsWith("## 统一参数与原始结构"), true);
|
|
assert.match(split.report, /## 事业/);
|
|
assert.match(split.report, /## 技法审计表/);
|
|
assert.match(split.report, /## 现代生活/);
|
|
});
|
|
|
|
test("the report side keeps the exact bytes from the cut", () => {
|
|
const split = splitSpokenAndReport(`口语\n\n## 统一参数与原始结构\n岁差。\n`);
|
|
assert.equal(split.report, "## 统一参数与原始结构\n岁差。\n");
|
|
});
|
|
|
|
test("spoken trailing blank lines are trimmed", () => {
|
|
const split = splitSpokenAndReport("结论。\n\n\n## 事业\n正文。");
|
|
assert.equal(split.spoken, "结论。");
|
|
});
|
|
|
|
test("an H2 on the first line leaves spoken empty", () => {
|
|
const split = splitSpokenAndReport("## 统一参数与原始结构\n岁差。");
|
|
assert.equal(split.spoken, "");
|
|
assert.equal(split.report.startsWith("## 统一参数与原始结构"), true);
|
|
});
|
|
|
|
test("a fenced backtick heading is not a cut", () => {
|
|
const text = [
|
|
"先看这段。",
|
|
"",
|
|
"```md",
|
|
"## 统一参数与原始结构",
|
|
"```",
|
|
"",
|
|
"仍是口语。",
|
|
].join("\n");
|
|
const split = splitSpokenAndReport(text);
|
|
assert.equal(split.report, "");
|
|
assert.equal(split.spoken, text);
|
|
assert.deepEqual(split.headings, []);
|
|
});
|
|
|
|
test("a fenced tilde heading is not a cut", () => {
|
|
const text = [
|
|
"先看这段。",
|
|
"",
|
|
"~~~",
|
|
"## 事业",
|
|
"~~~",
|
|
].join("\n");
|
|
const split = splitSpokenAndReport(text);
|
|
assert.equal(split.report, "");
|
|
assert.deepEqual(split.headings, []);
|
|
});
|
|
|
|
test("an H2 after a closed fence still cuts", () => {
|
|
const text = [
|
|
"口语。",
|
|
"```",
|
|
"## 不是切点",
|
|
"```",
|
|
"## 事业",
|
|
"正文。",
|
|
].join("\n");
|
|
const split = splitSpokenAndReport(text);
|
|
assert.equal(split.spoken, "口语。\n```\n## 不是切点\n```");
|
|
assert.equal(split.report.startsWith("## 事业"), true);
|
|
assert.deepEqual(split.headings, ["事业"]);
|
|
});
|
|
|
|
test("an unclosed fence keeps later H2s in the spoken layer", () => {
|
|
const text = "口语。\n```\n## 事业\n还在围栏里。";
|
|
const split = splitSpokenAndReport(text);
|
|
assert.equal(split.report, "");
|
|
assert.equal(split.spoken, text);
|
|
});
|
|
|
|
test("a fence indented by up to three spaces still counts", () => {
|
|
const text = "口语。\n ```\n## 事业\n ```\n## 财富\n正文。";
|
|
const split = splitSpokenAndReport(text);
|
|
assert.deepEqual(split.headings, ["财富"]);
|
|
assert.equal(split.report.startsWith("## 财富"), true);
|
|
});
|
|
|
|
test("headings are collected in order and skip fenced H2s", () => {
|
|
const split = splitSpokenAndReport(NATAL);
|
|
assert.deepEqual(split.headings, ["统一参数与原始结构", "事业", "技法审计表", "现代生活"]);
|
|
});
|
|
|
|
test("技法审计表 and 现代生活 both land on the report side", () => {
|
|
const split = splitSpokenAndReport(NATAL);
|
|
const auditAt = split.report.indexOf("## 技法审计表");
|
|
const wrapAt = split.report.indexOf("## 现代生活");
|
|
assert.ok(auditAt >= 0);
|
|
assert.ok(wrapAt > auditAt);
|
|
});
|
|
|
|
test("an H1 or H3 is not a cut", () => {
|
|
const text = "# 标题\n口语。\n### 小节\n仍是口语。";
|
|
const split = splitSpokenAndReport(text);
|
|
assert.equal(split.report, "");
|
|
assert.equal(split.spoken, text);
|
|
});
|
|
|
|
test("a bare ## without a title is not a cut", () => {
|
|
const text = "口语。\n##\n仍是口语。";
|
|
const split = splitSpokenAndReport(text);
|
|
assert.equal(split.report, "");
|
|
});
|
|
|
|
test("## with extra spaces still cuts", () => {
|
|
const split = splitSpokenAndReport("口语。\n## 事业\n正文。");
|
|
assert.deepEqual(split.headings, ["事业"]);
|
|
});
|
|
|
|
test("daily headings fold the same way as natal ones", () => {
|
|
const text = [
|
|
"今天整体宜把已谈妥的细节落成文字。",
|
|
"",
|
|
"## 今日趋势",
|
|
"节奏偏收束。",
|
|
"",
|
|
"## 适合推进 / 需要避开",
|
|
"- **适合推进** 合同核对。",
|
|
"",
|
|
"## 一个行动",
|
|
"今晚只做一件。",
|
|
].join("\n");
|
|
const split = splitSpokenAndReport(text);
|
|
assert.match(split.spoken, /落成文字/);
|
|
assert.deepEqual(split.headings, ["今日趋势", "适合推进 / 需要避开", "一个行动"]);
|
|
});
|
|
|
|
test("shortenAnswerHeading compresses the two long skeleton titles", () => {
|
|
assert.equal(shortenAnswerHeading("统一参数与原始结构"), "统一参数");
|
|
assert.equal(shortenAnswerHeading("技法审计表"), "技法审计");
|
|
assert.equal(shortenAnswerHeading("事业"), "事业");
|
|
assert.equal(shortenAnswerHeading("现代生活"), "现代生活");
|
|
});
|
|
|
|
test("caption with one heading is just that heading", () => {
|
|
assert.equal(answerDetailCaption(["事业"]), "事业");
|
|
});
|
|
|
|
test("caption with two headings joins them", () => {
|
|
assert.equal(answerDetailCaption(["统一参数与原始结构", "事业"]), "统一参数、事业");
|
|
});
|
|
|
|
test("caption with three headings has no 等 N 节", () => {
|
|
assert.equal(
|
|
answerDetailCaption(["统一参数与原始结构", "事业", "技法审计表"]),
|
|
"统一参数、事业、技法审计",
|
|
);
|
|
});
|
|
|
|
test("caption with more than three headings keeps the first three and the count", () => {
|
|
assert.equal(
|
|
answerDetailCaption(["统一参数与原始结构", "事业", "技法审计表", "现代生活", "附录"]),
|
|
"统一参数、事业、技法审计 等 5 节",
|
|
);
|
|
});
|
|
|
|
test("caption with no headings is empty", () => {
|
|
assert.equal(answerDetailCaption([]), "");
|
|
});
|