How to Use Playwright: The Beginner's Guide (First Install, First Test, Locators, UI Mode, and CI)

CodexFuse Team · 2026-10-31T03:57:27.257Z · 13 min read

What is Playwright, and why do beginners choose it?

Playwright is an open-source end-to-end testing framework, built by Microsoft, that drives real browsers — Chromium, Firefox, and WebKit — through a single API. If you are learning how to use Playwright from zero, the short version is this: it installs with one command, ships its own test runner, waits for pages intelligently so tests rarely flake, and gives you a visual debugger and CI workflow out of the box. This Playwright tutorial walks you from a blank folder to a first passing test, then to locators, UI mode, a real second test, and continuous integration — one page, no gaps.

Three design choices make Playwright beginner-friendly. First, everything is bundled: the test runner, assertions, test isolation, parallel execution, and debugging tools arrive together, so there is no plugin hunt on day one. Second, it auto-waits — Playwright waits for elements to become actionable before clicking or typing, which eliminates the sleep-and-hope pattern that makes beginners' Selenium tests flaky. Third, its assertions are web-first: a check like toHaveTitle() is automatically retried until the page reaches the expected state or a timeout hits, which is exactly what a dynamic web app needs.

PlaywrightSelenium
Browser controlDirect protocol per browser engine (Chromium, Firefox, WebKit)WebDriver protocol with a driver binary per browser
Auto-waitingBuilt in — waits for actionability before every actionManual waits you write yourself
Test runnerBundled (Playwright Test)Bring your own (JUnit, pytest, Jest…)
IsolationA fresh browser context per test, milliseconds to createYou manage profiles and cleanup
DebuggingUI mode, trace viewer, codegen, HTML report built inThird-party tools
LanguagesTypeScript/JavaScript, Python, .NET, JavaMany languages

The Playwright vs Selenium debate usually ends the same way for a beginner: Selenium has the longer history and wider language support, but Playwright removes the three chores beginners trip over — waits, runners, and debugging tooling. If your goal is a first green test today, Playwright is the faster road.

How do you install Playwright?

You need Node.js 22, 24, or 26 and a terminal — write your tests in VS Code, and our VS Code beginner guide gets the editor ready in minutes. Then the whole install is one command, exactly as the current official docs teach it:

  1. Check Node. Run node --version. Playwright's current docs require the latest 22.x, 24.x, or 26.x.
  2. Run the init command. npm init playwright@latest — in a new folder or inside an existing project. Do not substitute npx playwright install here; the init command is the one the current docs teach, and it does more than fetch browsers.
  3. Answer the wizard. Choose TypeScript or JavaScript (TypeScript is the default), confirm the tests folder name (default tests, or e2e if tests already exists), say yes to the GitHub Actions workflow (it writes your CI file for free), and say yes to installing the browsers.
  4. Run the example test. npx playwright test runs the scaffolded example headless across Chromium, Firefox, and WebKit.
  5. Open the report. npx playwright show-report opens the HTML report. It auto-opens on failure; otherwise open it manually.

What the wizard created: playwright.config.ts (browsers, timeouts, retries, reporters), a tests/ folder with a minimal example.spec.ts, your package.json updated with the @playwright/test dependency, and — if you said yes — a .github/workflows/playwright.yml CI file. You can re-run the init command later; it never overwrites your existing tests.

How do you write your first Playwright test?

Delete the example test and create tests/first.spec.ts. A good first test follows four habits: one clear scenario per test, await on every browser action, Playwright's web-first assertions instead of reading values into variables, and a real URL you can load anywhere:

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

test('homepage has the Playwright title', async ({ page }) => {
  await page.goto('https://playwright.dev');
  await expect(page).toHaveTitle(/Playwright/);
});

Reading it line by line: test() names one scenario, page is the browser tab Playwright hands you, page.goto() navigates like a user would, and expect(page).toHaveTitle() is a web-first assertion — it retries until the title matches or the timeout expires, so a slow page does not flake your test.

Run it with npx playwright test and you will see something like 1 passed (2.1s). Add --headed to watch the browser window open and click through the page. Run one file with npx playwright test tests/first.spec.ts, or one browser with --project=chromium. Beginners often start with Chromium only, then open the other two projects once the tests are green — fewer moving parts while you learn.

How do Playwright locators work?

Locators are how Playwright finds elements, and they are the reason its tests are resilient: every time you use a locator for an action, Playwright finds a fresh, up-to-date element in the current DOM — even if the page re-rendered between your calls. Two rules cover 90% of locator discipline: reach for getByRole() first, and never write long CSS or XPath chains.

The official docs recommend this priority order, because it mirrors how users and assistive technology perceive the page:

// Preferred: by role + accessible name
await page.getByRole('button', { name: 'Sign in' }).click();

