Public Access
Display and game names shrink to 0.875rem, then end with an ellipsis (FitText + fit-text utility, CSS container units, full text in title). Rows and buttons wrap at large fonts, header points chip can shrink. Guard: scripts/check-overflow.js.
66 lines
4.1 KiB
JavaScript
66 lines
4.1 KiB
JavaScript
/*
|
|
* Sideways scroll check for phones. Not part of the gate, run by hand on the local stack (see README, "Overflow check").
|
|
* A Playwright function, run with the Playwright MCP tool browser_run_code_unsafe (filename = this file) or
|
|
* `playwright-cli run-code`. Log in as an admin in that browser first. A logged-in browser redirects the logged-out
|
|
* pages, so check those in a second run with LOGGED_OUT = true in a clean browser (no session).
|
|
* A hit is: scrollWidth != clientWidth, an element sticking out on the left (never raises scrollWidth), or a
|
|
* redirect away from the page.
|
|
* Content clipped inside an overflow-hidden ancestor is not checked (same rule as scrollWidth).
|
|
*/
|
|
async (page) => {
|
|
const BASE = 'http://localhost:5173';
|
|
const WIDTHS = [360, 390, 412, 414];
|
|
const FONT_PERCENT = 100; // set 130 for the large system font run
|
|
const DARK = false; // set true for dark mode
|
|
const SKIN = ''; // e.g. 'pastel' to check a skin
|
|
const LOGGED_OUT = false; // true: only the logged-out pages, run in a browser without a session
|
|
const GAME_ID = 620; // any appid in the library
|
|
const PROFILE_NAME = 'Dev'; // display name of the logged-in user, the profile must be public
|
|
const PAGES = LOGGED_OUT ? ['/', '/login', '/register', '/reset-password', '/impressum', `/u/${encodeURIComponent(PROFILE_NAME)}`] : [
|
|
'/dashboard', '/library', `/library/${GAME_ID}`, '/wishlist', '/play-next', '/stats', '/import', '/points',
|
|
'/settings', '/whats-new', '/welcome', '/audit/games', '/audit/links', '/audit/log', '/admin/users',
|
|
'/admin/hints', '/admin/news', '/admin/broadcasts', `/u/${encodeURIComponent(PROFILE_NAME)}`,
|
|
];
|
|
|
|
const hits = [];
|
|
let checked = 0;
|
|
for (const width of WIDTHS) {
|
|
await page.setViewportSize({width, height: 800});
|
|
for (const path of PAGES) {
|
|
await page.goto(BASE + path); // no networkidle: some pages poll
|
|
await page.waitForTimeout(1500);
|
|
if (FONT_PERCENT !== 100) await page.addStyleTag({content: `html{font-size:${FONT_PERCENT}%}`});
|
|
await page.evaluate(([dark, skin]) => {
|
|
document.documentElement.classList.toggle('dark', dark);
|
|
if (skin) document.documentElement.dataset.skin = skin;
|
|
}, [DARK, SKIN]);
|
|
await page.waitForTimeout(300);
|
|
const result = await page.evaluate(() => {
|
|
const root = document.documentElement;
|
|
const describe = (element) => `${element.tagName.toLowerCase()}.${String(element.className).slice(0, 60)}`;
|
|
const all = [...document.querySelectorAll('body *')].map((element) => [element, element.getBoundingClientRect()]);
|
|
const right = all.filter(([, box]) => box.right > root.clientWidth).sort((a, b) => b[1].right - a[1].right);
|
|
// Clipped by an overflow:hidden/auto ancestor (an effect preview, a scroller): not visible, not a hit.
|
|
const clipped = (element) => {
|
|
for (let parent = element.parentElement; parent && parent !== document.body; parent = parent.parentElement) {
|
|
if (getComputedStyle(parent).overflowX !== 'visible') return true;
|
|
}
|
|
return false;
|
|
};
|
|
const left = all.filter(([element, box]) => box.width > 0 && box.left < 0 && !clipped(element));
|
|
if (root.scrollWidth === root.clientWidth && left.length === 0) return null;
|
|
return {
|
|
scrollWidth: root.scrollWidth, clientWidth: root.clientWidth,
|
|
widest: right.slice(0, 5).map(([element]) => describe(element)),
|
|
offLeft: left.slice(0, 5).map(([element]) => describe(element)),
|
|
};
|
|
});
|
|
checked++;
|
|
const landed = await page.evaluate(() => location.pathname);
|
|
if (decodeURIComponent(landed) !== decodeURIComponent(path)) hits.push({path, width, redirectedTo: landed});
|
|
if (result) hits.push({path, width, ...result});
|
|
}
|
|
}
|
|
return {checked, hits};
|
|
}
|