test: find cut-off placeholders, and let the calendar's sideways strip of weeks scroll

This commit is contained in:
Linus Rath
2026-11-04 05:58:50 +01:00
parent ea274b02e9
commit eda317721c
2 changed files with 23 additions and 4 deletions
+4 -3
View File
@@ -99,14 +99,15 @@ for (const scenario of SCENARIOS) {
await check(page, scenario, 'mail-list', found);
await item.click();
await page.getByRole('button', { name: text.showDetails }).first().click();
const routing = page.locator('section', { has: page.getByText(text.routing, { exact: true }) }).first();
// Sections nest: the last match is the innermost, the routing section itself.
const routing = page.locator('section', { has: page.getByText(text.routing, { exact: true }) }).last();
await expect(routing).toBeVisible();
await check(page, scenario, 'mail-details', found);
// The sender's address gets a column of its own width, not a sliver, and a date
// takes one line. Rows: From, To, Sent, Received.
const fit = await routing.evaluate((section) => {
const values = Array.from(section.querySelectorAll('dl > dd'));
const fit = await routing.locator('dl').first().evaluate((list) => {
const values = Array.from(list.querySelectorAll(':scope > dd'));
const from = values[0].getBoundingClientRect();
const sent = values[values.length - 2];
const lineHeight = parseFloat(getComputedStyle(sent).lineHeight) || 20;
+19 -1
View File
@@ -7,7 +7,8 @@
* Not counted: text that's meant to be cut (text-overflow: ellipsis, line clamp),
* text inside a box that scrolls only sideways, text that's wholly outside its box (a
* closed drawer, a slide), text in a box 1-2px wide (visually hidden), screen-reader-only text, and anything marked
* data-layout-fit-ignore. Frames (a message body) aren't looked into.
* data-layout-fit-ignore. Frames (a message body) aren't looked into. A field's
* placeholder counts too, unless it ends in an ellipsis.
*/
import type { Page } from '@playwright/test';
@@ -58,6 +59,10 @@ export async function findCutOffText(page: Page): Promise<CutOff[]> {
if (Number(s.opacity) === 0 || s.visibility === 'hidden') { meant = true; break; }
if (!clipsX(s)) continue;
if (scrollsX(s)) { meant = true; break; }
// A strip of pages that scrolls both ways (the calendar's weeks side by side):
// the next page peeking in is meant. Text spilling over by accident is a few
// pixels to a column, never half the box again.
if (s.overflowX !== 'hidden' && s.overflowX !== 'clip' && at.scrollWidth > at.clientWidth * 1.5) { meant = true; break; }
// A 1px box is the visually-hidden pattern: read out, not shown.
if (at.clientWidth <= 2) { meant = true; break; }
const r = at.getBoundingClientRect();
@@ -75,6 +80,19 @@ export async function findCutOffText(page: Page): Promise<CutOff[]> {
out.push({ where: describe(el), text: text.slice(0, 60), px: Math.round(px) });
}
}
// A placeholder cut off mid-word: "Termine durchs". With text-overflow: ellipsis
// it ends in "…" and says there's more.
const canvas = document.createElement('canvas').getContext('2d');
for (const input of Array.from(document.querySelectorAll<HTMLInputElement>('input[placeholder]'))) {
if (input.value || !input.placeholder || input.closest('[aria-hidden="true"], [data-layout-fit-ignore]')) continue;
const s = getComputedStyle(input);
if (s.display === 'none' || s.visibility === 'hidden' || input.offsetParent === null) continue;
if (s.textOverflow === 'ellipsis' || !canvas) continue;
canvas.font = `${s.fontStyle} ${s.fontWeight} ${s.fontSize} ${s.fontFamily}`;
const room = input.clientWidth - parseFloat(s.paddingLeft) - parseFloat(s.paddingRight);
const px = canvas.measureText(input.placeholder).width - room;
if (px > 1.5) out.push({ where: describe(input), text: input.placeholder.slice(0, 60), px: Math.round(px) });
}
return out;
});
}