#1 India's Top IT Training Institute

×

About Uncodemy

Know more about who we are and what we stand for.

About Us

About Us

Discover our mission to equip individuals with essential IT skills and industry expertise.

Read More →
UnCodeMy Gallery

UnCodeMy Gallery

Explore highlights from our IT training sessions and success stories.

Read More →
×

Free Resources

Learn, practice and plan your career — bilkul free.

Tutorials

Tutorials

Step-by-step written tutorials to learn any technology at your own pace.

Read More →
Free Counselling

Free Counselling

Book a free video counselling session and pick the right course for you.

Read More →
Online Compiler

Online Compiler

Write, run and test your code online — no setup required.

Read More →
Playwright Interview Prep · 2026-27 Edition

Playwright Interview
Questions and answers with examples.

49+ Playwright interview questions and answers covering locators, fixtures, auto-waiting, TypeScript, API testing, cross-browser execution and a 10-snippet coding round. From beginner concepts for freshers to scenario-based problems for experienced professionals — every code example uses @playwright/test and is complete.

0
Questions
0
Coding snippets
0
Difficulty levels
0
Day prep plan
Scroll
Written by Mr. Rishi Raj · Technically reviewed by Mr. Pradyumn Singh (Head of Training & Placement) · Last updated 14 October 2026
01
Before you begin

What interviewers actually check in a Playwright interview

Playwright interviews are not a memory test on API names. Interviewers are checking whether you understand the architecture, can write stable locators, and can build a framework that runs reliably in CI. The six areas below come up again and again.

01

Playwright fundamentals

Browser, context and page model, how Playwright communicates with browsers, supported languages and browsers, and how it differs from Selenium.

02

Locators and auto-waiting

getByRole, getByLabel, getByTestId, chaining, filtering, and why Playwright's actionability checks remove most flakiness.

03

Test structure and fixtures

test.describe, hooks, custom fixtures, Page Object pattern, test isolation and the storageState pattern for login reuse.

04

Network and API testing

page.route for mocking, request context for API calls, hybrid UI plus API tests, and how to assert on network payloads.

05

Parallelism, projects and CI

Workers, sharding, retries, projects for cross-browser, GitHub Actions or Jenkins pipelines, HTML and Allure reporters.

06

Debugging tooling

Trace viewer, UI mode, codegen, screenshots and videos on failure, and visual comparisons with toHaveScreenshot.

How to use this page: pick the level that matches your experience first, work through those questions, then attempt the coding round. Open each question, try to answer it yourself, and only then read the model answer. For every code snippet, type it out and run it — reading TypeScript is not the same as writing it.
Download this page as PDF
02
Beginner · 12 questions

Playwright interview questions for freshers

These are the questions that come up first in Playwright fresher interviews. They test whether your fundamentals are solid — answer with a short definition, then one line of why it matters.

Q01 What is Playwright, and what browsers and languages does it support? BeginnerFundamentals +

