Files
Jyotisha/frontend/tests/root-error-boundaries-contract.test.ts
T
Jesse_Chen 9c296f1e3f
Independent Staging Quality Gate / validate (push) Successful in 12m8s
Independent Staging Quality Gate / publish (push) Successful in 14m29s
fix(ux): surface hidden notices, bound report waits, add root boundaries
Framework-level UX fixes found while auditing staging (BUG-216..220).

- chat: route 44 previously discarded composer notices to sonner with
  dedupe, so recovery, cancel and archive feedback is actually visible
  (BUG-216)
- chat: anchor stream auto-scroll to bottom proximity and add a
  jump-to-latest control, so reading history is no longer interrupted
  on every token (BUG-218)
- reports: replace the silent 120s poll cutoff with an explicit
  timed-out state, an 8m budget, stepped backoff and an elapsed
  counter (BUG-217)
- reports: pause polling while the tab is hidden, via a shared hook
- app: add root error, global-error and not-found boundaries (BUG-219)
- admin: add antd SSR style extraction and the React 19 render adapter,
  and move admin-only css out of the global stylesheet (BUG-220)
- membership: run bootstrap fetches concurrently and pause payment
  polling while hidden
- build: configure optimizePackageImports

Verified on top of 2d370f2e: tsc, eslint, next build, and the related
frontend contract suites.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 13:14:15 +08:00

70 lines
2.6 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";
const read = (relative: string) => readFileSync(new URL(relative, import.meta.url), "utf8");
const error = read("../src/app/error.tsx");
const globalError = read("../src/app/global-error.tsx");
const notFound = read("../src/app/not-found.tsx");
const chinese = /[\u4e00-\u9fff]/;
test("the app root ships error, global-error and not-found boundaries", () => {
for (const source of [error, globalError, notFound]) {
assert.ok(source.length > 0);
assert.match(source, /export default function \w+\(/);
}
});
test("root error boundary is a client component that can reset and route home", () => {
assert.match(error, /^"use client";/);
assert.match(error, /reset,\s*\}: \{[\s\S]*reset: \(\) => void;/);
assert.match(error, /onClick=\{\(\) => reset\(\)\}/);
assert.match(error, /href="\/"/);
assert.match(error, /error\.digest/);
assert.doesNotMatch(error, /error\.stack/);
});
test("global error boundary replaces the root layout without external styling", () => {
assert.match(globalError, /^"use client";/);
assert.match(globalError, /<html lang="zh-CN">/);
assert.match(globalError, /<body/);
assert.match(globalError, /<\/body>/);
assert.match(globalError, /<\/html>/);
assert.doesNotMatch(globalError, /^import /m);
assert.doesNotMatch(globalError, /className="[a-z-]*(flex|text-|bg-|min-h-)/);
assert.match(globalError, /minHeight: "44px"/);
assert.match(globalError, /onClick=\{\(\) => reset\(\)\}/);
assert.match(globalError, /window\.location\.assign\("\/"\)/);
});
test("root not found stays a server component and links back to the chat home", () => {
assert.doesNotMatch(notFound, /"use client"/);
assert.doesNotMatch(notFound, /useState|useEffect|onClick/);
assert.match(notFound, /404/);
assert.match(notFound, /render=\{<Link href="\/" \/>\}/);
});
test("every boundary announces itself accessibly with one heading", () => {
for (const source of [error, globalError]) {
assert.match(source, /role="alert"/);
}
for (const source of [error, globalError, notFound]) {
assert.equal(source.match(/<h1/g)?.length, 1);
assert.doesNotMatch(source, /<h[3-6]/);
}
});
test("boundary copy is simplified chinese in the product voice", () => {
for (const source of [error, globalError, notFound]) {
const copy = source.match(/>\s*([^<>{}]*[\u4e00-\u9fff][^<>{}]*)\s*</g) ?? [];
assert.ok(copy.length >= 2);
for (const line of copy) {
assert.match(line, chinese);
assert.doesNotMatch(line, /[!]/);
assert.doesNotMatch(line, /\p{Extended_Pictographic}/u);
}
}
});