ScreenshotNeo

BlogGuides

Playwright Questions Answered: A Practical Guide

Learn Playwright with a complete TypeScript setup, reliable tests, browser projects, API checks, CI, trace debugging, and practical answers to common questions.

By the ScreenshotNeo team4 October 202611 min read

Playwright is a browser automation framework for testing, scripting, and AI-agent workflows. Its Playwright Test package adds a test runner, fixtures, cross-browser projects, retrying assertions, parallel execution, and trace-based debugging. This practical guide uses TypeScript and Playwright Test, then covers locators, timing, browser coverage, API checks, CI, and common failure fixes.

1. What is Playwright, and what should you learn first?

Playwright can mean the browser automation libraries or the Playwright Test runner. The library drives browsers; the runner organizes tests and provides fixtures, assertions, projects, reports, and debugging features. This guide uses the runner for browser tests and the API request context for HTTP checks. The project describes its purpose as reliable web automation for testing, scripting, and AI agents (Playwright).

You do not need deep browser internals to start. Be comfortable with basic TypeScript or JavaScript, async functions and await, HTML elements, and how a user moves through your application. The official project documents TypeScript, JavaScript, Python, Java, and .NET. Commands differ by language; the installation below is specifically for TypeScript/JavaScript with npm.

2. How do you install Playwright with TypeScript?

Start in the project directory. The setup command creates a test project and offers choices such as TypeScript, test directory, and CI configuration. It also installs the selected Playwright package.

npm init playwright@latest

Install the browser binaries that match the installed Playwright version. For the default browsers:

npx playwright install

To install a single browser, use its name, for example npx playwright install chromium. On Linux CI machines that need operating-system packages, the CLI can install browser dependencies too:

npx playwright install --with-deps

Playwright versions are paired with compatible browser binaries. When upgrading Playwright, rerun the browser installation command so the required browser builds are present. See the official browser installation and browser version guidance.

A typical generated project includes playwright.config.ts and a tests directory. A compact configuration for three browser engines looks like this:

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  reporter: 'html',
  use: {
    baseURL: 'http://127.0.0.1:3000',
    trace: 'on-first-retry',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
  webServer: {
    command: 'npm run start',
    url: 'http://127.0.0.1:3000',
    reuseExistingServer: !process.env.CI,
  },
});

Adjust the server command, URL, and scripts to match your application. The webServer entry starts the app for a run and waits for the configured URL. If your app is already running or a separate CI step starts it, configure accordingly.

3. What does a good first Playwright test look like?

A useful test follows a user-visible journey: open a page, locate an element the user recognizes, perform an action, and verify the resulting state. Save this as tests/navigation.spec.ts:

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

test('opens the installation guide', 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 it in all configured projects with npx playwright test. Run just this file with npx playwright test tests/navigation.spec.ts, or select a project with npx playwright test --project=chromium. Open the HTML report with npx playwright show-report.

This sample is documentation-based and has not been represented as executed here. In your own suite, use a stable test environment and choose the expected outcome based on your application behavior.

4. Which locators should you use?

Locators identify elements and are resolved when an action or assertion runs. Prefer selectors tied to user-facing semantics, because they tend to express what the user sees and does. Playwright recommends role and accessible-name locators where appropriate.

Locator Use it when Example
Role and name The element has an accessible role and label page.getByRole('button', { name: 'Save' })
Label Finding a form control by its label page.getByLabel('Email address')
Placeholder The placeholder is the intended identifying text page.getByPlaceholder('Search')
Text Visible text is the clearest identifier page.getByText('Order confirmed')
Test ID A stable test-specific hook is appropriate page.getByTestId('cart-count')
CSS selector The UI has no better semantic hook or a specific structural target is needed page.locator('.dialog button.confirm')

If a locator matches more than one element, refine it with a parent locator, additional role/name information, or a deliberate index only when ordering is part of the requirement. Avoid casually selecting the first match: it can hide an ambiguous page and click the wrong control. The locator guide describes locator behavior and options. The test generator can record interactions and suggest locators, but read the generated code and decide whether the locator describes the intended behavior.

5. How do you avoid timing mistakes?

Use Playwright actions and async web-first assertions. Actions such as click() wait for the relevant actionability conditions; assertions such as toBeVisible() retry until the expected state appears or the assertion times out. That is different from reading a state once.

// Retried assertion: waits for the visible state.
await expect(page.getByRole('status')).toHaveText('Saved');

// Immediate observation: reports only the state at this instant.
const visibleNow = await page.getByRole('status').isVisible();

Use the immediate check when an instantaneous observation is actually what the test needs. Do not use it as a substitute for waiting for an asynchronous page update. Fixed sleeps such as waitForTimeout(2000) often make tests slower while still failing on slower runs. Prefer waiting for a meaningful outcome:

await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByRole('heading', { name: 'Thank you' })).toBeVisible();

