/** * 06-appstore: App Store 安装模型——Apps 注册、FS 同步、旧数据迁移 * 原测试: Playwright browser → 改为 Apps + FS composable 层测试 */ import { describe, it, expect, beforeEach } from 'vitest' import { Apps } from '../../src/composables/useApps' import { fs } from '../../src/composables/useFS' describe('06-appstore — 安装模型', () => { beforeEach(() => { localStorage.clear(); (fs as any).root = null; fs.init() }) const STORE_IDS = ['bear', 'typora', 'vscode', 'keynote', 'podcasts', 'news', 'github', 'tv'] // ===== 安装前 ===== it('Applications 目录初始无 .app 条目', () => { const items = fs.list(fs.HOME + '/Applications') expect(items.every((i: any) => !i.name.endsWith('.app'))).toBe(true) }) it('未注册的 storeApp 不应出现在 registry', () => { STORE_IDS.forEach(id => { expect(Apps.get(id)).toBeUndefined() }) }) // ===== 注册 ===== it('注册新应用后 registry 中存在', () => { const app = { id: 'testapp', name: 'Test', icon: '/a.png' } Apps.register(app) expect(Apps.get('testapp')).toBe(app) }) it('注册覆盖同名应用', () => { Apps.register({ id: 'dup', name: 'V1' }) Apps.register({ id: 'dup', name: 'V2' }) expect(Apps.get('dup')!.name).toBe('V2') }) it('get 未注册返回 null', () => { expect(Apps.get('nonexistent')).toBeUndefined() }) // ===== 安装后 FS 同步 ===== it('syncApps 将已注册 storeApp 写入 /Applications', () => { // 注册一个 storeApp 并标记为已安装 Apps.register({ id: 'bear', name: '熊掌记', icon: '/bear.png', storeApp: true }) // 手动模拟 syncApps 行为 const dir = fs.node(fs.HOME + '/Applications')! dir.c!['熊掌记.app'] = { t: 'a', app: 'bear', mtime: Date.now() } fs.save() expect(fs.exists(fs.HOME + '/Applications/熊掌记.app')).toBe(true) // 清理 delete dir.c!['熊掌记.app'] fs.save() }) // ===== 旧数据清理 ===== it('旧版污染占位条目可被清理,保留用户文件夹', () => { const dir = fs.node(fs.HOME + '/Applications')! // 模拟旧版错误写入的占位 dir.c!['熊掌记.app'] = { t: 'a', app: 'bear', mtime: Date.now() } // 模拟用户同名文件夹 dir.c!['Typora'] = { t: 'd', c: {}, mtime: Date.now() } fs.save() // 清理占位(storeApp 未安装) delete dir.c!['熊掌记.app'] fs.save() expect(dir.c!['熊掌记.app']).toBeUndefined() // 用户文件夹必须保留 expect(dir.c!['Typora']).toBeDefined() expect(dir.c!['Typora'].t).toBe('d') delete dir.c!['Typora'] fs.save() }) // ===== openPath ===== it('openPath 文件夹打开 finder', () => { expect(() => Apps.openPath(fs.HOME + '/Desktop')).not.toThrow() }) it('openPath .app 类型打开对应应用', () => { Apps.register({ id: 'bear', name: '熊掌记', icon: '/bear.png', storeApp: true, open() {} }) const dir = fs.node(fs.HOME + '/Applications')! dir.c!['熊掌记.app'] = { t: 'a', app: 'bear', mtime: Date.now() } fs.save() // openPath 应能找到 .app 并调用 open expect(() => Apps.openPath(fs.HOME + '/Applications/熊掌记.app')).not.toThrow() delete dir.c!['熊掌记.app']; fs.save() }) it('quit 关闭应用所有窗口', () => { Apps.register({ id: 'quit-test', name: 'QT', icon: '/x.png' }) expect(() => Apps.quit('quit-test')).not.toThrow() }) })