refactor(web): adopt modular Vue architecture and isolate bearer sessions

This commit is contained in:
eezd committed 2026-10-25 20:47:32 +08:00
1 parent 1b2b2b12ff
commit 3cd641a380
379 files changed
+19736 -8712

No files matched your search

@@ -0,0 +1,97 @@
// @vitest-environment happy-dom
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { watchFontLoading } from './font-loading'
class Fonts extends EventTarget {
status = 'loading'
faces: { status: string }[] = []
resolve!: () => void
ready = this.pending()
pending() {
return new Promise<void>((resolve) => {
this.resolve = resolve
})
}
[Symbol.iterator]() {
return this.faces[Symbol.iterator]()
}
finish() {
this.status = 'loaded'; this.resolve()
}
load() {
this.status = 'loading'; this.ready = this.pending(); this.dispatchEvent(new Event('loading'))
}
}
let fonts: Fonts
let frame: FrameRequestCallback
const descriptor = Object.getOwnPropertyDescriptor(document, 'fonts')
beforeEach(() => {
document.body.innerHTML = '<div id="app">正文</div>'
fonts = new Fonts()
Object.defineProperty(document, 'fonts', { configurable: true, value: fonts })
vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => {
frame = callback; return 1
})
watchFontLoading()
})
afterEach(() => {
vi.unstubAllGlobals()
delete document.documentElement.dataset.rmFonts
document.body.innerHTML = ''
if (descriptor) Object.defineProperty(document, 'fonts', descriptor)
else Reflect.deleteProperty(document, 'fonts')
})
it('挂载后的字体就绪前保持隐藏,等待结束才显示', async () => {
expect(document.documentElement.dataset.rmFonts).toBe('loading')
frame(0)
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('loading')
fonts.finish()
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('ready')
})
it('字体已在缓存中时直接显示,不依赖再次触发 loading 事件', async () => {
fonts.finish()
frame(0)
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('ready')
})
it('切换笔记或字形需要新字体时重新等待,不用旧一轮完成信号提前显示', async () => {
frame(0)
fonts.finish()
fonts.load()
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('loading')
fonts.finish()
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('ready')
})
it('字体失败显示可刷新提示,之后 ready 完成和其他字体加载不能显示备用字体', async () => {
frame(0)
fonts.dispatchEvent(new Event('loadingerror'))
fonts.dispatchEvent(new Event('loadingerror'))
fonts.finish()
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('error')
expect(document.querySelectorAll('[role="alert"]')).toHaveLength(1)
expect(document.querySelector('[role="alert"]')!.textContent).toContain('字体加载失败')
expect(document.querySelector('[role="alert"] button')!.textContent).toBe('刷新页面')
fonts.load()
expect(document.documentElement.dataset.rmFonts).toBe('error')
})
it('开始监听前已有字体失败,也不能被 resolved ready 当作成功', async () => {
fonts.faces = [{ status: 'error' }]
fonts.finish()
frame(0)
await Promise.resolve()
expect(document.documentElement.dataset.rmFonts).toBe('error')
expect(document.querySelector('[role="alert"]')).not.toBeNull()
})