/** * The toast store + primitive surfaces ApiError messages on * user-initiated actions that previously failed silently (catch { ignore }). * This spec intercepts the like POST and forces a 429 to assert the German * error message reaches the user via the global toast region. */ import { test, expect } from '../../fixtures/test'; import { uploadRaw } from '../../helpers/upload-client'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; const SAMPLE_JPG = join(process.cwd(), 'fixtures', 'media', 'sample.jpg'); async function seedUpload(token: string): Promise<{ id: string }> { const res = await uploadRaw(token, readFileSync(SAMPLE_JPG), { filename: 'tf.jpg', contentType: 'image/jpeg', caption: 'Toast fixture', }); if (res.status !== 201) throw new Error(`Upload seed failed (${res.status})`); return (await res.json()) as { id: string }; } test.describe('Feed — error toast on user action failures', () => { test('like POST 429 surfaces a German error toast', async ({ page, guest, signIn }) => { const author = await guest('ToastAuthor'); const liker = await guest('ToastLiker'); await seedUpload(author.jwt); await signIn(page, liker); // Intercept the like endpoint with a forced rate-limit response. await page.route('**/api/v1/upload/*/like', (route) => route.fulfill({ status: 429, contentType: 'application/json', body: JSON.stringify({ error: 'rate_limited', message: 'Zu viele Anfragen — bitte kurz warten.' }), }) ); await page.goto('/feed'); const card = page.locator('article').filter({ hasText: author.displayName }).first(); await expect(card).toBeVisible({ timeout: 10_000 }); // Click the like button in the actions row — first visible match inside the card. await card.locator('button').filter({ hasText: /\d+/ }).first().click(); // The toast is rendered inside the global Toaster region with aria-live="polite". const toast = page.getByTestId('toast').first(); await expect(toast).toBeVisible({ timeout: 3_000 }); await expect(toast).toContainText(/Zu viele Anfragen/i); await expect(toast).toHaveAttribute('data-toast-tone', 'error'); }); });