refactor(e2e): visual spec shares seedBilateralPair + asserts person-bar

Rewires briefwechsel-rows.visual.spec.ts against the shared fixture
(seedBilateralPair + cleanupBilateralPair), adds afterAll cleanup,
and folds the conv-person-bar visibility gate into openBilateral()
so both the structural test and the snapshot block fail loudly on
a hero-state regression — matching the a11y spec's safety net.

Refs #305
Fixes @saraholt follow-ups 1 + 2 + 3 from PR round-2 review

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-23 21:02:36 +02:00
parent 00fa767419
commit b7083d426c

View File

@@ -1,8 +1,13 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
import {
seedBilateralPair,
cleanupBilateralPair,
type BilateralPair
} from './fixtures/bilateral-correspondence';
// Visual + structural coverage for the new briefwechsel row layout. // Visual + structural coverage for the new briefwechsel row layout.
// //
// Seeds a bilateral correspondence pair via the API (beforeAll) so the page // Seeds a bilateral correspondence pair via the shared fixture so the page
// reaches the row state. The structural test asserts that a // reaches the row state. The structural test asserts that a
// ConversationThumbnail tile AND the DistributionBar render — regressions // ConversationThumbnail tile AND the DistributionBar render — regressions
// that silently drop to the hero or break the {#each} wiring fail here. // that silently drop to the hero or break the {#each} wiring fail here.
@@ -12,40 +17,25 @@ import { test, expect } from '@playwright/test';
// regenerate after intentional UI changes). CI can opt in via VISUAL=1. // regenerate after intentional UI changes). CI can opt in via VISUAL=1.
const VISUAL = process.env.VISUAL === '1'; const VISUAL = process.env.VISUAL === '1';
let senderId: string; let pair: BilateralPair;
let receiverId: string;
test.describe('Briefwechsel — thumbnail-row layout', () => { test.describe('Briefwechsel — thumbnail-row layout', () => {
test.beforeAll(async ({ request }) => { test.beforeAll(async ({ request }) => {
const timestamp = Date.now(); pair = await seedBilateralPair(request, 'Visual');
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', { test.afterAll(async ({ request }) => {
data: { firstName: 'Visual', lastName: `Receiver-${timestamp}` } await cleanupBilateralPair(request, pair);
});
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()}`);
}); });
async function openBilateral(page: import('@playwright/test').Page) { async function openBilateral(page: import('@playwright/test').Page) {
await page.goto( await page.goto(
`/briefwechsel?senderId=${encodeURIComponent(senderId)}&receiverId=${encodeURIComponent(receiverId)}` `/briefwechsel?senderId=${encodeURIComponent(pair.senderId)}&receiverId=${encodeURIComponent(pair.receiverId)}`
); );
await page.waitForSelector('[data-hydrated]'); 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 }) => { test('renders a ConversationThumbnail tile and the DistributionBar', async ({ page }) => {