How to Use Playwright for Website Testing
Set up Playwright Test, write reliable browser checks, choose browser coverage, and run and debug tests locally and in CI.
Playwright Test lets you automate browser checks for a website with a test runner, assertions, isolated browser contexts, parallel execution, and debugging tools. Start a project with npm init playwright@latest, write tests that navigate, interact through locators, and assert expected page state, then run them with npx playwright test. Playwright supports Chromium, Firefox, and WebKit, with browser and device coverage selected through projects.
This guide builds a working JavaScript or TypeScript setup, explains reliable locators and assertions, shows browser configuration and CI execution, and covers common failures. Playwright’s official guides: installation, writing tests, and CI.
1. Install Playwright Test
Use Node.js and npm in the project directory. The initializer can create a new project or add Playwright to an existing one. It asks whether to use JavaScript or TypeScript, where to place tests, whether to add a GitHub Actions workflow, and whether to install browser binaries.
npm init playwright@latest
After installation, the scaffold includes a Playwright configuration file and an example test. To install or refresh browser binaries later, run:
npx playwright install
On Linux CI agents or systems without the required operating-system libraries, install those dependencies too:
npx playwright install --with-deps
Playwright versions expect matching browser binaries. After updating the Playwright package, run the browser installation command again. Check the official browser documentation for version-specific system requirements.
2. Write a first website test
Tests describe a user action and the expected page state. Save this as tests/homepage.spec.ts in a TypeScript project:
import { test, expect } from '@playwright/test';
test('opens the installation page', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
Run all discovered tests with:
npx playwright test
Playwright runs headless by default. Tests get an isolated browser context, much like a fresh browser profile, so cookies and page state do not leak between tests. Navigation, actions, and web assertions wait for conditions; avoid fixed sleeps unless a deliberate time delay is what the product behavior requires. See Writing tests and Locators.
A small test for your own site
Replace the example URL and expected content with your app’s public test environment. Use a visible, user-facing result such as a heading or confirmation message.
import { test, expect } from '@playwright/test';
test('visitor can open the pricing page', async ({ page }) => {
await page.goto('https://example.com');
await page.getByRole('link', { name: 'Pricing' }).click();
await expect(page).toHaveURL(/pricing/);
await expect(page.getByRole('heading', { name: /pricing/i })).toBeVisible();
});
The example assumes the site has a link named “Pricing” and a matching heading. Update those expectations to the labels and behavior your site actually exposes.
3. Choose robust locators and assertions
Prefer locators that express how a person identifies an element. Playwright resolves locators when actions run and automatically waits for actionability. Web-first assertions retry until the expected state appears or the timeout expires.
| Locator | Good fit |
|---|---|
getByRole(role, { name }) |
Buttons, links, headings, and accessible controls. |
getByLabel(text) |
Form controls with associated labels. |
getByText(text) |
Visible content where text is a stable identifier. |
getByPlaceholder(text) |
Inputs identified by their placeholder. |
getByTestId(id) |
A deliberate test-ID contract maintained by the team. |
locator(css) |
Specific cases where a CSS selector is appropriate; keep it scoped and understandable. |
Common web-first assertions include toHaveTitle, toHaveURL, toBeVisible, toBeEnabled, toHaveText, and toHaveValue. Use await expect(locator).matcher() for page state: these assertions retry. A plain JavaScript assertion evaluates immediately and is best reserved for values already retrieved synchronously.
For example, this form test fills labeled fields and checks the resulting confirmation:
test('submits the contact form', async ({ page }) => {
await page.goto('https://example.com/contact');
await page.getByLabel('Email').fill('dev@example.com');
await page.getByLabel('Message').fill('Please contact me.');
await page.getByRole('button', { name: 'Send message' }).click();
await expect(page.getByRole('status')).toContainText('sent');
});
This assumes the form has those labels and exposes a status message. If your site uses different accessible names, adjust the locators rather than choosing a brittle selector copied from the example.
4. Configure browser and device coverage
A Playwright project groups tests under a shared configuration. Projects let the same suite run on different browser engines, device profiles, or environments. The initializer’s default configuration is a useful starting point. A compact explicit configuration can look like this:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
retries: process.env.CI ? 1 : 0,
reporter: 'html',
use: {
baseURL: 'https://example.com',
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});
Then tests can navigate relative to baseURL, for example await page.goto('/pricing'). Project device descriptors configure emulation; they do not replace checking on physical devices where that matters. Playwright also documents branded Chrome and Edge channels and emulated mobile and tablet devices. Choose engines and devices according to your supported audience and the behavior at risk; every site does not need every possible project. See projects and browsers.
Run a selected configuration
npx playwright test --project=chromium
npx playwright test --project=webkit --headed
Use --headed to watch a browser window, or --ui to open interactive UI mode:
npx playwright test --ui
5. Run tests locally and inspect failures
Useful commands for everyday work:
# Run the full suite
npx playwright test
# Run a matching test file
npx playwright test tests/homepage.spec.ts
# Open a visible browser
npx playwright test --headed
# Use the interactive test UI
npx playwright test --ui
# Open the generated HTML report
npx playwright show-report
UI mode and the Playwright Inspector help inspect steps, page state, and locator choices. To keep a trace on a retry, configure trace: 'on-first-retry' and a retry policy. Open a saved trace with:
npx playwright show-trace path/to/trace.zip
The Trace Viewer provides a GUI for reviewing recorded test activity. Traces and HTML reports are particularly useful when a failure occurs in CI and the original browser session is gone. Refer to the official Trace Viewer guide.
6. Run Playwright in continuous integration
A CI job needs the project dependencies, Playwright’s matching browsers and any required operating-system dependencies, then the test command. A minimal shell sequence is:
npm ci
npx playwright install --with-deps
npx playwright test
For GitHub Actions, the initializer can add a workflow. The official CI guide recommends one worker as a stability-oriented default in CI. More workers can shorten elapsed time when the runner has enough resources and tests do not conflict; sharding can spread work across multiple jobs. Select based on runner capacity and observed failures rather than assuming one setting fits every pipeline.
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: process.env.CI ? 1 : undefined,
retries: process.env.CI ? 1 : 0,
reporter: [['list'], ['html', { open: 'never' }]],
use: { trace: 'on-first-retry' },
});
Keep the HTML report and trace artifacts when a CI job fails so the browser actions can be examined afterward. See Playwright CI guidance for provider-specific setup and artifact examples.
7. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Executable or browser binary is missing | The browser binaries are not installed, or the package was upgraded and now expects another browser version. | Run npx playwright install; on supported Linux CI, use npx playwright install --with-deps. |
| Browser fails to launch in a Linux runner | Required operating-system libraries are absent, or the environment cannot launch the browser. | Install dependencies with npx playwright install --with-deps and check the browser/system requirements for the Playwright version in use. |
| Locator resolves to no element or multiple elements | The accessible name changed, the page has not reached the expected state, or the locator is ambiguous. | Inspect the page with UI mode or Inspector; use a more specific role/name or scope the locator to its containing region. |
| Click times out | The element is covered, disabled, detached, or never becomes actionable; sometimes the test is on the wrong page. | Check the trace and current URL/state. Wait for the meaningful state with a web assertion and correct the locator or test setup. |
| Assertion times out | The expected state never appears, the application is slow or broken, or the assertion targets the wrong element. | Use the report or trace to inspect the rendered page, confirm the expected behavior manually, then fix the assertion or app condition. Increase a timeout only when the slower behavior is expected. |
| Works locally, fails in CI | Different dependencies, insufficient resources, race conditions, or missing test data/environment configuration. | Install matching browsers and OS dependencies, preserve traces and reports, reduce workers to one to diagnose resource contention, and make test data deterministic. |
| Test passes alone but fails in the suite | Shared external state or application data creates order dependence; tests may be mutating common accounts. | Keep tests isolated, use unique or resettable test data, and avoid relying on execution order. |
| Navigation hangs or page is partly loaded | The site has slow or long-lived network activity, or the selected navigation condition does not match the page. | Assert the specific page state needed for the test, inspect the trace, and avoid waiting for unrelated background activity. |
8. Reliability, speed, and cost considerations
- Prefer condition-based waiting. Locator actions and web assertions wait for actionable elements and expected state. Fixed sleeps make tests slower and can still fail when timing varies.
- Control parallelism. Parallel workers can reduce elapsed time but use more CPU and memory and can expose shared-data conflicts. Start conservatively in CI, then increase concurrency when the environment and tests support it.
- Keep browser versions aligned. Install browser binaries for the Playwright version in the lockfile, and refresh them when upgrading.
- Make external dependencies predictable. Use controlled test data and stable test environments where possible. A third-party site, network, or service can change independently of your code.
- Preserve failure evidence. HTML reports and traces take storage and can contain page content or interaction details. Retain them according to your team’s data handling needs.
- Budget for CI resources. Browser installation, multiple browser projects, retries, and parallel jobs all consume build time and runner capacity. Choose coverage around supported browsers and important user journeys.
Playwright itself is an open-source framework; this guide makes no claim about the price of your CI provider, hosted browsers, or the infrastructure used to run your tests. Those costs depend on the environment and workload.
9. Capture a page image for visual review
Playwright tests browser behavior through assertions. If you also need a standalone screenshot for a report or a quick visual review, a script can launch Chromium and save the page:
import { chromium } from '@playwright/test';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com');
await page.screenshot({ path: 'page.png', fullPage: true });
await browser.close();
This requires the Playwright package and installed browser binaries. Use Playwright Test screenshot assertions when the goal is to compare rendered output as part of a test; a manually saved image is simply an output file and does not by itself define an expected result.
Or skip the browser setup
For a screenshot without installing or maintaining a browser, call the ScreenshotNeo website screenshot API. The API documentation covers its request options.
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}`);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Can Playwright test a website without its source code?
Yes. Browser tests interact with the rendered website through navigation, locators, actions, and assertions; access to the site’s source code is not required to write a basic browser test.
Should every test run in every browser?
Not necessarily. Configure projects according to the browsers and devices your site supports and the risks the test covers. Running more projects adds execution and maintenance work.
What should a useful end-to-end test assert?
Assert the user-visible result that demonstrates the journey worked, such as a URL change, a visible heading, or a submitted status. Avoid treating a click alone as proof of success.
Can a screenshot prove a site works?
A screenshot records appearance at a point in time. Use assertions and interactions to verify behavior; use screenshots when visual evidence or comparison is useful.


