Files
kochwas/tests/e2e/remote/shopping.spec.ts

110 lines
4.8 KiB
TypeScript
Raw Normal View History

import { test, expect } from '@playwright/test';
import { setActiveProfile, HENDRIK_ID } from './fixtures/profile';
import { clearShoppingCart } from './fixtures/api-cleanup';
test.describe('Einkaufsliste E2E', () => {
test.afterEach(async ({ request }) => {
await clearShoppingCart(request);
});
test('Cart-Button auf der Wunschliste erzeugt Header-Badge', async ({ page, request }) => {
await setActiveProfile(page, HENDRIK_ID);
// Voraussetzung: Dev-System hat mindestens einen Wunschlisten-Eintrag
const wlRes = await request.get('/api/wishlist?sort=popular');
const wlBody = (await wlRes.json()) as { entries: { recipe_id: number }[] };
test.skip(wlBody.entries.length === 0, 'Wunschliste leer auf Dev — Test uebersprungen');
await page.goto('/wishlist');
await page.getByLabel('In den Einkaufswagen').first().click();
await expect(page.getByLabel(/Einkaufsliste \(\d+\)/)).toBeVisible();
});
test('Shopping-List-Seite zeigt Rezept-Chip + Zutaten', async ({ page, request }) => {
await setActiveProfile(page, HENDRIK_ID);
const wlRes = await request.get('/api/wishlist?sort=popular');
const wlBody = (await wlRes.json()) as { entries: { recipe_id: number }[] };
test.skip(wlBody.entries.length === 0, 'Wunschliste leer');
const recipeId = wlBody.entries[0].recipe_id;
await request.post('/api/shopping-list/recipe', { data: { recipe_id: recipeId } });
await page.goto('/shopping-list');
await expect(page.getByRole('heading', { level: 1, name: 'Einkaufsliste' })).toBeVisible();
// Chip fuers Rezept sichtbar
await expect(page.getByLabel('Portion weniger').first()).toBeVisible();
// Mindestens eine Zutatenzeile
await expect(page.locator('.row').first()).toBeVisible();
});
test('Portions-Stepper veraendert Mengen live', async ({ page, request }) => {
await setActiveProfile(page, HENDRIK_ID);
const wlRes = await request.get('/api/wishlist?sort=popular');
const wlBody = (await wlRes.json()) as { entries: { recipe_id: number }[] };
test.skip(wlBody.entries.length === 0, 'Wunschliste leer');
await request.post('/api/shopping-list/recipe', {
data: { recipe_id: wlBody.entries[0].recipe_id, servings: 4 }
});
await page.goto('/shopping-list');
// Menge der ersten Zeile "vorher" lesen
const qtyBefore = await page.locator('.qty').first().textContent();
// Portion +1
await page.getByLabel('Portion mehr').first().click();
// Nach Fetch+Rerender muss die Menge sich aendern (ungleich dem Vorher-Wert)
await expect
.poll(async () => (await page.locator('.qty').first().textContent())?.trim())
.not.toBe(qtyBefore?.trim());
});
test('Abhaken: Zeile durchgestrichen, Badge-Count sinkt, persistiert nach Reload', async ({ page, request }) => {
await setActiveProfile(page, HENDRIK_ID);
const wlRes = await request.get('/api/wishlist?sort=popular');
const wlBody = (await wlRes.json()) as { entries: { recipe_id: number }[] };
test.skip(wlBody.entries.length === 0, 'Wunschliste leer');
await request.post('/api/shopping-list/recipe', {
data: { recipe_id: wlBody.entries[0].recipe_id }
});
await page.goto('/shopping-list');
const countBadge = page.getByLabel(/Einkaufsliste \(\d+\)/);
const badgeTextBefore = await countBadge.textContent();
const numBefore = Number((badgeTextBefore ?? '').replace(/\D+/g, '')) || 0;
const firstRow = page.locator('label.row').first();
await firstRow.locator('input[type=checkbox]').check();
await expect(firstRow).toHaveClass(/checked/);
// Badge muss sinken (nach Store-Refresh)
await expect
.poll(async () => {
const t = (await countBadge.textContent()) ?? '';
return Number(t.replace(/\D+/g, '')) || 0;
})
.toBeLessThan(numBefore);
// Reload persistiert
await page.reload();
await expect(page.locator('label.row.checked').first()).toBeVisible();
});
test('Liste leeren: Confirm + Empty-State + Badge weg', async ({ page, request }) => {
await setActiveProfile(page, HENDRIK_ID);
const wlRes = await request.get('/api/wishlist?sort=popular');
const wlBody = (await wlRes.json()) as { entries: { recipe_id: number }[] };
test.skip(wlBody.entries.length === 0, 'Wunschliste leer');
await request.post('/api/shopping-list/recipe', {
data: { recipe_id: wlBody.entries[0].recipe_id }
});
await page.goto('/shopping-list');
await page.getByRole('button', { name: 'Liste leeren' }).click();
// Confirm-Dialog (ConfirmAction nutzt einen App-eigenen Dialog, kein native)
await page.getByRole('button', { name: 'Leeren', exact: true }).click();
await expect(page.getByText('Einkaufswagen ist leer.')).toBeVisible();
await expect(page.getByLabel(/Einkaufsliste \(\d+\)/)).toHaveCount(0);
});
});