Playwright is an open-source browser automation library from Microsoft. It drives Chromium, Firefox and WebKit (Safari's engine) with a single API, and supports TypeScript, JavaScript, Python, Java and C#.

It was built for testing modern web apps — it auto-waits for elements, runs tests in parallel by default, and includes tooling like codegen, trace viewer and UI mode out of the box.

Q02 How is Playwright different from Selenium? BeginnerFundamentals +
Playwright vs Selenium
FeaturePlaywrightSelenium
CommunicationBi-directional WebSocket-style protocolW3C WebDriver over HTTP
Auto-waitingBuilt-in actionability checks before every actionManual waits or explicit wait wrappers
BrowsersChromium, Firefox, WebKitChrome, Firefox, Edge, Safari, IE (via drivers)
LanguagesTypeScript, JavaScript, Python, Java, C#Java, Python, C#, Ruby, JavaScript, Kotlin
ParallelismWorkers built inNeeds TestNG, Grid or similar
ToolingCodegen, trace viewer, UI mode bundledThird-party or IDE plugins

Selenium has broader language and browser support and a larger existing ecosystem. Playwright is faster to set up, less flaky by default, and has stronger debugging tools.

Q03 What is the browser, context and page model in Playwright? BeginnerArchitecture +

Playwright has three layers:

  • Browser — a launched browser instance (Chromium, Firefox, WebKit).
  • Context — an isolated browser session, like a fresh incognito window. Each context has its own cookies, storage and cache.
  • Page — a single tab inside a context.

Test isolation works because each test gets its own context by default. Two tests never share cookies or local storage, so there is no cross-test pollution.

Q04 What is a locator in Playwright, and why is getByRole preferred? BeginnerLocators +

A locator is a lazy reference to an element on the page. It is not resolved until you perform an action or assertion, which is why Playwright can auto-wait and retry.

getByRole is preferred because it targets elements by their accessible role and name — the same way a screen reader sees them. This makes tests resilient to CSS class changes and improves accessibility coverage.

TypeScript
// preferred
page.getByRole('button', { name: 'Sign in' });

// fallback for elements with no accessible role
page.getByTestId('login-submit');

// last resort — brittle
page.locator('.btn.btn-primary.mt-4');
Q05 What is auto-waiting in Playwright? BeginnerAuto-waiting +

Auto-waiting means Playwright waits for an element to be actionable before performing an action. For a click, it waits until the element is:

  • attached to the DOM
  • visible
  • stable (not animating)
  • receiving events (not covered by another element)
  • enabled

This removes the need for Thread.sleep() or WebDriverWait. Assertions like expect(locator).toBeVisible() also retry until they pass or the timeout expires.

Q06 What is the difference between locator and element handle? BeginnerLocators +

A locator is lazy — it describes how to find the element and is resolved every time you use it. If the DOM changes, the locator re-resolves.

An element handle (page.$()) is eager — it points to a specific DOM node at a moment in time. If the page re-renders, the handle may become stale.

Playwright recommends locators for almost everything. Element handles are legacy and should only be used for advanced cases.

Q07 What are the main locator strategies in Playwright? BeginnerLocators +
Locator strategies, from most to least preferred
LocatorFindsExample
getByRoleAccessible role + namegetByRole('button', { name: 'Submit' })
getByLabelForm input by label textgetByLabel('Email')
getByPlaceholderInput by placeholdergetByPlaceholder('Search')
getByTextVisible textgetByText('Welcome')
getByTestIddata-testid attributegetByTestId('submit-btn')
getByAltTextImage alt textgetByAltText('Logo')
getByTitleTitle attributegetByTitle('Close')
locator (CSS/XPath)CSS or XPath selectorlocator('.card > h2')
Q08 What is the difference between toBeVisible and toBeAttached? BeginnerAssertions +

toBeAttached passes if the element exists in the DOM, even if it is hidden with CSS or display: none.

toBeVisible passes only if the element is in the DOM, has a non-zero size and is not hidden by CSS. It is the assertion you use for user-facing visibility.

TypeScript
// passes even if hidden with display: none
await expect(page.getByTestId('tooltip')).toBeAttached();

// passes only when actually visible to the user
await expect(page.getByTestId('tooltip')).toBeVisible();
Q09 What is a fixture in Playwright? BeginnerFixtures +

A fixture is a reusable setup step that Playwright provides to your test. Built-in fixtures include page, context, browser and request.

You can define your own fixture to encapsulate setup and teardown — for example, a logged-in page, a database connection, or a pre-seeded user. Fixtures are lazy: they only run if the test asks for them.

TypeScript — custom fixture
import { test as base } from '@playwright/test';

export const test = base.extend<{ todoPage: TodoPage }>({
  todoPage: async ({ page }, use) => {
    const todoPage = new TodoPage(page);
    await todoPage.goto();
    await use(todoPage);   // test runs here
  },
});
Q10 What is test isolation in Playwright, and why does it matter? BeginnerTest Structure +

Test isolation means each test runs in a fresh browser context with its own cookies, local storage and session storage. Nothing leaks from one test to the next.

This matters because isolated tests can run in parallel and in any order. You do not get the "passes alone, fails in the suite" problem that plagues shared-browser frameworks.

If a test needs a logged-in state, use storageState instead of logging in inside every test.

Q11 What is the difference between test.describe and test.step? BeginnerTest Structure +

test.describe groups related tests into a suite. You use it to organise tests and to apply shared configuration like test.use() or test.beforeEach to a group.

test.step breaks a single test into named steps. It improves the readability of the HTML report and the trace viewer, because each step appears separately with its own timing.

TypeScript
test.describe('Checkout', () => {
  test('user can place an order', async ({ page }) => {
    await test.step('Add item to cart', async () => {
      await page.getByRole('button', { name: 'Add to cart' }).click();
    });
    await test.step('Complete checkout', async () => {
      await page.getByRole('link', { name: 'Checkout' }).click();
    });
  });
});
Q12 What is codegen in Playwright? BeginnerTooling +

Codegen is Playwright's recorder. You run npx playwright codegen <url> and interact with the page; Playwright generates the corresponding test code in your chosen language.

It is best used to discover good locators quickly, not to produce final tests. Generated code still needs refactoring into Page Objects and meaningful assertions.

Terminal
npx playwright codegen https://example.com --target=typescript
03
Intermediate · 15 questions

Intermediate Playwright interview questions

These questions come up for candidates with 2–4 years of experience or for anyone who has built a small Playwright suite. They test whether you understand the why behind the framework, not just the API.

Q13 How does Playwright handle iframes? IntermediateLocators +

Playwright uses frameLocator to reach inside an iframe. It is cleaner than Selenium's switchTo().frame() because you do not have to switch back — the locator is scoped to the frame.

TypeScript
const frame = page.frameLocator('#payment-iframe');

await frame.getByLabel('Card number').fill('4242424242424242');
await frame.getByRole('button', { name: 'Pay' }).click();

// nested iframes
const nested = page.frameLocator('#outer').frameLocator('#inner');
Q14 How do you handle multiple tabs or windows in Playwright? IntermediateArchitecture +

Use context.waitForEvent('page') to catch a new tab that opens, usually triggered by a click.

TypeScript
const [newPage] = await Promise.all([
  context.waitForEvent('page'),
  page.getByRole('link', { name: 'Open in new tab' }).click(),
]);

await newPage.waitForLoadState();
await expect(newPage).toHaveTitle(/Dashboard/);

Because both pages share the same context, cookies and login state carry over automatically.

Q15 How do you handle file upload and download in Playwright? IntermediateActions +

For upload, use setInputFiles(). For download, use waitForEvent('download') and save the file.

TypeScript — upload
await page.getByLabel('Resume').setInputFiles('fixtures/resume.pdf');
await expect(page.getByText('Upload complete')).toBeVisible();
TypeScript — download
const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.getByRole('button', { name: 'Export CSV' }).click(),
]);

await download.saveAs('downloads/report.csv');
expect(download.suggestedFilename()).toBe('report.csv');
Q16 What is storageState, and how do you reuse a login session? IntermediateAuthentication +

storageState saves cookies and local storage to a JSON file. You log in once, save the state, and reuse it in every test — so you never log in through the UI again.

TypeScript — setup project
// tests/auth.setup.ts
import { test as setup } from '@playwright/test';

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('ravi@example.com');
  await page.getByLabel('Password').fill('secret');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('/dashboard');

  await page.context().storageState({ path: 'auth.json' });
});
TypeScript — playwright.config.ts
export default defineConfig({
  projects: [
    { name: 'setup', testMatch: /auth\.setup\.ts/ },
    {
      name: 'chromium',
      use: { storageState: 'auth.json' },
      dependencies: ['setup'],
    },
  ],
});
Q17 What is page.route, and how do you mock a network response? IntermediateNetwork +

page.route intercepts network requests matching a URL pattern. You can fulfil them with mock data, modify the response, or abort them to simulate failure.

