|
25 | 25 | * 交互用 `fireEvent`;且**没有** `globals` ⇒ RTL 的 auto-cleanup 不生效,必须显式 `afterEach(cleanup)` |
26 | 26 | * (先例 `components/NoteColorPicker.test.tsx:11`),否则多次 `render` 后 `getByTestId` 会撞多重命中。 |
27 | 27 | */ |
28 | | -import { readFileSync } from "node:fs"; |
29 | | -import { dirname, join } from "node:path"; |
30 | | -import { fileURLToPath } from "node:url"; |
31 | 28 | import { cleanup, fireEvent, render, screen } from "@testing-library/react"; |
32 | 29 | import { afterEach, describe, expect, it, vi } from "vitest"; |
33 | 30 | import { TopBar } from "./TopBar"; |
34 | 31 | import { NAV_ENTRIES } from "./navRegistry"; |
35 | 32 | import { BREAKPOINTS } from "./breakpoints"; |
36 | | - |
37 | | -const HERE = dirname(fileURLToPath(import.meta.url)); |
38 | | -/** 判据前先剥注释(与 `ui/primitives` 层同一口径):注释里提到 transition 不算犯规 */ |
39 | | -const CSS = readFileSync(join(HERE, "TopBar.css"), "utf8").replace(/\/\*[\s\S]*?\*\//g, ""); |
40 | | -/** |
41 | | - * G1 改判(T14)的**真源侧**只读件:`motion.css` 的元素级回执 `button:not(.ed-btn)` 与那条唯一的 |
42 | | - * reduced-motion 块。顶栏的交互元素全是裸 `<button>` ⇒ 顶栏的时长/缓动**不是顶栏自己的事**: |
43 | | - * 真源在那条元素级规则里,顶栏再写一份就是 R1.1 明禁的第二个真源(`motion.css:63` 同款论证)。 |
44 | | - */ |
45 | | -const MOTION_CSS = readFileSync(join(HERE, "..", "ui", "primitives", "motion.css"), "utf8").replace(/\/\*[\s\S]*?\*\//g, ""); |
46 | | -const TOPBAR_TSX = readFileSync(join(HERE, "TopBar.tsx"), "utf8"); |
47 | | -const APP_TSX = readFileSync(join(HERE, "..", "App.tsx"), "utf8"); |
48 | | -const AI_TOAST_TSX = readFileSync(join(HERE, "aiToast.tsx"), "utf8").replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, ""); // 批 7 T1:AI toast 装配件的新家(**剥注释** —— 不剥则注释里的字面量会满足判据,变异体 m9 实测过这个洞) |
49 | | -/** |
50 | | - * App.tsx 的**只留代码**版本:剥块注释与整行 `//` 注释。 |
51 | | - * Why 必须剥:App.tsx 的注释里逐字出现了 `<nav>`、`zIndex("toast")` 这些判据串 —— 不剥的话 |
52 | | - * 「toast 已搬出导航行」这条判据只要注释还在就会**假绿**(正是本批反复打击的「测不到失败的检查」)。 |
53 | | - */ |
54 | | -const APP_CODE = APP_TSX.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, ""); |
55 | | - |
56 | | -/** |
57 | | - * T10(B13/B15 授权的机械改写):AI toast 的定位/锚点/层级判据搬到**原语层** —— 它们是 |
58 | | - * `ui/primitives/Toast.{css,tsx}` 的事实,覆盖**所有** `Toast` 消费者(不再只覆盖 `App.tsx` 一处)。 |
59 | | - * 取规则体用「`选择器 {` 到第一个 `}`」(同 `ui/primitives/Toast.placement.test.tsx:38`): |
60 | | - * 防「同一个串写在别的规则里也算过」。 |
61 | | - */ |
62 | | -const TOAST_CSS = readFileSync(join(HERE, "..", "ui", "primitives", "Toast.css"), "utf8") |
63 | | - .replace(/\r\n/g, "\n") |
64 | | - .replace(/\/\*[\s\S]*?\*\//g, ""); |
65 | | -const TOAST_TSX = readFileSync(join(HERE, "..", "ui", "primitives", "Toast.tsx"), "utf8"); |
66 | | -const toastRuleBody = (selector: string): string => { |
67 | | - const at = TOAST_CSS.indexOf(selector); |
68 | | - return at < 0 ? "" : TOAST_CSS.slice(at, TOAST_CSS.indexOf("}", at)); |
69 | | -}; |
70 | | -const TOAST_BASE_BODY = toastRuleBody(".ed-toast {"); |
71 | | -const TOAST_BELOW_NAV_BODY = toastRuleBody(".ed-toast--below-nav"); |
72 | | - |
73 | | -/** emoji / 图形字符(A2 裁决的机器判据)。`\p{Extended_Pictographic}` 是 Unicode 属性类,覆盖 ✨📡🎙 等 */ |
74 | | -const PICTO = /\p{Extended_Pictographic}/u; |
75 | | - |
76 | | -/** |
77 | | - * G1 改判的**扫描口径**(一处定义,判据与仪器自证共用 ⇒ 不会各写一份正则而漂移)。 |
78 | | - * `withoutTokenVars` 必须先剥掉 `var(--ed-x, <同值兜底>)` 整段:本仓的 token 习惯写法**带同值兜底** |
79 | | - * (`--ed-dur-micro, 120ms`)⇒ 不剥的话「不得写裸 ms/s」会把合法 token 用法判成犯规(**假红**, |
80 | | - * 逼实施者绕开守卫 —— 计划 Task 14 的 G1 新断言原文就有这个洞,T14 实施时实测并修正)。 |
81 | | - * 🔴 R1.2(改判不得弱于原判据):属性名还认**厂商前缀 / 大写**(同 `motion-coverage.test.ts:91` 的孪生守卫)—— 旧 G1 是子串匹配,`-webkit-transition:` 在它下面是**红**,新口径漏掉前缀支就是强度回退(T14c 专属变异体实测)。 |
82 | | - */ |
83 | | -const motionDecls = (css: string): string[] => |
84 | | - css.split(";").filter((d) => /(?:^|[;\s])(?:-(?:webkit|moz|ms|o)-)?(?:transition|animation)(?:-duration|-name)?\s*:/i.test(d)); |
85 | | -const withoutTokenVars = (decl: string): string => decl.replace(/var\(--ed-[a-z0-9-]+,[^)]*\)/g, ""); |
86 | | -const rawTimes = (decls: string[]): string[] => decls.filter((d) => /\d+(?:\.\d+)?\s*(?:ms|s)\b/.test(withoutTokenVars(d))); |
87 | | -/** 裸缓动的第三类:**裸关键字** `linear`/`ease`/`steps()`(Important ②)。边界用 `(?<![\w-])…(?![\w-])` 而非 `\b`:后者会把无兜底的合法 token `var(--ed-ease)` 与 `linear-gradient` 误判成犯规。 */ |
88 | | -const rawEases = (decls: string[]): string[] => |
89 | | - decls.filter((d) => /cubic-bezier|ease-(?:in|out|in-out)\b|(?<![\w-])(?:linear|ease|steps)(?![\w-])/i.test(withoutTokenVars(d))); |
| 33 | +import { |
| 34 | + AI_TOAST_TSX, APP_CODE, CSS, MOTION_CSS, PICTO, TOAST_BASE_BODY, TOAST_BELOW_NAV_BODY, |
| 35 | + TOAST_TSX, TOPBAR_TSX, motionDecls, rawEases, rawTimes, |
| 36 | +} from "./topBarTestKit"; |
90 | 37 |
|
91 | 38 | afterEach(cleanup); |
92 | 39 |
|
|
0 commit comments