CohortX
Блог

Interface tests: meeting Playwright

2 августа 2026 г. · 3 мин чтения · Читать по-русски · Антон Молотило

What it is

A test that opens a real browser, clicks buttons and checks that a person sees the right thing. Essentially a robot repeating a user's actions.

Such tests are slower and costlier than unit tests but catch what nothing else can: a broken button, a form that doesn't submit, a break between interface and server.

For a learning project, two or three on the main flows is enough.

Installation

npm init playwright@latest

The installer asks a couple of questions and creates a structure with an example.

A first test

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

test("a visitor sees the notes list", async ({ page }) => {
  await page.goto("http://localhost:3000/notes");
  await expect(page.getByRole("heading", { name: "Notes" })).toBeVisible();
  await expect(page.getByRole("listitem")).toHaveCount(3);
});

Run it:

npx playwright test

With a visible browser, to watch what happens:

npx playwright test --headed

Locating elements

The most important topic: it decides whether your tests are robust or start failing on every styling change.

Good — search the way a person does, by role and label.

page.getByRole("button", { name: "Save" })
page.getByLabel("Email")
page.getByText("Note saved")

The benefit is twofold: the test survives restyling, and it incidentally checks accessibility — if an element can't be found by role, a screen reader won't find it either.

Bad — searching by generated class names. Restyle and every test fails, although nothing broke.

A compromise — dedicated test attributes. Use them where there's no human-readable way to find the element.

Waiting

The main cause of flakiness is checking before the page has updated.

Don't insert fixed pauses: they're either too short or slow everything down. The correct approach waits for a state:

await expect(page.getByText("Saved")).toBeVisible();

That assertion waits for the element to appear and fails only after a reasonable timeout.

Filling a form

test("a note can be added", async ({ page }) => {
  await page.goto("http://localhost:3000/notes");
  await page.getByLabel("Title").fill("Buy bread");
  await page.getByRole("button", { name: "Add" }).click();
  await expect(page.getByText("Buy bread")).toBeVisible();
});

That's a ready main-flow test worth having in any project with forms.

Why such tests get flaky

They depend on everything at once: the network, machine speed, database state, random delays. Hence the "passes sometimes" disease.

What helps:

  • Don't rely on data from a previous run. Each test prepares its own.
  • Don't depend on order. Tests must pass in any sequence.
  • Wait for state, not for time.
  • Don't test through the interface what can be checked more cheaply. Calculations belong in unit tests, not a browser.

What it adds to a portfolio

A couple of end-to-end tests in a learning project reads very well: it shows you think not only about writing something but about it continuing to work.

And for a tester it's the direct route into automation — a discipline where demand is consistently higher than for manual checking.

Хватит читать — пора делать

На CohortX можно найти команду под пет-проект и получить тот самый опыт, о котором спрашивают на собеседовании.

Похожие статьи