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.

94%

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

0Toast scenarios covered
0sTypical auto-dismiss delay
0+Libraries supported
0%Fewer lines with Assrt

Toast Notification Lifecycle

User ActionApp LogicToast ContainerARIA Live RegionTimerTrigger event (click, submit, error)Create toast elementAnnounce to screen readersStart auto-dismiss countdownHover: pause timerClick action or dismissTimeout: remove toastUpdate live region (removed)

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.

playwright.config.ts

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.

tests/helpers/disable-animations.ts
Installing Dependencies

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.

LibraryContainer SelectorIndividual 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

3

Basic Toast Appears and Auto-Dismisses

Straightforward

3. 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

tests/toast-basic.spec.ts

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);
});
50% fewer lines
4

Toast Stacking and Queue Limits

Moderate

4. 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

tests/toast-stacking.spec.ts

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.

Try Assrt for free

Open-source AI testing framework. No signup required.

Get Started
5

ARIA Live Region Announcements

Complex

5. 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

tests/toast-aria.spec.ts

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();
});
36% fewer lines
6

Action Buttons Inside Toasts

Moderate

6. Scenario: Action Buttons Inside Toasts

Goal:Verify that clicking an action button inside a toast (such as "Undo", "Retry", or "View") performs the expected operation and dismisses the toast. This tests the coordination between the toast's auto-dismiss timer and user interaction: clicking the action should cancel the timer, execute the callback, and remove the toast immediately.

Preconditions:The application triggers a toast that includes an action button. A common pattern is a delete action that shows "Item deleted" with an "Undo" button. The undo must reverse the delete and dismiss the toast.

Playwright Implementation

tests/toast-actions.spec.ts

What to Assert Beyond the UI

When testing action buttons, always verify the side effect, not just the toast dismissal. If the action is "Undo", confirm the original state is restored. If the action is "Retry", confirm the network request fires again (intercept with page.route()). If the action is "View", confirm navigation occurs. The toast disappearing is necessary but not sufficient.

7

Swipe to Dismiss on Mobile Viewports

Complex

7. 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

tests/toast-swipe.spec.ts

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.

8

Pause on Hover Extends Toast Lifetime

Moderate

8. 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

tests/toast-hover.spec.ts

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 });
});
23% fewer lines

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:

tests/helpers/toast-cleanup.ts

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)
Common Test Failure Output

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.

toast-notifications.assrt

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

Ready to automate your testing?

Assrt discovers test scenarios, writes Playwright tests from plain English, and self-heals when your UI changes.

$npm install @assrt/sdk