|
1 | 1 | import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; |
2 | | -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; |
| 2 | +import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'; |
3 | 3 | import { MemoryRouter, Route, Routes } from 'react-router-dom'; |
4 | 4 | import { ViewerPage } from './viewer'; |
5 | 5 | import type { Session, SessionParticipant } from '@pairux/shared-types'; |
@@ -72,16 +72,18 @@ const mockSFUHookResult = { |
72 | 72 | }; |
73 | 73 |
|
74 | 74 | vi.mock('@/hooks/useWebRTCViewerAPI', () => ({ |
75 | | - useWebRTCViewerAPI: (opts: { onKicked?: () => void }) => { |
| 75 | + useWebRTCViewerAPI: (opts: { onKicked?: () => void; onPresenceChange?: () => void }) => { |
76 | 76 | // Store callback for testing |
77 | 77 | (mockP2PHookResult as Record<string, unknown>)._onKicked = opts.onKicked; |
| 78 | + (mockP2PHookResult as Record<string, unknown>)._onPresenceChange = opts.onPresenceChange; |
78 | 79 | return mockP2PHookResult; |
79 | 80 | }, |
80 | 81 | })); |
81 | 82 |
|
82 | 83 | vi.mock('@/hooks/useWebRTCViewerSFUAPI', () => ({ |
83 | | - useWebRTCViewerSFUAPI: (opts: { onKicked?: () => void }) => { |
| 84 | + useWebRTCViewerSFUAPI: (opts: { onKicked?: () => void; onPresenceChange?: () => void }) => { |
84 | 85 | (mockSFUHookResult as Record<string, unknown>)._onKicked = opts.onKicked; |
| 86 | + (mockSFUHookResult as Record<string, unknown>)._onPresenceChange = opts.onPresenceChange; |
85 | 87 | return mockSFUHookResult; |
86 | 88 | }, |
87 | 89 | })); |
@@ -404,4 +406,47 @@ describe('ViewerPage', () => { |
404 | 406 |
|
405 | 407 | setIntervalSpy.mockRestore(); |
406 | 408 | }); |
| 409 | + |
| 410 | + it('refreshes participant list immediately on presence change', async () => { |
| 411 | + mockInvoke |
| 412 | + .mockResolvedValueOnce({ |
| 413 | + success: true, |
| 414 | + session: makeSession(), |
| 415 | + participants: makeParticipants(), |
| 416 | + }) |
| 417 | + .mockResolvedValueOnce({ |
| 418 | + success: true, |
| 419 | + session: makeSession(), |
| 420 | + participants: [ |
| 421 | + ...makeParticipants(), |
| 422 | + { |
| 423 | + id: 'part-viewer-2', |
| 424 | + session_id: 'session-1', |
| 425 | + user_id: 'user-2', |
| 426 | + role: 'viewer', |
| 427 | + display_name: 'Viewer Two', |
| 428 | + control_state: 'view-only', |
| 429 | + is_backup_host: false, |
| 430 | + connection_status: 'connected', |
| 431 | + last_seen_at: null, |
| 432 | + joined_at: new Date().toISOString(), |
| 433 | + left_at: null, |
| 434 | + } satisfies SessionParticipant, |
| 435 | + ], |
| 436 | + }); |
| 437 | + |
| 438 | + renderWithRouter(); |
| 439 | + |
| 440 | + await waitFor(() => { |
| 441 | + expect(screen.getByText('2 participants')).toBeInTheDocument(); |
| 442 | + }); |
| 443 | + |
| 444 | + await act(async () => { |
| 445 | + (mockP2PHookResult as unknown as { _onPresenceChange?: () => void })._onPresenceChange?.(); |
| 446 | + }); |
| 447 | + |
| 448 | + await waitFor(() => { |
| 449 | + expect(screen.getByText('3 participants')).toBeInTheDocument(); |
| 450 | + }); |
| 451 | + }); |
407 | 452 | }); |
0 commit comments