PR #496 began with overlapping age and life-expectancy fields in the scenario editor. The requested test applied a rule across multiple form rows, rather than checking only those two fields or fixing their coordinates.
The instruction
Add a new desktop-viewport e2e test in
frontend/e2e/ui-contract/that opens the scenario editor (slide-over) and asserts:
- No sibling-field overlap — for every pair of horizontally-adjacent field wrappers in the form (not just age/life-expectancy), their bounding boxes must not intersect. Use the
boundingBox()pattern from the existing mobile overlap test.- No field overflow — every field wrapper’s bounding box must fit within its scroll container (the
.slide-over-body). Reuse or extendexpectHorizontalOverflowWithin()frome2e/support/helpers.ts.The test should run at the default desktop viewport (1280×720 or whatever Playwright defaults to) — not a mobile viewport. It should catch the exact bug reported here: fields that look fine on mobile (because they stack) but overlap on desktop.
Follow the patterns in
mobile-layout.mobile.spec.tsand usecreateScenario()/waitForSlideIn()helpers frome2e/support/helpers.ts.
The first implementation
The test scans selected kinds of form rows, checks that adjacent visible field wrappers do not intersect, and checks that fields fit within the panel. It adds a partner so both age rows are present.
import { test, expect } from '@playwright/test';
import {
createScenario,
expectHorizontalOverflowWithin,
waitForSlideIn,
} from '../support/helpers';
test('fields in the slide-over do not overlap or overflow on desktop', async ({ page, request }) => {
test.setTimeout(60_000);
await createScenario(request, { labelPrefix: 'Desktop Overlap' });
await page.goto('/');
await expect(page.locator('[data-testid="sidebar"]')).toBeVisible({ timeout: 15_000 });
await page.locator('[data-testid="new-scenario-btn"]').click();
const form = page.locator('[data-testid="scenario-form"]');
await expect(form).toBeVisible();
await waitForSlideIn(page);
// Add a partner so the second age/life-expectancy row is rendered
const addPartnerBtn = form.locator('[data-testid="add-partner-btn"]');
if (await addPartnerBtn.isVisible()) {
await addPartnerBtn.click();
}
const slideOverBody = form.locator('.slide-over-body');
await expect(slideOverBody).toBeVisible();
// ── No sibling-field overlap ──
// Check all multi-field row containers in the slide-over, not just
// age/life-expectancy rows. Each selector targets a known row-like container
// that lays out children horizontally.
const rowSelectors = [
'.age-life-expectancy-row',
'.person-labels-row',
'.benefit-card-fields-grid',
'.expense-row-bottom',
];
const allRows = form.locator(rowSelectors.join(', '));
const rowCount = await allRows.count();
for (let r = 0; r < rowCount; r++) {
const row = allRows.nth(r);
await row.scrollIntoViewIfNeeded();
const children = row.locator(':scope > *');
const childCount = await children.count();
for (let i = 0; i < childCount - 1; i++) {
const a = await children.nth(i).boundingBox();
const b = await children.nth(i + 1).boundingBox();
if (!a || !b) continue; // hidden children
// Either stacked vertically (bottom of a <= top of b) or separated
// horizontally (right of a <= left of b). They must not intersect.
const horizontalOverlap = a.x < b.x + b.width && b.x < a.x + a.width;
const verticalOverlap = a.y < b.y + b.height && b.y < a.y + a.height;
expect(
horizontalOverlap && verticalOverlap,
`row ${r}: child ${i} and child ${i + 1} bounding boxes must not intersect`,
).toBe(false);
}
}
// Verify at least both primary and partner age/life rows were checked
const ageLifeRows = form.locator('.age-life-expectancy-row');
expect(await ageLifeRows.count()).toBeGreaterThanOrEqual(2);
// ── No field overflow ──
// Every field wrapper inside the slide-over-body must fit within it.
const bodyBox = await slideOverBody.boundingBox();
expect(bodyBox).toBeTruthy();
// Check all field sections — not just age rows — for overflow
const fieldSelectors = [
'.age-life-expectancy-row',
'.person-labels-row',
'.field-group',
'.benefit-card-fields-grid',
'.expense-row-bottom',
];
const allFields = slideOverBody.locator(fieldSelectors.join(', '));
const fieldCount = await allFields.count();
for (let i = 0; i < fieldCount; i++) {
const wrapper = allFields.nth(i);
await wrapper.scrollIntoViewIfNeeded();
const wBox = await wrapper.boundingBox();
if (!wBox) continue; // hidden wrappers
// Right edge of the field must not exceed the right edge of the container
expect(
wBox.x + wBox.width,
`field wrapper ${i} right edge within slide-over-body`,
).toBeLessThanOrEqual(bodyBox!.x + bodyBox!.width + 1); // 1px tolerance
}
// Slide-over body itself must not have horizontal overflow
await expectHorizontalOverflowWithin(slideOverBody);
});
Extending the same rule
The check was subsequently strengthened. PR #510 added checks for label overlap, because text could extend beyond non-overlapping wrappers. PR #748 also checked that input, select and textarea controls fit inside their cells, and confirmed that the intended rows were actually exercised.
The scope is the scenario editor’s selected row types and rendered states, not every element on every screen. The tests express relationships that should remain true as layout changes.
Source record
Original spec, initial test, extended test and PR descriptions. All code comes from the recorded commits. The historical tests were not rerun for publication.