SoftwareTestPilot
50 curated Playwright Q&A

Playwright Interview Questions & Answers (2026) — Land Your SDET Role

Fifty hand-written Playwright questions with answers a working SDET would recognise — browser/context/page architecture, locator strategy, auto-waiting and web-first assertions, fixtures and projects, storageState, network routing, APIRequestContext, workers and retries, traces, and framework design.

  • 22 min read
  • Difficulty: Mixed (Easy → Hard)
  • Freshers → 10+ yrs
  • Updated June 2026
  • Avinash Kamble
Avinash Kamble
Founder & QA Engineer at SoftwareTestPilot
Published:
Benchmarks

Playwright market & performance benchmarks (2026)

Numbers you can quote in interviews, blog posts, and RFPs. Sources: QA Jobs Radar live feed (Jan–Jun 2026), Playwright 1.48 test-runner telemetry against a 500-test suite on GitHub Actions (ubuntu-latest, 4 vCPU), and a Selenium 4 baseline on the same suite.

MetricPlaywright 1.48Selenium 4.x baselineDelta
500-test suite runtime (serial)~11 min 20 s~26 min 05 s2.3× faster
500-test suite runtime (4 workers, parallel)~3 min 40 s~9 min 15 s2.5× faster
Median flake rate (auto-wait vs explicit wait)0.8%3.6%4.5× lower
Cold browser launch (Chromium)~420 ms~980 ms2.3× faster
Trace file size (1 failing test)~1.4 MBN/A (needs 3rd-party)Built-in
CI minutes saved / 100 runs~1,470 min~24 hrs / week
Market signal (Jun 2026)IndiaUnited StatesGlobal
Open SDET roles requiring Playwright1,5601,4103,940+
Median SDET salary (Playwright primary, 3–5 YOE)₹18.2 LPA$132,000
Median SDET salary (Playwright primary, 5–8 YOE)₹28.6 LPA$156,500
% postings asking for TypeScript + Playwright64%77%71%
% postings asking for Playwright + API testing in same suite41%52%47%
% postings requiring CI (GitHub Actions / GitLab) fluency82%88%85%

Swipe horizontally to see all columns →

Read

Playwright is measurably ~2.3–2.5× faster and ~4.5× less flaky than a matched Selenium 4 suite on identical hardware — and 2026 Playwright postings pay a ~15–20% premium over Selenium-primary SDET roles at the same YOE. If you’re prepping past 3 YOE, own TypeScript + API testing + CI, not just locators.

0 / 51 reviewed
0%

1. Architecture: browser, context and page

Hard Very Common 1 minQ1 / 51

Q1.Explain the relationship between Browser, BrowserContext and Page in Playwright.

Why interviewers ask this

Almost every isolation, speed and auth question later in the interview depends on whether you understand this three-level model.

Detailed explanation

A Browser is one launched browser process. A BrowserContext is an isolated profile inside that process — its own cookie jar, local/session storage, permissions, geolocation and cache. A Page is a single tab inside a context.

The practical consequence: creating a context is cheap (a few milliseconds) while launching a browser is expensive (hundreds of milliseconds). That is why Playwright Test gives every test a fresh context but reuses the browser process across tests in the same worker — you get incognito-grade isolation without paying for a new browser each time.

const browser = await chromium.launch();
const context = await browser.newContext({ locale: 'en-GB' });
const page = await context.newPage();
Common mistakes
  • Saying a context is 'just a tab' — a context can hold many pages.
  • Assuming logging in on one page logs you in across contexts; storage is per-context.
RelatedQ3
Medium Very Common 1 minQ2 / 51

Q2.Why does Playwright Test create a new BrowserContext per test rather than reusing one?

Why interviewers ask this

Tests whether you connect isolation to flakiness rather than treating it as a config detail.

Detailed explanation

Because shared state is the most common source of order-dependent failures. A fresh context guarantees no cookies, tokens, service workers, IndexedDB entries or permission grants leak from the previous test, so tests can run in any order and in parallel.

The cost is that each test starts logged out — which is why storageState exists, letting you seed an authenticated context without re-running the UI login.

Medium Very Common 1 minQ3 / 51

Q3.What does test isolation actually cover in Playwright, and what does it NOT cover?

Why interviewers ask this

Candidates frequently over-trust isolation and then cannot explain why parallel tests still collide.

Detailed explanation

Isolation covers browser-side state: cookies, storage, cache, permissions and the page itself.

It does not cover anything outside the browser — your database, a shared staging account, a queue, a rate-limited third-party sandbox, or files on disk. Two workers registering the same email will still collide. The fix is data-level isolation: unique fixture data per test (timestamped or UUID-suffixed identifiers), or per-worker accounts.

Medium Very Common 1 minQ4 / 51

Q4.When would you deliberately share a BrowserContext across several tests?

Why interviewers ask this

Checks judgement about breaking a default rather than blind adherence.

Detailed explanation

Rarely, and only when the tests form one linear journey that is genuinely expensive to re-create — for example a multi-step wizard behind a slow SSO flow that cannot be seeded via storageState.

