Skip to content

Playwright Automation Engineer Interview Questions & Answers

40 questions for Playwright Automation Engineer interviews — 4 easy, 34 medium, 2 hard.

About the Playwright Automation Engineer role

Builds and maintains end-to-end test suites in Playwright with TypeScript or JavaScript. Writes stable tests for web journeys, creates page objects and fixtures, mocks APIs for edge cases and keeps the suite green in CI. Interviews test locator choice, auto-waiting, fixtures, API helpers, debugging with traces and short live coding tasks such as automating a login or checkout flow, plus questions on keeping the suite fast.

Playwright Test runner TypeScript Role-based locators Custom fixtures Trace debugging
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

How do you share one authenticated API context across all tests in a worker?

Save to ▾
Show answer
Create a worker-scoped fixture that calls request.newContext with baseURL and an auth header, or logs in through the API and keeps the cookies. Pass it to use, and dispose it after use returns. Every test in that worker gets the same ready context, so login happens once per worker. Do not store per-test data on it, or tests start depending on each other.
02

A third-party analytics script makes tests slow and flaky. What do you do?

Save to ▾
Show answer
Block it in tests with context.route on its domain and route.abort, or fulfill it with an empty script. Analytics is not what you are testing, and blocking it removes delays and random network errors. If tracking events matter, write a few separate tests that capture the requests with waitForRequest and assert their parameters.
03

How do you mock one GraphQL query when all calls go to /graphql?

Save to ▾
Show answer
await page.route('**/graphql', async route => { const body = route.request().postDataJSON(); if (body.operationName === 'GetCart') { return route.fulfill({ json: { data: { cart: { items: [] } } } }); } return route.fallback(); });
04

Write a custom fixture that gives a logged-in page.

Save to ▾
Show answer
import { test as base } from '@playwright/test'; export const test = base.extend({ loggedInPage: async ({ page }, use) => { await page.goto('/login'); await page.getByLabel('Email').fill('qa@test.in'); await page.getByLabel('Password').fill('Pass@123'); await page.getByRole('button', { name: 'Sign in' }).click(); await use(page); }, });
05

Tell me about a time you reduced pipeline time with parallel runs or sharding.

Save to ▾
Show answer
Situation: our regression suite took 55 minutes, so developers skipped it. Task: bring it under 15. Action: I removed shared test data so fullyParallel could be turned on, split the three slowest files, and ran four shards with blob reports merged into one HTML report. Result: the suite finished in 12 minutes and it became a required check on every pull request.
06

What does test.describe do?

Save to ▾
Show answer
test.describe groups related tests under one title, which shows up in reports. Hooks and test.use inside the group apply only to those tests. You can nest groups, for example Checkout then Guest user. Groups are only for organisation, they do not make tests share a page or state, so each test still gets fresh fixtures.
07

How do you handle a page that opens a browser permission prompt for notifications?

Save to ▾
Show answer
Browser permission prompts are not part of the page, so you cannot click them. Instead grant the permission up front with the permissions option in the config or context, for example permissions: ['notifications'], or call context.grantPermissions. To test the denied path, create a context without the permission and assert that the app shows its fallback message.
08

What is the difference between page.route and context.route?

Save to ▾
Show answer
page.route applies only to requests from that page. context.route applies to every page in the browser context, including pop-ups and new tabs opened later. Use context.route when a flow opens new windows, such as a payment pop-up, or when you set up mocks in a fixture before the page exists. Page routes take priority over context routes.
09

How do you provide page objects as fixtures?

Save to ▾
Show answer
type Pages = { loginPage: LoginPage; cartPage: CartPage }; export const test = base.extend<Pages>({ loginPage: async ({ page }, use) => { await use(new LoginPage(page)); }, cartPage: async ({ page }, use) => { await use(new CartPage(page)); }, }); // test('x', async ({ loginPage }) => { ... })
10

How does expect.poll work and when would you use it?

Save to ▾
Show answer
expect.poll calls a function again and again until the value it returns passes the matcher or the timeout runs out. It is useful for values that are not locators, such as an API status or a database count. Example: await expect.poll(async () => (await request.get('/api/job/7')).status()).toBe(200). You can tune the intervals and the timeout.
11

How do you use keyboard shortcuts in Playwright?

Save to ▾
Show answer
Use locator.press('Enter') for a key on a focused element, or page.keyboard.press('Control+A') for page-level shortcuts. Modifiers combine with a plus sign, and ControlOrMeta picks Ctrl on Windows and Linux and Cmd on macOS. keyboard.down and keyboard.up hold keys. Use pressSequentially only when the app needs real keystrokes, such as autocomplete.
12