TypeScript — mock an API response
await page.route('**/api/users', async route => {
  await route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify([{ id: 1, name: 'Mock User' }]),
  });
});

await page.goto('/users');
await expect(page.getByText('Mock User')).toBeVisible();

To simulate a server error, use route.fulfill({ status: 500 }). To simulate a network failure, use route.abort().

Q18 How does Playwright handle parallel execution? IntermediateParallelism +

Playwright runs test files in parallel by default, using a pool of workers. Each worker gets its own browser instance and its own contexts for isolation.

You can control parallelism in playwright.config.ts:

TypeScript
export default defineConfig({
  fullyParallel: true,     // run tests inside a file in parallel too
  workers: process.env.CI ? 2 : 4,
  retries: process.env.CI ? 2 : 0,
});

To keep some tests serial, wrap them in test.describe.serial or use test.describe.configure({ mode: 'serial' }).

Q19 What are projects in playwright.config.ts? IntermediateConfiguration +

A project is a named configuration — a combination of browser, viewport, base URL, or any other option. Projects let you run the same tests across browsers, devices or environments without duplicating code.

TypeScript — cross-browser projects
export default defineConfig({
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
    { name: 'mobile',   use: { ...devices['Pixel 7'] } },
  ],
});

Run a specific project with npx playwright test --project=firefox.

Q20 How do you debug a failing Playwright test? IntermediateDebugging +

Playwright gives you four debugging tools:

  • UI mode — npx playwright test --ui. Time-travel through each step, inspect locators and see network calls.
  • Trace viewer — npx playwright show-trace trace.zip. A full recording of the test: DOM snapshots, network, console, source.
  • Headed mode — npx playwright test --headed. Watch the browser as the test runs.
  • PWDEBUG — PWDEBUG=1 npx playwright test. Opens the Playwright Inspector to step through the test.
TypeScript — trace on first retry
export default defineConfig({
  use: { trace: 'on-first-retry' },
});
Q21 How do you do API testing with Playwright? IntermediateAPI Testing +

Playwright includes a request context that can make HTTP calls without a browser. Use it for pure API tests or to set up state before a UI test.

TypeScript — API test
import { test, expect } from '@playwright/test';

test('create user via API', async ({ request }) => {
  const response = await request.post('/api/users', {
    data: { name: 'Ravi', email: 'ravi@example.com' },
  });

  expect(response.status()).toBe(201);
  const body = await response.json();
  expect(body.name).toBe('Ravi');
});

You can also mix API and UI: create a user via the API, then verify the user appears in the UI.

Q22 What is the Page Object Model, and how do you structure it in Playwright? IntermediateFramework +

The Page Object Model puts all locators and page actions into a class. Tests then call readable methods instead of raw locators, so when the UI changes you fix one file.

TypeScript — LoginPage.ts
import { type Page, expect } from '@playwright/test';

export class LoginPage {
  constructor(private readonly page: Page) {}

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.page.getByLabel('Email').fill(email);
    await this.page.getByLabel('Password').fill(password);
    await this.page.getByRole('button', { name: 'Sign in' }).click();
  }

  async expectDashboard() {
    await expect(this.page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  }
}
TypeScript — login.spec.ts
test('valid user can log in', async ({ page }) => {
  const login = new LoginPage(page);
  await login.goto();
  await login.login('ravi@example.com', 'secret');
  await login.expectDashboard();
});
Q23 How do you run tests only on failure with screenshots and videos? IntermediateConfiguration +

Set screenshot and video capture in playwright.config.ts. The most useful default is only-on-failure, which keeps the artifacts small but captures evidence when a test breaks.

TypeScript
export default defineConfig({
  use: {
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    trace: 'on-first-retry',
  },
});

Artifacts are attached to the HTML report, so you can open them from the browser without hunting for files.

Q24 What are annotations like test.skip, test.fixme and test.slow? IntermediateTest Structure +
Playwright test annotations
AnnotationEffectWhen to use
test.skipTest is skipped and reported as skippedFeature not ready, environment not supported
test.fixmeTest is marked as expected to failKnown bug, tracked in a ticket
test.slowTriples the test timeoutGenuinely slow tests, not as a fix for flakiness
test.failTest is expected to fail; passes if it failsReproducing a bug before the fix
test.onlyRuns only this testLocal debugging only — never commit
TypeScript
test.skip(!process.env.API_KEY, 'API key not set');

test('payment flow', async ({ page }) => {
  test.slow();   // 3x timeout
  // ...
});
Q25 What is sharding, and how do you use it? IntermediateCI/CD +

Sharding splits the test suite across multiple CI machines. Each machine runs one shard, and the results are merged.

Terminal
# split 8 ways, run shard 1
npx playwright test --shard=1/8

# merge blob reports
npx playwright merge-reports --reporter html ./blob-report

Sharding cuts wall-clock time without changing how tests are written. Use it when the suite exceeds the time budget for a single CI job.

Q26 What is visual regression testing with toHaveScreenshot? IntermediateVisual Testing +

toHaveScreenshot compares a screenshot against a stored baseline image. On the first run it creates the baseline; on later runs it fails if the pixels differ beyond the configured threshold.

TypeScript
await expect(page).toHaveScreenshot('homepage.png', {
  maxDiffPixelRatio: 0.01,
});

On failure, Playwright writes three files: expected, actual and diff. Visual tests need deterministic data and fonts, and should not run against live third-party content.

Q27 How do you emulate a mobile device in Playwright? IntermediateConfiguration +

Use the built-in devices registry to apply viewport, user agent, device scale factor and touch support in one line.

TypeScript — playwright.config.ts
import { devices } from '@playwright/test';

