---
type: Task
status: AI Gen TODO
category: QA
isAgentGenerated: "true"
---

# Test: Toast Notification Timing and Dismissal

## Description

Verify that toast notifications appear with correct error/success styling, auto-dismiss after the expected duration (~1.3s), and multiple toasts stack properly when triggered in rapid succession.

## Rationale

The Build Calculator shows toast notifications for errors ("Missing Requirements", "Not Enough Ether") and potentially for success/addition confirmations. The existing toast test (`tests/buildCalculator.spec.js`) only checks whether a toast **appears** within 3 seconds; it does not validate:
- The toast's CSS class (`.error` vs `.success`).
- The exact display duration.
- Stacking behavior when multiple errors are triggered rapidly.
- Whether a toast persists if the condition that triggered it is resolved.

## Playwright Feature

This test uses **`page.clock`** (Playwright's clock API) to control time without waiting real seconds, plus **`page.waitForSelector`** with exact timeout values and **`page.evaluate`** to inspect DOM classes.

### Clock-based Timing Test

```js
// Install fake timers before navigation
await page.clock.install();
await page.clock.fastForward(5000);  // Simulate WASM loading delay
await calc.goto();

// Trigger a toast by clicking E (Soul Foundry without Legion Hall)
await calc.hotkeys.clickKey('E');
await page.clock.fastForward(500);   // small delay for render

// Assert toast appears with error styling
const toast = page.locator('.toastsContainer .toastContainer');
await expect(toast).toHaveClass(/error/);

// Fast-forward to just before auto-dismiss
await page.clock.fastForward(1200);
await expect(toast).toBeVisible();   // still visible at ~1.2s

// Fast-forward past dismissal
await page.clock.fastForward(200);
await expect(toast).not.toBeVisible(); // gone by ~1.4s
```

### Stacking Test

```js
// Trigger two toasts in rapid succession
calc.filter.selectFaction("Q'Rath");
await calc.hotkeys.clickKey('E');     // Missing Requirements

// Without waiting, trigger another (e.g., click an entity with zero ether)
await calc.hotkeys.clickKey('Q');
await calc.hotkeys.clickKey('E');     // Not Enough Ether

await page.waitForTimeout(300);
// Assert TWO toasts are visible, stacked
const toasts = page.locator('.toastsContainer .toastContainer');
await expect(toasts).toHaveCount(2);
// Assert the first toast has error styling and correct title
await expect(toasts.nth(0).locator('.toastTitle')).toHaveText('Missing Requirements');
await expect(toasts.nth(1).locator('.toastTitle')).toHaveText('Not Enough Ether');
```

### What This Test Catches

- Auto-dismiss timer drift (toast stays too long or disappears too early).
- Success vs. error CSS class mismatch (all toasts get the same styling).
- Stacking order (new toasts should appear above or below older ones).
- Memory leaks (stale toast DOM nodes that never get removed).
- Race conditions between `ToastService.AddToast` and `StateHasChanged`.
