SPB Git

Repository Public

performance.spec.ts 2.2 KB 37 lines typescript

1import { expect, test } from "@playwright/test";23/** Basic performance budget for the first viewport. Deliberately loose: it catches regressions, not milliseconds. */4test("home: small document, fast first paint, lean JavaScript", async ({ page }) => {5  const sizes: { url: string; type: string; bytes: number }[] = [];6  page.on("response", async (r) => {7    const type = r.request().resourceType();8    if (type === "document" || type === "script" || type === "stylesheet" || type === "font") {9      const len = (await r.body().catch(() => Buffer.alloc(0))).length;   // decoded size, not transfer size10      sizes.push({ url: r.url(), type, bytes: len });11    }12  });13  await page.goto("/", { waitUntil: "load" });14  const nav = await page.evaluate(() => {15    const n = performance.getEntriesByType("navigation")[0] as PerformanceNavigationTiming;16    const fcp = performance.getEntriesByName("first-contentful-paint")[0]?.startTime ?? null;17    return { ttfb: n.responseStart - n.requestStart, dcl: n.domContentLoadedEventEnd, fcp };18  });19  const total = (type: string) => sizes.filter((s) => s.type === type).reduce((n, s) => n + s.bytes, 0);20  console.log(`ttfb ${Math.round(nav.ttfb)} ms · DCL ${Math.round(nav.dcl)} ms · FCP ${nav.fcp === null ? "n/a" : Math.round(nav.fcp) + " ms"} · js ${Math.round(total("script") / 1024)} kB · css ${Math.round(total("stylesheet") / 1024)} kB (decoded)`);21  expect(nav.ttfb).toBeLessThan(1500);22  expect(nav.dcl).toBeLessThan(4000);23  if (nav.fcp !== null) expect(nav.fcp).toBeLessThan(3000);24  expect(total("script")).toBeLessThan(650 * 1024);       // framework runtime + player, uncompressed25  expect(total("stylesheet")).toBeLessThan(120 * 1024);26});2728test("the hero video is not downloaded in full before it plays", async ({ page, request }) => {29  const films = (await (await request.get("/api/films?limit=1")).json()).films;30  test.skip(!films.length, "no published film");31  let videoBytes = 0;32  page.on("response", (r) => { if (r.url().includes("/media/") && r.url().endsWith(".mp4")) videoBytes += Number(r.headers()["content-length"] ?? 0); });33  await page.goto("/", { waitUntil: "domcontentloaded" });34  await page.waitForTimeout(1500);35  expect(videoBytes).toBeLessThanOrEqual(films[0].bytes + 1024);36});37
Files main
Menu

Theme