You do it with a worker-scoped fixture plus test.describe.serial(), and you accept the trade-off: the tests can no longer run individually or in parallel, and one failure cascades into the rest. Most of the time, seeding state via API is the better answer.

Medium Very Common 1 minQ5 / 51

Q5.What is the difference between headless and headed mode, and does headless change test behaviour?

Why interviewers ask this

A classic 'it passes locally, fails in CI' opener.

Detailed explanation

Headed launches a visible browser window; headless runs the same engine without a window. Modern Chromium headless uses the same rendering path, so behaviour is largely identical — but not entirely.

Real differences that bite in CI: default viewport, device scale factor, available fonts (missing fonts shift layout and break visual or text-position assertions), timing (headless is faster, so races surface earlier), and video/audio codecs. When a test only fails headless, reproduce with --headed --slow-mo=300, then compare the trace screenshots rather than guessing.

Medium Very Common 1 minQ6 / 51

Q6.How do you handle a flow that opens a new tab or a popup window?

Why interviewers ask this

Popups expose whether you understand that pages arrive as events, not as return values.

Detailed explanation

You wait for the page event on the context before triggering the action, otherwise you race the browser.

const [popup] = await Promise.all([
  context.waitForEvent('page'),
  page.getByRole('link', { name: 'Open invoice' }).click(),
]);
await popup.waitForLoadState();
await expect(popup.getByRole('heading', { name: 'Invoice' })).toBeVisible();

Playwright also exposes page.waitForEvent('popup') for the common single-popup case. Both new pages live in the same context, so they share the login session.

Medium Very Common 1 minQ7 / 51

Q7.How do you interact with content inside an iframe?

Why interviewers ask this

Frame handling separates candidates who have automated real embedded widgets (payments, chat, maps) from those who have not.

Detailed explanation

Use page.frameLocator(), which is lazy and retries like any other locator:

const card = page.frameLocator('iframe[title="Secure card input"]');
await card.getByPlaceholder('Card number').fill('4242424242424242');

Key points: frame locators chain for nested iframes; a plain page.locator() will never pierce a frame boundary; and for cross-origin frames you still need frameLocator — Playwright drives them natively, unlike tools restricted by same-origin policy.

Confidence check

If you can confidently answer the Architecture: browser, context and page questions above, you're well prepared for this section of your interview. Move on, or rehearse the trickier ones aloud with our AI mock interviewer.

2. Locators, auto-waiting and web-first assertions

Medium Very Common 1 minQ8 / 51

Q8.Why is a Locator different from an ElementHandle, and which should you use?

Why interviewers ask this

The single most useful architectural distinction in the Playwright API.

Detailed explanation

An ElementHandle points at a specific DOM node captured at a moment in time. If React re-renders, that node is detached and every subsequent call throws.

A Locator is a lazy description of how to find an element. It resolves at the moment of each action and retries until the element is actionable. On a modern SPA this removes an entire class of "element is not attached to the DOM" failures.

Use locators everywhere. Reach for a handle only in rare interop cases such as passing a node into page.evaluate().

Medium Very Common 1 minQ9 / 51

Q9.What is your locator priority order, and why does getByRole come first?

Why interviewers ask this

Maintainability question — panels want a defensible rule, not a preference.

Detailed explanation

Order: getByRolegetByLabel / getByPlaceholdergetByTextgetByTestId → CSS → XPath (effectively never).

getByRole comes first because it queries the accessibility tree, which encodes what the element is and its accessible name. That survives CSS refactors and DOM restructuring, and a locator that fails because the role disappeared is usually reporting a genuine accessibility regression.

getByTestId is the honest fallback for elements with no meaningful role or stable text — icon-only buttons, chart canvases, virtualised rows.

Common mistakes
  • Defending XPath as 'more powerful'; power is not the constraint, churn is.
Medium Very Common 1 minQ10 / 51

Q10.A locator matches three elements and your click fails with strict mode violation. How do you fix it properly?

Why interviewers ask this

Strict mode is Playwright-specific and the naive fix (.first()) hides real bugs.

Detailed explanation

Strict mode is a feature: it refuses to guess which of three matches you meant. The correct fix is to narrow the query so it describes one element.

// Weak: silently picks whichever renders first
page.getByRole('button', { name: 'Delete' }).first().click();

// Better: scope to the row you mean
page.getByRole('row', { name: 'invoice-2201' })
    .getByRole('button', { name: 'Delete' })
    .click();

Use .filter({ hasText }), .filter({ has: locator }) or a parent scope. Reserve .first() for genuinely homogeneous lists where any item is valid.

Medium Very Common 1 minQ11 / 51

Q11.Explain auto-waiting. What checks does Playwright run before a click?

Why interviewers ask this

The answer distinguishes people who removed their sleeps from people who never needed them.

Detailed explanation

Before an action Playwright polls actionability checks until they all pass or the timeout expires. For click() the element must be attached to the DOM, visible, stable (not animating between two consecutive frames), able to receive pointer events (nothing overlapping it), and enabled.

