85 lines
3.6 KiB
TypeScript
85 lines
3.6 KiB
TypeScript
/**
|
||
* 25-quicktime: 深层测试——视频列表、导航、进度、音量、边界
|
||
*/
|
||
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||
import { h, render, nextTick } from 'vue'
|
||
import QuickTimeComponent from '../../src/apps/quicktime/QuickTime.vue'
|
||
import { setupDOM } from '../helpers'
|
||
|
||
function makeMockWin() {
|
||
const el = document.createElement('div'); el.className = 'window'
|
||
document.getElementById('window-layer')?.appendChild(el)
|
||
const body = document.createElement('div'); body.className = 'win-body'
|
||
el.appendChild(body)
|
||
return { id: 'qt', appId: 'quicktime', el, body, timers: [] as any[], data: {} }
|
||
}
|
||
function click(win: any, selector: string) {
|
||
(win.body.querySelector(selector) as HTMLElement)?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
||
}
|
||
|
||
describe('25-quicktime — 深层测试', () => {
|
||
let win: any
|
||
beforeEach(() => { setupDOM(); win = makeMockWin() })
|
||
afterEach(() => { render(null, win.body); win.el.remove() })
|
||
function mount() { const v = h(QuickTimeComponent, { win }); render(v, win.body) }
|
||
|
||
describe('结构', () => {
|
||
it('播放器完整结构', () => { mount(); expect(win.body.querySelector('.qt-stage')).toBeTruthy(); expect(win.body.querySelector('.qt-controls')).toBeTruthy() })
|
||
it('3 个控制按钮', () => { mount(); expect(win.body.querySelectorAll('.qt-btn').length).toBe(3) })
|
||
it('进度条和音量存在', () => { mount(); expect(win.body.querySelector('.qt-seek')).toBeTruthy(); expect(win.body.querySelector('.qt-vol')).toBeTruthy() })
|
||
it('时间显示初始值', () => { mount(); expect(win.body.querySelectorAll('.music-time').length).toBe(2) })
|
||
})
|
||
|
||
describe('导航', () => {
|
||
it('nav(1) 切换到下一个视频', async () => {
|
||
mount(); win.appState.nav(1); await nextTick()
|
||
// 第二个视频加载(花朵 → 星期五)
|
||
expect(win.body.querySelector('.qt-stage')).toBeTruthy()
|
||
})
|
||
it('nav(-1) 切换到上一个视频', async () => {
|
||
mount(); win.appState.nav(-1); await nextTick()
|
||
// 循环到最后一个(花朵 ← 大雄兔)
|
||
expect(win.body.querySelector('.qt-stage')).toBeTruthy()
|
||
})
|
||
it('连续 nav 循环', async () => {
|
||
mount()
|
||
for (let i = 0; i < 3; i++) { win.appState.nav(1); await nextTick() }
|
||
// 3 次后应回到第一个
|
||
expect(win.body.querySelector('.qt-stage')).toBeTruthy()
|
||
})
|
||
})
|
||
|
||
describe('控制', () => {
|
||
it('toggle 存在且可调用(jsdom 无 video API,验证不抛异常)', () => {
|
||
mount()
|
||
expect(typeof win.appState.toggle).toBe('function')
|
||
try { win.appState.toggle() } catch { /* video API 缺失 */ }
|
||
})
|
||
it('播放按钮点击', () => { mount(); click(win, '.qt-btn'); expect(win.body.querySelector('.qt-btn')).toBeTruthy() })
|
||
it('进度条拖拽', () => {
|
||
mount()
|
||
const seek = win.body.querySelector('.qt-seek') as HTMLInputElement
|
||
seek.value = '50'; seek.dispatchEvent(new Event('input', { bubbles: true }))
|
||
expect(seek.value).toBe('50')
|
||
})
|
||
it('音量拖拽触发 onVol', () => {
|
||
mount()
|
||
const vol = win.body.querySelector('.qt-vol') as HTMLInputElement
|
||
vol.value = '30'; vol.dispatchEvent(new Event('input', { bubbles: true }))
|
||
expect(vol.value).toBe('30')
|
||
})
|
||
})
|
||
|
||
describe('appState', () => {
|
||
it('暴露 toggle/nav', () => {
|
||
mount()
|
||
expect(typeof win.appState.toggle).toBe('function')
|
||
expect(typeof win.appState.nav).toBe('function')
|
||
})
|
||
})
|
||
|
||
describe('卸载', () => {
|
||
it('卸载后 DOM 为空', () => { mount(); render(null, win.body); expect(win.body.children.length).toBe(0) })
|
||
})
|
||
})
|