From 8cb179a8a1e421c478509115807bef228fb769a5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 23 Apr 2026 20:46:25 +0200 Subject: [PATCH] test(briefwechsel): visual spec seeds bilateral pair and asserts row structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the seeding pattern from the a11y spec: beforeAll creates two persons + one document so the page renders the row layout. The structural test now asserts the ConversationThumbnail tile AND the DistributionBar are present — a regression that drops to the hero or breaks the row wiring fails here instead of silently passing a hero-state check. Snapshot block stays gated on VISUAL=1 (baselines captured during review against a seeded backend) so the structural coverage ships immediately and the pixel-diff coverage ships once baselines land. Refs #305 Fixes @saraholt blocker 2 from PR review Co-Authored-By: Claude Sonnet 4.6 --- frontend/e2e/briefwechsel-rows.visual.spec.ts | 72 +++++++++++++++---- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/frontend/e2e/briefwechsel-rows.visual.spec.ts b/frontend/e2e/briefwechsel-rows.visual.spec.ts index e845204c..8b7130f0 100644 --- a/frontend/e2e/briefwechsel-rows.visual.spec.ts +++ b/frontend/e2e/briefwechsel-rows.visual.spec.ts @@ -2,23 +2,69 @@ import { test, expect } from '@playwright/test'; // Visual + structural coverage for the new briefwechsel row layout. // -// Snapshot assertions are gated on the VISUAL env flag because they require -// pre-captured baselines (see `playwright test --update-snapshots` to regenerate -// after intentional UI changes). CI only runs the structural assertions until -// the baseline set is populated and committed. +// Seeds a bilateral correspondence pair via the API (beforeAll) so the page +// reaches the row state. The structural test asserts that a +// ConversationThumbnail tile AND the DistributionBar render — regressions +// that silently drop to the hero or break the {#each} wiring fail here. +// +// Snapshot assertions are gated on the VISUAL env flag because they need +// pre-captured baselines (see `playwright test --update-snapshots` to +// regenerate after intentional UI changes). CI can opt in via VISUAL=1. const VISUAL = process.env.VISUAL === '1'; +let senderId: string; +let receiverId: string; + test.describe('Briefwechsel — thumbnail-row layout', () => { - test('structural row elements render for a bilateral pair', async ({ page }) => { - await page.goto('/briefwechsel'); - // Hero is visible until a person is selected. - await expect(page.getByTestId('conv-hero')).toBeVisible(); + test.beforeAll(async ({ request }) => { + const timestamp = Date.now(); + const senderRes = await request.post('/api/persons', { + data: { firstName: 'Visual', lastName: `Sender-${timestamp}` } + }); + if (!senderRes.ok()) throw new Error(`Create sender failed: ${senderRes.status()}`); + senderId = (await senderRes.json()).id; + + const receiverRes = await request.post('/api/persons', { + data: { firstName: 'Visual', lastName: `Receiver-${timestamp}` } + }); + if (!receiverRes.ok()) throw new Error(`Create receiver failed: ${receiverRes.status()}`); + receiverId = (await receiverRes.json()).id; + + const docRes = await request.post('/api/documents', { + multipart: { + title: 'Visual Test Brief', + documentDate: '1950-06-15', + senderId, + receiverIds: receiverId + } + }); + if (!docRes.ok()) throw new Error(`Create document failed: ${docRes.status()}`); }); - // Visual regression — one snapshot per (viewport × theme). Snapshot tolerance - // stays generous (maxDiffPixels: 100) so that antialiasing jitter on text - // doesn't flip them on unrelated runs; genuine layout changes are still - // caught because of the thumbnail tile and distribution bar. + async function openBilateral(page: import('@playwright/test').Page) { + await page.goto( + `/briefwechsel?senderId=${encodeURIComponent(senderId)}&receiverId=${encodeURIComponent(receiverId)}` + ); + await page.waitForSelector('[data-hydrated]'); + } + + test('renders a ConversationThumbnail tile and the DistributionBar', async ({ page }) => { + await openBilateral(page); + + // Tile appears for the seeded document + await expect(page.locator('[data-testid="conv-thumb-tile"]').first()).toBeVisible(); + + // DistributionBar is present (role=img with a descriptive aria-label) + const bar = page.locator('[role="img"]'); + await expect(bar).toBeVisible(); + const label = (await bar.getAttribute('aria-label')) ?? ''; + expect(label.length).toBeGreaterThan(0); + }); + + // Visual regression — one snapshot per (viewport × theme). Tolerance stays + // generous (maxDiffPixels: 100) so antialiasing jitter doesn't flip them on + // unrelated runs; genuine layout changes are still caught because the + // thumbnail tile and distribution bar dominate the frame. test.describe('snapshots', () => { test.skip(!VISUAL, 'VISUAL=1 required to compare baselines'); @@ -31,7 +77,7 @@ test.describe('Briefwechsel — thumbnail-row layout', () => { test(`${viewport.name} / ${theme}`, async ({ page }) => { await page.setViewportSize({ width: viewport.width, height: viewport.height }); await page.emulateMedia({ colorScheme: theme }); - await page.goto('/briefwechsel'); + await openBilateral(page); await expect(page).toHaveScreenshot(`briefwechsel-${viewport.name}-${theme}.png`, { maxDiffPixels: 100, fullPage: true