test: Initate instructions
This commit is contained in:
282
.agents/skills/playwright-e2e/SKILL.md
Normal file
282
.agents/skills/playwright-e2e/SKILL.md
Normal file
@@ -0,0 +1,282 @@
|
||||
---
|
||||
name: playwright-e2e
|
||||
description: >
|
||||
Write and run Playwright E2E tests for MetoYou (Angular chat + WebRTC voice/video app).
|
||||
Handles multi-browser-context WebRTC voice testing, fake media devices, signaling validation,
|
||||
audio channel verification, and UI state assertions. Use when: "E2E test", "Playwright",
|
||||
"end-to-end", "browser test", "test voice", "test call", "test WebRTC", "test chat",
|
||||
"test login", "test video", "test screen share", "integration test", "multi-client test".
|
||||
---
|
||||
|
||||
# Playwright E2E Testing — MetoYou
|
||||
|
||||
## Step 1 — Check Project Setup
|
||||
|
||||
Before writing any test, verify the Playwright infrastructure exists:
|
||||
|
||||
```
|
||||
e2e/ # Test root (lives at repo root)
|
||||
├── playwright.config.ts # Config
|
||||
├── fixtures/ # Custom fixtures (multi-client, auth, etc.)
|
||||
├── pages/ # Page Object Models
|
||||
├── tests/ # Test specs
|
||||
│ ├── auth/
|
||||
│ ├── chat/
|
||||
│ ├── voice/
|
||||
│ └── settings/
|
||||
└── helpers/ # Shared utilities (WebRTC introspection, etc.)
|
||||
```
|
||||
|
||||
If missing, scaffold it. See [reference/project-setup.md](./reference/project-setup.md).
|
||||
|
||||
## Step 2 — Identify Test Category
|
||||
|
||||
| Request | Category | Key Patterns |
|
||||
|---------|----------|-------------|
|
||||
| Login, register, invite | **Auth** | Single browser context, form interaction |
|
||||
| Send message, rooms, chat UI | **Chat** | May need 2 clients for real-time sync |
|
||||
| Voice call, mute, deafen, audio | **Voice/WebRTC** | Multi-client, fake media, WebRTC introspection |
|
||||
| Camera, video tiles | **Video** | Multi-client, fake video, stream validation |
|
||||
| Screen share | **Screen Share** | Multi-client, display media mocking |
|
||||
| Settings, themes | **Settings** | Single client, preference persistence |
|
||||
|
||||
For **Voice/WebRTC** and **Multi-client** tests, read [reference/multi-client-webrtc.md](./reference/multi-client-webrtc.md) immediately.
|
||||
|
||||
## Step 3 — Core Conventions
|
||||
|
||||
### Config Essentials
|
||||
|
||||
```typescript
|
||||
// e2e/playwright.config.ts
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
timeout: 60_000, // WebRTC needs longer timeouts
|
||||
expect: { timeout: 10_000 },
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: 1, // Sequential — shared server state
|
||||
reporter: [['html'], ['list']],
|
||||
use: {
|
||||
baseURL: 'http://localhost:4200',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'on-first-retry',
|
||||
permissions: ['microphone', 'camera'],
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
launchOptions: {
|
||||
args: [
|
||||
'--use-fake-device-for-media-stream',
|
||||
'--use-fake-ui-for-media-stream',
|
||||
// Feed a specific audio file as fake mic input:
|
||||
// '--use-file-for-fake-audio-capture=/path/to/audio.wav',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
webServer: [
|
||||
{
|
||||
command: 'cd server && npm run dev',
|
||||
port: 3001,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 30_000,
|
||||
},
|
||||
{
|
||||
command: 'cd toju-app && npx ng serve',
|
||||
port: 4200,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 60_000,
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Selector Strategy
|
||||
|
||||
Use in this order — stop at the first that works:
|
||||
|
||||
1. `getByRole('button', { name: 'Mute' })` — accessible, resilient
|
||||
2. `getByLabel('Email')` — form fields
|
||||
3. `getByPlaceholder('Enter email')` — when label missing
|
||||
4. `getByText('Welcome')` — visible text
|
||||
5. `getByTestId('voice-controls')` — last resort, needs `data-testid`
|
||||
6. `locator('app-voice-controls')` — Angular component selectors (acceptable in this project)
|
||||
|
||||
Angular component selectors (`app-*`) are stable in this project and acceptable as locators when semantic selectors are not feasible.
|
||||
|
||||
### Assertions — Always Web-First
|
||||
|
||||
```typescript
|
||||
// ✅ Auto-retries until timeout
|
||||
await expect(page.getByRole('heading')).toBeVisible();
|
||||
await expect(page.getByRole('alert')).toHaveText('Saved');
|
||||
await expect(page).toHaveURL(/\/room\//);
|
||||
|
||||
// ❌ No auto-retry — races with DOM
|
||||
const text = await page.textContent('.msg');
|
||||
expect(text).toBe('Saved');
|
||||
```
|
||||
|
||||
### Anti-Patterns
|
||||
|
||||
| ❌ Don't | ✅ Do | Why |
|
||||
|----------|-------|-----|
|
||||
| `page.waitForTimeout(3000)` | `await expect(locator).toBeVisible()` | Hard waits are flaky |
|
||||
| `expect(await el.isVisible())` | `await expect(el).toBeVisible()` | No auto-retry |
|
||||
| `page.$('.btn')` | `page.getByRole('button')` | Fragile selector |
|
||||
| `page.click('.submit')` | `page.getByRole('button', {name:'Submit'}).click()` | Not accessible |
|
||||
| Shared state between tests | `test.beforeEach` for setup | Tests must be independent |
|
||||
| `try/catch` around assertions | Let Playwright handle retries | Swallows real failures |
|
||||
|
||||
### Test Structure
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '../fixtures/base';
|
||||
|
||||
test.describe('Feature Name', () => {
|
||||
test('should do specific thing', async ({ page }) => {
|
||||
await test.step('Navigate to page', async () => {
|
||||
await page.goto('/login');
|
||||
});
|
||||
|
||||
await test.step('Fill form', async () => {
|
||||
await page.getByLabel('Email').fill('test@example.com');
|
||||
});
|
||||
|
||||
await test.step('Verify result', async () => {
|
||||
await expect(page).toHaveURL(/\/search/);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Use `test.step()` for readability in complex flows.
|
||||
|
||||
## Step 4 — Page Object Models
|
||||
|
||||
Use POM for any test file with more than 2 tests. Match the app's route structure:
|
||||
|
||||
```typescript
|
||||
// e2e/pages/login.page.ts
|
||||
import { type Page, type Locator } from '@playwright/test';
|
||||
|
||||
export class LoginPage {
|
||||
readonly emailInput: Locator;
|
||||
readonly passwordInput: Locator;
|
||||
readonly submitButton: Locator;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.emailInput = page.getByLabel('Email');
|
||||
this.passwordInput = page.getByLabel('Password');
|
||||
this.submitButton = page.getByRole('button', { name: /sign in|log in/i });
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/login');
|
||||
}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
await this.emailInput.fill(email);
|
||||
await this.passwordInput.fill(password);
|
||||
await this.submitButton.click();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key pages to model (match `app.routes.ts`):**
|
||||
|
||||
| Route | Page Object | Component |
|
||||
|-------|-------------|-----------|
|
||||
| `/login` | `LoginPage` | `LoginComponent` |
|
||||
| `/register` | `RegisterPage` | `RegisterComponent` |
|
||||
| `/search` | `ServerSearchPage` | `ServerSearchComponent` |
|
||||
| `/room/:roomId` | `ChatRoomPage` | `ChatRoomComponent` |
|
||||
| `/settings` | `SettingsPage` | `SettingsComponent` |
|
||||
| `/invite/:inviteId` | `InvitePage` | `InviteComponent` |
|
||||
|
||||
## Step 5 — MetoYou App Architecture Context
|
||||
|
||||
The agent writing tests MUST understand these domain boundaries:
|
||||
|
||||
### Voice/WebRTC Stack
|
||||
|
||||
| Layer | What It Does | Test Relevance |
|
||||
|-------|-------------|----------------|
|
||||
| `VoiceConnectionFacade` | High-level voice API (connect/disconnect/mute/deafen) | State signals to assert against |
|
||||
| `VoiceSessionFacade` | Session lifecycle, workspace layout | UI mode changes |
|
||||
| `VoiceActivityService` | Speaking detection (RMS threshold 0.015) | `isSpeaking()` signal validation |
|
||||
| `VoicePlaybackService` | Per-peer GainNode (0–200% volume) | Volume level assertions |
|
||||
| `PeerConnectionManager` | RTCPeerConnection lifecycle | Connection state introspection |
|
||||
| `MediaManager` | getUserMedia, mute, gain chain | Track state validation |
|
||||
| `SignalingManager` | WebSocket per signal URL | Connection establishment |
|
||||
|
||||
### Voice UI Components
|
||||
|
||||
| Component | Selector | Contains |
|
||||
|-----------|----------|----------|
|
||||
| `VoiceWorkspaceComponent` | `app-voice-workspace` | Stream tiles, layout |
|
||||
| `VoiceControlsComponent` | `app-voice-controls` | Mute, camera, screen share, hang-up buttons |
|
||||
| `FloatingVoiceControlsComponent` | `app-floating-voice-controls` | Floating variant of controls |
|
||||
| `VoiceWorkspaceStreamTileComponent` | `app-voice-workspace-stream-tile` | Per-peer audio/video tile |
|
||||
|
||||
### Voice UI Icons (Lucide)
|
||||
|
||||
| Icon | Meaning |
|
||||
|------|---------|
|
||||
| `lucideMic` / `lucideMicOff` | Mute toggle |
|
||||
| `lucideVideo` / `lucideVideoOff` | Camera toggle |
|
||||
| `lucideMonitor` / `lucideMonitorOff` | Screen share toggle |
|
||||
| `lucidePhoneOff` | Hang up / disconnect |
|
||||
| `lucideHeadphones` | Deafen state |
|
||||
| `lucideVolume2` / `lucideVolumeX` | Volume indicator |
|
||||
|
||||
### Server & Signaling
|
||||
|
||||
- **Signaling server**: Port `3001` (HTTP by default, HTTPS if `SSL=true`)
|
||||
- **Angular dev server**: Port `4200`
|
||||
- **WebSocket signaling**: Upgrades on same port as server
|
||||
- **Protocol**: `identify` → `server_users` → SDP offer/answer → ICE candidates
|
||||
- **PING/PONG**: Every 30s, 45s timeout
|
||||
|
||||
## Step 6 — Validation Workflow
|
||||
|
||||
After generating any test:
|
||||
|
||||
```
|
||||
1. npx playwright install --with-deps chromium # First time only
|
||||
2. npx playwright test --project=chromium # Run tests
|
||||
3. npx playwright test --ui # Interactive debug
|
||||
4. npx playwright show-report # HTML report
|
||||
```
|
||||
|
||||
If the test involves WebRTC, always verify:
|
||||
- Fake media flags are set in config
|
||||
- Timeouts are sufficient (60s+ for connection establishment)
|
||||
- `workers: 1` if tests share server state
|
||||
- Browser permissions granted for microphone/camera
|
||||
|
||||
## Quick Reference — Commands
|
||||
|
||||
```bash
|
||||
npx playwright test # Run all
|
||||
npx playwright test --ui # Interactive UI
|
||||
npx playwright test --debug # Step-through debugger
|
||||
npx playwright test tests/voice/ # Voice tests only
|
||||
npx playwright test --project=chromium # Single browser
|
||||
npx playwright test -g "voice connects" # By test name
|
||||
npx playwright show-report # HTML report
|
||||
npx playwright codegen http://localhost:4200 # Record test
|
||||
```
|
||||
|
||||
## Reference Files
|
||||
|
||||
| File | When to Read |
|
||||
|------|-------------|
|
||||
| [reference/multi-client-webrtc.md](./reference/multi-client-webrtc.md) | Voice/video/WebRTC tests, multi-browser contexts, audio validation |
|
||||
| [reference/project-setup.md](./reference/project-setup.md) | First-time scaffold, dependency installation, config creation |
|
||||
Reference in New Issue
Block a user