dd-timer/tests/unit/infrastructure/LocalStorageRepository.test.ts
POL Mickaël 3e485fd09b chore: normalise fins de ligne CRLF → LF dans tout le repo
Applique .gitattributes sur tous les fichiers existants.
Élimine les différences fantômes entre WSL et Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 08:55:10 +02:00

78 lines
3.1 KiB
TypeScript
Executable File

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LocalStorageRepository } from '@infrastructure/persistence/LocalStorageRepository';
// Mock localStorage
const store: Record<string, string> = {};
const mockLocalStorage = {
getItem: vi.fn((key: string) => store[key] ?? null),
setItem: vi.fn((key: string, val: string) => { store[key] = val; }),
removeItem: vi.fn((key: string) => { delete store[key]; }),
};
beforeEach(() => {
Object.keys(store).forEach(k => delete store[k]);
vi.stubGlobal('localStorage', mockLocalStorage);
// No electronAPI in test
if (typeof window !== 'undefined') {
delete (window as any).electronAPI;
}
});
describe('LocalStorageRepository', () => {
it('load returns null if no stored data', async () => {
const repo = new LocalStorageRepository();
const result = await repo.load();
expect(result).toBeNull();
});
it('save then load round-trips', async () => {
const repo = new LocalStorageRepository();
const state = {
enclos: [], activeId: null, nextEnclosId: 1,
alarmSound: 'arpege', notifsEnabled: true, ntfyTopic: '',
archivedStats: [], inventaire: {}, workflows: [], accouplements: [],
};
repo.save(state as any);
const loaded = await repo.load();
expect(loaded).not.toBeNull();
expect(loaded!.alarmSound).toBe('arpege');
expect(loaded!.accouplements).toEqual([]);
});
it('save resets timer running state', async () => {
const repo = new LocalStorageRepository();
const state = {
enclos: [{
id: 1, name: 'E1', activeGauges: [], gaugeLevels: { baffeur: 0, caresseur: 0, foudroyeur: 0, abreuvoir: 0, dragofesse: 0, mangeoire: 0 },
dragodindes: [], nextDdId: 1,
timer: { running: true, startTime: Date.now(), pausedAt: null, pausedMs: 0, snapGauges: {}, snapStats: {} },
alerted: {},
}],
activeId: 1, nextEnclosId: 2,
alarmSound: 'arpege', notifsEnabled: true, ntfyTopic: '',
archivedStats: [], inventaire: {}, workflows: [], accouplements: [],
};
repo.save(state as any);
const loaded = await repo.load();
expect(loaded!.enclos[0]!.timer.running).toBe(false);
});
it('deserialize applies default targets migration', async () => {
const repo = new LocalStorageRepository();
const raw = JSON.stringify({
enclos: [{
id: 1, name: 'E1', activeGauges: [], gaugeLevels: { baffeur: 0, caresseur: 0, foudroyeur: 0, abreuvoir: 0, dragofesse: 0, mangeoire: 0 },
dragodindes: [{ id: 1, name: 'DD1', stats: { serenite: 0, endurance: 0, maturite: 0, amour: 0 }, targets: {}, race: '', gender: 'n', reproducteur: 0 }],
nextDdId: 2,
timer: { running: false, startTime: null, pausedAt: null, pausedMs: 0, snapGauges: {}, snapStats: {} },
alerted: {},
}],
activeId: 1, nextEnclosId: 2, alarmSound: 'arpege', notifsEnabled: true,
});
store['dd3v3'] = raw;
const loaded = await repo.load();
expect(loaded!.enclos[0]!.dragodindes[0]!.targets.baffeur).toBe(-5000);
expect(loaded!.enclos[0]!.dragodindes[0]!.stats.xp).toBe(1);
});
});