macos-web/tests/cases/43-keynote.test.ts

50 lines
4.3 KiB
TypeScript

/**
* 43-keynote: Keynote — 深层测试
* 覆盖: 缩略图(3)+舞台(2)+导航(5)+播放(5)+边界(3)+卸载
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { h, render, nextTick } from 'vue'
import KeynoteComponent from '../../src/apps/keynote/Keynote.vue'
import { setupDOM } from '../helpers'
describe('43-keynote', () => {
let body: HTMLElement, el: HTMLElement, win: any
beforeEach(() => { setupDOM(); el = document.createElement('div'); el.className = 'window'; document.body.appendChild(el); body = document.createElement('div'); body.className = 'win-body'; el.appendChild(body); win = { el, body, timers: [], data: {} } })
afterEach(() => { render(null, body); el.remove() })
function m() { render(h(KeynoteComponent, { win }), body) }
function title() { return body.querySelector('.kn-title')?.textContent || '' }
function clickThumb(i: number) { (body.querySelectorAll('.kn-thumb')[i] as HTMLElement)?.dispatchEvent(new MouseEvent('click', { bubbles: true })) }
function key(k: string) { el.dispatchEvent(new KeyboardEvent('keydown', { key: k })) }
// --- 缩略图 ---
it('4 张幻灯片', () => { m(); expect(body.querySelectorAll('.kn-thumb').length).toBe(4) })
it('每张有标题', () => { m(); body.querySelectorAll('.kn-thumb').forEach(t => expect(t.textContent).toBeTruthy()) })
it('每张有渐变色背景', () => { m(); body.querySelectorAll('.kn-thumb').forEach(t => expect((t as HTMLElement).style.background).toBeTruthy()) })
// --- 舞台 ---
it('kn-slide 存在', () => { m(); expect(body.querySelector('.kn-slide')).toBeTruthy() })
it('默认标题含 macOS', () => { m(); expect(title()).toContain('macOS') })
// --- 导航 ---
it('点击缩略图切换', async () => { m(); clickThumb(1); await nextTick(); expect(title()).toContain('虚拟文件') })
it('ArrowRight→下一张', async () => { m(); await nextTick(); key('ArrowRight'); await nextTick(); expect(title()).toContain('虚拟文件') })
it('ArrowLeft→上一张', async () => { m(); await nextTick(); key('ArrowRight'); await nextTick(); key('ArrowLeft'); await nextTick(); expect(title()).toContain('macOS') })
it('到最后一张不越界', async () => { m(); await nextTick(); for (let i = 0; i < 10; i++) key('ArrowRight'); await nextTick(); expect(title()).toContain('谢谢观看') })
it('到第一张不越界', async () => { m(); await nextTick(); for (let i = 0; i < 10; i++) key('ArrowUp'); await nextTick(); expect(title()).toContain('macOS') })
// --- 播放 ---
it('appState.play 存在', () => { m(); expect(typeof win.appState.play).toBe('function') })
it('play→全屏显示', async () => { m(); win.appState.play(); await nextTick(); expect(body.querySelector('.kn-play')).toBeTruthy() })
it('播放中 Space→翻页', async () => { m(); win.appState.play(); await nextTick(); (body.querySelector('.kn-play') as HTMLElement).dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); await nextTick(); expect(body.querySelector('.kn-play .kn-title')?.textContent).toContain('虚拟文件') })
it('Escape→退出播放', async () => { m(); win.appState.play(); await nextTick(); (body.querySelector('.kn-play') as HTMLElement).dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); await nextTick(); expect(body.querySelector('.kn-play')).toBeNull() })
it('翻到最后播放结束', async () => { m(); win.appState.play(); await nextTick(); const play = body.querySelector('.kn-play') as HTMLElement; for (let i = 0; i < 5; i++) { play.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); await nextTick() }; expect(body.querySelector('.kn-play')).toBeNull() })
// --- 边界 ---
it('播放时键盘导航被拦截', async () => { m(); win.appState.play(); await nextTick(); key('ArrowRight'); await nextTick(); expect(title()).toContain('macOS') })
it('非播放时 Space 不触发', async () => { m(); await nextTick(); el.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); await nextTick(); expect(title()).toContain('macOS') })
it('卸载时移除 eventListener', () => { m(); render(null, body); expect(() => el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))).not.toThrow() })
// --- 卸载 ---
it('卸载后 DOM 为空', () => { m(); render(null, body); expect(body.children.length).toBe(0) })
})