#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
55932b0828
commit
9c87a14f51
@ -120,7 +120,8 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
|
||||
const report = await page.evaluate(() => {
|
||||
const doc = document.documentElement;
|
||||
const limit = doc.clientWidth;
|
||||
const describe = (el: HTMLElement): string => {
|
||||
|
||||
const describe = (el: Element): string => {
|
||||
const cls =
|
||||
el.className && typeof el.className === 'string'
|
||||
? `.${el.className.trim().split(/\s+/).join('.')}`
|
||||
@ -128,58 +129,41 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
|
||||
return `${el.tagName.toLowerCase()}${cls}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Content inside a scroll container may exceed the viewport — that is the
|
||||
* remedy, not the defect. But only if the CONTAINER itself fits: a
|
||||
* scroller that is wider than the viewport still pushes the page, and its
|
||||
* children are then symptoms, not causes.
|
||||
*/
|
||||
const containedByFittingScroller = (el: HTMLElement): boolean => {
|
||||
for (let node = el.parentElement; node && node !== doc; node = node.parentElement) {
|
||||
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)})`,
|
||||
// Every element whose own content is wider than its box. One of these is
|
||||
// the source; the ones that scroll it away on purpose are marked.
|
||||
const overflowing: string[] = [];
|
||||
for (const el of Array.from(document.querySelectorAll('*'))) {
|
||||
if (el.scrollWidth > el.clientWidth + 1 && el.clientWidth > 0) {
|
||||
const overflowX = getComputedStyle(el).overflowX;
|
||||
overflowing.push(
|
||||
`${describe(el)} client=${el.clientWidth} scroll=${el.scrollWidth} overflow-x=${overflowX}`,
|
||||
);
|
||||
if (!widest || rect.width > widest.getBoundingClientRect().width) widest = el;
|
||||
}
|
||||
}
|
||||
|
||||
// Where does the inflation start? The chain from body down to the widest
|
||||
// offender, with each box's width — the first entry wider than the
|
||||
// viewport is the element that actually needs constraining.
|
||||
const chain: string[] = [];
|
||||
for (let node: HTMLElement | null = widest; node && node !== doc; node = node.parentElement) {
|
||||
chain.unshift(`${describe(node)} w=${Math.round(node.getBoundingClientRect().width)}`);
|
||||
// Raw: every box reaching past the viewport, no filtering at all.
|
||||
const past: string[] = [];
|
||||
for (const el of Array.from(document.querySelectorAll('body *'))) {
|
||||
const rect = el.getBoundingClientRect();
|
||||
if (rect.width > 0 && rect.right > limit + 1) {
|
||||
past.push(`${describe(el)} right=${Math.round(rect.right)} w=${Math.round(rect.width)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
scrollWidth: doc.scrollWidth,
|
||||
clientWidth: limit,
|
||||
offenders: offenders.slice(0, 20),
|
||||
chain,
|
||||
overflowBy: doc.scrollWidth - limit,
|
||||
viewport: `html client=${limit} scroll=${doc.scrollWidth} | body client=${document.body.clientWidth} scroll=${document.body.scrollWidth} rect=${Math.round(document.body.getBoundingClientRect().width)}`,
|
||||
overflowing: overflowing.slice(0, 15),
|
||||
past: past.slice(0, 15),
|
||||
};
|
||||
});
|
||||
expect(
|
||||
{
|
||||
overflowBy: report.scrollWidth - report.clientWidth,
|
||||
offenders: report.offenders,
|
||||
chain: report.chain,
|
||||
},
|
||||
const diagnosis = [
|
||||
`${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', () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user