Glossary

Playwright

Playwright verifies what a user actually experiences in a browser. It is valuable for critical paths, but should not replace fast unit tests and focused integration tests.

Short definition

An automated browser for verifying user outcomes.

Playwright Test combines a test runner, assertions, isolated browser contexts, parallel execution, and diagnostic tools. In a JavaScript project, it is commonly run by npm through Node.js. A test can open a page, complete a form, click a button, and verify that the user reached the expected state.

Playwright protects a broader scenario than a unit test or integration test: it drives the application from the user’s perspective through a browser.

It supports Chromium, Firefox, and WebKit. Alongside desktop projects, it can configure a mobile viewport and selected device characteristics; emulation, however, is not the same as full verification on every physical phone.

The problem it solves

Errors that arise only when the browser and application work together

An end-to-end test has broad scope and is slower, so it should primarily protect paths that matter to users or revenue.

  • login, session handling, and protecting a page from signed-out users
  • finding a product, adding it to the cart, and validating checkout
  • correctly displaying errors after form submission
  • connecting a frontend form to the real HTTP endpoint and response
  • verifying critical behaviour across supported browsers before release

Practical example

An online-store flow from product to checkout validation

The scenario opens a specific product, adds it to the cart, and proceeds to checkout. It does not inspect an internal frontend store, but verifies that after submitting an incomplete form, the user sees clear validation and the order is not submitted. The test must prepare product data so it does not depend on a random catalogue.

The roles and labels used also support accessibility. If the appearance of a button changes but its user meaning remains, the test is more stable than one using a long CSS selector.

TypeScript

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

test('zobrazí validaci neúplného checkoutu', async ({ page }) => {
    await page.goto('/produkt/bezdratova-mys');
    await page.getByRole('button', { name: 'Přidat do košíku' }).click();
    await page.getByRole('link', { name: 'Přejít do košíku' }).click();
    await page.getByRole('button', { name: 'Objednat' }).click();

    await expect(page.getByText('Vyplňte e-mail')).toBeVisible();
});

How it works

An isolated scenario from browser to assertion

The exact setup varies by application, but a test should not depend on the result of a previous run or an arbitrary moment during loading.

  1. Browser and context The runner launches the selected browser. Each test gets its own browser context: an isolated profile with its own state.
  2. Page A Page represents a browser tab; the test navigates it, reads visible state, and performs actions.
  3. Locator A locator describes a target element and finds it again when an action runs. Roles, labels, and other stable user contracts are preferred.
  4. Actions and waiting Before clicking or filling, Playwright waits until the element is actionable. Assertions retry until the expected state is reached or the timeout expires.
  5. Result and diagnostics On failure, a screenshot, video, or trace can be saved; artefacts should help identify the cause, not conceal a flaky test through retries.

Main components

A stable test depends on good element selection and observable state

The tool API is broad, but a few disciplined habits matter more in everyday tests than a complex wrapper.

Locators

getByRole() with an accessible name usually matches how users and assistive technologies perceive an element. A label suits a field, while data-testid provides an explicit test contract. Long CSS or XPath chains break under ordinary HTML changes.

Web-first assertions

expect(locator).toBeVisible() or toHaveText() verifies user-observable state and can wait. Checking an internal implementation or JavaScript variable is usually more fragile and less valuable.

Auto-waiting

Actions wait until a target is visible, stable, and able to receive input, for example. Fixed sleep delays bypass this mechanism, and CI speed variations then create unreliable tests.

Fixtures and isolation

Built-in fixtures provide page and other resources with a clear lifecycle. A test should prepare its own data or safely use isolated state, not rely on an account and cart left by a previous test.

Projects and parallel execution

Configuration can define Chromium, Firefox, WebKit, and mobile emulation. Parallel execution saves time, but reveals shared accounts, conflicting test data, and order dependencies.

Benefits and limitations

Strong protection for a critical path at the cost of a broader test environment

Benefits

  • verification of a real user flow in the browser
  • auto-waiting and web-first assertions support clearer, more stable tests
  • the same scenario can run in several browser projects
  • traces and other artefacts speed up investigation of CI failures

Limitations and common mistakes

  • too many slow E2E scenarios instead of smaller tests of rules
  • sleep calls and excessive timeouts hiding a race or slow dependency
  • shared data, accounts, and test ordering causing intermittent failures
  • locators tied to fragile CSS structure instead of behaviour, role, or label

Pragmatic use

Test the outcome for the user, not every internal application step.

An E2E scenario should be short, clear, and focused on value that cannot be verified more cheaply at a lower level of the test suite. Checkout, password reset, and user permissions are good candidates. VAT calculation or validation of a value alone is usually faster and more precise outside the browser.

In CI, a small reliable suite over a controlled test environment is better than many scenarios against a randomly shared staging system. External payments, email, and third parties need to be isolated, simulated at a controlled boundary, or equipped with a safe test account.

What to consider

Stability is a property of test design, not a retry setting

Retries can help diagnose a transient issue, but must not become the normal way of tolerating an unreliable test.

  • use roles, labels, and test identifiers with a clear contract
  • wait for a real visible or network state, not a fixed time
  • create or reset the required data and identity for every test
  • separate critical E2E flows from fast unit and integration suites
  • save a trace or screenshot on CI failure to investigate the cause
  • regularly remove duplicate scenarios and tests that depend on implementation details

Common questions

Playwright in a testing strategy

Does Playwright replace PHPUnit?

No. PHPUnit is a PHP testing framework, while Playwright controls a browser and verifies E2E scenarios. Most business rules remain faster and more precise in unit or integration tests.

Why not use sleep after a click?

A fixed delay does not know whether the application is ready: sometimes it only wastes time, and sometimes it is too short. Locator actions and assertions can wait for specific actionability or a visible result.

Are CSS selectors always wrong?

No, but long selectors tied to DOM structure are fragile. For interactive elements, a role with an accessible name, label, or deliberate test ID is usually more stable.

Must every test run in every browser?

It depends on the supported platform and the risk of the change. Define browser projects for the environments actually supported, then choose which suite must pass everywhere and which may run more quickly.

How I approach testing

I verify the most important flows from the user’s perspective across the integrated application.

I structure the test suite so fast checks cover rules and browser scenarios protect only genuinely important user paths.

Request a call

I will call you on the next working day between 9:00 and 17:00.

You can also call me directly.

+420 605 181 728

Leave your phone number and send a callback request.

By sending, you agree to processing your data in order to handle your request.