Playwright needs one prerequisite: Node.js. The installer (npm init playwright@latest) handles everything else, downloading Chromium, Firefox, and WebKit as isolated binaries, creating the config file, setting up an example test, and configuring .gitignore. This guide walks through every step, explains what each installed file does, and covers the one config change (baseURL) that makes writing tests less repetitive from day one.
What you need before starting
One thing: Node.js. Check if you have it:
node --version
npm --versionIf you get version numbers back (Node 18 or higher, npm 9 or higher), you're ready. If not, go to nodejs.org and download the LTS version. Install it, then come back.
You don't need to install a browser separately. Playwright downloads its own browser binaries during setup: Chromium, Firefox, and WebKit. These are isolated from whatever browsers you have installed on your system.
Create your project
Pick a folder location and run:
mkdir playwright-tests
cd playwright-tests
npm init playwright@latestThe installer asks five questions:
Do you want to use TypeScript or JavaScript? › TypeScript
Where to put your end-to-end tests? › tests
Add a GitHub Actions workflow? › false
Install Playwright browsers? › trueUse TypeScript. It's the professional standard and Playwright's TypeScript support is excellent. Leave the tests folder as tests. Skip GitHub Actions for now (you can add it later). Yes to installing browsers.
The browser download takes 1–3 minutes depending on your connection. Playwright downloads Chromium, Firefox, and WebKit, about 300MB total.
What got installed
After setup, your project looks like this:
playwright-tests/
tests/
example.spec.ts ← sample test
playwright.config.ts ← configuration
package.json ← dependencies
package-lock.json
node_modules/ ← installed packages
.gitignore ← pre-configured for Playwrightplaywright.config.ts is the heart of the setup. Open it:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});Key settings to understand:
fullyParallel: true: tests run in parallel across multiple workers. Faster suite, but requires each test to be independent.
retries: process.env.CI ? 2 : 0: in CI, failed tests retry twice before being counted as failures. Locally, no retries. This is a sane default.
reporter: 'html': generates an HTML report after each run. Open it with npx playwright show-report.
projects: defines which browsers to run tests against. The default runs all three. During development you'll usually run only Chromium.
.gitignore already includes the right exclusions:
node_modules/
playwright-report/
test-results/Don't commit these folders. They're large and regenerated on every run.
Run the example test
Before writing anything, run what came with the installer:
npx playwright testOutput:
Running 6 tests using 6 workers
6 passed (8s)Six tests because the example runs across three browsers × two test cases. Open the report:
npx playwright show-reportA browser opens with a detailed breakdown: which tests ran, how long each took, which browser, and screenshots. Click any test to expand it.
Configure for your project
The defaults are fine, but two changes make daily development better. Open playwright.config.ts and add a baseURL:
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'https://lab.becomeqa.com',
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});With baseURL set, tests can use page.goto('/') instead of the full URL. When you change environments (dev → staging → production), you change one line in the config instead of every test.
Write your first real test
Delete the example test and create tests/login.spec.ts:
import { test, expect } from '@playwright/test';
test('user can log in', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByText('My Travel Items')).toBeVisible();
});Run only this file on Chromium to keep it fast:
npx playwright test tests/login.spec.ts --project=chromiumOutput:
Running 1 test using 1 worker
1 passed (3s)Useful CLI commands
# Run all tests
npx playwright test
# Run a specific file
npx playwright test tests/login.spec.ts
# Run only on Chromium
npx playwright test --project=chromium
# Run in headed mode (browser opens visibly)
npx playwright test --headed
# Run in debug mode (pauses at each step)
npx playwright test --debug
# Run tests matching a name pattern
npx playwright test --grep "login"
# Show last HTML report
npx playwright show-report
# Open interactive UI mode
npx playwright test --ui--ui opens Playwright's interactive test runner, where you can see tests listed, run them individually, and watch them execute step by step with time-travel debugging. Start there when you're learning.
Use the code generator
If you're not sure what locator to use for an element, let Playwright generate it for you:
npx playwright codegen https://lab.becomeqa.comA browser opens. Click around the page, and Playwright records your actions and generates the corresponding test code in a panel on the right. It's not always perfect, but it's the fastest way to find locators you're not sure about.
getByRole or getByLabel where possible.Understand the test structure
import { test, expect } from '@playwright/test';
test('description of what this tests', async ({ page }) => {
// your test code here
});test is the function that defines a test case. The callback is async because all browser actions are asynchronous: they take time to complete.
{ page } is a fixture, Playwright's system for providing pre-built objects to tests. page gives you a fresh browser tab. Other fixtures you'll use: request for API testing, context for browser context operations, browser for multi-tab scenarios.
expect is the assertion library. It has automatic retrying built in: expect(locator).toBeVisible() keeps checking for up to 5 seconds before failing.
Project structure as you grow
The default structure works for a handful of tests. As your suite grows:
playwright-tests/
tests/
auth/
login.spec.ts
logout.spec.ts
items/
items-list.spec.ts
pages/ ← Page Object classes go here
LoginPage.ts
DashboardPage.ts
fixtures/ ← Custom fixtures go here
auth.fixture.ts
playwright.config.ts
package.jsonStart with tests/ flat. Add subfolders when you have more than 5–6 test files. Add pages/ when you start repeating the same locators across tests.
pages/ folder on day one. Write 10–15 tests first. The patterns you need will become obvious from the repetition you're actually seeing, not the repetition you're imagining.FAQ
Which version of Playwright should I install?Always the latest. npm init playwright@latest handles this. Playwright releases frequently and breaking changes are rare. Staying current is easier than catching up after falling behind.
pnpm or yarn instead of npm?
Any works. npm is fine for learning. Some teams prefer pnpm for disk space efficiency in CI. Don't overthink this.
Run npx playwright install to retry. If a specific browser fails, install it individually: npx playwright install chromium.
No. Playwright is a Node.js library. Python, Java, and .NET bindings exist but they all require their respective runtimes to be installed.
The test hangs and never finishes. Why?Usually a baseURL misconfiguration or a network issue. Add --timeout=10000 to fail faster during debugging: npx playwright test --timeout=10000.