What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Playwright scripts in two common ways: a direct browser script for one-off automation, or a Playwright Test test with fixtures and assertions. The smallest useful flow is always the same—launch a browser, open a page, perform an interaction with a resilient locator, and verify an observable result. The examples below show both styles, then cover waiting, forms, network interception, debugging, reliability, and common failures.

Choose the right Playwright script style

Approach Best for Lifecycle and verification
Playwright Library script One-off automation, data collection, smoke checks, or a custom Node.js program You launch and close the browser yourself; add your own checks
Playwright Test Repeatable end-to-end tests in a test suite The runner supplies fixtures such as page, and expect provides retrying web-first assertions

Both styles use the same browser, context, page, locator, and network APIs. Install Playwright in the project where the script will run, install the browser binaries required by your selected browser, and keep examples aligned with the version installed in your project because APIs and tooling evolve.

Complete Playwright Library script

This CommonJS example launches Chromium, navigates to a page, clicks a link by its accessible role and name, then closes the browser even if an error occurs.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
    await page.getByRole('link', { name: 'More information' }).click();
    console.log('New URL:', page.url());
  } finally {
    await browser.close();
  }
})();

goto resolves after the selected navigation condition. The default navigation behavior is usually sufficient; choose a more specific condition only when your workflow needs it. Do not treat a successful HTTP response as proof that the application rendered correctly—verify the page state your user needs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run the script

  1. Create a Node.js project and install playwright.
  2. Install the browser binaries for the browsers you intend to launch.
  3. Save the file, for example as smoke.js, and run node smoke.js.

A Playwright Test example with an assertion

Tests should connect an action to an observable outcome. The following credentials are illustrative documentation values, not safe production credentials.

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

test('sign-in form accepts credentials', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByLabel('User Name').fill('John');
  await page.getByLabel('Password').fill('secret-password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page.getByText('Welcome, John!')).toBeVisible();
});

The page fixture is created and disposed by the test runner. The web-first assertion retries while checking the condition; the documented default assertion timeout is five seconds. Configure a different timeout when the application genuinely needs more time, rather than inserting arbitrary sleeps.

Locator examples that survive UI changes

Locators are evaluated when an operation runs, so they cope better with a framework rerendering the DOM between steps. Prefer selectors that describe the interface as a user experiences it.

Roles and accessible names

await page.getByRole('button', { name: 'Save changes' }).click();
await page.getByRole('checkbox', { name: 'Send me a receipt' }).check();
await page.getByRole('link', { name: 'Account settings' }).click();

A role plus meaningful accessible name is usually clearer than a generated class name. If several controls share a name, narrow the locator with a region or other stable relationship.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Labels, text, placeholders, and test IDs

await page.getByLabel('Email address').fill('person@example.test');
await page.getByPlaceholder('Search products').fill('keyboard');
await page.getByText('Order complete').toBeVisible();
await expect(page.getByTestId('status')).toHaveText('Submitted');

Use a test ID when the product team has deliberately made it part of the test contract. Text and placeholders are useful when they are stable and unique, but they can change during copy or localization work.

CSS and XPath when necessary

await page.locator('[data-state="open"] .menu-item').click();
await page.locator('xpath=//button[@data-action="archive"]').click();

CSS and XPath remain available, but long chains coupled to DOM structure are brittle. A redesign can leave the control visually unchanged while breaking a structural selector.

Actions and synchronization

Playwright actions such as click, fill, and check wait for the element to become actionable. Follow each important action with an assertion about the resulting state.

await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByTestId('status')).toHaveText('Submitted');

Wait for a condition, not an arbitrary delay

A fixed sleep can pass on a fast machine and fail on a slow one. Prefer a retrying assertion or an explicit wait for a state your application owns.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.getByRole('button', { name: 'Refresh' }).click();
await expect(page.getByRole('status')).toHaveText('Updated');

await page.waitForSelector('[data-testid="report-ready"]');

Use waitForSelector sparingly when an assertion can express the expected result more directly. For navigation, wait for the URL or a page-level outcome instead of assuming a click completed immediately.

await Promise.all([
  page.waitForURL('**/dashboard'),
  page.getByRole('link', { name: 'Dashboard' }).click(),
]);

Forms, dialogs, and new pages

Forms and validation

await page.getByLabel('Quantity').fill('2');
await page.getByRole('radio', { name: 'Express delivery' }).check();
await page.getByRole('button', { name: 'Place order' }).click();
await expect(page.getByRole('alert')).toContainText('Order received');

Browser dialogs

page.once('dialog', async dialog => {
  console.log(dialog.type(), dialog.message());
  await dialog.accept();
});
await page.getByRole('button', { name: 'Delete' }).click();

Popups and new tabs

const popupPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: 'Open receipt' }).click();
const popup = await popupPromise;
await popup.waitForLoadState('domcontentloaded');
await expect(popup.getByRole('heading', { name: 'Receipt' })).toBeVisible();

Mock, inspect, or block network requests

Playwright can monitor and modify HTTP and HTTPS traffic, including XHR and fetch. Register a route on a page or browser context before navigation so the application receives the intended response.

