134 lines
3.6 KiB
JavaScript
134 lines
3.6 KiB
JavaScript
import path from 'node:path';
|
|
import {
|
|
CONTEXT_TIMEOUT_MS,
|
|
PAGE_TIMEOUT_MS,
|
|
cleanupBrowserTmp,
|
|
openBrowser,
|
|
safeCloseBrowser,
|
|
safeCloseContext,
|
|
safeClosePage,
|
|
setupBrowserTmp,
|
|
shouldRetryNavigation,
|
|
withTimeout,
|
|
} from './lib/browser.mjs';
|
|
import { discoverRoutes, requireDist, startDistServer } from './lib/dist.mjs';
|
|
|
|
const browserTmp = path.resolve('.astro', 'playwright-overflow-tmp');
|
|
const MAX_NAV_RETRIES = 4;
|
|
// Common device widths: iPhone SE / Galaxy S / iPhone 14 / iPad portrait /
|
|
// iPad landscape / common laptop / full HD desktop.
|
|
const VIEWPORT_WIDTHS = [320, 390, 430, 768, 1024, 1440, 1920];
|
|
const MEASURE_TIMEOUT_MS = 25000;
|
|
|
|
await requireDist();
|
|
await setupBrowserTmp(browserTmp);
|
|
|
|
const routes = await discoverRoutes();
|
|
const { server, port } = await startDistServer();
|
|
const failures = [];
|
|
|
|
async function newMeasurementContext(browser, width) {
|
|
const context = await browser.newContext({
|
|
viewport: { width, height: 900 },
|
|
javaScriptEnabled: true,
|
|
});
|
|
await context.route('**/*', (route) => {
|
|
const type = route.request().resourceType();
|
|
if (type === 'media') {
|
|
route.abort('blockedbyclient');
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
return context;
|
|
}
|
|
|
|
async function openMeasurementContext(browser, width) {
|
|
return withTimeout(
|
|
newMeasurementContext(browser, width),
|
|
CONTEXT_TIMEOUT_MS,
|
|
`Timed out while creating ${width}px Playwright context`
|
|
);
|
|
}
|
|
|
|
async function measureViewport(page) {
|
|
await page.waitForLoadState('load', { timeout: 5000 }).catch(() => {});
|
|
return page.evaluate(() => ({
|
|
scrollWidth: document.documentElement.scrollWidth,
|
|
clientWidth: document.documentElement.clientWidth,
|
|
}));
|
|
}
|
|
|
|
async function measureRoute(context, route) {
|
|
let page;
|
|
try {
|
|
page = await withTimeout(
|
|
context.newPage(),
|
|
PAGE_TIMEOUT_MS,
|
|
`Timed out while creating page for ${route}`
|
|
);
|
|
return await withTimeout(
|
|
(async () => {
|
|
await page.goto(`http://127.0.0.1:${port}${route}`, {
|
|
waitUntil: 'domcontentloaded',
|
|
timeout: 15000,
|
|
});
|
|
return measureViewport(page);
|
|
})(),
|
|
MEASURE_TIMEOUT_MS,
|
|
`Timed out while measuring ${route}`
|
|
);
|
|
} finally {
|
|
if (page) await safeClosePage(page);
|
|
}
|
|
}
|
|
|
|
try {
|
|
for (const width of VIEWPORT_WIDTHS) {
|
|
let browser;
|
|
let context;
|
|
try {
|
|
browser = await openBrowser(browserTmp);
|
|
context = await openMeasurementContext(browser, width);
|
|
for (const route of routes) {
|
|
let result;
|
|
|
|
for (let attempt = 0; attempt < MAX_NAV_RETRIES; attempt += 1) {
|
|
try {
|
|
result = await measureRoute(context, route);
|
|
break;
|
|
} catch (error) {
|
|
if (!shouldRetryNavigation(error) || attempt === MAX_NAV_RETRIES - 1) {
|
|
throw error;
|
|
}
|
|
await safeCloseContext(context);
|
|
await safeCloseBrowser(browser);
|
|
browser = await openBrowser(browserTmp);
|
|
context = await openMeasurementContext(browser, width);
|
|
}
|
|
}
|
|
|
|
if (result.scrollWidth > result.clientWidth + 1) {
|
|
failures.push(
|
|
`${route} overflows at ${width}px: ${result.scrollWidth}px > ${result.clientWidth}px`
|
|
);
|
|
}
|
|
}
|
|
} finally {
|
|
if (context) await safeCloseContext(context);
|
|
if (browser) await safeCloseBrowser(browser);
|
|
}
|
|
}
|
|
} finally {
|
|
server.close();
|
|
await cleanupBrowserTmp(browserTmp);
|
|
}
|
|
|
|
if (failures.length > 0) {
|
|
console.error(failures.join('\n'));
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(
|
|
`No horizontal overflow detected at ${VIEWPORT_WIDTHS.join(', ')}px across ${routes.length} routes.`
|
|
);
|