Skip to content

Commit

Permalink
Automate dominant speaker, pinning, and spotlight tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mgamis-msft committed Sep 12, 2024
1 parent 0d73166 commit 8dccbd1
Show file tree
Hide file tree
Showing 3 changed files with 331 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export const _DrawerMenu = (props: _DrawerMenuProps): JSX.Element => {
styles={props.styles?.drawerSurfaceStyles}
onLightDismiss={props.onLightDismiss}
heading={props.heading}
data-ui-id="drawer-menu"
>
<Stack styles={props.styles} role="menu" data-ui-id="drawer-menu">
{menuItemsToRender?.slice(0, 1).map((item) =>
Expand Down
160 changes: 160 additions & 0 deletions packages/react-components/tests/browser/VideoGallery.spec.tsx
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 packages/react-composites/tests/browser/call/hermetic/Pinning.test.ts
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');
});
});

0 comments on commit 8dccbd1

Please sign in to comment.