For a specific prerequisite, wait for a locator or a page condition rather than inserting a guessed delay. See web-first assertions and Playwright best practices.

6. What are fixtures, and how do tests stay isolated?

The page parameter in the sample is a built-in fixture. Fixtures provide a test with resources and setup, such as a browser page, and can be composed into reusable test setup. Playwright Test creates an isolated browser context for each test, helping prevent cookies, local storage, and other browser state from leaking between tests.

Use hooks such as beforeEach when shared setup makes the suite easier to understand. Keep test outcomes independent: one test should not require another test to have run first. For example:

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

test.beforeEach(async ({ page }) => {
  await page.goto('/account');
});

test('shows the account heading', async ({ page }) => {
  await expect(page.getByRole('heading', { name: 'Account' })).toBeVisible();
});

Fixtures are more than naming conventions; they define reusable resources and their setup/teardown lifecycle. Use custom fixtures when they clarify repeated setup, and avoid building a large abstraction for a one-off action. See fixtures documentation.

7. How should you choose browser projects?

Projects run the same tests with different browser or device settings. Chromium, Firefox, and WebKit provide engine coverage, and device descriptors configure emulated profiles such as viewport and input characteristics. Pick coverage based on the browsers and devices your users need, your platform-sensitive features, and the time budget for local and CI feedback.

Need Starting choice What to know
Fast local feedback One project, often Chromium Broaden coverage in CI or before release
Engine regressions Chromium, Firefox, and WebKit projects Project browser builds are not identical to branded distributions
Specific Chrome or Edge behavior Configure a branded Chrome or Edge channel Useful when the production browser distribution itself matters
Mobile layout and input profile Configured device project Emulation is not the same as testing every physical device
Media codec behavior Consider branded channels where relevant Browser distribution and codec support may matter

Playwright uses its own Chromium build by default. Its Firefox relies on Playwright patches, and Playwright’s WebKit is based on WebKit sources rather than branded Safari. Choose branded browser channels when your regression target specifically calls for them. Read the browser documentation for current details and supported configuration.

8. When should you use Playwright for API testing?

Use a browser test when the behavior depends on the user journey through a page. Use an API request context when you need to check an HTTP endpoint, prepare test data, or verify a service response without driving the UI. API checks can be simpler for service behavior; they do not replace end-to-end tests when the browser journey itself matters.

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

test('health endpoint responds', async ({ request }) => {
  const response = await request.get('https://example.com/health');
  expect(response.ok()).toBeTruthy();
  const body = await response.json();
  expect(body.status).toBe('ok');
});

Replace the example URL and response contract with your service’s actual endpoint and schema. Playwright’s API testing guide documents API request contexts and their use for HTTP calls and validation.

9. How do you run Playwright in CI?

A CI job needs the project dependencies, compatible browser binaries, any required operating-system dependencies, and a way to start the application under test. A minimal GitHub Actions workflow for an npm project can look like this:

name: Playwright tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm test

Ensure the package’s test script runs playwright test and that your test configuration starts the app or CI starts it before the test command. Pin or deliberately manage Node and dependency versions according to your repository policy. Browser installation and system dependencies are part of the CI setup, not assumptions about a runner image. The official CI guide includes provider-specific guidance.

10. How do traces help debug failures?

A trace records a test run for later inspection. Trace Viewer can show a timeline, DOM snapshots associated with actions, and network requests, which helps answer what the test saw around a failure. A practical default in the configuration above is trace: 'on-first-retry': traces are collected when a test first retries. You can also enable tracing for a focused investigation or use another supported collection policy.

