11.12 — Cross-browser projects & devices
Your suite is green — on chromium. But 11.1 warned you about the engine that surprises everyone. The projects array runs your ENTIRE suite once per named config variant — three engines and a phone, from one set of specs, unchanged. Plus the config block that finally kills “oh, was the dev server running?”
projects: [
{ name: "chromium",
use: { ...devices["Desktop Chrome"] } },
{ name: "firefox",
use: { ...devices["Desktop Firefox"] } },
{ name: "webkit",
use: { ...devices["Desktop Safari"] } },
{ name: "mobile",
use: { ...devices["iPhone 14"] } },
],
webServer: {
command: "npm run start",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
// $ npx playwright test --project=webkit“Works in Chrome” is a fact about ONE engine. 9.1 taught that different runtimes wrap the same language differently — browsers too: chromium, firefox, and webkit each render, scroll, and focus with their own quirks. Safari’s webkit is the classic ambusher, and your users are on it.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Projects can form a DEPENDENCY graph: a setup project (11.11’s login bottling) with dependencies: ["setup"] on the browser projects — the runner topologically orders them (8.1’s graph, once more): bottle once, every lane drinks.
Projects aren’t only for browsers: teams define an api project (request-only tests, no browser — 11.10), a smoke project with a grep filter baked in (11.13), or per-environment projects with different baseURLs. “Named config variant” is the general tool; cross-browser is just its most famous use.
Mobile presets can be composed manually too: use: { viewport: { width: 390, height: 844 }, hasTouch: true } — the registry entries are just objects (4.x) with good defaults. Print one in the REPL (9.1) someday; demystification is a hobby now.
Job note: when asked “how do you handle cross-browser testing?”, the mature answer names the budget, not just the feature: all engines from one suite via projects, chromium per change, full matrix nightly — because engine-specific bugs are real but rare, and quadrupling every run buys little. Feature + judgment is what seniority sounds like.
⌨️ expand the project matrix
The runner multiplies suite × projects into a run matrix. Build the expander: every test on every project lane, named like the real report — with the total computed, not counted on fingers.
requirements:
- Create
testsas["login works", "coupon applies"]andprojectsas["chromium", "firefox", "webkit"]. - Write
expandMatrix(tests, projects): for every project, for every test (2.7’s clock — the inner hand spins fully per outer click), collecttest [project] namelines into an array and return it. - Print each line, then the summary
total runs: 6— from the array’s length.
when you press RUN, the console must show exactly:
✏️ Quick check 1
50 tests, 4 projects (3 engines + mobile). How many test RUNS does a full pass execute?
✏️ Quick check 2
Is devices["iPhone 14"] a real phone? What is it actually? (short phrase)
✏️ Quick check 3
Which config block starts your app and waits for it to answer before tests run?
🗣️ Now teach it back
Explain projects to a friend: what a project is, what the array does to your suite, what devices presets contain (and emulation’s honest limits), what webServer solves, and the cross-browser budget.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.