// Form fields: by their visible label
await page.getByLabel('Password').fill('secret-password');

// Placeholder only when there is no label
await page.getByPlaceholder('[email protected]').fill('[email protected]');

// Non-interactive content: by text
await expect(page.getByText('Welcome back')).toBeVisible();

// Images: by alt text
await page.getByAltText('playwright logo').click();

// Explicit contract for elements nothing else fits: test ids
await page.getByTestId('directions').click();

Two more behaviors beginners should know on day one. Locators are strict: if an action's locator matches more than one element, Playwright throws a strict-mode violation instead of guessing — narrow it with a name, or chain and filter (for example page.getByRole('listitem').filter({ hasText: 'Product 2' }).getByRole('button', { name: 'Add to cart' })). And locators pierce open Shadow DOM automatically, so web components do not need special handling.

Use page.locator('css=...') or XPath only as a last resort. A selector like #tsf > div:nth-child(2) > div.A8SBwf … is tied to the page's implementation and breaks the next time a designer moves a div; the official docs call this exact pattern a bad practice that leads to unstable tests.

How do you use Playwright codegen the right way?

Codegen records your clicks and typing and generates test code: npx playwright codegen https://playwright.dev. It is excellent for discovering selectors and learning the API quickly. It is not a finished test — treat its output as a rough draft and clean it up before committing.

A raw recording tends to look like this: an unnamed test, fragile selectors, and every pause you took baked in. The cleaned version keeps the actions but renames the test, uses getByRole() locators, groups the flow with test.step(), and asserts an outcome:

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

test('docs navigation reaches the installation guide', async ({ page }) => {
  await test.step('Open the homepage', async () => {
    await page.goto('https://playwright.dev');
  });

  await test.step('Open the installation guide', async () => {
    await page.getByRole('link', { name: 'Get started' }).click();
  });

  await test.step('Verify the guide loaded', async () => {
    await expect(
      page.getByRole('heading', { name: 'Installation' })
    ).toBeVisible();
  });
});

The habit to build: record with codegen, then rewrite. Every beginner tutorial that ships raw codegen output as "the test" is teaching a maintenance headache.

How do you debug a failing test with UI mode?

Run npx playwright test --ui. UI mode is a watch-mode debugger: tests re-run as you edit, every step renders with its own DOM snapshot, and you can time-travel back through each action to see exactly what the page looked like when it failed. It is the fastest way to understand a red test, and it is the tool the fragmented beginner guides skip most often.

Two companions cover the rest of debugging. For step-through debugging, run a single test with the inspector: npx playwright test tests/first.spec.ts --debug. For failures that only happen in CI, use the trace viewer: the config the init wizard generates records a trace whenever a test is retried (trace: 'on-first-retry'), and after npx playwright show-report you click the trace icon next to the failing test to walk its screencast, DOM snapshots, and action log.

When a test fails, read in this order: the error message (what was expected vs received), the call log, then the trace. Most beginner failures are locator problems — a name that changed or two elements matching — and the trace shows you which one in seconds.

How do you write a second, real test — a login flow?

Your second test should exercise a real multi-step flow, because single-page title checks do not teach you anything new. This one logs into a public demo site — the credentials below are the site's own published test account, verified live while writing this guide:

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

test('login with valid credentials succeeds', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/login');

  await page.getByLabel('Username').fill('tomsmith');
  await page.getByLabel('Password').fill('SuperSecretPassword!');
  await page.getByRole('button', { name: 'Login' }).click();

  await expect(page.getByText(/logged into a secure area/i)).toBeVisible();
});

Notice what this test does not do: no page.waitForTimeout() sleeps (the number-one beginner mistake — auto-waiting already handles timing), no reading page.title() into a variable for a bare assertion, and one scenario only — login success. A second test would cover wrong credentials; never stack both into one test, because a failure should tell you exactly which scenario broke.

For a real project, point the same structure at your own app's login page and keep credentials out of the test file — read them from environment variables and never commit secrets.

How do you run Playwright tests in CI?

If you accepted the wizard's GitHub Actions offer, you already have .github/workflows/playwright.yml. This is the current workflow the official docs generate — push it with your repo and your tests run on every push and pull request to main:

