Files
Mind-o-Mat/tests/sync-queue.test.ts
T
Jannik 99d9fa6b7c
CI / test (push) Canceled after 0s
CI / webapp (push) Canceled after 0s
v2.0.5: Background-Sync in PWA + Sync-Queue + 8 Tests (Phase 4.3 erweitert)
Phase 4.3 (Background-Sync):
- webapp/src/sync-queue.ts: localStorage-basierte Queue
  - enqueueSync, dequeueSync, loadQueue, saveQueue, clearQueue
  - TypeScript mit korrekten Typen
- webapp/src/EditorWithLoad.tsx: erweitert
  - Auto-Sync-Toggle (localStorage-Persistenz)
  - Online-Event-Listener: processQueue bei reconnect
  - Bei Auto-Sync: direkt speichern + sync
  - Bei Offline: in Queue, wird bei 'online' verarbeitet
  - Pending-Counter im UI
- webapp/public/service-worker.js: Background-Sync
  - 'sync'-Event-Handler mit tag 'mindomat-sync'
  - Ruft POST /api/sync, postet Ergebnis an Clients
  - Browser-Support: Chrome/Edge (Background-Sync API)

Tests:
- tests/sync-queue.test.ts: 8 neue Tests
  - Startzustand, enqueue, FIFO, dequeue, clearQueue
  - Roundtrip, kaputtes JSON
  - localStorage-Mock

Verifiziert:
- 79/79 Tests gruen (10 neu seit v2.0.4)
- Webapp tsc + build OK
- EditorWithLoad funktional mit Auto-Sync-Option

Pfad-Korrekturen:
- vite.config.ts: ../../src/ -> ../src/ (war falsch, webapp ist nur 1 Ebene tiefer)
2026-09-05 12:38:34 +02:00

89 lines
2.5 KiB
TypeScript

// Tests fuer Sync-Queue
// Phase 4.3
import { describe, it, expect, beforeEach } from 'vitest';
// Mock localStorage vor dem Import (das Modul nutzt es zur Initialisierung)
const store = new Map<string, string>();
(globalThis as any).localStorage = {
getItem: (key: string) => store.get(key) ?? null,
setItem: (key: string, value: string) => { store.set(key, value); },
removeItem: (key: string) => { store.delete(key); },
clear: () => { store.clear(); },
key: (i: number) => Array.from(store.keys())[i] ?? null,
get length() { return store.size; },
};
import {
enqueueSync,
dequeueSync,
loadQueue,
clearQueue,
saveQueue,
} from '../webapp/src/sync-queue';
describe('Sync-Queue', () => {
beforeEach(() => {
clearQueue();
});
it('Startzustand: leere Queue', () => {
expect(loadQueue()).toEqual([]);
});
it('enqueue fuegt Element hinzu', () => {
enqueueSync('note1.md', '# Inhalt 1');
const queue = loadQueue();
expect(queue.length).toBe(1);
expect(queue[0].noteId).toBe('note1.md');
expect(queue[0].content).toBe('# Inhalt 1');
expect(typeof queue[0].timestamp).toBe('number');
});
it('enqueue fuegt mehrere Elemente hinzu (FIFO)', () => {
enqueueSync('a.md', 'A');
enqueueSync('b.md', 'B');
enqueueSync('c.md', 'C');
const queue = loadQueue();
expect(queue.length).toBe(3);
expect(queue.map((q) => q.noteId)).toEqual(['a.md', 'b.md', 'c.md']);
});
it('dequeue entfernt Element nach noteId', () => {
enqueueSync('a.md', 'A');
enqueueSync('b.md', 'B');
dequeueSync('a.md');
const queue = loadQueue();
expect(queue.length).toBe(1);
expect(queue[0].noteId).toBe('b.md');
});
it('dequeue unbekannter noteId ist no-op', () => {
enqueueSync('a.md', 'A');
dequeueSync('nicht-da.md');
expect(loadQueue().length).toBe(1);
});
it('clearQueue leert komplett', () => {
enqueueSync('a.md', 'A');
enqueueSync('b.md', 'B');
clearQueue();
expect(loadQueue()).toEqual([]);
});
it('saveQueue/loadQueue roundtrip mit localStorage', () => {
const queue = [
{ noteId: 'a.md', content: 'A', timestamp: 1000 },
{ noteId: 'b.md', content: 'B', timestamp: 2000 },
];
saveQueue(queue);
const loaded = loadQueue();
expect(loaded).toEqual(queue);
});
it('kaputtes JSON in localStorage wird als leere Queue behandelt', () => {
store.set('mindomat-pending-syncs', '{invalid json');
expect(loadQueue()).toEqual([]);
});
});