Playwright CI: What Senior Engineers Do Differently

dmitryaqa1 pts0 comments

Playwright CI: What Senior Engineers Do Differently | BDR Methodology<br>Skip to content

Playwright CI: What Senior Engineers Do Differently

May 12, 2026<br>Dmitry<br>QA Automation Engineer

Playwright CI: What Senior Engineers Do Differently PRO IMPLEMENTATION<br>Section titled “Playwright CI: What Senior Engineers Do Differently ”

New to Playwright architecture? Start with the fundamentals first: Why Your Playwright Tests Fail in CI (And Never Locally) — the same concepts with more explanation and simpler examples.

Most teams reach a point where their test suite becomes a liability. Green locally, red in CI. Passes on retry, fails on the next run. The usual response is to increase timeouts, add waitForTimeout, and move on. The problem compounds quietly until someone spends a full day debugging a test that was never actually broken.

This guide is about the architectural decisions that prevent that from happening. Not “use better selectors” — you already know that. The decisions that determine whether your test infrastructure scales or slowly collapses under its own weight.

Code examples are intentionally simplified — focus on the architectural pattern, not the implementation details.

Mental Model Shift: Leaving Legacy Baggage Behind<br>Section titled “Mental Model Shift: Leaving Legacy Baggage Behind”

##TL;DR

Dependency Projects over globalSetup — fail fast when the environment is down, not after 800 tests. API auth in 50ms, not UI auth in 5 seconds. getByRole queries the accessibility tree — role survives refactoring, { name } doesn’t survive translation. Web-first assertions poll until ready — isVisible() is a snapshot. expect.poll for state that changes outside the UI — webhooks, background jobs, queues. Trace Viewer’s Action/Before/After snapshots show you why a click failed, not just that it did.

Before getting into architecture, a quick audit. Senior engineers migrating from Selenium or Puppeteer often bring habits that fight Playwright instead of leveraging it. These aren’t stylistic preferences — they’re architectural differences that affect reliability at scale.

If any of these look familiar in your codebase, fix them before layering on anything else:

page.$() or page.$$() → getByRole(), getByLabel(), getByTestId()<br>Playwright locators are lazy and auto-retried on assertions. $() executes immediately against the current DOM state and cannot be polled.

waitForSelector() or waitForTimeout() → Remove them<br>Playwright auto-waits for actionability before every interaction. Explicit waits are almost always either redundant or masking a real problem.

waitForNavigation() → await expect(page).toHaveURL('/dashboard')<br>waitForNavigation() is prone to race conditions — it can resolve before the page is actually ready. toHaveURL polls until the URL matches, which is what you actually want.

isVisible(), isEnabled() in assertions → expect(loc).toBeVisible(), expect(loc).toBeEnabled()<br>Snapshot methods return the state at one millisecond. Web-first assertions retry until the condition is true or the timeout expires.

console.log('HERE') → Trace Viewer<br>Logs tell you that something happened. Traces show you the DOM, network, and console at the exact moment it happened — in CI, after the fact.

If your team is mid-migration, this is worth a dedicated refactor sprint. The patterns below assume you’re past this baseline.

The Problem With How Most Teams Structure Test Infrastructure<br>Section titled “The Problem With How Most Teams Structure Test Infrastructure”

The typical Playwright setup looks like this: a globalSetup file that handles authentication, maybe some shared fixtures, and a flat list of test files. This works at 50 tests. At 500, the cracks appear.

globalSetup runs once, outside Playwright’s normal execution context. When it fails, you get dry Node.js logs. No trace, no network timeline, no DOM snapshots. You’re debugging blind.

More critically: there’s no built-in way to say “don’t run 800 tests if the environment is down.” You get 800 failures that all say the same thing and tell you nothing useful.

The Architecture: Dependency Projects as a Dependency Graph<br>Section titled “The Architecture: Dependency Projects as a Dependency Graph”

The senior approach treats test infrastructure as a directed acyclic graph. Each node has prerequisites. If a prerequisite fails, dependent nodes don’t run.

playwright.config.tsexport default defineConfig({

projects: [

name: 'auth-setup',

testMatch: /.*\.auth\.setup\.ts/,

},

name: 'healthcheck',

testMatch: /.*\.health\.setup\.ts/,

dependencies: ['auth-setup'],

},

name: 'chromium',

use: { ...devices['Desktop Chrome'] },

dependencies: ['healthcheck'],

},

name: 'firefox',

use: { ...devices['Desktop Firefox'] },

dependencies: ['healthcheck'],

},

],

});

The order in the array doesn’t matter — Playwright builds the graph automatically. What matters is the dependencies field.

What this buys you:

When the staging environment goes down at 2am, your CI doesn’t...

playwright senior test engineers differently dependency

Related Articles