refactor: 使用 Naive UI 重构后台布局与黑白双主题
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { effectScope, nextTick, type EffectScope } from 'vue'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useTheme } from './useTheme'
|
||||
|
||||
let scope: EffectScope | undefined
|
||||
afterEach(() => {
|
||||
scope?.stop()
|
||||
scope = undefined
|
||||
localStorage.clear()
|
||||
delete document.documentElement.dataset.theme
|
||||
document.documentElement.style.colorScheme = ''
|
||||
vi.unstubAllGlobals()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
/** 可控系统主题,验证跟随模式与事件清理,不依赖测试机器偏好。 */
|
||||
function setup(dark = false) {
|
||||
const listeners = new Set<(event: MediaQueryListEvent) => void>()
|
||||
vi.stubGlobal('matchMedia', () => ({
|
||||
matches: dark,
|
||||
addEventListener: (_: string, listener: (event: MediaQueryListEvent) => void) => listeners.add(listener),
|
||||
removeEventListener: (_: string, listener: (event: MediaQueryListEvent) => void) => listeners.delete(listener)
|
||||
}))
|
||||
scope = effectScope()
|
||||
const state = scope.run(useTheme)!
|
||||
return {
|
||||
...state,
|
||||
listeners,
|
||||
change: (matches: boolean) => listeners.forEach(listener => listener({ matches } as MediaQueryListEvent))
|
||||
}
|
||||
}
|
||||
|
||||
describe('黑白双主题', () => {
|
||||
it('默认跟随系统并实时响应,销毁时移除监听', async () => {
|
||||
const state = setup()
|
||||
expect(state.preference.value).toBe('system')
|
||||
expect(document.documentElement.dataset.theme).toBe('light')
|
||||
state.change(true)
|
||||
await nextTick()
|
||||
expect(state.isDark.value).toBe(true)
|
||||
expect(document.documentElement.style.colorScheme).toBe('dark')
|
||||
expect(state.listeners.size).toBe(1)
|
||||
scope!.stop()
|
||||
expect(state.listeners.size).toBe(0)
|
||||
})
|
||||
|
||||
it('手动选择优先于系统,并持久化到下一次挂载', async () => {
|
||||
const state = setup(true)
|
||||
state.preference.value = 'light'
|
||||
await nextTick()
|
||||
state.change(true)
|
||||
await nextTick()
|
||||
expect(state.isDark.value).toBe(false)
|
||||
expect(localStorage.getItem('drama-studio-theme')).toBe('light')
|
||||
scope!.stop()
|
||||
const restored = setup(true)
|
||||
expect(restored.preference.value).toBe('light')
|
||||
expect(restored.theme.value).toBeNull()
|
||||
})
|
||||
|
||||
it('深浅模式同时更新 Naive 主题和业务颜色标记', async () => {
|
||||
const state = setup()
|
||||
expect(state.overrides.value.common?.primaryColor).toBe('#202020')
|
||||
state.preference.value = 'dark'
|
||||
await nextTick()
|
||||
expect(state.theme.value?.name).toBe('dark')
|
||||
expect(state.overrides.value.common?.primaryColor).toBe('#f5f5f5')
|
||||
expect(state.overrides.value.Button?.textColorPrimary).toBe('#171717')
|
||||
expect(document.documentElement.dataset.theme).toBe('dark')
|
||||
})
|
||||
|
||||
it('无效存储回退系统模式', () => {
|
||||
localStorage.setItem('drama-studio-theme', 'unexpected')
|
||||
const state = setup(true)
|
||||
expect(state.preference.value).toBe('system')
|
||||
expect(state.isDark.value).toBe(true)
|
||||
})
|
||||
|
||||
it('禁用存储仍能切换当前会话的主题', async () => {
|
||||
vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {
|
||||
throw new Error('storage blocked')
|
||||
})
|
||||
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
|
||||
throw new Error('storage blocked')
|
||||
})
|
||||
const state = setup()
|
||||
state.preference.value = 'dark'
|
||||
await nextTick()
|
||||
expect(document.documentElement.dataset.theme).toBe('dark')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user