dd-timer/tests/unit/presentation/UndoManager.test.ts
POL Mickaël 8af626dd66 sécurité: audit commercialisation — hardening + 366 tests (24 E2E)
Sandbox Electron, HTTPS ntfy, validation import structurelle,
suppression executeJavaScript, nettoyage memory leaks, try/catch
sur tous les appels electronAPI. 27 nouveaux tests de sécurité
et validation. README mis à jour avec changelog et couverture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 07:18:28 +02:00

131 lines
4.2 KiB
TypeScript

// @vitest-environment happy-dom
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { UndoManager } from '@presentation/services/UndoManager';
describe('UndoManager', () => {
beforeEach(() => {
UndoManager.clear();
// Reset le callback
UndoManager.onUndo(() => {});
});
afterEach(() => {
// Toujours nettoyer electronAPI sans supprimer window
delete (window as any).electronAPI;
});
describe('snapshot / canUndo / label', () => {
it('canUndo retourne false sans snapshot', () => {
expect(UndoManager.canUndo()).toBe(false);
});
it('canUndo retourne true après un snapshot', () => {
UndoManager.snapshot('{"data":1}', 'test');
expect(UndoManager.canUndo()).toBe(true);
});
it('label retourne le label du snapshot', () => {
UndoManager.snapshot('{}', 'Suppression enclos');
expect(UndoManager.label()).toBe('Suppression enclos');
});
it('label retourne une chaîne vide sans snapshot', () => {
expect(UndoManager.label()).toBe('');
});
it('un nouveau snapshot écrase le précédent (1 seul niveau)', () => {
UndoManager.snapshot('{"v":1}', 'premier');
UndoManager.snapshot('{"v":2}', 'second');
expect(UndoManager.label()).toBe('second');
});
});
describe('clear', () => {
it('clear supprime le snapshot', () => {
UndoManager.snapshot('{}', 'test');
expect(UndoManager.canUndo()).toBe(true);
UndoManager.clear();
expect(UndoManager.canUndo()).toBe(false);
});
it('label retourne vide après clear', () => {
UndoManager.snapshot('{}', 'test');
UndoManager.clear();
expect(UndoManager.label()).toBe('');
});
});
describe('expiration', () => {
it('canUndo retourne false après expiration (10s)', () => {
UndoManager.snapshot('{}', 'test');
vi.spyOn(Date, 'now').mockReturnValue(Date.now() + 11_000);
expect(UndoManager.canUndo()).toBe(false);
vi.restoreAllMocks();
});
it('canUndo retourne true avant expiration', () => {
UndoManager.snapshot('{}', 'test');
vi.spyOn(Date, 'now').mockReturnValue(Date.now() + 5_000);
expect(UndoManager.canUndo()).toBe(true);
vi.restoreAllMocks();
});
});
describe('undo', () => {
it('undo retourne false sans snapshot', () => {
expect(UndoManager.undo()).toBe(false);
});
it('undo retourne false sans electronAPI', () => {
UndoManager.snapshot('{}', 'test');
expect(UndoManager.undo()).toBe(false);
});
it('undo appelle saveData et le callback', () => {
const saveData = vi.fn();
const undoCallback = vi.fn();
(window as any).electronAPI = { saveData };
UndoManager.onUndo(undoCallback);
UndoManager.snapshot('{"restored":true}', 'test');
const result = UndoManager.undo();
expect(result).toBe(true);
expect(saveData).toHaveBeenCalledWith('{"restored":true}');
expect(undoCallback).toHaveBeenCalled();
expect(UndoManager.canUndo()).toBe(false);
});
it('undo ne fonctionne pas deux fois de suite', () => {
const saveData = vi.fn();
(window as any).electronAPI = { saveData };
UndoManager.onUndo(() => {});
UndoManager.snapshot('{}', 'test');
UndoManager.undo();
expect(UndoManager.undo()).toBe(false);
});
});
describe('snapshotCurrent', () => {
it('retourne false sans electronAPI', async () => {
const result = await UndoManager.snapshotCurrent('test');
expect(result).toBe(false);
});
it('retourne false si loadData retourne null', async () => {
(window as any).electronAPI = { loadData: vi.fn().mockResolvedValue(null) };
const result = await UndoManager.snapshotCurrent('test');
expect(result).toBe(false);
});
it('prend un snapshot si loadData retourne des données', async () => {
(window as any).electronAPI = { loadData: vi.fn().mockResolvedValue('{"ok":true}') };
const result = await UndoManager.snapshotCurrent('Suppression DD');
expect(result).toBe(true);
expect(UndoManager.canUndo()).toBe(true);
expect(UndoManager.label()).toBe('Suppression DD');
});
});
});