It then re-checks after scrolling into view, and retries if the element moved. This is why an explicit sleep is almost always wrong: it either wastes time or is too short under CI load.

Medium Very Common 1 minQ12 / 51

Q12.What does auto-waiting NOT solve?

Why interviewers ask this

Balance question; a one-sided 'Playwright never flakes' answer is a red flag.

Detailed explanation

Auto-waiting waits for the element, not for your application's semantics. It cannot know that:

  • a table is showing stale rows while a background refetch is in flight,
  • a button is enabled but its click handler is not yet bound,
  • an async job (email, invoice PDF, search index) has not finished server-side,
  • a toast appeared and vanished before your assertion ran.

Those need explicit signals: page.waitForResponse(), waiting for a spinner to detach, or polling an API with expect.poll().

Medium Very Common 1 minQ13 / 51

Q13.What makes expect() assertions in Playwright 'web-first', and why does it matter?

Why interviewers ask this

Common misuse: awaiting textContent() then comparing, which reintroduces races.

Detailed explanation

Web-first assertions retry the whole assertion until it passes or times out.

// Retries for up to the expect timeout
await expect(page.getByRole('status')).toHaveText('Saved');

// One-shot snapshot — flaky the moment the UI is async
expect(await page.getByRole('status').textContent()).toBe('Saved');

The first form removes the need for a wait before the check. The second reads the DOM once and fails if you were a millisecond early.

Medium Very Common 1 minQ14 / 51

Q14.How do you assert on a list whose order or length is dynamic?

Why interviewers ask this

Tests whether the candidate knows locator-level assertions rather than looping.

Detailed explanation

Assert on the locator collection, not on a JavaScript array you built yourself:

const rows = page.getByRole('row').filter({ hasText: 'Pending' });
await expect(rows).toHaveCount(3);
await expect(rows.first()).toContainText('INV-1001');
// Order-sensitive check across all matches:
await expect(page.getByTestId('order-id')).toHaveText(['A-1', 'A-2', 'A-3']);

toHaveCount and array-form toHaveText retry, so they tolerate the list still rendering. A manual const items = await locator.all() loop does not.

Medium Very Common 1 minQ15 / 51

Q15.When is expect.poll() or expect.toPass() the right tool?

Why interviewers ask this

Separates candidates who reach for sleep from those who model eventual consistency.

Detailed explanation

Use them when the thing you are waiting on is not a DOM property — typically backend eventual consistency.

await expect.poll(async () => {
  const res = await request.get('/api/orders/1001');
  return (await res.json()).status;
}, { timeout: 30_000, intervals: [1000, 2000, 5000] }).toBe('SHIPPED');

expect.toPass() wraps a whole block of assertions and retries it, which is useful when a UI settles in multiple steps. Both are far better than a fixed wait because they exit as soon as the condition is true.

Medium Very Common 1 minQ16 / 51

Q16.How do you handle a genuinely unavoidable timing dependency, such as a debounced search field?

Why interviewers ask this

Realistic scenario where naive automation types too fast and asserts on stale results.

Detailed explanation

Do not sleep for the debounce; wait for the effect of the debounce. Either wait for the search request the debounce triggers, or assert on a response-derived signal:

const results = page.waitForResponse(r => r.url().includes('/api/search') && r.ok());
await page.getByRole('searchbox').fill('playwright');
await results;
await expect(page.getByRole('listitem')).not.toHaveCount(0);

If the field fires a request per keystroke, pressSequentially() with a small delay models a human better than fill(), which sets the value in one shot.

Confidence check

If you can confidently answer the Locators, auto-waiting and web-first assertions questions above, you're well prepared for this section of your interview. Move on, or rehearse the trickier ones aloud with our AI mock interviewer.

3. Fixtures, hooks, projects and configuration

Hard Very Common 1 minQ17 / 51

Q17.How do you test file upload and file download flows?

Why interviewers ask this

Both are commonly asked because they use Playwright-specific APIs people forget.

Detailed explanation

Upload — set files directly on the input, even if it is visually hidden behind a styled button:

await page.setInputFiles('input[type=file]', 'fixtures/invoice.pdf');
// Button-triggered picker:
const chooser = page.waitForEvent('filechooser');
await page.getByRole('button', { name: 'Attach' }).click();
await (await chooser).setFiles('fixtures/invoice.pdf');

Download — wait for the download event and read the stream rather than poking at the filesystem:

const download = page.waitForEvent('download');
await page.getByRole('button', { name: 'Export CSV' }).click();
const file = await (await download).path();

Assert on the file's contents where it matters — a download that produces an empty CSV still fires the event.

Medium Very Common 1 minQ18 / 51

Q18.What problem do Playwright fixtures solve that beforeEach does not?

Why interviewers ask this

Fixture literacy is the main dividing line between script writers and framework builders.

Detailed explanation

Three things. Fixtures are composable (one fixture can depend on another), lazily instantiated (a fixture a test never mentions is never built, so a test that needs no login pays nothing), and scoped (test or worker), with teardown attached to the same definition.

beforeEach runs for every test in the file regardless of need, shares state through outer-scope variables, and separates setup from its cleanup.

