All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m42s
Automatisierte End-to-End-Tests gegen ein deployed Environment. Loest die manuellen MCP-Playwright-Runs ab. 42 Tests in 9 Files: - homepage: H1, Sektionen, Sort-Tabs, Console-Errors - search: lokaler Treffer, Web-Fallback, Empty-State, Deep-Link - profile: Switcher, Auswahl-Persistenz, Favoriten-Section, Guard-Dialog - recipe-detail: Header, Portionen-Scaling (4->6), Favorit-Toggle, Rating-Persistenz ueber Reload, Gekocht-Counter, Wunschliste-Toggle - comments: eigenen erstellen+loeschen via UI, fremder hat kein Delete - wishlist: Seite, Sort-Tabs, Badge-Sync, requireProfile-Custom-Message - preview: Guard ohne ?url=, echte URL parst, unparsbare zeigt error-box - admin: alle 4 Subrouten + /admin redirect - api-errors: parsePositiveIntParam (4x Invalid id), validateBody (4x Invalid body + issues), 404, Sanity /health /profiles /domains Architektur: - Separate playwright.remote.config.ts (getrennt von local preview) - workers: 1 + afterEach API-Cleanup (rating, favorite, wishlist, comments) - Hardcoded Recipe-ID 66 + Profile 1/2/3 — stabile Dev-DB-Seeds - E2E_REMOTE_URL ueberschreibt die Ziel-URL Ausfuehrung: npm run test:e2e:remote Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
102 lines
4.3 KiB
TypeScript
102 lines
4.3 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
// Negative-Path Tests fuer die api-helpers: parsePositiveIntParam und
|
|
// validateBody. Jeder neue API-Handler sollte dieselben Error-Shapes
|
|
// liefern — wenn dieser Suite-Block kippt, ist der Helper-Contract kaputt.
|
|
|
|
test.describe('API Error-Shapes', () => {
|
|
test.describe('parsePositiveIntParam', () => {
|
|
test('GET /api/recipes/abc -> 400 Invalid id', async ({ request }) => {
|
|
const r = await request.get('/api/recipes/abc');
|
|
expect(r.status()).toBe(400);
|
|
expect(await r.json()).toEqual({ message: 'Invalid id' });
|
|
});
|
|
|
|
test('GET /api/recipes/-1 -> 400 Invalid id', async ({ request }) => {
|
|
const r = await request.get('/api/recipes/-1');
|
|
expect(r.status()).toBe(400);
|
|
expect(await r.json()).toEqual({ message: 'Invalid id' });
|
|
});
|
|
|
|
test('GET /api/recipes/0 -> 400 Invalid id', async ({ request }) => {
|
|
const r = await request.get('/api/recipes/0');
|
|
expect(r.status()).toBe(400);
|
|
expect(await r.json()).toEqual({ message: 'Invalid id' });
|
|
});
|
|
|
|
test('POST /api/recipes/abc/comments -> 400 Invalid id', async ({ request }) => {
|
|
const r = await request.post('/api/recipes/abc/comments', { data: {} });
|
|
expect(r.status()).toBe(400);
|
|
expect(await r.json()).toEqual({ message: 'Invalid id' });
|
|
});
|
|
});
|
|
|
|
test.describe('validateBody', () => {
|
|
test('POST /api/wishlist leer -> 400 {message, issues}', async ({ request }) => {
|
|
const r = await request.post('/api/wishlist', { data: {} });
|
|
expect(r.status()).toBe(400);
|
|
const body = (await r.json()) as { message: string; issues?: unknown[] };
|
|
expect(body.message).toBe('Invalid body');
|
|
expect(Array.isArray(body.issues)).toBe(true);
|
|
expect((body.issues ?? []).length).toBeGreaterThanOrEqual(2); // recipe_id + profile_id
|
|
});
|
|
|
|
test('POST /api/recipes/66/comments leer -> 400 {message, issues}', async ({ request }) => {
|
|
const r = await request.post('/api/recipes/66/comments', { data: {} });
|
|
expect(r.status()).toBe(400);
|
|
const body = (await r.json()) as { message: string; issues?: unknown[] };
|
|
expect(body.message).toBe('Invalid body');
|
|
expect((body.issues ?? []).length).toBeGreaterThanOrEqual(1); // profile_id oder text
|
|
});
|
|
|
|
test('PUT /api/recipes/66/favorite leer -> 400 {message, issues}', async ({ request }) => {
|
|
const r = await request.put('/api/recipes/66/favorite', { data: {} });
|
|
expect(r.status()).toBe(400);
|
|
const body = (await r.json()) as { message: string; issues?: unknown[] };
|
|
expect(body.message).toBe('Invalid body');
|
|
expect((body.issues ?? []).length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
test('POST /api/domains leer -> 400 {message, issues}', async ({ request }) => {
|
|
const r = await request.post('/api/domains', { data: {} });
|
|
expect(r.status()).toBe(400);
|
|
const body = (await r.json()) as { message: string; issues?: unknown[] };
|
|
expect(body.message).toBe('Invalid body');
|
|
expect((body.issues ?? []).length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
});
|
|
|
|
test.describe('404 auf missing Ressourcen', () => {
|
|
test('GET /api/recipes/99999 -> 404 Recipe not found', async ({ request }) => {
|
|
const r = await request.get('/api/recipes/99999');
|
|
expect(r.status()).toBe(404);
|
|
expect(await r.json()).toEqual({ message: 'Recipe not found' });
|
|
});
|
|
});
|
|
|
|
test.describe('Positive Sanity-Checks', () => {
|
|
test('GET /api/health -> 200 mit db:"ok"', async ({ request }) => {
|
|
const r = await request.get('/api/health');
|
|
expect(r.status()).toBe(200);
|
|
const body = (await r.json()) as { db: string };
|
|
expect(body.db).toBe('ok');
|
|
});
|
|
|
|
test('GET /api/profiles -> drei Profile', async ({ request }) => {
|
|
const r = await request.get('/api/profiles');
|
|
expect(r.status()).toBe(200);
|
|
const body = (await r.json()) as { id: number; name: string }[];
|
|
expect(body.length).toBeGreaterThanOrEqual(3);
|
|
const names = body.map((p) => p.name).sort();
|
|
expect(names).toEqual(expect.arrayContaining(['Hendrik', 'Leana', 'Verena']));
|
|
});
|
|
|
|
test('GET /api/domains -> liefert Array', async ({ request }) => {
|
|
const r = await request.get('/api/domains');
|
|
expect(r.status()).toBe(200);
|
|
const body = await r.json();
|
|
expect(Array.isArray(body)).toBe(true);
|
|
});
|
|
});
|
|
});
|