playwright vs nightmare vs puppeteer vs selenium-webdriver
Browser Automation and End-to-End Testing in Node.js
playwrightnightmarepuppeteerselenium-webdriverSimilar Packages:

Browser Automation and End-to-End Testing in Node.js

nightmare, playwright, puppeteer, and selenium-webdriver are libraries used to automate web browsers for testing, scraping, and task automation. They allow developers to script interactions like clicking buttons, filling forms, and navigating pages programmatically. While selenium-webdriver is the legacy standard supporting many languages and browsers, puppeteer and playwright are modern Node.js-first tools built for Chromium and multi-browser support respectively. nightmare was an early high-level abstraction but is no longer actively maintained.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
playwright37,148,79683,8373.72 MB607a month agoApache-2.0
nightmare12,47619,966-2037 years agoMIT
puppeteer093,77463 kB2905 days agoApache-2.0
selenium-webdriver034,09517.9 MB20318 days agoApache-2.0

Browser Automation Showdown: Playwright vs Puppeteer vs Selenium vs Nightmare

Automating browser interactions is a core requirement for modern web development, whether for end-to-end testing, web scraping, or generating screenshots. The ecosystem offers four major contenders: nightmare, playwright, puppeteer, and selenium-webdriver. While they all aim to control a browser, their architectures, maintenance status, and developer experiences differ significantly. Let's break down how they handle real-world engineering challenges.

⚠️ Maintenance Status: Active vs Abandoned

Before writing a single line of code, you must know which tools are safe to bet on.

nightmare is effectively abandoned.

  • It relies on Electron, and breaking changes in Electron halted its development years ago.
  • It does not support modern JavaScript features or current web standards reliably.
  • Recommendation: Do not use for new projects.
// nightmare: No longer receiving security patches or feature updates
// const Nightmare = require('nightmare');
// const nightmare = Nightmare({ show: true });
// Avoid this in production

playwright is actively maintained by Microsoft.

  • Receives frequent updates for new browser versions.
  • Built specifically for modern automation needs.
// playwright: Actively updated
// npm install playwright
const { chromium } = require('playwright');

puppeteer is actively maintained by the Chrome DevTools team.

  • Focuses heavily on Chrome/Chromium ecosystem features.
  • Stable and reliable for Google-centric workflows.
// puppeteer: Actively updated
// npm install puppeteer
const puppeteer = require('puppeteer');

selenium-webdriver is actively maintained by the Selenium project.

  • The industry standard for cross-language support.
  • Slower release cycle for Node.js specific bindings compared to Playwright.
// selenium-webdriver: Actively updated
// npm install selenium-webdriver
const { Builder } = require('selenium-webdriver');

🌐 Browser Support: Single vs Multi-Engine

Your choice often depends on which browsers your users actually use.

playwright supports three engines out of the box.

  • Chromium (Chrome/Edge), Firefox, and WebKit (Safari).
  • One API works across all of them without configuration changes.
// playwright: Multi-browser support
const { chromium, firefox, webkit } = require('playwright');

const browser = await chromium.launch(); // Or firefox.launch() or webkit.launch()
const page = await browser.newPage();

puppeteer focuses on Chromium.

  • Works best with Chrome.
  • Firefox support exists but is experimental and not the primary focus.
// puppeteer: Chromium focused
const puppeteer = require('puppeteer');

const browser = await puppeteer.launch({ product: 'chrome' }); // 'firefox' is experimental
const page = await browser.newPage();

selenium-webdriver supports everything.

  • Chrome, Firefox, Safari, Edge, and even legacy Internet Explorer.
  • Requires separate driver binaries (e.g., chromedriver, geckodriver) for each browser.
// selenium-webdriver: Requires external drivers
const { Builder, Browser } = require('selenium-webdriver');

// Must have chromedriver installed separately
const driver = await new Builder().forBrowser(Browser.CHROME).build();

nightmare supports Electron only.

  • Essentially a specific version of Chromium wrapped in Electron.
  • Cannot test Safari or Firefox behavior.
