Complete Claude Code plugin with: - 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report) - 3 specialized agents (test-architect, test-debugger, migration-planner) - 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility) - TestRail MCP server (TypeScript) — 8 tools for bidirectional sync - BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing - Smart hooks (auto-validate tests, auto-detect Playwright projects) - 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests) - Leverages Claude Code built-ins (/batch, /debug, Explore subagent) - Zero-config for core features; TestRail/BrowserStack via env vars - Both TypeScript and JavaScript support throughout Co-authored-by: Leo <leo@openclaw.ai>
140 lines
5.2 KiB
Markdown
140 lines
5.2 KiB
Markdown
# Toast Messages Template
|
||
|
||
Tests success, error, and warning toasts with auto-dismiss and manual close.
|
||
|
||
## Prerequisites
|
||
- Authenticated session via `{{authStorageStatePath}}`
|
||
- App running at `{{baseUrl}}`
|
||
|
||
---
|
||
|
||
## TypeScript
|
||
|
||
```typescript
|
||
import { test, expect } from '@playwright/test';
|
||
|
||
test.describe('Toast Messages', () => {
|
||
test.use({ storageState: '{{authStorageStatePath}}' });
|
||
|
||
// Happy path: success toast on action
|
||
test('shows success toast after save action', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.getByRole('textbox', { name: /{{fieldLabel}}/i }).fill('{{validValue}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /saved|success/i });
|
||
await expect(toast).toBeVisible();
|
||
});
|
||
|
||
// Happy path: error toast on failure
|
||
test('shows error toast when action fails', async ({ page }) => {
|
||
await page.route('{{baseUrl}}/api/{{endpoint}}*', route =>
|
||
route.fulfill({ status: 500, body: '{}' })
|
||
);
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /error|failed/i });
|
||
await expect(toast).toBeVisible();
|
||
});
|
||
|
||
// Happy path: warning toast shown
|
||
test('shows warning toast', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{warningTriggerPath}}');
|
||
await page.getByRole('button', { name: /{{warningAction}}/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /warning|attention/i });
|
||
await expect(toast).toBeVisible();
|
||
});
|
||
|
||
// Happy path: toast auto-dismisses
|
||
test('toast auto-dismisses after timeout', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.clock.install();
|
||
await page.getByRole('textbox', { name: /{{fieldLabel}}/i }).fill('{{validValue}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /saved/i });
|
||
await expect(toast).toBeVisible();
|
||
await page.clock.fastForward({{toastDurationMs}});
|
||
await expect(toast).toBeHidden();
|
||
});
|
||
|
||
// Happy path: toast manually dismissed
|
||
test('dismisses toast via close button', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /saved/i });
|
||
await expect(toast).toBeVisible();
|
||
await toast.getByRole('button', { name: /close|dismiss|×/i }).click();
|
||
await expect(toast).toBeHidden();
|
||
});
|
||
|
||
// Happy path: multiple toasts stack
|
||
test('stacks multiple toasts', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
// Trigger two saves quickly
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toasts = page.getByRole('alert');
|
||
const count = await toasts.count();
|
||
expect(count).toBeGreaterThanOrEqual(2);
|
||
});
|
||
|
||
// Edge case: toast announces to screen readers
|
||
test('toast has live region role for accessibility', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').first();
|
||
await expect(toast).toBeVisible();
|
||
// role="alert" implies aria-live="assertive"
|
||
const role = await toast.getAttribute('role');
|
||
expect(role).toMatch(/alert|status/);
|
||
});
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## JavaScript
|
||
|
||
```javascript
|
||
const { test, expect } = require('@playwright/test');
|
||
|
||
test.describe('Toast Messages', () => {
|
||
test.use({ storageState: '{{authStorageStatePath}}' });
|
||
|
||
test('shows success toast after save', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.getByRole('textbox', { name: /{{fieldLabel}}/i }).fill('{{validValue}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
await expect(page.getByRole('alert').filter({ hasText: /saved|success/i })).toBeVisible();
|
||
});
|
||
|
||
test('toast auto-dismisses after timeout', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.clock.install();
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /saved/i });
|
||
await expect(toast).toBeVisible();
|
||
await page.clock.fastForward({{toastDurationMs}});
|
||
await expect(toast).toBeHidden();
|
||
});
|
||
|
||
test('dismisses toast via close button', async ({ page }) => {
|
||
await page.goto('{{baseUrl}}/{{formPath}}');
|
||
await page.getByRole('button', { name: /save/i }).click();
|
||
const toast = page.getByRole('alert').filter({ hasText: /saved/i });
|
||
await toast.getByRole('button', { name: /close|×/i }).click();
|
||
await expect(toast).toBeHidden();
|
||
});
|
||
});
|
||
```
|
||
|
||
## Variants
|
||
| Variant | Description |
|
||
|---------|-------------|
|
||
| Success toast | Save → green/success alert visible |
|
||
| Error toast | 500 → red/error alert visible |
|
||
| Warning toast | Trigger action → warning alert |
|
||
| Auto-dismiss | Toast hidden after N ms (clock-controlled) |
|
||
| Manual dismiss | Close button hides toast |
|
||
| Stacked toasts | Multiple alerts visible simultaneously |
|
||
| Accessible | role=alert or role=status present |
|