From 59c6902bc43869ef09f749e5384a354515706749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B2=A9=E5=B2=A9?= Date: Thu, 23 Jul 2026 09:43:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=20photos=20=E2=80=94?= =?UTF-8?q?=209=20=E6=B5=8B=E8=AF=95=20+=20=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/cases/23-photos.test.ts | 112 ++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 tests/cases/23-photos.test.ts diff --git a/tests/cases/23-photos.test.ts b/tests/cases/23-photos.test.ts new file mode 100644 index 0000000..f132872 --- /dev/null +++ b/tests/cases/23-photos.test.ts @@ -0,0 +1,112 @@ +/** + * 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) + }) + }) +})