export default defineConfig({
  projects: [
    { name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
    { name: 'mobile-safari', use: { ...devices['iPhone 14'] } },
    { name: 'tablet',        use: { ...devices['iPad Pro 11'] } },
  ],
});

You can also override individual options per test with test.use({ viewport: { width: 375, height: 667 } }).

04
Experienced · 12 questions

Playwright interview questions for experienced professionals

These questions come up for candidates with 5+ years of experience or for anyone interviewing at a product company. They expect you to reason about framework design and reliability, not just write a test.

Q28 How would you design a Playwright automation framework from scratch? ExperiencedFramework +

Layer 1 — Configuration: playwright.config.ts with baseURL, timeouts, retries, projects for browsers, trace and video on failure.

Layer 2 — Environment config: separate config files or .env files for dev, staging and prod. Read baseURL and credentials from environment variables.

Layer 3 — Fixtures: a fixtures.ts that extends the base test with Page Objects and authenticated pages. Tests never instantiate Page Objects themselves.

Layer 4 — Page Objects: one class per page or component, encapsulating locators and actions.

Layer 5 — Test data: factories or JSON fixtures for creating users, orders, etc. Prefer creating data via API rather than UI.

Layer 6 — API clients: thin wrappers around the request context for setup, teardown and API-level assertions.

Layer 7 — Tests: organised by feature, using fixtures and Page Objects. No raw locators in tests.

Layer 8 — CI: GitHub Actions or Jenkins running npx playwright test, publishing the HTML report as an artifact, with sharding if the suite is large.

Q29 How do you deal with flaky tests in Playwright? ExperiencedReliability +

Flakiness almost always comes from one of five causes. Diagnose before you retry.

  1. Bad locators — CSS selectors that match multiple elements. Switch to getByRole or getByTestId.
  2. Race conditions — the test acts before the app is ready. Use web-first assertions and waitForResponse instead of fixed waits.
  3. Shared state — tests depend on data created by other tests. Make each test create and clean up its own data.
  4. Third-party dependencies — external APIs that are slow or rate-limited. Mock them with page.route.
  5. Resource contention — too many workers on a small CI machine. Reduce workers or increase the runner size.

Retries are a safety net, not a fix. If a test needs two retries to pass, treat it as a bug and investigate.

Q30 How do you test file upload, download and drag-and-drop reliably? ExperiencedActions +

Playwright handles all three with first-class APIs, but each needs a specific pattern.

  • Upload — use setInputFiles(). For drag-and-drop uploads, listen for the filechooser event with page.waitForEvent('filechooser').
  • Download — always wrap the click in Promise.all([waitForEvent('download'), click()]) to avoid a race.
  • Drag and drop — use locator.dragTo(target). For HTML5 drag events, dispatch them manually if the built-in method does not trigger the app's handler.
TypeScript — drag and drop
await page.locator('#source').dragTo(page.locator('#target'));

// HTML5 fallback
await page.locator('#source').dispatchEvent('dragstart');
await page.locator('#target').dispatchEvent('drop');
Q31 How do you integrate Playwright with GitHub Actions or Jenkins? ExperiencedCI/CD +

A CI pipeline for Playwright needs to install browsers, run tests, and publish the report.

YAML — GitHub Actions
name: Playwright Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/

On Jenkins, the pattern is the same: install Node, install browsers, run tests, archive the playwright-report folder, and use the JUnit reporter for test trends.

Q32 How do you handle authentication for tests that need multiple user roles? ExperiencedAuthentication +

Save a separate storageState file for each role, then use the right one per test.

TypeScript — playwright.config.ts
export default defineConfig({
  projects: [
    { name: 'admin-setup', testMatch: /auth\.admin\.setup\.ts/ },
    { name: 'user-setup',  testMatch: /auth\.user\.setup\.ts/  },
    {
      name: 'admin-tests',
      use: { storageState: 'auth/admin.json' },
      dependencies: ['admin-setup'],
    },
    {
      name: 'user-tests',
      use: { storageState: 'auth/user.json' },
      dependencies: ['user-setup'],
    },
  ],
});

In the tests, use test.use({ storageState: 'auth/admin.json' }) inside a describe block when only a few tests need a specific role.

Q33 How do you handle CAPTCHA, OTP and other third-party blockers? ExperiencedStrategy +

Do not try to defeat CAPTCHA — treat it as a boundary and design around it.

  • Disable in test env — ask the team to turn off CAPTCHA for test accounts. This is the cleanest approach.
  • Bypass with storageState — log in once manually in a real browser, save the state, and reuse it in tests so you never hit the login flow again.
  • OTP stubs — use a test OTP that always works in staging, or read the OTP from a test mailbox via API.
  • Mock third-party APIs — if a payment provider or SMS gateway is involved, mock it with page.route.

Never hard-code production credentials or attempt to automate a third-party service that forbids it.

Q34 How do you test a page that relies on WebSockets or Server-Sent Events? ExperiencedNetwork +

Playwright can observe WebSocket frames and SSE events, but it cannot mock them the way it mocks HTTP with page.route.

TypeScript — observe WebSocket frames
page.on('websocket', ws => {
  ws.on('framereceived', frame => {
    console.log('received:', frame.payload);
  });
  ws.on('framesent', frame => {
    console.log('sent:', frame.payload);
  });
});

For assertions, wait for the UI to reflect the pushed message rather than asserting on the frame itself. For SSE, listen to the network response with waitForResponse and read the body.

Q35 How do you keep Playwright tests fast when the suite grows to hundreds of tests? ExperiencedPerformance +

Practical levers, roughly in order of impact:

  • Reuse storageState — never log in via UI in every test.
  • Set up state via API — creating users, orders or accounts through the API is far faster than clicking through the UI.
  • Run in parallel — increase workers and use fullyParallel: true.
  • Shard in CI — split the suite across machines when wall-clock time matters.
  • Mock third-party calls — slow external APIs dominate test time. Mock them with page.route.
  • Use headless mode — headless is faster and is the default in CI.
  • Trim assertions — one meaningful assertion per behaviour, not ten redundant ones.
Q36 How do you handle test data setup and teardown across parallel workers? ExperiencedFramework +

Parallel workers mean tests must not depend on shared mutable data. The rule is: each test creates what it needs with unique identifiers, and cleans up after itself.

  • Unique data — generate unique emails, usernames or IDs per worker using workerInfo.workerIndex or a timestamp.
  • API-first setup — create test data through the API in a fixture. It is faster and less brittle than creating via the UI.
  • Teardown in the fixture — delete the created data after use() returns, so cleanup runs even if the test fails.
  • Do not rely on order — tests must pass in any order and in any grouping.
Q37 What is the difference between page.route and context.route, and when do you use each? ExperiencedNetwork +

page.route intercepts requests from one page only. Use it when the mock is specific to a single test's page.

context.route intercepts requests from every page in the context, including pages opened later. Use it when the mock should apply to all tabs, or when you want to set up a mock once for the whole test.

There is also browserContext.route which behaves like context.route. Handlers are matched in reverse order of registration, so the most recently added route wins.

Q38 How do you verify an API response without using a browser? ExperiencedAPI Testing +

Use the request fixture, which is a standalone HTTP client with no browser involved. It supports baseURL, headers, auth and all HTTP methods.

TypeScript
test('list users returns correct shape', async ({ request }) => {
  const res = await request.get('/api/users?page=1', {
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
  });

  expect(res.status()).toBe(200);
  expect(res.headers()['content-type']).toContain('application/json');

  const body = await res.json();
  expect(Array.isArray(body)).toBeTruthy();
  expect(body[0]).toHaveProperty('id');
});

You can also use a separate request context for different auth scopes with playwright.request.newContext().

Q39 How do you structure fixtures for a large project with many pages? ExperiencedFixtures +

Split fixtures into modules and merge them into one extended test object. This keeps each file small and makes dependencies explicit.

TypeScript — pages.fixture.ts
import { test as base } from '@playwright/test';
import { LoginPage } from './LoginPage';
import { DashboardPage } from './DashboardPage';

type Pages = {
  loginPage: LoginPage;
  dashboardPage: DashboardPage;
};

export const test = base.extend<Pages>({
  loginPage:     async ({ page }, use) => use(new LoginPage(page)),
  dashboardPage: async ({ page }, use) => use(new DashboardPage(page)),
});

export { expect } from '@playwright/test';

Tests import test from this file instead of from Playwright directly, and get all fixtures without any boilerplate.

Q40 How do you report test results to stakeholders? ExperiencedReporting +

Use a combination of reporters depending on the audience.

  • HTML reporter — Playwright's built-in report. Great for developers, includes traces, screenshots and videos.
  • JUnit reporter — XML format consumed by Jenkins, Azure DevOps and GitHub Actions for test trends.
  • Allure reporter — richer dashboards with history, categories and attachments. Common in enterprise setups.
  • Blob reporter — used with sharding; merge the blobs into a single HTML report at the end of the CI run.
TypeScript — multiple reporters
export default defineConfig({
  reporter: [
    ['html', { outputFolder: 'playwright-report', open: 'never' }],
    ['junit', { outputFile: 'results.xml' }],
    ['allure-playwright'],
  ],
});
05
Scenario · 10 questions

Scenario-based Playwright interview questions

These are the questions an interviewer asks when they want to see how you think under a realistic problem, not just how well you recall definitions. Answer with a step-by-step approach.

S01 A Playwright test passes locally but fails in CI with a timeout on a click. How do you debug it? ScenarioDebugging +

Step 1 — Open the trace from the CI run. The HTML report attaches the trace; look at the DOM snapshot at the moment of the click to see what was actually on screen.

Step 2 — Check the locator. CI runs headless at a different viewport, so elements that are visible on your machine may be off-screen or overlapped.

Step 3 — Check the actionability log in the trace. If the element is not stable, an animation is still running. If it is not receiving events, an overlay or cookie banner is covering it.

Step 4 — Check the environment. CI may hit a slower staging server, or the test may be missing data that exists locally. Compare the network tab in the trace.

Step 5 — If the failure is intermittent, look for shared state. CI runs with more workers, so tests may interfere with each other.

S02 A UI element has no accessible role, no label and no test ID. How do you locate it? ScenarioLocators +

Step 1 — Ask the developer to add a data-testid. This is the correct long-term fix and takes minutes.

Step 2 — In the meantime, use a chained locator. Start from a nearby element that does have an accessible role, then filter or use nth().

Step 3 — If the element is inside a table or list, use locator.filter({ hasText: '...' }) to narrow by content.

Step 4 — As a last resort, use a CSS selector. Keep it short and tied to structure, not to styling classes that change often.

TypeScript — chained locator
await page
  .getByRole('row', { name: /Ravi/ })
  .getByRole('button', { name: 'Edit' })
  .click();
S03 Your tests depend on a third-party API that is slow and sometimes down. How do you make the suite stable? ScenarioNetwork +

Step 1 — Identify every call to the third party. Use the network tab in UI mode or the trace viewer to see them.

Step 2 — For UI tests, mock the call with page.route. Return a realistic fixture response so the UI can render.

Step 3 — For integration tests that must hit the real API, add a small retry with backoff and a clear timeout. Do not let a slow third party hang the test forever.

Step 4 — Keep a small contract test that verifies the real API still returns the shape your mocks assume. Run this separately, not as part of the main suite.

Step 5 — Tag tests that need the real third party so they can be excluded from fast local runs.

S04 A test needs a logged-in user, but the login flow now has an OTP. How do you adapt? ScenarioAuthentication +

Step 1 — Do not automate the OTP. Ask the team to provide a static test OTP in the staging environment, or to disable OTP for test accounts.

Step 2 — If OTP must be read dynamically, expose it through a test-only API that your fixture calls, or read it from a test mailbox.

Step 3 — Save the logged-in state to storageState after the setup project runs. All other tests reuse it and never see the OTP step.

Step 4 — Keep one dedicated test that exercises the OTP flow end-to-end. Everything else uses the saved state.

S05 A visual regression test fails on every run because of a dynamic timestamp on the page. How do you fix it? ScenarioVisual Testing +

Step 1 — Do not increase the diff threshold to hide the problem. That defeats the purpose of visual testing.

Step 2 — Mask the dynamic element with the mask option, so it is excluded from the comparison.

Step 3 — If the whole page has timestamps, freeze the clock using page.clock so the rendered time is deterministic.

TypeScript
await expect(page).toHaveScreenshot('dashboard.png', {
  mask: [page.getByTestId('last-updated')],
});

Step 4 — Make sure fonts and viewport are fixed in the config. Different fonts on CI cause pixel diffs that have nothing to do with your app.

S06 Your suite takes 45 minutes to run. How do you bring it under 15? ScenarioPerformance +

Step 1 — Measure first. Use the HTML report to find the ten slowest tests. Optimise those, not the whole suite.

Step 2 — Replace UI logins with storageState. If every test logs in through the UI, this alone can cut a large chunk.

Step 3 — Replace UI setup with API setup. Creating a user or an order through the API takes milliseconds instead of seconds.

Step 4 — Increase workers. If the CI machine has capacity, more workers mean shorter wall-clock time.

Step 5 — Shard across multiple CI machines. This is the biggest single win for a large suite.

Step 6 — Mock slow third-party calls with page.route. If a payment provider takes 4 seconds, mock it.

S07 You need to test a feature that is behind a feature flag. How do you handle it? ScenarioStrategy +

Step 1 — Do not toggle flags from the UI. That makes tests slow and brittle.

Step 2 — Set the flag from the API or the database in a fixture, before the page loads. Tear it down after use() returns.

Step 3 — Alternatively, mock the flag endpoint with page.route and return the enabled or disabled state you need for the test.

Step 4 — Write one test for the flag on and one for the flag off. Both are valid states of the application and both should be covered.

Step 5 — Once the flag is removed, delete the mocked tests and keep the tests for the final behaviour.

S08 A test works in Chromium but fails in WebKit. What do you check first? ScenarioCross-browser +

Step 1 — Open the trace from the WebKit run and compare with the Chromium trace. The difference is usually visible in the DOM snapshot.

Step 2 — Check for layout differences. WebKit often renders spacing and overflow slightly differently, which can push elements off-screen.

Step 3 — Check for API support. WebKit may not support a newer JavaScript feature or a CSS property the app relies on.

Step 4 — Check for input behaviour. Date pickers, number inputs and file uploads behave differently across browsers.

Step 5 — If the bug is real and only affects WebKit, report it. If the test is over-specified, loosen the assertion.

S09 A developer changes a data-testid that your tests depend on. How do you make the suite resilient? ScenarioFramework +

Step 1 — Centralise test IDs. Do not scatter getByTestId('submit-btn') across fifty files. Put them in one constants file or inside Page Objects.

Step 2 — Prefer role-based locators over test IDs when the element has an accessible role. Roles rarely change.

Step 3 — Add a contract or smoke test that runs on every commit and covers the critical paths. It will fail immediately when a test ID changes.

Step 4 — Agree on a naming convention for test IDs and document it. This reduces accidental renames.

Step 5 — When a test ID must change, update it in one place. This is the whole point of Page Objects.

S10 You need to run the same tests against dev, staging and prod. How do you structure it? ScenarioConfiguration +

Step 1 — Externalise the base URL, credentials and any environment-specific values. Use .env files, one per environment, loaded by the config.

Step 2 — Define a project per environment in playwright.config.ts, each reading from the matching .env file.

Step 3 — Tag tests that must not run against production — anything that creates or deletes data — with @destructive, and exclude them from the prod project.

Step 4 — Use separate test accounts per environment. Never point production tests at real customer accounts.

Step 5 — In production, run only a small read-only smoke suite. The full regression belongs in staging.

Terminal
npx playwright test --project=staging
npx playwright test --project=prod --grep-invert @destructive
06
Coding round · 10 TypeScript snippets

Playwright coding round — TypeScript with @playwright/test

These are the ten snippets that come up most often in Playwright coding rounds. Write each one by hand at least once before your interview — reading code is not the same as writing it.

C01 First test with getByRole and expect auto-retry BeginnerBasics +
TypeScript — tests/home.spec.ts
import { test, expect } from '@playwright/test';

test('home page shows welcome heading', async ({ page }) => {
  await page.goto('https://example.com');

  await expect(
    page.getByRole('heading', { name: 'Welcome' })
  ).toBeVisible();

  await expect(page).toHaveTitle(/Example Domain/);
});
Output
1 passed (2.1s)
C02 Custom fixture that provides a logged-in page IntermediateFixtures +
TypeScript — fixtures.ts
import { test as base, expect } from '@playwright/test';

type Fixtures = { loggedInPage: import('@playwright/test').Page };

export const test = base.extend<Fixtures>({
  loggedInPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('ravi@example.com');
    await page.getByLabel('Password').fill('secret');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await page.waitForURL('/dashboard');

    await use(page);
  },
});

