Introduction: Why Coding Skills Matter in Playwright Interviews
Playwright coding interview questions are designed to test more than your knowledge of Playwright commands.
In a real QA Automation or SDET role, you must convert requirements into reliable automation code. You may be asked to write a login test, create a reusable Page Object, handle dynamic elements, validate an API, mock a backend response, debug a flaky test, or design a framework that can run hundreds of tests in CI.
That is why interviewers often combine theoretical questions with live coding.
Playwright’s locator model, auto-waiting, fixtures, authentication state, network interception, and parallel execution provide a strong foundation for writing maintainable automation.
This guide focuses on Playwright Coding Interview Questions and Answers using practical TypeScript examples.
The code assumes a standard Playwright Test project:
npm install -D @playwright/test
The goal is not to memorize code. Instead, understand the design decisions behind each solution.
How Playwright Coding Interviews Are Conducted
A Playwright coding interview may follow one of these formats:
| Interview Type | Typical Task |
| Live coding | Write a Playwright test |
| Take-home task | Build a small automation framework |
| Debugging | Fix a flaky test |
| Code review | Improve existing Playwright code |
| Framework design | Create POM/fixtures/configuration |
| API automation | Write API tests and data setup |
| CI/CD | Configure GitHub Actions |
| Migration | Convert Selenium code to Playwright |
For freshers, expect basic browser interactions and locators.
For 2–3 years of experience, expect POM, fixtures, dynamic elements, API testing, and debugging.
For senior SDETs, expect framework architecture, authentication, parallel execution, test isolation, CI/CD, and scalability.
Beginner Playwright Coding Interview Questions
1. Write a Basic Playwright Test
Difficulty: Beginner
Question: Write a Playwright test that opens a page and verifies its title.
Expected Approach
Use the page fixture, navigate with goto(), and validate the title with a web-first assertion.
Complete TypeScript Code
import { test, expect } from ‘@playwright/test’;
test(‘verify page title’, async ({ page }) => {
await page.goto(‘https://example.com’);
await expect(page).toHaveTitle(/Example/);
});
Explanation
The page fixture provides an isolated page for the test. Playwright assertions automatically retry until the condition is satisfied or the assertion timeout is reached.
Interview Tip
Do not use waitForTimeout() before checking the title. Use Playwright’s assertion mechanism.
2. Write a Login Test
Difficulty: Beginner
Question: Automate a login form containing Username, Password, and Login fields.
Expected Approach
Use semantic locators rather than fragile CSS or XPath.
Complete TypeScript Code
import { test, expect } from ‘@playwright/test’;
test(‘user can log in’, async ({ page }) => {
await page.goto(‘/login’);
await page.getByLabel(‘Username’).fill(‘john’);
await page.getByLabel(‘Password’).fill(‘secret’);
await page.getByRole(‘button’, {
name: ‘Login’
}).click();
await expect(page).toHaveURL(/dashboard/);
});
Explanation
getByLabel() is appropriate for form controls, while getByRole() is useful for buttons. Playwright recommends user-facing locators such as role and label because they are generally more resilient than selectors tied to DOM structure.
Interview Tip
Explain why you selected each locator. Interviewers often evaluate locator reasoning rather than syntax alone.
3. Write a Dashboard Validation Test
Difficulty: Beginner
Question: After login, verify that the dashboard heading and Logout button are displayed.
Expected Approach
Use web-first assertions.
Complete TypeScript Code
test(‘dashboard is displayed’, async ({ page }) => {
await page.goto(‘/dashboard’);
await expect(
page.getByRole(‘heading’, { name: ‘Dashboard’ })
).toBeVisible();
await expect(
page.getByRole(‘button’, { name: ‘Logout’ })
).toBeVisible();
});
Explanation
toBeVisible() waits for the expected state instead of checking visibility once.
Interview Tip
Mention that assertions can act as synchronization points.
Locator and Assertion Coding Problems
4. Create a Robust Locator for a Dynamic Button
Difficulty: Beginner
Question: A button has changing CSS classes but always has the accessible name “Submit Order.” Write a stable locator.
Expected Approach
Use the button’s role and accessible name.
Complete TypeScript Code
const submitButton = page.getByRole(‘button’, {
name: ‘Submit Order’
});
await submitButton.click();
Explanation
Avoid selectors such as:
page.locator(‘.css-x92ab-submit-button’).click();
Generated classes can change between builds.
Playwright specifically recommends prioritizing user-facing attributes and explicit contracts over brittle CSS/XPath selectors.
Interview Tip
A good locator should express what the user interacts with, not how the DOM happens to be implemented.
5. Handle a Locator That Matches Multiple Elements
Difficulty: Intermediate
Question: There are five “Edit” buttons on a page. Click Edit for the user named John.
Expected Approach
Locate the user’s row first, then find the Edit button within that row.
Complete TypeScript Code
const userRow = page
.getByRole(‘row’)
.filter({ hasText: ‘John’ });
await userRow
.getByRole(‘button’, { name: ‘Edit’ })
.click();
Explanation
This is better than:
await page
.getByRole(‘button’, { name: ‘Edit’ })
.nth(2)
.click();
The filtered approach identifies the business entity rather than relying on position.
Interview Tip
If an interviewer asks about strict mode, explain that you first make the locator unique instead of blindly using nth().
6. Validate Multiple Elements
Difficulty: Intermediate
Question: Verify that a product list contains exactly five products.
Complete TypeScript Code
const products = page.getByRole(‘listitem’);
await expect(products).toHaveCount(5);
You can then inspect a specific product:
await expect(
products.filter({ hasText: ‘Laptop’ })
).toBeVisible();
Explanation
Using assertions keeps the synchronization behavior inside Playwright’s test model.
Interview Tip
For dynamically loaded lists, avoid calling locator.all() before the list is stable because locator.all() does not wait for elements to appear.
Form, Dropdown, Table, Popup, Iframe, Upload, and Download Tasks
7. Automate a Dropdown
Difficulty: Beginner
Question: Select India from a native HTML <select>.
Complete TypeScript Code
await page.getByLabel(‘Country’).selectOption({
label: ‘India’
});
Or:
await page.getByLabel(‘Country’).selectOption(‘IN’);
Explanation
selectOption() is designed for native <select> controls.
Interview Tip
First determine whether the application uses a native select or a custom JavaScript dropdown. They require different strategies.
8. Automate a Table Row
Difficulty: Intermediate
Question: Find the order belonging to ORD-1001 and click its View button.
Complete TypeScript Code
const orderRow = page
.getByRole(‘row’)
.filter({ hasText: ‘ORD-1001’ });
await orderRow
.getByRole(‘button’, { name: ‘View’ })
.click();
Explanation
The locator is scoped to the relevant row, reducing ambiguity.
Interview Tip
This is a common real-world coding exercise because it tests chaining and filtering.
9. Handle a New Tab or Popup
Difficulty: Intermediate
Question: Click a link that opens a report in a new tab and verify the report page.
Complete TypeScript Code
const popupPromise = page.waitForEvent(‘popup’);
await page.getByRole(‘link’, {
name: ‘Open Report’
}).click();
const popup = await popupPromise;
await popup.waitForLoadState();
await expect(popup).toHaveTitle(/Report/);
Explanation
The event listener is established before the click that creates the popup.
Interview Tip
This demonstrates event-driven synchronization instead of fixed waits.
10. Handle an Iframe
Difficulty: Intermediate
Question: Enter a card number inside a payment iframe.
Complete TypeScript Code
const paymentFrame = page.frameLocator(‘#payment-frame’);
await paymentFrame
.getByLabel(‘Card Number’)
.fill(‘4111111111111111’);
await paymentFrame
.getByRole(‘button’, { name: ‘Pay’ })
.click();
Explanation
frameLocator() lets you continue using locator strategies inside the iframe.
Interview Tip
Remember that iframe content belongs to a separate document context.
11. Upload a File
Difficulty: Beginner
Question: Upload resume.pdf.
Complete TypeScript Code
await page
.getByLabel(‘Upload Resume’)
.setInputFiles(‘tests/data/resume.pdf’);
Explanation
setInputFiles() directly interacts with the file input.
Interview Tip
Do not unnecessarily automate the operating-system file picker.
12. Download and Validate a File
Difficulty: Intermediate
Question: Click Download and save the resulting PDF.
Complete TypeScript Code
const downloadPromise = page.waitForEvent(‘download’);
await page.getByRole(‘button’, {
name: ‘Download’
}).click();
const download = await downloadPromise;
await download.saveAs(‘downloads/report.pdf’);
Explanation
The download event is captured before triggering the action.
Interview Tip
For robust tests, validate meaningful properties such as the suggested filename or file contents, not only that a download occurred.
Intermediate Playwright Automation Coding Questions
13. Create a Page Object Model
Difficulty: Intermediate
Question: Create a reusable Login Page Object.
Expected Approach
Encapsulate locators and business actions in a class.
Complete TypeScript Code
import type { Page, Locator } from ‘@playwright/test’;
export class LoginPage {
readonly username: Locator;
readonly password: Locator;
readonly loginButton: Locator;
constructor(private readonly page: Page) {
this.username = page.getByLabel(‘Username’);
this.password = page.getByLabel(‘Password’);
this.loginButton = page.getByRole(‘button’, {
name: ‘Login’
});
}
async login(username: string, password: string) {
await this.username.fill(username);
await this.password.fill(password);
await this.loginButton.click();
}
}
Usage:
const loginPage = new LoginPage(page);
await loginPage.login(‘john’, ‘secret’);
Explanation
POM separates test intent from UI implementation.
Interview Tip
A senior answer should mention that POM should expose business actions rather than simply wrapping every Playwright command.
14. Create a Custom Fixture
Difficulty: Advanced
Question: Create a fixture that provides a reusable LoginPage.
Complete TypeScript Code
import {
test as base,
expect
} from ‘@playwright/test’;
import { LoginPage } from ‘./pages/LoginPage’;
type Fixtures = {
loginPage: LoginPage;
};
export const test = base.extend<Fixtures>({
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await use(loginPage);
}
});
export { expect };
Test:
import { test, expect } from ‘./fixtures’;
test(‘login’, async ({ loginPage, page }) => {
await page.goto(‘/login’);
await loginPage.login(‘john’, ‘secret’);
await expect(page).toHaveURL(/dashboard/);
});
Explanation
Fixtures establish reusable test dependencies. Playwright fixtures are isolated and can be composed from other fixtures.
Interview Tip
Understand fixture scope. A worker-scoped fixture is appropriate when setup should happen once per worker rather than once per test.
Authentication and Storage State Coding Questions
15. Save and Reuse Authentication State
Difficulty: Advanced
Question: How would you authenticate once and reuse the session across tests?
Expected Approach
Perform login in a setup test, save storageState, and configure subsequent tests to use it.
Complete TypeScript Code
import { test as setup } from ‘@playwright/test’;
setup(‘authenticate’, async ({ page }) => {
await page.goto(‘/login’);
await page.getByLabel(‘Username’).fill(
process.env.TEST_USERNAME!
);
await page.getByLabel(‘Password’).fill(
process.env.TEST_PASSWORD!
);
await page.getByRole(‘button’, {
name: ‘Login’
}).click();
await page.waitForURL(/dashboard/);
await page.context().storageState({
path: ‘playwright/.auth/user.json’
});
});
Then:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
use: {
storageState: ‘playwright/.auth/user.json’
}
});
Explanation
Reusing authenticated state avoids repeating UI login for every test. Playwright recommends keeping authentication-state files out of source control because they can contain sensitive cookies and headers.
Interview Tip
Always mention .gitignore and secret protection.
16. How Would You Handle Authentication Per Parallel Worker?
Difficulty: Advanced
Question: Tests modify server-side state, so one shared account creates conflicts. What would you do?
Expected Approach
Use a unique account per worker and generate or acquire worker-specific authentication state.
Complete TypeScript Pattern
import { test as base } from ‘@playwright/test’;
type Fixtures = {
account: {
username: string;
password: string;
};
};
export const test = base.extend<
Fixtures,
{ workerAccount: Fixtures[‘account’] }
>({
workerAccount: [async ({}, use, workerInfo) => {
const account = {
username: `worker-${workerInfo.workerIndex}`,
password: ‘test-password’
};
await use(account);
}, { scope: ‘worker’ }],
account: async ({ workerAccount }, use) => {
await use(workerAccount);
}
});
Explanation
When tests modify server-side state, Playwright’s authentication guidance recommends approaches such as one account per parallel worker rather than sharing a single account across conflicting tests.
Interview Tip
The important concept is test-data isolation, not simply generating random usernames.
API Testing Coding Questions
17. Write an API Test
Difficulty: Intermediate
Question: Send a POST request and verify the response.
Complete TypeScript Code
import { test, expect } from ‘@playwright/test’;
test(‘create user through API’, async ({ request }) => {
const response = await request.post(‘/api/users’, {
data: {
name: ‘John’,
role: ‘tester’
}
});
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body.name).toBe(‘John’);
expect(body.role).toBe(‘tester’);
});
Explanation
The Playwright request fixture provides an API request context suitable for API testing and test-data setup.
Interview Tip
Mention that API setup is often faster and more deterministic than creating data through the UI.
18. Create Test Data Through an API Before a UI Test
Difficulty: Advanced
Question: Create an order using an API and verify it through the UI.
Complete TypeScript Code
test(‘order appears in UI’, async ({
request,
page
}) => {
const response = await request.post(‘/api/orders’, {
data: {
productId: 101,
quantity: 2
}
});
expect(response.ok()).toBeTruthy();
const order = await response.json();
await page.goto(`/orders/${order.id}`);
await expect(
page.getByText(‘Order created’)
).toBeVisible();
});
Explanation
This hybrid approach keeps UI tests focused on UI behavior while using APIs for efficient setup.
Interview Tip
Explain the test boundary: API creates the precondition; UI validates the user-facing behavior.
Network Mocking and Interception Coding Tasks
19. Mock an API Response
Difficulty: Advanced
Question: The /api/products endpoint is unstable. Mock it so the UI always receives one product.
Complete TypeScript Code
test(‘display mocked product’, async ({ page }) => {
await page.route(‘**/api/products’, async route => {
await route.fulfill({
status: 200,
contentType: ‘application/json’,
body: JSON.stringify({
products: [
{
id: 1,
name: ‘Laptop’
}
]
})
});
});
await page.goto(‘/products’);
await expect(
page.getByText(‘Laptop’)
).toBeVisible();
});
Explanation
Playwright can intercept and modify HTTP and HTTPS traffic, including XHR and fetch requests. Network routing can be used for deterministic API mocking.
Interview Tip
Register the route before the request is made.
20. Simulate a Server Error
Difficulty: Scenario-Based
Question: How would you test the UI behavior when the backend returns HTTP 500?
Complete TypeScript Code
await page.route(‘**/api/orders’, async route => {
await route.fulfill({
status: 500,
contentType: ‘application/json’,
body: JSON.stringify({
error: ‘Internal server error’
})
});
});
await page.goto(‘/orders’);
await expect(
page.getByRole(‘alert’)
).toContainText(‘Unable to load orders’);
Explanation
This allows you to test failure handling without depending on an actual backend outage.
Interview Tip
Senior candidates should mention that mocks must be maintained carefully so they do not diverge from the real API contract.
Data-Driven Playwright Coding Questions
21. Write a Data-Driven Login Test
Difficulty: Intermediate
Question: Test multiple login scenarios.
Complete TypeScript Code
import { test, expect } from ‘@playwright/test’;
const users = [
{
username: ‘valid-user’,
password: ‘valid-pass’,
expected: ‘success’
},
{
username: ‘invalid-user’,
password: ‘wrong-pass’,
expected: ‘failure’
}
];
for (const user of users) {
test(`login – ${user.expected}`, async ({ page }) => {
await page.goto(‘/login’);
await page.getByLabel(‘Username’)
.fill(user.username);
await page.getByLabel(‘Password’)
.fill(user.password);
await page.getByRole(‘button’, {
name: ‘Login’
}).click();
if (user.expected === ‘success’) {
await expect(page).toHaveURL(/dashboard/);
} else {
await expect(
page.getByRole(‘alert’)
).toBeVisible();
}
});
}
Explanation
The test data is separated from the interaction logic.
Interview Tip
For larger projects, consider Playwright projects, fixtures, or parameterization patterns rather than generating enormous numbers of test cases in one file.
Debugging and Flaky-Test Coding Scenarios
22. Fix a Test Using waitForTimeout()
Difficulty: Scenario-Based
Question: Improve this code:
await page.click(‘#save’);
await page.waitForTimeout(5000);
expect(await page.locator(‘.success’).isVisible()).toBeTruthy();
Expected Approach
Replace fixed waiting with a web-first assertion.
Complete TypeScript Code
await page.getByRole(‘button’, {
name: ‘Save’
}).click();
await expect(
page.getByRole(‘status’)
).toContainText(‘Saved’);
Explanation
Playwright’s locators and assertions are designed around auto-waiting and retryability.
Interview Tip
Say: “I wait for a meaningful application condition, not an arbitrary amount of time.”
23. Debug a CI-Only Failure
Difficulty: Scenario-Based
Question: A test passes locally but fails in CI. Modify the configuration to collect useful diagnostics.
Complete TypeScript Code
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
retries: process.env.CI ? 1 : 0,
use: {
trace: ‘retain-on-failure’,
screenshot: ‘only-on-failure’,
video: ‘retain-on-failure’
}
});
Explanation
Artifacts can reveal whether the failure is caused by timing, rendering, navigation, network behavior, or test-data problems.
Interview Tip
Do not immediately increase the timeout. Diagnose first.
24. Configure Parallel Workers
Difficulty: Intermediate
Question: Configure Playwright to use four workers in CI.
Complete TypeScript Code
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
workers: process.env.CI ? 4 : undefined
});
Explanation
Workers allow tests to execute concurrently. However, excessive workers can cause resource contention and failures. Playwright’s CI guidance warns that increasing workers beyond the runner’s available resources can cause unnecessary timeouts and failures.
Interview Tip
Always discuss CPU, memory, application capacity, and test isolation when discussing worker count.
Parallel Execution and Sharding
25. Write a Sharding Command
Difficulty: Advanced
Question: Your regression suite is too slow. How would you run one of four shards?
Complete TypeScript/CLI Example
npx playwright test –shard=1/4
Other jobs execute:
npx playwright test –shard=2/4
npx playwright test –shard=3/4
npx playwright test –shard=4/4
Explanation
Workers parallelize execution within a CI job. Shards split the suite between separate machines or CI jobs. Playwright supports sharding specifically for distributing tests across machines.
Interview Tip
Explain the difference clearly. This is a common Senior SDET coding question.
CI/CD and Configuration Coding Questions
26. Create a GitHub Actions Playwright Workflow
Difficulty: Intermediate
Question: Write a basic CI workflow.
Complete YAML
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
– name: Checkout
uses: actions/checkout@v4
– name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
– name: Install dependencies
run: npm ci
– name: Install browsers
run: npx playwright install –with-deps
– name: Run tests
run: npx playwright test
– name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
Explanation
The pipeline establishes a repeatable environment, installs browsers, executes tests, and preserves the report after failures.
Interview Tip
Know why if: always() matters. Without it, a failed test step can prevent artifact upload.
Screenshots, Traces, and Reporting Coding Questions
27. Configure Failure Diagnostics
Difficulty: Intermediate
Question: Capture screenshots and traces only when tests fail.
Complete TypeScript Code
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
use: {
screenshot: ‘only-on-failure’,
trace: ‘retain-on-failure’,
video: ‘retain-on-failure’
}
});
Explanation
This provides useful failure evidence without storing large artifacts for every successful test.
Interview Tip
Explain the trade-off between observability and CI storage.
Advanced Playwright Coding Interview Questions
28. Build a Reusable Framework Structure
Difficulty: Advanced
Question: Design a scalable Playwright framework.
Expected Approach
Separate responsibilities.
Example Structure
tests/
pages/
components/
fixtures/
api/
data/
utils/
auth/
config/
Example API Client
import type { APIRequestContext } from ‘@playwright/test’;
export class UserApi {
constructor(
private readonly request: APIRequestContext
) {}
async createUser(name: string) {
return this.request.post(‘/api/users’, {
data: { name }
});
}
}
Explanation
This architecture keeps API operations, UI objects, fixtures, and test cases separate.
Interview Tip
A framework is not good because it has many folders. It is good when its boundaries make change easier.
29. Create a Reusable API Fixture
Difficulty: Advanced
Question: Make a reusable UserApi available to tests.
Complete TypeScript Code
import {
test as base,
expect
} from ‘@playwright/test’;
import { UserApi } from ‘./api/UserApi’;
type Fixtures = {
userApi: UserApi;
};
export const test = base.extend<Fixtures>({
userApi: async ({ request }, use) => {
await use(new UserApi(request));
}
});
export { expect };
Usage:
test(‘create user’, async ({ userApi }) => {
const response =
await userApi.createUser(‘John’);
expect(response.ok()).toBeTruthy();
});
Explanation
This demonstrates dependency injection through Playwright fixtures.
Interview Tip
Fixtures are especially useful when a dependency needs controlled setup and teardown.
30. Design a Multi-Role Test
Difficulty: Advanced
Question: An admin and normal user must interact in the same scenario. Implement isolated contexts.
Complete TypeScript Code
test(‘admin and user workflow’, async ({ browser }) => {
const adminContext = await browser.newContext({
storageState: ‘playwright/.auth/admin.json’
});
const userContext = await browser.newContext({
storageState: ‘playwright/.auth/user.json’
});
const adminPage = await adminContext.newPage();
const userPage = await userContext.newPage();
await adminPage.goto(‘/admin’);
await userPage.goto(‘/dashboard’);
await expect(
adminPage.getByRole(‘heading’, {
name: ‘Admin Dashboard’
})
).toBeVisible();
await expect(
userPage.getByRole(‘heading’, {
name: ‘Dashboard’
})
).toBeVisible();
await adminContext.close();
await userContext.close();
});
Explanation
Browser contexts provide isolated sessions, making multi-user scenarios practical. Playwright’s authentication documentation also demonstrates separate authenticated contexts for multiple roles.
Interview Tip
This is an excellent Senior SDET coding problem because it tests authentication, isolation, and context management together.
Scenario-Based Playwright Coding Questions
31. Fix a Brittle Locator
Difficulty: Scenario-Based
Question: Replace this locator:
await page.locator(
‘#app > div:nth-child(2) > div:nth-child(3) > button’
).click();
Expected Approach
Find a stable semantic or testing contract.
Complete TypeScript Code
await page.getByRole(‘button’, {
name: ‘Save’
}).click();
Explanation
Long CSS paths depend heavily on DOM structure. Playwright recommends user-facing locators and explicit test IDs instead.
Interview Tip
Explain why the new locator survives unrelated DOM changes.
32. Fix a Strict Mode Violation
Difficulty: Scenario-Based
Question: This fails because there are multiple Delete buttons:
await page.getByRole(‘button’, {
name: ‘Delete’
}).click();
Solution
const account = page
.getByRole(‘row’)
.filter({ hasText: ‘John Smith’ });
await account.getByRole(‘button’, {
name: ‘Delete’
}).click();
Explanation
The locator is scoped to the business entity.
Interview Tip
Avoid using .nth(0) unless ordering is intentionally part of the requirement.
33. Debug a Detached Element Scenario
Difficulty: Scenario-Based
Question: A React component is re-rendered after every save. How do you make the test resilient?
Expected Approach
Use a Locator rather than storing a DOM element reference.
Complete TypeScript Code
const saveButton = page.getByRole(‘button’, {
name: ‘Save’
});
await saveButton.click();
await expect(
page.getByRole(‘status’)
).toContainText(‘Saved’);
await saveButton.click();
Explanation
Playwright locators resolve the current matching element when used, which helps when the DOM changes between actions.
Interview Tip
Explain locator re-resolution rather than describing this simply as “Playwright waits.”
Real-World Playwright Framework Coding Challenge
34. Build a Mini E-Commerce Automation Framework
Difficulty: Senior SDET
Question: Design an automation framework that tests:
- Login.
- Product search.
- Add to cart.
- Checkout.
- Order validation.
- API-based test data.
- CI execution.
Expected Approach
Use:
tests/
pages/
api/
fixtures/
data/
auth/
playwright.config.ts
Product Page
import type { Page } from ‘@playwright/test’;
export class ProductPage {
constructor(private readonly page: Page) {}
async search(product: string) {
await this.page
.getByPlaceholder(‘Search products’)
.fill(product);
await this.page.getByRole(‘button’, {
name: ‘Search’
}).click();
}
async addToCart(product: string) {
const card = this.page
.getByRole(‘listitem’)
.filter({ hasText: product });
await card.getByRole(‘button’, {
name: ‘Add to cart’
}).click();
}
}
API Data Setup
export async function createProduct(
request: APIRequestContext,
name: string
) {
return request.post(‘/api/products’, {
data: { name }
});
}
Test
test(‘buy product’, async ({
page,
request
}) => {
const response = await createProduct(
request,
‘Laptop’
);
const product = await response.json();
await page.goto(‘/products’);
const productCard = page
.getByRole(‘listitem’)
.filter({ hasText: product.name });
await productCard
.getByRole(‘button’, {
name: ‘Add to cart’
})
.click();
await page.getByRole(‘link’, {
name: ‘Cart’
}).click();
await expect(
page.getByText(product.name)
).toBeVisible();
});
Explanation
This combines POM, API setup, semantic locators, assertions, and UI validation.
Interview Tip
For an architect-level answer, discuss test isolation, authentication, data cleanup, parallel execution, CI artifacts, and environment configuration.
Common Playwright Coding Interview Mistakes
Mistake 1: Using waitForTimeout()
Avoid:
await page.waitForTimeout(5000);
Prefer a condition:
await expect(
page.getByRole(‘status’)
).toBeVisible();
Mistake 2: Choosing brittle locators
Avoid deeply nested CSS and XPath selectors. Playwright recommends user-facing locators and explicit test contracts.
Mistake 3: Ignoring async/await
Playwright APIs are asynchronous.
Use:
await page.goto(‘/’);
await page.getByRole(‘button’).click();
not:
page.goto(‘/’);
page.getByRole(‘button’).click();
Playwright’s own best practices recommend TypeScript linting such as no-floating-promises to catch missing awaits.
Mistake 4: Overusing force: true
force: true can hide genuine UI/actionability problems.
Mistake 5: Sharing mutable test data
Parallel tests need isolated accounts, records, files, and other mutable state.
Mistake 6: Putting everything in POM
Do not put API clients, random utilities, environment configuration, and unrelated business logic into every page class.
Mistake 7: Treating retries as a fix
Retries can reduce transient CI failures, but persistent flakiness needs investigation.
Mistake 8: Committing authentication state
Authentication files can contain sensitive cookies and headers. Playwright recommends keeping the .auth directory out of source control.
Playwright Coding Interview Preparation Roadmap
Freshers
Master:
- TypeScript basics
- async/await
- test()
- expect()
- page
- goto()
- click()
- fill()
- Locators
- Basic assertions
- Forms
- Screenshots
Practice the beginner Playwright Coding Questions first.
2–3 Years Experience
Add:
- POM
- Fixtures
- Dynamic locators
- Tables
- Frames
- Popups
- Upload/download
- API testing
- Authentication
- Network mocking
- Test data
4–5 Years Experience
Focus on:
- Custom fixtures
- Worker fixtures
- API/UI hybrid testing
- Parallel execution
- Sharding
- CI/CD
- Docker
- Authentication architecture
- Flaky-test debugging
- Framework organization
Senior SDET
Be prepared to code:
- Multi-user workflows
- Worker-specific authentication
- API clients
- Custom fixtures
- Network mocks
- Data factories
- CI matrices
- Sharded execution
- Reporting configuration
- Failure diagnostics
Automation Architect
Expect design questions involving:
- Framework scalability
- Dependency boundaries
- Test isolation
- Authentication architecture
- Test-data services
- CI cost
- Browser strategy
- Observability
- Migration from Selenium
- Long-term maintainability
Playwright Coding Interview Checklist
Before your interview, make sure you can code these without documentation:
- Basic Playwright test
- Login flow
- Dashboard validation
- Role-based locator
- Filtered table row
- Dynamic element
- Strict-mode fix
- Form submission
- Dropdown
- Iframe
- Popup
- File upload
- File download
- POM class
- Custom fixture
- Authentication setup
- storageState
- API request
- API test-data setup
- Network mock
- Multiple browser contexts
- Data-driven tests
- Parallel workers
- Test sharding
- CI workflow
- Trace configuration
- Screenshot configuration
- Flaky-test debugging
FAQs: Playwright Coding Interview Questions
What are common Playwright coding interview questions?
Common tasks include writing login tests, creating robust locators, handling dynamic elements, building Page Objects, creating fixtures, performing API requests, mocking network responses, managing authentication, and debugging flaky tests.
Is TypeScript required for Playwright coding interviews?
Not always. Playwright supports multiple programming languages, but TypeScript is especially common in Playwright automation roles. If the job description specifies TypeScript, expect hands-on TypeScript coding.
What should freshers practice?
Freshers should practice navigation, locators, actions, assertions, forms, dropdowns, popups, frames, uploads, and downloads.
What should experienced SDETs practice?
Experienced candidates should focus on POM, fixtures, authentication, API testing, network mocking, test-data management, parallel execution, CI/CD, and framework architecture.
What is the most important coding concept in Playwright?
There is no single answer, but reliable locator design and synchronization are foundational. Playwright describes locators as central to its auto-waiting and retryability model.
How do I prepare for Playwright live coding?
Build small applications or use a public demo application and practice implementing complete workflows without copying code. Focus on explaining your choices while coding.
What is a good Playwright coding challenge for Senior SDET interviews?
A strong challenge combines POM, custom fixtures, authentication, API setup, test-data isolation, network mocking, parallel execution, and CI/CD.
How should I debug a Playwright coding problem?
Start with the failure symptom. Check the locator, application state, synchronization, browser context, network, test data, and environment. Use traces, screenshots, logs, and Playwright Inspector when appropriate.
Should I use CSS or XPath in coding interviews?
You can use them when appropriate, but demonstrate that you know how to select stable user-facing locators. Playwright recommends role, label, text, placeholder, and test-ID strategies before brittle DOM-dependent selectors.
How do I demonstrate senior-level Playwright knowledge?
Do not just produce working code. Explain isolation, maintainability, scalability, failure diagnostics, security, CI performance, and test-data strategy.
