import { test, expect } from '@playwright/test'; import { seedBilateralPair, cleanupBilateralPair, type BilateralPair } from './fixtures/bilateral-correspondence'; // Visual + structural coverage for the new briefwechsel row layout. // // Seeds a bilateral correspondence pair via the shared fixture 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 pair: BilateralPair; test.describe('Briefwechsel — thumbnail-row layout', () => { test.beforeAll(async ({ request }) => { pair = await seedBilateralPair(request, 'Visual'); }); test.afterAll(async ({ request }) => { await cleanupBilateralPair(request, pair); }); async function openBilateral(page: import('@playwright/test').Page) { await page.goto( `/briefwechsel?senderId=${encodeURIComponent(pair.senderId)}&receiverId=${encodeURIComponent(pair.receiverId)}` ); await page.waitForSelector('[data-hydrated]'); // Parity with the a11y spec: fail loudly if we ever end up on the hero // instead of the row layout. await expect(page.getByTestId('conv-person-bar')).toBeVisible(); } 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'); for (const viewport of [ { name: 'mobile', width: 375, height: 812 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'desktop', width: 1280, height: 800 } ] as const) { for (const theme of ['light', 'dark'] as const) { test(`${viewport.name} / ${theme}`, async ({ page }) => { await page.setViewportSize({ width: viewport.width, height: viewport.height }); await page.emulateMedia({ colorScheme: theme }); await openBilateral(page); await expect(page).toHaveScreenshot(`briefwechsel-${viewport.name}-${theme}.png`, { maxDiffPixels: 100, fullPage: true }); }); } } }); });