import { test, expect } from '@playwright/test'; import { resolve } from 'node:path'; // Wir stubben den Extract-Endpoint server-side nicht (das Feature liegt auf // dev hinter dem echten Gemini-Key), sondern auf context-Ebene: Playwright // fängt alle Requests auf /api/recipes/extract-from-photo ab und liefert // einen deterministischen JSON-Body zurück. Keine Gemini-Kosten in CI. test('Foto-Import Happy-Path mit gestubtem Extract-Endpoint', async ({ page, context }) => { await context.route('**/api/recipes/extract-from-photo', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ recipe: { id: null, title: 'E2E Testrezept', description: 'Aus dem Bild herbeigezaubert.', source_url: null, source_domain: null, image_path: null, servings_default: 2, servings_unit: 'Portionen', prep_time_min: 5, cook_time_min: 10, total_time_min: null, cuisine: null, category: null, ingredients: [ { position: 1, quantity: 1, unit: 'Stk', name: 'E2E-Apfel', note: null, raw_text: '1 Stk E2E-Apfel', section_heading: null } ], steps: [{ position: 1, text: 'Apfel waschen.' }], tags: [] } }) }); }); await page.goto('/new/from-photo'); await expect(page.getByRole('heading', { name: 'Rezept aus Foto' })).toBeVisible(); const fixture = resolve(__dirname, '../../fixtures/photo-recipe/sample-printed.jpg'); await page.locator('input[type="file"]').setInputFiles(fixture); await expect(page.getByText('Aus Foto erstellt')).toBeVisible({ timeout: 5000 }); // Titel-Feld (das erste text-input im Editor) await expect(page.locator('input[type="text"]').first()).toHaveValue( 'E2E Testrezept' ); }); test('Camera-Icon im Header wird disabled, wenn der Client offline geht', async ({ page, context }) => { await page.goto('/'); const icon = page.locator('[aria-label="Rezept aus Foto erstellen"]'); // Nur relevant, wenn der Dev-Server einen Gemini-Key hat — andernfalls ist // das Icon per Graceful-Degradation gar nicht gerendert und der Test wird // hier early-skipped. (Im Prod und Dev mit Key gilt der zweite Pfad.) if ((await icon.count()) === 0) { test.skip(true, 'Dev-Env hat keinen GEMINI_API_KEY gesetzt.'); return; } await expect(icon).toBeVisible(); await context.setOffline(true); await page.waitForFunction(() => !navigator.onLine); await expect(icon).toHaveClass(/disabled/); });