Playwright Intermediate Interview Practice
Prepare for your next SDET or QA interview with curated questions and answers.
Answer: Auto-waiting is one of Playwright's strongest features. Before performing actions like click(), fill(), or check(), Playwright automatically waits for the element to become:
- Present in DOM
- Visible
- Stable (not moving/animating)
- Enabled
- Ready to receive action
This removes the need for unnecessary sleep() or manual waits.
Benefits:
- Reduces flaky tests
- Faster execution
- Cleaner code without manual waits
- Enabled
- Better synchronization with dynamic apps
This removes the need for unnecessary sleep() or manual waits.
Example:
await page.locator('#loginBtn').click();Playwright automatically waits until the button is clickable.
❌Selenium style:
Thread.sleep(5000);
driver.findElement(...).click();✅ Playwright handles this automatically.
Answer: Fixtures are reusable setup and teardown resources used by tests.
Playwright provides built-in fixtures like:
- page
- browser
- context
- request
We can also create custom fixtures.
Example:
import { test } from '@playwright/test';
test('homepage test', async ({ page }) => {
await page.goto('https://example.com');
});Here page is a fixture.
Custom Fixture Example:
import { test as base } from '@playwright/test';
export const test = base.extend({
loggedInPage: async ({ page }, use) => {
await page.goto('/login');
await page.fill('#user', 'admin');
await page.fill('#pass', '1234');
await page.click('#login');
await use(page);
}
});Then use:
test('dashboard test', async ({ loggedInPage }) => {
await loggedInPage.click('text=Dashboard');
});Answer:
1. Basic Authentication
Use httpCredentials.
use: {
httpCredentials: {
username: 'admin',
password: 'pass123'
}
}2. Token-Based Authentication
Add headers.
await page.setExtraHTTPHeaders({
Authorization: 'Bearer my_token'
});3. OAuth / UI Login Once
Login once and save session.
await page.goto('/login');
await page.fill('#email', 'user@test.com');
await page.fill('#password', '1234');
await page.click('#login');
await page.context().storageState({ path: 'auth.json' });Reuse:
use: {
storageState: 'auth.json'
}Answer: Cookies and session data are managed through browserContext.
Get Cookies
const cookies = await context.cookies();
console.log(cookies);Add Cookies
await context.addCookies([
{
name: 'token',
value: 'abc123',
domain: 'example.com',
path: '/'
}
]);Clear Cookies
await context.clearCookies();Save Session
await context.storageState({ path: 'state.json' });Reuse later:
use: { storageState: 'state.json' }Answer:
Trace Viewer is a debugging tool that records:
- Test steps
- Screenshots
- DOM snapshots
- Errors
- Console logs
- Network calls
Useful for failed tests.
Enable Trace:
use: {
trace: 'on-first-retry'
}Run tests:
npx playwright testOpen Trace:
npx playwright show-trace trace.zipBenefits:
- Visual debugging
- Understand failure reason
- Time travel debugging
Answer: Use event listeners.
Capture Requests
page.on('request', request => {
console.log('Request:', request.url());
});Capture Responses
page.on('response', response => {
console.log('Response:', response.status(), response.url());
});Example:
await page.goto('https://example.com');Useful for API validation and debugging.
Answer: Playwright provides APIRequestContext.
Example GET Request
import { test, expect } from '@playwright/test';
test('GET users API', async ({ request }) => {
const response = await request.get('https://reqres.in/api/users/2');
expect(response.status()).toBe(200);
const body = await response.json();
expect(body.data.id).toBe(2);
});POST Example
const response = await request.post('/users', {
data: {
name: 'Dhiraj',
job: 'QA'
}
});Benefits:
- API + UI testing in same framework
- Fast execution
- Great for backend validation
Answer: codegen opens a browser and records your actions, generating Playwright code automatically.
Command:
npx playwright codegen https://example.comIt records:
- Clicks
- Fills
- Navigation
- Assertions
Example Generated Code:
await page.goto('https://example.com');
await page.click('text=Login');
await page.fill('#username', 'admin');Benefits:
- Fast script creation
- Good for beginners
- Helps identify locators
Answer: Use external files, fixtures, environment variables, or dynamic generation.
JSON Test Data Example
testdata.json
{
"username": "admin",
"password": "1234"
}Use in test:
const data = require('./testdata.json');
await page.fill('#user', data.username);
await page.fill('#pass', data.password);Using Environment Variables
process.env.USERNAME
process.env.PASSWORDDynamic Data
const email = `user${Date.now()}@test.com`;Best Practices:
- Keep data separate from scripts
- Avoid hardcoded values
- Use unique data for parallel runs
Answer:
Playwright supports parallel execution by default, which helps reduce test execution time. It can run:
- Keep data separate from scripts
- Avoid hardcoded values
- Use unique data for parallel runs
page.expect() (Actually page.waitForEvent() / event expectations)
Playwright commonly uses event expectations like:
const popup = await page.waitForEvent('popup');Or:
const downloadPromise = page.waitForEvent('download');
await page.click('#download');
const download = await downloadPromise;Important Clarification:
There is no common page.expect() assertion API like locator assertions. Usually interviewers want this difference:
| Feature | expect(locator) | page.waitForEvent()/expect event |
|---|---|---|
| Purpose | Assertions | Wait for browser/page events |
| Use Case | Verify UI state | Popup, download, request |
| Example | toHaveText() | waitForEvent('popup') |
Answer:
Playwright supports parallel execution by default, which helps reduce test execution time. It can run:
- Multiple test files in parallel
- Multiple tests in workers within a file
- Across multiple browsers simultaneously
In Config File:
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 4
});This runs tests using 4 parallel workers.
CLI Example:
npx playwright test --workers=4Fully Parallel Inside File:
test.describe.configure({ mode: 'parallel' });Benefits:
- Faster CI execution
- Better scalability
- Supports large test suites
Answer: playwright.config.js is the central configuration file used to control test execution.
Common Options:
| Option | Purpose |
|---|---|
| testDir | Test folder location |
| timeout | Global timeout |
| retries | Retry failed tests |
| workers | Number of parallel workers |
| reporter | HTML, list, JSON reports |
| use | Browser settings |
| projects | Multi-browser setup |
| fullyParallel | Run all tests parallel |
Example:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
timeout: 30000,
retries: 2,
workers: 4,
reporter: 'html',
use: {
headless: true,
screenshot: 'only-on-failure',
video: 'retain-on-failure'
}
});Answer:
Skip Test
test.skip('Skip this test', async ({ page }) => {
});Conditional Skip
test.skip(browserName === 'firefox', 'Not supported');Only Run Specific Test
test.only('Run only this test', async ({ page }) => {
});Tagging Tests
test('login test @smoke', async ({ page }) => {
});Run tagged tests:
npx playwright test --grep @smokeBenefits:
- Smoke testing
- Regression grouping
- CI selective runs
Answer:
File Upload
Use setInputFiles().
await page.setInputFiles('#upload', 'sample.pdf');Multiple files:
await page.setInputFiles('#upload', [
'a.pdf',
'b.pdf'
]);File Download
const downloadPromise = page.waitForEvent('download');
await page.click('#download');
const download = await downloadPromise;
await download.saveAs('report.pdf');Benefits:
- Supports drag-drop upload inputs
- Download validation possible
Answer:
Global Retries
Use setInputFiles().
export default defineConfig({
retries: 2
});If a test fails, Playwright retries 2 times.
CI Based Retries
retries: process.env.CI ? 2 : 0Benefits:
- Helps handle flaky tests
- Improves CI stability
- Integrates with trace on retry for debugging
Answer:
Global Timeout
export default defineConfig({
timeout: 30000
});Each test gets 30 seconds.
Per Test Timeout
test('slow test', async ({ page }) => {
}, { timeout: 60000 });Action Timeout
use: {
actionTimeout: 10000
}Expect Timeout
use: {
expect: {
timeout: 5000
}
}Answer: Use arrays + loops to run same test with multiple data sets.
Example:
const users = [
{ user: 'admin' },
{ user: 'manager' },
{ user: 'guest' }
];
for (const data of users) {
test(`Login test for ${data.user}`, async ({ page }) => {
await page.goto('/login');
await page.fill('#user', data.user);
});
}Benefits:
- Reusable tests
- Data-driven testing
- Easy maintenance
Answer: Use page.route() to intercept requests.
Example:
await page.route('**/api/users', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ name: 'John' },
{ name: 'David' }
])
});
});Now UI receives mocked API data.
Abort Request
await page.route('**/*.png', route => route.abort());Now UI receives mocked API data.
Benefits:
- Test without backend
- Faster tests
- Simulate failures
Answer:
Headless Mode (Default)
npx playwright testRuns browser without UI.
Headed Mode
npx playwright test --headedRuns browser with UI.
Config Example
use: {
headless: false
}Runs browser with UI.
Benefits:
| Mode | Use Case |
|---|---|
| Headless | CI/CD fast execution |
| Headed | Debugging visually |
Answer:
Benefits:
| Feature | Playwright Test Runner | Jest / Mocha |
|---|---|---|
| Browser Automation | Built-in | Needs Selenium/Puppeteer |
| Fixtures | Built-in | Custom setup |
| Parallel Testing | Yes | Limited/manual |
| Parallel Testing | Yes | Limited/manual |
| Screenshots/Videos | Built-in | Plugins needed |
| Trace Viewer | Yes | No |
| Cross Browser | Chromium, Firefox, WebKit | No native support |
| Auto Waiting | Yes | No |
With Playwright:
test('login', async ({ page }) => {
await page.goto('/login');
});With Mocha/Jest you need extra browser library.