Medium Very Common 1 minQ19 / 51

Q19.Write a typed custom fixture that provides a logged-in page.

Why interviewers ask this

Code question that quickly reveals whether the candidate has actually extended the base test.

Detailed explanation
import { test as base, expect, Page } from '@playwright/test';

type Fixtures = { adminPage: Page };

export const test = base.extend<Fixtures>({
  adminPage: async ({ browser }, use) => {
    const context = await browser.newContext({ storageState: '.auth/admin.json' });
    const page = await context.newPage();
    await use(page);          // test body runs here
    await context.close();    // teardown
  },
});
export { expect };

Everything before use() is setup, everything after is teardown, and it runs even when the test fails.

Medium Very Common 1 minQ20 / 51

Q20.What is the difference between test-scoped and worker-scoped fixtures?

Why interviewers ask this

Directly affects suite runtime and is a frequent follow-up to the fixtures question.

Detailed explanation

A test-scoped fixture is created and torn down around every test — the safe default. A worker-scoped fixture ({ scope: 'worker' }) is created once per worker process and reused by all tests that worker runs.

Worker scope is for expensive, read-only or per-worker-partitioned resources: a database connection pool, a seeded tenant, an account whose index is testInfo.workerIndex. Never put mutable shared state in worker scope, or you rebuild the flakiness that isolation was meant to remove.

Medium Common 1 minQ21 / 51

Q21.What are projects in playwright.config.ts and what do teams actually use them for?

Why interviewers ask this

Projects are frequently reduced to 'browsers', missing their real power.

Detailed explanation

A project is a named run configuration — its own browser, viewport, base URL, test directory and dependencies. Common uses beyond cross-browser:

  • Setup dependency: a setup project that authenticates once and writes storageState, declared via dependencies: ['setup'].
  • Suite tiers: a fast smoke project on PRs, full regression nightly.
  • Environments: same tests against staging and pre-prod with different baseURL.
  • Device emulation: ...devices['iPhone 14'].
Medium Common 1 minQ22 / 51

Q22.How do the timeout layers in Playwright interact?

Why interviewers ask this

Timeout confusion is the most common cause of misdiagnosed 'Playwright is slow' complaints.

Detailed explanation

There are four, and they are independent:

  • timeout — whole test, default 30 s.
  • expect.timeout — a single web-first assertion, default 5 s.
  • actionTimeout / navigationTimeout — one action or navigation, unset by default (falls back to the test timeout).
  • globalTimeout — the entire run.

Raising the test timeout does not help an assertion that failed at 5 s. Conversely a 5-minute test timeout hides a hung action instead of fixing it. Tune the narrowest one that is actually being hit, and read the error message — it names the timeout that fired.

Medium Common 1 minQ23 / 51

Q23.How should baseURL and environment configuration be handled?

Why interviewers ask this

Framework-design signal; hardcoded URLs are the classic maintenance smell.

Detailed explanation

Set use.baseURL in the config from an environment variable with a sane default, then navigate with relative paths (page.goto('/checkout')). Keep secrets out of the repo and inject them from CI variables.

use: { baseURL: process.env.BASE_URL ?? 'http://localhost:3000' }

Combine with per-project overrides so a single command can target staging or a preview deployment without editing test code.

Medium Common 1 minQ24 / 51

Q24.What does globalSetup do, and when is a setup project the better choice?

Why interviewers ask this

Modern Playwright favours setup projects; knowing why shows the candidate keeps current.

Detailed explanation

globalSetup is a plain Node function that runs once before everything. It has no fixtures, no tracing, no retries and no reporting — failures there produce poor diagnostics.

A setup project is a real test file matched by a project with dependencies. It gets fixtures, traces, retries and shows up in the report. Use it for authentication and seeding; keep globalSetup for infrastructure-level work such as starting a container or writing a config file.

Medium Common 1 minQ25 / 51

Q25.How do you skip or conditionally run tests without littering the code with if-statements?

Why interviewers ask this

Checks knowledge of the annotation API and of honest reporting.

Detailed explanation

Use annotations, which record the reason in the report:

test.skip(({ browserName }) => browserName === 'webkit', 'Upload dialog unsupported in WebKit');
test.fixme('known bug PROJ-812');
test.fail();     // expected to fail; fails the run if it passes
test.slow();     // triples the timeout

fixme and fail are better than deleting or commenting out a test — the suite keeps tracking the known defect.

Confidence check

If you can confidently answer the Fixtures, hooks, projects and configuration questions above, you're well prepared for this section of your interview. Move on, or rehearse the trickier ones aloud with our AI mock interviewer.

4. Network interception, API requests and auth state

Medium Common 1 minQ26 / 51

Q26.Where should test data live in a Playwright framework?

Why interviewers ask this

Data strategy is the difference between a suite that scales and one that fights itself.

Detailed explanation

Three tiers, in order of preference:

  1. Created per test via API inside a fixture, with teardown that deletes it. Fastest, fully isolated, no cross-test coupling.
  2. Generated inline for values that only need to be unique (user-${Date.now()}-${workerIndex}@example.test).
  3. Static fixture files for genuinely constant reference data — country lists, sample PDFs.

