Playwright Advanced Assertions: Your Testing Superpower 🦸‍♀️
The Story of the Careful Detective
Imagine you’re a detective checking if everything is just right in a house. Normal detectives stop at the first problem. But YOU are a super detective—you check EVERYTHING, even if you find one thing wrong!
That’s what Advanced Assertions in Playwright are all about. They give you superpowers to check things in smarter, more flexible ways.
🧸 What We’ll Learn Today
Think of assertions like a mom checking if you did your homework:
| Power | What It Does |
|---|---|
| Soft Assertions | Check many things, report all problems at once |
Negating with .not |
Check that something is NOT there |
| Custom Messages | Add helpful notes when things go wrong |
| Timeout | Wait patiently for slow things |
| expect.poll | Keep checking until something happens |
| toPass | Keep trying until it works |
1. Soft Assertions: The Patient Teacher 📝
The Story
Imagine a teacher grading your test. A strict teacher stops grading after the first wrong answer. But a patient teacher checks ALL answers and tells you EVERYTHING you got wrong.
Soft assertions are like the patient teacher!
How It Works
// Import soft assertions
import { expect } from '@playwright/test';
// Normal (strict) - STOPS at first failure
await expect(page.locator('.title')).toBeVisible();
await expect(page.locator('.price')).toHaveText('$10');
// Soft - Checks EVERYTHING, reports all failures
await expect.soft(page.locator('.title')).toBeVisible();
await expect.soft(page.locator('.price')).toHaveText('$10');
await expect.soft(page.locator('.cart')).toHaveCount(3);
Why Use It?
- See ALL problems in one test run
- Save time debugging
- Great for checking forms with many fields
graph TD A["Start Test"] --> B["Check Title"] B --> C["Check Price"] C --> D["Check Cart"] D --> E["Show ALL Errors"] style E fill:#ff6b6b,color:#fff
2. Negating Assertions with .not: The Opposite Day 🔄
The Story
Remember playing “Opposite Day” as a kid? Everything you say means the opposite!
.not is like Opposite Day for checks. Instead of checking IF something exists, you check that it does NOT exist.
How It Works
// Check that error message is NOT visible
await expect(page.locator('.error'))
.not.toBeVisible();
// Check that button is NOT disabled
await expect(page.locator('button'))
.not.toBeDisabled();
// Check that list is NOT empty
await expect(page.locator('.items'))
.not.toHaveCount(0);
Common Uses
| What You Want | Code |
|---|---|
| No errors showing | .not.toBeVisible() |
| Button is clickable | .not.toBeDisabled() |
| Text changed | .not.toHaveText('old') |
3. Custom Assertion Messages: Leave Helpful Notes 📌
The Story
Imagine you’re leaving notes for your future self. Without a note: “Something broke.” With a good note: “The login button disappeared after clicking submit!”
Custom messages are your helpful notes when tests fail.
How It Works
// Without custom message (confusing)
await expect(page.locator('.btn')).toBeVisible();
// Error: Expected to be visible
// With custom message (helpful!)
await expect(
page.locator('.btn'),
'Login button should appear after form loads'
).toBeVisible();
// Error: Login button should appear after form loads
Pro Tips
// Add context about what you were testing
await expect(
page.locator('.price'),
`Price should be $${expectedPrice} after discount`
).toHaveText(`$${expectedPrice}`);
// Explain the expected state
await expect(
page.locator('.cart-count'),
'Cart should be empty after checkout completes'
).toHaveText('0');
4. Assertion Timeout: The Patient Waiter ⏳
The Story
Imagine waiting for your pizza to arrive. Sometimes it takes 5 minutes, sometimes 20 minutes. You don’t give up after 5 minutes if the pizza shop is busy!
Timeout tells Playwright how long to wait before giving up.
How It Works
// Default timeout (5 seconds)
await expect(page.locator('.result')).toBeVisible();
// Custom timeout - wait up to 10 seconds
await expect(page.locator('.result'))
.toBeVisible({ timeout: 10000 });
// Wait up to 30 seconds for slow operations
await expect(page.locator('.download-complete'))
.toBeVisible({ timeout: 30000 });
When to Use Longer Timeouts
graph TD A["What are you waiting for?"] --> B{Type of action} B --> C["Fast: Button click"] B --> D["Medium: API call"] B --> E["Slow: File upload"] C --> F["5 seconds"] D --> G["10-15 seconds"] E --> H["30+ seconds"]
5. expect.poll: The Persistent Checker 🔍
The Story
Imagine checking if cookies are done baking. You don’t just check once—you keep peeking every minute until they’re golden brown!
expect.poll keeps running a function and checking the result until it passes.
How It Works
// Poll a function until it returns expected value
await expect.poll(async () => {
const response = await page.request.get('/api/status');
return response.json();
}).toEqual({ status: 'complete' });
// With custom intervals and timeout
await expect.poll(async () => {
return await page.locator('.count').textContent();
}, {
intervals: [1000, 2000, 5000], // Check at 1s, 2s, 5s
timeout: 30000 // Give up after 30s
}).toBe('100');
Real Example: Wait for API
// Keep checking until order status is "shipped"
await expect.poll(async () => {
const status = await getOrderStatus(orderId);
return status;
}, {
message: 'Waiting for order to ship',
timeout: 60000
}).toBe('shipped');
6. toPass: The Never-Give-Up Helper đź’Ş
The Story
Learning to ride a bike: You fall, get up, try again. Fall, get up, try again. Eventually, you succeed!
toPass keeps retrying your entire check until it works (or times out).
How It Works
// Retry the entire block until it passes
await expect(async () => {
const response = await page.request.get('/api/data');
expect(response.status()).toBe(200);
expect(await response.json()).toHaveProperty('ready', true);
}).toPass();
// With custom settings
await expect(async () => {
const items = await page.locator('.item').count();
expect(items).toBeGreaterThan(5);
}).toPass({
intervals: [1000, 2000, 5000],
timeout: 30000
});
toPass vs expect.poll
| Feature | expect.poll |
toPass |
|---|---|---|
| What it checks | One value | Multiple assertions |
| Use when | Waiting for one thing | Complex conditions |
| Example | Wait for status | Check status AND data |
graph TD A["Need to retry?"] --> B{How many checks?} B --> C["Just one value"] B --> D["Multiple things"] C --> E["Use expect.poll"] D --> F["Use toPass"] style E fill:#4ecdc4,color:#fff style F fill:#667eea,color:#fff
🎯 Quick Reference
// 1. Soft assertions - check everything
expect.soft(locator).toBeVisible();
// 2. Negating - check NOT there
expect(locator).not.toBeVisible();
// 3. Custom message - helpful notes
expect(locator, 'My helpful message').toBeVisible();
// 4. Timeout - wait longer
expect(locator).toBeVisible({ timeout: 10000 });
// 5. Poll - keep checking one value
await expect.poll(() => getValue()).toBe('done');
// 6. toPass - retry complex checks
await expect(async () => {
// multiple checks here
}).toPass();
🌟 You Did It!
Now you have six superpowers for testing:
- Soft assertions - See all problems at once
.not- Check that things are NOT there- Custom messages - Leave helpful notes
- Timeout - Wait patiently
- expect.poll - Keep checking one thing
- toPass - Keep trying until it all works
You’re not just a tester anymore—you’re a testing superhero! 🦸‍♂️
