← Back to blog

Playwright interview questions and answers

Playwright interview questions and answers — cover from Greenroom, the AI mock interviewer

Somewhere in every Playwright interview, someone asks "how do you handle a second tab opening after a click?" and a candidate who learned test automation on Cypress freezes for a second — because on Cypress, that question doesn't really have a clean answer, and on Playwright it's a five-line, completely normal piece of code. That pause is the interview, in miniature. Playwright interview questions aren't really testing whether you've memorized page.locator() syntax. They're testing whether you understand why Playwright can do things Cypress and Selenium structurally can't — and why that matters for a real test suite, not a toy one.

This guide covers the Playwright interview questions that actually come up — the architecture behind Playwright, Cypress and Selenium and why it matters more than which one you "prefer," auto-waiting as the mental model underneath every action, locators versus selectors, the commands and fixtures you need cold, parallelization and sharding, the trace viewer for debugging flaky tests, and how a Playwright round fits into a broader SDET loop. If you're prepping across the whole automation stack, pair this with our Cypress interview questions, Selenium interview questions, and SDET interview questions guides — this one goes deep on what's actually unique to Playwright instead of repeating general test-automation ground three times over.

Why Playwright interviews keep circling back to architecture

Playwright, Cypress and Selenium all automate a browser, and every strong Playwright interview question is really asking the same underlying thing: which layer does the tool talk to, and what does that buy or cost you? Get the layer right and the rest — waiting behavior, multi-tab support, language bindings — falls out as a consequence, not a list of features to memorize separately.

Selenium drives the browser from the outside, through a driver binary, over the WebDriver protocol — a synchronous, request-response HTTP API. Cypress runs inside the browser tab, in the same JavaScript run loop as your application, with no separate driver process and no tab boundary it can cross. Playwright, built by former Puppeteer engineers at Microsoft, takes a third path: it talks to the browser directly over the Chrome DevTools Protocol (CDP) for Chromium, and equivalent low-level protocols for Firefox and WebKit, using a persistent WebSocket connection rather than per-command HTTP round-trips or in-page injection. That's the fact the whole interview keeps testing.

  • Why Playwright can auto-wait accurately — the WebSocket connection gives it a live event stream from the browser, so it knows the instant an element is attached, visible, and stable, without polling.
  • Why Playwright supports real multi-tab, multi-origin, and multi-browser-context testing — it isn't running inside the page's world the way Cypress is, so opening a second tab, navigating across origins, or even driving Chromium, Firefox, and WebKit from one script is just a normal API call, not an architectural workaround.
  • Why Playwright ships in Python, Java, C#, and .NET as well as JavaScript/TypeScript — the protocol layer is language-agnostic, so Microsoft built native bindings on top of it instead of being locked into the browser's own language the way Cypress is.

Playwright vs Cypress vs Selenium — the architecture questions

Comparison table: Playwright vs Cypress across runtime, waiting, multi-tab and multi-origin support, language bindings and browser coverage
Interviewers rarely ask "which is best" — they ask which mechanism explains a specific capability or limitation.

"Why can Playwright open a second tab, when Cypress can't?"

Because Playwright's automation layer sits outside any single page — it drives the browser process itself over CDP/WebSocket, and a "page" is just one object it happens to be controlling. When your app opens a new tab, Playwright's context.on('page', ...) event fires and hands you a second, fully independent Page object to assert against, while the first tab keeps running. Cypress can't do this because its test runner is embedded in the tab it's testing — there's no external controller to hand off to. A strong interview answer names this as the direct consequence of the CDP architecture, not a random feature Playwright happened to add.

// handling a link that opens in a new tab
const [newPage] = await Promise.all([
  context.waitForEvent('page'),
  page.getByRole('link', { name: 'Open pricing' }).click(),
]);
await newPage.waitForLoadState();
await expect(newPage).toHaveURL(/pricing/);

"Does Playwright support cross-origin and multi-browser testing natively?"

Yes, and this is the second half of the same architectural point. Because Playwright isn't trapped inside one page's origin sandbox, navigating to a different domain mid-test just works — no equivalent of Cypress's cy.origin() wrapper is needed. Playwright also drives Chromium, Firefox, and WebKit from the same API and the same test file, so a single suite can genuinely assert cross-browser behavior instead of running an entirely separate Selenium grid for Safari coverage. Selenium still supports more real, installed browsers (including legacy ones) through actual browser vendor drivers; Playwright's WebKit is Apple's engine, not literally Safari, which is a fair, honest caveat to raise if asked how "real" the cross-browser coverage is.