Tracing every test can have a performance cost. Choose a policy that balances diagnostic detail, storage, and runtime; retain more data for retries or targeted investigations when that is enough. For a failed CI run, open the generated report or trace with the viewer and inspect the action timeline, page snapshot, and network activity around the failing step. See Trace Viewer documentation and best practices.

11. What common Playwright errors should you fix first?

Symptom Likely cause Fix
Executable or browser binary not found The compatible browser build was not installed, or Playwright was upgraded Run npx playwright install; install system dependencies on Linux CI if needed
Timeout waiting for a locator The locator is wrong or ambiguous, the app is not ready, or the expected state never occurs Inspect the locator and page state; assert the user-visible result; confirm the app and test data are available
Click fails because the element is not actionable The target is hidden, covered, disabled, or not yet stable Check the page state and intended target; wait for the correct visible/enabled condition instead of forcing the click blindly
Test passes locally but fails in CI Environment differences, missing dependencies, slower startup, or shared state Install browsers with dependencies, verify server readiness, isolate test data, and inspect a trace on retry
Assertion fails immediately after an action A one-time state read or non-retrying check ran before the UI settled Use an async web-first assertion such as await expect(locator).toBeVisible()
Different browser gives a different result Engine behavior, browser build, application support, or device emulation differs Reproduce in the named project and decide whether branded channel or broader browser coverage is required
Parallel run has intermittent failures Tests share accounts, records, ports, or other mutable resources Give tests independent data and resources, or limit parallelism for the shared bottleneck

Do not increase every timeout as the first response. A larger timeout can mask a missing state transition or slow test setup. Use the report and trace to determine whether the issue is a locator, app readiness, environment setup, or real product behavior.

12. How do you capture a website screenshot from a test?

Playwright can capture a page screenshot or a particular locator as part of a test. For example:

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

test('capture the account page', async ({ page }) => {
  await page.goto('https://example.com/account');
  await expect(page.getByRole('heading', { name: 'Account' })).toBeVisible();
  await page.screenshot({ path: 'account.png', fullPage: true });
  await page.getByRole('main').screenshot({ path: 'account-main.png' });
});

Use this when the capture belongs in your browser test workflow. Screenshot behavior and available options can depend on whether you capture a page or locator; consult the current Playwright API documentation for the exact options you need. If your goal is to fetch screenshots as an API response rather than manage browsers in a test project, ScreenshotNeo is a separate website screenshot API and MCP server for developers.

13. Or skip the browser setup

If your task is to retrieve a website image or PDF rather than test a browser journey, ScreenshotNeo provides a one-call screenshot API. The ScreenshotNeo API docs cover 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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.

14. What is the practical learning path after your first test?

  1. Install the runner and matching browser binaries.
  2. Write one user-facing journey with a semantic locator and a retrying assertion.
  3. Add fixtures or hooks only where they make repeated setup clearer.
  4. Run the browser projects that match your users and product risks.
  5. Use API request contexts for service-level checks that do not require a browser journey.
  6. Set up CI with browser dependencies and a predictable application startup.
  7. Collect traces on retries or during focused investigations, considering their runtime and storage cost.

Frequently asked questions

What should you know before starting Playwright?

Basic TypeScript or JavaScript, async/await, HTML, and the user flow you want to verify are enough to begin. Learn the framework incrementally through locators, assertions, fixtures, and browser projects.

Does Playwright wait automatically?

Actions wait for actionability, and web-first assertions retry for expected conditions. Arbitrary script logic and immediate state reads do not become safe automatically; wait on meaningful conditions.

Can Playwright test multiple browsers?

Yes. Projects can run tests in Chromium, Firefox, WebKit, and configured device profiles. Playwright’s browser builds are not identical to branded Chrome, Firefox, or Safari.

Can Playwright test APIs?

Yes. Its API request context supports HTTP calls and response checks, useful for service behavior, setup, and validation outside a browser journey.

How do you investigate a CI-only failure?

Check browser and OS dependencies, app readiness, and test isolation, then inspect a trace collected on retry for the action timeline, DOM snapshots, and network requests.