export { expect };
TypeScript — tests/dashboard.spec.ts
import { test, expect } from '../fixtures';

test('dashboard loads for logged-in user', async ({ loggedInPage }) => {
  await expect(
    loggedInPage.getByRole('heading', { name: 'Dashboard' })
  ).toBeVisible();
});
Output
1 passed (3.4s)
C03 Page Object class with typed locators IntermediatePage Object +
TypeScript — pages/TodoPage.ts
import { type Locator, type Page, expect } from '@playwright/test';

export class TodoPage {
  readonly page: Page;
  readonly input: Locator;
  readonly addButton: Locator;
  readonly items: Locator;

  constructor(page: Page) {
    this.page = page;
    this.input = page.getByPlaceholder('What needs to be done?');
    this.addButton = page.getByRole('button', { name: 'Add' });
    this.items = page.getByTestId('todo-item');
  }

  async addTodo(text: string) {
    await this.input.fill(text);
    await this.addButton.click();
  }

  async expectItemCount(n: number) {
    await expect(this.items).toHaveCount(n);
  }
}
Usage
const todo = new TodoPage(page);
await todo.addTodo('Buy milk');
await todo.expectItemCount(1);
C04 Reuse login with storageState IntermediateAuth +
TypeScript — tests/auth.setup.ts
import { test as setup, expect } from '@playwright/test';