"When would you actually choose Selenium or Cypress over Playwright?"

The honest interview answer, and the one that signals real experience: Selenium still wins when a team needs a non-JS test language locked to their app's stack (a Java or C# shop with existing Selenium infrastructure), true legacy-browser coverage, or an existing investment too large to rewrite. Cypress still wins on raw developer experience for frontend-heavy teams that want the fastest possible test-writing loop with a built-in time-travel debugger, and its ecosystem of plugins is more mature. Playwright wins when a team needs genuine multi-tab/multi-origin/multi-browser coverage, wants one test file portable across languages, or is building automation fresh in 2026 without legacy constraints. A candidate who says "Playwright is just strictly better" hasn't run all three in production long enough to have hit Playwright's own rough edges — like a smaller ecosystem of third-party plugins than Cypress, or a steeper day-one learning curve than Cypress's more opinionated defaults.

Auto-waiting: the mental model that answers half the interview

Ask a Selenium veteran how to wait for a button, and you'll hear WebDriverWait and ExpectedConditions. Ask them the same question about Playwright, and the correct answer is: you usually don't write a wait at all. Every Playwright action — click(), fill(), check() — automatically waits for the target element to be attached to the DOM, visible, stable (not mid-animation), enabled, and able to receive events, before it acts. If that doesn't happen within the timeout, Playwright fails with a specific, readable error naming exactly which actionability check didn't pass — not a vague "element not found."

// no explicit wait needed — click() waits for actionability on its own
await page.getByRole('button', { name: 'Save' }).click();

// assertions retry too — this polls until the toast text matches or times out
await expect(page.getByText('Saved')).toBeVisible();

This is the same retry-ability idea Cypress interviews probe, expressed through a different mechanism — CDP's live event stream instead of Cypress's shared run loop — and interviewers will ask you to name the anti-pattern it replaces:

// wrong: guessing at a fixed delay, still flaky on a slow CI runner
await page.click('.save-btn');
await page.waitForTimeout(2000);
const toast = await page.isVisible('.toast');

// right: let the assertion poll until it's true, or fail with a clear reason
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.locator('.toast')).toContainText('Saved');

page.waitForTimeout() exists, but Playwright's own docs discourage it for anything but debugging — a good answer names it as the "smell," the same way cy.wait(2000) is the tell in a Cypress interview.

Locators vs selectors — what's actually different

This is a favorite trick question because the two terms sound interchangeable and aren't. A selector (an older Playwright concept, page.$()/page.click(selector)) is evaluated once, immediately, against the current DOM — if the element isn't there yet, or gets re-rendered, the reference is stale. A locator (page.locator(), and the more specific page.getByRole(), page.getByText(), page.getByLabel()) is a lazy, re-evaluating description of how to find an element — it doesn't query the DOM until you act on it or assert against it, and every time it's used it re-queries fresh. That laziness is why auto-waiting works at all: a locator can retry, because it re-resolves the query each time, where a captured selector reference cannot.

// prefer role/label/text-based locators over CSS — they mirror how users find things
await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
await page.getByLabel('Password').fill('••••••••');
await page.getByRole('button', { name: 'Log in' }).click();
await expect(page.getByText('Welcome back')).toBeVisible();

The Playwright team's official recommendation, and a strong thing to say unprompted in an interview: prefer role-based and user-facing locators (getByRole, getByLabel, getByText) over CSS or XPath selectors, because they test the app the way a real user and a screen reader perceive it, and they survive a class-name refactor that would break a brittle .css-4a2b1 selector. data-testid attributes remain the right fallback when nothing user-facing distinguishes an element.

The commands and fixtures you need cold

  • page.locator(selector) and its getBy* family — the core query API; every one of them is auto-retrying when paired with an action or expect().
  • expect(locator).toBeVisible() / toHaveText() / toHaveValue() — Playwright's built-in assertion library, which is web-first and retrying by default, unlike a plain Jest/Chai assertion evaluated once against a snapshot value.
  • page.route() — intercepts and mocks network requests, Playwright's equivalent of Cypress's cy.intercept(), and just as central to writing deterministic tests instead of ones dependent on a real backend.
