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

Together mode e2e testcases #5658

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -120,7 +120,7 @@ export class _MockCallAdapter implements CallAdapter {
}
/* @conditional-compile-remove(together-mode) */
setTogetherModeSceneSize(width: number, height: number): void {
throw Error(`Setting Together Mode scene to width ${width} and height ${height} is not implemented`);
return;
}
disposeStreamView(): Promise<void> {
return Promise.resolve();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class MockCallAdapter implements CallAdapter {
}
/* @conditional-compile-remove(together-mode) */
setTogetherModeSceneSize(width: number, height: number): void {
throw Error(`Setting Together Mode width ${width} and height: ${height} not implemented`);
return;
}
/* @conditional-compile-remove(together-mode) */
disposeTogetherModeStreamView(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import {
// addScreenshareStream,
addTogetherModeStream,
addVideoStream,
buildUrlWithMockAdapter,
defaultMockCallAdapterState,
defaultMockRemoteParticipant,
test
} from './fixture';
import { expect } from '@playwright/test';
import {
dataUiId,
// dragToRight,
// existsOnPage,
isTestProfileMobile,
pageClick,
stableScreenshot,
waitForSelector
} from '../../common/utils';
import { IDS } from '../../common/constants';
import { CallKind } from '@azure/communication-calling';
// import type { MockCallState } from '../../../common';

/* @conditional-compile-remove(together-mode) */
test.describe('Confirm Start Together layout view option ', async () => {
test('Confirm together mode is not shown in ACS Call', async ({ page, serverUrl }, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
// Remote Participants is ACS Identity
const vasily = defaultMockRemoteParticipant('Vasily');
const paul = defaultMockRemoteParticipant('Paul');
const participants = [vasily, paul];
addVideoStream(vasily, true);
addVideoStream(paul, true);
// Local Participant is ACS Identity
const initialState = defaultMockCallAdapterState(participants);
if (initialState.call) {
initialState.call.togetherMode.isActive = true;
}
await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-option-hidden-in-acs-call.png');
});

test('Confirm together mode is enabled for Teams Call', async ({ page, serverUrl }, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
// Remote Participant is Teams Identity
const vasily = defaultMockRemoteParticipant('Vasily', true);
const paul = defaultMockRemoteParticipant('Paul', true);
const participants = [vasily, paul];
addVideoStream(vasily, true);
addVideoStream(paul, true);
// Local Participant is Teams Identity
const initialState = defaultMockCallAdapterState(participants);
initialState.userId = { kind: 'microsoftTeamsUser', microsoftTeamsUserId: `8:orgid:localUser` };
initialState.isTeamsCall = true;
if (initialState.call) {
initialState.call.togetherMode.isActive = true;
initialState.call.kind = 'TeamsCall' as CallKind;
}
await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-option-in-teams-call.png');
});

test('Confirm together mode is enabled for Teams Meeting', async ({ page, serverUrl }, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
// Remote Participant is Teams Identity
const vasily = defaultMockRemoteParticipant('Vasily', true);
const paul = defaultMockRemoteParticipant('Paul', true);
const participants = [vasily, paul];
addVideoStream(vasily, true);
addVideoStream(paul, true);
// Local Participant is Teams Identity
const initialState = defaultMockCallAdapterState(participants);
initialState.userId = { kind: 'microsoftTeamsUser', microsoftTeamsUserId: `8:orgid:localUser` };
initialState.isTeamsMeeting = true;
if (initialState.call) {
initialState.call.togetherMode.isActive = true;
initialState.call.kind = 'TeamsCall' as CallKind;
}
await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-option-in-teams-meeting.png');
});
});

test.describe('Confirm Together Mode Stream signaling events', async () => {
test.only('Confirm raiseHand icon and display Name in together mode layout', async ({
page,
serverUrl
}, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
const paul = defaultMockRemoteParticipant('Paul Bridges', true);
const vasily = defaultMockRemoteParticipant('Vasily');
const participants = [paul, vasily];
addVideoStream(vasily, true);
vasily.raisedHand = { raisedHandOrderPosition: 1 };
addVideoStream(vasily, true);
const initialState = defaultMockCallAdapterState(participants);

if (initialState.call?.togetherMode) {
initialState.call.togetherMode.isActive = true;
addTogetherModeStream(initialState.call.togetherMode.streams, true);
initialState.call.togetherMode.seatingPositions = {
'8:acs:Vasily-id': { left: 0, top: 0, width: 200, height: 200 }
};
}
initialState.isTeamsCall = true;
if (initialState.call) {
initialState.call.kind = 'TeamsCall' as CallKind;
}
await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

const id = `together-mode-participant-8:acs:Vasily-id`;
await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
await page.locator('button:has-text("Together mode")').click();
await waitForSelector(page, dataUiId(IDS.togetherModeStream));
await waitForSelector(page, dataUiId(id));
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-raisehand-icon.png');
});

test.only('Confirm spotlight icon and display Name in together mode layout', async ({
page,
serverUrl
}, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
const paul = defaultMockRemoteParticipant('Paul Bridges', true);
const vasily = defaultMockRemoteParticipant('Vasily');
const participants = [paul, vasily];
addVideoStream(vasily, true);
vasily.spotlight = { spotlightedOrderPosition: 1 };
addVideoStream(vasily, true);
const initialState = defaultMockCallAdapterState(participants);

if (initialState.call?.togetherMode) {
initialState.call.togetherMode.isActive = true;
addTogetherModeStream(initialState.call.togetherMode.streams, true);
initialState.call.togetherMode.seatingPositions = {
'8:acs:Vasily-id': { left: 0, top: 0, width: 200, height: 200 }
};
}
initialState.isTeamsCall = true;
if (initialState.call) {
initialState.call.kind = 'TeamsCall' as CallKind;
}
await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

const id = `together-mode-participant-8:acs:Vasily-id`;
await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
await page.locator('button:has-text("Together mode")').click();
await waitForSelector(page, dataUiId(IDS.togetherModeStream));
await waitForSelector(page, dataUiId(id));
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-spotlight-icon.png');
});

test.only('Confirm mute icon and display Name in together mode layout', async ({ page, serverUrl }, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
const paul = defaultMockRemoteParticipant('Paul Bridges', true);
const vasily = defaultMockRemoteParticipant('Vasily');
const participants = [paul, vasily];
addVideoStream(vasily, true);
vasily.spotlight = { spotlightedOrderPosition: 1 };
vasily.isMuted = true;
addVideoStream(vasily, true);
const initialState = defaultMockCallAdapterState(participants);

if (initialState.call?.togetherMode) {
initialState.call.togetherMode.isActive = true;
addTogetherModeStream(initialState.call.togetherMode.streams, true);
initialState.call.togetherMode.seatingPositions = {
'8:acs:Vasily-id': { left: 0, top: 0, width: 200, height: 200 }
};
}
initialState.isTeamsCall = true;
if (initialState.call) {
initialState.call.kind = 'TeamsCall' as CallKind;
}
await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

const id = `together-mode-participant-8:acs:Vasily-id`;
await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
await page.locator('button:has-text("Together mode")').click();
await waitForSelector(page, dataUiId(IDS.togetherModeStream));
await waitForSelector(page, dataUiId(id));
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-mute-icon.png');
});

test.only('Confirm only icons show when seating width is 100px in together mode layout', async ({
page,
serverUrl
}, testInfo) => {
test.skip(isTestProfileMobile(testInfo));
const paul = defaultMockRemoteParticipant('Paul Bridges', true);
const vasily = defaultMockRemoteParticipant('Vasily');
const participants = [paul, vasily];
addVideoStream(vasily, true);
vasily.spotlight = { spotlightedOrderPosition: 1 };
vasily.isMuted = true;
vasily.raisedHand = { raisedHandOrderPosition: 1 };
addVideoStream(vasily, true);
const initialState = defaultMockCallAdapterState(participants);

if (initialState.call?.togetherMode) {
initialState.call.togetherMode.isActive = true;
addTogetherModeStream(initialState.call.togetherMode.streams, true);
initialState.call.togetherMode.seatingPositions = {
'8:acs:Vasily-id': { left: 0, top: 0, width: 100, height: 100 }
};
}
initialState.isTeamsCall = true;
if (initialState.call) {
initialState.call.kind = 'TeamsCall' as CallKind;
}

await page.goto(
buildUrlWithMockAdapter(serverUrl, initialState, {
newControlBarExperience: 'true'
})
);

const id = `together-mode-participant-8:acs:Vasily-id`;
await waitForSelector(page, dataUiId(IDS.moreButton));
await pageClick(page, dataUiId(IDS.moreButton));
await page.locator('button:has-text("View")').click();
await page.locator('button:has-text("Together mode")').click();
await waitForSelector(page, dataUiId(IDS.togetherModeStream));
await waitForSelector(page, dataUiId(id));
expect(await stableScreenshot(page)).toMatchSnapshot('together-mode-view-icons-only.png');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import type {
} from '../../../common';
import type { CallKind, DominantSpeakersInfo, ParticipantRole } from '@azure/communication-calling';
import type { ParticipantCapabilities } from '@azure/communication-calling';
import { CallState, CapabilitiesFeatureState } from '@internal/calling-stateful-client';
import {
CallFeatureStreamState,
CallState,
CapabilitiesFeatureState,
TogetherModeStreamsState
} from '@internal/calling-stateful-client';

const SERVER_URL = 'http://localhost';
const APP_DIR = path.join(__dirname, '../../../app/call');
Expand Down Expand Up @@ -56,7 +61,8 @@ export function defaultMockCallAdapterState(
role?: ParticipantRole,
isRoomsCall?: boolean,
callEndReasonSubCode?: number,
isReactionCapability?: boolean
isReactionCapability?: boolean,
isTeamsUser?: boolean
): MockCallAdapterState {
const remoteParticipants: Record<string, MockRemoteParticipantState> = {};
participants?.forEach((p) => {
Expand Down Expand Up @@ -94,7 +100,7 @@ export function defaultMockCallAdapterState(
/* @conditional-compile-remove(together-mode) */
togetherMode: { isActive: false, streams: {}, seatingPositions: {} },
pptLive: { isActive: false },
role: role ?? 'Unknown',
role: 'Presenter',
dominantSpeakers: dominantSpeakers,
totalParticipantCount:
Object.values(remoteParticipants).length > 0 ? Object.values(remoteParticipants).length + 1 : undefined,
Expand Down Expand Up @@ -133,7 +139,9 @@ export function defaultMockCallAdapterState(
}
}
: undefined,
userId: { kind: 'communicationUser', communicationUserId: '1' },
userId: isTeamsUser
? { kind: 'microsoftTeamsUser', microsoftTeamsUserId: '8:orgid:localUser' }
: { kind: 'communicationUser', communicationUserId: '8:orgid:localUser' },
devices: {
isSpeakerSelectionAvailable: true,
selectedCamera: { id: 'camera1', name: '1st Camera', deviceType: 'UsbCamera' },
Expand Down Expand Up @@ -166,9 +174,14 @@ export function defaultMockCallAdapterState(
*
* Use this to add participants to state created via {@link defaultCallAdapterState}.
*/
export function defaultMockRemoteParticipant(displayName?: string): MockRemoteParticipantState {
export function defaultMockRemoteParticipant(
displayName?: string,
isTeamsUser: boolean = false
): MockRemoteParticipantState {
return {
identifier: { kind: 'communicationUser', communicationUserId: `8:acs:${displayName}-id` },
identifier: isTeamsUser
? { kind: 'microsoftTeamsUser', microsoftTeamsUserId: `8:orgid:${displayName}-id` }
: { kind: 'communicationUser', communicationUserId: `8:acs:${displayName}-id` },
state: 'Connected',
videoStreams: {
1: {
Expand Down Expand Up @@ -262,6 +275,32 @@ export function addScreenshareStream(
addDummyView(streams[0], isReceiving, scalingMode);
}

/**
* Add a screenshare stream to {@link MockRemoteParticipantState}.
*
* Use to add video to participant created via {@link defaultMockRemoteParticipant}.
*/
export function addTogetherModeStream(
togetherModeStreamState: TogetherModeStreamsState,
isReceiving: boolean,
scalingMode?: 'Stretch' | 'Crop' | 'Fit'
): void {
const togetherModeStream =
togetherModeStreamState.mainVideoStream ||
({
feature: 'togetherMode',
mediaStreamType: 'Video',
isAvailable: true,
isReceiving: true
} as CallFeatureStreamState);
// if (!togetherModeStream) {
// throw new Error(`Expected togetherMode Stream to be active`);
// }
if (togetherModeStreamState.mainVideoStream) {
addDummyView(togetherModeStreamState.mainVideoStream, isReceiving, scalingMode);
}
}

/**
* Add a dummy view to a stream that will be replaced by an actual {@link HTMLElement} by the test app.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export const IDS = {
reactionButtonSubMenu: 'reaction-sub-menu',
reactionMobileDrawerMenuItem: 'reaction-mobile-drawer-menu-item',
cameraButton: 'call-composite-camera-button',
microphoneButton: 'call-composite-microphone-button'
microphoneButton: 'call-composite-microphone-button',
togetherModeStream: 'together-mode-layout'
};

export const spokenLanguageStrings = [
Expand Down
Loading