Skip to content
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

Automate bug bash tests involving dominant speakers, pinning, and spotlight #5173

Merged
merged 29 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8dccbd1
Automate dominant speaker, pinning, and spotlight tests
mgamis-msft Jul 16, 2024
1ca459e
changelogs
mgamis-msft Jul 16, 2024
59737eb
lint fix
mgamis-msft Jul 16, 2024
14b6270
lint fix
mgamis-msft Jul 16, 2024
a76fa9f
remove test.only
mgamis-msft Sep 12, 2024
9c3fcb5
remove helper unused function
mgamis-msft Sep 12, 2024
5e7a76a
Update packages/react-components browser test snapshots
github-actions[bot] Sep 12, 2024
7b009bf
Update packages/react-components browser test snapshots
github-actions[bot] Sep 12, 2024
dbf4b6b
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Sep 12, 2024
6b76fcd
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Sep 12, 2024
3a753a6
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Sep 12, 2024
ec00a72
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Sep 12, 2024
79fa775
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Sep 12, 2024
e790432
Merge branch 'main' into mgamis/automate-bug-bash-tests-phase-1
mgamis-msft Oct 4, 2024
f6df5fb
Merge branch 'main' into mgamis/automate-bug-bash-tests-phase-1
mgamis-msft Dec 2, 2024
cb12357
Update packages/react-components browser test snapshots
github-actions[bot] Dec 2, 2024
f22cb31
Update packages/react-components browser test snapshots
github-actions[bot] Dec 2, 2024
beef4e7
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Dec 2, 2024
b6092f0
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Dec 2, 2024
87f8ebe
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Dec 2, 2024
ffcb31a
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Dec 2, 2024
f367cc3
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Dec 2, 2024
fc733a4
Update packages/react-components browser test snapshots
github-actions[bot] Dec 2, 2024
c2a3c94
lint fixes
mgamis-msft Dec 3, 2024
b1f955b
Merge branch 'main' into mgamis/automate-bug-bash-tests-phase-1
mgamis-msft Dec 3, 2024
3bb8667
Update packages/react-components browser test snapshots
github-actions[bot] Dec 3, 2024
4b1000c
Update packages/react-components browser test snapshots
github-actions[bot] Dec 3, 2024
dc9e631
Update packages/react-components browser test snapshots
github-actions[bot] Dec 3, 2024
2fe7bd3
Merge branch 'mgamis/automate-bug-bash-tests-phase-1' of https://gith…
github-actions[bot] Dec 3, 2024
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
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
151 changes: 151 additions & 0 deletions packages/react-components/tests/browser/VideoGallery.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// 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.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' };
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.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');
});
});
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these control bars because we are just testing in the components?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean the scroll bars, then no. I will fix the component or window size of the test config so that there are no xcrollbars.

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.
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('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('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');
});
});
Loading