// nightmare: Electron only
const Nightmare = require('nightmare');
const nightmare = Nightmare({ show: true });
// Limited to Electron's Chromium version

⏳ Waiting for Elements: Auto-Wait vs Manual Polling

Flaky tests often happen because scripts run faster than the UI renders. How each library handles this defines your debugging time.

playwright has auto-waiting built in.

  • Actions like click() wait for elements to be visible and actionable automatically.
  • Reduces the need for explicit sleep or waitFor calls.
// playwright: Auto-waits before action
await page.click('button#submit'); // Waits for element to be stable

puppeteer requires explicit waiting.

  • You must tell it to wait for selectors before interacting.
  • Gives more control but adds boilerplate.
// puppeteer: Manual wait required
await page.waitForSelector('button#submit');
await page.click('button#submit');

selenium-webdriver uses explicit waits via ExpectedConditions.

  • Verbose syntax for waiting for specific states.
  • Highly configurable but verbose.
// selenium-webdriver: Explicit wait logic
const { until } = require('selenium-webdriver');
await driver.wait(until.elementLocated(By.id('submit')), 5000);
await driver.findElement(By.id('submit')).click();

nightmare chains actions implicitly.

  • Queues actions and runs them sequentially.
  • Can be unpredictable if dynamic content loads slowly.
// nightmare: Implicit chaining
await nightmare
  .wait('button#submit')
  .click('button#submit')
  .end();

πŸ› οΈ API Design: High-Level vs Low-Level

The way you write code affects how easy it is to maintain large test suites.

playwright uses a context and page model.

  • Browser Contexts are isolated like incognito profiles.
  • Great for parallel testing without cookie leakage.
// playwright: Context isolation
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com');

puppeteer uses a browser and page model.

  • Similar to Playwright but slightly less abstraction on contexts.
  • Direct access to Chrome DevTools Protocol (CDP).
// puppeteer: Browser/Page model
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');

selenium-webdriver uses a driver and element model.

  • More verbose, closer to how the browser protocol actually works.
  • Requires finding elements before acting on them.
// selenium-webdriver: Driver/Element model
const driver = await new Builder().forBrowser('chrome').build();
await driver.get('https://example.com');
const element = await driver.findElement(By.css('h1'));

nightmare uses a chainable queue model.

  • Fluent interface that reads like a story.
  • Harder to handle complex conditional logic inside the chain.
// nightmare: Chainable queue
await nightmare
  .goto('https://example.com')
  .evaluate(() => document.title);

πŸ“Έ Advanced Features: Screenshots, PDF, and Network

Modern automation often requires more than just clicking buttons.

playwright includes network interception natively.

  • Can mock API responses or block resources easily.
  • Supports PDF and screenshots out of the box.
// playwright: Network mocking
await page.route('**/api/data', route => route.fulfill({ json: { mock: true } }));
await page.pdf({ path: 'page.pdf' });

puppeteer excels at PDF generation.

  • Since it controls Chrome directly, PDF output is high fidelity.
  • Network interception is possible but slightly more verbose than Playwright.
// puppeteer: PDF focus
await page.setRequestInterception(true);
await page.pdf({ path: 'page.pdf', format: 'A4' });

selenium-webdriver lacks native network mocking.

  • Requires proxy tools (like BrowserMob) to intercept traffic.
  • Screenshots are supported, PDF is not native.
// selenium-webdriver: Limited network control
await driver.takeScreenshot(); // Returns base64 string
// No native PDF support

nightmare has basic screenshot support.

  • Simple API for capturing viewports.
  • No advanced network control.
// nightmare: Basic screenshot
await nightmare.screenshot('screen.png');

🀝 Similarities: Shared Ground

Despite their differences, these tools share common goals and patterns.

1. πŸ€– Headless Execution

  • All support running browsers without a visible UI for CI/CD environments.
// All support headless mode
// Playwright
await chromium.launch({ headless: true });

// Puppeteer
await puppeteer.launch({ headless: 'new' });

// Selenium
new Builder().setChromeOptions(new chrome.Options().headless());

// Nightmare
Nightmare({ show: false });

