Skip to content

Commit

Permalink
Automate bug bash tests involving dominant speakers, pinning, and spo…
Browse files Browse the repository at this point in the history
…tlight (#5173)

* Automate dominant speaker, pinning, and spotlight tests

* changelogs

* lint fix

* lint fix

* remove test.only

* remove helper unused function

* Update packages/react-components browser test snapshots

* Update packages/react-components browser test snapshots

* Update packages/react-composites CallComposite browser test snapshots

* Update packages/react-composites CallComposite browser test snapshots

* Update packages/react-components browser test snapshots

* Update packages/react-components browser test snapshots

* Update packages/react-composites CallComposite browser test snapshots

* Update packages/react-composites CallComposite browser test snapshots

* Update packages/react-components browser test snapshots

* lint fixes

* Update packages/react-components browser test snapshots

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
mgamis-msft and github-actions[bot] authored Dec 3, 2024
1 parent 8a18570 commit a4f3225
Show file tree
Hide file tree
Showing 108 changed files with 345 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "improvement",
"workstream": "Automated tests",
"comment": "Automate dominant speaker, pinning, and spotlight tests",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "improvement",
"workstream": "Automated tests",
"comment": "Automate dominant speaker, pinning, and spotlight tests",
"packageName": "@azure/communication-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
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
150 changes: 150 additions & 0 deletions packages/react-components/tests/browser/VideoGallery.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// 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('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' };
const 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[1].videoStream = { isAvailable: true };
remoteParticipants[2].videoStream = { isAvailable: true };
remoteParticipants[4].videoStream = { isAvailable: true };
remoteParticipants[6].videoStream = { isAvailable: true };
remoteParticipants[8].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' };
const 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[7].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');
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
176 changes: 176 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,176 @@
// 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('PIN - Pinning tests', async () => {
test('Pin and unpin remote participants via video tile', async ({ page, serverUrl }, testInfo) => {
const participants = displayNames.map((name) => defaultMockRemoteParticipant(name));
if (participants[1]) {
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('Pin and unpin remote participants via participant item', async ({ page, serverUrl }, testInfo) => {
const participants = displayNames.map((name) => defaultMockRemoteParticipant(name));
if (participants[1]) {
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));
if (participants[1]) {
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 a4f3225

Please sign in to comment.