What to avoid: a shared "test user" spreadsheet, and tests that depend on rows an earlier test created.

Medium Common 1 minQ27 / 51

Q27.Explain page.route() and the difference between fulfill, continue and abort.

Why interviewers ask this

Network control is Playwright's strongest feature and the most commonly probed advanced topic.

Detailed explanation

page.route(pattern, handler) registers an interceptor. Inside the handler you choose one of:

  • route.fulfill() — reply from the test; the request never leaves the browser.
  • route.continue() — let it through, optionally with modified URL, method, headers or post data.
  • route.abort() — fail it, optionally with a specific error code.
await page.route('**/api/pricing', route =>
  route.fulfill({ status: 200, json: { plan: 'enterprise', seats: 500 } }));

Handlers are matched most-recently-registered first, and a route must be resolved exactly once or the request hangs until timeout.

Medium Common 1 minQ28 / 51

Q28.When should you mock a network response and when should you hit the real service?

Why interviewers ask this

Over-mocking produces green suites that miss real integration breakage.

Detailed explanation

Mock when you need determinism the backend cannot give you: error branches (500, 429, malformed payload), slow responses, empty and maximum-size states, third-party services you do not control, and paid APIs.

Hit the real service for your critical happy paths — checkout, login, the main create/read flow. Otherwise a backend contract change ships silently because every test is asserting against your own fixtures. A common split: one real end-to-end journey per critical flow, mocks for the surrounding edge cases.

Hard Common 1 minQ29 / 51

Q29.How do you deterministically test a loading state and a failure state?

Why interviewers ask this

Practical interception question with an obvious wrong answer (throttling the whole browser).

Detailed explanation
// Loading: delay the response, assert the skeleton, then release
await page.route('**/api/orders', async route => {
  await new Promise(r => setTimeout(r, 2000));
  await route.continue();
});
await page.goto('/orders');
await expect(page.getByTestId('orders-skeleton')).toBeVisible();

// Failure: force the error branch
await page.route('**/api/orders', route => route.fulfill({ status: 500, body: '{}' }));
await expect(page.getByRole('alert')).toContainText('Could not load orders');

This is far more reliable than trying to catch a real slow response, and it lets you assert on retry and error-recovery UI that would otherwise be untestable.

Hard Common 1 minQ30 / 51

Q30.What is APIRequestContext and how does it differ from calling fetch in the test?

Why interviewers ask this

The answer reveals whether the candidate uses API calls to speed up UI tests.

Detailed explanation

APIRequestContext is Playwright's HTTP client. Unlike a bare fetch, it participates in the framework: it honours baseURL, can share cookies and storageState with a browser context, appears in traces, and supports the same expect(response) matchers.

test('order appears after API creation', async ({ request, page }) => {
  const res = await request.post('/api/orders', { data: { sku: 'A-1', qty: 2 } });
  await expect(res).toBeOK();
  const { id } = await res.json();
  await page.goto(`/orders/${id}`);
  await expect(page.getByRole('heading')).toContainText('A-1');
});

Sharing cookies matters: request from the page's context is already authenticated, so you can set up state as the same user the UI is logged in as.

Medium Common 1 minQ31 / 51

Q31.Should you use Playwright as your main API testing tool?

Why interviewers ask this

Judgement question; the honest answer is 'for some things'.

Detailed explanation

It is excellent for API calls that support UI tests — seeding data, cleaning up, asserting a side effect the UI does not display. It is also fine for a small suite of endpoint smoke tests, since you get one toolchain and one report.

It is a weaker fit as a dedicated contract-testing or large API-regression platform: no built-in schema registry, no consumer-driven contract workflow, and a browser-oriented runner. Teams typically pair it with a schema validator or a contract tool rather than replacing them.

Hard Common 1 minQ32 / 51

Q32.How does storageState work and how do you set up authentication once for a whole suite?

Why interviewers ask this

The single biggest runtime win in most Playwright suites.

Detailed explanation

storageState serialises cookies and origin local storage to JSON. You log in once in a setup project, save the state, and every other test starts a context pre-loaded with it — no UI login per test.

// auth.setup.ts
setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill(process.env.USER_EMAIL!);
  await page.getByLabel('Password').fill(process.env.USER_PASSWORD!);
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await page.context().storageState({ path: '.auth/user.json' });
});

// playwright.config.ts
projects: [
  { name: 'setup', testMatch: /auth\.setup\.ts/ },
  { name: 'chromium', dependencies: ['setup'],
    use: { ...devices['Desktop Chrome'], storageState: '.auth/user.json' } },
]
Common mistakes
  • Saving state before the post-login assertion, capturing a half-finished session.
  • Committing the auth JSON to git — it contains live session tokens.
Hard Common 1 minQ33 / 51

Q33.How do you run tests as several different roles in the same suite?

Why interviewers ask this

Realistic in any product with permissions; tests fixture design.

Detailed explanation

Generate one storage-state file per role in the setup project, then expose role-specific fixtures or projects.

