test(ops): verify Docker persistence, browser workflows and document deployment

This commit is contained in:
OpenCode committed 2026-10-24 15:43:37 +02:00
1 parent a58516e424
commit 8fa63415f1
35 files changed
+1105

No files matched your search

+129
View File
@@ -0,0 +1,129 @@
import { test, expect } from '@playwright/test';
import { mkdir } from 'node:fs/promises';
const id = '10000000-0000-4000-8000-000000000001';
test('editor remains usable on the current viewport in dark mode', async ({ page }, info) => {
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();
await page.getByRole('link', { name: 'Neues Rezept' }).click();
await page.getByRole('button', { name: 'Theme wechseln', exact: true }).click();
await page.getByRole('button', { name: 'Zutaten', exact: true }).click();
await page.getByRole('textbox', { name: 'Zutat 1', exact: true }).fill('Kartoffeln');
await page.getByRole('button', { name: 'Zutat hinzufügen' }).click();
await page.getByRole('textbox', { name: 'Zutat 2', exact: true }).fill('Karotten');
await page.getByRole('button', { name: 'Zutat 2 nach oben' }).click();
await expect(page.getByRole('textbox', { name: 'Zutat 1', exact: true })).toHaveValue('Karotten');
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
await page.screenshot({
path: `docs/screenshots/editor-dark-${info.project.name}.png`,
fullPage: true,
animations: 'disabled',
});
});
test('public library, shareable search, favorites, themes and responsive layouts', async ({
page,
}, info) => {
const errors: string[] = [];
page.on('pageerror', (e) => errors.push(e.message));
await page.goto('/');
await expect(page.getByRole('heading', { name: 'Zuhause schmeckt’s am besten.' })).toBeVisible();
await expect(page.locator('.recipe-card')).toHaveCount(3);
await expect(page.getByText('Geheimer Entwurf')).toHaveCount(0);
await page.evaluate(() => document.fonts.ready);
for (const card of await page.locator('.recipe-card').all())
await expect(card).toHaveCSS('opacity', '1');
await mkdir('docs/screenshots', { recursive: true });
await page.screenshot({
path: `docs/screenshots/library-${info.project.name}.png`,
fullPage: true,
animations: 'disabled',
});
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
await page.getByRole('textbox', { name: 'Rezepte suchen' }).fill('Basilikum');
await expect(page.locator('.recipe-card')).toHaveCount(1);
await expect(page).toHaveURL(/q=Basilikum/);
await page.getByRole('button', { name: 'Suche löschen' }).click();
await page.getByRole('button', { name: 'Grüne Pesto-Pasta als Favorit merken' }).click();
const nav =
page.viewportSize()!.width <= 700
? page.getByRole('navigation', { name: 'Mobile Navigation' })
: page.getByRole('navigation', { name: 'Hauptnavigation' });
await nav.getByRole('link', { name: /Favoriten|Lieblingsrezepte/ }).click();
await expect(page.locator('.recipe-card')).toHaveCount(1);
await page.getByRole('button', { name: 'Theme wechseln', exact: true }).click();
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
await expect(page.locator('.recipe-card')).toHaveCSS('opacity', '1');
await page.screenshot({
path: `docs/screenshots/favorites-dark-${info.project.name}.png`,
fullPage: true,
animations: 'disabled',
});
await page.reload();
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
expect(errors).toEqual([]);
});
test('portion calculator, ingredient anchor, shopping list and print content', async ({
page,
}, info) => {
await page.goto(`/rezepte/${id}`);
await page.getByRole('spinbutton', { name: 'Portionen', exact: true }).fill('8');
await page.getByRole('spinbutton', { name: 'Portionen', exact: true }).press('Tab');
await expect(page.getByRole('button', { name: 'Menge Pasta ändern' })).toHaveText('800 g');
await expect(page.locator('.steps-list')).toContainText('800 g Pasta');
await page.getByRole('button', { name: 'Nach Zutat', exact: true }).click();
await page.getByRole('button', { name: 'Menge Pasta ändern' }).click();
await page.getByRole('textbox', { name: 'Neue Menge Pasta' }).fill('0,4');
await page.getByRole('combobox', { name: 'Einheit Pasta' }).selectOption('kg');
await page.getByRole('button', { name: 'Menge übernehmen' }).click();
await expect(page.getByRole('button', { name: 'Menge Pasta ändern' })).toHaveText('400 g');
await expect(page.locator('.panel-title')).toContainText('FÜR 4 PORTIONEN');
await expect(page.getByRole('button', { name: 'Menge Olivenöl ändern' })).toHaveText('120 ml');
await page.screenshot({
path: `docs/screenshots/recipe-${info.project.name}.png`,
fullPage: true,
});
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
await page.getByRole('button', { name: 'Zur Einkaufsliste hinzufügen' }).click();
await page.goto('/einkaufsliste');
await expect(page.locator('.shopping-list')).toContainText('400 g');
await page.locator('.shopping-item').first().click();
await page.reload();
await expect(page.locator('.shopping-list li.is-checked')).toHaveCount(1);
await page.goto(`/rezepte/${id}`);
await page.emulateMedia({ media: 'print' });
await expect(page.locator('.site-header')).toBeHidden();
await expect(page.locator('.ingredients-panel')).toBeVisible();
});
test('guided cooking restores scaled progress and timestamp timers through navigation', async ({
page,
}, info) => {
await page.goto(`/rezepte/${id}/kochen?faktor=2`);
await expect(page.locator('.cooking-instruction')).toContainText('80 g Basilikum');
await page.locator('.step-ingredients button').first().click();
await page.getByRole('button', { name: 'Nächster Schritt' }).click();
await expect(page.getByRole('heading', { name: 'Das Pesto zum Leuchten bringen' })).toBeVisible();
await page.getByRole('button', { name: 'Pinienkerne rösten starten' }).click();
await expect(page.getByRole('button', { name: 'Pinienkerne rösten pausieren' })).toBeVisible();
await page.screenshot({
path: `docs/screenshots/cooking-${info.project.name}.png`,
fullPage: true,
});
await page.getByRole('button', { name: 'Nächster Schritt' }).click();
await expect(page.getByRole('button', { name: 'Pinienkerne rösten pausieren' })).toBeVisible();
await page.getByRole('button', { name: 'Zurück', exact: true }).click();
await page.reload();
await expect(page.getByRole('heading', { name: 'Das Pesto zum Leuchten bringen' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Pinienkerne rösten pausieren' })).toBeVisible();
await page.getByRole('button', { name: 'Pinienkerne rösten pausieren' }).click();
await page.getByRole('button', { name: 'Pinienkerne rösten zurücksetzen' }).click();
await expect(page.getByRole('timer')).toHaveText('03:00');
await page.getByRole('button', { name: 'Nächster Schritt' }).click();
await page.getByRole('button', { name: 'Nächster Schritt' }).click();
await page.getByRole('button', { name: 'Fertig gekocht' }).click();
await expect(page.getByRole('heading', { name: 'Guten Appetit!' })).toBeVisible();
await page.reload();
await expect(page.getByRole('heading', { name: 'Guten Appetit!' })).toBeVisible();
await page.getByRole('button', { name: 'Erneut kochen' }).click();
await expect(page.getByRole('heading', { name: 'Ein bisschen Vorbereitung' })).toBeVisible();
});