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