export const test = base.extend<{ adminPage: Page; viewerPage: Page }>({
  adminPage: async ({ browser }, use) => {
    const ctx = await browser.newContext({ storageState: '.auth/admin.json' });
    await use(await ctx.newPage()); await ctx.close();
  },
  viewerPage: async ({ browser }, use) => {
    const ctx = await browser.newContext({ storageState: '.auth/viewer.json' });
    await use(await ctx.newPage()); await ctx.close();
  },
});

This also lets one test drive two roles at once — an admin approving in one context while a viewer watches the change appear in another.

Medium Common 1 minQ34 / 51

Q34.Your session tokens expire after 15 minutes and a long suite starts failing halfway. How do you handle it?

Why interviewers ask this

Debugging scenario with several valid answers; panels want reasoning, not a memorised fix.

Detailed explanation

Options, roughly in order of preference:

  • Re-run the auth setup per worker instead of once per run, so each worker's token is fresh (worker-scoped fixture writing to a per-worker state file).
  • Mint the token via API rather than the UI, so refreshing is cheap and can happen mid-suite.
  • Request a longer-lived token for the test environment only — a config change, not a test hack.

What not to do: add retries. Retrying an expired-session failure just burns CI minutes and produces a suite that is green only on the second attempt.

Confidence check

If you can confidently answer the Network interception, API requests and auth state questions above, you're well prepared for this section of your interview. Move on, or rehearse the trickier ones aloud with our AI mock interviewer.

5. Parallelism, retries, artifacts and CI

Medium Common 1 minQ35 / 51

Q35.How would you test an OAuth or third-party SSO login you do not control?

Why interviewers ask this

Extremely common in enterprise apps and a good test of pragmatism.

Detailed explanation

Test the integration once, then stop driving it. A workable strategy:

  1. One dedicated test that completes the real provider flow in a non-production tenant, tagged so it runs nightly rather than on every PR.
  2. For everything else, obtain a session by API (client-credentials or a test-only token endpoint) and inject it via storageState.
  3. Where the provider offers a mock/emulator mode, use it in CI.

Driving a real third-party login screen on every test is the main cause of slow, externally-flaky suites — and it tests the identity provider, not your product.

Medium Common 1 minQ36 / 51

Q36.How does parallelism work in Playwright, and what is the difference between workers and shards?

Why interviewers ask this

Directly relevant to CI cost; frequently confused.

Detailed explanation

A worker is a Node process on one machine. Playwright runs files in parallel across workers by default; tests inside one file run serially unless you opt in with test.describe.configure({ mode: 'parallel' }).

A shard splits the suite across machines: --shard=2/4 runs the second quarter. Each shard then uses its own workers. In CI you combine both — four containers, each with four workers — and merge the blob reports at the end.

Medium Common 1 minQ37 / 51

Q37.How many workers should you configure, and why is the CI value usually lower?

Why interviewers ask this

Tests whether the candidate has actually tuned a pipeline.

Detailed explanation

Locally, roughly the number of physical cores. In CI, hosted runners are typically 2–4 vCPU and shared, so the default drops to 1 in CI unless you set it. Over-provisioning workers on a small runner makes every test slower and starts producing timeout failures that look like application bugs.

The other constraint is your backend: 16 workers hammering a staging database with a connection limit of 20 will fail for reasons that have nothing to do with the browser. Scale with sharding across machines rather than piling workers onto one.

Medium Common 1 minQ38 / 51

Q38.Are retries a good idea? How do you use them without hiding bugs?

Why interviewers ask this

Retries are the most-abused setting in test automation.

Detailed explanation

Retries are a reporting tool, not a fix. Configure retries: 1 or 2 in CI so a genuine infrastructure blip does not block a release, but treat every retried test as a defect: Playwright marks it flaky in the report precisely so you can track it.

Guardrails worth adopting: track the flaky count as a metric and fail the build if it exceeds a threshold; keep retries at 0 locally so developers feel their own flakiness; and quarantine repeat offenders with test.fixme plus a ticket rather than leaving them silently retrying forever.

Medium Occasional 1 minQ39 / 51

Q39.What is in a Playwright trace and how do you use it to debug a CI-only failure?

Why interviewers ask this

Tracing is the answer to 'it only fails in CI', and many candidates have never opened one.

Detailed explanation

A trace bundles a screenshot filmstrip, DOM snapshots you can inspect at every step, the action log with timings, network requests, console output and any test source. Open it with npx playwright show-trace trace.zip or on trace.playwright.dev.

Configure trace: 'on-first-retry' — you get traces exactly for the runs that failed, without the storage cost of tracing everything. Workflow: download the artifact, scrub to the failing action, hover the DOM snapshot to see what the locator actually matched at that instant, then check the network panel for the request that never came back.

Medium Occasional 1 minQ40 / 51

Q40.Which artifacts should CI collect, and at what settings?

Why interviewers ask this

Practical pipeline question with a cost dimension.

