@vercel/devlow-bench
Advanced tools
Comparing version 0.2.4 to 0.3.0
import { chromium } from "playwright-chromium"; | ||
import { measureTime, reportMeasurement } from "./index.js"; | ||
const browserOutput = !!process.env.BROWSER_OUTPUT; | ||
const browserOutput = Boolean(process.env.BROWSER_OUTPUT); | ||
async function withRequestMetrics(metricName, page, fn) { | ||
@@ -12,3 +12,5 @@ const activePromises = []; | ||
const status = response.status(); | ||
const extension = /^[^\?#]+\.([a-z0-9]+)(?:[\?#]|$)/i.exec(url)?.[1] ?? "none"; | ||
const extension = | ||
// eslint-disable-next-line prefer-named-capture-group -- TODO: address lint | ||
/^[^?#]+\.([a-z0-9]+)(?:[?#]|$)/i.exec(url)?.[1] ?? "none"; | ||
const currentRequests = requestsByExtension.get(extension) ?? 0; | ||
@@ -21,3 +23,5 @@ requestsByExtension.set(extension, currentRequests + 1); | ||
} | ||
catch { } | ||
catch { | ||
// empty | ||
} | ||
if (body) { | ||
@@ -96,4 +100,11 @@ const size = body.length; | ||
} | ||
function networkIdle(page, delay = 300, rejectTimeout = 180000) { | ||
return new Promise((resolve, reject) => { | ||
/** | ||
* Waits until network requests have all been resolved | ||
* @param page - Playwright page object | ||
* @param delayMs - Amount of time in ms to wait after the last request resolves before cleaning up | ||
* @param timeoutMs - Amount of time to wait before continuing. In case of timeout, this function resolves | ||
* @returns | ||
*/ | ||
function networkIdle(page, delayMs = 300, timeoutMs = 180000) { | ||
return new Promise((resolve) => { | ||
const cleanup = () => { | ||
@@ -108,15 +119,17 @@ page.off("request", requestHandler); | ||
}; | ||
let activeRequests = 0; | ||
const requests = new Map(); | ||
const start = Date.now(); | ||
let lastRequest; | ||
let timeout = null; | ||
const requests = new Set(); | ||
const fullTimeout = setTimeout(() => { | ||
cleanup(); | ||
reject(new Error(`Timeout while waiting for network idle. These requests are still pending: ${Array.from(requests).join(", ")}}`)); | ||
}, rejectTimeout); | ||
const requestFilter = async (request) => { | ||
return (await request.headers().accept) !== "text/event-stream"; | ||
// eslint-disable-next-line no-console -- logging | ||
console.error(`Timeout while waiting for network idle. These requests are still pending: ${Array.from(requests).join(", ")}} time is ${lastRequest - start}`); | ||
resolve(Date.now() - lastRequest); | ||
}, timeoutMs); | ||
const requestFilter = (request) => { | ||
return request.headers().accept !== "text/event-stream"; | ||
}; | ||
const requestHandler = async (request) => { | ||
requests.add(request.url()); | ||
activeRequests++; | ||
const requestHandler = (request) => { | ||
requests.set(request.url(), (requests.get(request.url()) ?? 0) + 1); | ||
if (timeout) { | ||
@@ -128,19 +141,30 @@ clearTimeout(timeout); | ||
// so we need to do this weird stunt to ensure that | ||
if (!(await requestFilter(request))) { | ||
await requestFinishedInternal(request); | ||
if (!requestFilter(request)) { | ||
requestFinishedInternal(request); | ||
} | ||
}; | ||
const requestFinishedHandler = async (request) => { | ||
if (await requestFilter(request)) { | ||
const requestFinishedHandler = (request) => { | ||
if (requestFilter(request)) { | ||
requestFinishedInternal(request); | ||
} | ||
}; | ||
const requestFinishedInternal = async (request) => { | ||
requests.delete(request.url()); | ||
activeRequests--; | ||
if (activeRequests === 0) { | ||
const requestFinishedInternal = (request) => { | ||
lastRequest = Date.now(); | ||
const currentCount = requests.get(request.url()); | ||
if (currentCount === undefined) { | ||
// eslint-disable-next-line no-console -- basic logging | ||
console.error(`Unexpected untracked but completed request ${request.url()}`); | ||
return; | ||
} | ||
if (currentCount === 1) { | ||
requests.delete(request.url()); | ||
} | ||
else { | ||
requests.set(request.url(), currentCount - 1); | ||
} | ||
if (requests.size === 0) { | ||
timeout = setTimeout(() => { | ||
cleanup(); | ||
resolve(); | ||
}, delay); | ||
resolve(Date.now() - lastRequest); | ||
}, delayMs); | ||
} | ||
@@ -167,5 +191,6 @@ }; | ||
async hardNavigation(metricName, url) { | ||
const page = (this.page = this.page ?? (await this.context.newPage())); | ||
this.page = this.page ?? (await this.context.newPage()); | ||
const page = this.page; | ||
await withRequestMetrics(metricName, page, async () => { | ||
measureTime(`${metricName}/start`); | ||
await measureTime(`${metricName}/start`); | ||
const idle = networkIdle(page, 3000); | ||
@@ -175,16 +200,16 @@ await page.goto(url, { | ||
}); | ||
measureTime(`${metricName}/html`, { | ||
await measureTime(`${metricName}/html`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await page.waitForLoadState("domcontentloaded"); | ||
measureTime(`${metricName}/dom`, { | ||
await measureTime(`${metricName}/dom`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await page.waitForLoadState("load"); | ||
measureTime(`${metricName}/load`, { | ||
await measureTime(`${metricName}/load`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await idle; | ||
measureTime(`${metricName}`, { | ||
offset: 3000, | ||
const offset = await idle; | ||
await measureTime(`${metricName}`, { | ||
offset, | ||
relativeTo: `${metricName}/start`, | ||
@@ -201,12 +226,16 @@ }); | ||
await withRequestMetrics(metricName, page, async () => { | ||
measureTime(`${metricName}/start`); | ||
const firstResponse = new Promise((resolve) => page.once("response", () => resolve())); | ||
await measureTime(`${metricName}/start`); | ||
const firstResponse = new Promise((resolve) => { | ||
page.once("response", () => { | ||
resolve(); | ||
}); | ||
}); | ||
const idle = networkIdle(page, 3000); | ||
await page.click(selector); | ||
await firstResponse; | ||
measureTime(`${metricName}/firstResponse`, { | ||
await measureTime(`${metricName}/firstResponse`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await idle; | ||
measureTime(`${metricName}`, { | ||
await measureTime(`${metricName}`, { | ||
offset: 3000, | ||
@@ -223,3 +252,3 @@ relativeTo: `${metricName}/start`, | ||
await withRequestMetrics(metricName, page, async () => { | ||
measureTime(`${metricName}/start`); | ||
await measureTime(`${metricName}/start`); | ||
const idle = networkIdle(page, 3000); | ||
@@ -229,15 +258,15 @@ await page.reload({ | ||
}); | ||
measureTime(`${metricName}/html`, { | ||
await measureTime(`${metricName}/html`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await page.waitForLoadState("domcontentloaded"); | ||
measureTime(`${metricName}/dom`, { | ||
await measureTime(`${metricName}/dom`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await page.waitForLoadState("load"); | ||
measureTime(`${metricName}/load`, { | ||
await measureTime(`${metricName}/load`, { | ||
relativeTo: `${metricName}/start`, | ||
}); | ||
await idle; | ||
measureTime(`${metricName}`, { | ||
await measureTime(`${metricName}`, { | ||
offset: 3000, | ||
@@ -252,2 +281,3 @@ relativeTo: `${metricName}/start`, | ||
headless: options.headless ?? process.env.HEADLESS !== "false", | ||
devtools: true, | ||
timeout: 60000, | ||
@@ -254,0 +284,0 @@ }); |
{ | ||
"name": "@vercel/devlow-bench", | ||
"version": "0.2.4", | ||
"version": "0.3.0", | ||
"description": "Benchmarking tool for the developer workflow", | ||
@@ -37,3 +37,3 @@ "type": "module", | ||
"pidusage-tree": "^2.0.5", | ||
"playwright-chromium": "^1.35.0", | ||
"playwright-chromium": "^1.39.0", | ||
"split2": "^4.2.0", | ||
@@ -40,0 +40,0 @@ "tree-kill": "^1.2.2" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
66556
1196
Updatedplaywright-chromium@^1.39.0