- Unit : domain (GaugeCalculator, Enclos, Dragodinde, XpTable, Race, Tier...) - Unit : application (commands, queries, CommandBus) - Fonctionnel : breeding-workflow, enclos-management, timer-workflow - Régression : gauge-tier, gauge-recharge, xp-timer, level-target, breeding - E2E Playwright + Electron : navigation, timer, recharge jauge, accouplement, persistance des données Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
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);
|
|
});
|
|
});
|