|
27 | 27 | */ |
28 | 28 | import { cleanup, fireEvent, render, screen } from "@testing-library/react"; |
29 | 29 | import { afterEach, describe, expect, it, vi } from "vitest"; |
| 30 | +import { useEffect } from "react"; |
30 | 31 | import { TopBar } from "./TopBar"; |
| 32 | +import { PROOFREAD_MODE_ATTR, useProofreadMode } from "./proofreadMode"; |
31 | 33 | import { NAV_ENTRIES } from "./navRegistry"; |
32 | 34 | import { BREAKPOINTS } from "./breakpoints"; |
33 | 35 | import { |
@@ -142,7 +144,7 @@ describe("TopBar(规格 §6.1)", () => { |
142 | 144 | expect( |
143 | 145 | classes, |
144 | 146 | "顶栏按钮的类序变了:带 `ed-btn` ⇒ 掉出 `button:not(.ed-btn)` 的元素级回执(也就掉出 reduced-motion 名单)", |
145 | | - ).toEqual([...NAV_ENTRIES.map(() => "ed-topbar__tab"), "ed-topbar__action", "ed-topbar__action"]); |
| 147 | + ).toEqual([...NAV_ENTRIES.map(() => "ed-topbar__tab"), "ed-topbar__action", "ed-topbar__action", "ed-topbar__action"]); |
146 | 148 | // ③a-2 落点覆盖面(今天 0 处声明 ⇒ 条件式;变异体证明有牙:把落点写到 `.ed-topbar__brand`(`<span>`)⇒ 红) |
147 | 149 | const buttons = new Set(classes.flatMap((c) => c.split(/\s+/))); |
148 | 150 | const rules = [...CSS.matchAll(/([^{}]+)\{([^{}]*)\}/g)].filter((m) => motionDecls(m[2]).length > 0); |
@@ -212,6 +214,55 @@ describe("TopBar(规格 §6.1)", () => { |
212 | 214 | }); |
213 | 215 | }); |
214 | 216 |
|
| 217 | +/** |
| 218 | + * 批 8 T11 · 审校模式**入口按钮**(规格 §4.3① 可发现的入口 + ③ 三条等价出口)。通道层判据在 |
| 219 | + * `proofreadMode.test.ts` / `.dom.test.tsx`;本节判**装配层**:宿主 `Harness` = `App.tsx:330-339` |
| 220 | + * 的**最小镜像**(单一 `useProofreadMode()` + 两条键盘出口)⇒ 三条出口必须**合流**。 |
| 221 | + */ |
| 222 | +describe("审校模式入口(批 8 T11;规格 §4.3①③)", () => { |
| 223 | + const mode = () => document.documentElement.getAttribute(PROOFREAD_MODE_ATTR); |
| 224 | + afterEach(() => document.documentElement.removeAttribute(PROOFREAD_MODE_ATTR)); |
| 225 | + function Harness() { |
| 226 | + const [proofread, toggleProofread] = useProofreadMode(); |
| 227 | + useEffect(() => { |
| 228 | + const onKey = (e: KeyboardEvent) => { |
| 229 | + const hit = e.ctrlKey && e.shiftKey && (e.key === "R" || e.key === "r"); |
| 230 | + if (hit) { e.preventDefault(); toggleProofread(); } |
| 231 | + else if (e.key === "Escape" && proofread === "on") toggleProofread(); |
| 232 | + }; |
| 233 | + window.addEventListener("keydown", onKey); |
| 234 | + return () => window.removeEventListener("keydown", onKey); |
| 235 | + }, [proofread, toggleProofread]); |
| 236 | + return <TopBar {...base} proofread={proofread} onToggleProofread={toggleProofread} />; |
| 237 | + } |
| 238 | + const entry = () => screen.getByTestId("topbar-proofread"); |
| 239 | + |
| 240 | + it("① 入口常驻在右簇动作按钮族里、有可读文案与快捷键提示(条件 ① 的字面要求)", () => { |
| 241 | + render(<Harness />); |
| 242 | + expect([entry().className.includes("ed-topbar__action"), entry().textContent?.trim(), entry().getAttribute("title") ?? ""], |
| 243 | + "入口不在动作按钮族 / 缺可读文案 / 缺快捷键提示").toEqual([true, "审校", "审校模式(Ctrl/⌘+Shift+R)"]); |
| 244 | + }); |
| 245 | + |
| 246 | + it("②③ 点一次 ⇒ `<html>` 置 `on`;再点 ⇒ **摘属性**(不是写成 \"off\")", () => { |
| 247 | + render(<Harness />); |
| 248 | + expect(document.documentElement.hasAttribute(PROOFREAD_MODE_ATTR), "起点必须干净").toBe(false); |
| 249 | + fireEvent.click(entry()); |
| 250 | + expect(mode(), "点击后没置属性").toBe("on"); |
| 251 | + fireEvent.click(entry()); |
| 252 | + expect(document.documentElement.hasAttribute(PROOFREAD_MODE_ATTR), "再点必须能退出(缺省不落属性)").toBe(false); |
| 253 | + }); |
| 254 | + |
| 255 | + it("④ 三出口合流:按钮打开的模式,`Esc` 与 `Ctrl+Shift+R` 都必须能关掉", () => { |
| 256 | + render(<Harness />); |
| 257 | + fireEvent.click(entry()); |
| 258 | + fireEvent.keyDown(window, { key: "Escape" }); |
| 259 | + expect(mode(), "Esc 对按钮打开的模式无效 ⇒ 按钮与快捷键各持一份 state").toBeNull(); |
| 260 | + fireEvent.click(entry()); |
| 261 | + fireEvent.keyDown(window, { key: "R", ctrlKey: true, shiftKey: true }); |
| 262 | + expect(mode(), "快捷键关不掉按钮打开的模式").toBeNull(); |
| 263 | + }); |
| 264 | +}); |
| 265 | + |
215 | 266 | describe("裁决 A3:AI toast 不在导航行里", () => { |
216 | 267 | it("toast 的定位/锚点/层级都在原语层(B13/B15 授权的机械改写)", () => { |
217 | 268 | // 原四条断言(`:182`–`:185`)断的是「`App.tsx` 源码里出现过自足 toast 的特征串」——批 3 |
|
0 commit comments