/** * 28-launchpad: Launchpad Vue 组件化测试 * 覆盖: show/hide、搜索过滤、网格排序、Escape/背景关闭、storeApp 过滤 */ import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { nextTick } from 'vue' import { setupDOM, bootSystem } from '../helpers' import { Launchpad } from '../../src/apps/launchpad/index' import { Apps } from '../../src/composables/useApps' describe('28-launchpad — Vue 组件化', () => { beforeEach(async () => { localStorage.clear() await bootSystem() }) afterEach(() => { Launchpad.hide() }) // ==================== show/hide ==================== describe('show/hide', () => { it('show() 创建 overlay', () => { Launchpad.show() expect(document.querySelector('.launchpad')).toBeTruthy() }) it('第二次 show() 先 hide 再 show(不重复)', () => { Launchpad.show() Launchpad.show() expect(document.querySelectorAll('.launchpad').length).toBe(1) }) it('hide() 移除 overlay(250ms 后)', async () => { Launchpad.show() Launchpad.hide() await new Promise(r => setTimeout(r, 300)) expect(document.querySelector('.launchpad')).toBeNull() }) }) // ==================== 结构 ==================== describe('结构', () => { it('有搜索框', () => { Launchpad.show() expect(document.querySelector('.lp-search')).toBeTruthy() }) it('有应用网格', () => { Launchpad.show() expect(document.querySelector('.lp-grid')).toBeTruthy() expect(document.querySelectorAll('.lp-cell').length).toBeGreaterThan(0) }) it('应用按名称排序', () => { Launchpad.show() const names = [...document.querySelectorAll('.lp-name')].map(e => e.textContent || '') const sorted = [...names].sort((a, b) => a.localeCompare(b, 'zh-Hans-CN')) expect(names).toEqual(sorted) }) it('不包含自身(launchpad)', () => { Launchpad.show() const names = [...document.querySelectorAll('.lp-name')].map(e => e.textContent) expect(names).not.toContain('启动台') }) }) // ==================== 搜索 ==================== describe('搜索', () => { it('输入关键字过滤应用', async () => { Launchpad.show() const input = document.querySelector('.lp-search') as HTMLInputElement input.value = '终端' input.dispatchEvent(new Event('input', { bubbles: true })) await nextTick() const names = [...document.querySelectorAll('.lp-name')].map(e => e.textContent) expect(names.length).toBeGreaterThanOrEqual(1) expect(names.every(n => n!.includes('终端'))).toBe(true) }) it('无匹配显示"没有匹配的 App"', async () => { Launchpad.show() const input = document.querySelector('.lp-search') as HTMLInputElement input.value = 'zzzzz_no_match' input.dispatchEvent(new Event('input', { bubbles: true })) await nextTick() expect(document.querySelector('.lp-empty')?.textContent).toContain('没有匹配') }) it('清除搜索恢复全量', async () => { Launchpad.show() const input = document.querySelector('.lp-search') as HTMLInputElement input.value = '终端'; input.dispatchEvent(new Event('input', { bubbles: true })) await nextTick() const filtered = document.querySelectorAll('.lp-cell').length input.value = ''; input.dispatchEvent(new Event('input', { bubbles: true })) await nextTick() expect(document.querySelectorAll('.lp-cell').length).toBeGreaterThan(filtered) }) }) // ==================== 关闭 ==================== describe('关闭', () => { it('Escape 键关闭', async () => { Launchpad.show() const lp = document.querySelector('.launchpad') as HTMLElement lp.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })) await new Promise(r => setTimeout(r, 300)) expect(document.querySelector('.launchpad')).toBeNull() }) it('点击背景关闭', async () => { Launchpad.show() const lp = document.querySelector('.launchpad') as HTMLElement lp.dispatchEvent(new MouseEvent('click', { bubbles: true })) await new Promise(r => setTimeout(r, 300)) expect(document.querySelector('.launchpad')).toBeNull() }) it('点击应用单元格关闭并打开应用', async () => { Launchpad.show() const cell = document.querySelector('.lp-cell') as HTMLElement cell.dispatchEvent(new MouseEvent('click', { bubbles: true })) await new Promise(r => setTimeout(r, 300)) expect(document.querySelector('.launchpad')).toBeNull() }) }) // ==================== Apps.open 拦截 ==================== describe('Apps.open 拦截', () => { it('open("launchpad") 调用 show', () => { const result = Apps.open('launchpad') expect(document.querySelector('.launchpad')).toBeTruthy() expect(result).toBeNull() }) }) })