await page.route('**/api/users/*', (route) =>
  route.fulfill({ path: 'fixtures/user.json' })
);
await page.goto('/profile');
await expect(page.getByTestId('username')).toHaveText('Priya');
  • Fixtures — Playwright Test's dependency-injection system. A test function declares the fixtures it needs as parameters ({ page }, or a custom one you define), and Playwright constructs, hands off, and tears down each one automatically. This is Playwright's answer to Cypress's custom commands, and interviewers use it to check whether you've built a real framework or only run the quickstart.
// fixtures.ts — a custom fixture that logs in once and hands back an authenticated page
export const test = base.extend({
  authedPage: async ({ page }, use) => {
    await page.request.post('/api/login', { data: { email, password } });
    await use(page);
  },
});

// in a spec file:
test('shows the dashboard', async ({ authedPage }) => {
  await authedPage.goto('/dashboard');
});

page.evaluate() — runs arbitrary JavaScript inside the page context and returns the result, useful for reading application state directly, but a fallback, not a default — over-reliance on it is a sign a candidate is testing internals instead of user-visible behavior.

Parallelization and sharding

Playwright Test parallelizes by default — each test file runs in its own worker process, in parallel, out of the box, with no separate plugin required (unlike Cypress, where parallelization requires the paid Cypress Cloud or a third-party orchestration layer). Beyond parallel workers on one machine, Playwright supports sharding: splitting the full test suite across multiple CI machines with --shard=1/4, --shard=2/4, and so on, so a 40-minute suite can run in 10 minutes across four runners. Interviewers at companies with large suites ask this specifically because it's a real cost and CI-time lever, not a theoretical one — expect a follow-up like "how do you keep shards balanced" (answer: Playwright's built-in test-duration-based sharding logic, or a custom --shard split by historical timing data).

// playwright.config.ts
export default defineConfig({
  fullyParallel: true,
  workers: process.env.CI ? 4 : undefined,
  retries: process.env.CI ? 2 : 0,
});

A good answer also names the tradeoff: parallel workers mean tests must not share mutable state (a shared test account, a shared database row) — the same test-isolation discipline good Cypress and Selenium suites need, just enforced harder because Playwright makes parallelism the default rather than an opt-in.

The trace viewer — the interview's favorite debugging question

Ask almost any Playwright interviewer "how do you debug a flaky test that only fails in CI," and the answer they're listening for starts with the trace viewer. Configured with trace: 'on-first-retry' (or 'retain-on-failure'), Playwright records a full trace of the failing run — a DOM snapshot at every action, network requests, console logs, and a screenshot-by-screenshot timeline you can scrub through, all in a local, offline HTML viewer (npx playwright show-trace trace.zip). It's a materially different debugging experience from Cypress's screenshots-and-video (useful, but not scrubbable state-by-state) or Selenium's bare stack trace with no visual record at all.

// playwright.config.ts
export default defineConfig({
  use: { trace: 'on-first-retry' },
});

The interview signal here isn't "do you know the flag" — it's whether you can describe using a trace to diagnose a specific failure: a network response that came back slower in CI than locally, an element that was covered by a still-animating overlay for 200ms, or a race between two page.route() mocks. Naming a real trace-viewer debugging session beats reciting "I checked the CI logs."

The core truth: almost every Playwright interview question — multi-tab support, why waits are automatic, why locators beat selectors, why sharding is built in — is the same architectural fact wearing a different costume: Playwright drives the browser over CDP/WebSocket from outside the page, with a live event stream and first-class parallelism, and neither Cypress nor Selenium was built that way.

How this fits an SDET interview loop

A Playwright round rarely stands alone — it's usually one segment inside a broader SDET interview loop: a coding round (plain TypeScript/JavaScript or Python, not Playwright-specific), a framework-design conversation where fixtures, page-object patterns, and sharding strategy come up, a test-design discussion (given a feature, enumerate the cases), and a behavioral round about a flaky test you actually fixed. Job descriptions increasingly list "Playwright or Cypress" as roughly interchangeable — expect the interviewer to ask you to justify whichever one the team didn't pick, briefly, to confirm you understand the tradeoff rather than only knowing the tool you happened to use.

