Frontend Labs
⋯

84. Testing the lab, extending it, and where to go next

The lab's own test suite

tests/lab.spec.js runs every test under both projects, http1 (http://localhost:18080) and h2 (https://localhost:18443, with ignoreHTTPSErrors for the self-signed cert). Because the same assertions run over both protocols, they double as protocol tests:

test("request burst: protocol decides how many connections", async ({ page }, testInfo) => {
  await open(page, "network");
  await page.getByLabel("Number of requests").fill("12");
  await page.getByLabel("Server delay (ms)").fill("300");
  await page.getByRole("button", { name: "Fire the burst" }).click();
  const readout = (label) => page.locator(".readout", { hasText: label }).locator("b");
  if (testInfo.project.name === "h2") {
    await expect(readout("Protocol")).toHaveText("h2");
    await expect(readout("Connections used")).toHaveText("1");
  } else {
    await expect(readout("Protocol")).toHaveText("http/1.1");
    await expect(readout("Connections used")).toHaveText(/^[2-7]$/);
  }
});

test("the event-loop model matches the real engine for every snippet", async ({ page }) => {
  await open(page, "eventloop");
  for (const tab of await page.getByRole("tab").all()) {
    await tab.click();
    await page.getByRole("button", { name: "Run it for real" }).click();
    await expect(page.locator("[data-match]")).toHaveAttribute("data-match", "true");
  }
});

Other tests cover: every station renders without unexpected page errors, the page is cross-origin isolated, the render tree drops display:none, thrashing is slower than batching, Atomics loses 0 updates, the worker keeps the page responsive, IndexedDB round-trips and the TransactionInactiveError pitfall, a late ad pushes CLS past 0.1, a 350 ms handler registers as a slow interaction, errors and CSP violations are captured, and missions persist across a reload. The suite is 46 tests, and it passed 225 out of 225 runs with --repeat-each=5 (the HTTPS-only check is skipped on HTTP/1.1).

Extend it

Each extension below exercises a different Part of the guide:

  1. A Service Worker station (§40): register a worker, show the cache filling, take the lab offline in DevTools, and watch it still load.
  2. A WebGPU station (§57): run the vector-add shader against a CPU loop and chart the crossover point where the GPU wins.
  3. An HTTP/3 lane (§26): put the lab behind a QUIC-capable proxy (Caddy, or a CDN) and add h3 to the waterfall comparison.
  4. A memory-leak hunt mission: make the lab leak in three hidden ways (a listener, a closure, a timer) and have the user find each with heap snapshots.
  5. A field-data panel: send the vitals with navigator.sendBeacon to a /rum endpoint on the server and chart p75 across visits.
  6. Your own station: pick the concept you understood last, build the station that would have taught it to you, then write the Playwright test that proves it works.

Where to go from here: a path to expert

You've now built and tested, rather than just read about, every layer between a keypress and a pixel: language, engine, event loop, network, rendering, threads, storage, security, AI integration and testing. Expert JavaScript engineers keep going in three directions:

  • Depth: read the specs when docs disagree. ECMA-262 for the language, the WHATWG HTML spec's "event loops" section for scheduling, and Fetch for networking. Read V8's blog (v8.dev) and engine release notes.
  • Breadth across runtimes: Node, Deno, Bun and edge isolates (§62) share V8/JSC and Web APIs but differ in I/O, permissions and module loading. Port Browser Lab's server to each.
  • Teaching: build the station, write the test, explain the bug you hit. This guide's war stories came from exactly that process. Every one of them was found by a test that someone bothered to write.

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36