End-to-End Testing with Playwright

Unit Tests vs End-to-End Tests

Let’s understand the difference between unit tests and end-to-end (e2e) tests.

Unit Tests (Vitest)

  • Test small pieces of code (like single functions)
  • Run very quickly
  • Don’t use a real browser
  • Good for testing business logic

End-to-End Tests (Playwright)

  • Test your whole website
  • Run in a real browser
  • Test how everything works together
  • Good for testing what real users do

Why Do We Need Both Types of Tests?

Think of checking a car:

  • Unit tests are like testing one part at a time (brakes, lights)
  • E2E tests are like driving the whole car to make sure it works

We need both because:

  • Unit tests help us find problems in specific parts quickly
  • E2E tests make sure everything works together properly

Setting Up Playwright

Let’s set up Playwright in our project.

First, install Playwright:

bash
	npm init playwright@latest

During installation, answer these questions:

  • Do you want to use TypeScript? → No
  • Where to put your end-to-end tests? → tests
  • Add a GitHub Actions workflow? → No
  • Install Playwright browsers? → Yes

This creates:

  • A tests folder for your test files
  • A playwright.config.js file with settings

The First Test

Let’s write our first real test.

We’ll check if we can find a link on the Noroff website:

javascript
	import { test, expect } from "@playwright/test";
	
	test("homepage should show the News link", async ({ page }) => {
	  // Start at the English homepage
	  await page.goto("https://www.noroff.no/en");
	
	  // Wait for the new page to load and check its content
	  await expect(page.getByRole("link", { name: "News" })).toBeVisible();
	});

Let’s understand this test:

  • test - Creates a new test (like in Vitest)
  • "homepage should show the News link" - Describes what we’re testing
  • async - Tells JavaScript to wait for things to complete
  • ({ page }) - Gives us a browser page to work with
  • goto - Opens a specific URL
  • getByRole - Finds elements by their HTML role
  • toBeVisible - Checks if we can see something on the page

This simple test is doing what a real user would do:

  • Go to the Noroff website
  • Check if they can see the News link

Running Tests

bash
	npx playwright test
	# Runs tests in all browsers in the background (headless mode)
	
	npx playwright test --ui
	# Opens Playwright's test interface where you can see tests, run them, and see results
	
	npx playwright test --headed
	# Shows the actual browser windows while tests run - useful to see what's happening
	
	npx playwright test --debug
	# Runs tests step by step - helpful when tests fail and you need to see why

Run in specific browsers:

bash
	npx playwright test --project=webkit   # Only Safari
	npx playwright test --project=firefox  # Only Firefox
	npx playwright test --project=webkit --project=firefox   # Safari and Firefox

By default, tests run in:

  • Chromium (Chrome)
  • Firefox
  • WebKit (Safari)

You can change this in playwright.config.js:

javascript
	// Only run in Chrome and Firefox
	export default {
	  projects: [
	    {
	      name: "chromium",
	      use: { ...devices["Desktop Chrome"] },
	    },
	    {
	      name: "firefox",
	      use: { ...devices["Desktop Firefox"] },
	    },
	  ],
	};

Common Matchers

Here are some useful ways to check things on a webpage. These work just like the Vitest matchers we learned about before:

javascript
	// Check if something is visible
	await expect(page.getByRole("button")).toBeVisible();
	
	// Check if something is hidden
	await expect(page.getByRole("button")).toBeHidden();
	
	// Check page URL
	await expect(page).toHaveURL("https://www.noroff.no/en");
	
	// Check page title
	await expect(page).toHaveTitle("Noroff");
	
	// Check text content
	await expect(page.getByRole("heading")).toHaveText("Welcome");
	
	// Check if something exists
	await expect(page.getByRole("button")).toBeEnabled();

What We Learned

In this lesson, we:

  • Understood the difference between unit and e2e tests
  • Set up Playwright in our project
  • Wrote our first e2e test
  • Learned different ways to run tests
  • Discovered common matchers that work like Vitest