name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    - uses: actions/setup-node@v6
      with:
        node-version: lts/*
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright Browsers
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test
    - uses: actions/upload-artifact@v4
      if: ${{ !cancelled() }}
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

New to GitHub? Our GitHub beginner guide covers repos, pushes, and pull requests first. Once the workflow exists, open the repo's Actions tab after a push: green check means green tests, and clicking a failed run shows the same error messages, expected-vs-received output, and call log you see locally — learn to read a failed run before you ever add a deploy step.

Three CI habits worth adopting early: keep browser installs in the workflow (--with-deps also installs the OS libraries the browsers need), upload the HTML report as an artifact so teammates can download it, and set a globalTimeout in playwright.config.ts (the docs suggest an hour) so a hung suite fails with a report instead of burning CI minutes forever. If your tests need a containerized app to run against, our Docker beginner guide shows how to package it.

Where should your tests run — your laptop, CI, or a self-hosted runner?

Run on your laptop while writing, run in CI on every push, and only add infrastructure when you feel real pain. GitHub-hosted runners are free for public repos and cheap for private ones, and they are the right default for a beginner suite. Consider a self-hosted runner on your own VPS hosting plan when tests need resources CI cannot give you — a GPU, a private network, or browser versions you control — but accept that you then own updates, security, and uptime.

Either way, keep the suite fast enough to respect: parallelize (Playwright runs tests in parallel by default), keep each test independent, and delete or quarantine flaky tests instead of letting them train everyone to ignore red builds.

What mistakes do Playwright beginners always make?

Sleeping with waitForTimeout. await page.waitForTimeout(5000) is the classic beginner fix for timing issues. It slows every run and still flakes under load. Delete it and trust auto-waiting plus web-first assertions.

Asserting on variables instead of the page. const title = await page.title(); expect(title).toBe(...) checks once and moves on. await expect(page).toHaveTitle(...) retries until the condition holds — that retry loop is the whole point.

One giant test per file. A 200-line script that logs in, buys a thing, and logs out tells you nothing when step 14 fails. One scenario per test, named for what it proves.

Long CSS and XPath chains. Selectors tied to div depth break on every redesign. The docs' bad-practice example is a selector six levels deep — if yours looks like that, rewrite it with getByRole() or a test id.

Committing raw codegen. Recorded code is a draft: unnamed tests, fragile selectors, recorded pauses. Clean every recording before it reaches main, or the suite rots within a month.

Sources

  • Playwright official docs — Installation (install command npm init playwright@latest, wizard prompts, run/report/UI-mode commands, Node.js 22/24/26 requirement): https://playwright.dev/docs/intro — fetched 2026-10-30.
  • Playwright official docs — Release notes (Playwright 1.64 current; bundled Chromium 156, Firefox 157, WebKit 27.2): https://playwright.dev/docs/release-notes — fetched 2026-10-30.
  • Playwright official docs — Setting up CI (current GitHub Actions workflow: checkout@v6, setup-node@v6, npx playwright install --with-deps, artifact upload, globalTimeout guidance): https://playwright.dev/docs/ci-intro — fetched 2026-10-30.
  • Playwright official docs — Locators (getByRole/getByText/getByLabel/getByPlaceholder/getByAltText/getByTitle/getByTestId priority order, strictness, Shadow DOM, CSS/XPath caution): https://playwright.dev/docs/locators — fetched 2026-10-30.
  • Anton — "Playwright Tutorial for Beginners: Your First Test in 10 Minutes" (first-test structure, codegen command, auto-waiting examples): https://www.anton.qa/blog/posts/playwright-tutorial-for-beginners-your-first-test-in-10-minutes — search result fetched 2026-10-30.
  • DEV Community — "Playwright Basics: Your First Test with Page, Test Structure, and Codegen" (page object, test.step structure, codegen-as-draft cleanup): https://dev.to/autonekoqa/playwright-basics-your-first-test-with-page-test-structure-and-codegen-592 — search result fetched 2026-10-30.
  • The Internet demo login page (test credentials tomsmith / SuperSecretPassword!, Username/Password labels — used for the runnable login example): https://the-internet.herokuapp.com/login — fetched 2026-10-30.

Frequently Asked Questions

Is Playwright free to use?

Yes. Playwright is open-source and free, maintained by Microsoft. You pay nothing for the framework itself; the only costs are the machines that run your tests — your laptop, CI minutes, or a self-hosted runner.

Do I need to know JavaScript to use Playwright?

Enough JavaScript or TypeScript to write short async functions — variables, await, and imports cover most tests. Playwright also ships official libraries for Python, .NET, and Java, so you can write tests in the language your team already uses.

Should I learn Playwright or Cypress?

Learn Playwright if you want cross-browser coverage (Chromium, Firefox, and WebKit in one API) with auto-waiting and built-in parallelism from day one. Cypress remains popular and has an excellent developer experience for Chromium-first teams, but it does not drive real Firefox or WebKit the way Playwright does.

Can Playwright test Safari?

Yes — Playwright bundles WebKit, the engine Safari is built on, and runs it on any OS. It also runs against real Chrome and Edge channels when you need the genuine browser rather than the bundled build.

How long does it take to learn Playwright basics?

Most developers go from zero to a first passing test in under an hour — the install is one command and the example test runs immediately. Reliable locators, debugging in UI mode, and a CI workflow take another few days of practice, and this guide covers all three.

Featured on Twelve Tools Launch Llama Tools Launch your MCP