Source mirror · commit 369512f · 2026-10-26 · MIT · canonical repo: gitlab.com/jiapunk/upgrade-airlock
git clone https://upgrade-airlock.pages.dev/source/upgrade-airlock.git
upgrade-airlock / web / test / domstub.ts
web/test/domstub.ts69 lines · 2133 bytes
1// 自测用的最小 DOM 替身:getElementById 对任何 id 都给一个只记 innerHTML 的假元素,
2// 够 ui.render() 在 Node 里跑完一遍,好检查各语言渲染出的 HTML。不模拟排版(autoFit 直接跳过)。
3interface 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
13function 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
36export 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}