You know what Playwright is. Time to install it. This lesson takes you from an empty folder to a fully scaffolded TypeScript Playwright project — including a working playwright.config.ts, a typed test file you can run before the lesson ends, and the npm scripts you'll use every day. Every command is meant to be typed into your terminal as you read.
Prerequisites
Before you start, you need:
- Node.js v18 or later. Run
node --versionto confirm. Playwright bundles its own browser binaries, but the test runner itself needs a modern Node runtime. - A code editor. VS Code is what every example in this course assumes. Microsoft maintains the official
Playwright Test for VS Codeextension that runs and debugs tests directly from the editor — install it now. - Working knowledge of JavaScript and TypeScript as covered in JavaScript for QA and TypeScript for QA. We won't re-explain
const, generics, async/await, or interfaces here.
If node --version prints something v18 or higher and the Playwright VS Code extension is installed, you're ready.
Creating the project — one command does everything
Unlike Cypress, Playwright ships a single scaffolder that handles the whole setup:
mkdir playwright-ecommerce-tests
cd playwright-ecommerce-tests
npm init playwright@latestThe installer asks four questions:
- TypeScript or JavaScript? Choose TypeScript.
- Where to put your tests? Accept the default:
tests. - Add a GitHub Actions workflow? Choose Yes — we'll wire it up in chapter 8.
- Install Playwright browsers? Choose Yes — Chromium, Firefox, and WebKit get downloaded.
The download is around 200 MB total because Playwright bundles three pinned browser builds — that's normal, not a mistake. The whole flow takes 60 seconds on a decent connection.
When it finishes, your project tree looks like this:
playwright-ecommerce-tests/
├── tests/ ← your test files (.spec.ts) live here
│ └── example.spec.ts ← starter test against playwright.dev
├── tests-examples/ ← additional starter examples (delete or keep)
├── .github/
│ └── workflows/
│ └── playwright.yml ← CI config (chapter 8)
├── playwright.config.ts ← top-level configuration
├── package.json
└── package-lock.json
You don't need a separate tsconfig.json unless you want strict TypeScript options — @playwright/test ships its own type declarations and the runner compiles .ts directly. If you do add one for IDE polish, it'll go at the root.
Reading playwright.config.ts
Open playwright.config.ts. The generated file looks like this (trimmed for clarity):
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./tests",
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: "html",
use: {
baseURL: "http://localhost:3000",
trace: "on-first-retry",
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
],
});defineConfig is a typed wrapper that gives you autocomplete on every option — hover any field in VS Code and you'll see the schema. The fields you'll touch most:
testDir— root folder for spec files. Default./tests.fullyParallel— run tests in parallel within each file too (not just across files). On by default for new projects.retries— how many times to retry a failed test before giving up. The patternprocess.env.CI ? 2 : 0retries on CI but not locally — exactly what you want.workers— number of parallel worker processes. Default is half the logical CPUs locally;1on CI is conservative but predictable.reporter—'html'opens an interactive report;'list'is the CLI summary;'junit'writes JUnit XML for CI.use.baseURL— callpage.goto('/products')instead of the full URL. The same role asbaseUrlin Cypress.use.trace— when to capture traces.'on-first-retry'means: only on a retry, so you get evidence of flaky failures without bloating disk on every run.projects— the multi-browser magic. Each project runs every test once with its own config. Three projects → every test runs in three browsers automatically.
Save the file with baseURL pointing at your local app (or a public sandbox like https://www.saucedemo.com if you don't have one yet). Playwright picks up changes on the next run; no restart needed.
Verify with a one-line test
Open tests/example.spec.ts (auto-generated by the scaffolder) — it tests against playwright.dev. Replace it with a tiny sanity check you control:
import { test, expect } from "@playwright/test";
test("Playwright is wired up correctly", async () => {
expect("hello playwright").toEqual("hello playwright");
});This doesn't touch a browser at all — it's a pure assertion. Run it:
npx playwright testThree green ticks (one per browser project) and you're confirmed installed. If you see a missing-browser error, run npx playwright install to fetch the binaries.
Running tests — three modes
Playwright has three ways to run, and you'll use all of them:
Headless (CI-style). No UI, fast, terminal output:
npx playwright testThis runs every spec under tests/ against every project (Chromium, Firefox, WebKit) and prints a pass/fail summary. CI pipelines call this exact command.
UI Mode (interactive). A desktop app with the test list, a browser preview, time-travel through every action, and live re-runs on save:
npx playwright test --uiThis is your day-to-day authoring environment — the closest equivalent to Cypress's runner, but with a richer trace timeline and a built-in locator picker.
Headed (browser visible). Useful when you want to watch the browser do things during a CI-style run, usually for one-off debugging:
npx playwright test --headed
npx playwright test tests/login.spec.ts --headedYou can combine flags: --debug opens the inspector, --project=chromium runs only one browser, --workers=1 disables parallelism.
Add npm scripts
Stop typing npx playwright every time. In package.json:
{
"scripts": {
"test": "playwright test",
"test:ui": "playwright test --ui",
"test:headed": "playwright test --headed",
"test:debug": "playwright test --debug",
"report": "playwright show-report"
}
}Now npm test runs the whole suite, npm run test:ui opens UI Mode, and npm run report opens the HTML report from the last run. Every Playwright project in this course assumes these five scripts exist — get into the habit now.
The full installation flow
Step 1 of 5
npm init playwright
mkdir + cd + npm init playwright@latest. The single command scaffolds the whole project — config, tests folder, GitHub Actions workflow, and browser downloads.
Coming from Cypress?
The mapping is direct:
cypress/e2e/*.cy.ts→tests/*.spec.tscypress.config.ts→playwright.config.tscy.visit('/products')→await page.goto('/products')cy.get(...).should(...)→await expect(page.locator(...)).toBe...cypress open→playwright test --uicypress run→playwright test
The biggest mental shift is async/await everywhere. Cypress chains commands and resolves them serially under the hood; Playwright is fully async — every action returns a Promise and needs an await. We'll use this pattern from the very first test.
Recommended VS Code extensions
While the install is fresh, pull up VS Code and install:
- Playwright Test for VS Code (
ms-playwright.playwright) — Microsoft's official extension. Run/debug tests from the test explorer, see results inline, click locators in the page picker, generate code with one click. The killer feature is the inline run button next to everytest()call. - ESLint (
dbaeumer.vscode-eslint) — catches typos and dead code in your specs. - Prettier (
esbenp.prettier-vscode) — auto-formats.tsfiles on save.
The full Playwright reference is on the Playwright tools page and the Playwright commands cheat sheet.
⚠️ Common mistakes
- Forgetting to install browsers after
npm install. The browser binaries are not part of the npm package — they download separately into~/.cache/ms-playwrightvianpx playwright install. After cloning a repo or installing on CI, you need that step. The errorbrowserType.launch: Executable doesn't existalways means missing binaries. - Setting
workers: 1to silence flaky tests. Forcing serial execution masks race conditions in your tests rather than fixing them. The right move is to letfullyParallel: truestay on and usetest.describe.configure({ mode: 'serial' })only for the small set of tests that genuinely need shared state. - Editing the package's
playwrightinstead of@playwright/test. There are two packages:@playwright/testis the test runner withtest,expect, and thepagefixture;playwrightis the lower-level browser-automation library used outside the test runner. For tests, you only ever import from@playwright/test.
🎯 Practice task
Build the project you'll use for the rest of this course. 20-30 minutes.
-
Create a folder
playwright-ecommerce-tests/and runnpm init playwright@latest. Pick the four options as recommended (TypeScript,tests, yes GitHub Actions, yes install browsers). Confirm the file tree matches the diagram in the lesson. -
Open
playwright.config.tsand setuse.baseURLto'https://www.saucedemo.com'(a public e-commerce sandbox designed for automation practice — credentialsstandard_user/secret_sauce). -
Replace
tests/example.spec.tswith the sanity assertion from the lesson. Runnpx playwright test— three green ticks across Chromium, Firefox, and WebKit confirm everything's wired up. -
Now create a second spec,
tests/visit.spec.ts:import { test, expect } from "@playwright/test"; test("loads the Sauce Demo login page", async ({ page }) => { await page.goto("/"); await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible(); });Run it with
npx playwright test --ui— UI Mode opens, you click the test in the sidebar, and the browser navigates to Sauce Demo with the login fields visible. -
Add the five npm scripts (
test,test:ui,test:headed,test:debug,report) topackage.json. Runnpm test— both specs execute headlessly with a green summary. -
Run
npm run report. The HTML report opens in your browser. Click any test to see the action timeline, the snapshots, and (for failures) the screenshot. This report is what you'll send to a teammate when you need a second pair of eyes. -
Stretch: in
playwright.config.ts, comment out thewebkitproject. Save. Runnpm testagain — only Chromium and Firefox results appear. Uncomment and re-run. You've just learned howprojectscontrols browser coverage; chapter 6 will use this exact mechanism for parallel role-based auth setups.
Once npm test passes against three browsers and npm run test:ui lets you replay any test interactively, you've completed the loop a real Playwright engineer runs daily. The next lesson takes this scaffold and writes a multi-test e-commerce spec — test.describe, beforeEach, real assertions, real navigation.