Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions apps/meteor/tests/e2e/page-objects/fragments/voice-calls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import type { Locator, Page } from '@playwright/test';

import { expect } from '../../utils/test';

export class VoiceCalls {
private readonly page: Page;

constructor(page: Page) {
this.page = page;
}

async initiateCall(): Promise<void> {
await expect(this.callWidget).toBeVisible();
await this.btnCall.click();
await expect(this.btnCancel).toBeVisible();
}

async acceptCall(): Promise<void> {
await expect(this.btnAcceptCall).toBeVisible();
await this.btnAcceptCall.click();
await expect(this.btnOpenDialpad).toBeVisible();
}

async rejectCall(): Promise<void> {
await expect(this.btnRejectCall).toBeVisible();
await this.btnRejectCall.click();
}

get callWidget(): Locator {
return this.page.getByRole('article', { name: 'New call' });
}

get btnCall(): Locator {
return this.page.getByRole('button', { name: 'Call', exact: true });
}

get callControlGroup(): Locator {
return this.page.getByRole('group');
}

btnEndCall(name: string): Locator {
return this.callControlGroup.getByRole('button', { name: `End call with ${name}` });
}

get btnCancel(): Locator {
return this.page.getByRole('button', { name: 'Cancel', exact: true });
}

get btnAcceptCall(): Locator {
return this.page.getByRole('button', { name: 'Accept', exact: true });
}

get btnRejectCall(): Locator {
return this.page.getByRole('button', { name: 'Reject', exact: true });
}

get btnOpenDialpad(): Locator {
return this.page.getByRole('button', { name: /Dialpad/i });
}

get btnMute(): Locator {
return this.page.getByRole('button', { name: /Mute/i });
}

get btnHold(): Locator {
return this.page.getByRole('button', { name: /Hold|Resume/i });
}

get btnTransfer(): Locator {
return this.page.getByRole('button', { name: 'Forward', exact: true });
}

get transferModal(): Locator {
return this.page.getByRole('dialog', { name: 'Transfer call' });
}

get inputUsername(): Locator {
return this.transferModal.getByRole('textbox', { name: 'Enter username or number' });
}

get btnHangupAndTransfer(): Locator {
return this.transferModal.getByRole('button', { name: 'Hang up and transfer call', exact: true });
}

get callTransferWidget(): Locator {
return this.page.getByRole('heading', { name: 'Transferring call...' });
}

get incommingCallTransferWidget(): Locator {
return this.page.getByRole('heading', { name: 'Incoming call transfer...' });
}

async transferCall(username: string): Promise<void> {
await this.btnTransfer.click();
await expect(this.transferModal).toBeVisible();
await this.inputUsername.click();
await this.inputUsername.fill(username);
await this.page.getByRole('option', { name: username }).getByRole('figure').click();
await expect(this.transferModal).toContainText(username);
await this.btnHangupAndTransfer.click();
}
}
4 changes: 4 additions & 0 deletions apps/meteor/tests/e2e/page-objects/home-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Locator, Page } from '@playwright/test';

import { HomeContent, HomeSidenav, HomeFlextab, Navbar, Sidepanel, RoomSidebar, ToastMessages } from './fragments';
import { RoomToolbar } from './fragments/toolbar';
import { VoiceCalls } from './fragments/voice-calls';

