import { describe, expect, it, vi } from 'vitest'; import { AUDIO, STORAGE_KEYS } from '../../app/static/js/config.js'; import { SafeStorage, Settings } from '../../app/static/js/systems/Settings.js'; /** Einfacher localStorage-Ersatz. */ function memoryBackend(initial = {}) { const data = new Map(Object.entries(initial)); return { getItem: (key) => (data.has(key) ? data.get(key) : null), setItem: (key, value) => data.set(key, String(value)), data, }; } /** localStorage, der jeden Zugriff verweigert (z. B. blockierte Website-Daten). */ const throwingBackend = { getItem: () => { throw new Error('SecurityError'); }, setItem: () => { throw new Error('QuotaExceededError'); }, }; describe('SafeStorage', () => { it('speichert JSON-Werte', () => { const storage = new SafeStorage(memoryBackend()); expect(storage.set('a', { x: 1 })).toBe(true); expect(storage.get('a', null)).toEqual({ x: 1 }); }); it('liefert den Standardwert bei fehlenden oder kaputten Einträgen', () => { const storage = new SafeStorage(memoryBackend({ kaputt: '{nicht json' })); expect(storage.get('fehlt', 42)).toBe(42); expect(storage.get('kaputt', 'fallback')).toBe('fallback'); }); it('funktioniert ohne localStorage für die laufende Sitzung weiter', () => { for (const backend of [null, throwingBackend]) { const storage = new SafeStorage(backend); expect(storage.set('k', 'v')).toBe(false); expect(storage.get('k', null)).toBe('v'); } }); }); describe('Settings', () => { it('merkt sich die Disclaimer-Bestätigung', () => { const backend = memoryBackend(); const settings = new Settings(new SafeStorage(backend)); expect(settings.isDisclaimerAccepted()).toBe(false); settings.acceptDisclaimer(); expect(new Settings(new SafeStorage(backend)).isDisclaimerAccepted()).toBe(true); expect(backend.data.get(STORAGE_KEYS.disclaimerAccepted)).toBe('true'); }); it('startet mit Standardlautstärken und begrenzt Werte auf 0..1', () => { const settings = new Settings(new SafeStorage(memoryBackend())); expect(settings.getVolume('music')).toBe(AUDIO.defaultVolumes.music); settings.setVolume('music', 5); expect(settings.getVolume('music')).toBe(1); settings.setVolume('sfx', -1); expect(settings.getVolume('sfx')).toBe(0); settings.setVolume('sfx', Number.NaN); expect(settings.getVolume('sfx')).toBe(0); }); it('ignoriert ungültige gespeicherte Lautstärken', () => { const backend = memoryBackend({ [STORAGE_KEYS.volumes]: JSON.stringify({ master: 'laut', music: 0.25, sfx: 9 }) }); const settings = new Settings(new SafeStorage(backend)); expect(settings.getVolume('master')).toBe(AUDIO.defaultVolumes.master); expect(settings.getVolume('music')).toBe(0.25); expect(settings.getVolume('sfx')).toBe(1); }); it('berechnet die effektive Lautstärke inklusive Gesamtregler', () => { const settings = new Settings(new SafeStorage(memoryBackend())); settings.setVolume('master', 0.5); settings.setVolume('sfx', 0.5); expect(settings.effectiveVolume('sfx')).toBeCloseTo(0.25); }); it('benachrichtigt Zuhörer bei Lautstärkeänderungen', () => { const settings = new Settings(new SafeStorage(memoryBackend())); const listener = vi.fn(); const unsubscribe = settings.onVolumeChange(listener); settings.setVolume('master', 0.3); expect(listener).toHaveBeenCalledWith('master', 0.3); unsubscribe(); settings.setVolume('master', 0.4); expect(listener).toHaveBeenCalledTimes(1); }); it('speichert den letzten Namen gekürzt', () => { const settings = new Settings(new SafeStorage(memoryBackend())); settings.setLastName('A'.repeat(40)); expect(settings.getLastName()).toHaveLength(16); }); });