How people actually prepare for this — and where it falls short

Most Playwright prep is the (genuinely well-written) official docs at playwright.dev — Microsoft invested real effort in the documentation, and it shows — or a GeeksforGeeks-style question dump skimmed the night before. Both hand you vocabulary: you'll be able to define a locator or name the trace viewer. Neither rehearses the part that actually costs candidates points: explaining out loud, to a stranger who then asks a real follow-up, why a page.waitForTimeout() in a PR you're reviewing should be replaced with an auto-waiting assertion — and what you'd say if they push back with "but the animation genuinely takes 800ms." Pasting the same question into ChatGPT gets a correct written explanation to read silently, which is a different skill from producing one verbally, under a little pressure, while someone is actively listening for whether you understand the CDP/retry mechanism or just memorized the term.

Greenroom, built around Ari, our AI interviewer, runs spoken technical mock interviews that ask real follow-ups — including the "what if the animation genuinely takes 800ms" kind — and gives feedback on how clearly you explained the mechanism, not just whether your final answer was technically correct. It won't invent a "% of Playwright interviews passed" statistic, because we don't have one yet; what it does is make you say the auto-waiting and CDP answers out loud before a real interviewer asks first. The honest tradeoff: Greenroom doesn't run or grade an actual Playwright suite the way a real take-home assignment would — it's a spoken concept check, not a coding sandbox, so keep writing and running real Playwright specs against a real app as the other half of your prep.

Frequently asked questions

What Playwright interview questions come up most often?

The architecture difference from Cypress and Selenium (CDP/WebSocket vs WebDriver vs in-browser), why actions and assertions auto-wait and what "actionability" checks mean, locators vs selectors and why role-based locators are preferred, page.route() for network mocking, how parallelization and sharding work, and how to use the trace viewer to debug a flaky test are the questions that come up in nearly every Playwright round, from mid-level QA screens to senior SDET loops.

Is Playwright or Cypress more likely to come up in an interview?

It depends on the company's actual stack, and interviewers are specifically testing whether you know both well enough to justify a choice rather than only knowing whichever one the job description happens to mention. Teams that need real multi-tab, multi-origin, or multi-browser coverage, or that want one test suite portable across languages, lean Playwright; teams that prioritize the fastest possible developer experience and have deep Cypress plugin investment lean Cypress. See our Cypress interview questions guide for the in-browser side.

Does Playwright support testing multiple tabs or browser contexts?

Yes, natively — because Playwright drives the browser from outside any single page over the Chrome DevTools Protocol, opening a new tab just hands you a second, independent Page object via the context.on('page', ...) event, and you can assert against both tabs in the same test. This is a direct consequence of Playwright's architecture and one of the clearest ways it differs from Cypress, which runs inside a single tab and can't natively drive a second one.

Why doesn't Playwright need explicit waits like Selenium?

Because every Playwright action and web-first assertion automatically waits for the target element to become actionable — attached, visible, stable, and enabled — by reading a live event stream from the browser over its WebSocket connection, rather than polling. Selenium drives the browser externally over the WebDriver protocol with no equivalent built-in signal, which is why Selenium tests typically need explicit WebDriverWait calls that Playwright tests generally don't.

What is the difference between a Playwright locator and a selector?

A selector is resolved once, immediately, against the current DOM, so a stale or not-yet-rendered element breaks it. A locator (page.locator(), page.getByRole(), and similar) is a lazy description of how to find an element that re-queries the DOM fresh every time it's used, which is exactly what lets Playwright retry an action or assertion until the element is actually ready instead of failing immediately.

How do I debug a flaky Playwright test?

Start with the trace viewer: set trace: 'on-first-retry' in your config, reproduce the failure, and run npx playwright show-trace on the resulting trace file to scrub through a DOM snapshot, network requests, and console logs at every step of the failing run. Common root causes to name specifically: a waitForTimeout() masking a real race condition, a shared mutable fixture breaking under Playwright's default parallel workers, or a page.route() mock racing with a real network call — a specific diagnosis beats a vague "I made it more stable."

Playwright interviews test whether you understand the CDP-driven, auto-waiting architecture, not whether you've memorized locator syntax. Greenroom runs spoken technical mock interviews with real follow-up questions and feedback on how clearly you explain your reasoning. Free to start.
Try free →