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.
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.
Before writing a single line of code, you must know which tools are safe to bet on.
nightmare is effectively abandoned.
// 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.
// playwright: Actively updated
// npm install playwright
const { chromium } = require('playwright');
puppeteer is actively maintained by the Chrome DevTools team.
// puppeteer: Actively updated
// npm install puppeteer
const puppeteer = require('puppeteer');
selenium-webdriver is actively maintained by the Selenium project.
// selenium-webdriver: Actively updated
// npm install selenium-webdriver
const { Builder } = require('selenium-webdriver');
Your choice often depends on which browsers your users actually use.
playwright supports three engines out of the box.
// 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.
// 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.
// 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.
// nightmare: Electron only
const Nightmare = require('nightmare');
const nightmare = Nightmare({ show: true });
// Limited to Electron's Chromium version
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.
click() wait for elements to be visible and actionable automatically.sleep or waitFor calls.// playwright: Auto-waits before action
await page.click('button#submit'); // Waits for element to be stable
puppeteer requires explicit waiting.
// puppeteer: Manual wait required
await page.waitForSelector('button#submit');
await page.click('button#submit');
selenium-webdriver uses explicit waits via ExpectedConditions.
// 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.
// nightmare: Implicit chaining
await nightmare
.wait('button#submit')
.click('button#submit')
.end();
The way you write code affects how easy it is to maintain large test suites.
playwright uses a context and page model.
// 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.
// 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.
// 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.
// nightmare: Chainable queue
await nightmare
.goto('https://example.com')
.evaluate(() => document.title);
Modern automation often requires more than just clicking buttons.
playwright includes network interception natively.
// 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.
// puppeteer: PDF focus
await page.setRequestInterception(true);
await page.pdf({ path: 'page.pdf', format: 'A4' });
selenium-webdriver lacks native network mocking.
// selenium-webdriver: Limited network control
await driver.takeScreenshot(); // Returns base64 string
// No native PDF support
nightmare has basic screenshot support.
// nightmare: Basic screenshot
await nightmare.screenshot('screen.png');
Despite their differences, these tools share common goals and patterns.
// 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 });
// 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');
// 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');
| Feature | playwright | puppeteer | selenium-webdriver | nightmare |
|---|---|---|---|---|
| Status | β Active | β Active | β Active | β Abandoned |
| Browsers | Chromium, Firefox, WebKit | Chromium (mostly) | All (via drivers) | Electron |
| Auto-Wait | β Yes | β Manual | β Manual | β οΈ Implicit |
| Network Mock | β Native | β Native | β External Proxy | β No |
| PDF Support | β Yes | β Yes | β No | β No |
| Setup | Easy (bundled browsers) | Easy (bundled browser) | Hard (manage drivers) | Easy (bundled) |
playwright is the modern standard for end-to-end testing.
puppeteer is the specialist for Chrome tasks.
selenium-webdriver is the enterprise legacy choice.
nightmare is a legacy tool.
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.
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.
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.
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.
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.
Playwright is a framework for web automation and testing. It drives Chromium, Firefox, and WebKit with a single API β in your tests, in your scripts, and as a tool for AI agents.
Choose the path that fits your workflow:
| Best for | Install | |
|---|---|---|
| Playwright Test | End-to-end testing | npm init playwright@latest |
| Playwright CLI | Coding agents (Claude Code, Copilot) | npm i -g @playwright/cli@latest |
| Playwright MCP | AI agents and LLM-driven automation | npx @playwright/mcp@latest |
| Playwright Library | Browser automation scripts | npm i playwright |
| VS Code Extension | Test authoring and debugging in VS Code | Install from Marketplace |
Playwright Test is a full-featured test runner built for end-to-end testing. It runs tests across Chromium, Firefox, and WebKit with full browser isolation, auto-waiting, and web-first assertions.
npm init playwright@latest
Or add manually:
npm i -D @playwright/test
npx playwright install
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
npx playwright test
Tests run in parallel across all configured browsers, in headless mode by default. Each test gets a fresh browser context β full isolation with near-zero overhead.
Auto-wait and web-first assertions. No artificial timeouts. Playwright waits for elements to be actionable, and assertions automatically retry until conditions are met.
Locators. Find elements with resilient locators that mirror how users see the page:
page.getByRole('button', { name: 'Submit' })
page.getByLabel('Email')
page.getByPlaceholder('Search...')
page.getByTestId('login-form')
Test isolation. Each test runs in its own browser context β equivalent to a fresh browser profile. Save authentication state once and reuse it across tests:
// Save state after login
await page.context().storageState({ path: 'auth.json' });
// Reuse in other tests
test.use({ storageState: 'auth.json' });
Tracing. Capture execution traces, screenshots, and videos on failure. Inspect every action, DOM snapshot, network request, and console message in the Trace Viewer:
// playwright.config.ts
export default defineConfig({
use: {
trace: 'on-first-retry',
},
});
npx playwright show-trace trace.zip
Parallelism. Tests run in parallel by default across all configured browsers.
Playwright CLI is a command-line interface for browser automation designed for coding agents. It's more token-efficient than MCP β commands avoid loading large tool schemas and accessibility trees into the model context.
npm install -g @playwright/cli@latest
Optionally install skills for richer agent integration:
playwright-cli install --skills
Point your coding agent at a task:
Test the "add todo" flow on https://demo.playwright.dev/todomvc using playwright-cli.
Take screenshots for all successful and failing scenarios.
Or run commands directly:
playwright-cli open https://demo.playwright.dev/todomvc/ --headed
playwright-cli type "Buy groceries"
playwright-cli press Enter
playwright-cli screenshot
Use playwright-cli show to open a visual dashboard with live screencast previews of all running browser sessions. Click any session to zoom in and take remote control.
playwright-cli show
Full CLI documentation | GitHub
The Playwright MCP server gives AI agents full browser control through the Model Context Protocol. Agents interact with pages using structured accessibility snapshots β no vision models or screenshots required.
Add to your MCP client (VS Code, Cursor, Claude Desktop, Windsurf, etc.):
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
One-click install for VS Code:
For Claude Code:
claude mcp add playwright npx @playwright/mcp@latest
Ask your AI assistant to interact with any web page:
Navigate to https://demo.playwright.dev/todomvc and add a few todo items.
The agent sees the page as a structured accessibility tree:
- heading "todos" [level=1]
- textbox "What needs to be done?" [ref=e5]
- listitem:
- checkbox "Toggle Todo" [ref=e10]
- text: "Buy groceries"
It uses element refs like e5 and e10 to click, type, and interact β deterministically and without visual ambiguity. Tools cover navigation, form filling, screenshots, network mocking, storage management, and more.
Full MCP documentation | GitHub
Use playwright as a library for browser automation scripts β web scraping, PDF generation, screenshot capture, and any workflow that needs programmatic browser control without a test runner.
npm i playwright
Take a screenshot:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev/');
await page.screenshot({ path: 'screenshot.png' });
await browser.close();
Generate a PDF:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev/');
await page.pdf({ path: 'page.pdf', format: 'A4' });
await browser.close();
Emulate a mobile device:
import { chromium, devices } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext(devices['iPhone 15']);
const page = await context.newPage();
await page.goto('https://playwright.dev/');
await page.screenshot({ path: 'mobile.png' });
await browser.close();
Intercept network requests:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
await page.goto('https://playwright.dev/');
await browser.close();
Library documentation | API reference
The Playwright VS Code extension brings test running, debugging, and code generation directly into your editor.
Run and debug tests from the editor with a single click. Set breakpoints, inspect variables, and step through test execution with a live browser view.
Generate tests with CodeGen. Click "Record new" to open a browser β navigate and interact with your app while Playwright writes the test code for you.
Pick locators. Hover over any element in the browser to see the best available locator, then click to copy it to your clipboard.
Trace Viewer integration. Enable "Show Trace Viewer" in the sidebar to get a full execution trace after each test run β DOM snapshots, network requests, console logs, and screenshots at every step.
Install the extension | VS Code guide
| Linux | macOS | Windows | |
|---|---|---|---|
| Chromium1 148.0.7778.96 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit 26.4 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox 150.0.2 | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Headless and headed execution on all platforms. 1 Uses Chrome for Testing by default.
Playwright is also available for Python, .NET, and Java.