Files
Toju/.agents/skills/playwright-e2e/SKILL.md
Myx 391d9235f1
Some checks failed
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Successful in 21s
Queue Release Build / build-linux (push) Successful in 27m44s
Queue Release Build / build-windows (push) Successful in 32m16s
Queue Release Build / finalize (push) Successful in 1m54s
test: Add playwright main usage test
2026-04-12 03:02:29 +02:00

12 KiB
Raw Blame History

name, description
name description
playwright-e2e 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.

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

Step 3 — Core Conventions

Config Essentials

// 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

// ✅ 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

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:

// 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 (0200% 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: identifyserver_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

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 Voice/video/WebRTC tests, multi-browser contexts, audio validation
reference/project-setup.md First-time scaffold, dependency installation, config creation