Created
November 11, 2024 14:04
-
-
Save mizchi/a137d2870f51ae2b1c94c48944ba615f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* # Lighthouse performance testing with n times by deno | |
$ deno run -A src/lhs.ts https://www.npmjs.com/package/puppeteer --times 3 | |
{ | |
avg: 0.45999999999999996, | |
times: 3, | |
scores: { | |
"first-contentful-paint": 0.03, | |
"first-meaningful-paint": 0.12, | |
"speed-index": 0.45, | |
"total-blocking-time": 0.53, | |
"max-potential-fid": 0.02, | |
interactive: 0.15, | |
"mainthread-work-breakdown": 0.98, | |
"bootup-time": 0.96, | |
"uses-rel-preconnect": 0.89, | |
"uses-long-cache-ttl": 0.5, | |
"render-blocking-resources": 0.25, | |
"unused-css-rules": 0.78, | |
"unused-javascript": 0.29, | |
"legacy-javascript": 0.9, | |
"largest-contentful-paint": 0.01, | |
"unminified-css": 0.9 | |
} | |
} | |
*/ | |
import chromeFinder from "npm:[email protected]"; | |
import puppeteer, { Page } from "npm:[email protected]"; | |
import Lighthouse, { Config as LhsConfig, Audit } from "npm:[email protected]"; | |
const lhsMobileConfig: LhsConfig = { | |
extends: "lighthouse:default", | |
settings: { | |
onlyCategories: ["performance"], | |
formFactor: "mobile", | |
screenEmulation: { | |
mobile: true, | |
width: 412, | |
height: 823, | |
deviceScaleFactor: 2, | |
disabled: false, | |
}, | |
throttling: { | |
cpuSlowdownMultiplier: 4, | |
throughputKbps: 1.6 * 1024, | |
rttMs: 150, | |
}, | |
}, | |
}; | |
interface AuditResult extends Audit { | |
id: string; | |
score: number | null; | |
} | |
async function getLhPerf( | |
page: Page, | |
options: { | |
url: string; | |
} | |
): Promise<{ at: number; score: number | null; audits: Array<AuditResult> }> { | |
const now = Date.now(); | |
const runnerResult = await Lighthouse( | |
options.url, | |
{ | |
port: Number(new URL(page.browser().wsEndpoint()).port), | |
logLevel: "error", | |
output: "json", | |
}, | |
lhsMobileConfig | |
); | |
if (!runnerResult) throw new Error("Lighthouse failed to run"); | |
const perf = runnerResult.lhr.categories.performance; | |
const score = perf.score; | |
const currentAudits = Object.values(runnerResult.lhr.audits).filter( | |
(v) => v.score && v.score < 1 | |
); | |
return { | |
at: now, | |
score, | |
audits: currentAudits, | |
}; | |
} | |
const SCORE_ACCURACY = 100; | |
async function getLhPerfAvg( | |
page: Page, | |
options: { | |
url: string; | |
times: number; | |
interval: number; | |
} | |
) { | |
const results: number[] = []; | |
const uniqAudits: Map<string, number[]> = new Map(); | |
for (let i = 0; i < options.times; i++) { | |
await new Promise((resolve) => setTimeout(resolve, options.interval)); | |
const lhPerf = await getLhPerf(page, options); | |
lhPerf.score && results.push(lhPerf.score); | |
for (const audit of lhPerf.audits) { | |
if (!uniqAudits.has(audit.id)) { | |
uniqAudits.set(audit.id, []); | |
} | |
uniqAudits.get(audit.id)!.push(audit.score!); | |
} | |
} | |
const scores: { [key: string]: number } = {}; | |
for (const [key, value] of uniqAudits) { | |
scores[key] = | |
Math.floor( | |
(SCORE_ACCURACY * value.reduce((a, b) => a + b, 0)) / value.length | |
) / SCORE_ACCURACY; | |
} | |
return { | |
avg: results.reduce((a, b) => a + b, 0) / results.length, | |
times: options.times, | |
scores, | |
}; | |
} | |
// CLI | |
import { parseArgs } from "node:util"; | |
const parsed = parseArgs({ | |
options: { | |
times: { | |
type: "string", | |
short: "t", | |
}, | |
interval: { | |
type: "string", | |
short: "i", | |
}, | |
}, | |
allowPositionals: true, | |
}); | |
if (import.meta.main) { | |
let browser: puppeteer.Browser | undefined = undefined; | |
try { | |
browser = await puppeteer.launch({ | |
headless: true, | |
executablePath: chromeFinder(), | |
args: ["--no-sandbox", "--disable-setuid-sandbox"], | |
}); | |
const url = parsed.positionals[0]; | |
const page = await browser.newPage(); | |
// await page.goto(url, { waitUntil: "networkidle2" }); | |
const summary = await getLhPerfAvg(page, { | |
url, | |
times: Number(parsed.values.times ?? 1), | |
interval: Number(parsed.values.interval ?? 5000), | |
}); | |
console.log(summary); | |
} finally { | |
browser?.close(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment