Installing Playwright with TypeScript

8 min read

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 --version to 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 Code extension 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@latest

The installer asks four questions:

  1. TypeScript or JavaScript? Choose TypeScript.
  2. Where to put your tests? Accept the default: tests.
  3. Add a GitHub Actions workflow? Choose Yes — we'll wire it up in chapter 8.
  4. 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 pattern process.env.CI ? 2 : 0 retries on CI but not locally — exactly what you want.
  • workers — number of parallel worker processes. Default is half the logical CPUs locally; 1 on CI is conservative but predictable.
  • reporter'html' opens an interactive report; 'list' is the CLI summary; 'junit' writes JUnit XML for CI.
  • use.baseURL — call page.goto('/products') instead of the full URL. The same role as baseUrl in 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 test

Three 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 test

This 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 --ui

This 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 --headed

You 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.tstests/*.spec.ts
  • cypress.config.tsplaywright.config.ts
  • cy.visit('/products')await page.goto('/products')
  • cy.get(...).should(...)await expect(page.locator(...)).toBe...
  • cypress openplaywright test --ui
  • cypress runplaywright 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.

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 every test() call.
  • ESLint (dbaeumer.vscode-eslint) — catches typos and dead code in your specs.
  • Prettier (esbenp.prettier-vscode) — auto-formats .ts files 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-playwright via npx playwright install. After cloning a repo or installing on CI, you need that step. The error browserType.launch: Executable doesn't exist always means missing binaries.
  • Setting workers: 1 to silence flaky tests. Forcing serial execution masks race conditions in your tests rather than fixing them. The right move is to let fullyParallel: true stay on and use test.describe.configure({ mode: 'serial' }) only for the small set of tests that genuinely need shared state.
  • Editing the package's playwright instead of @playwright/test. There are two packages: @playwright/test is the test runner with test, expect, and the page fixture; playwright is 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.

  1. Create a folder playwright-ecommerce-tests/ and run npm 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.

  2. Open playwright.config.ts and set use.baseURL to 'https://www.saucedemo.com' (a public e-commerce sandbox designed for automation practice — credentials standard_user / secret_sauce).

  3. Replace tests/example.spec.ts with the sanity assertion from the lesson. Run npx playwright test — three green ticks across Chromium, Firefox, and WebKit confirm everything's wired up.

  4. 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.

  5. Add the five npm scripts (test, test:ui, test:headed, test:debug, report) to package.json. Run npm test — both specs execute headlessly with a green summary.

  6. 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.

  7. Stretch: in playwright.config.ts, comment out the webkit project. Save. Run npm test again — only Chromium and Firefox results appear. Uncomment and re-run. You've just learned how projects controls 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.

// tip to track lessons you complete and pick up where you left off across devices.