The demand for Playwright automation testers is rapidly growing in 2025 as organizations shift from traditional frameworks like Selenium to faster, more scalable tools.
Playwright, developed by Microsoft, has become the go-to framework for end-to-end web automation — known for its reliability, cross-browser support, and seamless integration with CI/CD pipelines.
At Testleaf, our experts have trained thousands of QA professionals through Playwright training in Chennai to become job-ready and confident in real-world automation projects.
To help you crack your next interview, we’ve compiled the top 20 Playwright interview questions and answers, along with real-time scenarios and expert insights — ideal for professionals with 2 to 5 years of experience in automation testing.
Q1. Explain the differences between slice() and substring() with examples.
Both methods are used to extract parts of a string, but they behave differently in edge cases.
let str = “Playwright”;
console.log(str.slice(0, 4)); // “Play”
console.log(str.substring(0, 4)); // “Play”console.log(str.slice(-5)); // “right”
console.log(str.substring(-5)); // “Playwright”
Key Differences
Feature | slice(start, end) | substring(start, end) |
Negative indices | ✅ Supports (counts from end) | ❌ Treated as 0 |
Parameter order | Keeps given order | Swaps if start > end |
Use cases | Strings and arrays | Only strings |
Internal Explanation
- slice() calculates indices relative to string length when negative values are used.
- substring() simply clamps values below 0 to 0.
Q2. Difference Between null and undefined
let a;
let b = null;console.log(a); // undefined
console.log(b); // null
Explanation
- undefined means that a variable has been declared, but no value has been assigned to it.
It’s the default state of any variable that hasn’t been initialized.
- null is a special value that represents an intentional absence of value.
It is assigned by the programmer to indicate that a variable should be empty or have no object reference.
Type Comparison
typeof null; // “object”
typeof undefined; // “undefined”
null == undefined; // true (loose)
null === undefined; // false (strict)
Common Use
- Use undefined for uninitialized values.
- Use null when intentionally clearing a variable or object property.
Q3. Benefits of TypeScript Over JavaScript
TypeScript is a superset of JavaScript that adds static typing.
function divide(a: number, b: number): number {
return a / b;
}
Benefits
- Static Type Checking – errors caught at compile time.
- Better Tooling – IDEs provide autocompletion and refactor tools.
- Object-Oriented Features – interfaces, enums, generics, access modifiers.
- Improved Maintainability – type contracts reduce ambiguity.
- Transpilation – compiles to clean JS that runs everywhere.
Example of Compile-Time Safety
let num: number = 10;
// num = “ten”; // ❌ Error caught before runtime
Q4. How do callback functions work in JavaScript, and why are they important?
A callback function is a function that is passed as an argument to another function, and then executed later by that function.
In simple words, a callback allows one function to call another function when it’s ready — especially useful for asynchronous operations (like file reading, network requests, or timers).
function processUserInput(callback) {
let name = “Alice”;
callback(name);
}processUserInput((user) => console.log(“Hello ” + user));
Problem
Callback nesting leads to callback hell:
Callbacks are powerful, but they can get messy and hard to manage when there are multiple asynchronous steps that depend on each other.
This situation is called Callback Hell (also known as “Pyramid of Doom”).
setTimeout(() => {
console.log(“Task 1”);
setTimeout(() => {
console.log(“Task 2”);
}, 1000);
}, 1000);
Modern replacements: Promises and async/await.
Q5. Difference Between for…in and for…of
const arr = [“x”, “y”, “z”];
const obj = { a: 10, b: 20 };for (let i in arr) console.log(i); // indexes: 0, 1, 2
for (let v of arr) console.log(v); // values: x, y, z
for (let key in obj) console.log(key); // keys: a, b
Feature | for…in | for…of |
Iterates over | Keys (property names or indexes) | Values (actual data items) |
Works on | Objects, arrays, strings | Iterables (arrays, maps, sets, strings, NodeLists, etc.) |
Return value | String keys | Actual values |
Use case | Enumerating object properties | Iterating over iterable elements |
Can be used on Objects | ✅ Yes | ❌ No (throws error) |
Works with Arrays | ✅ Yes (but not recommended) | ✅ Yes (preferred) |
Includes inherited properties | ✅ Yes | ❌ No |
Symbol.iterator support | ❌ Not required | ✅ Required |
Q6. What is the difference between synchronous and asynchronous code in JavaScript?
🔹 Definition
Synchronous code means the program executes line by line, in order, and each statement must finish before the next one starts.
It’s also called blocking code, because each operation blocks the next from running until it finishes.
🔹 Example : Simple Synchronous Execution
console.log(“Step 1”);
console.log(“Step 2”);
console.log(“Step 3”);
Output:
Step 1
Step 2
Step 3
Each line executes in sequence — nothing else can happen until the current task is done.
Definition
Asynchronous code allows a program to start a task and move on to the next one before the first task finishes.
When the async task completes, its result is handled later — without blocking other operations.
This is non-blocking behavior.
🔹 Example : Using setTimeout (Async Callback)
console.log(“Start”);
setTimeout(() => {
console.log(“Async task complete”);
}, 2000);console.log(“End”);
Output:
Start
End
Async task complete
Even though we asked setTimeout to wait for 2 seconds, JavaScript doesn’t stop — it continues executing “End” immediately.
Q7.How do type annotations improve code safety in TypeScript?
Definition:
Type annotations allow you to explicitly declare the type of a variable, function parameter, or return value. This ensures that the TypeScript compiler can catch type-related errors at compile time rather than at runtime.
Example:
let name: string = “Alice”; // variable of type string
let count: number = 42; // variable of type number
let isActive: boolean = true; // variable of type booleanfunction greet(person: string): string { // function parameter and return type
return `Hello, ${person}`;
}
Q8. What is type inference in TypeScript, and how does it work?
Definition:
Type inference is TypeScript’s ability to automatically deduce the type of a variable based on the value assigned to it. This means you don’t always need explicit type annotations, but you still get type safety.
Example:
let city = “London”; // TypeScript infers type as string
let age = 25; // TypeScript infers type as number
let isAdmin = true; // TypeScript infers type as boolean
Q9.Explain the difference between optional and default parameters with examples.
1. Optional Parameters (?)
- A parameter can be marked as optional using the ? symbol.
- If a caller doesn’t provide a value, TypeScript assigns it undefined.
- Optional parameters must come after required parameters.
Example:
function log(message: string, user?: string) {
console.log(user ? `${user}: ${message}` : message);
}log(“System starting”); // Output: System starting
log(“System starting”, “Admin”); // Output: Admin: System starting
2. Default Parameters
- Default parameters allow you to set a fallback value if the caller doesn’t provide one.
- Useful for avoiding undefined and providing default behavior.
Example:
function greet(name: string = “Guest”) {
console.log(`Hello ${name}`);
}greet(); // Output: Hello Guest
greet(“Alice”); // Output: Hello Alice
Q10. What are Enums in TypeScript and How to Use Them?
Definition:
Enums in TypeScript allow you to define a set of named constants, grouping related values together. They improve readability, type safety, and prevent the use of “magic strings” or arbitrary numbers in code.
1. String Enums
- Each member has an explicit string value.
- Useful when the value matters for readability or API consistency.
enum Role {
Admin = “ADMIN”,
User = “USER”,
Guest = “GUEST”
}let currentUser: Role = Role.Admin;
console.log(currentUser); // Output: ADMIN
Key Points:
- Makes code self-documenting.
- Prevents accidental typos like “admin” vs “Admin”.
2. Numeric Enums
- Members are assigned numeric values automatically (starting at 0) or can be set manually.
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}let move: Direction = Direction.Left;
console.log(move); // Output: 2
Key Points:
- Auto-incremented by default, can be overridden.
- Can be used in logic where numbers are needed but labels improve clarity.
✅ Benefits of Enums:
- Readability: Use descriptive names instead of arbitrary values.
- Maintainability: Easier to manage a set of related constants.
- Type Safety: Compiler prevents invalid assignments.
Q11. Type Assertion vs Type Casting
1. Type Assertion
- Definition: Tells TypeScript what type you know a value is.
- Important: It does not change the value at runtime; it only informs the compiler.
- Syntax: value as Type or <Type>value
Example:
let input: unknown = “Playwright”;
let len = (input as string).length; // tells TypeScript ‘input’ is a string
console.log(len); // Output: 10
Key Points:
- Useful when TypeScript cannot infer the type.
- Does not perform any runtime conversion.
2. Type Casting
- Definition: Actually converts a value from one type to another at runtime.
- Example:
let num = Number(“42”); // converts string “42” to number 42
let str = String(100); // converts number 100 to string “100”
Key Points:
- Changes the actual value type at runtime.
- Needed when working with different data types in operations.
Q12. What are common HTTP status codes and how do you handle them in code?
Common HTTP Status Codes:
Code | Meaning | Explanation |
200 | OK | Request succeeded |
401 | Unauthorized | Authentication required |
403 | Forbidden | Access denied |
404 | Not Found | Resource doesn’t exist |
500 | Server Error | Unexpected server failure |
Example Usage:
if (response.status === 404) {
console.error(“Resource not found”);
} else if (response.status === 200) {
console.log(“Request successful”);
}
Key Points:
- 2xx codes → Success
- 4xx codes → Client errors
- 5xx codes → Server errors
- Handling status codes properly ensures your application can respond gracefully to errors.
Q13. Key Benefits of Playwright
- Cross-browser testing (Chromium, Firefox, WebKit)
- Headless & headed execution
- Built-in waiting mechanism
- Network interception and mocking
- Screenshots, videos, and trace recording
- Multi-tab and multi-context support
import * as pw from ‘playwright’;
const browser = await pw.chromium.launch({ headless: true });
const page = await browser.newPage();
await page.goto(‘https://example.com’);
await browser.close();
Q14. Playwright: Storing and Reusing Authentication with storageState()
Definition:
storageState() is used to store and reuse authentication data and cookies. This allows tests to skip repeated logins and maintain session state across different test contexts.
Example: Saving Authentication State
const context = await browser.newContext();
await context.storageState({ path: ‘auth.json’ }); // saves cookies and local storage
Example: Reusing Saved State
const newContext = await browser.newContext({ storageState: ‘auth.json’ });
Key Points:
- Skip Login Steps: Ideal for tests that require an authenticated user.
- Persistent Session: Cookies, localStorage, and sessionStorage can be reused.
- Separate Contexts: You can create multiple contexts with different stored states for parallel testing.
Q15. What are test hooks in Playwright and how are they used?
Definition:
Test hooks are functions that run before or after tests to handle setup, cleanup, or resource management. They help make tests organized, reusable, and maintainable.
Example:
import { test } from ‘@playwright/test’;
test.beforeAll(async () => console.log(“Before all tests”)); // Runs once before all tests
test.beforeEach(async () => console.log(“Before each test”)); // Runs before each test
test.afterEach(async () => console.log(“After each test”)); // Runs after each test
test.afterAll(async () => console.log(“After all tests”)); // Runs once after all tests
Key Points:
- beforeAll / afterAll → Setup/cleanup tasks for the entire test suite.
- beforeEach / afterEach → Setup/cleanup tasks for individual tests.
- Use Cases: Initializing browser contexts, loading test data, closing resources, resetting states.
Q16. How do test.describe() and test.step() help organize and debug tests in Playwright?
Definition:
- test.describe(): Groups related tests together, making suites more readable and organized.
- test.step(): Breaks a test into smaller, labeled steps to improve debugging and reporting.
Example:
import { test } from ‘@playwright/test’;
test.describe(‘User Authentication’, () => {
test(‘Login flow’, async ({ page }) => {await test.step(‘Open login page’, async () => {
await page.goto(‘/login’);
});await test.step(‘Submit form’, async () => {
await page.fill(‘#user’, ‘admin’);
await page.fill(‘#pass’, ‘1234’);
await page.click(‘button’);
});});
});
Key Points:
- Readability: Grouping tests with test.describe() provides a clear hierarchy in reports.
- Debugging: Using test.step() allows each step to appear in reports, making it easier to pinpoint failures.
- Maintainability: Helps organize large test suites and clearly separate logical actions
Q17. What is the difference between PUT and PATCH HTTP methods?
Definition & Usage:
- PUT: Replaces the entire resource with the new data.
- PATCH: Updates only specific fields of a resource.
Examples:
// PUT replaces the entire user resource
fetch(‘/user/1’, {
method: ‘PUT’,
body: JSON.stringify({ name: ‘John’, age: 30 })
});// PATCH updates only part of the user resource
fetch(‘/user/1’, {
method: ‘PATCH’,
body: JSON.stringify({ age: 31 })
});
Key Points:
- PUT → Full Update (all fields must be provided).
- PATCH → Partial Update (only the fields to change are needed).
Q18. What is the difference between path parameters and query parameters in APIs?
Definition & Usage:
- Path Parameters: Identify a specific resource in the URL.
- Query Parameters: Provide additional filtering, sorting, or searching information.
Examples:
// Path parameter
GET /users/123 // Access user with ID 123// Query parameter
GET /users?role=admin // Get users with role “admin”
Type | Example | Purpose |
Path | /users/5 | Identify a specific resource |
Query | /users?sort=asc | Filter, sort, or search data |
Key Points:
- Path parameters are mandatory in URLs for resource identification.
- Query parameters are optional and provide additional context or filters.
Q19. How do you implement setup and teardown in Playwright tests?
Definition:
- Setup: Prepares the test environment before execution (e.g., navigating to a page, initializing data).
- Teardown: Cleans up after test execution (e.g., clearing cookies, closing contexts).
Example:
import { test } from ‘@playwright/test’;
test.beforeEach(async ({ page }) => {
await page.goto(‘https://app.com/login’); // Setup
});test.afterEach(async ({ context }) => {
await context.clearCookies(); // Teardown
});
Key Points:
- beforeEach / beforeAll → Setup tasks for tests.
- afterEach / afterAll → Cleanup tasks.
- Ensures tests are isolated, repeatable, and reliable
Q20. How can retries be configured in Playwright to handle flaky tests?
Definition:
Retries allow automatically rerunning failed tests to handle intermittent issues like network instability or timing problems.
Example:
import { test } from ‘@playwright/test’;
test.describe.configure({ retries: 2 }); // Retry failed tests twice
test(‘retryable test’, async ({ page }) => {
await page.goto(‘https://unstable.com’);
});
Key Points:
- Helps reduce false negatives caused by flaky environments.
- Can be configured per test or for an entire test suite.
- Improves test reliability without manual intervention.
Why Organizations Prefer Playwright in 2025
- Faster cross-browser performance
- Easy integration with software testing with Selenium frameworks
- Lower maintenance cost
- Built-in test runner and parallel execution
- Excellent debugging and trace viewer support
Expert Tip from Testleaf
At Testleaf, we’ve observed many QA teams transitioning from Selenium to Playwright in their real-world projects.
Our Playwright course in Chennai helps professionals build end-to-end test frameworks, handle parallel execution, and integrate CI/CD pipelines effectively.
If you’ve mastered Selenium, the next step in your automation career is learning Playwright — the tool that’s redefining modern web testing.
Conclusion
Playwright’s rise in 2025 isn’t just about new features — it’s about efficiency, scalability, and developer experience.
Its open-source power, auto-wait features, and cloud integration make it a perfect tool for today’s test automation engineers.
Whether you’re preparing for interviews or upgrading your skills, mastering test automation with Playwright will make you stand out in the evolving QA landscape.
🚀 Ready to get hands-on?
Join Testleaf’s Playwright Training in Chennai — and transform your testing career with real-time frameworks, industry projects, and expert mentorship.
We Also Provide Training In:
- Advanced Selenium Training
- Playwright Training
- Gen AI Training
- AWS Training
- REST API Training
- Full Stack Training
- Appium Training
- DevOps Training
- JMeter Performance Training
Author’s Bio:
Content Writer at Testleaf, specializing in SEO-driven content for test automation, software development, and cybersecurity. I turn complex technical topics into clear, engaging stories that educate, inspire, and drive digital transformation.
Ezhirkadhir Raja
Content Writer – Testleaf