setup('authenticate once', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('ravi@example.com');
  await page.getByLabel('Password').fill('secret');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();

  await page.context().storageState({ path: 'auth.json' });
});
TypeScript — playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  projects: [
    { name: 'setup', testMatch: /auth\.setup\.ts/ },
    {
      name: 'chromium',
      use: { storageState: 'auth.json' },
      dependencies: ['setup'],
    },
  ],
});
Output
Running 2 projects: setup, chromium
setup: 1 passed
chromium: 5 passed
C05 API request context test IntermediateAPI +
TypeScript — tests/api.spec.ts
import { test, expect } from '@playwright/test';

test('create and fetch user via API', async ({ request }) => {
  const createRes = await request.post('/api/users', {
    data: { name: 'Ravi', email: 'ravi@example.com' },
  });

  expect(createRes.status()).toBe(201);
  const created = await createRes.json();

  const getRes = await request.get(`/api/users/${created.id}`);
  expect(getRes.status()).toBe(200);

  const fetched = await getRes.json();
  expect(fetched.email).toBe('ravi@example.com');
});
Output
1 passed (0.8s)
C06 Network mocking with page.route IntermediateNetwork +
TypeScript
import { test, expect } from '@playwright/test';

test('shows empty state when API returns no orders', async ({ page }) => {
  await page.route('**/api/orders', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([]),
    });
  });

  await page.goto('/orders');
  await expect(page.getByText('No orders yet')).toBeVisible();
});
Output
1 passed (1.6s)

