Playwright Has Free E2E Testing That Is Faster and More Reliable Than Cypress

# playwright# testing# e2e# automation
Playwright Has Free E2E Testing That Is Faster and More Reliable Than CypressAlex Spinov

Cypress runs tests in the browser. Playwright controls the browser from outside. This fundamental...

Cypress runs tests in the browser. Playwright controls the browser from outside. This fundamental difference makes Playwright faster, more reliable, and more capable.

Why Playwright Wins

Feature Cypress Playwright
Multi-browser Chrome only (real) Chromium, Firefox, WebKit
Multi-tab No Yes
iframes Limited Full support
File download Workarounds Native
Network interception Yes Yes (more powerful)
Parallel tests Paid (Dashboard) Free (built-in)
Auto-waiting Yes Yes (smarter)
Speed Medium Fast
Mobile emulation Limited Full (devices, geolocation)

Setup

npm init playwright@latest
# Installs browsers, creates config, example tests
Enter fullscreen mode Exit fullscreen mode

Writing Tests

import { test, expect } from "@playwright/test";

test("user can sign up", async ({ page }) => {
  await page.goto("/signup");

  await page.getByLabel("Email").fill("test@example.com");
  await page.getByLabel("Password").fill("SecurePass123!");
  await page.getByRole("button", { name: "Create Account" }).click();

  await expect(page.getByText("Welcome!")).toBeVisible();
  await expect(page).toHaveURL("/dashboard");
});
Enter fullscreen mode Exit fullscreen mode

Auto-Waiting (No Flaky Tests)

Playwright auto-waits for elements to be:

  • Visible
  • Enabled
  • Stable (not animating)
  • Receiving events
// Cypress — needs explicit waits
cy.get("button").should("be.visible").click();

// Playwright — auto-waits, just click
await page.getByRole("button").click();
Enter fullscreen mode Exit fullscreen mode

Locators (Best Practice)

// By role (most reliable)
page.getByRole("button", { name: "Submit" });
page.getByRole("heading", { level: 1 });
page.getByRole("link", { name: "Dashboard" });

// By label (forms)
page.getByLabel("Email address");
page.getByLabel("Password");

// By placeholder
page.getByPlaceholder("Search...");

// By text
page.getByText("Welcome back");
page.getByText(/total: \$\d+/);

// By test ID (last resort)
page.getByTestId("submit-btn");
Enter fullscreen mode Exit fullscreen mode

API Testing

test("API: create user", async ({ request }) => {
  const response = await request.post("/api/users", {
    data: { name: "Alice", email: "alice@test.com" },
  });

  expect(response.ok()).toBeTruthy();
  const body = await response.json();
  expect(body.id).toBeDefined();
  expect(body.name).toBe("Alice");
});
Enter fullscreen mode Exit fullscreen mode

Visual Regression Testing

test("homepage looks correct", async ({ page }) => {
  await page.goto("/");
  await expect(page).toHaveScreenshot("homepage.png");
});

test("button states", async ({ page }) => {
  await page.goto("/components");
  const button = page.getByRole("button", { name: "Submit" });

  await expect(button).toHaveScreenshot("button-default.png");
  await button.hover();
  await expect(button).toHaveScreenshot("button-hover.png");
});
Enter fullscreen mode Exit fullscreen mode

Built-in pixel diffing — no extra tools needed.

Network Mocking

test("shows error on API failure", async ({ page }) => {
  await page.route("/api/users", (route) =>
    route.fulfill({ status: 500, body: "Server Error" })
  );

  await page.goto("/users");
  await expect(page.getByText("Failed to load")).toBeVisible();
});
Enter fullscreen mode Exit fullscreen mode

Parallel Execution (Free)

// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 4 : undefined,
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
});
Enter fullscreen mode Exit fullscreen mode

Run tests across multiple workers for free. No paid dashboard needed.


Need test automation or web scraping? I build browser automation and data tools. Email spinov001@gmail.com or check my Apify tools.