import { expect, type Page, type Locator } from '@playwright/test'; export class ChatRoomPage { readonly chatMessages: Locator; readonly voiceWorkspace: Locator; readonly channelsSidePanel: Locator; readonly usersSidePanel: Locator; constructor(private page: Page) { this.chatMessages = page.locator('app-chat-messages'); this.voiceWorkspace = page.locator('app-voice-workspace'); this.channelsSidePanel = page.locator('app-rooms-side-panel').first(); this.usersSidePanel = page.locator('app-rooms-side-panel').last(); } /** Click a voice channel by name in the channels sidebar to join voice. */ async joinVoiceChannel(channelName: string) { const channelButton = this.page.locator('app-rooms-side-panel') .getByRole('button', { name: channelName, exact: true }); await expect(channelButton).toBeVisible({ timeout: 15_000 }); await channelButton.click(); } /** Click "Create Voice Channel" button in the channels sidebar. */ async openCreateVoiceChannelDialog() { await this.page.locator('button[title="Create Voice Channel"]').click(); } /** Click "Create Text Channel" button in the channels sidebar. */ async openCreateTextChannelDialog() { await this.page.locator('button[title="Create Text Channel"]').click(); } /** Fill the channel name in the create channel dialog and confirm. */ async createChannel(name: string) { const dialog = this.page.locator('app-confirm-dialog'); const channelNameInput = dialog.getByPlaceholder('Channel name'); const createButton = dialog.getByRole('button', { name: 'Create', exact: true }); await expect(channelNameInput).toBeVisible({ timeout: 10_000 }); await channelNameInput.fill(name); await createButton.click(); } /** Get the voice controls component. */ get voiceControls() { return this.page.locator('app-voice-controls'); } /** Get the mute toggle button inside voice controls. */ get muteButton() { return this.voiceControls.locator('button:has(ng-icon[name="lucideMic"]), button:has(ng-icon[name="lucideMicOff"])').first(); } /** Get the disconnect/hang-up button (destructive styled). */ get disconnectButton() { return this.voiceControls.locator('button:has(ng-icon[name="lucidePhoneOff"])').first(); } /** Get all voice stream tiles. */ get streamTiles() { return this.page.locator('app-voice-workspace-stream-tile'); } /** Get the count of voice users listed under a voice channel. */ async getVoiceUserCountInChannel(channelName: string): Promise { const channelSection = this.page.locator('app-rooms-side-panel') .getByRole('button', { name: channelName }) .locator('..'); const userAvatars = channelSection.locator('app-user-avatar'); return userAvatars.count(); } }