Files
Jyotisha/frontend/tests/onboarding-presentation.test.ts
T
Jesse_Chen fc811d2c39 refactor(home): drop the redundant hero note and move the birth-time boundary to the topics
The hero carried three lines saying the same thing: a salutation, a
question, and an Agent greeting that repeated both. Removing the third
line would also have removed the only place where accounts without a
usable birth minute are told that unsupported claims will be disclosed,
so that sentence moves to the topic section subheading, where the reader
is about to pick a question.

The starter loading copy claimed three starting points while the page
renders every domain in the registry, only three of which the Agent
writes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 15:44:19 +08:00

78 lines
4.1 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { readFileSync } from "node:fs";
import {
createOnboardingFallbackGreeting,
createStartGreeting,
createStartGreetingParts,
isCurrentOnboardingRequest,
onboardingProfileFingerprint,
onboardingRequestIdentity,
} from "../src/lib/onboarding-client.ts";
const completeProfile = {
name: "林遥", date: "1990-06-15", time: "12:30", reportedTime: "12:30",
birthTimeSource: "hospital_record", birthTimePeriod: "", birthTimeClue: "出生证明", birthTimeStatus: "confirmed",
uncertaintyBeforeMinutes: 0, uncertaintyAfterMinutes: 0, rectificationCaseId: "", countryCode: "CN", provinceCode: "110000", cityCode: "110000-city", districtCode: "110101",
} as const;
test("rejects stale A presentation and derives terminal and fallback greetings from profile B", () => {
// Given: one account starts onboarding for a complete persisted profile A.
const firstIdentity = onboardingRequestIdentity("account-1", onboardingProfileFingerprint(completeProfile));
// When: the name changes before that request completes and profile B is presented.
const changedProfile = { ...completeProfile, name: "周宁" };
const currentIdentity = onboardingRequestIdentity("account-1", onboardingProfileFingerprint(changedProfile));
const terminalGreeting = createStartGreeting(changedProfile.name, new Date("2026-07-19T08:00:00+08:00"), 0);
const fallbackGreeting = createOnboardingFallbackGreeting(changedProfile.name);
// Then: stale work is rejected and neither presentation path can retain A's name.
assert.notEqual(currentIdentity, firstIdentity);
assert.equal(isCurrentOnboardingRequest(currentIdentity, firstIdentity), false);
for (const greeting of [terminalGreeting, fallbackGreeting]) {
assert.match(greeting, /周宁/);
assert.doesNotMatch(greeting, /林遥/);
}
assert.equal(fallbackGreeting, "周宁,从你此刻最关心的问题开始吧。");
});
test("the starter hero splits one time-aware greeting instead of drawing from a separate pool", () => {
// Given: the same daypart variant, asked for as parts and as one line.
const noon = new Date("2026-07-19T12:00:00+08:00");
const parts = createStartGreetingParts("周宁", noon, 0);
const whole = createStartGreeting("周宁", noon, 0);
// Then: the salutation carries the name and the question carries no name, and they recompose exactly.
assert.equal(parts.salutation, "中午好,周宁。");
assert.equal(parts.question, "现在最想理清哪件事?");
assert.equal(`${parts.salutation}${parts.question}`, whole);
assert.doesNotMatch(parts.question, /周宁/);
// And: the extremes stay inside the variant list rather than falling off the end.
assert.equal(createStartGreetingParts("周宁", noon, 1).question, "事业、关系或选择,想先聊哪个?");
assert.equal(createStartGreetingParts("周宁", noon, 0.999999).question, "事业、关系或选择,想先聊哪个?");
// And: every daypart is reachable, so the heading is not one fixed line in disguise.
const questions = new Set([0, 8, 12, 16, 20, 23].flatMap((hour) =>
[0, 1 / 3, 2 / 3].map((selection) =>
createStartGreetingParts("周宁", new Date(2026, 6, 19, hour), selection).question)));
assert.ok(questions.size >= 12);
});
test("the starter hero stays at a salutation and a question", () => {
const source = readFileSync(new URL("../src/app/page.tsx", import.meta.url), "utf8");
// Given: the hero heading is the question half of the time-aware greeting.
assert.match(source, /<p className="starter-greeting">\{starterGreeting\.salutation\}<\/p>/);
assert.match(source, /<h1 id="starter-heading">\{starterGreeting\.question\}<\/h1>/);
// Then: no third line repeats the welcome the two lines above already carry.
assert.doesNotMatch(source, /starter-hero-note/);
const styles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
assert.doesNotMatch(styles, /starter-hero-note/);
// And: the separate static heading pool is gone for good.
assert.doesNotMatch(source, /starterPrompt|createStarterPrompt|greetingForHour/);
assert.doesNotMatch(source, /greeting: createStartGreeting\(/);
});