Top 25+ Playwright and JavaScript Interview Questions for QA Testers (2025 Guide)
In today’s fast-paced software development world, automation testing has become an essential skill for QA professionals. As companies shift towards modern frameworks, Playwright is gaining popularity for its powerful browser automation capabilities. Combine that with JavaScript, and you have a robust testing stack that many organizations now prefer.
Whether you're a fresher or an experienced QA engineer preparing for your next interview, this blog covers top Playwright and JavaScript interview questions, complete with real-world context to help you ace your QA interview in 2025.
🔍 Why Playwright + Java/JavaScript is a Powerful QA Combo?
While Playwright is primarily used with JavaScript or TypeScript, it's now compatible with multiple languages including Java. QA teams are leveraging Java’s mature ecosystem alongside Playwright’s modern browser control to:
-
Automate web UI tests across Chromium, Firefox, and WebKit.
-
Handle real-world scenarios like file uploads, authentication, geolocation.
-
Integrate easily with CI/CD pipelines and reporting tools.
📋 Playwright & Java Interview Questions (With Contextual Tips)
✅ Basic Questions (Freshers / 0-2 Years Experience)
-
What is Playwright, and how does it differ from Selenium?
Playwright is a Node.js library for browser automation by Microsoft. Unlike Selenium, it supports modern web features like single-page apps better and handles multiple browser contexts efficiently. Unlike Cypress, it supports multiple browser engines (Chromium, Firefox, WebKit), multiple tabs, and native iframe handling.
Tip: Talk about multi-browser support, auto-waiting, faster execution, and better developer experience. -
Can Playwright be used with Java? How?
Yes. Playwright for Java is officially supported via Maven. Talk about setup usingplaywright-java
package. -
How do you launch a browser and open a page using Playwright in Java?
Playwright playwright = Playwright.create(); Browser browser = playwright.chromium().launch(); Page page = browser.newPage(); page.navigate("https://example.com");
-
What are auto-waiting and retry-ability in Playwright?
Playwright is known for being flaky-test resistant due to its built-in auto-waiting and retry-ability mechanisms. These features reduce the need for manual waits (setTimeout
,waitForTimeout
, etc.) and make your tests faster and more stable.🔄 Auto-Waiting (Built-in Waits)
Playwright automatically waits for elements to be:
-
Visible
-
Enabled
-
Attached to the DOM
✅ Example:
await page.click('button#submit');
No need to manually wait — Playwright will wait until the button is clickable.
🔁 Retry-ability (Smart Retrying)
Playwright retries actions and assertions until they succeed or timeout.
✅ Example:
await expect(page.locator('#status')).toHaveText('Success');
It keeps checking until the text is
"Success"
(within timeout), no manual loops needed.📌 Benefit:
-
No
waitForTimeout()
needed -
Reduces flaky tests
-
Cleaner & smarter test scripts
Let me know if you want real test case examples!
-
Difference between
page.waitForSelector()
andpage.locator().waitFor()
waitForSelector()
is older and returns an element handle.locator().waitFor()
is newer and used with Locator API (recommended in newer versions).How do you validate title and URL in Playwright?
expect(await page.title()).toBe('Expected Title'); expect(page.url()).toBe('https://expected-url.com');
How do you launch a browser and open a page in Playwright using JavaScript?
const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com'); await browser.close(); })();
-
What is the difference between
page.click()
andlocator.click()
?🔹
page.click(selector)
-
Works with string-based selectors (e.g., CSS, XPath).
-
Executes the click immediately after resolving the selector.
-
No smart waiting — only basic checks.
-
More prone to flakiness on dynamic UIs.
✅ Example:
await page.click('#submitBtn');
🔹
locator.click()
-
Uses Locator API, which is more robust and reliable.
-
Automatically waits for the element to be:
-
Attached to the DOM
Visible
-
Enabled
-
-
Supports chaining and filtering.
-
Recommended for modern, stable automation.
✅ Example:
const button = page.locator('#submitBtn'); await button.click();
🆚 Key Differences
Feature page.click()
locator.click()
Syntax Selector string Locator object Auto-waiting Limited ✅ Yes (better handling) Retry-ability ❌ No ✅ Yes Reliability Moderate ✅ High Chaining support ❌ No ✅ Yes ✅ Recommendation:
➡️ Use
**locator.click()**
for more reliable, stable, and maintainable tests. -
How do you wait for element visibility or clickability?
await page.locator('#element').waitFor({ state: 'visible' }); await page.click('#element');
-
How does Playwright handle synchronization differently than Selenium?
Playwright and Selenium are both browser automation tools, but Playwright handles synchronization more intelligently and automatically than Selenium.
Here's a short and clear comparison:
🔹 Synchronization in Selenium
-
Manual waits are often needed, like:
-
Thread.sleep()
-
WebDriverWait
+ExpectedConditions
-
-
You have to explicitly wait for elements to load, be visible, or clickable.
-
Tests may become flaky if timing is not handled well.
❌ Example in Selenium:
WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("loginBtn"))); driver.findElement(By.id("loginBtn")).click();
🔹 Synchronization in Playwright
-
Auto-waits by default for:
-
Element to be attached to DOM
-
Element to be visible
-
Element to be enabled and stable
-
-
No need to add explicit waits for most actions.
-
More stable and reliable for dynamic web apps.
✅ Example in Playwright:
await page.locator('#loginBtn').click(); // Auto-waits under the hood
🆚 Summary Table
Feature Selenium Playwright Default auto-waiting ❌ No ✅ Yes Manual wait needed often ✅ Yes ❌ Rarely Flakiness in dynamic UIs ❌ High chance ✅ Reduced Easy handling of async UI ❌ Complex ✅ Built-in ✅ Verdict:
Playwright is better at handling synchronization automatically, reducing the need for manual waits and making your test scripts cleaner and more reliable.
-
-
How do you capture a screenshot in Playwright using Java?
To capture a screenshot in Playwright using Java, you can use the
screenshot()
method provided by thePage
class.✅ Example Code: Capture Full Page Screenshot
import com.microsoft.playwright.*; public class ScreenshotExample { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false)); Page page = browser.newPage(); page.navigate("https://example.com"); // Capture screenshot page.screenshot(new Page.ScreenshotOptions() .setPath(Paths.get("screenshot.png")) .setFullPage(true)); System.out.println("✅ Screenshot saved as 'screenshot.png'"); } } }
📌 Key Notes:
-
setPath(Paths.get("filename.png"))
→ saves the screenshot to the specified file. -
setFullPage(true)
→ captures the entire scrollable page. Omit this to capture just the visible area.
📸 To Capture a Screenshot of an Element:
Locator element = page.locator("h1"); element.screenshot(new Locator.ScreenshotOptions() .setPath(Paths.get("element.png")));
-
-
Explain the page object model (POM) in the context of Playwright and JavaScript.
The Page Object Model (POM) is a popular design pattern used in test automation that helps create a clean separation between test logic and UI interactions. In Playwright with JavaScript or TypeScript, POM improves code readability, reusability, and maintainability.
✅ What is Page Object Model (POM)?
POM suggests that each web page (or component) in your application should have a corresponding JavaScript class. That class:
-
Defines the locators for elements on the page
-
Encapsulates interactions (e.g., clicking, filling, validating)
📦 Folder Structure Example:
tests/ └── login.spec.js pages/ └── loginPage.js
📄 Example:
loginPage.js
// pages/loginPage.js export class LoginPage { constructor(page) { this.page = page; this.usernameInput = page.locator('#username'); this.passwordInput = page.locator('#password'); this.loginButton = page.locator('#login'); } async goto() { await this.page.goto('https://example.com/login'); } async login(username, password) { await this.usernameInput.fill(username); await this.passwordInput.fill(password); await this.loginButton.click(); } }
📄 Example:
login.spec.js
import { test, expect } from '@playwright/test'; import { LoginPage } from '../pages/loginPage'; test('Successful login', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.goto(); await loginPage.login('admin', 'password123'); await expect(page).toHaveURL('https://example.com/dashboard'); });
✅ Benefits of Using POM in Playwright:
Feature Benefit Reusability Methods like login()
can be reused across testsMaintainability UI changes in locators only need updates in one place Readability Test files are cleaner and focus on test intent Scalability Easier to add more pages and tests 📌 Summary:
POM with Playwright + JavaScript means:
-
Creating separate
Page
classes -
Keeping UI logic out of test files
-
Writing modular, readable, and maintainable automation
-
-
How do you handle dropdowns or select elements in Playwright?
In Playwright, handling dropdowns or
<select>
elements is simple and can be done using the.selectOption()
method.✅ Basic Syntax
await page.selectOption('select#dropdownId', 'value');
🎯 Example: Selecting from a Dropdown
HTML:
<select id="fruits"> <option value="apple">Apple</option> <option value="banana">Banana</option> <option value="mango">Mango</option> </select>
Playwright Code:
await page.selectOption('#fruits', 'mango');
🔁 Select by Label or Value
You can use:
-
value
: actualvalue
attribute -
label
: visible text
// By value await page.selectOption('#fruits', { value: 'banana' }); // By label await page.selectOption('#fruits', { label: 'Apple' });
📋 Multiple Selection Example
For
<select multiple>
:await page.selectOption('#multi-select', ['option1', 'option2']);
🧠 Tips:
-
Always wait for the dropdown to be visible if it's dynamic:
await page.waitForSelector('#fruits'); await page.selectOption('#fruits', 'mango');
-
For custom dropdowns (not
<select>
), useclick()
andlocator.getByText()
.
-
-
What are locators in Playwright, and how are they different from selectors?
In Playwright, both locators and selectors are used to find elements on a page, but they serve slightly different purposes and offer different levels of abstraction and power.
✅ What is a Selector?
A selector is a string used to query an element from the DOM.
Examples:
await page.click('text=Submit'); await page.click('#login-button'); await page.click('.nav > li:nth-child(2)');
✅ What is a Locator?
A locator is a Playwright-specific object that encapsulates a selector and provides additional methods for interaction.
Syntax:
const button = page.locator('text=Submit'); await button.click();
🔍 Key Differences
Feature Selector Locator Type String Object Retry-ability No (must manage retries manually) ✅ Built-in auto-retry until stable Readability Can become messy with complex logic ✅ Cleaner and reusable Performance One-time DOM query ✅ Lazy evaluation (waits for conditions) Methods Available Basic query only ✅ Rich API ( click()
,fill()
,isVisible()
, etc.)🎯 Locator Example
const usernameField = page.locator('#username'); await usernameField.fill('myuser'); await usernameField.press('Enter');
🧠 Summary:
-
Selectors are useful for quick, one-off actions.
-
Locators are recommended for all robust and maintainable test automation — they auto-wait, are more stable, and chainable.
-
🔧 Advanced Playwright & JavaScript QA Interview Questions for 2–4 Years Experience.
-
Explain test parallelism in Playwright and how it improves test speed.
✅ Test Parallelism in Playwright – Explained
Test parallelism in Playwright means running multiple test files or test cases simultaneously rather than one after the other. This is done using multiple worker threads, and it significantly improves test execution speed, especially in large test suites.
🔧 How It Works
Playwright uses a test runner that can spawn multiple workers, each running one or more tests in parallel. Each worker gets its own browser instance, ensuring test isolation and no shared state.
⚙️ Enable Parallelism (Default Behavior)
By default, Playwright automatically runs test files in parallel (if system resources allow).
In
playwright.config.js
:// playwright.config.js const config = { workers: 4, // Number of parallel workers (threads) }; module.exports = config;
📈 Benefits of Parallelism
Benefit Description 🚀 Faster Execution Reduces total test time by running tests at the same time. 🧪 Better Resource Use Utilizes multiple CPU cores and browser instances. 🧼 Test Isolation Each test runs in a separate browser context to avoid side effects. 🛠 Example Command
Run with 3 parallel workers:
npx playwright test --workers=3
🔒 Optional: Disable Parallelism for Debugging
In the test config or CLI, you can run tests serially for debugging:
npx playwright test --workers=1
✅ Best Practices
-
Avoid shared state (e.g., global variables, shared sessions).
-
Use
test.describe.configure({ mode: 'serial' })
when a group of tests must run in order. -
Use
beforeEach
/afterEach
hooks for test isolation.
-
-
How do you handle browser context in Playwright? Why is it useful?
🔍 What is a Browser Context?
In Playwright, a browser context is an isolated session within a single browser instance — just like using incognito tabs in Chrome.
Each context has its own cookies, cache, localStorage, and session — so they do not share state.🎯 Why It’s Useful
Feature Benefit 🧪 Test Isolation Each test runs in its own clean environment (no shared cookies, etc). 🔄 Multiple Logins You can simulate multiple users (e.g., admin vs. customer) in one test. 🚀 Parallel Testing Run tests in the same browser instance but with separate sessions. 🔐 Better Security Mimics real-world user behavior like private/incognito browsing. 🛠 How to Use Browser Context in Playwright (JavaScript Example)
const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); // Create a new browser context const context = await browser.newContext(); // Create a new page inside the context const page = await context.newPage(); await page.goto('https://example.com'); // Do your testing actions here... await browser.close(); })();
🧪 Simulating Multiple Users
const userContext = await browser.newContext(); const adminContext = await browser.newContext(); const userPage = await userContext.newPage(); const adminPage = await adminContext.newPage(); await userPage.goto('https://yourapp.com/login'); await adminPage.goto('https://yourapp.com/admin');
📌 Pro Tip:
-
Use
context.storageState()
andcontext.addCookies()
to manage sessions or reuse logins. -
Especially useful in e2e testing or testing microservices with multiple roles.
-
-
What is the best way to manage test data in Playwright test automation?
Managing test data efficiently is crucial in any Playwright test automation strategy to ensure tests are reliable, maintainable, and scalable. Here's the best way to manage test data in Playwright:
✅ 1. Use External Data Files (JSON / CSV / Excel)
-
Why? Keeps test data separate from test logic, making maintenance easier.
-
Example (JSON):
// testData.json { "login": { "username": "testuser", "password": "testpass" } }
// test.spec.js const testData = require('./testData.json'); await page.fill('#username', testData.login.username); await page.fill('#password', testData.login.password);
✅ 2. Use Data-Driven Testing (via
test.describe()
or loops)Run the same test for multiple datasets:
const users = [ { username: 'admin', password: 'adminpass' }, { username: 'user1', password: 'userpass1' } ]; for (const user of users) { test(`Login test for ${user.username}`, async ({ page }) => { await page.goto('/login'); await page.fill('#username', user.username); await page.fill('#password', user.password); await page.click('button[type="submit"]'); // Assertions... }); }
✅ 3. Use Fixtures for Test Setup and Teardown
Fixtures (especially in Playwright Test Runner) help set up dynamic test data.
// Using a fixture to create user data before tests test.beforeEach(async ({ page }) => { await createTestUser('userA'); // Custom function calling API or DB });
✅ 4. Seed and Clean Test Data Using APIs or DB Hooks
-
Use Playwright to call REST APIs or scripts to seed/cleanup test data before or after test runs.
await request.post('/api/testdata/seed', { data: { user: 'testuser' } });
✅ 5. Use
.env
Files for Configurable Secrets-
Store dynamic values or environment-based data (like API keys, base URL) in
.env
file usingdotenv
.
BASE_URL=https://staging.app.com ADMIN_USER=admin
require('dotenv').config(); await page.goto(process.env.BASE_URL);
✅ Summary of Best Practices:
Practice Why It's Good Use JSON/CSV for static data Separation of concerns Use API/DB setup for dynamic data Ensure tests run on fresh, known data Use fixtures Standardized setup & cleanup Use .env
configsEasy environment switching Avoid hardcoded values Makes tests brittle and hard to scale -
-
How do you handle alerts, pop-ups, and confirmation dialogs in Playwright using JavaScript?
In Playwright (JavaScript), handling alerts, pop-ups, and confirmation dialogs (like
alert()
,confirm()
,prompt()
) is done using thepage.on('dialog', callback)
event.✅ How it works:
Playwright listens for dialog events and allows you to interact with them (accept, dismiss, read the message, etc.).
📌 Example 1: Handling
alert()
page.on('dialog', async dialog => { console.log('Alert message:', dialog.message()); await dialog.accept(); }); await page.click('#trigger-alert'); // This button triggers alert()
📌 Example 2: Handling
confirm()
page.on('dialog', async dialog => { console.log('Confirm message:', dialog.message()); await dialog.accept(); // or dialog.dismiss(); }); await page.click('#trigger-confirm'); // Triggers a confirmation popup
📌 Example 3: Handling
prompt()
page.on('dialog', async dialog => { console.log('Prompt message:', dialog.message()); await dialog.accept('My input'); // Send input to the prompt }); await page.click('#trigger-prompt'); // Triggers a prompt dialog
🔒 Important Notes:
-
dialog.accept([promptText])
: Accepts the dialog (optionally sending input for prompt). -
dialog.dismiss()
: Dismisses the dialog (like clicking Cancel). -
You must handle dialogs before they appear, otherwise the page may hang.
🧠 Pro Tip:
For reliability, attach the
page.on('dialog', ...)
handler before the action that triggers the popup. -
-
How do you perform API testing in Playwright JavaScript? Can Playwright do backend checks?
Yes, Playwright (JavaScript) supports API testing and backend checks using its built-in
request
API — so you can test RESTful APIs alongside your UI tests.✅ Why use Playwright for API testing?
-
You can send HTTP requests directly (GET, POST, PUT, DELETE, etc.).
-
You can chain backend validation with frontend actions.
-
No need for extra libraries like Postman or Axios.
📌 Example: Basic API Test in Playwright
import { test, expect, request } from '@playwright/test'; test('API Test - Validate GET User', async ({ request }) => { const response = await request.get('https://jsonplaceholder.typicode.com/users/1'); expect(response.ok()).toBeTruthy(); const body = await response.json(); expect(body.name).toBe('Leanne Graham'); });
📌 Example: Backend Check After UI Action
test('Create User via UI and validate backend', async ({ page, request }) => { // Perform frontend action await page.goto('https://example.com/create-user'); await page.fill('#name', 'John Doe'); await page.click('#submit'); // Validate on backend const apiResponse = await request.get('https://api.example.com/users?name=John Doe'); const result = await apiResponse.json(); expect(result[0].name).toBe('John Doe'); });
🧪 Available HTTP methods:
-
request.get(url)
-
request.post(url, { data })
-
request.put(url, { data })
-
request.delete(url)
🎯 Real-World Use Cases:
-
Validating DB records via APIs after form submission.
-
Auth token testing.
-
CRUD operations on REST APIs.
-
Smoke tests for microservices.
🔐 You can also pass headers and authentication:
const apiContext = await request.newContext({ baseURL: 'https://api.example.com', extraHTTPHeaders: { 'Authorization': `Bearer your_token_here` } });
-
-
What is
BrowserContext
and how is it different from aBrowser
in Playwright?In Playwright,
Browser
andBrowserContext
are core concepts for managing test environments. Here's the difference in a simple and clear way:✅ What is a
Browser
in Playwright?-
A
Browser
instance represents the entire running browser (like Chrome, Firefox, or WebKit). -
It’s like launching Chrome manually — all users (tabs/windows) share the same memory, cookies, etc.
const browser = await chromium.launch(); // Starts a full browser
✅ What is a
BrowserContext
?-
A
BrowserContext
is like an isolated browser profile/session within the same browser. -
Each context is independent — separate cookies, storage, sessions, and cache.
-
Great for parallel tests without interference.
const context = await browser.newContext(); // Like an incognito window const page = await context.newPage();
🎯 Key Differences
Feature Browser
BrowserContext
Scope Entire browser A single session/profile Session Storage Shared across all pages Isolated Cookies/Cache Shared Isolated Use Case One per test suite One per user/test to avoid conflicts Performance Heavy (full browser instance) Lightweight (faster, memory-efficient) 🧪 Example: Creating Two Isolated Sessions
const browser = await chromium.launch(); const context1 = await browser.newContext(); const page1 = await context1.newPage(); await page1.goto('https://example.com'); const context2 = await browser.newContext(); const page2 = await context2.newPage(); await page2.goto('https://example.com'); // Separate session await browser.close();
✅ Why Use
BrowserContext
?-
Run parallel tests without launching multiple browsers.
-
Simulate multiple users with different sessions.
-
Improve performance and reduce test flakiness.
-
-
How do you handle authentication (like login) in Playwright tests?
Handling authentication (login) in Playwright can be done in several efficient ways depending on the use case. Here's a short and practical breakdown with code examples:
✅ 1. UI-Based Login (Form Login)
Perform login like a real user:
test('Login using UI', async ({ page }) => { await page.goto('https://example.com/login'); await page.fill('#username', 'yourUsername'); await page.fill('#password', 'yourPassword'); await page.click('button[type="submit"]'); await expect(page).toHaveURL('https://example.com/dashboard'); });
🔁 Use only if login is part of the test or for initial setup.
✅ 2. API Login (Faster & Headless)
Use API call to authenticate and reuse cookies or tokens:
test('Login via API and reuse session', async ({ browser }) => { const context = await browser.newContext(); const page = await context.newPage(); // Send API login request const response = await page.request.post('https://example.com/api/login', { data: { username: 'yourUsername', password: 'yourPassword', }, }); const token = (await response.json()).token; // Set token as local storage or cookie await context.addCookies([{ name: 'auth_token', value: token, domain: 'example.com', path: '/', httpOnly: true, secure: true, sameSite: 'Lax' }]); // Proceed with authenticated page await page.goto('https://example.com/dashboard'); });
✅ 3. Storage State (Recommended for Reuse)
Login once, save the session, and reuse it across tests.
Step 1: Create login script (
login.setup.js
)const { chromium } = require('@playwright/test'); (async () => { const browser = await chromium.launch(); const context = await browser.newContext(); const page = await context.newPage(); await page.goto('https://example.com/login'); await page.fill('#username', 'yourUsername'); await page.fill('#password', 'yourPassword'); await page.click('button[type="submit"]'); await context.storageState({ path: 'auth.json' }); await browser.close(); })();
Step 2: Use in tests
test.use({ storageState: 'auth.json' }); test('Dashboard loads with logged-in user', async ({ page }) => { await page.goto('https://example.com/dashboard'); await expect(page.locator('text=Welcome')).toBeVisible(); });
🎯 Best Practice
-
✅ Use storageState for speed and reusability.
-
✅ Use API login if frontend login is slow or flaky.
-
❌ Avoid logging in via UI in every test — it's time-consuming.
-
-
What kind of reporting frameworks can be integrated with Playwright Java (e.g., Allure, ExtentReports)?
Playwright with JavaScript (using Playwright Test Runner or Jest) supports integration with several popular reporting frameworks to generate clean, detailed test reports. Here are the most commonly used ones:
✅ 1. Allure Report (Most Popular)
Allure is widely used for its rich UI, step-by-step details, and attachments (screenshots, logs, etc.).
🔧 Setup:
npm i -D allure-playwright
🔌 Configuration:
In
playwright.config.ts
or.js
:reporters: [ ['allure-playwright'], ]
📸 Run tests and generate report:
npx playwright test npx allure generate ./allure-results --clean -o ./allure-report npx allure open ./allure-report
✅ 2. HTML Reporter (Built-in)
Playwright comes with a built-in HTML reporter:
reporters: ['html']
Run:
npx playwright show-report
📌 Good for quick local viewing. Interactive UI with test details.
✅ 3. JSON & JUnit Reporters
Useful for CI/CD integration (like Jenkins, GitHub Actions, Azure DevOps).
reporters: [ ['json', { outputFile: 'report.json' }], ['junit', { outputFile: 'results.xml' }] ]
📂 Use these for downstream processing or visual dashboards in CI pipelines.
✅ 4. ExtentReports (Not native but possible via plugins/wrappers)
While not officially supported like Allure, you can use ExtentReports with:
-
Jest + custom reporter wrappers
-
Playwright + Cucumber + ExtentReports (in BDD setups)
🛠️ Requires more manual setup, but possible for teams already using ExtentReports in Selenium.
✅ 5. Custom HTML Reporter
You can write a custom reporter if you need full control over format:
// custom-reporter.js class MyReporter { onTestBegin(test) { /* logic */ } onTestEnd(test, result) { /* logic */ } } module.exports = MyReporter;
In config:
reporters: [['./custom-reporter.js']]
🎯 Recommended Setup for Playwright JS:
reporters: [ ['list'], // console logs ['html'], // local interactive reports ['allure-playwright'], // advanced reporting ['junit', { outputFile: 'results.xml' }] // CI ]
-
-
How do you handle dynamic content in Playwright that loads after scrolling or clicking?
In Playwright, handling dynamic content (like infinite scroll, lazy-loaded elements, or AJAX-loaded sections) requires waiting and interaction strategies. Here's how to effectively deal with such content:
✅ 1. Wait for Element to Appear
Use Playwright’s auto-waiting or
await locator.waitFor()
to wait for elements that load dynamically.await page.locator('.dynamic-item').waitFor({ state: 'visible' });
✅ 2. Scroll to Load More Content
If the content appears on scroll, simulate scrolling using
page.evaluate()
orpage.mouse.wheel()
.await page.evaluate(() => { window.scrollBy(0, window.innerHeight); }); await page.waitForTimeout(1000); // wait for content to load
✅ Use in a loop if you're scrolling multiple times:
for (let i = 0; i < 5; i++) { await page.mouse.wheel(0, 1000); await page.waitForTimeout(1500); }
✅ 3. Wait for Network to Be Idle
Wait for all AJAX/XHR requests to finish:
await page.waitForLoadState('networkidle');
✅ 4. Click to Load More Content
Click a button and wait for new content to load:
await page.click('button.load-more'); await page.locator('.new-item').waitFor({ state: 'visible' });
✅ 5. Use
locator.first()
orlocator.last()
for Dynamic Listsconst lastItem = page.locator('.list-item').last(); await lastItem.scrollIntoViewIfNeeded();
🔁 Full Example:
for (let i = 0; i < 3; i++) { await page.mouse.wheel(0, 1000); await page.waitForLoadState('networkidle'); await page.waitForTimeout(2000); // wait for items to render }
🧠 Advanced & Real-Time Scenario Questions (4+ Years Experience)
-
You have flaky tests in Playwright. How would you debug and stabilize them?
Here’s a simplified version of the explanation on how to debug and fix flaky tests in Playwright:
🧪 How to Fix Flaky Tests in Playwright
Flaky tests are tests that pass sometimes and fail randomly, usually due to timing, animations, or dynamic content. Here’s how to fix them:
✅ 1. Use Smart Locators (
locator.click()
notpage.click()
)Instead of:
await page.click('#submit');
Use:
await page.locator('#submit').click();
Why? Because
locator.click()
waits for the element to be ready.⏱️ 2. Avoid Hardcoded Timeouts
❌ Bad:
await page.waitForTimeout(3000);
✅ Better:
await page.locator('#message').waitFor({ state: 'visible' });
Let Playwright wait for the right condition, not just time.
🔁 3. Add Retry Logic for CI
In your
playwright.config.js
:retries: process.env.CI ? 2 : 0
This makes tests more stable on CI servers.
🔍 4. Use Trace Viewer to Debug
Run tests with:
npx playwright test --trace on
After failure:
npx playwright show-trace trace.zip
This shows a full timeline of what happened.
🧼 5. Keep Tests Isolated
Each test should have its own browser context or data to avoid interference. Don’t let tests share state.
🚫 6. Don’t Depend on Animations or Loading Delays
If something appears after a scroll or click, wait for it properly:
await page.locator('#loadedContent').waitFor({ state: 'visible' });
🧠 Final Tip
Flaky tests waste time and hurt confidence. Fix them by:
-
Using smart waits
-
Avoiding fixed delays
-
Debugging with traces
-
Keeping tests independent
-
-
How do you integrate Playwright with CI/CD tools like Jenkins or GitHub Actions?
Integrating Playwright with CI/CD tools like Jenkins or GitHub Actions is essential for running tests automatically on every code push. Here's a simple explanation for your understanding:
✅ Why CI/CD Integration?
CI/CD ensures your Playwright tests run automatically:
-
After each commit
-
On pull requests
-
In clean environments
Let’s look at how to do it.
🛠️ Jenkins Integration
1. Install Dependencies on Jenkins Server
Make sure Jenkins has:
-
Node.js
-
Git
-
Playwright (install with
npx playwright install
)
2. Create Jenkins Pipeline (Declarative)
pipeline { agent any stages { stage('Install Dependencies') { steps { sh 'npm ci' sh 'npx playwright install --with-deps' } } stage('Run Tests') { steps { sh 'npx playwright test' } } stage('Archive Results') { steps { archiveArtifacts 'playwright-report/**' } } } }
3. Optional: Publish HTML Report
After test run, archive or serve
playwright-report/index.html
.💥 GitHub Actions Integration
1. Create
.github/workflows/playwright.yml
name: Playwright Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: | npm ci npx playwright install --with-deps - name: Run tests run: npx playwright test - name: Upload Playwright report if: failure() uses: actions/upload-artifact@v3 with: name: playwright-report path: playwright-report
📦 Bonus Tips
-
Use
retries
in config to reduce flakiness in CI:
retries: process.env.CI ? 2 : 0
-
Enable tracing:
npx playwright test --trace on
🔍 Summary
CI Tool Steps Needed Jenkins Pipeline + Shell commands GitHub .yml
Workflow + Upload artifactsIntegrating Playwright with CI/CD makes your test suite automated, scalable, and reliable 💪
-
-
What challenges have you faced while migrating from Selenium to Playwright?
Migrating from Selenium to Playwright can be a big step forward — but it comes with some challenges. Here's a concise and breakdown:
1. ⚙️ Rewriting All Tests
-
Challenge: Playwright uses a completely different API compared to Selenium.
-
Impact: Existing test scripts must be rewritten from scratch.
-
Tip: Focus on key flows first, then gradually migrate others.
2. 🎯 Locator Strategy Differences
-
Challenge: Selenium heavily relies on XPath or CSS selectors; Playwright encourages
getByRole
,getByText
, etc. -
Impact: Locators need to be updated for better auto-waiting and stability in Playwright.
3. 🧪 Switching from TestNG/JUnit to Playwright Test (or Jest)
-
Challenge: Selenium tests are usually built with Java frameworks like TestNG or JUnit.
-
Impact: Test structure needs a revamp using Playwright Test Runner or other JS/TS test frameworks.
4. 🔐 Authentication & Session Handling
-
Challenge: Selenium stores cookies/sessions differently.
-
Playwright Solution: Use
storageState.json
for reusing authenticated sessions — a new concept for Selenium users.
5. 🧰 Framework & Language Shift
-
Challenge: Moving from Java (Selenium) to JavaScript/TypeScript (Playwright).
-
Impact: Requires learning a new language, test runner, and ecosystem (e.g., Node.js, npm).
6. 📄 Reporting Tools Compatibility
-
Challenge: Tools like Allure, ExtentReports, etc., work out-of-the-box in Selenium Java.
-
Playwright Solution: Needs manual setup or plugins to integrate with custom reports.
7. 📦 Parallel Execution & Isolation
-
Challenge: Playwright encourages using multiple browser contexts and parallelism.
-
Impact: Tests need to be written keeping parallel-safe practices in mind.
8. 🔄 Mindset Shift
-
Challenge: Selenium users manually wait for elements.
-
Playwright Advantage: Built-in auto-waiting and retry-ability — requires a new approach to test flow design.
✅ Final Thought
While the migration requires effort, the payoff is huge: faster execution, better stability, and modern automation features.
-
-
How would you design a scalable Playwright test framework using JavaScript?
1. Project Folder Structure
Organizing files in a structured way helps maintain and grow your test suite.
playwright-framework/ ├── tests/ → Contains test files ├── pages/ → Page Object classes for UI elements and actions ├── utils/ → Helper functions (e.g., random data, API calls) ├── fixtures/ → Test data (JSON, JS) ├── reports/ → Test execution reports ├── playwright.config.js → Central configuration file └── package.json → Scripts and dependencies
2. Page Object Model (POM)
Separate UI locators and actions into their own classes.
Example:
// pages/LoginPage.js class LoginPage { constructor(page) { this.page = page; this.username = page.locator('#username'); this.password = page.locator('#password'); this.loginBtn = page.locator('button[type="submit"]'); } async login(user, pass) { await this.username.fill(user); await this.password.fill(pass); await this.loginBtn.click(); } } module.exports = { LoginPage };
3. Writing Tests
Use Playwright's built-in test runner to write readable and maintainable tests.
Example:
const { test, expect } = require('@playwright/test'); const { LoginPage } = require('../pages/LoginPage'); test('Login with valid credentials', async ({ page }) => { const loginPage = new LoginPage(page); await page.goto('https://example.com/login'); await loginPage.login('admin', 'admin123'); await expect(page).toHaveURL('https://example.com/dashboard'); });
4. Reuse Setup with Hooks
Add repeated actions like login, navigation in
beforeEach
.test.beforeEach(async ({ page }) => { await page.goto('https://example.com'); });
5. Managing Test Data
You can use static files like
.json
or use dynamic data withfaker.js
.const users = require('../fixtures/users.json');
6. Reports
Use built-in HTML reporting:
npx playwright test --reporter=html
Or install Allure for advanced reports:
npm i -D allure-playwright npx allure generate ./allure-results --clean npx allure open
7. Multi-Environment Support
Use environment variables to switch between staging, QA, and production.
In
playwright.config.js
:use: { baseURL: process.env.ENV === 'staging' ? 'https://staging.site.com' : 'https://prod.site.com', }
Run with:
ENV=staging npx playwright test
8. Continuous Integration
Add test scripts to
package.json
:"scripts": { "test": "npx playwright test" }
Use this in GitHub Actions or Jenkins to trigger tests on every push or PR.
9. Tips for Stability
-
Use proper locators (e.g.,
getByRole
,locator('data-test-id')
) -
Retry flaky tests using
test.retry(2)
-
Keep your tests atomic and independent
-
Run tests in parallel using
workers
setting inplaywright.config.js
-
-
What’s your approach to handling cross-browser testing with Playwright and JavaScript?
Cross-browser testing with Playwright and JavaScript is simple and efficient, thanks to Playwright’s built-in support for Chromium, Firefox, and WebKit.
1. Built-in Multi-Browser Support
Playwright supports:
-
Chromium (Chrome, Edge)
-
Firefox
-
WebKit (Safari)
You don't need to install anything extra — Playwright downloads all browser binaries automatically.
2. Configure Browsers in
playwright.config.js
// playwright.config.js import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'Chromium', use: { browserName: 'chromium' }, }, { name: 'Firefox', use: { browserName: 'firefox' }, }, { name: 'WebKit', use: { browserName: 'webkit' }, }, ], });
3. Writing Tests (No Change Needed)
Your test cases remain the same. Playwright runs each test against all configured browsers.
test('Cross-browser login test', async ({ page }) => { await page.goto('https://example.com'); // your test steps... });
4. Running Tests
npx playwright test
This runs the test in all three browsers defined in the config.
5. Reporting by Browser
Each browser will generate its own set of results. You can:
-
Use built-in HTML report
-
Use Allure for advanced reporting per browser/project
npx playwright show-report
✅ Benefits of This Approach
-
Zero setup for browser drivers
-
Same test code runs on all browsers
-
Easy CI/CD integration for multi-browser execution
-
Supports both desktop and mobile emulation
🧠 Pro Tip
You can also limit browsers in CI to speed up execution or only test critical paths in Firefox/WebKit.
-
💡 Bonus Tips for Your QA Interview
-
Speak from experience: Interviewers love real-world examples.
-
Mention challenges and solutions: Show problem-solving skills.
-
Talk about tools: Mention integrations (Jenkins, Git, Docker, Allure, etc.).
-
Keep learning: Playwright is evolving—follow their GitHub for updates.
🔚 Final Thoughts
Mastering Playwright and JavaScript together can significantly boost your value as a QA automation engineer. These interview questions are designed not only to help you crack interviews but also deepen your understanding of modern test automation practices.
If you're preparing for roles in 2025, focus on practical knowledge, hands-on testing, and tool integration experience. Employers are looking for engineers who can build, maintain, and scale automation frameworks—not just write test cases.