2. πŸ” DOM Querying

  • All use CSS selectors to find elements on the page.
// Common selector usage
// Playwright
await page.click('.btn-primary');

// Puppeteer
await page.click('.btn-primary');

// Selenium
await driver.findElement(By.css('.btn-primary'));

// Nightmare
nightmare.click('.btn-primary');

3. πŸ“· Screenshot Capability

  • Visual regression testing is possible with all tools.
// Screenshot examples
// Playwright
await page.screenshot({ path: 'test.png' });

// Puppeteer
await page.screenshot({ path: 'test.png' });

// Selenium
await driver.takeScreenshot();

// Nightmare
await nightmare.screenshot('test.png');

πŸ“Š Summary: Key Differences

Featureplaywrightpuppeteerselenium-webdrivernightmare
Statusβœ… Activeβœ… Activeβœ… Active❌ Abandoned
BrowsersChromium, Firefox, WebKitChromium (mostly)All (via drivers)Electron
Auto-Waitβœ… Yes❌ Manual❌ Manual⚠️ Implicit
Network Mockβœ… Nativeβœ… Native❌ External Proxy❌ No
PDF Supportβœ… Yesβœ… Yes❌ No❌ No
SetupEasy (bundled browsers)Easy (bundled browser)Hard (manage drivers)Easy (bundled)

πŸ’‘ The Big Picture

playwright is the modern standard for end-to-end testing.

  • Best for teams needing cross-browser coverage and reliability.
  • Handles flakiness better than any other tool due to auto-waiting.

puppeteer is the specialist for Chrome tasks.

  • Best for scraping, PDF generation, or testing Chrome extensions.
  • Ideal if you don't care about Safari or Firefox.

selenium-webdriver is the enterprise legacy choice.

  • Best if you need to test Internet Explorer or integrate with a Java-based grid.
  • Requires more maintenance but offers maximum compatibility.

nightmare is a legacy tool.

  • Do not use.
  • Migrate to playwright for a similar high-level API with active support.

Final Thought: For most new Node.js projects, playwright offers the best balance of power, reliability, and developer experience. Use puppeteer only if you have specific Chrome-only requirements. Avoid nightmare entirely.

How to Choose: playwright vs nightmare vs puppeteer vs selenium-webdriver

  • playwright:

    Choose playwright if you need reliable cross-browser testing (Chromium, Firefox, WebKit) with a single API. It is ideal for complex end-to-end testing scenarios requiring network interception, mobile emulation, and auto-waiting capabilities. It is maintained by Microsoft and designed for modern web applications.

  • nightmare:

    Do NOT choose nightmare for new projects. It is effectively deprecated and unmaintained, relying on older versions of Electron that lack modern web standards support. Existing projects using it should plan a migration to playwright or puppeteer to ensure security and compatibility with current websites.

  • puppeteer:

    Choose puppeteer if your focus is primarily on Chromium or Chrome-specific features like PDF generation or Chrome extension testing. It is a solid choice for scraping tasks or CI pipelines where Chrome is guaranteed. It offers a lower-level API than Playwright but is highly stable for single-browser use cases.

  • selenium-webdriver:

    Choose selenium-webdriver if you need to support legacy browsers like Internet Explorer or require a language-agnostic grid setup (e.g., Java backend with Node tests). It is suitable for large enterprises with existing Selenium infrastructure, though it often requires more boilerplate code than modern alternatives.

README for playwright

🎭 Playwright

npm version Chromium version Firefox version WebKit version Join Discord

Documentation | API reference

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable, and fast.

LinuxmacOSWindows
Chromium 145.0.7632.6:white_check_mark::white_check_mark::white_check_mark:
WebKit 26.0:white_check_mark::white_check_mark::white_check_mark:
Firefox 146.0.1:white_check_mark::white_check_mark::white_check_mark:

Headless execution is supported for all browsers on all platforms. Check out system requirements for details.

Looking for Playwright for Python, .NET, or Java?

Installation

Playwright has its own test runner for end-to-end tests, we call it Playwright Test.

Using init command

