What Should We Test?
Let’s think about what kind of features are most important to test:
Login and Register
- Why? Because users can’t do anything without an account
- If these break, nobody can use our site
Important Actions (like shopping cart or posting)
- Why? These are the main things users want to do
- If these break, users get frustrated and leave
Search and Navigation
- Why? Users need to find things on our site
- If these break, users get lost
We will look at writing some e2e tests for the example repo.
Setting Up baseURL
First, we need to tell Playwright where to find our website. In your config file set the baseUrl
:
// playwright.config.js
export default defineConfig({
use: {
baseURL: 'http://localhost:5500'
}
});
Now when you use, for example, page.goto('/auth/login')
in your tests, Playwright will go to http://localhost:5500/auth/login
.
To make this work, we need a local server running our site during tests. Let’s add live-server:
npm i live-server -D
Add a script to your package.json:
{
"scripts": {
"start": "live-server --port=5500"
}
}
Update your Playwright config to start the server automatically before running tests:
export default defineConfig({
webServer: {
command: 'npm run start',
url: 'http://localhost:5500',
reuseExistingServer: !process.env.CI,
},
use: {
baseURL: 'http://localhost:5500'
}
});
This way, Playwright will start your local server before running tests and shut it down when they’re done. The reuseExistingServer option tells Playwright to use any running server when testing locally, but always start a fresh one in CI environments.
Using Environment Variables
Even though we’re using vanilla JavaScript in the example repo, we can use environment variables in our Playwright tests since they run in Node.js.
When testing, we often need to use things like emails, passwords or API keys. Never put these directly in your test files, rather use environment variables.
- Install dotenv:
npm install dotenv --save-dev
- Create a
.env
file in the root of your project and add variables you require in your tests, for example:
TEST_USER_EMAIL=workflowuser@stud.noroff.no
TEST_USER_PASSWORD=workflowpass
- Make sure
.env
is in your.gitignore
:
node_modules
.env
- In
playwright.config.js
configuredotenv
:
require("dotenv").config();
- Now we can access the variables in our tests like this:
process.env.TEST_USER_EMAIL;
Login test
Now that we’ve configured everything, let’s write our login test.
Our test will check two things:
Happy Path: Everything works
- User types correct email and password
- User gets logged in successfully
Error Path: Wrong password
- User types correct email but wrong password
- User sees error message
Here’s the code:
import { test, expect } from "@playwright/test";
test.describe("login", () => {
test("user can login", async ({ page }) => {
// Go to login page
await page.goto("/auth/login");
// Fill in form using name attributes
await page.locator('input[name="email"]').fill(process.env.TEST_USER_EMAIL);
await page
.locator('input[name="password"]')
.fill(process.env.TEST_USER_PASSWORD);
// Click login
await page.getByRole("button", { name: "Login" }).click();
// Check if we see logout button - means we're logged in
await expect(page.getByRole("button", { name: "Logout" })).toBeVisible();
});
test("wrong password shows error", async ({ page }) => {
await page.goto("/auth/login");
await page.locator('input[name="email"]').fill(process.env.TEST_USER_EMAIL);
await page.locator('input[name="password"]').fill("wrongpassword");
await page.getByRole("button", { name: "Login" }).click();
// Check for error in message container
await expect(page.locator("#message-container")).toContainText(
"Invalid email or password"
);
});
});
Understanding the Login Test
In each test, we:
- Go to the login page
- Fill in the form fields
- Click the login button
- Check if we got the result we expected
Similar to Vitest, we group tests within a describe
block.
Ways we can find things on the page:
locator('input[name="email"]')
finds input boxes by their namegetByRole("button")
finds buttons by their textlocator("#message-container")
finds things by their ID
How we check results:
- For success: Look for the Logout button
- For failure: Look for the error message
That’s it - we’re just copying what a real user would do: visit the page, fill in the form, click the button, check what happened.
Understanding test.describe and page
In our test above, we used some special Playwright features:
test.describe
is how we group related tests in Playwright:javascript test.describe("login", () => { // login-related tests go here });
The
{ page }
argument:javascript test("user can login", async ({ page }) => { // test code using page });
Each test gets a page object that lets us control the browser - we use it to:
- Go to pages:
page.goto()
- Find things:
page.locator()
- Click things:
page.click()
- Check things:
expect(page.locator())
Running Your Tests
In a terminal, run the tests:
npx playwright test
To see the tests running in the browser:
npx playwright test --headed
To see a report of what happened:
npx playwright show-report
This shows you which tests passed, failed, and why.
What We Learned
In this lesson, we:
- Identified which features are most important to test (login/register, key actions)
- Set up Playwright with a baseURL for easier testing
- Learned how to use environment variables to keep test credentials secure
- Created real-world login tests that check both successful and failed login attempts
- Discovered different ways to find elements on a page:
- By input name:
locator('input[name="email"]')
- By button text:
getByRole("button", { name: "Login" })
- By ID:
locator("#message-container")
- By input name:
Remember:
- Always test both successful and error cases
- Keep sensitive information in .env files
- Write tests that follow what real users would do
- Check for visible changes on the page to confirm test results
- Use meaningful test descriptions that explain what you’re testing