Skip to content

Commit 0a758fb

Browse files
fix: Firefox requiring click to connect to Daily (Issue #1187) (#1189)
* Add comprehensive video call testing strategy and implementation This commit addresses the lack of tests for the videocall interface by introducing a layered testing approach that balances speed, cost, and confidence. ## What's Added ### Documentation - **VIDEOCALL_TESTING_STRATEGY.md**: Complete testing philosophy and implementation plan covering unit tests (mocked), contract tests, smoke tests (live Daily.co), and cross-browser E2E - **VIDEOCALL_TESTING_QUICKSTART.md**: 30-minute quick start guide to get first tests running ### Unit Tests (Vitest) - Mock utilities for @daily-co/daily-react components and hooks - Example tests for VideoCall subscription management and device alignment - Fast feedback loop without hitting Daily.co API ### E2E Smoke Tests (Playwright) - New playwright-videocall/ directory for gradual migration from Cypress - Live Daily.co integration tests with automatic room creation/cleanup - Helpers for managing test rooms and minimizing API usage - Cross-browser support (Chromium, Firefox, WebKit) ### CI/CD Integration - GitHub Actions workflow with smart path filtering - Runs unit tests on every commit to call-related code - Runs smoke tests only on main branch or manual trigger - Supports manual cross-browser test execution ## Testing Philosophy Uses a 4-layer approach: 1. Unit tests (mocked) - Fast feedback, 90% of runs 2. Contract tests - Verify Daily.co API assumptions 3. Smoke tests (live) - Confidence before deployment 4. Cross-browser E2E - Catch browser-specific bugs ## Cost Management - Free tier Daily.co usage: ~1% per month (100 min / 10,000 available) - Short-lived rooms (1 hour expiry) - Conditional CI execution (path filters) - Automated room cleanup ## Playwright vs Cypress Recommends Playwright for new video tests due to: - Better multi-browser support - Improved WebRTC/permission mocking - Faster execution - Modern API design Existing Cypress tests remain untouched for gradual migration. ## Next Steps See VIDEOCALL_TESTING_QUICKSTART.md for: - Setting up Playwright - Running first tests - Customizing for your app - Adding to CI https://claude.ai/code/session_01WNWJMiUufNo4NTgPx3pbU8 * Add Playwright component testing infrastructure - Create playwright/ directory with clean separation: - mocks/ for reusable Empirica mock classes (MockPlayer, MockGame, MockStage) - helpers/ for scenario builders and Daily.co room setup - component-tests/ for test files - Add initial VideoCall component tests with real Daily.co rooms - Integrate production server code for room management - Add npm scripts for running component tests - Create comprehensive documentation and README This enables component-level testing with real WebRTC while mocking Empirica state, avoiding the complexity of full E2E batch setup. https://claude.ai/code/session_b1DG1 * Remove old testing infrastructure, keep only clean Playwright component tests - Removed old E2E setup in playwright-videocall/ - Removed old subscription tests - Removed old documentation and CI workflow - Kept only clean playwright/ directory with reusable mocks and component tests https://claude.ai/code/session_01WNWJMiUufNo4NTgPx3pbU8 * Remove old testing infrastructure and finalize clean component test setup - Removed playwright-videocall/ directory (old E2E approach) - Removed client/src/call/__tests__/ (old subscription tests) - Removed docs/VIDEOCALL_TESTING_*.md (old documentation) - Removed .github/workflows/videocall-tests.yml (old CI workflow) - Kept only the new clean playwright/ component testing setup https://claude.ai/code/session_b1DG1 * Refactor Playwright component tests for scalability and organization Restructure component tests from single monolithic file into organized, concern-based architecture to support scaling to hundreds of tests. Changes: - Split VideoCall.ct.jsx into three focused files by concern: - VideoCall.basic.ct.jsx (smoke tests) - VideoCall.states.ct.jsx (tile states: muted, waiting, etc.) - VideoCall.layout.ct.jsx (multi-player scenarios) - Create shared/fixtures.js with 5 reusable test configurations - Move debug test to organized debug/ folder - Add comprehensive documentation: - playwright/README.md (architecture, config, troubleshooting) - component-tests/README.md (organization principles) - video-call/README.md (test catalog and guidelines) - mocks/README.md (mock architecture deep dive) - Add npm scripts for headed and UI modes - Rename playwright.config.js to .mjs for ESM compatibility All 7 tests passing. Ready for expansion with clear patterns established. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * test(playwright): migrate Cypress layout tests and add comprehensive assertions - Migrate 8 Cypress discussionLayout scenarios to Playwright component tests (~30x faster) - Create VideoCall.customLayouts.ct.jsx with 6 custom layout tests (2x2 grid, PiP, telephone game, breakout rooms, hide self view) - Rename VideoCall.layout.ct.jsx → VideoCall.responsiveLayout.ct.jsx - Expand responsive tests from 1 to 17 tests (player counts, screen widths, dynamic resizing) - Add layout-helpers.js with deep assertions (overlap detection, z-index verification, space filling) - Add layout-fixtures.js with reusable layout configurations - Fix bug in DailyVideo mock showing "mirrored" on all tiles instead of just local participant - Fix z-index test by checking parent element instead of Tile itself - Add MIGRATION.md tracking Cypress → Playwright migration progress All 28 component tests passing ✅ Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: Add dual-config Playwright component testing for VideoCall Implements comprehensive component testing infrastructure with separate configs for mocked and integration tests. ## Mocked Tests (29 passing ✓) - Fast execution (~12s for all tests) - Full control over Daily.co state for edge case testing - No external dependencies (no API keys needed) - Test coverage: * Basic rendering and smoke tests (2 tests) * Tile states (muted, waiting, connected) (3 tests) * Responsive layouts (17 tests) * Custom layouts (grid, PiP, breakout rooms) (7 tests) ## Integration Tests (Infrastructure ready) - Separate config for real Daily.co WebRTC testing - Requires DAILY_APIKEY for real connections - Tests browser-specific behavior (Safari audio, device recovery) - Currently under development ## Configuration - playwright.config.mjs: Mocked Daily + Empirica (runs mocked/ tests) - playwright.integration.config.mjs: Real Daily, mocked Empirica (runs integration/ tests) - Package scripts: npm run test:component, test:component:integration ## File Organization - component-tests/video-call/mocked/: Fast mocked tests - component-tests/video-call/integration/: Real Daily integration tests - component-tests/shared/: Shared fixtures and helpers Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Resolve MockEmpiricaProvider reactivity bug and add extensive documentation ## The Problem Components using usePlayer() weren't seeing updated player state after player.set() was called, even though the mutation happened correctly. The root cause was that MockPlayer instances were being recreated on every render, destroying all state. ## Root Causes Identified 1. Inline array literals as default props (players = []) created NEW array references on every render, causing useMemo to re-evaluate and create brand new MockPlayer instances 2. Including handleChange callback in useMemo dependencies caused unnecessary re-creation of mock instances 3. Missing renderCount dependency in contextValue memoization meant Context consumers weren't triggered to update after forceUpdate() ## The Three-Part Fix ### 1. Stable Default Values - Define EMPTY_PLAYERS_ARRAY and EMPTY_OBJECT outside component - React's useMemo uses Object.is() for comparison: [] !== [] but EMPTY_PLAYERS_ARRAY === EMPTY_PLAYERS_ARRAY ### 2. Remove handleChange from useMemo Dependencies - handleChange is stable (useCallback with empty deps) - Including it caused mysterious re-creation bugs - Safe to omit since it's passed to constructors, not read from closure ### 3. Memoize contextValue with renderCount Dependency - When renderCount increments (via forceUpdate), create NEW contextValue object - React Context detects reference change and propagates to consumers - Consumers re-render and call player.get() to see fresh data ## Documentation Added Added ~600 lines of detailed comments explaining: - The observable mutation pattern (mutable state + manual re-render triggers) - How React Context propagation works with the reactivity system - Why instance stability is critical for state preservation - Complete end-to-end reactivity flow diagrams ## Debug Test Suite Added MockHarnessDebug.ct.jsx with 6 isolated tests that verify harness reactivity without making Daily API calls, saving WebRTC call minutes during development. ## Verification - All 6 debug tests pass - All 6 VideoCall integration tests pass - Player instance stability confirmed (sameInstance: true across re-renders) - State mutations propagate correctly to all components Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * playwright tests * test: Add 93 mocked component tests for VideoCall features Implements Phase 2 of the video call test plan: mocked component tests covering all testable behaviors without requiring real Daily.co infrastructure. New test files (playwright/component-tests/video-call/mocked/): - Tile.ct.jsx (9 tests): self-view, video/audio muted states, waiting state - Tray.ct.jsx (7 tests): Fix A/V button, camera/mic toggles, missing participant button - FixAV.ct.jsx (14 tests): modal workflow, multi-select, mic/camera muted diagnosis+fix, AudioContext suspended fix, success state, auto-close (FIXAV-001 to FIXAV-014) - Subscriptions.ct.jsx (6 tests): drift detection, repair payload, cooldown enforcement, unsubscribable tracks, log content (SUB-001 to SUB-006) - AudioContext.ct.jsx (6 tests): suspended detection, banner UI, resume, auto-resume, state logging (AUDIO-001 to AUDIO-006) - ErrorReporting.ct.jsx (5 tests): camera/mic errors, Sentry capture, breadcrumbs - DeviceAlignmentLogs.ct.jsx (3 tests): log deduplication, fallback messages - Speaker.ct.jsx (4 tests): setSpeaker error handling, fallback retry, Sentry breadcrumb - VideoCall.deviceAlignment.ct.jsx (3 tests): device label matching, skip conditions - VideoCall.historyAndData.ct.jsx (5 tests): dailyId history, avReports logging Mock infrastructure updates: - MockDailyProvider: add MockCallObject EventEmitter with updateParticipants spy, localAudio/localVideo stubs, setLocalAudio/setLocalVideo for soft-fix testing, _audioEnabled/_videoEnabled state control, device stubs - sentry-mock.js: full rewrite with window.mockSentryCaptures capturing all calls - console-capture.js: new helper for intercepting console output in tests - daily-hooks.jsx: fix useParticipantProperty to read from context - playwright.config.mjs: set workers=2 to prevent parallel load failures Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * test: Add FIXAV-006/008 (mic/camera track-ended + re-acquire) Extends mock infrastructure to support track-ended diagnosis: - MockCallObject: add _audioReadyState/_videoReadyState (default 'live') Tests set these to 'ended' to simulate mic/camera track ending mid-call. - setInputDevicesAsync() now resets readyState='live' on re-acquisition, allowing re-diagnosis to confirm the fix succeeded. New tests in FixAV.ct.jsx (16 total, up from 14): - FIXAV-006: mic track ended → microphoneTrackEnded detected → setInputDevicesAsync re-acquires mic → readyState resets to 'live' → "Issue resolved" - FIXAV-008: camera track ended → cameraTrackEnded detected → setInputDevicesAsync re-acquires camera → readyState resets to 'live' → "Issue resolved" Both tests use twoPlayerConfigWithDevices which provides devices.microphones and devices.cameras (required by attemptSoftFixes). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * test: Add PERM-001 to PERM-004 (browser permission monitoring) navigator.permissions.query() is a getter-only prototype property in Chrome, so tests must use Object.defineProperty(navigator, 'permissions', ...) to install a controllable mock (direct assignment silently fails). New PermissionMonitoring.ct.jsx (4 tests): - PERM-001: camera permission change → [Permissions] console.warn logged - PERM-002: mic permission change → [Permissions] console.warn logged - PERM-003: permission revoked to 'denied' → additional console.error logged - PERM-004: browserPermissions field captured in Sentry reportedAVError (hint.extra.beforeDiagnostics.browserPermissions.camera === 'denied') Mock strategy: - installPermissionsMock(): uses Object.defineProperty on navigator to shadow the prototype getter with a configurable own property - makePerm(): creates PermissionStatus objects with getter/setter on 'onchange' so VideoCall's handler can be assigned and then triggered - window.triggerPermChange(type, newState): fires synthetic permission change events from test code PERM-005 (Daily participant permissions) and PERM-006 (blocked tracks) remain deferred — different underlying systems. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * test: Add SPEAKER-004/006 (gesture prompt for Safari speaker gestures) After merging main (f463d5d) which implemented the unified setup completion prompt, add tests for the newly-available gesture prompt behavior: - SPEAKER-004: gesture prompt overlay shown when setSpeaker throws NotAllowedError - SPEAKER-006: gesture prompt dismisses after user clicks "Enable Audio" - SPEAKER-002: updated to use a generic Error (not NotAllowedError) so it tests the fallback-speaker path, which is now separate from the gesture prompt path Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: Add Playwright CT section to Claude.md and fix Speaker test count - Claude.md: add Component Tests section with run commands, file→feature mapping, and in-page mock control reference for debugging sessions - TEST-PROGRESS.md: fix Speaker.ct.jsx count (4→6 after SPEAKER-004/006) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: Add CLAUDE.md with project context and Playwright CT reference Includes project structure, dev commands, Sentry/GitHub tool notes, key conventions, and a Component Tests section with: - Run commands for Playwright CT - File→feature mapping for video-call tests - In-page mock control reference for debugging sessions Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * handle tab losing focus * save position to daily object * chore: Reduce console log spam from audio/visibility monitoring Removed noisy repeated logs: - DEBUG overlay IIFE that logged on every render - User gesture detection logs (fired on every click/keydown) - Auto-resume check logs (fired every 5 seconds) - Visibility focus/blur console logs (still stored to player state) Kept important debug logs for weird states: - AudioContext suspended on creation (warning) - Page blurred while AudioContext suspended (warning, critical for #1187) - AudioContext resumed successfully (confirmation) - AudioContext resume failure (error) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Show stall prompt faster when page is unfocused during join When Firefox suspends WebRTC due to unfocused tab, we now show the "Video connection paused" prompt after 500ms instead of 5s: - If page is already unfocused when join starts → prompt in 500ms - If page blurs during join → prompt in 500ms from blur - Fallback 5s timer remains for edge cases Also removed verbose "Attempting join" log to reduce noise. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Delay AudioContext prompt to prevent modal flicker Firefox often auto-resumes AudioContext shortly after page load when the page is focused. Previously we would show the "enable audio" modal immediately on suspension, causing it to flash and disappear. Now we: - Wait 800ms before showing prompt (if page is focused) - Show immediately if page is unfocused (Firefox won't auto-resume) - Cancel the delay if AudioContext resumes on its own This prevents the modal from flickering when Firefox auto-resumes. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Skip visibility tracking in tests and add AudioContext debug logs - Skip blur/focus visibility tracking in component tests (detected via window.mockPlayers) - fixes Playwright click timeouts - Add AudioContext creation, state change, and auto-resume debug logs - Update test mocks to provide AudioContext and document.hasFocus stubs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ci: Add Playwright component tests workflow Runs the 102 mocked VideoCall component tests on every push and PR. Only installs chromium browser to keep CI fast. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update ConditionalRender.jsx --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9194252 commit 0a758fb

76 files changed

Lines changed: 17020 additions & 186 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Playwright Component Tests
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
playwright-component-tests:
9+
timeout-minutes: 15
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Setup Node.js
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: "18"
20+
21+
- name: Install root dependencies
22+
run: npm ci
23+
24+
- name: Install client dependencies
25+
run: cd client && npm ci
26+
27+
- name: Install Playwright browsers
28+
run: npx playwright install --with-deps chromium
29+
30+
- name: Run Playwright component tests (mocked)
31+
run: npx playwright test --config playwright/playwright.config.mjs "video-call/mocked"
32+
33+
- name: Upload test results
34+
uses: actions/upload-artifact@v4
35+
if: failure()
36+
with:
37+
name: playwright-report
38+
path: playwright-report/
39+
retention-days: 7

CLAUDE.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,48 @@ Admin UI: `http://localhost:3000/admin`
2525
- **GitHub**: Use `gh` CLI for all GitHub workflows — viewing issues, creating PRs, reading and responding to PR review comments. Paste issue/PR URLs or numbers directly into the conversation.
2626
- **Sentry**: Sentry MCP is installed. Use it to fetch error events, look up issues by ID, search for recent errors, etc.
2727

28+
## Component Tests (Playwright)
29+
30+
101 mocked component tests for the `call/` subsystem live in `playwright/component-tests/video-call/mocked/`.
31+
They run without a real Daily.co connection — everything is mocked.
32+
33+
```bash
34+
# Run all video-call component tests
35+
npx playwright test --config playwright/playwright.config.mjs "video-call/mocked"
36+
37+
# Run a specific test file
38+
npx playwright test --config playwright/playwright.config.mjs "video-call/mocked/FixAV"
39+
40+
# Run a single test by name grep
41+
npx playwright test --config playwright/playwright.config.mjs "video-call/mocked/Speaker" --grep "SPEAKER-004"
42+
```
43+
44+
**Test file → feature mapping:**
45+
| File | Feature area |
46+
|---|---|
47+
| `FixAV.ct.jsx` | Fix A/V modal + diagnosis flow |
48+
| `Speaker.ct.jsx` | Speaker alignment + gesture prompt |
49+
| `AudioContext.ct.jsx` | AudioContext suspension recovery |
50+
| `Subscriptions.ct.jsx` | Subscription drift + repair heartbeat |
51+
| `PermissionMonitoring.ct.jsx` | Browser permission change events |
52+
| `Tile.ct.jsx` / `Tray.ct.jsx` | UI components |
53+
| `ErrorReporting.ct.jsx` | Sentry captures |
54+
| `VideoCall.historyAndData.ct.jsx` | dailyIdHistory + avReports |
55+
| `DeviceAlignmentLogs.ct.jsx` | Device alignment log spam |
56+
| `VideoCall.deviceAlignment.ct.jsx` | Device ID alignment effect |
57+
58+
**In-page test controls (via `page.evaluate`):**
59+
- `window.mockCallObject._audioEnabled = false` — simulate muted mic
60+
- `window.mockCallObject._videoEnabled = false` — simulate muted camera
61+
- `window.mockCallObject._audioReadyState = 'ended'` — simulate ended mic track
62+
- `window.mockCallObject._videoReadyState = 'ended'` — simulate ended camera track
63+
- `window.mockCallObject._updateParticipantsCalls` — inspect `updateParticipants` call log
64+
- `window.mockDailyDeviceOverrides = { setSpeaker: () => Promise.reject(...) }` — override device calls
65+
- `window.mockSentryCaptures` — inspect Sentry captures (`.messages`, `.breadcrumbs`, `.exceptions`)
66+
- `window.triggerPermChange('camera', 'denied')` — fire synthetic permission change (after `installPermissionsMock`)
67+
68+
**Progress tracking:** `playwright/component-tests/video-call/TEST-PROGRESS.md`
69+
2870
## Key Conventions
2971

3072
- **Tests are spec**: Cypress e2e tests define expected UX and data outputs. Any behavior change requires updating the relevant test.

client/package-lock.json

Lines changed: 40 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
}
1515
},
1616
"dependencies": {
17-
"@daily-co/daily-js": "^0.85.0",
18-
"@daily-co/daily-react": "^0.24.0",
1917
"@empirica/core": "1.11.2",
2018
"@hello-pangea/dnd": "^16.3.0",
2119
"@sentry/react": "^7.1.1",
@@ -39,17 +37,20 @@
3937
},
4038
"devDependencies": {
4139
"@babel/core": "^7.17.8",
40+
"@daily-co/daily-js": "^0.87.0",
41+
"@daily-co/daily-react": "^0.24.0",
4242
"@sentry/vite-plugin": "^2.22.6",
4343
"@types/react": "18.0.14",
4444
"@types/react-dom": "18.0.5",
4545
"@vitejs/plugin-react": "^4.3.3",
4646
"@vitejs/plugin-react-refresh": "1.3.1",
4747
"autoprefixer": "10.4.7",
4848
"babel-loader": "^8.2.4",
49+
"dotenv": "^17.2.4",
4950
"path": "0.12.7",
5051
"vite": "^5.4.11",
5152
"vite-plugin-restart": "^0.4.1",
5253
"vite-plugin-windicss": "^1.9.3",
5354
"vitest": "^1.6.0"
5455
}
55-
}
56+
}

0 commit comments

Comments
 (0)