Detailed explanation
  • trace: 'on-first-retry' — the highest-value artifact.
  • screenshot: 'only-on-failure' — cheap, instantly readable in the HTML report.
  • video: 'retain-on-failure' — useful for animation or focus bugs; expensive, so failure-only.
  • HTML reporter for humans plus a machine reporter (junit or blob for shard merging) for the CI UI.

Set a retention policy — trace and video artifacts from a large nightly suite grow quickly.

Hard Occasional 1 minQ41 / 51

Q41.Walk through a sensible GitHub Actions setup for a Playwright suite.

Why interviewers ask this

CI fluency is expected at mid level and above.

Detailed explanation
- uses: actions/setup-node@v4
  with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test --shard=${{ matrix.shard }}/4
- uses: actions/upload-artifact@v4
  if: always()
  with: { name: report-${{ matrix.shard }}, path: blob-report/ }

Points worth calling out: --with-deps installs the OS libraries the browsers need; if: always() so artifacts upload on failure; a matrix for sharding; and a final job that merges blob reports into one HTML report. Using the official Playwright Docker image instead pins browser and OS versions so local and CI rendering match.

Confidence check

If you can confidently answer the Parallelism, retries, artifacts and CI questions above, you're well prepared for this section of your interview. Move on, or rehearse the trickier ones aloud with our AI mock interviewer.

6. Debugging scenarios and framework design

Hard Occasional 1 minQ42 / 51

Q42.A test passes locally but fails only in CI. How do you triage it systematically?

Why interviewers ask this

The most common real-world Playwright problem; panels want a method, not a guess.

Detailed explanation

Work from evidence, in this order:

  1. Open the trace for the failing run and identify the exact failing action and what the DOM looked like.
  2. Classify: timing (element never became actionable), data (different seed data in CI), environment (viewport, fonts, timezone, locale), or auth (expired or missing state).
  3. Reproduce the CI conditions locally — same container image, same viewport, --workers=1 vs the CI worker count, and TZ/locale env vars.
  4. Run the single test repeatedly (--repeat-each=20) to confirm whether it is genuinely flaky or deterministically CI-only.

Only after the class is known do you change code. Bumping the timeout first is how flaky suites become slow flaky suites.

Medium Occasional 1 minQ43 / 51

Q43.Which debugging tools does Playwright provide and when do you use each?

Why interviewers ask this

Tooling breadth question; the good answer maps each tool to a situation.

Detailed explanation
  • UI Mode (--ui) — day-to-day development: watch mode, time-travel, pick locator.
  • Inspector (--debug or PWDEBUG=1) — step through a specific failing test and try locators live.
  • Codegen (npx playwright codegen) — discovering the right role/label for an unfamiliar UI, not for generating production tests.
  • Trace viewer — post-mortem on a run you cannot reproduce, especially CI.
  • page.pause() — freeze mid-test at exactly the point you care about.
Hard Occasional 1 minQ44 / 51

Q44.How would you diagnose a test that fails roughly one run in twenty?

Why interviewers ask this

Flaky-test diagnosis is a senior-level differentiator.

Detailed explanation

Make it fail on demand first: npx playwright test flaky.spec.ts --repeat-each=50 --workers=4. Running with several workers matters, because contention is often the trigger.

Then look for the usual causes in order of frequency: a race between an assertion and an in-flight request; shared test data two workers touch; an animation or toast that the assertion catches mid-transition; a locator that matches a transient placeholder element; and time/timezone dependence.

Fix by adding a deterministic signal (wait for the response, or assert on a stable end state), not by increasing the timeout — a longer timeout changes the failure rate without changing the race.

Hard Occasional 1 minQ45 / 51

Q45.Is the Page Object Model still worth using with Playwright?

Why interviewers ask this

Opinionated question; panels want a reasoned position, not dogma.

Detailed explanation

Yes, but lighter than the classic Selenium version. Because locators are lazy and auto-waiting, page objects no longer need wait helpers, driver plumbing or element-getter boilerplate. What they should still hold is naming and flows: meaningful locator names, and methods for multi-step journeys such as completeCheckout(card).

Anti-patterns to avoid: page objects that wrap every single click in a one-line method, page objects containing assertions for every scenario, and inheritance hierarchies three levels deep. Many teams get further with small component objects (a header, a data grid) than with one object per URL.

Hard Occasional 1 minQ46 / 51

Q46.How would you structure a Playwright framework for a team of ten engineers?

Why interviewers ask this

Framework-design question that separates senior candidates.

Detailed explanation

A structure that holds up in practice:

  • tests/ split by domain, not by page, with tags (@smoke, @regression) driving what runs where.
  • fixtures/ — the extended test object: auth, API client, seeded data, per-worker resources.
  • pages/ or components/ — locators and flows only, no assertions on business rules.
  • api/ — typed helpers for setup and teardown against the backend.
  • playwright.config.ts — projects for setup, smoke, regression and browsers; everything environment-specific from env vars.

Conventions matter as much as folders: one assertion style, no waitForTimeout allowed in review, and every new test must pass --repeat-each=5 before merge.

Medium Occasional 1 minQ47 / 51

Q47.How do you decide what belongs in a Playwright end-to-end test at all?

Why interviewers ask this

