mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
ref: Refactor tests (#16399)
* ref: Inline enterTileView.
* ref: Refactor tileView, remove tileView.LastN.
The "last n" cases are not related to tile view and are covered in lastN.spec.ts.
* ref: Remove redundant "skipInMeetingChecks: true".
skipInMeetingChecks is only used in ensureTwoParticipants, ensureThreeParticipants and ensureFourParticipants.
* ref: Move recording test to jaas/, more refactoring.
* ref: Rename and document switchToAPI() and switchInPage().
* ref: Move the tileView into 2way (temp).
This commit is contained in:
@@ -27,7 +27,6 @@ import Toolbar from '../pageobjects/Toolbar';
|
||||
import VideoQualityDialog from '../pageobjects/VideoQualityDialog';
|
||||
import Visitors from '../pageobjects/Visitors';
|
||||
|
||||
import { config as testsConfig } from './TestsConfig';
|
||||
import { LOG_PREFIX, logInfo } from './browserLogger';
|
||||
import { IToken } from './token';
|
||||
import { IParticipantJoinOptions, IParticipantOptions } from './types';
|
||||
@@ -219,8 +218,8 @@ export class Participant {
|
||||
// @ts-ignore
|
||||
url = `${this.driver.iframePageBase}${url}&domain="${baseUrl.host}"&room="${options.roomName}"`;
|
||||
|
||||
if (testsConfig.iframe.tenant) {
|
||||
url = `${url}&tenant="${testsConfig.iframe.tenant}"`;
|
||||
if (options.tenant) {
|
||||
url = `${url}&tenant="${options.tenant}"`;
|
||||
} else if (baseUrl.pathname.length > 1) {
|
||||
// remove leading slash
|
||||
url = `${url}&tenant="${baseUrl.pathname.substring(1)}"`;
|
||||
@@ -235,8 +234,9 @@ export class Participant {
|
||||
// drop the leading '/' so we can use the tenant if any
|
||||
url = url.startsWith('/') ? url.substring(1) : url;
|
||||
|
||||
if (options.forceTenant) {
|
||||
url = `/${options.forceTenant}/${url}`;
|
||||
if (options.tenant && !this._iFrameApi) {
|
||||
// For the iFrame API the tenant is passed in a different way.
|
||||
url = `/${options.tenant}/${url}`;
|
||||
}
|
||||
|
||||
await this.driver.url(url);
|
||||
@@ -244,9 +244,7 @@ export class Participant {
|
||||
await this.waitForPageToLoad();
|
||||
|
||||
if (this._iFrameApi) {
|
||||
const mainFrame = this.driver.$('iframe');
|
||||
|
||||
await this.driver.switchFrame(mainFrame);
|
||||
await this.switchToIFrame();
|
||||
}
|
||||
|
||||
if (!options.skipWaitToJoin) {
|
||||
@@ -319,7 +317,7 @@ export class Participant {
|
||||
/**
|
||||
* Waits for the tile view to display.
|
||||
*/
|
||||
async waitForTileViewDisplay(reverse = false) {
|
||||
async waitForTileViewDisplayed(reverse = false) {
|
||||
await this.driver.$('//div[@id="videoconference_page" and contains(@class, "tile-view")]').waitForDisplayed({
|
||||
reverse,
|
||||
timeout: 10_000,
|
||||
@@ -626,19 +624,23 @@ export class Participant {
|
||||
|
||||
|
||||
/**
|
||||
* Switches to the iframe API context
|
||||
* Switches to the main frame context (outside the iFrame; where the Jitsi Meet iFrame API is available).
|
||||
*
|
||||
* If this Participant was initialized with iFrameApi=false this has no effect, as there aren't any other contexts.
|
||||
*/
|
||||
async switchToAPI() {
|
||||
async switchToMainFrame() {
|
||||
await this.driver.switchFrame(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches to the meeting page context.
|
||||
* Switches to the iFrame context (inside the iFrame; where the Jitsi Meet application runs).
|
||||
*
|
||||
* If this Participant was initialized with iFrameApi=false this will result in an error.
|
||||
*/
|
||||
switchInPage() {
|
||||
const mainFrame = this.driver.$('iframe');
|
||||
async switchToIFrame() {
|
||||
const iframe = this.driver.$('iframe');
|
||||
|
||||
return this.driver.switchFrame(mainFrame);
|
||||
await this.driver.switchFrame(iframe);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -187,7 +187,7 @@ async function joinParticipant( // eslint-disable-line max-params
|
||||
|
||||
if (p) {
|
||||
if (ctx.testProperties.useIFrameApi) {
|
||||
await p.switchInPage();
|
||||
await p.switchToIFrame();
|
||||
}
|
||||
|
||||
if (await p.isInMuc()) {
|
||||
@@ -196,7 +196,7 @@ async function joinParticipant( // eslint-disable-line max-params
|
||||
|
||||
if (ctx.testProperties.useIFrameApi) {
|
||||
// when loading url make sure we are on the top page context or strange errors may occur
|
||||
await p.switchToAPI();
|
||||
await p.switchToMainFrame();
|
||||
}
|
||||
|
||||
// Change the page so we can reload same url if we need to, base.html is supposed to be empty or close to empty
|
||||
@@ -209,16 +209,16 @@ async function joinParticipant( // eslint-disable-line max-params
|
||||
// @ts-ignore
|
||||
ctx[participantOptions.name] = newParticipant;
|
||||
|
||||
let forceTenant = options?.forceTenant;
|
||||
let tenant = options?.tenant;
|
||||
|
||||
if (options?.preferGenerateToken && !ctx.testProperties.useIFrameApi
|
||||
&& config.iframe.usesJaas && config.iframe.tenant) {
|
||||
forceTenant = config.iframe.tenant;
|
||||
tenant = config.iframe.tenant;
|
||||
}
|
||||
|
||||
return await newParticipant.joinConference({
|
||||
...options,
|
||||
forceTenant,
|
||||
tenant: tenant,
|
||||
roomName: options?.roomName || ctx.roomName,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,11 +53,6 @@ export type IParticipantJoinOptions = {
|
||||
*/
|
||||
configOverwrite?: IConfig;
|
||||
|
||||
/**
|
||||
* An optional tenant to use. If provided the URL is prepended with /$forceTenant
|
||||
*/
|
||||
forceTenant?: string;
|
||||
|
||||
/** The name of the room to join */
|
||||
roomName: string;
|
||||
|
||||
@@ -71,6 +66,11 @@ export type IParticipantJoinOptions = {
|
||||
* based on the logic of the test.
|
||||
*/
|
||||
skipWaitToJoin?: boolean;
|
||||
|
||||
/**
|
||||
* An optional tenant to use. If provided it overrides the default.
|
||||
*/
|
||||
tenant?: string;
|
||||
};
|
||||
|
||||
export type IJoinOptions = {
|
||||
@@ -80,11 +80,6 @@ export type IJoinOptions = {
|
||||
*/
|
||||
configOverwrite?: IConfig;
|
||||
|
||||
/**
|
||||
* An optional tenant to use. If provided the URL is prepended with /$forceTenant
|
||||
*/
|
||||
forceTenant?: string;
|
||||
|
||||
/**
|
||||
* When joining the first participant and jwt singing material is available and a provided token
|
||||
* is available, prefer generating a new token for the first participant.
|
||||
@@ -116,6 +111,11 @@ export type IJoinOptions = {
|
||||
*/
|
||||
skipWaitToJoin?: boolean;
|
||||
|
||||
/**
|
||||
* An optional tenant to use. If provided the default tenant is changed to it.
|
||||
*/
|
||||
tenant?: string;
|
||||
|
||||
/**
|
||||
* Options used when generating a token.
|
||||
*/
|
||||
|
||||
@@ -21,8 +21,7 @@ describe('Lock Room', () => {
|
||||
it('enter participant in locked room', async () => {
|
||||
// first enter wrong pin then correct one
|
||||
await joinSecondParticipant({
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
skipWaitToJoin: true
|
||||
});
|
||||
|
||||
const { p2 } = ctx;
|
||||
@@ -98,8 +97,7 @@ describe('Lock Room', () => {
|
||||
await ctx.p2.hangup();
|
||||
await participant1LockRoom();
|
||||
await joinSecondParticipant({
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
skipWaitToJoin: true
|
||||
});
|
||||
|
||||
const { p2 } = ctx;
|
||||
|
||||
@@ -10,8 +10,7 @@ describe('PreJoin', () => {
|
||||
requireDisplayName: true
|
||||
},
|
||||
skipDisplayName: true,
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
skipWaitToJoin: true
|
||||
});
|
||||
|
||||
const p1PreJoinScreen = ctx.p1.getPreJoinScreen();
|
||||
@@ -38,8 +37,7 @@ describe('PreJoin', () => {
|
||||
}
|
||||
},
|
||||
skipDisplayName: true,
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
skipWaitToJoin: true
|
||||
});
|
||||
|
||||
const p1PreJoinScreen = ctx.p1.getPreJoinScreen();
|
||||
@@ -61,8 +59,7 @@ describe('PreJoin', () => {
|
||||
}
|
||||
},
|
||||
skipDisplayName: true,
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
skipWaitToJoin: true
|
||||
});
|
||||
|
||||
const { p1 } = ctx;
|
||||
@@ -109,8 +106,7 @@ describe('PreJoin', () => {
|
||||
}
|
||||
},
|
||||
skipDisplayName: true,
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
skipWaitToJoin: true
|
||||
});
|
||||
|
||||
const p1PreJoinScreen = ctx.p2.getPreJoinScreen();
|
||||
|
||||
53
tests/specs/2way/tileView.spec.ts
Normal file
53
tests/specs/2way/tileView.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Participant } from '../../helpers/Participant';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
/**
|
||||
* The CSS selector for local video when outside of tile view. It should
|
||||
* be in a container separate from remote videos so remote videos can
|
||||
* scroll while local video stays docked.
|
||||
*/
|
||||
const FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '#filmstripLocalVideo #localVideoContainer';
|
||||
|
||||
/**
|
||||
* The CSS selector for local video tile view is enabled. It should display
|
||||
* at the end of all the other remote videos, as the last tile.
|
||||
*/
|
||||
const TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '.remote-videos #localVideoContainer';
|
||||
|
||||
describe('TileView', () => {
|
||||
let p1: Participant, p2: Participant;
|
||||
|
||||
before('join the meeting', async () => {
|
||||
await ensureTwoParticipants();
|
||||
p1 = ctx.p1;
|
||||
p2 = ctx.p2;
|
||||
});
|
||||
it('entering tile view', async () => {
|
||||
await p1.getToolbar().clickEnterTileViewButton();
|
||||
await p1.waitForTileViewDisplayed();
|
||||
});
|
||||
it('exit tile view by pinning', async () => {
|
||||
await p1.getFilmstrip().pinParticipant(p2);
|
||||
await p1.waitForTileViewDisplayed(true);
|
||||
});
|
||||
it('local video is displayed in tile view', async () => {
|
||||
await p1.getToolbar().clickEnterTileViewButton();
|
||||
await p1.waitForTileViewDisplayed();
|
||||
await p1.driver.$(TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({ timeout: 3000 });
|
||||
await p1.driver.$(FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({
|
||||
timeout: 3000,
|
||||
reverse: true
|
||||
});
|
||||
});
|
||||
it('exit tile view by clicking "exit tile view"', async () => {
|
||||
await p1.getToolbar().clickExitTileViewButton();
|
||||
await p1.waitForTileViewDisplayed(true);
|
||||
});
|
||||
it('local video display independently from remote', async () => {
|
||||
await p1.driver.$(TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({
|
||||
timeout: 3000,
|
||||
reverse: true
|
||||
});
|
||||
await p1.driver.$(FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({ timeout: 3000 });
|
||||
});
|
||||
});
|
||||
@@ -21,7 +21,7 @@ describe('URL Normalisation', () => {
|
||||
}
|
||||
|
||||
await ensureTwoParticipants({
|
||||
forceTenant: 'tenant@example.com',
|
||||
tenant: 'tenant@example.com',
|
||||
roomName: `${ctx.roomName}@example.com`
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,22 +47,22 @@ describe('Follow Me', () => {
|
||||
|
||||
const { p1, p2, p3 } = ctx;
|
||||
|
||||
await p1.waitForTileViewDisplay();
|
||||
await p1.waitForTileViewDisplayed();
|
||||
|
||||
await p1.getToolbar().clickExitTileViewButton();
|
||||
|
||||
await Promise.all([
|
||||
p1.waitForTileViewDisplay(true),
|
||||
p2.waitForTileViewDisplay(true),
|
||||
p3.waitForTileViewDisplay(true)
|
||||
p1.waitForTileViewDisplayed(true),
|
||||
p2.waitForTileViewDisplayed(true),
|
||||
p3.waitForTileViewDisplayed(true)
|
||||
]);
|
||||
|
||||
await p1.getToolbar().clickEnterTileViewButton();
|
||||
|
||||
await Promise.all([
|
||||
p1.waitForTileViewDisplay(),
|
||||
p2.waitForTileViewDisplay(),
|
||||
p3.waitForTileViewDisplay()
|
||||
p1.waitForTileViewDisplayed(),
|
||||
p2.waitForTileViewDisplayed(),
|
||||
p3.waitForTileViewDisplayed()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -40,10 +40,7 @@ describe('StartMuted', () => {
|
||||
await p1.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
|
||||
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true);
|
||||
|
||||
await joinSecondParticipant({
|
||||
...options,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
await joinSecondParticipant(options);
|
||||
|
||||
// Enable screenshare on p1.
|
||||
p1.getToolbar().clickDesktopSharingButton();
|
||||
@@ -80,10 +77,7 @@ describe('StartMuted', () => {
|
||||
await p1.waitForRemoteVideo(p2EndpointId, false);
|
||||
|
||||
// Add a third participant and check p3 is able to receive audio and video from p2.
|
||||
await joinThirdParticipant({
|
||||
...options,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
await joinThirdParticipant(options);
|
||||
|
||||
const { p3 } = ctx;
|
||||
|
||||
@@ -110,20 +104,14 @@ describe('StartMuted', () => {
|
||||
};
|
||||
|
||||
await ensureOneParticipant(options);
|
||||
await joinSecondParticipant({
|
||||
...options,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
await joinSecondParticipant(options);
|
||||
|
||||
const { p2 } = ctx;
|
||||
|
||||
await p2.waitForIceConnected();
|
||||
await p2.waitForReceiveMedia();
|
||||
|
||||
await joinThirdParticipant({
|
||||
...options,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
await joinThirdParticipant(options);
|
||||
|
||||
const { p3 } = ctx;
|
||||
|
||||
@@ -233,8 +221,7 @@ describe('StartMuted', () => {
|
||||
p2p: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
skipInMeetingChecks: true
|
||||
}
|
||||
});
|
||||
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import { ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
/**
|
||||
* The CSS selector for local video when outside of tile view. It should
|
||||
* be in a container separate from remote videos so remote videos can
|
||||
* scroll while local video stays docked.
|
||||
*/
|
||||
const FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '#filmstripLocalVideo #localVideoContainer';
|
||||
|
||||
/**
|
||||
* The CSS selector for local video tile view is enabled. It should display
|
||||
* at the end of all the other remote videos, as the last tile.
|
||||
*/
|
||||
const TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '.remote-videos #localVideoContainer';
|
||||
|
||||
describe('TileView', () => {
|
||||
it('joining the meeting', () => ensureTwoParticipants());
|
||||
|
||||
// TODO: implements etherpad check
|
||||
|
||||
it('pinning exits', async () => {
|
||||
await enterTileView();
|
||||
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p1.getFilmstrip().pinParticipant(p2);
|
||||
|
||||
await p1.waitForTileViewDisplay(true);
|
||||
});
|
||||
|
||||
it('local video display', async () => {
|
||||
await enterTileView();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.driver.$(TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({ timeout: 3000 });
|
||||
await p1.driver.$(FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({
|
||||
timeout: 3000,
|
||||
reverse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('can exit', async () => {
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.getToolbar().clickExitTileViewButton();
|
||||
await p1.waitForTileViewDisplay(true);
|
||||
});
|
||||
|
||||
it('local video display independently from remote', async () => {
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.driver.$(TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({
|
||||
timeout: 3000,
|
||||
reverse: true
|
||||
});
|
||||
await p1.driver.$(FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({ timeout: 3000 });
|
||||
});
|
||||
|
||||
it('lastN', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
if (p1.driver.isFirefox) {
|
||||
// Firefox does not support external audio file as input.
|
||||
// Not testing as second participant cannot be dominant speaker.
|
||||
return;
|
||||
}
|
||||
|
||||
await p2.getToolbar().clickAudioMuteButton();
|
||||
|
||||
await ensureThreeParticipants({
|
||||
configOverwrite: {
|
||||
channelLastN: 1,
|
||||
startWithAudioMuted: true
|
||||
}
|
||||
});
|
||||
|
||||
const { p3 } = ctx;
|
||||
|
||||
// one inactive icon should appear in few seconds
|
||||
await p3.waitForNinjaIcon();
|
||||
|
||||
const p1EpId = await p1.getEndpointId();
|
||||
|
||||
await p3.waitForRemoteVideo(p1EpId);
|
||||
|
||||
const p2EpId = await p2.getEndpointId();
|
||||
|
||||
await p3.waitForNinjaIcon(p2EpId);
|
||||
|
||||
// no video for participant 2
|
||||
await p3.waitForRemoteVideo(p2EpId, true);
|
||||
|
||||
// mute audio for participant 1
|
||||
await p1.getToolbar().clickAudioMuteButton();
|
||||
|
||||
// unmute audio for participant 2
|
||||
await p2.getToolbar().clickAudioUnmuteButton();
|
||||
|
||||
await p3.waitForDominantSpeaker(p2EpId);
|
||||
|
||||
// check video of participant 2 should be received
|
||||
await p3.waitForRemoteVideo(p2EpId);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Attempts to enter tile view and verifies tile view has been entered.
|
||||
*/
|
||||
async function enterTileView() {
|
||||
await ctx.p1.getToolbar().clickEnterTileViewButton();
|
||||
await ctx.p1.waitForTileViewDisplay();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Participant } from '../../helpers/Participant';
|
||||
import { config } from '../../helpers/TestsConfig';
|
||||
import { IToken, ITokenOptions, generateToken } from '../../helpers/token';
|
||||
import { IParticipantJoinOptions } from '../../helpers/types';
|
||||
import { IParticipantJoinOptions, IParticipantOptions } from '../../helpers/types';
|
||||
|
||||
export function generateJaasToken(options: ITokenOptions): IToken {
|
||||
if (!config.jaas.enabled) {
|
||||
@@ -21,24 +21,22 @@ export function generateJaasToken(options: ITokenOptions): IToken {
|
||||
* environment variables (see env.example and TestsConfig.ts). If no room name is specified, the default room name
|
||||
* from the context is used.
|
||||
*
|
||||
* @param instanceId This is the "name" passed to the Participant, I think it's used to match against one of the
|
||||
* pre-configured browser instances in wdio? It must be one of 'p1', 'p2', 'p3', or 'p4'. TODO: figure out how this
|
||||
* should be used.
|
||||
* @param token the token to use, if any.
|
||||
* @param participantOptions
|
||||
* @param joinOptions options to use when joining the MUC.
|
||||
* @returns {Promise<Participant>} The Participant that has joined the MUC.
|
||||
*/
|
||||
export async function joinMuc(
|
||||
instanceId: 'p1' | 'p2' | 'p3' | 'p4',
|
||||
token?: IToken,
|
||||
participantOptions?: Partial<IParticipantOptions>,
|
||||
joinOptions?: Partial<IParticipantJoinOptions>): Promise<Participant> {
|
||||
|
||||
const name = participantOptions?.name || 'p1';
|
||||
|
||||
if (!config.jaas.enabled) {
|
||||
throw new Error('JaaS is not configured.');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const p = ctx[instanceId] as Participant;
|
||||
const p = ctx[name] as Participant;
|
||||
|
||||
if (p) {
|
||||
// Load a blank page to make sure the page is reloaded (in case the new participant uses the same URL). Using
|
||||
@@ -47,16 +45,17 @@ export async function joinMuc(
|
||||
}
|
||||
|
||||
const newParticipant = new Participant({
|
||||
name: instanceId,
|
||||
token
|
||||
iFrameApi: participantOptions?.iFrameApi || false,
|
||||
name,
|
||||
token: participantOptions?.token
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
ctx[instanceId] = newParticipant;
|
||||
ctx[name] = newParticipant;
|
||||
|
||||
return await newParticipant.joinConference({
|
||||
...joinOptions,
|
||||
forceTenant: config.jaas.tenant,
|
||||
tenant: joinOptions?.tenant || config.jaas.tenant,
|
||||
roomName: joinOptions?.roomName || ctx.roomName,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ describe('Chat', () => {
|
||||
it('send message', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p2.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
await p2.switchToMainFrame();
|
||||
|
||||
await p2.getIframeAPI().addEventListener('chatUpdated');
|
||||
await p2.getIframeAPI().addEventListener('incomingMessage');
|
||||
|
||||
@@ -72,11 +72,11 @@ describe('Invite iframeAPI', () => {
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
|
||||
await p1.getIframeAPI().invitePhone(process.env.DIAL_OUT_URL);
|
||||
|
||||
await p1.switchInPage();
|
||||
await p1.switchToIFrame();
|
||||
|
||||
await p1.waitForParticipants(1);
|
||||
|
||||
@@ -92,11 +92,11 @@ describe('Invite iframeAPI', () => {
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
|
||||
await p1.getIframeAPI().inviteSIP(process.env.SIP_JIBRI_DIAL_OUT_URL);
|
||||
|
||||
await p1.switchInPage();
|
||||
await p1.switchToIFrame();
|
||||
|
||||
await p1.waitForParticipants(1);
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ describe('Participants presence', () => {
|
||||
p2.getEndpointId()
|
||||
]);
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p2.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
await p2.switchToMainFrame();
|
||||
|
||||
expect(await p1.getIframeAPI().getEventResult('isModerator')).toBe(true);
|
||||
expect(await p2.getIframeAPI().getEventResult('isModerator')).toBe(false);
|
||||
@@ -148,7 +148,7 @@ describe('Participants presence', () => {
|
||||
it('participants pane', async () => {
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
|
||||
expect(await p1.getIframeAPI().isParticipantsPaneOpen()).toBe(false);
|
||||
|
||||
@@ -226,7 +226,7 @@ describe('Participants presence', () => {
|
||||
it('kick participant', async () => {
|
||||
// we want to join second participant with token, so we can check info in webhook
|
||||
await ctx.p2.getIframeAPI().addEventListener('videoConferenceLeft');
|
||||
await ctx.p2.switchToAPI();
|
||||
await ctx.p2.switchToMainFrame();
|
||||
await ctx.p2.getIframeAPI().executeCommand('hangup');
|
||||
await ctx.p2.driver.waitUntil(() =>
|
||||
ctx.p2.getIframeAPI().getEventResult('videoConferenceLeft'), {
|
||||
@@ -248,8 +248,8 @@ describe('Participants presence', () => {
|
||||
const p1DisplayName = await p1.getLocalDisplayName();
|
||||
const p2DisplayName = await p2.getLocalDisplayName();
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p2.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
await p2.switchToMainFrame();
|
||||
|
||||
const roomsInfo = (await p1.getIframeAPI().getRoomsInfo()).rooms[0];
|
||||
|
||||
@@ -347,7 +347,7 @@ describe('Participants presence', () => {
|
||||
expect(event.data.name).toBe(p2.name);
|
||||
}
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
|
||||
const event = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('participantJoined'), {
|
||||
timeout: 2000,
|
||||
@@ -381,7 +381,7 @@ describe('Participants presence', () => {
|
||||
|
||||
await p1.getIframeAPI().executeCommand('overwriteNames', newNames);
|
||||
|
||||
await p1.switchInPage();
|
||||
await p1.switchToIFrame();
|
||||
|
||||
expect(await p1.getLocalDisplayName()).toBe(newP1Name);
|
||||
|
||||
@@ -392,8 +392,8 @@ describe('Participants presence', () => {
|
||||
it('hangup', async () => {
|
||||
const { p1, p2, roomName } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p2.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
await p2.switchToMainFrame();
|
||||
|
||||
await p2.getIframeAPI().clearEventResults('videoConferenceLeft');
|
||||
await p2.getIframeAPI().addEventListener('videoConferenceLeft');
|
||||
@@ -423,7 +423,7 @@ describe('Participants presence', () => {
|
||||
it('dispose conference', async () => {
|
||||
const { p1, roomName, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
|
||||
await p1.getIframeAPI().clearEventResults('videoConferenceLeft');
|
||||
await p1.getIframeAPI().addEventListener('videoConferenceLeft');
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { config as testsConfig } from '../../helpers/TestsConfig';
|
||||
import { ensureOneParticipant } from '../../helpers/participants';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true
|
||||
});
|
||||
|
||||
const { tenant, customerId } = testsConfig.iframe;
|
||||
|
||||
describe('Recording', () => {
|
||||
let recordingDisabled: boolean;
|
||||
let liveStreamingDisabled: boolean;
|
||||
|
||||
it('join participant', async () => {
|
||||
await ensureOneParticipant();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
// check for dial-in dial-out sip-jibri maybe
|
||||
if (await p1.execute(() => config.disableIframeAPI)) {
|
||||
// skip the test if iframeAPI is disabled
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
recordingDisabled = Boolean(!await p1.execute(() => config.recordingService?.enabled));
|
||||
liveStreamingDisabled = Boolean(!await p1.execute(() => config.liveStreaming?.enabled))
|
||||
|| !process.env.YTUBE_TEST_STREAM_KEY;
|
||||
});
|
||||
|
||||
it('start/stop function', async () => {
|
||||
if (recordingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await testRecordingStarted(true);
|
||||
await testRecordingStopped(true);
|
||||
|
||||
// to avoid limits
|
||||
await ctx.p1.driver.pause(30000);
|
||||
});
|
||||
|
||||
it('start/stop command', async () => {
|
||||
if (recordingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await testRecordingStarted(false);
|
||||
await testRecordingStopped(false);
|
||||
|
||||
// to avoid limits
|
||||
await ctx.p1.driver.pause(30000);
|
||||
});
|
||||
|
||||
it('start/stop Livestreaming command', async () => {
|
||||
if (liveStreamingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { p1, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.getIframeAPI().addEventListener('recordingStatusChanged');
|
||||
|
||||
await p1.getIframeAPI().executeCommand('startRecording', {
|
||||
youtubeBroadcastID: process.env.YTUBE_TEST_BROADCAST_ID,
|
||||
mode: 'stream',
|
||||
youtubeStreamKey: process.env.YTUBE_TEST_STREAM_KEY
|
||||
});
|
||||
|
||||
if (webhooksProxy) {
|
||||
const liveStreamEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('LIVE_STREAM_STARTED');
|
||||
|
||||
expect('LIVE_STREAM_STARTED').toBe(liveStreamEvent.eventType);
|
||||
expect(liveStreamEvent.customerId).toBe(customerId);
|
||||
}
|
||||
|
||||
const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(statusEvent.mode).toBe('stream');
|
||||
expect(statusEvent.on).toBe(true);
|
||||
|
||||
if (process.env.YTUBE_TEST_BROADCAST_ID) {
|
||||
const liveStreamUrl = await p1.getIframeAPI().getLivestreamUrl();
|
||||
|
||||
expect(liveStreamUrl.livestreamUrl).toBeDefined();
|
||||
}
|
||||
|
||||
await p1.getIframeAPI().executeCommand('stopRecording', 'stream');
|
||||
|
||||
if (webhooksProxy) {
|
||||
const liveStreamEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED');
|
||||
|
||||
expect('LIVE_STREAM_ENDED').toBe(liveStreamEvent.eventType);
|
||||
expect(liveStreamEvent.customerId).toBe(customerId);
|
||||
}
|
||||
|
||||
const stoppedStatusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(stoppedStatusEvent.mode).toBe('stream');
|
||||
expect(stoppedStatusEvent.on).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks if the recording is started.
|
||||
* @param command
|
||||
*/
|
||||
async function testRecordingStarted(command: boolean) {
|
||||
const { p1, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.getIframeAPI().addEventListener('recordingStatusChanged');
|
||||
await p1.getIframeAPI().addEventListener('recordingLinkAvailable');
|
||||
|
||||
if (command) {
|
||||
await p1.getIframeAPI().executeCommand('startRecording', {
|
||||
mode: 'file'
|
||||
});
|
||||
} else {
|
||||
await p1.getIframeAPI().startRecording({
|
||||
mode: 'file'
|
||||
});
|
||||
}
|
||||
|
||||
if (webhooksProxy) {
|
||||
const recordingEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_STARTED');
|
||||
|
||||
expect('RECORDING_STARTED').toBe(recordingEvent.eventType);
|
||||
expect(recordingEvent.customerId).toBe(customerId);
|
||||
|
||||
webhooksProxy?.clearCache();
|
||||
}
|
||||
|
||||
const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(statusEvent.mode).toBe('file');
|
||||
expect(statusEvent.on).toBe(true);
|
||||
|
||||
const linkEvent = (await p1.getIframeAPI().getEventResult('recordingLinkAvailable'));
|
||||
|
||||
expect(linkEvent.link.startsWith('https://')).toBe(true);
|
||||
expect(linkEvent.link.includes(tenant)).toBe(true);
|
||||
expect(linkEvent.ttl > 0).toBe(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the recording is stopped.
|
||||
* @param command
|
||||
*/
|
||||
async function testRecordingStopped(command: boolean) {
|
||||
const { p1, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
if (command) {
|
||||
await p1.getIframeAPI().executeCommand('stopRecording', 'file');
|
||||
} else {
|
||||
await p1.getIframeAPI().stopRecording('file');
|
||||
}
|
||||
|
||||
if (webhooksProxy) {
|
||||
const liveStreamEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_ENDED');
|
||||
|
||||
expect('RECORDING_ENDED').toBe(liveStreamEvent.eventType);
|
||||
expect(liveStreamEvent.customerId).toBe(customerId);
|
||||
|
||||
const recordingUploadedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
initiatorId: string;
|
||||
participants: Array<string>;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_UPLOADED');
|
||||
|
||||
const jwtPayload = p1.getToken()?.payload;
|
||||
|
||||
expect(recordingUploadedEvent.data.initiatorId).toBe(jwtPayload?.context?.user?.id);
|
||||
expect(recordingUploadedEvent.data.participants.some(
|
||||
// @ts-ignore
|
||||
e => e.id === jwtPayload?.context?.user?.id)).toBe(true);
|
||||
|
||||
webhooksProxy?.clearCache();
|
||||
}
|
||||
|
||||
const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(statusEvent.mode).toBe('file');
|
||||
expect(statusEvent.on).toBe(false);
|
||||
|
||||
await p1.getIframeAPI().clearEventResults('recordingStatusChanged');
|
||||
}
|
||||
@@ -24,7 +24,7 @@ describe('Transcriptions', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
|
||||
await ensureTwoParticipants({
|
||||
configOverwrite: {
|
||||
@@ -40,8 +40,8 @@ describe('Transcriptions', () => {
|
||||
p2.getEndpointId()
|
||||
]);
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p2.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
await p2.switchToMainFrame();
|
||||
|
||||
expect(await p1.getIframeAPI().getEventResult('isModerator')).toBe(true);
|
||||
expect(await p1.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined();
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('Visitors', () => {
|
||||
await p1.driver.waitUntil(() => p1.execute(() => APP.conference._room.isVisitorsSupported()), {
|
||||
timeout: 2000
|
||||
}).then(async () => {
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
}).catch(() => {
|
||||
ctx.skipSuiteTests = true;
|
||||
});
|
||||
@@ -91,7 +91,7 @@ describe('Visitors', () => {
|
||||
expect(event.data.role).toBe('visitor');
|
||||
expect(event.customerId).toBe(testsConfig.iframe.customerId);
|
||||
|
||||
await p2.switchToAPI();
|
||||
await p2.switchToMainFrame();
|
||||
await p2.getIframeAPI().executeCommand('hangup');
|
||||
|
||||
// PARTICIPANT_LEFT webhook
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('Visitors', () => {
|
||||
await p1.driver.waitUntil(() => p1.execute(() => APP.conference._room.isVisitorsSupported()), {
|
||||
timeout: 2000
|
||||
}).then(async () => {
|
||||
await p1.switchToAPI();
|
||||
await p1.switchToMainFrame();
|
||||
}).catch(() => {
|
||||
ctx.skipSuiteTests = true;
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ setTestProperties(__filename, {
|
||||
describe('XMPP login and MUC join test', () => {
|
||||
it('with a valid token (wildcard room)', async () => {
|
||||
console.log('Joining a MUC with a valid token (wildcard room)');
|
||||
const p = await joinMuc('p1', t({ room: '*' }));
|
||||
const p = await joinMuc({ token: t({ room: '*' }) });
|
||||
|
||||
expect(await p.isInMuc()).toBe(true);
|
||||
expect(await p.isModerator()).toBe(false);
|
||||
@@ -17,7 +17,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
|
||||
it('with a valid token (specific room)', async () => {
|
||||
console.log('Joining a MUC with a valid token (specific room)');
|
||||
const p = await joinMuc('p1', t({ room: ctx.roomName }));
|
||||
const p = await joinMuc({ token: t({ room: ctx.roomName }) });
|
||||
|
||||
expect(await p.isInMuc()).toBe(true);
|
||||
expect(await p.isModerator()).toBe(false);
|
||||
@@ -29,7 +29,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
|
||||
token.jwt = token.jwt + 'badSignature';
|
||||
|
||||
const p = await joinMuc('p1', token);
|
||||
const p = await joinMuc({ token });
|
||||
|
||||
expect(Boolean(await p.isInMuc())).toBe(false);
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
|
||||
it('with an expired token', async () => {
|
||||
console.log('Joining a MUC with an expired token');
|
||||
const p = await joinMuc('p1', t({ exp: '-1m' }));
|
||||
const p = await joinMuc({ token: t({ exp: '-1m' }) });
|
||||
|
||||
expect(Boolean(await p.isInMuc())).toBe(false);
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
|
||||
it('with a token using the wrong key ID', async () => {
|
||||
console.log('Joining a MUC with a token using the wrong key ID');
|
||||
const p = await joinMuc('p1', t({ keyId: 'invalid-key-id' }));
|
||||
const p = await joinMuc({ token: t({ keyId: 'invalid-key-id' }) });
|
||||
|
||||
expect(Boolean(await p.isInMuc())).toBe(false);
|
||||
|
||||
@@ -63,7 +63,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
|
||||
it('with a token for a different room', async () => {
|
||||
console.log('Joining a MUC with a token for a different room');
|
||||
const p = await joinMuc('p1', t({ room: ctx.roomName + 'different' }));
|
||||
const p = await joinMuc({ token: t({ room: ctx.roomName + 'different' }) });
|
||||
|
||||
expect(Boolean(await p.isInMuc())).toBe(false);
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
|
||||
it('with a moderator token', async () => {
|
||||
console.log('Joining a MUC with a moderator token');
|
||||
const p = await joinMuc('p1', t({ moderator: true }));
|
||||
const p = await joinMuc({ token: t({ moderator: true }) });
|
||||
|
||||
expect(await p.isInMuc()).toBe(true);
|
||||
expect(await p.isModerator()).toBe(true);
|
||||
@@ -84,7 +84,7 @@ describe('XMPP login and MUC join test', () => {
|
||||
// disabled.
|
||||
it('without a token', async () => {
|
||||
console.log('Joining a MUC without a token');
|
||||
const p = await joinMuc('p1');
|
||||
const p = await joinMuc();
|
||||
|
||||
expect(Boolean(await p.isInMuc())).toBe(false);
|
||||
|
||||
|
||||
@@ -13,19 +13,19 @@ describe('MaxOccupants limit enforcement', () => {
|
||||
maxOccupants: 2
|
||||
};
|
||||
|
||||
const p1 = await joinMuc('p1', t({ room: ctx.roomName }));
|
||||
const p2 = await joinMuc('p2', t({ room: ctx.roomName }));
|
||||
const p1 = await joinMuc({ token: t({ room: ctx.roomName }) });
|
||||
const p2 = await joinMuc({ name: 'p2', token: t({ room: ctx.roomName }) });
|
||||
|
||||
expect(await p1.isInMuc()).toBe(true);
|
||||
expect(await p2.isInMuc()).toBe(true);
|
||||
|
||||
// Third participant should be rejected (exceeding maxOccupants), even if it's a moderator
|
||||
let p3 = await joinMuc('p3', t({ room: ctx.roomName, moderator: true }));
|
||||
let p3 = await joinMuc({ name: 'p3', token: t({ room: ctx.roomName, moderator: true }) });
|
||||
|
||||
expect(Boolean(await p3.isInMuc())).toBe(false);
|
||||
|
||||
await p1.hangup();
|
||||
p3 = await joinMuc('p3', t({ room: ctx.roomName }));
|
||||
p3 = await joinMuc({ name: 'p3', token: t({ room: ctx.roomName }) });
|
||||
expect(await p3.isInMuc()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('Setting passcode through settings provisioning', () => {
|
||||
*/
|
||||
async function joinWithPassword(instanceId: string, token: IToken) {
|
||||
// @ts-ignore
|
||||
const p = await joinMuc(instanceId, token, ctx.roomName);
|
||||
const p = await joinMuc({ name: instanceId, token }, { roomName: ctx.roomName });
|
||||
|
||||
await p.waitForMucJoinedOrError();
|
||||
expect(await p.isInMuc()).toBe(false);
|
||||
|
||||
@@ -14,7 +14,7 @@ describe('Setting passcode through settings provisioning', () => {
|
||||
passcode: 'passcode-must-be-digits-only'
|
||||
};
|
||||
|
||||
const p = await joinMuc('p1', t({ room: ctx.roomName }), ctx.roomName);
|
||||
const p = await joinMuc({ token: t({ room: ctx.roomName }) }, { roomName: ctx.roomName });
|
||||
|
||||
// The settings provisioning contains an invalid passcode, the expected result is that the room is not
|
||||
// configured to require a passcode.
|
||||
|
||||
208
tests/specs/jaas/recording.spec.ts
Normal file
208
tests/specs/jaas/recording.spec.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import { Participant } from '../../helpers/Participant';
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { config as testsConfig } from '../../helpers/TestsConfig';
|
||||
import WebhookProxy from '../../helpers/WebhookProxy';
|
||||
import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useJaas: true,
|
||||
// Note this just for posterity. We don't depend on the framework doing anything for us because of this flag (we
|
||||
// pass it as a parameter directly)
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the recording and live-streaming functionality of JaaS (including relevant webhooks) exercising the iFrame API
|
||||
* commands and functions.
|
||||
* TODO: read flags from config.
|
||||
* TODO: also assert "this meeting is being recorder" notificaitons are show/played?
|
||||
*/
|
||||
describe('Recording and Live Streaming', () => {
|
||||
const tenant = testsConfig.jaas.tenant;
|
||||
const customerId = tenant?.replace('vpaas-magic-cookie-', '');
|
||||
// TODO: read from config
|
||||
let recordingDisabled: boolean;
|
||||
// TODO: read from config
|
||||
let liveStreamingDisabled: boolean;
|
||||
let p: Participant;
|
||||
let webhooksProxy: WebhookProxy;
|
||||
|
||||
it('setup', async () => {
|
||||
webhooksProxy = ctx.webhooksProxy;
|
||||
p = await joinMuc({ iFrameApi: true, token: t({ moderator: true }) }, { roomName: ctx.roomName });
|
||||
|
||||
// TODO: what should we do in this case? Add a config for this?
|
||||
if (await p.execute(() => config.disableIframeAPI)) {
|
||||
// skip the test if iframeAPI is disabled
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: only read if config says so
|
||||
recordingDisabled = Boolean(!await p.execute(() => config.recordingService?.enabled));
|
||||
liveStreamingDisabled = Boolean(!await p.execute(() => config.liveStreaming?.enabled))
|
||||
|| !process.env.YTUBE_TEST_STREAM_KEY;
|
||||
|
||||
await p.switchToMainFrame();
|
||||
});
|
||||
|
||||
/**
|
||||
* Starts recording and asserts that the expected iFrame and JaaS events are received.
|
||||
* @param command whether to use the "command" or the "function" iFrame API.
|
||||
*/
|
||||
async function startRecording(command: boolean) {
|
||||
await p.getIframeAPI().addEventListener('recordingStatusChanged');
|
||||
await p.getIframeAPI().addEventListener('recordingLinkAvailable');
|
||||
|
||||
if (command) {
|
||||
await p.getIframeAPI().executeCommand('startRecording', {
|
||||
mode: 'file'
|
||||
});
|
||||
} else {
|
||||
await p.getIframeAPI().startRecording({
|
||||
mode: 'file'
|
||||
});
|
||||
}
|
||||
|
||||
const jaasEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_STARTED');
|
||||
|
||||
expect('RECORDING_STARTED').toBe(jaasEvent.eventType);
|
||||
expect(jaasEvent.customerId).toBe(customerId);
|
||||
|
||||
webhooksProxy.clearCache();
|
||||
|
||||
const iFrameEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(iFrameEvent.mode).toBe('file');
|
||||
expect(iFrameEvent.on).toBe(true);
|
||||
|
||||
const linkEvent = (await p.getIframeAPI().getEventResult('recordingLinkAvailable'));
|
||||
|
||||
expect(linkEvent.link.startsWith('https://')).toBe(true);
|
||||
expect(linkEvent.link.includes(tenant)).toBe(true);
|
||||
expect(linkEvent.ttl > 0).toBe(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops recording and asserts that the expected iFrame and JaaS events are received.
|
||||
* @param command whether to use the "command" or the "function" iFrame API.
|
||||
*/
|
||||
async function stopRecording(command: boolean) {
|
||||
if (command) {
|
||||
await p.getIframeAPI().executeCommand('stopRecording', 'file');
|
||||
} else {
|
||||
await p.getIframeAPI().stopRecording('file');
|
||||
}
|
||||
|
||||
const jaasEndedEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_ENDED');
|
||||
|
||||
expect('RECORDING_ENDED').toBe(jaasEndedEvent.eventType);
|
||||
expect(jaasEndedEvent.customerId).toBe(customerId);
|
||||
|
||||
const jaasUploadedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
initiatorId: string;
|
||||
participants: Array<string>;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_UPLOADED');
|
||||
|
||||
const jwtPayload = p.getToken()?.payload;
|
||||
|
||||
expect(jaasUploadedEvent.data.initiatorId).toBe(jwtPayload?.context?.user?.id);
|
||||
expect(jaasUploadedEvent.data.participants.some(
|
||||
// @ts-ignore
|
||||
e => e.id === jwtPayload?.context?.user?.id)).toBe(true);
|
||||
|
||||
webhooksProxy.clearCache();
|
||||
|
||||
const iFrameEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(iFrameEvent.mode).toBe('file');
|
||||
expect(iFrameEvent.on).toBe(false);
|
||||
|
||||
await p.getIframeAPI().clearEventResults('recordingStatusChanged');
|
||||
}
|
||||
|
||||
it('start/stop recording using the iFrame command', async () => {
|
||||
if (recordingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await startRecording(true);
|
||||
await stopRecording(true);
|
||||
|
||||
// to avoid rate limits
|
||||
await p.driver.pause(30000);
|
||||
});
|
||||
|
||||
it('start/stop recording using the iFrame function', async () => {
|
||||
if (recordingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await startRecording(false);
|
||||
await stopRecording(false);
|
||||
|
||||
// to avoid rate limits
|
||||
await p.driver.pause(30000);
|
||||
});
|
||||
|
||||
it('start/stop live-streaming using the iFrame command', async () => {
|
||||
if (liveStreamingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await p.getIframeAPI().addEventListener('recordingStatusChanged');
|
||||
|
||||
await p.getIframeAPI().executeCommand('startRecording', {
|
||||
youtubeBroadcastID: process.env.YTUBE_TEST_BROADCAST_ID,
|
||||
mode: 'stream',
|
||||
youtubeStreamKey: process.env.YTUBE_TEST_STREAM_KEY
|
||||
});
|
||||
|
||||
const jaasEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('LIVE_STREAM_STARTED');
|
||||
|
||||
expect('LIVE_STREAM_STARTED').toBe(jaasEvent.eventType);
|
||||
expect(jaasEvent.customerId).toBe(customerId);
|
||||
|
||||
const iFrameEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(iFrameEvent.mode).toBe('stream');
|
||||
expect(iFrameEvent.on).toBe(true);
|
||||
|
||||
if (process.env.YTUBE_TEST_BROADCAST_ID) {
|
||||
const liveStreamUrl = await p.getIframeAPI().getLivestreamUrl();
|
||||
|
||||
expect(liveStreamUrl.livestreamUrl).toBeDefined();
|
||||
}
|
||||
|
||||
await p.getIframeAPI().executeCommand('stopRecording', 'stream');
|
||||
|
||||
const jaasEndedEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED');
|
||||
|
||||
expect(jaasEndedEvent.eventType).toBe('LIVE_STREAM_ENDED');
|
||||
expect(jaasEndedEvent.customerId).toBe(customerId);
|
||||
|
||||
const iFrameEndedEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(iFrameEndedEvent.mode).toBe('stream');
|
||||
expect(iFrameEndedEvent.on).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,10 +15,9 @@ describe('Visitors triggered by reaching participantsSoftLimit', () => {
|
||||
};
|
||||
|
||||
/// XXX the "name" of the participant MUST match one of the "capabilities" defined in wdio. It's not a "participant", it's an instance configuration!
|
||||
const m = await joinMuc(
|
||||
'p1',
|
||||
t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
|
||||
);
|
||||
const m = await joinMuc({
|
||||
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
|
||||
});
|
||||
|
||||
expect(await m.isInMuc()).toBe(true);
|
||||
expect(await m.isModerator()).toBe(true);
|
||||
@@ -26,10 +25,10 @@ describe('Visitors triggered by reaching participantsSoftLimit', () => {
|
||||
console.log('Moderator joined');
|
||||
|
||||
// Joining with a participant token before participantSoftLimit has been reached
|
||||
const p = await joinMuc(
|
||||
'p2',
|
||||
t({ room: ctx.roomName, displayName: 'Parti Cipant' })
|
||||
);
|
||||
const p = await joinMuc({
|
||||
name: 'p2',
|
||||
token: t({ room: ctx.roomName, displayName: 'Parti Cipant' })
|
||||
});
|
||||
|
||||
expect(await p.isInMuc()).toBe(true);
|
||||
expect(await p.isModerator()).toBe(false);
|
||||
@@ -37,10 +36,10 @@ describe('Visitors triggered by reaching participantsSoftLimit', () => {
|
||||
console.log('Participant joined');
|
||||
|
||||
// Joining with a participant token after participantSoftLimit has been reached
|
||||
const v = await joinMuc(
|
||||
'p3',
|
||||
t({ room: ctx.roomName, displayName: 'Visi Tor' })
|
||||
);
|
||||
const v = await joinMuc({
|
||||
name: 'p3',
|
||||
token: t({ room: ctx.roomName, displayName: 'Visi Tor' })
|
||||
});
|
||||
|
||||
expect(await v.isInMuc()).toBe(true);
|
||||
expect(await v.isModerator()).toBe(false);
|
||||
|
||||
@@ -23,21 +23,20 @@ describe('Visitor receiving video from a single remote participant', () => {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
const sender = await joinMuc(
|
||||
'p1',
|
||||
t({ room: ctx.roomName, displayName: 'Sender', moderator: true }), {
|
||||
configOverwrite
|
||||
}
|
||||
);
|
||||
const sender = await joinMuc({
|
||||
token: t({ room: ctx.roomName, displayName: 'Sender', moderator: true })
|
||||
}, {
|
||||
configOverwrite
|
||||
});
|
||||
const senderEndpointId = await sender.getEndpointId();
|
||||
|
||||
const testVisitor = async function(instanceId: 'p1' | 'p2' | 'p3' | 'p4') {
|
||||
const visitor = await joinMuc(
|
||||
instanceId,
|
||||
t({ room: ctx.roomName, displayName: 'Visitor', visitor: true }), {
|
||||
configOverwrite
|
||||
}
|
||||
);
|
||||
const visitor = await joinMuc({
|
||||
name: instanceId,
|
||||
token: t({ room: ctx.roomName, displayName: 'Visitor', visitor: true })
|
||||
}, {
|
||||
configOverwrite
|
||||
});
|
||||
|
||||
await visitor.waitForIceConnected();
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ describe('Visitors triggered by visitor tokens', () => {
|
||||
visitorsEnabled: true
|
||||
};
|
||||
|
||||
const m = await joinMuc(
|
||||
'p1',
|
||||
t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
|
||||
);
|
||||
const m = await joinMuc({
|
||||
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
|
||||
});
|
||||
|
||||
expect(await m.isInMuc()).toBe(true);
|
||||
expect(await m.isModerator()).toBe(true);
|
||||
@@ -24,10 +23,10 @@ describe('Visitors triggered by visitor tokens', () => {
|
||||
console.log('Moderator joined');
|
||||
|
||||
// Joining with a participant token before any visitors
|
||||
const p = await joinMuc(
|
||||
'p2',
|
||||
t({ room: ctx.roomName, displayName: 'Parti Cipant' })
|
||||
);
|
||||
const p = await joinMuc({
|
||||
name: 'p2',
|
||||
token: t({ room: ctx.roomName, displayName: 'Parti Cipant' })
|
||||
});
|
||||
|
||||
expect(await p.isInMuc()).toBe(true);
|
||||
expect(await p.isModerator()).toBe(false);
|
||||
@@ -35,10 +34,10 @@ describe('Visitors triggered by visitor tokens', () => {
|
||||
console.log('Participant joined');
|
||||
|
||||
// Joining with a visitor token
|
||||
const v = await joinMuc(
|
||||
'p3',
|
||||
t({ room: ctx.roomName, displayName: 'Visi Tor', visitor: true })
|
||||
);
|
||||
const v = await joinMuc({
|
||||
name: 'p3',
|
||||
token: t({ room: ctx.roomName, displayName: 'Visi Tor', visitor: true })
|
||||
});
|
||||
|
||||
expect(await v.isInMuc()).toBe(true);
|
||||
expect(await v.isModerator()).toBe(false);
|
||||
@@ -46,9 +45,10 @@ describe('Visitors triggered by visitor tokens', () => {
|
||||
console.log('Visitor joined');
|
||||
|
||||
// Joining with a participant token after visitors...:mindblown:
|
||||
const v2 = await joinMuc(
|
||||
'p2',
|
||||
t({ room: ctx.roomName, displayName: 'Visi Tor 2' }));
|
||||
const v2 = await joinMuc({
|
||||
name: 'p2',
|
||||
token: t({ room: ctx.roomName, displayName: 'Visi Tor 2' })
|
||||
});
|
||||
|
||||
expect(await v2.isInMuc()).toBe(true);
|
||||
expect(await v2.isModerator()).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user