Select a category to see more related content
Testing has changed a lot in the last few years. Releases are faster, apps are more complex, and nobody has the patience (or the budget) for endless manual regression cycles. That's why so many teams have turned to automation and why Playwright keeps coming up in QA conversations.
Playwright is an open-source browser automation framework from Microsoft. It lets you write reliable end-to-end tests that run on Chromium, Firefox, and WebKit. So what makes it different from the older tools? A handful of things: auto-waiting, isolated browser contexts, built-in parallel execution, reusable fixtures, API testing, network interception, and some really good debugging tools.
Maybe you're a manual tester thinking about moving into automation. Maybe you've spent years with Selenium and want to see what the fuss is about. Or maybe you're already building frameworks and want to sharpen your skills. Either way, this guide walks through the 20 Playwright features worth knowing, with JavaScript examples and the kind of scenarios you'll run into on real projects.
|
TL;DR:
|
Playwright is an end-to-end testing and browser automation framework built by Microsoft. You can write tests in JavaScript, TypeScript, Python, Java, or .NET, and run them against Chromium, Firefox, and WebKit.
With it, you can automate the journeys your users actually take: logging in, registering, searching for products, filling a cart, checking out, submitting forms, and so on.
Here's a simple login test to give you a feel for it:
const { test, expect } = require('@playwright/test');
test('Verify successful login', async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
await page.locator('#user-name').fill('standard_user');
await page.locator('#password').fill('secret_sauce');
await page.locator('#login-button').click();
await expect(page).toHaveURL(/inventory.html/);
});
The script opens the app, types in the credentials, clicks Login, and checks that you landed on the inventory page. Now let's look at the features that make this so smooth.
A page that looks perfect in Chrome can behave strangely in Firefox or Safari. Playwright lets you run the same tests across three engines:
const { test, expect } = require('@playwright/test');
test('Verify application title', async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
await expect(page).toHaveTitle(/Swag Labs/);
});
Imagine an e-commerce site that has to work for Chrome, Firefox, and Safari users. Instead of writing three separate suites, you run one suite on all three engines and catch browser-specific bugs before your customers do.
If you've worked with Selenium, you know the pain of sprinkling explicit waits everywhere just to get a click to land. The playwright handles most of that for you. Before it performs an action, it checks that the element is visible, stable, enabled, and able to receive events.
await page.getByRole('button', { name: 'Login' }).click();
That's it. No extra wait code. If the Login button only becomes enabled after valid credentials are entered, Playwright simply waits until it's ready.
Compare that with the habit you should avoid:
// Avoid fixed waits like this
await page.waitForTimeout(5000);
await page.getByRole('button', { name: 'Login' }).click();
Fixed sleeps slow your suite down and still fail when the app is slower than expected. Auto-waiting means fewer flaky tests and faster runs.
Locators are how you point. Playwright at elements: textboxes, buttons, links, checkboxes, dropdowns. You get plenty of options:
// CSS locator
await page.locator('#user-name').fill('standard_user');
// XPath locator
await page.locator('//input[@id="password"]').fill('secret_sauce');
// Text locator
await page.getByText('Products').isVisible();
// Role locator
await page.getByRole('button', { name: 'Login' }).click();
On a registration form, for example, you might find fields by their labels, the submit button by its role, and error messages by their visible text. Locators that mirror how a user sees the page tend to be easier to read and much less likely to break when the markup changes.
Playwright's assertions don't just check once and give up. They keep retrying until the condition is met or the timeout runs out, which is exactly what you want in an app where things load dynamically.
const { test, expect } = require('@playwright/test');
test('Verify product page', async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
await page.locator('#user-name').fill('standard_user');
await page.locator('#password').fill('secret_sauce');
await page.locator('#login-button').click();
await expect(page.locator('.title')).toHaveText('Products');
});
Here Playwright keeps checking until the title reads "Products". Some assertions you'll use all the time:
await expect(locator).toBeVisible();
await expect(locator).toBeEnabled();
await expect(locator).toHaveText('Welcome');
await expect(page).toHaveURL(/dashboard/);
await expect(locator).toHaveValue('admin');
A browser context is like a fresh, independent browser profile, with its own cookies, local storage, and session storage. You can spin up several in a single test, which makes it easy to simulate multiple users without installing anything extra.
const { test, expect } = require('@playwright/test');
test('Multiple user sessions', async ({ browser }) => {
const user1 = await browser.newContext();
const user2 = await browser.newContext();
const page1 = await user1.newPage();
const page2 = await user2.newPage();
await page1.goto('https://www.saucedemo.com/');
await page2.goto('https://www.saucedemo.com/');
await page1.locator('#user-name').fill('standard_user');
await page2.locator('#user-name').fill('problem_user');
await user1.close();
await user2.close();
});
Think of an app with an admin and a customer. With two contexts, you can test both roles side by side, and nothing leaks from one session into the other.
Running tests one after another gets old fast. Playwright Test runs them in parallel using worker processes:
npx playwright test --workers=4
Or set it once in your config:
const { defineConfig } = require('@playwright/test');
module.exports = defineConfig({
workers: 4,
fullyParallel: true
});
If you have 100 independent tests, spreading them across several workers can cut total run time dramatically. It's especially handy for big regression suites and for getting quicker feedback during development. Just keep in mind that real-world speed-ups depend on your machine, how your tests are written, and how fast the application is.
Fixtures take care of reusable setup and teardown. Rather than repeating browser setup, login steps, or page initialization in every test, you define it once and let tests ask for what they need.
Playwright ships with built-in fixtures like page, browser, context, browserName, and request.
const { test, expect } = require('@playwright/test');
test.beforeEach(async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
});
test('Verify login page', async ({ page }) => {
await expect(page.locator('#login-button')).toBeVisible();
});
test('Verify username field', async ({ page }) => {
await expect(page.locator('#user-name')).toBeVisible();
});
When you need something more specific, you can extend Playwright's base test object and write your own custom fixtures. The payoff is less duplicate code and a framework that's easier to maintain.
Hooks run common setup and cleanup at specific points in a test run.
|
Hook |
When it runs |
|---|---|
|
beforeEach |
Before every test |
|
afterEach |
After every test |
|
beforeAll |
Once, before the tests in a scope |
|
afterAll |
Once, after the tests in a scope |
const { test, expect } = require('@playwright/test');
test.beforeEach(async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
});
test('Verify login button', async ({ page }) => {
await expect(page.locator('#login-button')).toBeVisible();
});
test.afterEach(async ({ page }) => {
console.log('Test execution completed');
});
If every test needs to start on the login page, one beforeEach handles the navigation and keeps your tests short and tidy.
POM is a design pattern that keeps your code organized. Instead of stuffing locators and actions into every test file, you put them in separate page classes and reuse them.
A simple folder structure looks like this:
PlaywrightProject/
│
├── pages/
│ └── LoginPage.js
│
├── tests/
│ └── login.spec.js
│
└── playwright.config.js
LoginPage.js
class LoginPage {
constructor(page) {
this.page = page;
this.username = page.locator('#user-name');
this.password = page.locator('#password');
this.loginButton = page.locator('#login-button');
}
async open() {
await this.page.goto('https://www.saucedemo.com/');
}
async login(username, password) {
await this.username.fill(username);
await this.password.fill(password);
await this.loginButton.click();
}
}
module.exports = LoginPage;
login.spec.js
const { test, expect } = require('@playwright/test');
const LoginPage = require('../pages/LoginPage');
test('Login using POM', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.open();
await loginPage.login('standard_user', 'secret_sauce');
await expect(page).toHaveURL(/inventory.html/);
});
The big win is maintenance. When a locator changes, you fix it in one place instead of hunting through dozens of tests.
Codegen records what you do in the browser and turns it into Playwright code. It's a great way to learn the syntax when you're starting out.
npx playwright codegen https://www.saucedemo.com/
Playwright opens a browser, you click around, and it writes the script as you go. A beginner can open SauceDemo, log in, and immediately see what the generated code looks like.
One word of caution: treat generated code as a starting point. Review it and clean it up before it goes anywhere near your real framework.
When a test fails and you can't tell why, the Inspector is your friend. It lets you step through a test line by line, pause execution, inspect locators, and look at the live state of the browser.
npx playwright test --debug
It's the fastest way to see which locator is being used, which action is running, and exactly where things go wrong.
Trace Viewer is like a flight recorder for your tests. It captures an action timeline, DOM snapshots, screenshots, network requests, console output, and errors.
npx playwright test --trace on
Then open the report:
npx playwright show-report
Say a checkout test fails because the Submit Order button wasn't there. Instead of re-running it again and again, you open the trace, scrub through the timeline, and look at the DOM right before the failure. It saves a lot of time.
Playwright generates a built-in HTML report after a run. It shows how many tests passed, failed, or were skipped, how long they took, the error details, and any attachments or traces.
npx playwright test
npx playwright show-report
After a regression run, you can share that report with developers, managers, and stakeholders so everyone sees the same picture.
Playwright isn't only for UI automation. You can send HTTP requests (GET, POST, PUT, PATCH, DELETE) and validate the responses, all in the same framework.
const { test, expect } = require('@playwright/test');
test('Verify API response', async ({ request }) => {
const response = await request.get(
'https://jsonplaceholder.typicode.com/posts/1'
);
expect(response.status()).toBe(200);
const responseBody = await response.json();
expect(responseBody.id).toBe(1);
});
For instance, before testing a product listing page, you can quickly confirm the product API returns what you expect. For basic workflows, it can reduce the need for a separate API testing tool, and it makes combined API plus UI tests straightforward.
Playwright can watch, intercept, modify, and mock network requests. That's useful when your app depends on external services you don't control.
const { test, expect } = require('@playwright/test');
test('Mock product API response', async ({ page }) => {
await page.route('**/api/products', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, name: 'Laptop', price: 50000 }
])
});
});
await page.goto('https://example.com');
});
If a product API is down or flaky, you can mock the response and still test how the front end behaves. It's also a great way to simulate errors and edge cases, and it keeps your test data predictable.
If your app lets people upload resumes, images, or documents, or download reports, Playwright has you covered.
Upload:
await page.locator('input[type="file"]')
.setInputFiles('tests/sample.pdf');
Download:
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: 'Download' }).click();
const download = await downloadPromise;
console.log(await download.suggestedFilename());
In a recruitment app, for example, you could automate uploading a resume and then check that the downloaded application document has the right filename.
Logging in before every single test is a waste of time. Playwright lets you log in once, save the session, and reuse it.
Save the session:
const { test } = require('@playwright/test');
test('Save login session', async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
await page.locator('#user-name').fill('standard_user');
await page.locator('#password').fill('secret_sauce');
await page.locator('#login-button').click();
await page.context().storageState({
path: 'auth.json'
});
});
Reuse it:
const context = await browser.newContext({
storageState: 'auth.json'
});
Think of a banking app where dozens of tests need an authenticated customer. Reusing a saved session skips the repeated logins and speeds up your regression runs.
One important note: those state files can contain sensitive cookies or tokens. Keep them out of source control and protect them like any other secret.
Playwright can imitate real devices by setting the viewport, user agent, device scale factor, touch support, and other mobile browser traits.
const { test, devices } = require('@playwright/test');
test.use({
...devices['iPhone 13']
});
test('Verify mobile application', async ({ page }) => {
await page.goto('https://www.saucedemo.com/');
await page.screenshot({
path: 'mobile-view.png'
});
});
If your e-commerce site has to look right on desktop, tablet, and phone, this is a quick way to check the responsive layout without a drawer full of devices.
Sometimes the easiest way to explain a failure is to show it. Playwright can capture screenshots and record video during a run.
Screenshot:
await page.screenshot({
path: 'homepage.png',
fullPage: true
});
Video and screenshot settings in the config:
const { defineConfig } = require('@playwright/test');
module.exports = defineConfig({
use: {
video: 'retain-on-failure',
screenshot: 'only-on-failure'
}
});
When a checkout test fails, a screenshot or recording shows testers and developers exactly what the screen looked like at that moment. It makes bug reports clearer and cuts down the back-and-forth.
Automation really pays off when it runs on its own. Playwright works with GitHub Actions, Jenkins, Azure DevOps, GitLab CI, and more, so your tests can run on every code push or deployment.
Here's a basic GitHub Actions workflow. Save it as .github/workflows/playwright.yml:
name: Playwright Tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
Now every push to main kicks off your regression tests and publishes the report as an artifact. Bugs get caught earlier, manual regression shrinks, and releases move faster.
Selenium has been the standard for years, and it's still a solid choice. But modern web apps put a lot of pressure on synchronization, debugging, parallel runs, and cross-browser coverage, and Playwright bundles many of those things in one package.
|
Feature |
Playwright |
Selenium |
|---|---|---|
|
Browser support |
Chromium, Firefox, WebKit |
Multiple browsers through drivers |
|
Waiting |
Built-in actionability checks |
Explicit waits commonly used |
|
Locators |
CSS, XPath, role, text, label, and more |
CSS, XPath, ID, name, and more |
|
Isolation |
Browser contexts |
Separate browser sessions |
|
Parallel execution |
Built into Playwright Test |
Usually via Grid or test frameworks |
|
API testing |
Built in |
Usually needs extra libraries |
|
Debugging |
Inspector and Trace Viewer |
Browser and framework tools |
|
Code generation |
Built-in Codegen |
Tooling and external options |
|
Languages |
JavaScript, TypeScript, Python, Java, .NET |
Multiple languages |
So which should you learn? Honestly, it depends on your project, your team, and the framework you already have. Selenium is still very valuable for organizations with established suites. Playwright is a great pick if you're building something new.
A lot of manual testers assume automation means being a programming expert from day one. It doesn't. With a clear path and some patience, you can build these skills step by step. Here's a roadmap that works well:
|
Stage |
What to learn |
|---|---|
|
1 |
Software testing fundamentals |
|
2 |
JavaScript basics |
|
3 |
Installing and setting up Playwright |
|
4 |
Locators and XPath |
|
5 |
Browser actions and assertions |
|
6 |
Hooks and fixtures |
|
7 |
Page Object Model |
|
8 |
API testing |
|
9 |
Building a framework |
|
10 |
Reporting and CI/CD |
My advice: pick a practice site like SauceDemo and work through real scenarios.
Building something end to end is the best way to see how all these features fit together.
If you're searching for Playwright automation training in Pune, Mumbai, or anywhere in India, look for a program that's genuinely hands-on. A good course should cover:
At TestoMeter Edutech, we focus on practical, industry-oriented IT training that helps students and working professionals build skills employers actually look for. Our software testing and automation programs are built around hands-on exercises, real-world scenarios, and framework development.
Whether you're a manual tester, a Selenium professional, or someone just starting out in software testing, building practical Playwright skills can be a great next step. Learn more at testometer.co.in.
1. What is Playwright used for?
Answer: Automating web application testing: logins, navigation, form submissions, cart workflows, API checks, and full end-to-end business scenarios.
2. What are its most important features?
Answer: Auto-waiting, cross-browser testing, browser context isolation, flexible locators, web-first assertions, parallel execution, fixtures, API testing, Trace Viewer, and CI/CD integration.
3. Is Playwright better than Selenium?
Answer: Playwright offers built-in auto-waiting, browser contexts, API testing, and strong debugging tools. Selenium has wide browser and language support and is still widely used. The right choice depends on your project and your existing stack.
4. Can beginners learn Playwright?
Answer: Yes. Start with JavaScript basics, get comfortable with locators and browser actions, then move on to fixtures, POM, API testing, and framework development.
5. Does Playwright support API testing?
Answer: Yes, through its APIRequestContext, which lets you send HTTP requests and validate responses.
6. Which language is best for Playwright?
Answer: JavaScript and TypeScript are the most popular. Beginners can start with JavaScript and pick up TypeScript as their projects grow.
7. Can Playwright run tests on Chrome, Firefox, and Safari?
Answer: Yes. It supports Chromium, Firefox, and WebKit. WebKit is the engine associated with Safari, so it helps you validate Safari-like behavior.
8. What is the Page Object Model?
Answer: A design pattern that keeps page locators and reusable actions separate from test cases, which makes code easier to organize, reuse, and maintain.
9. What's the difference between fixtures and hooks?
Answer: Hooks run setup and cleanup at specific points in a test run. Fixtures provide reusable setup and resources that tests request when they need them.
10. Where can I learn Playwright in Pune or Mumbai?
Answer: Look for a practical program that covers JavaScript, locators, hooks, fixtures, POM, API testing, framework development, and real projects. TestoMeter Edutech offers IT training and software testing courses for students and working professionals.
Playwright is more than a browser automation tool. From locators and auto-waiting to API testing, network mocking, tracing, and CI/CD, it gives QA engineers what they need to build reliable, scalable, maintainable test frameworks.
If you're a manual tester, it's a real opening into automation. If you're a Selenium veteran, it's another strong tool for your toolkit. The best time to start is now, and the best way to learn is by building something.
Ready to get going? Start building your Playwright skills with TestoMeter Edutech at testometer.co.in, and follow us for more practical tutorials, testing tips, and certification updates.
Learn how Agentic AI transforms software testing with real-world examples, Playwright automation, API testing, benefits, risks, and QA career skills in India.
Discover AI-powered software testing in 2027, including Generative AI, test automation tools, benefits, challenges, QA career skills, and training options in India.
Thinking about ISTQB CTAL-TAE v2.0? See the syllabus, eligibility, exam format, prep tips and upcoming training with Yogesh sir at TestoMeter.
Compare PSM I vs PSM-AI Essentials, including exam format, syllabus, benefits and career relevance. Explore Scrum certification training with TestoMeter.
Planning to take ISTQB CTFL v4.0? Explore the syllabus, 40-question exam format, passing score, study tips and how to choose training in Pune, Mumbai and India.