UI Component Testing Guide
How to Test Toast Notifications with Playwright: Complete 2026 Guide
A scenario-by-scenario walkthrough of testing toast notifications with Playwright. Auto-dismiss timing, stacking and queue behavior, ARIA live regions, action buttons, swipe to dismiss, pause on hover, and the pitfalls that break real notification test suites.
“According to the WebAIM Million report, 94% of the top one million homepages have detectable WCAG failures, with dynamic content like toast notifications being one of the most common sources of accessibility violations.”
WebAIM 2024
Toast Notification Lifecycle
1. Why Testing Toast Notifications Is Harder Than It Looks
Toast notifications appear simple on the surface: show a message, wait a few seconds, remove it. In practice, they sit at the intersection of timing, accessibility, animation, and state management, making them one of the most deceptively tricky UI components to test reliably. Every major toast library (react-toastify, sonner, react-hot-toast, Chakra UI toast, Radix Toast) handles these concerns differently, which means your test strategy must account for implementation-specific behavior rather than relying on a single pattern.
The first structural challenge is timing. Toasts auto-dismiss after a configurable delay, typically three to five seconds. Your test needs to assert that the toast appeared, verify its content, and then confirm it disappeared, all within that window. If your assertion runs too early, the toast might not have rendered yet. If it runs too late, the toast has already been removed from the DOM. Playwright's auto-waiting helps, but only if you know exactly which element to wait for and when to expect it to vanish.
The second challenge is stacking. When multiple toasts fire in rapid succession, they must queue, stack vertically, and respect a maximum visible count. Testing the queue requires triggering several toasts in sequence and verifying both the visible count and the order. Some libraries cap visible toasts at three and queue the rest; others push older toasts upward and let them overflow. The third challenge is accessibility. Screen readers rely on ARIA live regions (role="status" or aria-live="polite") to announce toasts without interrupting the user. If the live region is missing or misconfigured, sighted users see the toast but screen reader users hear nothing.
The fourth challenge is interactivity. Modern toasts often include action buttons ("Undo", "Retry", "View") and close buttons. Clicking these before the auto-dismiss timer fires requires precise coordination. The fifth challenge is gesture support: on mobile viewports, many libraries support swipe to dismiss, which requires simulating touch events. The sixth challenge is pause on hover, where hovering over a toast pauses its auto-dismiss timer, changing the expected timing of every subsequent assertion.
Toast Notification Challenge Map
Trigger Toast
User action or app event
Render + Animate
Entry animation plays
ARIA Announce
Live region update
Auto-Dismiss Timer
3-5 second countdown
Hover Pauses
Timer freezes on hover
Dismiss
Auto, click, or swipe
2. Setting Up Your Test Environment
Before writing any toast test, you need a predictable environment. Toast libraries depend on timers, animations, and DOM mutation, all of which can produce flaky results if your test environment is not configured correctly. The following setup applies to any toast library; later scenarios include library-specific selectors.
Playwright Configuration
Disable CSS animations and transitions in your test configuration. This eliminates the timing gap between when a toast enters the DOM and when it becomes visually stable. Most toast libraries use CSS transitions for enter/exit animations. Disabling them makes the toast appear and disappear instantly in tests, letting you focus on logic rather than animation frames.
Injecting a CSS Override for Animations
If the --force-prefers-reduced-motion flag does not fully suppress your toast library's animations, inject a global CSS override in your test setup. This is the nuclear option that guarantees instant visibility transitions.
Library-Specific Selectors Reference
Each toast library renders its container and individual toasts using different selectors. Use this reference table throughout the scenarios that follow. Replace the generic selectors with the ones matching your library.
| Library | Container Selector | Individual Toast |
|---|---|---|
| react-toastify | .Toastify__toast-container | .Toastify__toast |
| sonner | [data-sonner-toaster] | [data-sonner-toast] |
| react-hot-toast | [data-hot-toast] | [role="status"] |
| Chakra UI | .chakra-toast__inner | [role="alert"] |
| Radix Toast | [data-radix-toast-viewport] | [data-radix-toast-root] |
Test Environment Setup Flow
Install Playwright
npm install -D
Configure Viewports
Desktop + mobile
Disable Animations
CSS override or flag
Map Selectors
Library-specific
Run Tests
npx playwright test
Basic Toast Appears and Auto-Dismisses
Straightforward3. Scenario: Basic Toast Appears and Auto-Dismisses
Goal: Verify that triggering a success action shows a toast with the correct message and that the toast disappears after the configured timeout. This is the foundational test that every toast suite starts with.
Preconditions:The application is loaded and the user is on a page where a toast can be triggered (for example, a form submission page or a settings page with a "Save" button). Animations are disabled per the setup in section 2.
Playwright Implementation
What to Assert Beyond the UI
Basic Toast Assertions
- Toast text matches expected message exactly
- Toast type attribute (success, error, info) is correct
- Toast disappears within the expected timeout window
- Toast is fully removed from the DOM after dismissal
- No duplicate toasts appear for a single trigger
Basic Toast: Playwright vs Assrt
import { test, expect } from '@playwright/test';
test('success toast appears and auto-dismisses', async ({ page }) => {
await page.goto('/settings');
await page.getByRole('button', { name: 'Save changes' }).click();
const toast = page.locator('[data-sonner-toast]').filter({
hasText: 'Settings saved successfully',
});
await expect(toast).toBeVisible();
await expect(toast).toHaveAttribute('data-type', 'success');
await expect(toast).toBeHidden({ timeout: 6_000 });
await expect(
page.locator('[data-sonner-toast]').filter({
hasText: 'Settings saved successfully',
})
).toHaveCount(0);
});Toast Stacking and Queue Limits
Moderate4. Scenario: Toast Stacking and Queue Limits
Goal: Verify that when multiple toasts fire in rapid succession, they stack correctly and respect the maximum visible count. Most toast libraries default to showing three visible toasts and queuing the rest. When a visible toast dismisses, the next queued toast should slide in.
Preconditions: The toast library is configured with a visible limit (for example, visibleToasts: 3 in sonner). The application has a way to trigger multiple toasts rapidly, such as a bulk action that fires individual success messages per item.
Playwright Implementation
What to Assert Beyond the UI
Beyond checking visible count, verify that queued toasts do not lose their content or type when they transition from the queue to the visible stack. Also confirm that rapidly dismissing all visible toasts drains the queue correctly without dropping messages or rendering duplicates.
ARIA Live Region Announcements
Complex5. Scenario: ARIA Live Region Announcements
Goal: Verify that toast notifications are properly announced to screen readers via ARIA live regions. This is not just an accessibility checkbox; it is a functional requirement. If a user cannot perceive a toast, the notification has failed at its core purpose.
Preconditions: The toast library renders toasts inside or alongside an element with role="status" or aria-live="polite". For error toasts, the expected role is role="alert" or aria-live="assertive". These attributes must be present on the container before the toast content is injected; adding them dynamically after content insertion causes screen readers to miss the announcement.
Playwright Implementation
What to Assert Beyond the UI
The most common accessibility failure with toasts is a live region that gets created dynamically when the first toast fires. Screen readers like VoiceOver and NVDA only detect content changes inside elements that already had aria-live when the page loaded. If the library injects the container and content simultaneously, the announcement is silently swallowed. Your test for this is the third test above: verify the container exists before any toast is triggered.
ARIA Live Region: Playwright vs Assrt
test('error toast uses assertive live region', async ({ page }) => {
await page.goto('/settings');
await page.getByLabel('Email').fill('not-an-email');
await page.getByRole('button', { name: 'Save changes' }).click();
const assertiveRegion = page.locator(
'[aria-live="assertive"], [role="alert"]'
);
const errorToast = assertiveRegion.locator(
'text=Please enter a valid email'
);
await expect(errorToast).toBeVisible();
});Action Buttons Inside Toasts
ModerateSwipe to Dismiss on Mobile Viewports
Complex7. Scenario: Swipe to Dismiss on Mobile Viewports
Goal: Verify that on mobile viewports with touch enabled, swiping a toast horizontally dismisses it. This tests the gesture recognition logic that many toast libraries implement for touch devices. The swipe threshold, direction (left, right, or both), and animation must all work correctly.
Preconditions: The Playwright project uses a mobile viewport with hasTouch: true and isMobile: true. The toast library has swipe dismiss enabled (sonner enables it by default; react-toastify requires draggable: true).
Playwright Implementation
What to Assert Beyond the UI
Test both the threshold and the direction. If your library supports only right swipe, verify that a left swipe does not dismiss the toast. Also verify that vertical swipes (scrolling the page) do not accidentally trigger toast dismissal, which is a common regression when gesture handlers capture events too broadly.
Pause on Hover Extends Toast Lifetime
Moderate8. Scenario: Pause on Hover Extends Toast Lifetime
Goal: Verify that hovering over a toast pauses its auto-dismiss timer, and that moving the mouse away resumes the countdown. This is a critical usability feature: users hover over toasts to read them, and having the toast disappear under the cursor is a frustrating experience. Most toast libraries enable this by default.
Preconditions: The toast library is configured with pause on hover enabled (this is the default for sonner, react-toastify, and react-hot-toast). The auto-dismiss timeout is set to a known value, such as 3000 milliseconds.
Playwright Implementation
What to Assert Beyond the UI
A subtle edge case: hover should pause the timer based on the remaining time, not restart it from zero. If a toast has 1 second left when you hover, moving the mouse away should dismiss it after approximately 1 second, not after a fresh 3 second countdown. This is hard to test precisely, but you can approximate it by hovering late in the countdown and verifying that dismissal after unhover happens faster than the full timeout.
Pause on Hover: Playwright vs Assrt
test('hovering pauses auto-dismiss timer', async ({ page }) => {
await page.goto('/settings');
await page.getByRole('button', { name: 'Save changes' }).click();
const toast = page.locator('[data-sonner-toast]').filter({
hasText: 'Settings saved successfully',
});
await expect(toast).toBeVisible();
await toast.hover();
await page.waitForTimeout(4_000);
await expect(toast).toBeVisible();
await page.mouse.move(0, 0);
await expect(toast).toBeHidden({ timeout: 4_000 });
});9. Common Pitfalls That Break Toast Test Suites
These pitfalls come from real issues reported in GitHub repositories for major toast libraries. Each one has caused flaky or outright broken test suites in production projects.
Pitfall 1: Racing Against the Auto-Dismiss Timer
The most common source of flaky toast tests is asserting on a toast that has already been removed from the DOM. If your test triggers a toast and then performs other actions before checking the toast, the auto-dismiss timer may fire in between. The fix is to assert on the toast immediately after the trigger, before any other operations. Alternatively, increase the toast duration in your test environment to give yourself a wider assertion window.
Pitfall 2: Animation State During Assertions
Toast libraries use CSS transitions for enter and exit animations. During the exit animation, the toast is still in the DOM but may have opacity: 0 or transform: translateX(100%). Playwright's toBeVisible() checks computed visibility, so a toast mid-exit-animation may fail the visibility check even though it has not been removed from the DOM. Use toBeAttached() if you need to check DOM presence regardless of visual state, or disable animations entirely as recommended in section 2.
Pitfall 3: Duplicate Toast Deduplication
Some libraries (sonner, react-hot-toast) deduplicate toasts with identical content by default. If your test triggers the same action twice expecting two toasts, only one may appear. This is a feature, not a bug, but it breaks tests that rely on counting toast instances. Use unique toast IDs or unique messages in test scenarios, or configure the library to disable deduplication in the test environment.
Pitfall 4: Portal Rendering and Selector Scope
Toast containers are typically rendered via a React portal at the root of the document body, outside your application's component tree. If your test scopes locators to a specific container (like page.locator('#app').locator(...)), the toast will not be found. Always query toasts from the root page object, not from a scoped container.
Pitfall 5: Test Isolation and Leftover Toasts
If a previous test triggers a toast that has not fully dismissed before the next test starts, the leftover toast can interfere with assertions in the subsequent test. This is especially common when tests share a browser context without full page reloads between them. Add a cleanup step in your test teardown that waits for all toasts to dismiss or forcefully clears the toast container:
Toast Testing Anti-Patterns
- Using fixed sleep instead of waiting for toast selector
- Asserting on toast after performing unrelated page actions
- Scoping toast locators inside a container instead of page root
- Expecting duplicate toasts when deduplication is enabled
- Not cleaning up toasts between tests in shared contexts
- Testing animations without disabling them first
- Checking DOM presence when you mean visibility (or vice versa)
10. Writing These Scenarios in Plain English with Assrt
Every scenario in this guide requires careful Playwright orchestration: waiting for the right selector, coordinating with auto-dismiss timers, checking ARIA attributes, simulating swipe gestures. Assrt lets you express the same test intent in plain English, and it compiles each scenario into the Playwright TypeScript you would have written by hand.
Here is the full toast notification test suite from this guide, written as an Assrt .assrt file. Each scenario block maps directly to one of the sections above.
Assrt compiles each scenario block into the same Playwright TypeScript you saw in the preceding sections, committed to your repo as real tests you can read, run, and modify. When your toast library updates its DOM structure (sonner changed its data attributes between v1 and v2, for example), Assrt detects the failure, analyzes the new DOM, and opens a pull request with the updated selectors. Your scenario files stay untouched.
Start with the basic auto-dismiss test. Once it passes in your CI, add the stacking scenario, then ARIA live regions, then action buttons, then swipe dismiss, then pause on hover. In a single afternoon you can have complete toast notification coverage that most applications never achieve by hand, covering timing, accessibility, gesture support, and state management in one coherent test suite.
Related Guides
How to Test Geolocation Prompt
A practical guide to testing browser geolocation permission prompts with Playwright....
How to Test PWA Install Prompt
A practical guide to testing PWA install prompts with Playwright. Covers...
How to Test Service Worker Offline
A practical guide to testing service worker offline behavior with Playwright. Covers...
Ready to automate your testing?
Assrt discovers test scenarios, writes Playwright tests from plain English, and self-heals when your UI changes.