import { describe, it, expect, beforeEach } from 'vitest'; import { loadOfflinePref, saveOfflinePref, offlineReady, missingDicts, offlinePreloadEligible, shouldBootOffline } from './offline'; import type { Variant } from './model'; // A minimal in-memory localStorage for the persistence tests (node has none). beforeEach(() => { const store = new Map(); (globalThis as unknown as { localStorage: Storage }).localStorage = { getItem: (k: string) => store.get(k) ?? null, setItem: (k: string, v: string) => void store.set(k, v), removeItem: (k: string) => void store.delete(k), clear: () => store.clear(), key: () => null, length: 0, } as Storage; }); describe('offline mode helpers', () => { it('persists and reads the device-scoped offline flag', () => { expect(loadOfflinePref()).toBe(false); saveOfflinePref(true); expect(loadOfflinePref()).toBe(true); saveOfflinePref(false); expect(loadOfflinePref()).toBe(false); }); it('offlineReady requires every enabled variant to have a dictionary', () => { const has = (v: Variant): boolean => v !== 'erudit_ru'; expect(offlineReady(['scrabble_en', 'scrabble_ru'], has)).toBe(true); expect(offlineReady(['scrabble_en', 'erudit_ru'], has)).toBe(false); expect(offlineReady([], has)).toBe(false); }); it('missingDicts lists the enabled variants without a dictionary', () => { const has = (v: Variant): boolean => v === 'scrabble_en'; expect(missingDicts(['scrabble_en', 'scrabble_ru', 'erudit_ru'], has)).toEqual(['scrabble_ru', 'erudit_ru']); }); it('offlinePreloadEligible requires a standalone PWA with email, online, outside mini-apps', () => { const base = { hasEmail: true, standalone: true, inTelegram: false, inVK: false, online: true }; expect(offlinePreloadEligible(base)).toBe(true); expect(offlinePreloadEligible({ ...base, hasEmail: false })).toBe(false); expect(offlinePreloadEligible({ ...base, standalone: false })).toBe(false); expect(offlinePreloadEligible({ ...base, inTelegram: true })).toBe(false); expect(offlinePreloadEligible({ ...base, inVK: true })).toBe(false); expect(offlinePreloadEligible({ ...base, online: false })).toBe(false); }); it('shouldBootOffline requires the offline flag plus a cached session and profile', () => { expect(shouldBootOffline({ offlineActive: true, hasSession: true, hasProfile: true })).toBe(true); expect(shouldBootOffline({ offlineActive: false, hasSession: true, hasProfile: true })).toBe(false); expect(shouldBootOffline({ offlineActive: true, hasSession: false, hasProfile: true })).toBe(false); expect(shouldBootOffline({ offlineActive: true, hasSession: true, hasProfile: false })).toBe(false); }); });