- jsdom + @testing/react als dev-Dependencies - vitest.config.mjs: environmentMatchGlobs fuer *.webapp.* und sync-queue Tests - tests/webapp/notes-api-client.test.ts: 9 Tests - loadNote: 404 -> null, 200 -> parsed, 500 -> throws - URL-Encoding: Leerzeichen in noteId - saveNote: PUT mit Content-Type, Body korrekt - triggerSync: 200 -> ok, 500 -> throws - listNotes: parst Notes-Liste, Fehlerfall Verifiziert: - 88/88 Tests gruen (9 neue fuer notes-api Client) - Tool + Webapp Builds OK
123 lines
4.1 KiB
TypeScript
123 lines
4.1 KiB
TypeScript
// Tests fuer notes-api (Client-Funktionen mit fetch-Mock)
|
|
// Phase 4.1
|
|
|
|
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
import { loadNote, saveNote, triggerSync, listNotes, type NoteData } from '../../webapp/src/notes-api';
|
|
|
|
describe('notes-api Client', () => {
|
|
let originalFetch: typeof globalThis.fetch;
|
|
|
|
beforeEach(() => {
|
|
originalFetch = globalThis.fetch;
|
|
});
|
|
|
|
afterEach(() => {
|
|
globalThis.fetch = originalFetch;
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe('loadNote', () => {
|
|
it('gibt null zurueck bei 404', async () => {
|
|
globalThis.fetch = vi.fn(async () =>
|
|
new Response('Not Found', { status: 404, statusText: 'Not Found' }),
|
|
) as typeof fetch;
|
|
const result = await loadNote('notiz.md');
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('parst Notiz-Antwort korrekt', async () => {
|
|
const noteData: NoteData = {
|
|
id: 'test.md',
|
|
content: '# Test',
|
|
frontmatter: { title: 'Test' },
|
|
lastModified: '2026-09-05T10:00:00Z',
|
|
};
|
|
globalThis.fetch = vi.fn(async () =>
|
|
new Response(JSON.stringify(noteData), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
) as typeof fetch;
|
|
const result = await loadNote('test.md');
|
|
expect(result).toEqual(noteData);
|
|
});
|
|
|
|
it('wirft bei HTTP-Fehler (500)', async () => {
|
|
globalThis.fetch = vi.fn(async () =>
|
|
new Response('Server Error', { status: 500, statusText: 'Internal Server Error' }),
|
|
) as typeof fetch;
|
|
await expect(loadNote('test.md')).rejects.toThrow('HTTP 500');
|
|
});
|
|
|
|
it('encoded den noteId in der URL', async () => {
|
|
const fetchMock = vi.fn(async () => new Response('null', { status: 404 })) as typeof fetch;
|
|
globalThis.fetch = fetchMock;
|
|
await loadNote('path with spaces.md');
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'/api/notes/path%20with%20spaces.md',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('saveNote', () => {
|
|
it('sendet PUT mit Content im Body', async () => {
|
|
const fetchMock = vi.fn(async () =>
|
|
new Response(JSON.stringify({ id: 'x.md', content: 'Y', frontmatter: {}, lastModified: 'now' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
) as typeof fetch;
|
|
globalThis.fetch = fetchMock;
|
|
await saveNote('x.md', 'Y');
|
|
const [url, init] = fetchMock.mock.calls[0];
|
|
expect(url).toBe('/api/notes/x.md');
|
|
expect(init?.method).toBe('PUT');
|
|
expect((init?.headers as Record<string, string>)['Content-Type']).toBe('application/json');
|
|
const body = JSON.parse(init?.body as string);
|
|
expect(body.content).toBe('Y');
|
|
});
|
|
});
|
|
|
|
describe('triggerSync', () => {
|
|
it('sendet POST an /api/sync und gibt ok zurueck', async () => {
|
|
const fetchMock = vi.fn(async () =>
|
|
new Response(JSON.stringify({ ok: true, message: 'Sync OK' }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
) as typeof fetch;
|
|
globalThis.fetch = fetchMock;
|
|
const result = await triggerSync();
|
|
expect(result.ok).toBe(true);
|
|
expect(result.message).toBe('Sync OK');
|
|
});
|
|
|
|
it('gibt Fehler zurueck bei 500', async () => {
|
|
globalThis.fetch = vi.fn(async () =>
|
|
new Response('Fail', { status: 500 }),
|
|
) as typeof fetch;
|
|
await expect(triggerSync()).rejects.toThrow('HTTP 500');
|
|
});
|
|
});
|
|
|
|
describe('listNotes', () => {
|
|
it('parst Notiz-Liste', async () => {
|
|
globalThis.fetch = vi.fn(async () =>
|
|
new Response(JSON.stringify({ notes: ['a.md', 'b.md'] }), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
) as typeof fetch;
|
|
const notes = await listNotes();
|
|
expect(notes).toEqual(['a.md', 'b.md']);
|
|
});
|
|
|
|
it('gibt leeres Array bei Fehler', async () => {
|
|
globalThis.fetch = vi.fn(async () =>
|
|
new Response('Boom', { status: 500 }),
|
|
) as typeof fetch;
|
|
await expect(listNotes()).rejects.toThrow('HTTP 500');
|
|
});
|
|
});
|
|
});
|