web/test/domstub.ts69 lines · 2133 bytes
| 1 | // 自测用的最小 DOM 替身:getElementById 对任何 id 都给一个只记 innerHTML 的假元素, |
| 2 | // 够 ui.render() 在 Node 里跑完一遍,好检查各语言渲染出的 HTML。不模拟排版(autoFit 直接跳过)。 |
| 3 | interface FakeEl { |
| 4 | id: string |
| 5 | innerHTML: string |
| 6 | style: Record<string, unknown> |
| 7 | classList: { toggle(c: string, on?: boolean): boolean; contains(c: string): boolean; add(c: string): void; remove(c: string): void } |
| 8 | querySelector(): null |
| 9 | closest(): null |
| 10 | clientHeight: number |
| 11 | } |
| 12 | |
| 13 | function makeEl(id: string): FakeEl { |
| 14 | const cls = new Set<string>() |
| 15 | return { |
| 16 | id, |
| 17 | innerHTML: '', |
| 18 | style: { setProperty() {}, removeProperty() {} }, |
| 19 | classList: { |
| 20 | toggle(c, on) { |
| 21 | const want = on ?? !cls.has(c) |
| 22 | if (want) cls.add(c) |
| 23 | else cls.delete(c) |
| 24 | return want |
| 25 | }, |
| 26 | contains: (c) => cls.has(c), |
| 27 | add: (c) => void cls.add(c), |
| 28 | remove: (c) => void cls.delete(c), |
| 29 | }, |
| 30 | querySelector: () => null, |
| 31 | closest: () => null, |
| 32 | clientHeight: 0, |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | export function installDom() { |
| 37 | const g = globalThis as unknown as Record<string, unknown> |
| 38 | const els = new Map<string, FakeEl>() |
| 39 | const doc = { |
| 40 | title: '', |
| 41 | documentElement: { lang: '' }, |
| 42 | getElementById(id: string) { |
| 43 | let e = els.get(id) |
| 44 | if (!e) els.set(id, (e = makeEl(id))) |
| 45 | return e |
| 46 | }, |
| 47 | addEventListener() {}, |
| 48 | visibilityState: 'visible', |
| 49 | } |
| 50 | const mem = new Map<string, string>() |
| 51 | g.document = doc |
| 52 | g.localStorage = { |
| 53 | getItem: (k: string) => mem.get(k) ?? null, |
| 54 | setItem: (k: string, v: string) => void mem.set(k, String(v)), |
| 55 | removeItem: (k: string) => void mem.delete(k), |
| 56 | } |
| 57 | if (typeof g.addEventListener !== 'function') g.addEventListener = () => {} |
| 58 | return { |
| 59 | doc, |
| 60 | els, |
| 61 | mem, |
| 62 | /** 目前所有区块的 innerHTML 串在一起(=整个画面);收起的详情抽屉与快捷键说明不算在画面上 */ |
| 63 | html: () => |
| 64 | [...els.values()] |
| 65 | .filter((e) => !['details', 'help'].includes(e.id) || e.classList.contains('open')) |
| 66 | .map((e) => e.innerHTML) |
| 67 | .join('\n'), |
| 68 | } |
| 69 | } |