Replace an API response with fixture data

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

test('renders mocked products', async ({ page }) => {
  await page.route('**/api/products', route => route.fulfill({
    json: [{ id: 1, name: 'Product 1' }],
  }));

  await page.goto('https://example.com/products');
  await expect(page.getByText('Product 1')).toBeVisible();
});

This replaces the matching response; it is deterministic fixture testing, not an integration test against the live service.

Modify or abort a request

await page.route('**/api/profile', async route => {
  const response = await route.fetch();
  const body = await response.json();
  body.plan = 'trial';
  await route.fulfill({ response, json: body });
});

await page.route('**/*.{png,jpg,jpeg,gif}', route => route.abort());

Use modification when you need most of the real response with one controlled change. Abort selected resources to test degraded behavior or to reduce irrelevant traffic in a focused test.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose live or intercepted traffic deliberately

Goal Traffic choice Reason
Verify the complete integration Live service or a controlled test environment Catches contract, authentication, and deployment problems
Exercise UI states predictably Intercepted fixture Removes latency and changing backend data
Test failures and empty states Fulfill errors, empty JSON, or abort requests Makes rare conditions repeatable

Debug failing scripts

UI Mode and Inspector

Use Playwright UI Mode or the Inspector to step through a test, inspect locator calls, view logs and network activity, and examine DOM snapshots. These tools reveal whether the selector is wrong, the page is at the wrong URL, or the expected request never occurred.

HTML Reporter

The HTML Reporter lets you open an individual failure and review its steps and diagnostics. Preserve traces, screenshots, and videos in continuous integration only when they provide useful evidence; they increase artifact size and storage time.

Make a failing state reproducible

  • Record the exact URL, browser, viewport, locale, and test data.
  • Replace unstable live responses with a route fixture when diagnosing UI behavior.
  • Use a locator assertion to identify the first missing state, rather than adding sleeps throughout the script.
  • Run the smallest test that reproduces the problem before changing application code.

Reliability, speed, and maintenance

Contexts and isolation

In a library script, create a fresh browser context for an independent user session. In Playwright Test, use the supplied fixtures unless a deliberate shared-state design is required. Isolated contexts prevent cookies and local storage from leaking between scenarios.

Navigation and timeout choices

Set timeouts around real service limits and keep them consistent across environments. A generous timeout can hide a broken endpoint; an aggressive one creates false failures. Prefer a specific readiness assertion over waiting for every network request to become idle, because analytics and long polling can keep a page busy indefinitely.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Locator maintenance

Ask the application team to expose accessible names and stable test IDs for controls that cannot be identified by role or label. Review locators when copy, localization, or accessibility changes are made. Avoid selecting by position, such as “the third button,” unless position is the actual contract.

Parallelism and test data

Parallel tests are faster only when data and accounts are independent. Give each worker isolated records or reset state between tests; otherwise a speed improvement becomes an ordering-dependent failure.

Common errors and fixes

Symptom Likely cause Fix
“Locator resolved to multiple elements” The role, label, or text is not unique Scope to a region, add an accessible name, or use a deliberate test ID; do not blindly use first()
Timeout waiting for a locator Wrong URL, hidden control, changed copy, or an iframe Inspect the page in Inspector, assert the URL, verify visibility, and use frameLocator for an iframe
Click times out because another element covers it Consent dialog, animation, or overlay Handle the dialog as a real user would, wait for its disappearance, and avoid forcing the click unless covering behavior itself is under test
Assertion is flaky Fixed sleeps, shared state, or an eventually consistent backend Use a web-first assertion, isolate data, and intercept the response for deterministic UI tests
Route did not intercept Route registered after navigation or pattern does not match Register before goto, log the request URL, and correct the glob pattern
Browser executable is missing Playwright package is installed but its browser binaries are not Run the browser installation command for your project and repeat the script
Works locally but fails in CI Different viewport, permissions, environment variables, or timing Pin the runtime setup, capture the failure report, and make dependencies and test data explicit
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is simply to obtain a clean screenshot rather than interact with a browser in a test, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; each response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

One GET request returns PNG, JPEG, WebP, or PDF. The API supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper and margin settings, custom CSS and JavaScript, clicks before capture, selector hiding, selector/delay/network-idle waits, request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work for easier migration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

See the ScreenshotNeo documentation for option names and response details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

An MCP server supplies take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can I use Playwright without the Playwright Test runner?

Yes. Install the Playwright library, launch a browser in your own code, create pages or contexts, and close the browser yourself.

When should I mock an API instead of calling it?

Mock it for deterministic UI states, errors, and empty results; use a live or controlled test environment when you need integration coverage.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does a locator work once and then fail after rerendering?

Prefer a locator created from a role, label, text, or test ID. Locators are resolved at operation time and are more resilient than storing an obsolete element handle.

What is the fastest way to investigate a timeout?

Run the test with UI Mode or Inspector, check the current URL and DOM snapshot, and identify whether the locator, navigation, overlay, iframe, or network response is the first missing condition.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.