Note: Route handlers run in reverse registration order. If you register two handlers for the same pattern, the last one wins.

C07 File upload and download IntermediateActions +
TypeScript — upload
test('user can upload a resume', async ({ page }) => {
  await page.goto('/profile');
  await page.getByLabel('Resume').setInputFiles('fixtures/resume.pdf');
  await expect(page.getByText('Upload complete')).toBeVisible();
});
TypeScript — download
test('user can export a report', async ({ page }) => {
  await page.goto('/reports');

  const [download] = await Promise.all([
    page.waitForEvent('download'),
    page.getByRole('button', { name: 'Export CSV' }).click(),
  ]);

  expect(download.suggestedFilename()).toBe('report.csv');
  await download.saveAs('downloads/report.csv');
});
Output
2 passed (2.9s)
C08 Parallel tests and cross-browser projects in playwright.config.ts IntermediateConfiguration +
TypeScript — playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : 4,

  reporter: [['html', { open: 'never' }]],

  use: {
    baseURL: process.env.BASE_URL ?? 'https://staging.example.com',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
    { name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
  ],
});
Output
Running 4 projects with 2 workers
chromium: 12 passed
firefox:  12 passed
webkit:   12 passed
mobile-chrome: 12 passed
C09 Trace viewer on failure — configure and read traces IntermediateDebugging +
TypeScript — playwright.config.ts
export default defineConfig({
  use: {
    trace: 'on-first-retry',
  },
});
Terminal — view trace
# after a failing run
npx playwright show-trace test-results/login-chromium/trace.zip

# or open the HTML report, which links traces automatically
npx playwright show-report

In the trace viewer, use the timeline to see each action, the DOM snapshot at that moment, network calls and console logs. The "Before" and "After" snapshots for a failing step usually show exactly what went wrong.

Options: off, on, retain-on-failure, on-first-retry. on-first-retry is the best default for CI.

C10 Hybrid test: seed via API, verify via UI IntermediateHybrid +
TypeScript
import { test, expect } from '@playwright/test';

test('created order appears in the orders list', async ({ page, request }) => {
  // seed via API — fast and reliable
  const createRes = await request.post('/api/orders', {
    data: { product: 'Keyboard', quantity: 2 },
  });
  expect(createRes.status()).toBe(201);
  const order = await createRes.json();

  // verify via UI
  await page.goto('/orders');
  await expect(
    page.getByRole('row', { name: new RegExp(order.id) })
  ).toBeVisible();
});
Output
1 passed (1.9s)

Why hybrid: creating data through the UI is slow and brittle. Creating it through the API and verifying through the UI gives you both speed and confidence.

07
Quick revision

Quick-revision cheat table

Fifteen one-line definitions and syntax reminders to skim on the morning of your interview.

One-line reminders for common Playwright interview topics
TopicOne-line reminder
PlaywrightMicrosoft's browser automation library — Chromium, Firefox, WebKit; TypeScript, JS, Python, Java, C#.
Browser / Context / PageBrowser is the instance; context is an isolated session; page is a tab.
Auto-waitingEvery action waits for the element to be attached, visible, stable, enabled and receiving events.
LocatorLazy reference to an element; re-resolves on each use; preferred over element handles.
getByRoleLocates by accessible role and name; the most resilient and accessible-first locator.
toBeVisible vs toBeAttachedVisible requires non-zero size and no CSS hiding; attached only requires presence in DOM.
FixtureReusable setup injected into a test; built-in fixtures include page, context, browser, request.
storageStateSaved cookies and local storage; use it to reuse a login across tests.
page.routeIntercept and mock or abort network requests for one page.
request fixtureStandalone HTTP client for API testing without a browser.
WorkersParallel test processes; each gets its own browser and isolated contexts.
ProjectsNamed configs for browsers, devices or environments in playwright.config.ts.
ShardingSplit the suite across CI machines with --shard=1/8 and merge blob reports.
Trace viewerFull recording of a test — DOM snapshots, network, console, source.
toHaveScreenshotVisual comparison against a stored baseline; supports maxDiffPixelRatio and mask.
test.skip / fixme / slowSkip a test; mark as expected-to-fail; triple the timeout.
CodegenRecord interactions and generate test code — use it to discover locators, then refactor.
ReportersHTML for developers, JUnit for CI trends, Allure for enterprise dashboards, blob for sharding.
08
Preparation plan

