بنقرة واحدة
e2e-test
Run E2E browser tests via Tidewave browser_eval against the running Phoenix dev server
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Run E2E browser tests via Tidewave browser_eval against the running Phoenix dev server
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Create or manage Architecture Decision Records (ADRs) in docs/decisions/
Use this skill working with Ash Framework or any of its extensions. Always consult this when making any domain changes, features or fixes.
Record a development session summary in the project devlog
Inspect LiveView state (assigns, components) via Phoenix.LiveView.Debug in project_eval
Morning briefing — where we left off, what to work on today. Fast, compact report from canonical sources.
Manage plan lifecycle — list plans, mark completed/abandoned, move between directories
| name | e2e-test |
| description | Run E2E browser tests via Tidewave browser_eval against the running Phoenix dev server |
| argument-hint | [path] e.g. 'pos/terminal-basic', 'pos/terminal-basic#3', 'all', 'list' |
Run markdown-based E2E browser tests using Tidewave browser_eval against the running Phoenix dev server.
All E2E tests live in docs/e2e/. Each .md file contains numbered scenarios.
/e2e [path]Run all scenarios in a test file. Path is relative to docs/e2e/.
/e2e pos/terminal-basic # Run all 10 scenarios
/e2e staff/members-list # Run all scenarios
/e2e public/checkout-link # Run all scenarios
/e2e [path]#[number]Run only one scenario from a test file.
/e2e pos/terminal-basic#3 # Run only Scenario 3: Product Search
/e2e pos/terminal-basic#8-10 # Run Scenarios 8 through 10
/e2e listList all test files with scenario counts and last-verified dates.
/e2e statusShow overall test suite status — which tests have been verified, which are pending.
When running tests, follow these steps precisely:
Read the .md file from docs/e2e/{path}.md. Parse the frontmatter for:
Test files specify an Auth type and a Persona. Use the appropriate method:
| Auth Type | Action |
|---|---|
clerk | Use Clerk login (see below) |
public (token) | Generate/use token, navigate directly |
pos_device + pos_session | Check if already on /pos/ — if redirected to activate/login, handle it |
none | Navigate directly |
Persona emails use +clerk_test subaddress — Clerk verification codes are always 424242.
// 1. Sign out from Clerk JS
await browser.eval(async () => {
if (window.Clerk) await window.Clerk.signOut();
});
await browser.wait(1000);
// 2. Navigate to sign-in page
await browser.reload('/clerk/sign-in');
await browser.wait(4000); // Wait for Clerk widget to load
// 3. Fill email (use snapshot refs — don't use broad selectors)
// Snapshot → find email textbox ref → fill → click Continue
const snapshot = await browser.snapshot(browser.locator('#clerk-sign-in'));
// Find textbox "Email address" ref from snapshot
await browser.fill(browser.getBySnapshotRef('eXX'), 'e2e-admin+clerk_test@example.com');
// Find "Continue" button ref from snapshot
await browser.click(browser.getBySnapshotRef('eYY'));
await browser.wait(3000);
// 4. Password step — snapshot again, find password textbox + Continue
await browser.fill(browser.getBySnapshotRef('eZZ'), 'E2eTest!2026');
await browser.click(browser.getBySnapshotRef('eWW'));
await browser.wait(4000);
// 5. Client Trust verification — snapshot, find code input + Continue
await browser.fill(browser.getBySnapshotRef('eVV'), '424242');
await browser.click(browser.getBySnapshotRef('eUU'));
await browser.wait(5000);
// 6. Verify redirected away from /clerk/sign-in
IMPORTANT: The Clerk widget renders dynamic refs — always take a fresh snapshot before each step. Don't reuse refs from earlier snapshots.
Available personas (from GsNet.E2E.Personas):
| Key | Password | Tenant Role | Org Role | Interfaces | |
|---|---|---|---|---|---|
admin | e2e-admin+clerk_test@example.com | E2eTest!2026 | owner | executive @ company | admin, hq, staff, customer |
staff | e2e-staff+clerk_test@example.com | E2eTest!2026 | member | staff @ center | staff, customer |
manager | e2e-manager+clerk_test@example.com | E2eTest!2026 | member | center_manager @ center | staff, customer |
customer | e2e-customer+clerk_test@example.com | E2eTest!2026 | member | — (no org role) | customer |
hq | e2e-hq+clerk_test@example.com | E2eTest!2026 | member | director @ division | hq, staff, customer |
affiliate | e2e-affiliate+clerk_test@example.com | E2eTest!2026 | member | — (no org role) | affiliate, customer |
hr_manager | e2e-hr+clerk_test@example.com | E2eTest!2026 | member | director @ HR Team | hq, staff, customer |
Provisioned by mix gs_net.e2e_setup (creates Clerk accounts + GsNet records).
To fully sign out both Clerk JS and Phoenix session:
// 1. Sign out from Clerk JS
await browser.eval(async () => {
if (window.Clerk) await window.Clerk.signOut();
});
await browser.wait(1000);
// 2. Clear Phoenix session
await browser.reload('/clerk/session-destroy');
await browser.wait(500);
For each scenario (or the selected subset):
## Scenario N: [Name] headerAfter all scenarios complete, print a summary table:
## Results: pos/terminal-basic
| # | Scenario | Result | Notes |
|---|----------|--------|-------|
| 1 | Product Grid Loads | PASS | |
| 2 | Category Filtering | PASS | |
| 3 | Product Search | FAIL | Search input not found |
...
Total: 8/10 passed
If ALL scenarios pass, update the test file's Last Verified date to today.
Use these exact patterns to translate markdown steps into browser_eval calls:
| Step | Tidewave |
|---|---|
Navigate to /path | browser.reload("/path") |
| Snapshot [element] | console.log(await browser.snapshot(browser.locator('selector'))) |
| Snapshot page | console.log(await browser.snapshot(browser.locator('body'))) |
| Verify [element] visible with text "[text]" | Snapshot → check output contains text |
| Verify [element] exists | Snapshot → check element appears in tree |
| Click [button with text] | await browser.click(browser.locator('button', { hasText: 'text' })) |
| Click [link with text] | await browser.click(browser.locator('a', { hasText: 'text' })) |
| Click [element by ref] | await browser.click(browser.getBySnapshotRef('eXX')) |
| Fill "[field name]" with "[value]" | await browser.fill(browser.locator('input[name="field"]'), "value") |
| Fill [placeholder text] with "[value]" | await browser.fill(browser.locator('input[placeholder="text"]'), "value") |
| Wait [N] ms | await browser.wait(N) |
| Select "[option]" from "[select]" | Use browser.eval with dispatchEvent |
| Sign out from Clerk | await browser.eval(async () => { if (window.Clerk) await window.Clerk.signOut(); }) |
| Fill verification code | await browser.fill(codeInputRef, '424242') |
Always prefer snapshots over DOM scripting:
eXX)browser.getBySnapshotRef('eXX') to interact with found elementsbrowser.click() and browser.fill() over browser.eval() where possibleThis avoids brittle CSS selectors and matches how the test files describe elements (by text/role, not by class).
Clerk widget note: The Clerk sign-in widget re-renders between steps (email → password → verification). Always take a fresh snapshot after each transition — refs from previous steps will be stale.
/clerk/sign-in for the widget to mount.Test files mirror the app's route structure:
docs/e2e/
├── README.md # This guide
│
├── auth/ # Authentication flows
│ ├── clerk-login.md # 6 scenarios — Clerk SSO login/logout
│ └── pos-device-auth.md # 6 scenarios — POS device activation + PIN
│
├── pos/ # POS terminal (/pos/*)
│ ├── terminal-basic.md # 10 scenarios — product grid, cart, payment
│ └── terminal-membership.md # 5 scenarios — membership + agreement signing
│
├── staff/ # Staff portal (/staff/*)
│ ├── members-list.md # 10 scenarios — Cinder table, search, filter
│ ├── dashboard.md # 4 scenarios — staff landing, navigation
│ └── member-detail.md # 6 scenarios — member view, transfers, status
│
├── public/ # Public pages (no auth)
│ └── checkout-link.md # 10 scenarios — review → payment → sign → confirm
│
├── admin/ # Admin console (/admin/*)
│ ├── catalog-management.md # 8 scenarios — product CRUD, pricing, publishing
│ ├── organization.md # 5 scenarios — org tree view, edit units
│ ├── users-invites.md # 6 scenarios — user list, invite code CRUD
│ └── events-roster.md # 6 scenarios — roster, attendance, enrollment
│
├── customer/ # Customer portal (/my/*)
│ ├── workshop-catalog.md # 5 scenarios — browse, filter, enroll
│ └── events.md # 5 scenarios — my events, deferred registration
│
└── hq/ # HQ dashboard (/hq/*) — future
kebab-case.md, match the feature/page namepos/terminal-basic.md, NOT pos/terminal/basic.mdsmoke/, regression/ folders)Every test file MUST have this frontmatter:
# E2E: [Page/Feature Name]
> **Route**: /path/to/page
> **Auth**: clerk | pos_device | pos_session | public (token) | none
> **Persona**: admin | staff | manager
> **Prerequisites**: [what must be true before tests run]
> **Last Verified**: YYYY-MM-DD | —
When creating a new E2E test file:
### Scenario 1:, ### Scenario 2:, etc.**Expected**: after each scenario's steps--- horizontal rules/e2e list # Show all test files
/e2e status # Overall pass/fail status
/e2e pos/terminal-basic # Run all scenarios in file
/e2e pos/terminal-basic#3 # Run one scenario
/e2e pos/terminal-basic#8-10 # Run scenario range
| Concept | Value |
|---|---|
| Test email suffix | +clerk_test (e.g., user+clerk_test@example.com) |
| Verification code | 424242 (always, no email sent) |
| Password (all personas) | E2eTest!2026 |
| Provisioning | mix gs_net.e2e_setup |
| Clerk docs | https://clerk.com/docs/guides/development/testing/test-emails-and-phones |