Playwright Basics
Before writing real tests, you need a mental model of Playwright's core pieces: the test runner, fixtures, async actions, and how a typical project is organized. This lesson walks through those building blocks with runnable examples.
The Page Fixture and Async Actions Every Playwright test receives fixtures — most commonly page, a single browser tab. All navigation and interaction methods (goto, click, fill) are asynchronous and must be awaited.
Locators represent elements on the page. They are lazy: creating a locator does not query the DOM immediately. Playwright re-resolves locators at action and assertion time, which powers auto-waiting.
import { test, expect } from '@playwright/test';
test('dashboard menu', async ({ page }) => {
await page.goto('/dashboard');
await page.getByRole('button', { name: 'Account' }).click();
await expect(page.getByRole('menuitem', { name: 'Logout' })).toBeVisible();
}); Each await waits for the action or assertion to succeed (or time out). No manual sleep is needed for typical UI updates.
Default Playwright Project Structure playwright.config.ts
tests/
example.spec.ts
auth.setup.ts
package.json playwright.config.ts configures browsers, base URL, timeouts, and reporters. tests/ holds spec files named *.spec.ts by convention. @playwright/test provides test, expect, and built-in fixtures. Run tests with npx playwright test from the project root. Playwright Basics Cheat Sheet Essential vocabulary before writing your first spec file.
Term Meaning Fixture Built-in dependency injected into tests (page, context, request) Page A single browser tab for navigation and interaction Locator A query for element(s), resolved at action time Spec file A file containing tests (*.spec.ts) Auto-wait Playwright waits for elements to be actionable before acting Config playwright.config.ts centralizes project settings Headless Run browsers without a visible window (default in CI)
Why Every Playwright Call Needs await Playwright's API is promise-based. Forgetting await means the test may finish before actions complete, producing false passes or confusing errors.
// Wrong: click may not finish before the assertion runs
page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Saved')).toBeVisible();
// Right
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Saved')).toBeVisible(); Enable @typescript-eslint/no-floating-promises to catch missing awaits in TypeScript projects.
Anatomy of a Spec File import { test, expect } from '@playwright/test';
test.describe('Login page', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
});
test('shows error for invalid credentials', async ({ page }) => {
await page.getByLabel('Email').fill('bad@example.com');
await page.getByLabel('Password').fill('wrong');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page.getByRole('alert')).toContainText('Invalid credentials');
});
}); test.beforeEach() runs before every test in the describe block.
Common Mistakes Forgetting await on Playwright methods, causing race conditions. Storing locators in variables and expecting them to stay valid across navigations without re-querying. Putting all tests in one giant spec file instead of organizing by feature. Confusing page (one tab) with context (isolated session) or browser (process). Key Takeaways Playwright tests are async; always await actions and assertions. The page fixture is your primary interface for browser interaction. Locators are lazy and re-resolved, enabling reliable auto-waiting. Project structure centers on playwright.config.ts and tests/*.spec.ts.
Pro Tip
Install the official Playwright VS Code extension — it highlights missing awaits, runs tests inline, and opens Trace Viewer on failure.
Introduction to Playwright Go to next item