How do you test a page in dark mode or with reduced motion?

Save to ▾
Show answer
Set colorScheme: 'dark' or reducedMotion: 'reduce' in the context options or test.use. You can also switch during a test with page.emulateMedia({ colorScheme: 'dark' }). Then assert the right styles or take a screenshot comparison. Reduced motion also helps stability, because many apps skip animations when that preference is set.
13

What is the difference between page, context and browser?

Save to ▾
Show answer
A browser is the engine instance; a context is an isolated session (cookies, storage); a page is a tab inside a context.
14

What is expect(...).toPass and how is it different from expect.poll?

Save to ▾
Show answer
toPass retries a whole block of code, including several actions and assertions, until it runs without throwing. expect.poll retries only one function that returns a value. Use toPass for flows like 'click refresh, then check the status' that must be repeated together. It has no timeout by default, so always pass a timeout to avoid a hung test.
15

How do you assert an element's CSS class, attribute or value?

Save to ▾
Show answer
const btn = page.getByRole('button', { name: 'Submit' }); await expect(btn).toHaveClass(/disabled/); await expect(btn).toHaveAttribute('aria-busy', 'true'); await expect(page.getByLabel('City')).toHaveValue('Pune'); await expect(btn).toHaveCSS('cursor', 'not-allowed');
16

Why choose Playwright over Selenium?

Save to ▾
Show answer
Auto-waiting, a built-in test runner, Chromium/Firefox/WebKit with one API, network interception, the trace viewer and parallel runs out of the box — usually faster and less flaky.
17

How does Playwright talk to browsers, and why does that make it less flaky?

Save to ▾
Show answer
Playwright runs outside the browser and controls it through a persistent connection using each engine's own protocol, such as CDP for Chromium and patched protocols for Firefox and WebKit. It can watch DOM, network and events directly, so it waits on real state instead of polling from inside the page. This gives reliable auto-waiting, multi-tab and network control.
18

How do you test accessibility with Playwright?

Save to ▾
Show answer
Integrate axe-core through @axe-core/playwright, run an analysis on the page and assert that there are no violations.
19

How do you test geolocation, permissions and time zones?

Save to ▾
Show answer
Set them in the context options (geolocation, permissions, timezoneId, locale) to simulate users from other regions.
20

Tell me about a time mocking helped you test a hard edge case.

Save to ▾
Show answer
Situation: we could not reproduce a bug where the cart broke when the stock API returned zero for one item. Task: cover it with a test. Action: I mocked only the stock endpoint with route.fetch and changed one item's quantity, then asserted the out-of-stock label. Result: the bug was fixed and the test now protects that case on every run.
21

How do you do visual regression testing?

Save to ▾
Show answer
Use expect(page).toHaveScreenshot() to compare with stored baselines, with thresholds and masks for dynamic regions.
22

What are web-first assertions?

Save to ▾
Show answer
Assertions like expect(locator).toBeVisible() that retry until they pass or time out, unlike one-off checks.
23

Describe a time you convinced developers to add accessible labels or test ids.

Save to ▾
Show answer
Situation: many inputs had no labels, so our tests relied on long CSS selectors that broke often. Task: get stable hooks without slowing developers. Action: I listed the worst screens, showed how getByLabel tests read and how the same labels help screen reader users, and offered to raise small pull requests myself. Result: the team added labels to new work as a rule and locator breakages dropped sharply.
24

Which locators does Playwright recommend?

Save to ▾
Show answer
User-facing ones: getByRole, getByText, getByLabel, getByPlaceholder and getByTestId, which are more resilient than CSS or XPath.
25

How do you simulate a slow API to test a loading spinner?

Save to ▾
Show answer
await page.route('**/api/report', async route => { await new Promise(r => setTimeout(r, 3000)); await route.continue(); }); await page.getByRole('button', { name: 'Generate' }).click(); await expect(page.getByRole('progressbar')).toBeVisible(); await expect(page.getByRole('progressbar')).toBeHidden();
26

How do you test file downloads and verify their content?

Save to ▾
Show answer
Capture the download event, save it with download.saveAs, then read the file in Node and assert on name, size or content.
27

What is the Clock API in Playwright?

Save to ▾
Show answer
page.clock lets tests control time. clock.install() fakes Date and timers, clock.setFixedTime() freezes the current date, and clock.fastForward() or runFor() moves time ahead. This is useful for session timeouts, countdowns, scheduled banners and date-based logic, without waiting real minutes. Install the clock before the page loads so the app uses fake time from the start.
28

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

