Playwright: The Modern E2E Testing Framework That Replaced Cypress for Many Teams
Playwright runs real browsers in parallel, supports multiple tabs and origins in one test, and has a codegen recorder - here is why teams are switching from Cypress and how to get started.
Mahmudul Haque Qudrati
CEO & ML Engineer
One AI engineering post, weekly
LLM benchmarks, prompt techniques, and token-cost breakdowns — not another AI news roundup.
Cypress was the gold standard for E2E testing from 2018-2022. Playwright (launched by Microsoft in 2020) has since overtaken it for many teams because of four specific limitations Cypress has that Playwright does not:
- Multiple browsers in one test run - Playwright tests against Chromium, Firefox, and WebKit simultaneously
- Multiple tabs and origins - Playwright can open two tabs and test cross-tab communication
- No port 4000 restriction - Playwright talks to any URL, including production
- Faster - Playwright uses a CDP/WebSocket bridge instead of running inside the browser iframe
Installation
pnpm add -D @playwright/test
npx playwright install # installs Chromium, Firefox, WebKit
// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e",
fullyParallel: true,
reporter: "html",
use: {
baseURL: "http://localhost:3000",
trace: "on-first-retry",
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "mobile", use: { ...devices["iPhone 15"] } },
],
webServer: {
command: "pnpm dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
Team workspace
Ship faster with chat, meetings, and projects in one place — Zlyqor.
Writing Tests With Role-Based Locators
Playwright prefers getByRole() locators because they test accessibility at the same time:
import { test, expect } from "@playwright/test";
test("create a task", async ({ page }) => {
await page.goto("/projects/proj-123");
// Prefer getByRole over CSS selectors
await page.getByRole("button", { name: "Add Task" }).click();
await page.getByRole("textbox", { name: "Task title" }).fill("Fix login bug");
await page.getByRole("button", { name: "Save" }).click();
// Assert the task appears
await expect(page.getByText("Fix login bug")).toBeVisible();
await expect(page.getByRole("status")).toHaveText("Task created");
});
Test Fixtures for Auth
// e2e/fixtures.ts
import { test as base } from "@playwright/test";
type Fixtures = { authenticatedPage: Page };
export const test = base.extend<Fixtures>({
authenticatedPage: async ({ page }, use) => {
await page.goto("/login");
await page.getByLabel("Email").fill("test@example.com");
await page.getByLabel("Password").fill("testpass123");
await page.getByRole("button", { name: "Sign in" }).click();
await page.waitForURL("/dashboard");
await use(page);
},
});
// In tests:
import { test } from "./fixtures";
test("view dashboard", async ({ authenticatedPage: page }) => {
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
API Testing
Playwright can test your API routes directly - no browser needed:
import { test, expect } from "@playwright/test";
test("POST /api/tasks", async ({ request }) => {
const response = await request.post("/api/tasks", {
data: { title: "New task", projectId: "proj-123" },
headers: { Authorization: "Bearer test-token" },
});
expect(response.status()).toBe(201);
const body = await response.json();
expect(body).toMatchObject({ title: "New task" });
});
Codegen - Record Your Tests
# Open the browser and record interactions as Playwright test code
npx playwright codegen http://localhost:3000
The codegen recorder generates test code in real-time as you click through the app. It is a starting point - you will clean it up - but it dramatically speeds up writing the initial test skeleton.
Parallel Runs and CI
# Run all tests in parallel
npx playwright test
# Run with UI for debugging
npx playwright test --ui
# CI: run headless with retries
CI=true npx playwright test --retries=2
References: Playwright · GitHub · VS Code extension

Mahmudul Haque Qudrati
CEO & ML Engineer
Visionary technologist, software engineer, and machine learning specialist. Founder and CEO of Pristren, directing engineering teams that ship production-grade AI/ML pipelines, mission-critical full-stack applications, and developer tooling. Creator of Zlyqor, the unified team workspace platform. Author of 540+ technical guides and benchmark research reports on large language models, agentic workflows, Model Context Protocol (MCP), and modern web stacks.
More from Mahmudul
Related Articles
How to Use Claude to Make Videos Like Vox and Others
Claude can help you make Vox-style videos by generating scripts, editing with code, and automating animation. Here's a practical guide with real workflows and costs.
OpenAI Ends Cursor Model Access on Nov 12, 2026: What Developers Need to Know
OpenAI will terminate Cursor's access to its models on November 12, 2026, following SpaceX's acquisition. This guide explains the timeline, why it happened, and practical steps to migrate your workflow.
I Used Claude Code to Get a Second Opinion on My MRI: A Practical Overview
A developer fed his MRI scan to Claude Code and got a second opinion. Here's how the experiment worked, what it cost, and why you shouldn't rely on it for medical decisions.
// discussion
Comments