IKivan-kozenko -aqa
Try yourself as a QA tester
All topics·Beginner·3 / 16

Writing your first tests

test() blocks, locators, web-first expect assertions, and the page fixture.

Anatomy of a test

A test is a function passed to test('name', async ({ page }) => { ... }). Playwright injects fixtures (page, context, request, …) into the destructured argument. Each test gets a fresh isolated browser context, so there is no shared cookie or storage pollution between tests by default.

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

test('homepage has title and CTA', async ({ page }) => {
  await page.goto('https://playwright.dev/')

  await expect(page).toHaveTitle(/Playwright/)
  await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible()
})

Locators

Locators are how Playwright finds elements. Prefer user-facing locators: getByRole, getByLabel, getByText, getByPlaceholder, getByAltText, getByTitle. They are auto-waiting and re-resolving: every action retries until the locator points to exactly one actionable element or the timeout is reached.

Use CSS or XPath only when there is no semantic alternative. Locators chain: page.getByRole('list').getByRole('listitem').nth(0).

ts
await page.getByRole('button', { name: 'Sign in' }).click()
await page.getByLabel('Email').fill('user@example.com')
await page.getByPlaceholder('Search').press('Enter')
await page.getByText('Welcome', { exact: false }).waitFor()

Web-first assertions

expect() from @playwright/test auto-retries until the assertion passes or the assertion timeout expires. This removes most manual waitFor calls and the dreaded sleep(...). Prefer assertions like toBeVisible, toHaveText, toHaveURL over checking DOM state immediately.

ts
await expect(page.getByRole('alert')).toHaveText('Saved')
await expect(page).toHaveURL(/\/dashboard$/)
await expect(page.getByTestId('cart-count')).toHaveText('3')