The new reader app replaces the old UI: Checklist with a rail (focus pills, Next to expire, today's dailies, pinned routines), Timeline in a new and a classic look, Routines, Archive and a Settings area, in a Structured (default) and a Relaxed interface. It follows the approved Round 6 B and Round 7 mockups, corrected screen by screen with the owner. Reader data moves to a v2 store (gacha-tracker:v2:*). Existing v1 data is migrated on first load, a recovery copy is written first, and the v1 keys are never removed, so a rollback finds them unchanged. Redeem codes for Genshin arrive as their own feed. Also: live's urgency ramp, editable per theme; completion and section-complete fireworks; a returning-user tour; the design mockups, specs, plans, reviews and handoff record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { renderToStaticMarkup as render } from "react-dom/server";
|
|
import {
|
|
RoutineDrawer,
|
|
scheduleWords,
|
|
sourceLabel,
|
|
} from "../src/client/components/redesign/RoutineDrawer.tsx";
|
|
import { migrateLegacy } from "../src/client/state/reader/migration.ts";
|
|
import { createAccount } from "../src/client/state/reader/accounts.ts";
|
|
|
|
const at = "2026-10-16T12:00:00.000Z";
|
|
const drawer = (
|
|
pane: "library" | "create",
|
|
state = migrateLegacy({}, ["genshin"], at, () => "account:one"),
|
|
) =>
|
|
render(
|
|
<RoutineDrawer
|
|
state={state}
|
|
now={Date.parse(at)}
|
|
pane={pane}
|
|
editing={null}
|
|
onPane={() => {}}
|
|
onClose={() => {}}
|
|
onAction={async () => ({ ok: true })}
|
|
onSave={async () => true}
|
|
/>,
|
|
);
|
|
|
|
test("schedule lines read like the mockup", () => {
|
|
expect(scheduleWords({ unit: "week", startsOn: 1 }, 4)).toBe(
|
|
"Every Monday at 04:00",
|
|
);
|
|
expect(scheduleWords({ unit: "month", startsOn: 16 }, 4)).toBe(
|
|
"Every month on the 16th at 04:00",
|
|
);
|
|
expect(scheduleWords({ unit: "day" }, 20)).toBe("Every day at 20:00");
|
|
expect(
|
|
sourceLabel("https://genshin-impact.fandom.com/wiki/Weekly_Boss"),
|
|
).toBe("Community wiki");
|
|
});
|
|
|
|
test("the library is a shelf of built-ins with bulk follows and dated sources", () => {
|
|
const html = drawer("library");
|
|
expect(html).toContain('class="routine-drawer"');
|
|
expect(html).toContain("Create your own routine");
|
|
expect(html).toContain("New follows also show in");
|
|
expect(html).toContain("Follow all weeklies");
|
|
expect(html).toContain("Every Monday at 04:00 · 3 runs · Community wiki");
|
|
expect(html).toContain(">Follow<");
|
|
});
|
|
|
|
test("a game on two servers gets one follow button per server", () => {
|
|
const s = migrateLegacy({}, ["genshin"], at, () => "account:one");
|
|
s.accounts["account:asia"] = createAccount(
|
|
"genshin",
|
|
"asia",
|
|
at,
|
|
"account:asia",
|
|
);
|
|
s.data["account:asia"] = {
|
|
progress: {},
|
|
ignored: {},
|
|
daily: {},
|
|
amounts: {},
|
|
};
|
|
const html = drawer("library", s);
|
|
expect(html).toContain(">EU<");
|
|
expect(html).toContain(">Asia<");
|
|
expect(html).toContain("2 servers");
|
|
});
|
|
|
|
test("the create shelf asks every mockup question and previews the result", () => {
|
|
const html = drawer("create");
|
|
for (const label of [
|
|
"Start from an example",
|
|
"What is it",
|
|
"Repeats",
|
|
"Every day",
|
|
"Resets at",
|
|
"Another hour",
|
|
"Done when",
|
|
"Note",
|
|
"Link",
|
|
"Also show in",
|
|
"Preview",
|
|
"Create and follow",
|
|
])
|
|
expect(html).toContain(label);
|
|
expect(html).toContain("Still needed: a name.");
|
|
});
|