Playwright is a modern end-to-end testing framework from Microsoft. It tests Chromium, Firefox and WebKit (Safari's engine) with one API. It waits for elements automatically and ships with excellent debugging tools, which is why many teams now choose it for new projects.
Install
You need Node.js 18 or newer.
npm init playwright@latestChoose TypeScript, keep the tests folder, and say yes to installing browsers. You get a playwright.config.ts file and an example test.
Run the tests:
npx playwright test
npx playwright show-reportYour first test
import { test, expect } from "@playwright/test";
test("home page has the right title", async ({ page }) => {
await page.goto("https://playwright.dev/");
await expect(page).toHaveTitle(/Playwright/);
});
test("get started link opens the installation page", async ({ page }) => {
await page.goto("https://playwright.dev/");
await page.getByRole("link", { name: "Get started" }).click();
await expect(page.getByRole("heading", { name: "Installation" })).toBeVisible();
});Each test gets a fresh, isolated browser context (like a new incognito window), so tests do not leak cookies or state into each other.
Locators: find elements the way users do
Playwright recommends user-facing locators. They match what a real user sees, so they survive CSS and layout changes.
| Locator | Example |
|---|---|
| By role | page.getByRole("button", { name: "Login" }) |
| By label | page.getByLabel("Email") |
| By placeholder | page.getByPlaceholder("Search courses") |
| By text | page.getByText("Welcome back") |
| By test id | page.getByTestId("cart-count") |
Locators are strict: if a locator matches two elements when you act on it, Playwright throws an error instead of silently clicking the wrong one. Narrow it with .filter() or by locating inside a parent.
const row = page.getByRole("listitem").filter({ hasText: "Playwright + AI" });
await row.getByRole("button", { name: "Enroll" }).click();Auto-waiting and web-first assertions
Before clicking, Playwright waits until the element is attached, visible, stable and enabled. Assertions such as toBeVisible() and toHaveText() keep retrying until they pass or the timeout ends.
await page.getByLabel("Email").fill("ravi@test.com");
await page.getByLabel("Password").fill("Valid@123");
await page.getByRole("button", { name: "Login" }).click();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByRole("heading", { level: 1 })).toContainText("Welcome");There are no sleep calls and no manual waits. That alone removes most flaky tests.
Debugging tools
npx playwright test --uiopens UI mode: run tests, watch every step and time-travel through them.npx playwright codegen https://your-app.testrecords your clicks and generates test code.- Set
trace: "on-first-retry"in the config, then open a failed run withnpx playwright show-traceto see screenshots, network calls and console logs for every step.
Playwright vs Selenium
| Playwright | Selenium | |
|---|---|---|
| Waiting | Automatic | Manual explicit waits |
| Languages | TS/JS, Python, Java, .NET | Java, Python, C#, JS, Ruby and more |
| Browsers | Chromium, Firefox, WebKit | All major browsers via drivers |
| Built-in runner & reports | Yes | Use TestNG/JUnit + add-ons |
| API testing | Built in (request) | Separate tool needed |
Both are valuable. Selenium dominates existing enterprise suites; Playwright is growing fast for new projects.
Interview questions
- What is a browser context? An isolated session inside one browser instance. It is cheap to create and gives each test a clean state.
- How do you reuse login across tests? Log in once in a setup project, save
storageStateto a file, and load it in other tests. - How do you run tests in parallel? Playwright runs test files in parallel workers by default; control this with
workersandfullyParallelin the config.
Next steps
Add a Page Object for the login page and run your suite in GitHub Actions. Learn API testing, visual testing and AI-assisted test generation in the Playwright + AI course.
