Writing Your First Playwright Test#
In this guide, we’ll walk through creating a basic Playwright test from scratch. You’ll learn how to:
Structure a test file
Navigate to pages
Interact with elements
Make assertions
Handle test fixtures
Basic Test Structure#
A Playwright test follows this basic pattern:
import { test, expect } from "@playwright/test";
test("test name", async ({ page }) => {
// Test code goes here
});
Example: Testing a Login Form#
Let’s create a complete test that logs into a website:
import { test, expect } from "@playwright/test";
test("successful login", async ({ page }) => {
// Navigate to the login page
await page.goto("https://demo.playwright.dev/login");
// Fill in the login form
await page.getByLabel("Username").fill("user");
await page.getByLabel("Password").fill("password123");
// Click the login button
await page.getByRole("button", { name: "Login" }).click();
// Assert successful login - check for welcome message
await expect(page.getByText("Welcome, user!")).toBeVisible();
// Assert URL has changed to dashboard
await expect(page).toHaveURL(/.*dashboard/);
});
Testing Multiple Scenarios#
You can add multiple test cases in a single file:
test("failed login with incorrect password", async ({ page }) => {
await page.goto("https://demo.playwright.dev/login");
await page.getByLabel("Username").fill("user");
await page.getByLabel("Password").fill("wrong-password");
await page.getByRole("button", { name: "Login" }).click();
// Assert error message is shown
await expect(page.getByText("Invalid credentials")).toBeVisible();
// Assert we're still on the login page
await expect(page).toHaveURL(/.*login/);
});
Test Hooks#
You can use test hooks to set up and clean up test resources:
test.beforeEach(async ({ page }) => {
// This runs before each test in this file
await page.goto("https://demo.playwright.dev/login");
});
test.afterEach(async ({ page }) => {
// This runs after each test in this file
await page.close();
});
Grouping Tests#
Organize related tests using test descriptions:
test.describe("Authentication", () => {
test("login with valid credentials", async ({ page }) => {
// Test code...
});
test("login with invalid credentials", async ({ page }) => {
// Test code...
});
});
Running a Specific Test#
You can run a single test by adding .only:
test.only("focus on this test", async ({ page }) => {
// Only this test will run
});
Or skip tests with .skip:
test.skip("skip this test", async ({ page }) => {
// This test will be skipped
});
Next Steps#
Once you’re comfortable with basic tests, move on to:
Using page objects to organize test code
Creating custom fixtures for test setup
Managing test data
Running tests in parallel
Basic Test Example#
Simple Test Structure#
import { test, expect } from "@playwright/test";
test("test name", async ({ page }) => {
// Test code goes here
});
Form Interaction Example#
test("login test", async ({ page }) => {
await page.goto("/login");
// Fill form fields
await page.fill('input[name="username"]', "testuser");
await page.fill('input[name="password"]', "password123");
// Submit form
await page.click('button[type="submit"]');
// Assert successful login
await expect(page.locator(".welcome-message")).toBeVisible();
});
Element Assertions#
test("element assertions", async ({ page }) => {
await page.goto("/products");
// Check if element is visible
await expect(page.locator(".product-item")).toBeVisible();
// Check element count
await expect(page.locator(".product-item")).toHaveCount(5);
// Check text content
await expect(page.locator("h1")).toHaveText("Products");
});
Tip
Use locators over selectors when possible for better readability and maintainability.