#301: dump raw box metrics from the reflow guard
Two rounds now reported no element past the viewport edge while the document still claimed 417px of overflow — a combination that rules out every hypothesis I had, including my own filter. So stop inferring. The guard now prints the html/body metrics, every element whose own content is wider than its box (with its overflow-x, so the intentional scrollers are distinguishable), and every box reaching past the edge with no filtering at all. Diagnostics ride in the assertion message, not the compared value, so they show up even when they match.
This commit is contained in:
parent
9fce824a8e
commit
194f144797
@ -120,7 +120,8 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
|
|||||||
const report = await page.evaluate(() => {
|
const report = await page.evaluate(() => {
|
||||||
const doc = document.documentElement;
|
const doc = document.documentElement;
|
||||||
const limit = doc.clientWidth;
|
const limit = doc.clientWidth;
|
||||||
const describe = (el: HTMLElement): string => {
|
|
||||||
|
const describe = (el: Element): string => {
|
||||||
const cls =
|
const cls =
|
||||||
el.className && typeof el.className === 'string'
|
el.className && typeof el.className === 'string'
|
||||||
? `.${el.className.trim().split(/\s+/).join('.')}`
|
? `.${el.className.trim().split(/\s+/).join('.')}`
|
||||||
@ -128,58 +129,41 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
|
|||||||
return `${el.tagName.toLowerCase()}${cls}`;
|
return `${el.tagName.toLowerCase()}${cls}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Every element whose own content is wider than its box. One of these is
|
||||||
* Content inside a scroll container may exceed the viewport — that is the
|
// the source; the ones that scroll it away on purpose are marked.
|
||||||
* remedy, not the defect. But only if the CONTAINER itself fits: a
|
const overflowing: string[] = [];
|
||||||
* scroller that is wider than the viewport still pushes the page, and its
|
for (const el of Array.from(document.querySelectorAll('*'))) {
|
||||||
* children are then symptoms, not causes.
|
if (el.scrollWidth > el.clientWidth + 1 && el.clientWidth > 0) {
|
||||||
*/
|
const overflowX = getComputedStyle(el).overflowX;
|
||||||
const containedByFittingScroller = (el: HTMLElement): boolean => {
|
overflowing.push(
|
||||||
for (let node = el.parentElement; node && node !== doc; node = node.parentElement) {
|
`${describe(el)} client=${el.clientWidth} scroll=${el.scrollWidth} overflow-x=${overflowX}`,
|
||||||
const overflowX = getComputedStyle(node).overflowX;
|
|
||||||
if (overflowX === 'auto' || overflowX === 'scroll' || overflowX === 'hidden') {
|
|
||||||
return node.getBoundingClientRect().right <= limit + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const offenders: string[] = [];
|
|
||||||
let widest: HTMLElement | null = null;
|
|
||||||
for (const el of Array.from(document.querySelectorAll<HTMLElement>('body *'))) {
|
|
||||||
const rect = el.getBoundingClientRect();
|
|
||||||
// 1 px Toleranz gegen Subpixel-Rundung.
|
|
||||||
if (rect.width > 0 && rect.right > limit + 1 && !containedByFittingScroller(el)) {
|
|
||||||
offenders.push(
|
|
||||||
`${describe(el)} (right=${Math.round(rect.right)}, width=${Math.round(rect.width)})`,
|
|
||||||
);
|
);
|
||||||
if (!widest || rect.width > widest.getBoundingClientRect().width) widest = el;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where does the inflation start? The chain from body down to the widest
|
// Raw: every box reaching past the viewport, no filtering at all.
|
||||||
// offender, with each box's width — the first entry wider than the
|
const past: string[] = [];
|
||||||
// viewport is the element that actually needs constraining.
|
for (const el of Array.from(document.querySelectorAll('body *'))) {
|
||||||
const chain: string[] = [];
|
const rect = el.getBoundingClientRect();
|
||||||
for (let node: HTMLElement | null = widest; node && node !== doc; node = node.parentElement) {
|
if (rect.width > 0 && rect.right > limit + 1) {
|
||||||
chain.unshift(`${describe(node)} w=${Math.round(node.getBoundingClientRect().width)}`);
|
past.push(`${describe(el)} right=${Math.round(rect.right)} w=${Math.round(rect.width)}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scrollWidth: doc.scrollWidth,
|
overflowBy: doc.scrollWidth - limit,
|
||||||
clientWidth: limit,
|
viewport: `html client=${limit} scroll=${doc.scrollWidth} | body client=${document.body.clientWidth} scroll=${document.body.scrollWidth} rect=${Math.round(document.body.getBoundingClientRect().width)}`,
|
||||||
offenders: offenders.slice(0, 20),
|
overflowing: overflowing.slice(0, 15),
|
||||||
chain,
|
past: past.slice(0, 15),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
expect(
|
const diagnosis = [
|
||||||
{
|
|
||||||
overflowBy: report.scrollWidth - report.clientWidth,
|
|
||||||
offenders: report.offenders,
|
|
||||||
chain: report.chain,
|
|
||||||
},
|
|
||||||
`${label}: horizontaler Überlauf bei 320 px`,
|
`${label}: horizontaler Überlauf bei 320 px`,
|
||||||
).toEqual({ overflowBy: 0, offenders: [], chain: [] });
|
report.viewport,
|
||||||
|
`eigener Inhaltsüberlauf: ${JSON.stringify(report.overflowing, null, 1)}`,
|
||||||
|
`Boxen über dem Rand: ${JSON.stringify(report.past, null, 1)}`,
|
||||||
|
].join('\n');
|
||||||
|
expect({ overflowBy: report.overflowBy }, diagnosis).toEqual({ overflowBy: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('reflow at 320px', () => {
|
test.describe('reflow at 320px', () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user