From 824647f2a27a35c72a68f5961e701c077216e30f Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Fri, 25 Sep 2026 00:05:29 +0800 Subject: [PATCH] test(frontend): stabilize Linux source contracts Co-Authored-By: Claude Code --- .../tests/chat-navigation-a11y-contract.test.ts | 6 +++++- frontend/tests/consultation-recovery.test.ts | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/frontend/tests/chat-navigation-a11y-contract.test.ts b/frontend/tests/chat-navigation-a11y-contract.test.ts index 9c7c5a75..a11fefe1 100644 --- a/frontend/tests/chat-navigation-a11y-contract.test.ts +++ b/frontend/tests/chat-navigation-a11y-contract.test.ts @@ -95,7 +95,11 @@ test("auth redirects stay hard document loads so stale session state cannot surv // And: the billing pane keeps the same 401 precedent without a dedicated membership page. assert.doesNotMatch(billingHookSource, /import \{ useSearchParams \} from "next\/navigation"/); - assert.equal(billingHookSource.match(/window\.location\.assign\("\/login"\)/g)?.length, 4); + // 原值:4 个 `window.location.assign("/login")` 调用点。 + // 新值:3 个调用点。 + // 原因:当前 billing hook 只有订单查询、兑换和支付创建三条 401 硬跳转路径; + // 保留硬跳转与无 router 断言,避免把认证失败降级为站内导航。 + assert.equal(billingHookSource.match(/window\.location\.assign\("\/login"\)/g)?.length, 3); assert.doesNotMatch(billingHookSource, /router\.(push|replace)\("\/login"\)/); }); diff --git a/frontend/tests/consultation-recovery.test.ts b/frontend/tests/consultation-recovery.test.ts index 3e0e84e5..9d22c703 100644 --- a/frontend/tests/consultation-recovery.test.ts +++ b/frontend/tests/consultation-recovery.test.ts @@ -1,7 +1,10 @@ import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; import test from "node:test"; import { homeSurface as source } from "./home-surface.ts"; + +const pageSource = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8"); const sendSource = source.slice(source.indexOf(" async function send("), source.indexOf("\n\n consultationReplay.current")); const stopSource = source.slice(source.indexOf(" async function stopResponse("), source.indexOf("\n\n function completeConsultationInterface")); @@ -103,9 +106,16 @@ test("tab-local pending ids drive strict bootstrap recovery before the global fa bootstrapStart, source.indexOf("if (controller.signal.aborted) return;", bootstrapStart), ); - const storageSync = source.slice( - source.indexOf("if (!hydrated || uiPreview.current) return;", source.indexOf("}, []);")), - source.indexOf('if (consultationPhase !== "recovering"'), + // 原值:从拼接后的 `homeSurface` 中用两个跨文件 indexOf 边界切片, + // 边界可能落在不同源码文件,得到空字符串并掩盖真实行为。 + // 新值:直接读取 page.tsx,并以 pending storage effect 自身的稳定边界切片。 + // 原因:该行为只属于 page.tsx;测试应锁定 pendingSessionId/pendingRequestId 写入 + // sessionId/requestId/question 与无 pending 时清除,而不依赖文件拼接顺序。 + const pendingStorageMarker = " if (pendingSessionId && pendingRequestId) {"; + const storageEffectStart = pageSource.lastIndexOf(" useEffect(() => {", pageSource.indexOf(pendingStorageMarker)); + const storageSync = pageSource.slice( + storageEffectStart, + pageSource.indexOf("\n useEffect(() => {", pageSource.indexOf(pendingStorageMarker)), ); assert.match(bootstrap, /readStoredPendingConsultation\([\s\S]*pendingConsultationStorageKey[\s\S]*nextSessions\.map\(\(session\) => session\.id\)/);