SDET QA LogoSDET QA

Playwright Intermediate Interview Practice

Prepare for your next SDET or QA interview with curated questions and answers.

Q1: What is auto-waiting in Playwright and how does it improve stability?

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:

javascript
await page.locator('#loginBtn').click();

Playwright automatically waits until the button is clickable.

❌Selenium style:

javascript
Thread.sleep(5000);
driver.findElement(...).click();

✅ Playwright handles this automatically.

Q2: What is fixtures and how are they used in Playwright Test Runner?

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:

javascript
import { test } from '@playwright/test';

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

Here page is a fixture.

Custom Fixture Example:

javascript
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:

javascript
test('dashboard test', async ({ loggedInPage }) => {
   await loggedInPage.click('text=Dashboard');
});
Q3: How to handle authentication (basic, token-based, or OAuth) in Playwright?

Answer:

1. Basic Authentication

Use httpCredentials.

javascript
use: {
  httpCredentials: {
    username: 'admin',
    password: 'pass123'
  }
}

2. Token-Based Authentication

Add headers.

javascript
await page.setExtraHTTPHeaders({
  Authorization: 'Bearer my_token'
});

3. OAuth / UI Login Once

Login once and save session.

javascript
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:

javascript
use: {
  storageState: 'auth.json'
}
Q4: How to manage cookies and sessions?

Answer: Cookies and session data are managed through browserContext.

Get Cookies

javascript
const cookies = await context.cookies();
console.log(cookies);

Add Cookies

javascript
await context.addCookies([
 {
   name: 'token',
   value: 'abc123',
   domain: 'example.com',
   path: '/'
 }
]);

Clear Cookies

javascript
await context.clearCookies();

Save Session

javascript
await context.storageState({ path: 'state.json' });

Reuse later:

javascript
use: { storageState: 'state.json' }
Q5: What is the Playwright Trace Viewer and how is it used?

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:

javascript
use: {
   trace: 'on-first-retry'
}

Run tests:

Bash
npx playwright test

Open Trace:

Bash
npx playwright show-trace trace.zip

Benefits:

  • Visual debugging
  • Understand failure reason
  • Time travel debugging
Q6: How to capture network requests and responses in Playwright?

Answer: Use event listeners.

Capture Requests

javascript
page.on('request', request => {
   console.log('Request:', request.url());
});

Capture Responses

javascript
page.on('response', response => {
   console.log('Response:', response.status(), response.url());
});

Example:

javascript
await page.goto('https://example.com');

Useful for API validation and debugging.

Q7: How to test REST APIs using Playwright?

Answer: Playwright provides APIRequestContext.

Example GET Request

javascript
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

javascript
const response = await request.post('/users', {
   data: {
      name: 'Dhiraj',
      job: 'QA'
   }
});

Benefits:

  • API + UI testing in same framework
  • Fast execution
  • Great for backend validation
Q8: What is npx playwright codegen and how does it help?

Answer: codegen opens a browser and records your actions, generating Playwright code automatically.

Command:

Bash
npx playwright codegen https://example.com

It records:

  • Clicks
  • Fills
  • Navigation
  • Assertions

Example Generated Code:

javascript
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
Q9: How to manage test data in Playwright tests?

Answer: Use external files, fixtures, environment variables, or dynamic generation.

JSON Test Data Example

testdata.json

JSON
{
  "username": "admin",
  "password": "1234"
}

Use in test:

javascript
const data = require('./testdata.json');

await page.fill('#user', data.username);
await page.fill('#pass', data.password);

Using Environment Variables

javascript
process.env.USERNAME
process.env.PASSWORD

Dynamic Data

javascript
const email = `user${Date.now()}@test.com`;

Best Practices:

  • Keep data separate from scripts
  • Avoid hardcoded values
  • Use unique data for parallel runs
Q10: What is the difference between expect(locator) and page.expect()?

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:

javascript
const popup = await page.waitForEvent('popup');

Or:

javascript
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:

