import { describe, expect, it, vi } from 'vitest'; import { notificationFor, notificationsEnabled, previewText, setNotificationsEnabled, } from '@/lib/desktopNotifications'; import type { NotificationInput } from '@/lib/desktopNotifications'; /** Сообщение по умолчанию: обычное сообщение в канале сервера. */ function input(overrides: Partial = {}): NotificationInput { return { message: { type: 'default', content: 'привет', mentions: [], channel_id: 'c1', author_id: 'u2', }, meId: 'u1', isDirect: false, channelName: 'общий', guildId: 'g1', isChannelOpen: false, windowFocused: false, enabled: true, ...overrides, }; } describe('notificationFor', () => { it('уведомляет об упоминании в неактивной комнате', () => { const result = notificationFor(input({ message: { ...input().message, mentions: ['u1'] } })); expect(result).toMatchObject({ title: 'Упоминание в #общий', body: 'привет', channelId: 'c1' }); }); it('уведомляет о личном сообщении', () => { const result = notificationFor(input({ isDirect: true, channelName: 'Аня', guildId: null })); expect(result).toMatchObject({ title: 'Личное сообщение — Аня', guildId: null }); }); it('молчит об обычном сообщении в канале', () => { expect(notificationFor(input())).toBeNull(); }); it('молчит о собственных сообщениях', () => { const result = notificationFor( input({ isDirect: true, message: { ...input().message, author_id: 'u1' }, }), ); expect(result).toBeNull(); }); it('молчит, когда открытая комната в фокусе', () => { const result = notificationFor( input({ isDirect: true, isChannelOpen: true, windowFocused: true }), ); expect(result).toBeNull(); }); it('уведомляет об открытой комнате, если окно не в фокусе', () => { const result = notificationFor( input({ isDirect: true, isChannelOpen: true, windowFocused: false }), ); expect(result).not.toBeNull(); }); it('молчит при выключенных уведомлениях', () => { expect(notificationFor(input({ isDirect: true, enabled: false }))).toBeNull(); }); it('пропускает системные сообщения', () => { const result = notificationFor( input({ isDirect: true, message: { ...input().message, type: 'system' }, }), ); expect(result).toBeNull(); }); it('подставляет «Вложение» для сообщения без текста', () => { const result = notificationFor( input({ isDirect: true, message: { ...input().message, content: '', mentions: ['u1'] }, }), ); expect(result?.body).toBe('Вложение'); }); }); describe('previewText', () => { it('убирает разметку и лишние переводы строк', () => { expect(previewText('**жирный**\n\n[ссылка](https://example.com) `код`')).toBe( 'жирный ссылка код', ); }); it('обрезает длинный текст', () => { const result = previewText('а'.repeat(400)); expect(result.length).toBeLessThanOrEqual(140); expect(result.endsWith('…')).toBe(true); }); }); describe('предпочтение уведомлений', () => { it('включено по умолчанию и выключается переключателем', () => { expect(notificationsEnabled()).toBe(true); setNotificationsEnabled(false); expect(notificationsEnabled()).toBe(false); setNotificationsEnabled(true); expect(notificationsEnabled()).toBe(true); }); it('не падает, если localStorage недоступен', () => { const stub = vi.spyOn(window, 'localStorage', 'get').mockImplementation(() => { throw new Error('нет доступа'); }); expect(notificationsEnabled()).toBe(true); stub.mockRestore(); }); });