Now that we’ve covered the Playwright Test Runner and its setup in Day 2, it’s time to dive into writing clear and effective tests using Playwright’s flexible API. Whether you’re checking page elements, simulating user actions, or verifying data flows, mastering the core syntax is essential to creating reliable and maintainable tests.
Test Structure and Basic Syntax
In Playwright, every test starts with the test() function, which is part of the @playwright/test package. Think of test() as the container where you define all the steps and validations for a single test case.
Here’s a simple example:
ts
import { test, expect } from '@playwright/test';
test('basic test example', async ({ page }) => {
await page.goto('https://example.com'); // Navigate to the example website
const title = await page.title(); // Get the page title
expect(title).toBe('Example Domain'); // Verify that the title matches our expectation
});
What’s Happening Here?
We import test and expect from Playwright’s testing library.
Inside our test, we navigate to https://example.com.
Then, we retrieve the page’s title and store it in a variable.
Finally, we assert that the title equals "Example Domain".
This simple validation helps ensure that the page loaded correctly, which is especially useful after navigating or redirecting.
Key Components
test(): Defines a test case — a set of instructions to run and verify.
expect(): Provides assertion methods to check if your app behaves as expected.
page: Represents the browser tab or context you interact with in your test. Use page to visit URLs, click elements, type text, and more.
Commonly Used Playwright Functions
- Navigating and Waiting for Page Load When opening a webpage, it’s important to wait until it fully loads before interacting with it. Here’s how you can do that:
ts
await page.goto('https://example.com');
await page.waitForLoadState('networkidle'); // Wait until there are no ongoing network requests
The first line navigates to the URL, and the second ensures Playwright waits until all network activity stops. This helps prevent flaky tests that try to interact too soon.
2. Interacting with Elements
Once the page is ready, you can simulate user actions like clicking buttons or filling forms:
ts
await page.click('text=Login'); // Click the Login button or link
await page.fill('#username', 'testuser'); // Type the username
await page.fill('#password', 'securepass123'); // Type the password
await page.press('#password', 'Enter'); // Press Enter to submit the form
These steps mimic how a real user logs into a website.
3. Assertions with expect()
After performing actions, you’ll want to confirm that the app responded correctly:
ts
await expect(page).toHaveURL(/dashboard/); // Check the URL includes /dashboard
await expect(page.locator('h1')).toContainText('Welcome'); // Verify the heading contains "Welcome"
await expect(page.locator('#error')).toBeHidden(); // Ensure any error messages are hidden
await expect(page.locator('.status')).toHaveClass(/active/); // Confirm a status element has the "active" class
These assertions help catch issues early by verifying the expected page state.
Working with Locators
Playwright’s locator() API lets you target elements flexibly:
ts
const loginButton = page.locator('button:has-text("Login")');
await loginButton.click();
Here, we locate a button containing the text "Login" and click it. Using locators makes your tests more readable and maintainable.
Locator Tips
Use CSS selectors for speed.
Use text-based or role-based selectors (page.getByRole(), page.getByText()) for clarity.
Combine selectors to precisely target elements.
Handling Multiple Elements
If you need to interact with a list of items, locators make it easy:
ts
const items = page.locator('.list-item');
await expect(items).toHaveCount(5); // Check there are 5 items
await items.nth(0).click(); // Click the first item
Form Handling Example
Here’s a full example of testing a user registration form:
ts
test('User can register', async ({ page }) => {
await page.goto('/register');
await page.fill('#email', '[email protected]');
await page.fill('#password', 'Test@1234');
await page.check('#agree');
await page.click('button[type="submit"]');
await expect(page.locator('.success')).toHaveText('Registration complete');
});
Uploading Files
Uploading files is straightforward with Playwright:
ts
await page.setInputFiles('input[type="file"]', 'tests/files/sample.pdf');
Keyboard and Mouse Actions
You can simulate typing and clicking anywhere on the page:
ts
await page.keyboard.type('Hello World');
await page.mouse.click(100, 200);
Handling Alerts and Dialogs
To manage pop-ups or confirmation dialogs, use event handlers:
ts
page.on('dialog', async dialog => {
expect(dialog.message()).toContain('Are you sure');
await dialog.accept();
});
await page.click('#delete-btn');
Screenshots and Video Recording
Playwright can capture screenshots or videos to help with debugging:
ts
await page.screenshot({ path: 'screenshots/home.png' });
You can also configure your tests to automatically save videos and screenshots on failure.
Timeouts and Retry Logic
You can control how long Playwright waits for elements or actions:
ts
await page.waitForSelector('.loading', { timeout: 5000 }); // Wait up to 5 seconds
Using Tags and Annotations
Control which tests run with these handy commands:
ts
test.skip('feature not ready', async ({ page }) => { /* ... / });
test.only('run this test only', async ({ page }) => { / ... */ });
test.describe('Checkout Flow', () => {
test('User can place order', async ({ page }) => { /* ... */ });
});
Summary
By mastering these Playwright functions and best practices, you can:
Write expressive, maintainable test cases
Interact seamlessly with modern, dynamic UIs
Accurately validate web application behavior
Improve debugging with powerful locators, traces, and reports
Keep practicing these fundamentals, and you’ll be writing robust Playwright tests in no time!
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.