-
Notifications
You must be signed in to change notification settings - Fork 13.7k
test: Add automation tests for internal voice call flow #37374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
18be2d5
internal voice call
Harmeet221 28f822e
updated locators
Harmeet221 2f66d75
Merge branch 'develop' into voice-calls-ee
kodiakhq[bot] 4e5147e
Merge branch 'develop' into voice-calls-ee
Harmeet221 d90f02e
fix lint issue
Harmeet221 2e2bb22
fix type issue
Harmeet221 ec086f8
resolved toast messages error
Harmeet221 4ca0812
update beforeAll
Harmeet221 dd247e3
Merge branch 'develop' into voice-calls-ee
Harmeet221 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
102 changes: 102 additions & 0 deletions
102
apps/meteor/tests/e2e/page-objects/fragments/voice-calls.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| 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 api.post('/users.setStatus', { status: 'online', username: 'user1' }); | ||
| await api.post('/users.setStatus', { status: 'online', username: 'user2' }); | ||
|
gabriellsh marked this conversation as resolved.
Outdated
|
||
| }); | ||
|
|
||
| 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 expect(user1.poHomeChannel.toastSuccess).toBeVisible(); | ||
| 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(); | ||
| }); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.