Definition
- Auto-waiting – Actions and assertions automatically wait for elements to be actionable, removing explicit waits and fixed sleeps.
- Tri-engine cross-browser – Drives Chromium, Firefox, and WebKit (Safari's engine) through one API on any OS.
- Parallel workers – Built-in worker-based test runner shards tests across processes without needing Selenium Grid.
- Network interception – Native request routing, mocking, and modification for deterministic tests without a live backend.
- Isolated contexts – Lightweight independent browser sessions in milliseconds for clean test isolation.
- Tracing and diagnostics – Interactive trace viewer with DOM snapshots, network logs, plus automatic screenshots and videos on failure.
Both Playwright and Selenium can drive a browser through the National Bank of India app, but Playwright comes with better built-in tools for a modern single-page app. It waits intelligently so tests flake less, covers Safari's engine too, runs many tests at once without extra infrastructure, can fake the backend's responses, gives each test its own clean session cheaply, and when something fails hands you a video and a trace. Saying "use Playwright because it is newer" is like recommending a car because it is this year's model.
Understanding the Concepts
What is auto-waiting and why does it matter?
Playwright's actions and assertions automatically wait for elements to be attached, visible, stable, and actionable before interacting, and retry until they pass or time out.
// Playwright: auto-waiting built in
await page.getByRole('button', { name: 'Transfer' }).click();
await expect(page.getByText('Success')).toBeVisible();
// No explicit waits needed
// Selenium equivalent requires explicit waits
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.elementToBeClickable(
By.xpath("//button[text()='Transfer']")
)).click();
// Teams often fall back to Thread.sleep(5000)
What is true cross-browser coverage?
Playwright drives Chromium, Firefox, and WebKit through one API. WebKit is Safari's engine, so you can test Safari behavior on Linux or Windows.
// playwright.config.ts: test all three engines
export default defineConfig({
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
{ name: 'webkit', use: { browserName: 'webkit' } },
],
});
# Run against all browsers
npx playwright test
# Playwright bundles the browser binaries it controls
npx playwright install
How does built-in parallelism work?
Playwright's runner shards tests across worker processes without needing Selenium Grid.
// playwright.config.ts
export default defineConfig({
workers: process.env.CI ? 4 : undefined, // 4 workers in CI
fullyParallel: true, // Parallelize within files too
});
# Override on command line
npx playwright test --workers=8
How does network interception work?
Stub APIs, simulate errors, and make tests deterministic without a live backend.
// Mock the balance API to return a controlled response
await page.route('**/api/accounts/*/balance', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ balance: 50000.00, currency: 'INR' }),
});
});
// Simulate a server error
await page.route('**/api/transfer', (route) => {
route.fulfill({ status: 500, body: 'Internal Server Error' });
});
// Simulate slow response
await page.route('**/api/accounts', (route) => {
setTimeout(() => route.continue(), 5000);
});
What are isolated browser contexts?
A context is a lightweight independent session created in milliseconds.
// Two users in one test: multi-context
const senderContext = await browser.newContext();
const receiverContext = await browser.newContext();
const senderPage = await senderContext.newPage();
const receiverPage = await receiverContext.newPage();
// Each has independent cookies and storage
await senderPage.goto('/transfer');
await receiverPage.goto('/accounts');
How does tracing and debugging work?
The trace viewer shows every step with DOM snapshots, network requests, and console logs.
// Enable tracing in config
export default defineConfig({
use: {
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
# Open the trace viewer for a failed test
npx playwright show-trace test-results/transfer-test/trace.zip
How does Playwright compare to Selenium?
| Capability | Playwright | Selenium |
|---|---|---|
| Auto-waiting | Built-in, all actions | Manual: explicit waits, or sleeps |
| Cross-browser | Chromium + Firefox + WebKit | Chrome + Firefox + Edge (Safari needs Mac) |
| Parallelism | Built-in worker model | Needs external runner + Grid |
| Network mocking | Native route/fulfill | No built-in, needs proxy |
| Test isolation | Lightweight contexts (ms) | Full browser sessions (heavier) |
| Tracing | Interactive trace viewer | Screenshots only (manual video) |
| Languages | JS/TS, Python, Java, .NET | Java, Python, JS, C#, Ruby |
| Ecosystem | Growing, modern | Mature, vast, legacy support |
When is Selenium still the right choice?
Selenium remains justified when you need its vast language and grid ecosystem, legacy browser support like older Internet Explorer, or there is a large existing Selenium investment.
Choose Playwright when:
- Modern SPA with dynamic content
- JavaScript/TypeScript team
- Need fast, reliable CI integration
- Need network mocking for determinism
- Need Safari/WebKit testing
Choose Selenium when:
- Large existing Selenium investment
- Need legacy browser support (IE)
- Non-JS team (Ruby, extensive Java)
- Need Selenium Grid infrastructure already in place
What are common interview follow-up questions?
- "What is the single biggest advantage?" Auto-waiting. It removes the largest source of flakiness and the need for explicit waits.
- "Can Playwright do mobile testing?" It can emulate mobile devices (viewport, user agent, touch) but does not test native apps.
- "Is Playwright production-ready?" Yes. Backed by Microsoft, used by major companies, with regular releases.
Visual Overview
Playwright vs Selenium: Key Differences ──────────────────────────────────────── PLAYWRIGHT SELENIUM ┌──────────────────────┐ ┌──────────────────────┐ │ Auto-waiting │ │ Manual waits / sleeps│ │ (built into all │ │ (explicit waits or │ │ actions + assertions)│ │ Thread.sleep) │ ├──────────────────────┤ ├──────────────────────┤ │ Chromium + Firefox │ │ Chrome + Firefox │ │ + WebKit (Safari) │ │ + Edge (Safari: Mac) │ ├──────────────────────┤ ├──────────────────────┤ │ Built-in workers │ │ External Grid needed │ ├──────────────────────┤ ├──────────────────────┤ │ Native network mock │ │ No built-in mocking │ ├──────────────────────┤ ├──────────────────────┤ │ Lightweight contexts │ │ Full browser sessions│ ├──────────────────────┤ ├──────────────────────┤ │ Trace viewer + video │ │ Screenshots only │ └──────────────────────┘ └──────────────────────┘
Key Takeaway
I choose Playwright over Selenium for a modern web app on specific capability grounds, never because "it is newer." Auto-waiting removes explicit waits and sleeps, cutting flakiness. Tri-engine coverage tests Chromium, Firefox, and WebKit through one API. Built-in workers parallelize without Grid. Native network mocking makes tests deterministic. Lightweight contexts give clean isolation in milliseconds. And the trace viewer with screenshots and videos enables fast diagnosis of CI failures.
The balanced point is that Playwright is the strong default for modern SPAs, but Selenium remains justified for its vast ecosystem, legacy browser support, or large existing investments.
At the National Bank of India I migrated a flaky Selenium suite to Playwright. Auto-waiting removed dozens of sleeps, WebKit caught a Safari-only bug, workers cut the run from forty minutes to under ten, and network mocking let us test error states we previously could not reproduce.
Remember one sentence: choose Playwright for auto-waiting, tri-engine coverage, built-in parallelism, network mocking, isolated contexts, and tracing, and justify each capability rather than saying "it is newer."
---