How to prepare for a Playwright interview in 7 days

If your interview is next week, follow this plan. Each day has a single focus and a practice task — do not just read, write code.

Seven-day Playwright interview preparation plan
DayFocusPractice task
Day 1 Fundamentals and architecture Answer Q1–Q6 out loud. Install Playwright, run codegen on any public site, and look at the generated test.
Day 2 Locators and assertions Answer Q4, Q7, Q8, Q13. Write a test that uses getByRole, getByLabel and getByTestId. Compare toBeVisible with toBeAttached.
Day 3 Fixtures and Page Objects Answer Q9, Q11, Q22, Q39. Write a Page Object class and a custom fixture that provides it. Refactor a test to use both.
Day 4 Authentication and network Answer Q16, Q17, Q37. Set up storageState, then write a test that mocks an API with page.route.
Day 5 API testing and hybrid Answer Q21, Q38. Write the API test from C05 and the hybrid test from C10. Run both.
Day 6 Configuration and CI Answer Q18, Q19, Q25, Q31. Set up projects for chromium and firefox. Add the GitHub Actions workflow from Q31.
Day 7 Debugging and mock interview Answer Q20, Q29, and all scenario questions out loud. Run the coding round snippets without looking.
09
What to avoid

Common mistakes candidates make

01

Using fixed waits

page.waitForTimeout() is almost always wrong. Use web-first assertions and waitForResponse instead. Fixed waits are slow and still flaky.

02

Fragile CSS selectors

.btn.btn-primary.mt-4 breaks the moment styling changes. Use getByRole or getByTestId. Ask the developer for a test ID if none exists.

03

Logging in through the UI in every test

This is the single biggest time waster. Use storageState with a setup project. Tests should start already authenticated.

04

Not using fixtures for Page Objects

Instantiating Page Objects inside every test adds boilerplate. Extend the test with fixtures so Page Objects are injected automatically.

05

Confusing toBeVisible with toBeAttached

An element hidden with display: none is attached but not visible. Use toBeVisible() for user-facing assertions and toBeAttached() only when you truly need presence in the DOM.

06

Ignoring parallel safety

Tests that share data will fail when workers increase. Generate unique data per test, create it via API, and clean up in a fixture.

10
Training

Practise these topics with Uncodemy

Uncodemy's Professional Certificate Program in Playwright Automation covers JavaScript, Playwright, Mocha, API testing, Jenkins, GitHub, Allure and GitHub Copilot, with 5 domain projects (retail, HRTech, banking, healthcare booking, hospital).

Uncodemy programs covering Playwright and automation testing topics
ProgramWhat it coversDuration / fee
Professional Certificate Program in Playwright Automation JavaScript, Playwright, Mocha, API testing, Jenkins, GitHub, Allure, GitHub Copilot. 5 domain projects (retail, HRTech, banking, healthcare booking, hospital). 2.5–3 months. Fee not published; ask a counsellor for the current fee and batch.
Software Testing Manual testing, SQL, API testing (Postman, REST Assured), Selenium with Java, TestNG, Maven, Jenkins, JIRA. Good foundation before Playwright. ₹17,500 + GST · 4–5 months
Automation Testing 90+ hours, 45+ live sessions. Selenium WebDriver with Java, TestNG, Maven, Jenkins, POM/hybrid frameworks, Cucumber. Ask a counsellor for current batch details.

Placement assistance (as published by Uncodemy): resume building, LinkedIn optimisation, portfolio review, interview preparation, mock interviews and opportunity sharing through a network of 850+ companies. Uncodemy does not guarantee employment, as hiring decisions rest with employers. See placement support.

Also useful: AI career guidance for picking a path, and the 2-class free trial to see how a live batch runs before you commit.

11
FAQs

Playwright interview preparation FAQs

F1 How do I prepare for a Playwright interview? +
Start with the beginner questions, then work through the coding round — locators, fixtures, storageState, API testing and config. Practise writing tests by hand. Revise the cheat table the morning of the interview. Follow the 7-day plan in section 08.
F2 Is Playwright better than Selenium for a career in 2026? +
Neither is universally better. Playwright is faster to set up, less flaky by default, and has stronger debugging tools. Selenium still has broader language and browser support and a larger existing job market. Many testers learn both, because companies run mixed stacks.
F3 Should I learn Playwright with TypeScript or JavaScript? +
Learn TypeScript. It is the language Playwright's own documentation and examples use, it catches errors at compile time, and most job descriptions list TypeScript. If you already know JavaScript, picking up TypeScript for Playwright takes a few days.
F4 Can a Selenium tester switch to Playwright easily? +
Yes. The concepts transfer — locators, waits, Page Objects, assertions. The main adjustments are auto-waiting (you stop writing explicit waits), the context/page model, and fixtures instead of base classes. A Selenium tester can be productive in Playwright within a week or two.
F5 What projects should I show for a Playwright role? +
Build one suite with a Page Object structure and custom fixtures, one with storageState login reuse, and one that combines UI and API testing. Add CI with GitHub Actions and publish the HTML report. A project with traces and a green pipeline is worth more than ten small demos.
Want to practise with a trainer?

Join a live Playwright batch — 2 free classes first

Reading questions is the easy part. Writing and explaining Playwright tests under time pressure is what gets you hired. Uncodemy runs a live Playwright Automation program at Noida and Delhi, and online. Attend two classes before you decide.

Page contents 49+ questions · 10 snippets
Levels covered Fresher · Intermediate · Experienced
Reviewed by Mr. Pradyumn Singh
Last updated 14 October 2026