11.13 — Tags, selective runs & suite hygiene
Five hundred tests is not one thing — pre-merge wants a two-minute pulse check, nightly wants everything, and you want just the one you’re debugging. Tags, grep, honest skips, and the hygiene habits that separate a professional suite from a junk drawer — including the famous .only disaster and the config line that guards it.
test("checkout works @smoke", async ({ page }) => {…});
test("coupon math @regression", async ({ page }) => {…});
// $ npx playwright test --grep @smoke
// $ npx playwright test --grep-invert @slow
test.skip(({ browserName }) => browserName === "webkit",
"drag flaky on webkit — issue #142");
test.fixme("drag-and-drop reorders", async () => {…});
test.only("the one I'm debugging", async () => {…});
// ⚠ NEVER commit — forbidOnly catches it on CI (11.3)
await test.step("add three items", async () => {…});
expect.soft(price).toBe(900);The need first: different moments want different slices. Pre-merge: a fast pulse check, minutes. Nightly: the full matrix (11.12), hours are fine. Debugging: exactly one test, over and over. A suite you can only run WHOLE is a suite you’ll avoid running — selection is daily bread.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Playwright also supports tags as a first-class option — test("name", { tag: "@smoke" }, fn) — which reports can group by. The in-title convention and the option coexist; teams pick one style and stay consistent (the hygiene is the point, not the syntax).
Selection composes into PROJECTS (11.12): a smoke project with grep: /@smoke/ baked in means CI can run “the smoke lane” by name — which is exactly how 11.16’s pipeline will ask for it. Tags feed grep; grep feeds projects; projects feed CI. One convention, all the way up.
skip has more moods: test.skip(true, reason) unconditionally, test.skip(({ browserName }) => …, reason) conditionally per fixture values, and test.slow() — not a skip at all: it triples the timeout for a legitimately slow test, on the record.
Job note: suite hygiene is disproportionately VISIBLE seniority. Consistent tags, reasons on every skip, zero merged .onlys, steps in long tests — none of it is clever, all of it compounds. The engineer whose suite reads like a spec sheet gets trusted with the release button. That’s the actual promotion path hiding in this lesson.
⌨️ build --grep (and witness the .only disaster)
Two selection mechanisms, one lesson: a grep that filters the suite by tag, and an .only simulation that shows exactly how the disaster lies.
requirements:
- Create
suite: five test names —"checkout works @smoke","login works @smoke","coupon math @regression","sort by price @regression","bulk import @slow". - Write
grep(suite, tag): the tests whose names include the tag (4.9 + 1.6’s includes). Run the smoke set: print each match, then2 of 5 ran(computed from both lengths). - Simulate the disaster:
only(suite, name)returns just the one matching test. Run it for"coupon math @regression"and print1 of 5 ran — 4 silently skipped ⚠— the counts computed.
when you press RUN, the console must show exactly:
✏️ Quick check 1
A committed test.only reaches CI with forbidOnly enabled. What happens to the build?
✏️ Quick check 2
What defines a good smoke set? (roughly how many tests, covering what)
✏️ Quick check 3
expect.soft(price).toBe(900) fails mid-test. Does the test keep running, and does it still fail overall? (yes/yes, yes/no, …)
🗣️ Now teach it back
Teach suite hygiene to a friend: tags and grep (what a smoke set is), honest skipping vs fixme, the .only disaster and its config guard, and what test.step and expect.soft each buy.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.