import { test, expect } from '@playwright/test'; import { readFile } from 'node:fs/promises'; import { exportRecipes } from '../../src/lib/import'; import { pasta } from '../fixtures'; test('admin creates and edits recipes with recoverable drafts and image upload', async ({ page, }) => { await page.goto('/admin'); await page.getByRole('textbox', { name: 'Benutzername' }).fill('testadmin'); await page.getByLabel('Passwort', { exact: true }).fill(process.env.TEST_PASSWORD!); await page.getByRole('button', { name: 'Anmelden', exact: true }).click(); await expect(page.getByRole('heading', { name: 'Hallo, testadmin.' })).toBeVisible(); await page.getByRole('link', { name: 'Neues Rezept' }).click(); await page .getByRole('textbox', { name: 'Rezepttitel', exact: true }) .fill('Browser-Küchenrezept'); await page .getByRole('textbox', { name: 'Beschreibung', exact: true }) .fill('Ein echtes Rezept aus dem grafischen Editor.'); page.on('dialog', (d) => d.accept()); await page.reload(); await expect(page.getByRole('textbox', { name: 'Rezepttitel', exact: true })).toHaveValue( 'Browser-Küchenrezept', ); await page.getByLabel('Titelbild hochladen').setInputFiles('public/images/garden.jpg'); await expect(page.getByRole('img', { name: 'Titelbild', exact: true })).toBeVisible(); await page.getByRole('checkbox', { name: /Rezept veröffentlichen/ }).check(); await page.getByRole('button', { name: 'Zutaten', exact: true }).click(); await page.getByRole('textbox', { name: 'Zutat 1', exact: true }).fill('Kartoffeln'); await page.getByRole('spinbutton', { name: 'Menge 1', exact: true }).fill('400'); await page.getByRole('button', { name: 'Zutat hinzufügen' }).click(); await page.getByRole('textbox', { name: 'Zutat 2', exact: true }).fill('Salz'); await page.getByRole('combobox', { name: 'Einheit 2', exact: true }).selectOption(''); await page.getByRole('button', { name: 'Zubereitung', exact: true }).click(); await page.getByRole('textbox', { name: 'Schritttitel 1' }).fill('Kartoffeln kochen'); await page.getByRole('textbox', { name: 'Anleitung 1' }).fill('In Salzwasser weich kochen.'); await page.getByRole('button', { name: 'Kartoffeln', exact: true }).click(); await page.getByRole('button', { name: 'Timer hinzufügen' }).click(); await page.getByRole('button', { name: 'Live-Vorschau', exact: true }).click(); await expect(page.locator('.editor-preview')).toContainText('400 g Kartoffeln'); await page.screenshot({ path: 'docs/screenshots/editor-desktop.png', fullPage: true }); await page.getByRole('button', { name: 'Rezept speichern' }).click(); await expect(page).toHaveURL(/\/admin\/rezepte\/[a-f0-9-]{36}$/); await page.getByRole('button', { name: 'Das Rezept', exact: true }).click(); await page .getByRole('textbox', { name: 'Rezepttitel', exact: true }) .fill('Browser-Küchenrezept verbessert'); await page.getByRole('button', { name: 'Rezept speichern' }).click(); await expect(page.getByRole('status')).toContainText('erfolgreich gespeichert'); await page.goto('/admin'); await expect( page.getByRole('heading', { name: 'Browser-Küchenrezept verbessert' }), ).toBeVisible(); const downloadPromise = page.waitForEvent('download'); await page .getByRole('link', { name: 'Browser-Küchenrezept verbessert als JSON exportieren' }) .click(); const download = await downloadPromise; const data = JSON.parse(await readFile((await download.path())!, 'utf8')); expect(data.recipes[0].title).toBe('Browser-Küchenrezept verbessert'); await page.getByRole('button', { name: 'Browser-Küchenrezept verbessert löschen' }).click(); await page.getByRole('button', { name: 'Endgültig löschen' }).click(); await expect(page.getByRole('heading', { name: 'Browser-Küchenrezept verbessert' })).toHaveCount( 0, ); await page.getByRole('button', { name: 'Abmelden' }).click(); await expect(page.getByRole('heading', { name: 'Willkommen zurück.' })).toBeVisible(); }); test('JSON and XML import with a real preview, duplicate prevention and export', async ({ page, }) => { await page.goto('/admin'); await page.getByLabel('Benutzername').fill('testadmin'); await page.getByLabel('Passwort', { exact: true }).fill(process.env.TEST_PASSWORD!); await page.getByRole('button', { name: 'Anmelden', exact: true }).click(); for (const format of ['json', 'xml'] as const) { await page.getByRole('button', { name: 'Importieren', exact: true }).click(); await page.getByLabel('Importformat').selectOption(format); const recipe = { ...pasta, id: undefined, title: `Importierte ${format.toUpperCase()} Pasta`, published: false, }; await page.getByLabel('Rezeptdaten').fill(exportRecipes([recipe], format)); await page.getByRole('button', { name: 'Importvorschau prüfen' }).click(); await expect(page.locator('.import-preview')).toContainText(recipe.title); await expect(page.locator('.import-preview')).toContainText('200 g Pasta'); await page.getByRole('button', { name: '1 Rezept(e) jetzt importieren' }).click(); await expect(page.getByRole('heading', { name: recipe.title })).toBeVisible(); } await page.getByRole('button', { name: 'Importieren', exact: true }).click(); await page.getByLabel('Rezeptdaten').fill(exportRecipes([pasta], 'json')); await page.getByRole('button', { name: 'Importvorschau prüfen' }).click(); await expect(page.getByText('Duplikat – Import gesperrt')).toBeVisible(); await expect(page.getByRole('button', { name: '1 Rezept(e) jetzt importieren' })).toBeDisabled(); });