-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automate dominant speaker, pinning, and spotlight tests
- Loading branch information
1 parent
0d73166
commit 8dccbd1
Showing
3 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
160 changes: 160 additions & 0 deletions
160
packages/react-components/tests/browser/VideoGallery.spec.tsx
This file contains 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,160 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import React from 'react'; | ||
import { test, expect } from '@playwright/experimental-ct-react'; | ||
import { VideoGallery } from '../../src/components/VideoGallery'; | ||
import { VideoGalleryLocalParticipant, VideoGalleryRemoteParticipant } from '../../src'; | ||
import { Stack } from '@fluentui/react'; | ||
|
||
test.describe.only('VGL - VideoGallery tests', () => { | ||
test.beforeEach(async ({ page }) => { | ||
await page.evaluate(() => document.fonts.ready); | ||
}); | ||
|
||
test('VideoGallery with only audio participants and dominant speakers', async ({ mount }) => { | ||
const localParticipant: VideoGalleryLocalParticipant = { userId: 'test' }; | ||
let remoteParticipants: VideoGalleryRemoteParticipant[] = Array.from({ length: 10 }, (_, i) => i + 1).map((i) => ({ | ||
userId: `${i}`, | ||
displayName: `${i}` | ||
})); | ||
const component = await mount( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-1-1-videogallery-with-audio-only-before-dominant-speakers.png'); | ||
await component.update( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
dominantSpeakers={['10']} | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-1-2-videogallery-with-audio-only-after-dominant-speakers.png'); | ||
}); | ||
|
||
test('VideoGallery with video participants and dominant speakers', async ({ mount }) => { | ||
const localParticipant: VideoGalleryLocalParticipant = { userId: 'test' }; | ||
const remoteParticipants: VideoGalleryRemoteParticipant[] = Array.from({ length: 10 }, (_, i) => i + 1).map( | ||
(i) => ({ | ||
userId: `${i}`, | ||
displayName: `${i}` | ||
}) | ||
); | ||
// Assign video stream to some participants | ||
remoteParticipants.find((p) => p.userId === '2')!.videoStream = { isAvailable: true }; | ||
remoteParticipants.find((p) => p.userId === '3')!.videoStream = { isAvailable: true }; | ||
remoteParticipants.find((p) => p.userId === '5')!.videoStream = { isAvailable: true }; | ||
remoteParticipants.find((p) => p.userId === '7')!.videoStream = { isAvailable: true }; | ||
remoteParticipants.find((p) => p.userId === '9')!.videoStream = { isAvailable: true }; | ||
|
||
const component = await mount( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-2-1-videogallery-with-some-video-before-dominant-speakers.png'); | ||
await component.update( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
dominantSpeakers={['9']} | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-2-2-videogallery--with-some-video-after-dominant-speakers.png'); | ||
}); | ||
|
||
test('VideoGallery with screen share on and dominant speakers', async ({ mount }) => { | ||
const localParticipant: VideoGalleryLocalParticipant = { userId: 'test' }; | ||
let remoteParticipants: VideoGalleryRemoteParticipant[] = Array.from({ length: 10 }, (_, i) => i + 1).map((i) => ({ | ||
userId: `${i}`, | ||
displayName: `${i}` | ||
})); | ||
remoteParticipants[5].isScreenSharingOn = true; | ||
remoteParticipants[5].screenShareStream = { isAvailable: true }; | ||
const component = await mount( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-3-1-videogallery-with-screen-share-before-dominant-speakers.png'); | ||
await component.update( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
dominantSpeakers={['10']} | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-3-2-videogallery-with-screen-share-after-dominant-speakers.png'); | ||
}); | ||
|
||
test('VideoGallery spotlight participant test', async ({ mount }) => { | ||
const localParticipant: VideoGalleryLocalParticipant = { userId: 'test' }; | ||
const remoteParticipants: VideoGalleryRemoteParticipant[] = Array.from({ length: 10 }, (_, i) => i + 1).map( | ||
(i) => ({ userId: `${i}`, displayName: `${i}` }) | ||
); | ||
const screenSharingParticipant: VideoGalleryRemoteParticipant = { | ||
userId: '11', | ||
displayName: '11' | ||
}; | ||
remoteParticipants.push(screenSharingParticipant); | ||
const component = await mount( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-4-1-videogallery-before-spotlight.png'); | ||
|
||
remoteParticipants.find((p) => p.userId === '8')!.spotlight = { spotlightedOrderPosition: 1 }; | ||
component.update( | ||
<Stack styles={{ root: { width: '100vw', height: '100vh' } }}> | ||
<VideoGallery | ||
localParticipant={localParticipant} | ||
remoteParticipants={remoteParticipants} | ||
layout="floatingLocalVideo" | ||
spotlightedParticipants={['8']} | ||
/> | ||
</Stack> | ||
); | ||
await expect(component).toHaveScreenshot('VGL-4-2-videogallery-after-spotlight.png'); | ||
}); | ||
}); | ||
|
||
const createMockVideoStream = (): HTMLElement => { | ||
const mockVideoElement = document.createElement('div'); | ||
mockVideoElement.style.width = '100%'; | ||
mockVideoElement.style.height = '100%'; | ||
mockVideoElement.style.textAlign = 'center'; | ||
const imageElement = document.createElement('img'); | ||
imageElement.src = 'images/screenshare-example.png'; | ||
imageElement.style.maxWidth = decodeURIComponent('100%25'); | ||
imageElement.style.maxHeight = decodeURIComponent('100%25'); | ||
mockVideoElement.appendChild(imageElement); | ||
return mockVideoElement as HTMLElement; | ||
}; |
170 changes: 170 additions & 0 deletions
170
packages/react-composites/tests/browser/call/hermetic/Pinning.test.ts
This file contains 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,170 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { | ||
addVideoStream, | ||
buildUrlWithMockAdapter, | ||
defaultMockCallAdapterState, | ||
defaultMockRemoteParticipant, | ||
test | ||
} from './fixture'; | ||
import { expect } from '@playwright/test'; | ||
import { dataUiId, waitForSelector, stableScreenshot, isTestProfileMobile, pageClick } from '../../common/utils'; | ||
import { IDS } from '../../common/constants'; | ||
|
||
const displayNames = [ | ||
'Tony Hawk', | ||
'Marie Curie', | ||
'Gal Gadot', | ||
'Margaret Atwood', | ||
'Kobe Bryant', | ||
"Conan O'Brien", | ||
'Paul Bridges', | ||
'Fiona Harper', | ||
'Reina Takizawa', | ||
'Vasily Podkolzin', | ||
'Antonie van Leeuwenhoek', | ||
'Luciana Rodriguez' | ||
]; | ||
|
||
test.describe.only('PIN - Pinning tests', async () => { | ||
test('Pin and unpin remote participants via video tile', async ({ page, serverUrl }, testInfo) => { | ||
const participants = displayNames.map((name) => defaultMockRemoteParticipant(name)); | ||
addVideoStream(participants[1], true); | ||
const initialState = defaultMockCallAdapterState(participants); | ||
|
||
await page.goto(buildUrlWithMockAdapter(serverUrl, initialState, { newControlBarExperience: 'true' })); | ||
const videoGallery = await waitForSelector(page, dataUiId(IDS.videoGallery)); | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-1-1-pin-video-tile-before.png'); | ||
|
||
const isMobile = isTestProfileMobile(testInfo); | ||
if (isMobile) { | ||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=2`); | ||
await videoTile.dispatchEvent('touchstart'); | ||
await pageClick(page, 'div[role="menu"] >> text=Pin for me'); | ||
} else { | ||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=2`); | ||
await videoTile.hover(); | ||
const moreButton = await videoTile.waitForSelector(dataUiId(IDS.videoTileMoreOptionsButton)); | ||
await moreButton.hover(); | ||
await moreButton.click(); | ||
await waitForSelector(page, dataUiId('video-tile-pin-participant-button')); | ||
await pageClick(page, dataUiId('video-tile-pin-participant-button')); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-1-2-pin-video-tile-after.png'); | ||
|
||
if (isMobile) { | ||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=1`); | ||
await videoTile.dispatchEvent('touchstart'); | ||
await page.waitForSelector(dataUiId('drawer-menu')); | ||
} else { | ||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=1`); | ||
await videoTile.hover(); | ||
const moreButton = await videoTile?.waitForSelector(dataUiId(IDS.videoTileMoreOptionsButton)); | ||
await moreButton?.hover(); | ||
await moreButton?.click(); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-1-3-unpin-video-tile-before.png'); | ||
|
||
if (isMobile) { | ||
await pageClick(page, 'div[role="menu"] >> text=Unpin'); | ||
} else { | ||
await pageClick(page, dataUiId('video-tile-unpin-participant-button')); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-1-4-unpin-video-tile-after.png'); | ||
}); | ||
|
||
test.only('Pin and unpin remote participants via participant item', async ({ page, serverUrl }, testInfo) => { | ||
const participants = displayNames.map((name) => defaultMockRemoteParticipant(name)); | ||
addVideoStream(participants[1], true); | ||
const initialState = defaultMockCallAdapterState(participants); | ||
|
||
await page.goto(buildUrlWithMockAdapter(serverUrl, initialState, { newControlBarExperience: 'true' })); | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-2-1-pin-participant-item-before.png'); | ||
|
||
const isMobile = isTestProfileMobile(testInfo); | ||
if (isMobile) { | ||
await pageClick(page, dataUiId('common-call-composite-more-button')); | ||
const drawerPeopleMenuDiv = await page.$('div[role="menu"] >> text=People'); | ||
await drawerPeopleMenuDiv?.click(); | ||
await pageClick(page, dataUiId('participant-item')); | ||
await pageClick(page, 'div[role="menu"] >> text=Pin for me'); | ||
await pageClick(page, 'button[aria-label="Back"]'); | ||
} else { | ||
await pageClick(page, dataUiId('common-call-composite-people-button')); | ||
const participantItem = await page.waitForSelector(dataUiId('participant-item')); | ||
await participantItem.hover(); | ||
await pageClick(page, dataUiId(IDS.participantItemMenuButton)); | ||
await pageClick(page, dataUiId('participant-item-pin-participant-button')); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-2-2-pin-participant-item-after.png'); | ||
|
||
if (isMobile) { | ||
await pageClick(page, dataUiId('common-call-composite-more-button')); | ||
const drawerPeopleMenuDiv = await page.$('div[role="menu"] >> text=People'); | ||
await drawerPeopleMenuDiv?.click(); | ||
await pageClick(page, dataUiId('participant-item')); | ||
} else { | ||
const participantItem = await page.waitForSelector(dataUiId('participant-item')); | ||
await participantItem.hover(); | ||
await pageClick(page, dataUiId(IDS.participantItemMenuButton)); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-2-3-unpin-participant-item-before.png'); | ||
|
||
if (isMobile) { | ||
await pageClick(page, 'div[role="menu"] >> text=Unpin'); | ||
await pageClick(page, 'button[aria-label="Back"]'); | ||
} else { | ||
await pageClick(page, dataUiId('participant-item-unpin-participant-button')); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-2-4-unpin-participant-item-after.png'); | ||
}); | ||
|
||
test('Pin max remote participants', async ({ page, serverUrl }, testInfo) => { | ||
const participants = displayNames.map((name) => defaultMockRemoteParticipant(name)); | ||
addVideoStream(participants[1], true); | ||
const initialState = defaultMockCallAdapterState(participants); | ||
|
||
await page.goto(buildUrlWithMockAdapter(serverUrl, initialState, { newControlBarExperience: 'true' })); | ||
const videoGallery = await waitForSelector(page, dataUiId(IDS.videoGallery)); | ||
|
||
const isMobile = isTestProfileMobile(testInfo); | ||
if (isMobile) { | ||
for (let i = 0; i < 4; i++) { | ||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=-1`); | ||
await videoTile.dispatchEvent('touchstart'); | ||
await pageClick(page, 'div[role="menu"] >> text=Pin for me'); | ||
} | ||
|
||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=-1`); | ||
await videoTile.dispatchEvent('touchstart'); | ||
await page.waitForSelector(dataUiId('drawer-menu')); | ||
} else { | ||
for (let i = 0; i < 4; i++) { | ||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=-1`); | ||
await videoTile.hover(); | ||
const moreButton = await videoTile.waitForSelector(dataUiId(IDS.videoTileMoreOptionsButton)); | ||
await moreButton.hover(); | ||
await moreButton.click(); | ||
await waitForSelector(page, dataUiId('video-tile-pin-participant-button')); | ||
await pageClick(page, dataUiId('video-tile-pin-participant-button')); | ||
} | ||
|
||
const videoTile = await videoGallery.waitForSelector(dataUiId(IDS.videoTile) + ` >> nth=-1`); | ||
await videoTile.hover(); | ||
const moreButton = await videoTile?.waitForSelector(dataUiId(IDS.videoTileMoreOptionsButton)); | ||
await moreButton?.hover(); | ||
await moreButton?.click(); | ||
} | ||
|
||
expect(await stableScreenshot(page)).toMatchSnapshot('PIN-3-1-pin-max-tiles.png'); | ||
}); | ||
}); |