The easiest way to get started with Playwright Test is to run the init command.

# Run from your project's root directory
npm init playwright@latest
# Or create a new project
npm init playwright@latest new-project

This will create a configuration file, optionally add examples, a GitHub Action workflow and a first test example.spec.ts. You can now jump directly to writing assertions section.

Manually

Add dependency and install browsers.

npm i -D @playwright/test
# install supported browsers
npx playwright install

You can optionally install only selected browsers, see install browsers for more details. Or you can install no browsers at all and use existing browser channels.

Capabilities

Resilient β€’ No flaky tests

Auto-wait. Playwright waits for elements to be actionable prior to performing actions. It also has a rich set of introspection events. The combination of the two eliminates the need for artificial timeouts - a primary cause of flaky tests.

Web-first assertions. Playwright assertions are created specifically for the dynamic web. Checks are automatically retried until the necessary conditions are met.

Tracing. Configure test retry strategy, capture execution trace, videos and screenshots to eliminate flakes.

No trade-offs β€’ No limits

Browsers run web content belonging to different origins in different processes. Playwright is aligned with the architecture of the modern browsers and runs tests out-of-process. This makes Playwright free of the typical in-process test runner limitations.

Multiple everything. Test scenarios that span multiple tabs, multiple origins and multiple users. Create scenarios with different contexts for different users and run them against your server, all in one test.

Trusted events. Hover elements, interact with dynamic controls and produce trusted events. Playwright uses real browser input pipeline indistinguishable from the real user.

Test frames, pierce Shadow DOM. Playwright selectors pierce shadow DOM and allow entering frames seamlessly.

Full isolation β€’ Fast execution

Browser contexts. Playwright creates a browser context for each test. Browser context is equivalent to a brand new browser profile. This delivers full test isolation with zero overhead. Creating a new browser context only takes a handful of milliseconds.

Log in once. Save the authentication state of the context and reuse it in all the tests. This bypasses repetitive log-in operations in each test, yet delivers full isolation of independent tests.

Powerful Tooling

Codegen. Generate tests by recording your actions. Save them into any language.

Playwright inspector. Inspect page, generate selectors, step through the test execution, see click points and explore execution logs.

Trace Viewer. Capture all the information to investigate the test failure. Playwright trace contains test execution screencast, live DOM snapshots, action explorer, test source and many more.

Looking for Playwright for TypeScript, JavaScript, Python, .NET, or Java?

Examples

To learn how to run these Playwright Test examples, check out our getting started docs.

Page screenshot

This code snippet navigates to Playwright homepage and saves a screenshot.

import { test } from '@playwright/test';

test('Page Screenshot', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await page.screenshot({ path: `example.png` });
});

Mobile and geolocation

This snippet emulates Mobile Safari on a device at given geolocation, navigates to maps.google.com, performs the action and takes a screenshot.

import { test, devices } from '@playwright/test';

test.use({
  ...devices['iPhone 13 Pro'],
  locale: 'en-US',
  geolocation: { longitude: 12.492507, latitude: 41.889938 },
  permissions: ['geolocation'],
})

test('Mobile and geolocation', async ({ page }) => {
  await page.goto('https://maps.google.com');
  await page.getByText('Your location').click();
  await page.waitForRequest(/.*preview\/pwa/);
  await page.screenshot({ path: 'colosseum-iphone.png' });
});

Evaluate in browser context

This code snippet navigates to example.com, and executes a script in the page context.

import { test } from '@playwright/test';

test('Evaluate in browser context', async ({ page }) => {
  await page.goto('https://www.example.com/');
  const dimensions = await page.evaluate(() => {
    return {
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight,
      deviceScaleFactor: window.devicePixelRatio
    }
  });
  console.log(dimensions);
});

Intercept network requests

This code snippet sets up request routing for a page to log all network requests.

import { test } from '@playwright/test';

test('Intercept network requests', async ({ page }) => {
  // Log and continue all network requests
  await page.route('**', route => {
    console.log(route.request().url());
    route.continue();
  });
  await page.goto('http://todomvc.com');
});

Resources