Checks that the candidate does not push everything to the slowest layer.

Detailed explanation

End-to-end tests should cover journeys that only break when real components are wired together: authentication, checkout, permissions, anything crossing a service boundary in a way users notice.

Field validation, formatting rules, error copy and calculation logic belong in unit or component tests, where they run in milliseconds and point straight at the broken function. A useful filter: if the test would still be meaningful with the backend stubbed and the browser removed, it probably should not be an E2E test.

Medium Occasional 1 minQ48 / 51

Q48.How do you keep a large Playwright suite fast as it grows past a thousand tests?

Why interviewers ask this

Scalability question with several legitimate levers.

Detailed explanation
  • Seed state by API instead of clicking through prerequisite screens — usually the single biggest win.
  • Authenticate once via storageState.
  • Shard across CI machines and merge blob reports.
  • Split tiers: tagged smoke on every PR, full regression nightly.
  • Mock third-party calls that are slow and not under test.
  • Delete tests. A suite that duplicates coverage costs runtime forever; a coverage review each quarter is legitimate maintenance.
Medium Occasional 1 minQ49 / 51

Q49.How do you handle testing across timezones, locales and currencies?

Why interviewers ask this

A frequent source of 'passes in the morning, fails at night' bugs.

Detailed explanation

Pin them rather than tolerate them. Set timezoneId and locale in the context or project config so the browser is deterministic:

use: { locale: 'en-IN', timezoneId: 'Asia/Kolkata' }

For date-sensitive logic, either freeze the clock with page.clock or generate expected values with the same timezone rules the app uses rather than hardcoding a formatted string. Run one project per locale you genuinely support instead of writing locale branches inside tests.

Medium Occasional 1 minQ50 / 51

Q50.What is the right way to run visual comparisons, and what are the traps?

Why interviewers ask this

Visual testing is commonly requested and commonly abandoned after it turns flaky.

Detailed explanation

await expect(page).toHaveScreenshot() stores a baseline per project and platform. It is usable, but only with discipline:

  • Baselines are OS- and browser-specific — generate them in the same container CI uses, or every developer's machine produces a diff.
  • Mask dynamic regions (mask: [page.getByTestId('timestamp')]) and disable animations.
  • Set a small maxDiffPixelRatio rather than demanding pixel equality.
  • Snapshot components, not whole pages — a full-page baseline changes on every unrelated tweak and gets rubber-stamped.
Confidence check

If you can confidently answer the Debugging scenarios and framework design questions above, you're well prepared for this section of your interview. Move on, or rehearse the trickier ones aloud with our AI mock interviewer.

Quick revision

  1. Q1: Explain the relationship between Browser, BrowserContext and Page in Playwright. — A Browser is one launched browser process.
  2. Q2: Why does Playwright Test create a new BrowserContext per test rather than reusing one — Because shared state is the most common source of order-dependent failures.
  3. Q3: What does test isolation actually cover in Playwright, and what does it NOT cover — Isolation covers browser-side state: cookies, storage, cache, permissions and the page itself.
  4. Q4: When would you deliberately share a BrowserContext across several tests — Rarely, and only when the tests form one linear journey that is genuinely expensive to re-create — for example a multi-step wizard behind a slow SSO flow that cannot be seeded via
  5. Q5: What is the difference between headless and headed mode, and does headless change test behaviour — Headed launches a visible browser window; headless runs the same engine without a window.

Frequently asked questions

1.How many Playwright questions should I actually prepare for an interview?
<p>Depth beats breadth. A panel will typically ask 8–12 questions and follow up hard on two or three. Being able to explain auto-waiting, locator choice, fixtures, storageState and how you debug a CI-only failure will carry you further than memorising fifty definitions.</p>
2.Do interviewers expect me to write Playwright code on the spot?
<p>For mid and senior automation roles, usually yes — a fixture, a page object method, or an interception snippet. Exact API spelling is rarely the point; interviewers watch whether you reach for locators over handles, avoid fixed waits, and clean up what you create.</p>
3.Is TypeScript required for Playwright interviews?
<p>Not required, but it is the default in most Playwright job descriptions because the runner's fixture typing works best there. If you interview in Python, Java or .NET, be ready to explain which runner features (fixtures, projects, trace viewer) come from Playwright Test specifically and are not identical in the other bindings.</p>
4.Should I mention Selenium experience in a Playwright interview?
<p>Yes, when it is relevant — for example explaining why you no longer need explicit waits, or how you migrated a suite. Avoid answering Playwright questions with Selenium mechanics; a candidate who describes ChromeDriver setup or implicit waits signals they have not actually used Playwright.</p>
5.What single topic do candidates most often get wrong?
<p>Waiting. Many candidates still describe adding sleeps or increasing timeouts as a fix for flakiness. The expected answer is to wait for a deterministic signal — a response, a state change, a retrying assertion — and to use the trace to find out what the test was actually waiting for.</p>

Playwright automation jobs hiring now

Live, indexable Playwright openings — updated daily in Jobs Radar.

Browse all QA jobs on Jobs Radar

Loading current openings…

Home