Save to ▾
Show answer
A test-scoped fixture is created and torn down for every test, which keeps tests isolated. A worker-scoped fixture is set up once per worker process and shared by all tests that run in it, which suits expensive things like a started server or a seeded account. Declare it with { scope: 'worker' }. Worker fixtures cannot depend on test fixtures.
29

A beforeAll creates one user and tests in the same file fail randomly in parallel mode. Why?

Save to ▾
Show answer
With fullyParallel, the file's tests run in different workers, so each worker creates its own user, or tests in one worker share one user and change its state. Shared mutable data causes random failures. Give each test its own data through a test-scoped fixture, or create a unique user per worker with workerInfo.workerIndex in the name.
30

What is the difference between click and dispatchEvent?

Save to ▾
Show answer
click simulates a real user click with actionability checks; dispatchEvent fires a DOM event directly without those checks.
31

How does URL matching work in page.route?

Save to ▾
Show answer
The first argument can be a glob string, a regular expression or a function that receives a URL object. Globs like '**/api/users*' are simple but * does not cross slashes and ** does. Regex gives precise control over query strings. A predicate function suits complex rules, for example matching host plus a specific search parameter.
32

How do you assert that a request was sent with the correct payload?

Save to ▾
Show answer
const reqPromise = page.waitForRequest(r => r.url().endsWith('/api/orders') && r.method() === 'POST' ); await page.getByRole('button', { name: 'Place order' }).click(); const req = await reqPromise; expect(req.postDataJSON()).toMatchObject({ qty: 2, sku: 'MOU-01' });
33

How do you handle multiple tabs or pop-ups?

Save to ▾
Show answer
Wait for the popup event with const [popup] = await Promise.all([page.waitForEvent('popup'), page.click('a')]), then interact with popup.
34

How do you locate elements inside a specific row or card?

Save to ▾
Show answer
Chain locators: find the container first, then search inside it. Example: page.getByRole('row', { name: 'INV-104' }).getByRole('button', { name: 'Download' }). Each step narrows the search area, so short names like Edit or Download stay unique. Chaining also documents intent clearly and avoids long CSS paths that break when markup changes.
35

How do you filter a list of locators by text or by a child element?

Save to ▾
Show answer
// pick one product card from many const card = page.getByRole('listitem') .filter({ hasText: 'Wireless Mouse' }) .filter({ has: page.getByRole('button', { name: 'Add to cart' }) }); await card.getByRole('button', { name: 'Add to cart' }).click(); // hasNotText and hasNot exclude matches
36

Tell me about a time you explained a failing test report to a non-technical stakeholder.

Save to ▾
Show answer
Situation: a product manager saw a red build and wanted to delay the release. Task: explain what actually failed. Action: I opened the HTML report, grouped failures into one real bug in the coupon flow and two flaky tests, and showed the screenshot of the real bug in plain words. Result: we shipped after the coupon fix, and the manager asked for that short summary on every release.
37

How do you test API error scenarios?

Save to ▾
Show answer
Send invalid payloads or missing auth and assert 400, 401 or 404 codes and the error message bodies.
38

What are global setup and teardown?

Save to ▾
Show answer
Scripts that run once before or after the whole run, for seeding data, starting services or creating authentication state.
39

How do you handle authentication to avoid logging in every test?

Save to ▾
Show answer
Log in once in a setup project, save storageState to a file and reuse it in other projects or tests.
40

How do you select an option from a native select dropdown?

Save to ▾
Show answer
const country = page.getByLabel('Country'); await country.selectOption('India'); // by value or label await country.selectOption({ label: 'India' }); await expect(country).toHaveValue('IN'); // multi-select: selectOption(['a', 'b'])

Frequently asked questions

What does a Playwright Automation Engineer do?

Builds and maintains end-to-end test suites in Playwright with TypeScript or JavaScript. Writes stable tests for web journeys, creates page objects and fixtures, mocks APIs for edge cases and keeps the suite green in CI. Interviews test locator choice, auto-waiting, fixtures, API helpers, debugging with traces and short live coding tasks such as automating a login or checkout flow, plus questions on keeping the suite fast.

Which skills are tested in a Playwright Automation Engineer interview?

Expect questions on Playwright Test runner, TypeScript, Role-based locators, Custom fixtures, Trace debugging. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Playwright Automation Engineer interview?

Revise Playwright Core, Locators and Assertions, Fixtures and Hooks, Network Mocking first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.