/** * 07-notify — 通知系统 * * 使用 vitest 原汁原味的 vi.fn() + mock 依赖注入模式。 * 不依赖真实 DOM 渲染,仅测试 Notify 核心逻辑。 */ import { describe, it, expect, beforeEach, beforeAll, vi } from 'vitest' import { Notify, setSysForNotify } from '../../src/composables/useNotify' // ---- Mock Sys 依赖 ---- function createMockSys(overrides: Record = {}) { return { settings: { notificationsEnabled: true, notifAllow: {} as Record, focus: false, ...overrides, }, renderDock: vi.fn(), } } let mockSys = createMockSys() setSysForNotify(mockSys) describe('07-notify — 通知系统', () => { beforeAll(() => { document.body.innerHTML = ` ` }) beforeEach(() => { Notify.list = [] mockSys = createMockSys() setSysForNotify(mockSys) }) // ==================== 基本发送 ==================== describe('发送通知', () => { it('初始列表为空', () => { expect(Notify.list).toHaveLength(0) }) it('send 添加通知到列表', () => { const n = Notify.send({ appId: 'finder', title: '测试', body: '内容', silent: true }) expect(n).not.toBeNull() expect(Notify.list).toHaveLength(1) expect(n!.appId).toBe('finder') expect(n!.title).toBe('测试') }) it('返回的通知包含 id 和 ts', () => { const n = Notify.send({ appId: 'f', title: 'T', body: 'B', silent: true }) expect(n!.id).toBeTruthy() expect(n!.ts).toBeGreaterThan(0) }) }) // ==================== 开关控制 ==================== describe('开关控制', () => { it('总开关关闭时 send 返回 null', () => { mockSys.settings.notificationsEnabled = false expect(Notify.send({ appId: 'f', title: 'T', body: 'B', silent: true })).toBeNull() }) it('per-app 关闭时 send 返回 null', () => { mockSys.settings.notifAllow['finder'] = false expect(Notify.send({ appId: 'finder', title: 'T', body: 'B', silent: true })).toBeNull() }) it('allowed() 正确判断', () => { expect(Notify.allowed('clock')).toBe(true) mockSys.settings.notifAllow['clock'] = false expect(Notify.allowed('clock')).toBe(false) mockSys.settings.notifAllow['clock'] = true mockSys.settings.notificationsEnabled = false expect(Notify.allowed('clock')).toBe(false) }) }) // ==================== 已读/未读 ==================== describe('已读/未读', () => { it('新通知默认未读', () => { const n = Notify.send({ appId: 'f', title: 'T', body: 'B', silent: true }) expect(n!.read).toBe(false) }) it('markRead 标记已读', () => { const n = Notify.send({ appId: 'f', title: 'T', body: 'B', silent: true }) Notify.markRead(n!.id) expect(n!.read).toBe(true) }) it('badgeCount 统计未读数', () => { Notify.send({ appId: 'clock', title: 'A', body: '1', silent: true }) Notify.send({ appId: 'clock', title: 'B', body: '2', silent: true }) expect(Notify.badgeCount('clock')).toBe(2) }) it('markRead 减少未读计数', () => { const n = Notify.send({ appId: 'clock', title: 'T', body: 'B', silent: true }) expect(Notify.badgeCount('clock')).toBe(1) Notify.markRead(n!.id) expect(Notify.badgeCount('clock')).toBe(0) }) }) // ==================== 专注模式 ==================== describe('专注模式', () => { it('突破型通知在专注模式下仍发送', () => { mockSys.settings.focus = true const n = Notify.send({ appId: 'clock', title: '计时器', body: '时间到!', breakthrough: true, silent: true }) expect(n).not.toBeNull() }) it('非突破型通知在专注模式下仍记录但不弹横幅', () => { mockSys.settings.focus = true const n = Notify.send({ appId: 'clock', title: '普通', body: '', silent: true }) expect(n).not.toBeNull() expect(Notify.list).toHaveLength(1) }) }) // ==================== 清理 ==================== describe('清理', () => { it('clearAll 清空所有通知', () => { Notify.send({ appId: 'f', title: 'T', body: 'B', silent: true }) expect(Notify.list).toHaveLength(1) Notify.clearAll() expect(Notify.list).toHaveLength(0) }) }) })