export class HomeChannel {
public readonly page: Page;
Expand All @@ -20,6 +21,8 @@ export class HomeChannel {

readonly roomToolbar: RoomToolbar;

readonly voiceCalls: VoiceCalls;

readonly toastMessage: ToastMessages;

constructor(page: Page) {
Expand All @@ -31,6 +34,7 @@ export class HomeChannel {
this.navbar = new Navbar(page);
this.tabs = new HomeFlextab(page);
this.roomToolbar = new RoomToolbar(page);
this.voiceCalls = new VoiceCalls(page);
this.toastMessage = new ToastMessages(page);
}

Expand Down
155 changes: 155 additions & 0 deletions apps/meteor/tests/e2e/voice-calls-ee.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import type { Page } from '@playwright/test';

import { IS_EE } from './config/constants';
import { createAuxContext } from './fixtures/createAuxContext';
import { Users } from './fixtures/userStates';
import { HomeChannel } from './page-objects';
import { expect, test } from './utils/test';

test.describe('Internal Voice Calls - Enterprise Edition', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let sessions: { page: Page; poHomeChannel: HomeChannel }[];

test.beforeAll(async ({ api }) => {
await Promise.all([
api.post('/users.setStatus', { status: 'online', username: 'user1' }),
api.post('/users.setStatus', { status: 'online', username: 'user2' }),
]);
});

test.beforeAll(async ({ browser }) => {
sessions = await Promise.all([
createAuxContext(browser, Users.user1).then(({ page }) => ({ page, poHomeChannel: new HomeChannel(page) })),
createAuxContext(browser, Users.user2).then(({ page }) => ({ page, poHomeChannel: new HomeChannel(page) })),
]);
});

test('should initiate voice call from direct message', async () => {
const [user1, user2] = sessions;

await test.step('should open direct message with user2', async () => {
await user1.poHomeChannel.sidenav.openChat('user2');
await expect(user1.poHomeChannel.content.inputMessage).toBeVisible();
});

await test.step('initiate a voice call from room toolbar', async () => {
await user1.poHomeChannel.content.btnVoiceCall.click();
await expect(user1.poHomeChannel.voiceCalls.callWidget).toBeVisible();
await user1.poHomeChannel.voiceCalls.initiateCall();
});

await test.step('user2 accepts the call', async () => {
await user2.poHomeChannel.voiceCalls.acceptCall();
});

await test.step('user2 ends the call', async () => {
await user2.poHomeChannel.voiceCalls.btnEndCall('user1').click();
await expect(user2.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
await expect(user1.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
});
});

test('should handle call controls during active call', async () => {
const [user1, user2] = sessions;
await test.step('establish call connection', async () => {
await user1.poHomeChannel.sidenav.openChat('user2');
await expect(user1.poHomeChannel.content.inputMessage).toBeVisible();
await user1.poHomeChannel.content.btnVoiceCall.click();
await user1.poHomeChannel.voiceCalls.initiateCall();
await user2.poHomeChannel.voiceCalls.acceptCall();
});

await test.step('should mute/unmute microphone from user1', async () => {
// User1 mutes microphone
await user1.poHomeChannel.voiceCalls.btnMute.click();
await expect(user1.poHomeChannel.voiceCalls.btnMute).toHaveAttribute('title', 'Unmute');

// User1 unmutes microphone
await user1.poHomeChannel.voiceCalls.btnMute.click();
await expect(user1.poHomeChannel.voiceCalls.btnMute).toHaveAttribute('title', 'Mute');
});

await test.step('should put call on hold from user1', async () => {
// User1 puts call on hold
await user1.poHomeChannel.voiceCalls.btnHold.click();
await expect(user1.poHomeChannel.voiceCalls.btnHold).toHaveAttribute('title', 'Resume');

// User1 resumes call
await user1.poHomeChannel.voiceCalls.btnHold.click();
await expect(user1.poHomeChannel.voiceCalls.btnHold).toHaveAttribute('title', 'Hold');
});

await test.step('should access dialpad during call', async () => {
// User1 opens dial pad
await user1.poHomeChannel.voiceCalls.btnOpenDialpad.click();
await expect(user1.poHomeChannel.voiceCalls.btnOpenDialpad).toHaveAttribute('title', 'Close dialpad');

// User1 closes dial pad
await user1.poHomeChannel.voiceCalls.btnOpenDialpad.click();
await expect(user1.poHomeChannel.voiceCalls.btnOpenDialpad).toHaveAttribute('title', 'Open dialpad');
});

await test.step('should end the call from user1', async () => {
await user1.poHomeChannel.voiceCalls.btnEndCall('user2').click();
await expect(user1.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
await expect(user2.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
});
});

test('should transfer call to another user', async ({ browser, api }) => {
const [user1, user2] = sessions;

// Create user3 session only for this test
await api.post('/users.setStatus', { status: 'online', username: 'user3' });

const user3Context = await createAuxContext(browser, Users.user3);
const user3 = { page: user3Context.page, poHomeChannel: new HomeChannel(user3Context.page) };

await test.step('establish call between user1 and user2', async () => {
await user1.poHomeChannel.sidenav.openChat('user2');
await expect(user1.poHomeChannel.content.inputMessage).toBeVisible();
await user1.poHomeChannel.content.btnVoiceCall.click();
await user1.poHomeChannel.voiceCalls.initiateCall();
await user2.poHomeChannel.voiceCalls.acceptCall();
});

await test.step('user1 transfers call to user3', async () => {
await user1.poHomeChannel.voiceCalls.transferCall('user3');
await user1.poHomeChannel.toastMessage.waitForDisplay({ type: 'success' });
await expect(user1.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
await expect(user2.poHomeChannel.voiceCalls.callTransferWidget).toBeVisible();
});

await test.step('user3 receives transferred call', async () => {
await expect(user3.poHomeChannel.voiceCalls.incommingCallTransferWidget).toBeVisible();
await user3.poHomeChannel.voiceCalls.acceptCall();
});

await test.step('user3 ends the call', async () => {
await user3.poHomeChannel.voiceCalls.btnEndCall('user2').click();
await expect(user3.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
await expect(user2.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
});
await user3.page.close();
});

test('should decline incoming voice call', async () => {
const [user1, user2] = sessions;

await test.step('user1 initiates call to user2', async () => {
await user1.poHomeChannel.sidenav.openChat('user2');
await expect(user1.poHomeChannel.content.inputMessage).toBeVisible();
await user1.poHomeChannel.content.btnVoiceCall.click();
await user1.poHomeChannel.voiceCalls.initiateCall();
});

await test.step('user2 declines the call', async () => {
await user2.poHomeChannel.voiceCalls.btnRejectCall.click();
});

await test.step('Verify call widget disappears', async () => {
await expect(user1.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
await expect(user2.poHomeChannel.voiceCalls.callWidget).not.toBeVisible();
});
});
});
Loading