macos-web/tests/cases/23-photos.test.ts

113 lines
4.2 KiB
TypeScript

/**
* 23-photos: Photos Vue 组件化测试
*
* 覆盖: 挂载、照片网格、lightbox 打开/关闭/导航、键盘控制、图片错误回退
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { h, render, nextTick } from 'vue'
import PhotosComponent from '../../src/apps/photos/Photos.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: 'ph', appId: 'photos', el, body, timers: [] as any[], data: {} }
}
describe('23-photos — Photos Vue 组件化', () => {
let win: any
beforeEach(() => { setupDOM(); win = makeMockWin() })
afterEach(() => { render(null, win.body); win.el.remove() })
function mount() { const v = h(PhotosComponent, { win }); render(v, win.body) }
// ==================== 初始化 ====================
describe('初始化', () => {
it('挂载后渲染照片网格', () => {
mount()
expect(win.body.querySelector('.photos-grid')).toBeTruthy()
expect(win.body.querySelectorAll('.photo-cell').length).toBeGreaterThan(0)
})
it('侧边栏包含图库选项', () => {
mount()
const items = win.body.querySelectorAll('.fb-side-item')
expect(items.length).toBeGreaterThanOrEqual(3)
})
it('每张照片有名称', () => {
mount()
const names = [...win.body.querySelectorAll('.photo-name')].map(e => e.textContent)
expect(names.length).toBeGreaterThan(0)
expect(names.every(n => !!n)).toBe(true)
})
})
// ==================== Lightbox ====================
describe('Lightbox', () => {
it('点击照片打开 lightbox', async () => {
mount()
const cell = win.body.querySelector('.photo-cell') as HTMLElement
cell.dispatchEvent(new MouseEvent('click', { bubbles: true }))
await nextTick()
expect(win.body.querySelector('.lightbox')).toBeTruthy()
})
it('lightbox 显示计数器', async () => {
mount()
const cell = win.body.querySelector('.photo-cell') as HTMLElement
cell.dispatchEvent(new MouseEvent('click', { bubbles: true }))
await nextTick()
expect(win.body.querySelector('.lb-counter')?.textContent).toContain('/')
})
it('关闭按钮关闭 lightbox', async () => {
mount();
(win.body.querySelector('.photo-cell') as HTMLElement)
.dispatchEvent(new MouseEvent('click', { bubbles: true }))
await nextTick()
const closeBtn = win.body.querySelector('.lb-close') as HTMLElement
closeBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }))
await nextTick()
expect(win.body.querySelector('.lightbox')).toBeNull()
})
it('Escape 关闭 lightbox', async () => {
mount();
(win.body.querySelector('.photo-cell') as HTMLElement)
.dispatchEvent(new MouseEvent('click', { bubbles: true }))
await nextTick()
const lb = win.body.querySelector('.lightbox') as HTMLElement
lb.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
await nextTick()
expect(win.body.querySelector('.lightbox')).toBeNull()
})
it('ArrowRight 切换到下一张', async () => {
mount();
(win.body.querySelector('.photo-cell') as HTMLElement)
.dispatchEvent(new MouseEvent('click', { bubbles: true }))
await nextTick()
const counter = win.body.querySelector('.lb-counter')?.textContent || ''
const m = counter.match(/(\d+) \/ (\d+)/)
if (m && +m[1] < +m[2]) {
const lb = win.body.querySelector('.lightbox') as HTMLElement
lb.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }))
await nextTick()
const newCounter = win.body.querySelector('.lb-counter')?.textContent
expect(newCounter).not.toBe(counter)
}
})
})
// ==================== 卸载 ====================
describe('卸载', () => {
it('卸载后 DOM 为空', () => {
mount(); render(null, win.body)
expect(win.body.children.length).toBe(0)
})
})
})