Featureexpect(locator)page.waitForEvent()/expect event
PurposeAssertionsWait for browser/page events
Use CaseVerify UI statePopup, download, request
ExampletoHaveText()waitForEvent('popup')
Q11: How to run tests in parallel in Playwright?

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:

javascript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  workers: 4
});

This runs tests using 4 parallel workers.

CLI Example:

Bash
npx playwright test --workers=4

Fully Parallel Inside File:

javascript
test.describe.configure({ mode: 'parallel' });

Benefits:

  • Faster CI execution
  • Better scalability
  • Supports large test suites
Q12: What configuration options are available in playwright.config.js?

Answer: playwright.config.js is the central configuration file used to control test execution.

Common Options:

OptionPurpose
testDirTest folder location
timeoutGlobal timeout
retriesRetry failed tests
workersNumber of parallel workers
reporterHTML, list, JSON reports
useBrowser settings
projectsMulti-browser setup
fullyParallelRun all tests parallel

Example:

javascript
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'
  }
});
Q13: How do you skip or tag tests for selective execution?

Answer:

Skip Test

javascript
test.skip('Skip this test', async ({ page }) => {
});

Conditional Skip

javascript
test.skip(browserName === 'firefox', 'Not supported');

Only Run Specific Test

javascript
test.only('Run only this test', async ({ page }) => {
});

Tagging Tests

javascript
test('login test @smoke', async ({ page }) => {
});

Run tagged tests:

Bash
npx playwright test --grep @smoke

Benefits:

  • Smoke testing
  • Regression grouping
  • CI selective runs
Q14: How do you handle file uploads and downloads?

Answer:

File Upload

Use setInputFiles().

javascript
await page.setInputFiles('#upload', 'sample.pdf');

Multiple files:

javascript
await page.setInputFiles('#upload', [
  'a.pdf',
  'b.pdf'
]);

File Download

javascript
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
Q15: How do you set up retries for failed tests?

Answer:

Global Retries

Use setInputFiles().

javascript
export default defineConfig({
  retries: 2
});

If a test fails, Playwright retries 2 times.

CI Based Retries

javascript
retries: process.env.CI ? 2 : 0

Benefits:

  • Helps handle flaky tests
  • Improves CI stability
  • Integrates with trace on retry for debugging
Q16: Integrates with trace on retry

Answer:

Global Timeout

javascript
export default defineConfig({
  timeout: 30000
});

Each test gets 30 seconds.

Per Test Timeout

javascript
test('slow test', async ({ page }) => {
}, { timeout: 60000 });

Action Timeout

javascript
use: {
  actionTimeout: 10000
}

Expect Timeout

javascript
use: {
  expect: {
    timeout: 5000
  }
}
Q17: How do you parameterize tests in Playwright?

Answer: Use arrays + loops to run same test with multiple data sets.

Example:

javascript
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
Q18: How do you handle API mocking or stubbing?

Answer: Use page.route() to intercept requests.

Example:

javascript
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

javascript
await page.route('**/*.png', route => route.abort());

Now UI receives mocked API data.

Benefits:

  • Test without backend
  • Faster tests
  • Simulate failures
Q19: How can you run Playwright tests headless or headed?

Answer:

Headless Mode (Default)

javascript
npx playwright test

Runs browser without UI.

Headed Mode

javascript
npx playwright test --headed

Runs browser with UI.

Config Example

javascript
use: {
  headless: false
}

Runs browser with UI.

Benefits:

ModeUse Case
HeadlessCI/CD fast execution
HeadedDebugging visually
Q20: What's the difference between Playwright Test Runner and Jest/Mocha?

Answer:

Benefits:

FeaturePlaywright Test RunnerJest / Mocha
Browser AutomationBuilt-inNeeds Selenium/Puppeteer
FixturesBuilt-inCustom setup
Parallel TestingYesLimited/manual
Parallel TestingYesLimited/manual
Screenshots/VideosBuilt-inPlugins needed
Trace ViewerYesNo
Cross BrowserChromium, Firefox, WebKitNo native support
Auto WaitingYesNo

With Playwright:

javascript
test('login', async ({ page }) => {
  await page.goto('/login');
});

With Mocha/Jest you need extra browser library.