test: Refactor, update and fix JaaS tests (#16463)

* ref: Move the jaas util out of specs/.
* ref: Extract a more generic joinMuc utility.
* ref: Rename joinMuc to joinJaasMuc.
* ref: Move tileView.spec.ts out of 2way, use joinMuc.
* ref: Enforce that "name" is p1, p2, p3, p4 using types.
* fix: Fix mute test filename.
* ref: Split the chat test into jaas and iframe tests.
* test: Add webhook verification to jaas visitor tests.
* ref: Remove the iframe/visitors test (ported to jaas).
* ref: Move the transcriptions test to jaas.
* ref: Make getEndpointId work from outside the iframe.
* ref: Remove TestProperties.useIFrameApi. Use the flag in IParticipantOptions instead.
* ref: Do not set a special tenant when the iFrame API is used, leave it to tests to determine.
* ref: Remove the jaas-specific tests from iframe/participantsPresnce (will be re-added under jaas/ later).
* ref: Move the dial in/out tests to jaas/.
* Add tests for jaas join/leave webhooks (port back from iframe/participantsPresence).
* config: Fallback to IFRAME_TENANT and JWT_* for jaas configuration.
* ref: Simplify boolean expression.
* ref: Remove the skipFirstModerator option (unused).
* ref: Do not override token if specified.
* fix: Do not generate token for alone/invite test.
* ref: Extract more dial-in utilities.
* test: Verify Invite UI in jaas.
* Do not generate token for dial in (case covered in jaas/).
* ref: Remove preferGenerateToken (unused).
* ref: Move mute utils in their own helper.
* fix: Fix setting the jaas enabled flag.
* Do not run alone/invite for jaas (temp fix).
* fix: Switch back to meeting window.
* Do not run alone/dialInAudio on jaas.
* Disable the SIP jibri test (broken).
This commit is contained in:
bgrozev
2025-10-01 11:40:02 -05:00
committed by GitHub
parent 9bb1c36508
commit 70c3c8db13
41 changed files with 1119 additions and 1021 deletions

View File

@@ -59,6 +59,12 @@ export class Participant {
private _iFrameApi: boolean = false;
/**
* Whether the current frame is the main frame. This could coincide with the Jitsi Meet frame (when it's loaded
* directly), or not (when it's loaded in an iframe).
*/
private _inMainFrame: boolean = true;
/**
* The default config to use when joining.
*
@@ -149,9 +155,17 @@ export class Participant {
*/
async getEndpointId(): Promise<string> {
if (!this._endpointId) {
const wasInMainFrame = this._inMainFrame;
await this.switchToIFrame();
this._endpointId = await this.execute(() => { // eslint-disable-line arrow-body-style
return APP?.conference?.getMyUserId();
});
if (wasInMainFrame) {
await this.switchToMainFrame();
}
}
return this._endpointId;
@@ -626,21 +640,31 @@ export class Participant {
/**
* 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.
* If this Participant was initialized with iFrameApi=false this is a no-op.
*/
async switchToMainFrame() {
if (!this._iFrameApi || this._inMainFrame) {
return;
}
await this.driver.switchFrame(null);
this._inMainFrame = true;
}
/**
* 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.
* If this Participant was initialized with iFrameApi=false this is a no-op.
*/
async switchToIFrame() {
if (!this._iFrameApi || !this._inMainFrame) {
return;
}
const iframe = this.driver.$('iframe');
await this.driver.switchFrame(iframe);
this._inMainFrame = false;
}
/**

View File

@@ -2,8 +2,6 @@
* An interface that tests can export (as a TEST_PROPERTIES property) to define what they require.
*/
export type ITestProperties = {
/** The test uses the iFrame API. */
useIFrameApi: boolean;
/** The test requires jaas, it should be skipped when the jaas configuration is not enabled. */
useJaas: boolean;
/** The test requires the webhook proxy. */
@@ -12,7 +10,6 @@ export type ITestProperties = {
};
const defaultProperties: ITestProperties = {
useIFrameApi: false,
useWebhookProxy: false,
useJaas: false,
usesBrowsers: [ 'p1', 'p2', 'p3', 'p4' ]

View File

@@ -4,26 +4,67 @@
export const config = {
/** Enable debug logging. Note this includes private information from .env */
debug: Boolean(process.env.JITSI_DEBUG?.trim()),
iframe: {
customerId: process.env.IFRAME_TENANT?.trim()?.replace('vpaas-magic-cookie-', ''),
tenant: process.env.IFRAME_TENANT?.trim(),
/** Whether the configuration specifies a JaaS account for the iFrame API tests. */
usesJaas: Boolean(process.env.JWT_PRIVATE_KEY_PATH && process.env.JWT_KID?.startsWith('vpaas-magic-cookie-')),
},
/** Whether to expect the environment to automatically elect a new moderator when the existing moderator leaves. */
autoModerator: (() => {
if (typeof process.env.AUTO_MODERATOR !== 'undefined') {
return process.env.AUTO_MODERATOR?.trim() === 'true';
}
// If not explicitly configured, fallback to recognizing whether we're running against one of the JaaS
// environments which are known to have the setting disabled.
return !Boolean(
process.env.JWT_PRIVATE_KEY_PATH && process.env.JWT_KID?.startsWith('vpaas-magic-cookie-')
);
})(),
jaas: {
customerId: (() => {
if (typeof process.env.JAAS_TENANT !== 'undefined') {
return process.env.JAAS_TENANT?.trim()?.replace('vpaas-magic-cookie-', '');
}
return process.env.IFRAME_TENANT?.trim()?.replace('vpaas-magic-cookie-', '');
})(),
/** Whether the configuration for JaaS specific tests is enabled. */
enabled: Boolean(process.env.JAAS_TENANT && process.env.JAAS_PRIVATE_KEY_PATH && process.env.JAAS_KID),
enabled: Boolean(
(process.env.JAAS_TENANT || process.env.IFRAME_TENANT)
&& (process.env.JAAS_PRIVATE_KEY_PATH || process.env.JWT_PRIVATE_KEY_PATH)
&& (process.env.JAAS_KID || process.env.JWT_KID)),
/** The JaaS key ID, used to sign the tokens. */
kid: process.env.JAAS_KID?.trim(),
kid: (() => {
if (typeof process.env.JAAS_KID !== 'undefined') {
return process.env.JAAS_KID?.trim();
}
return process.env.JWT_KID?.trim();
})(),
/** The path to the JaaS private key, used to sign JaaS tokens. */
privateKeyPath: process.env.JAAS_PRIVATE_KEY_PATH?.trim(),
privateKeyPath: (() => {
if (typeof process.env.JAAS_PRIVATE_KEY_PATH != 'undefined') {
return process.env.JAAS_PRIVATE_KEY_PATH?.trim();
}
return process.env.JWT_PRIVATE_KEY_PATH?.trim();
})(),
/** The JaaS tenant (vpaas-magic-cookie-<ID>) . */
tenant: process.env.JAAS_TENANT?.trim(),
tenant: (() => {
if (typeof process.env.JAAS_TENANT !== 'undefined') {
return process.env.JAAS_TENANT?.trim();
}
return process.env.IFRAME_TENANT?.trim();
})()
},
jwt: {
kid: process.env.JWT_KID?.trim(),
/** A pre-configured token used by some tests. */
preconfiguredToken: process.env.JWT_ACCESS_TOKEN?.trim(),
preconfiguredJwt: process.env.JWT_ACCESS_TOKEN?.trim(),
preconfiguredToken: (() => {
if (process.env.JWT_ACCESS_TOKEN) {
return { jwt: process.env.JWT_ACCESS_TOKEN?.trim() };
}
return undefined;
})(),
privateKeyPath: process.env.JWT_PRIVATE_KEY_PATH?.trim()
},
roomName: {

41
tests/helpers/jaas.ts Normal file
View File

@@ -0,0 +1,41 @@
import { Participant } from './Participant';
import { config } from './TestsConfig';
import { joinMuc } from './joinMuc';
import { IToken, ITokenOptions, generateToken } from './token';
import { IParticipantJoinOptions, IParticipantOptions } from './types';
export function generateJaasToken(options: ITokenOptions): IToken {
if (!config.jaas.enabled) {
throw new Error('JaaS is not configured.');
}
// Don't override the keyId and keyPath if they are already set in options, allow tests to set them.
return generateToken({
...options,
keyId: options.keyId || config.jaas.kid,
keyPath: options.keyPath || config.jaas.privateKeyPath
});
}
/**
* Creates a new Participant and joins the MUC with the given options. The jaas-specific properties must be set as
* environment variables (see env.example and TestsConfig.ts). If no room name is specified, the default room name
* from the context is used.
*
* @param participantOptions
* @param joinOptions options to use when joining the MUC.
* @returns {Promise<Participant>} The Participant that has joined the MUC.
*/
export async function joinJaasMuc(
participantOptions?: Partial<IParticipantOptions>,
joinOptions?: Partial<IParticipantJoinOptions>): Promise<Participant> {
if (!config.jaas.enabled) {
throw new Error('JaaS is not configured.');
}
return await joinMuc(participantOptions, {
...joinOptions,
tenant: joinOptions?.tenant || config.jaas.tenant
});
}

View File

@@ -1,25 +1,9 @@
import { Participant } from '../../helpers/Participant';
import { config } from '../../helpers/TestsConfig';
import { IToken, ITokenOptions, generateToken } from '../../helpers/token';
import { IParticipantJoinOptions, IParticipantOptions } from '../../helpers/types';
export function generateJaasToken(options: ITokenOptions): IToken {
if (!config.jaas.enabled) {
throw new Error('JaaS is not configured.');
}
// Don't override the keyId and keyPath if they are already set in options, allow tests to set them.
return generateToken({
...options,
keyId: options.keyId || config.jaas.kid,
keyPath: options.keyPath || config.jaas.privateKeyPath
});
}
import { Participant } from './Participant';
import { IParticipantJoinOptions, IParticipantOptions } from './types';
/**
* Creates a new Participant and joins the MUC with the given options. The jaas-specific properties must be set as
* environment variables (see env.example and TestsConfig.ts). If no room name is specified, the default room name
* from the context is used.
* Creates a new Participant and joins the MUC with the given options. If no room name is specified, the default room
* name from the context is used.
*
* @param participantOptions
* @param joinOptions options to use when joining the MUC.
@@ -31,10 +15,6 @@ export async function joinMuc(
const name = participantOptions?.name || 'p1';
if (!config.jaas.enabled) {
throw new Error('JaaS is not configured.');
}
// @ts-ignore
const p = ctx[name] as Participant;
@@ -55,7 +35,6 @@ export async function joinMuc(
return await newParticipant.joinConference({
...joinOptions,
tenant: joinOptions?.tenant || config.jaas.tenant,
roomName: joinOptions?.roomName || ctx.roomName,
});
}

View File

@@ -1,6 +1,5 @@
import { P1, P2, P3, P4, Participant } from './Participant';
import { config } from './TestsConfig';
import { generateToken } from './token';
import { IJoinOptions, IParticipantOptions } from './types';
const SUBJECT_XPATH = '//div[starts-with(@class, "subject-text")]';
@@ -10,31 +9,22 @@ const SUBJECT_XPATH = '//div[starts-with(@class, "subject-text")]';
* Ensure that the first participant is moderator if there is such an option.
*
* @param {IJoinOptions} options - The options to use when joining the participant.
* @param participantOptions
* @returns {Promise<void>}
*/
export async function ensureOneParticipant(options?: IJoinOptions): Promise<void> {
const participantOps = { name: P1 } as IParticipantOptions;
export async function ensureOneParticipant(
options?: IJoinOptions, participantOptions?: IParticipantOptions): Promise<void> {
if (!participantOptions) {
participantOptions = { name: P1 };
}
participantOptions.name = P1;
if (!options?.skipFirstModerator) {
const jwtPrivateKeyPath = config.jwt.privateKeyPath;
// we prioritize the access token when iframe is not used and private key is set,
// otherwise if private key is not specified we use the access token if set
if (config.jwt.preconfiguredToken
&& ((jwtPrivateKeyPath && !ctx.testProperties.useIFrameApi && !options?.preferGenerateToken)
|| !jwtPrivateKeyPath)) {
participantOps.token = { jwt: config.jwt.preconfiguredToken };
} else if (jwtPrivateKeyPath) {
participantOps.token = generateToken({
...options?.tokenOptions,
displayName: participantOps.name,
moderator: true
});
}
if (!participantOptions.token) {
participantOptions.token = config.jwt.preconfiguredToken;
}
// make sure the first participant is moderator, if supported by deployment
await joinParticipant(participantOps, options);
await joinParticipant(participantOptions, options);
}
/**
@@ -136,23 +126,18 @@ export async function ensureFourParticipants(options?: IJoinOptions): Promise<vo
* Ensure that there are two participants.
*
* @param {IJoinOptions} options - The options to join.
* @param participantOptions
*/
export async function ensureTwoParticipants(options?: IJoinOptions): Promise<void> {
await ensureOneParticipant(options);
export async function ensureTwoParticipants(
options?: IJoinOptions, participantOptions?: IParticipantOptions): Promise<void> {
await ensureOneParticipant(options, participantOptions);
const participantOptions = { name: P2 } as IParticipantOptions;
if (options?.preferGenerateToken) {
participantOptions.token = generateToken({
...options?.tokenOptions,
displayName: participantOptions.name,
});
if (!participantOptions) {
participantOptions = { name: P2 };
}
participantOptions.name = P2;
await joinParticipant({
...participantOptions,
name: P2
}, options);
await joinParticipant(participantOptions, options);
if (options?.skipInMeetingChecks) {
return Promise.resolve();
@@ -171,8 +156,7 @@ export async function ensureTwoParticipants(options?: IJoinOptions): Promise<voi
/**
* Creates a new participant instance, or returns an existing one if it is already joined.
* @param participantOptions - The participant options, with required name set.
* @param {boolean} options - Join options.
* @param reuse whether to reuse an existing participant instance if one is available.
* @param options - Join options.
* @returns {Promise<Participant>} - The participant instance.
*/
async function joinParticipant( // eslint-disable-line max-params
@@ -180,13 +164,11 @@ async function joinParticipant( // eslint-disable-line max-params
options?: IJoinOptions
): Promise<Participant> {
participantOptions.iFrameApi = ctx.testProperties.useIFrameApi;
// @ts-ignore
const p = ctx[participantOptions.name] as Participant;
if (p) {
if (ctx.testProperties.useIFrameApi) {
if (participantOptions.iFrameApi) {
await p.switchToIFrame();
}
@@ -194,7 +176,7 @@ async function joinParticipant( // eslint-disable-line max-params
return p;
}
if (ctx.testProperties.useIFrameApi) {
if (participantOptions.iFrameApi) {
// when loading url make sure we are on the top page context or strange errors may occur
await p.switchToMainFrame();
}
@@ -209,87 +191,12 @@ async function joinParticipant( // eslint-disable-line max-params
// @ts-ignore
ctx[participantOptions.name] = newParticipant;
let tenant = options?.tenant;
if (options?.preferGenerateToken && !ctx.testProperties.useIFrameApi
&& config.iframe.usesJaas && config.iframe.tenant) {
tenant = config.iframe.tenant;
}
if (!tenant && ctx.testProperties.useIFrameApi) {
tenant = config.iframe.tenant;
}
return await newParticipant.joinConference({
...options,
tenant: tenant,
roomName: options?.roomName || ctx.roomName,
});
}
/**
* Toggles the mute state of a specific Meet conference participant and verifies that a specific other Meet
* conference participants sees a specific mute state for the former.
*
* @param {Participant} testee - The {@code Participant} which represents the Meet conference participant whose
* mute state is to be toggled.
* @param {Participant} observer - The {@code Participant} which represents the Meet conference participant to verify
* the mute state of {@code testee}.
* @returns {Promise<void>}
*/
export async function muteAudioAndCheck(testee: Participant, observer: Participant): Promise<void> {
await testee.getToolbar().clickAudioMuteButton();
await observer.getFilmstrip().assertAudioMuteIconIsDisplayed(testee);
await testee.getFilmstrip().assertAudioMuteIconIsDisplayed(testee);
await observer.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee);
await testee.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee);
}
/**
* Unmute audio, checks if the local UI has been updated accordingly and then does the verification from
* the other observer participant perspective.
* @param testee
* @param observer
*/
export async function unmuteAudioAndCheck(testee: Participant, observer: Participant) {
await testee.getNotifications().closeAskToUnmuteNotification(true);
await testee.getNotifications().closeAVModerationMutedNotification(true);
await testee.getToolbar().clickAudioUnmuteButton();
await testee.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, true);
await observer.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, true);
await testee.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee, true);
await observer.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee, true);
}
/**
* Stop the video on testee and check on observer.
* @param testee
* @param observer
*/
export async function unmuteVideoAndCheck(testee: Participant, observer: Participant): Promise<void> {
await testee.getToolbar().clickVideoUnmuteButton();
await testee.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee, true);
await observer.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee, true);
}
/**
* Starts the video on testee and check on observer.
* @param testee
* @param observer
*/
export async function muteVideoAndCheck(testee: Participant, observer: Participant): Promise<void> {
await testee.getToolbar().clickVideoMuteButton();
await testee.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee);
await observer.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee);
}
/**
* Parse a JID string.
* @param str the string to parse.

View File

@@ -37,8 +37,8 @@ export type IContext = {
export type IParticipantOptions = {
/** Whether it should use the iFrame API. */
iFrameApi?: boolean;
/** Must be 'p1', 'p2', 'p3', or 'p4'. */
name: string;
/** Determines the browser instance to use. */
name: 'p1' | 'p2' | 'p3' | 'p4';
/** An optional token to use. */
token?: IToken;
};
@@ -80,12 +80,6 @@ export type IJoinOptions = {
*/
configOverwrite?: IConfig;
/**
* 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.
*/
preferGenerateToken?: boolean;
/**
* To be able to override the ctx generated room name. If missing the one from the context will be used.
*/
@@ -96,11 +90,6 @@ export type IJoinOptions = {
*/
skipDisplayName?: boolean;
/**
* Whether to skip setting the moderator role for the first participant (whether to use jwt for it).
*/
skipFirstModerator?: boolean;
/**
* Whether to skip in meeting checks like ice connected and send receive data. For single in meeting participant.
*/

View File

@@ -20,6 +20,20 @@ export default class ChatPanel extends BasePageObject {
await this.participant.driver.keys([ 'c' ]);
}
async sendMessage(message: string) {
if (!await this.isOpen()) {
await this.pressShortcut();
}
if (!await this.isOpen()) {
throw new Error('Chat panel failed to open');
}
const inputField = this.participant.driver.$('#chat-input');
await inputField.click();
await this.participant.driver.keys(`${message}\n`);
}
/**
* Opens the polls tab in the chat panel.
*/

View File

@@ -4,10 +4,12 @@ import {
ensureOneParticipant,
ensureTwoParticipants,
joinSecondParticipant,
} from '../../helpers/participants';
import {
muteAudioAndCheck,
unmuteAudioAndCheck,
unmuteVideoAndCheck
} from '../../helpers/participants';
} from '../helpers/mute';
describe('Mute', () => {
it('joining the meeting', () => ensureTwoParticipants());

View File

@@ -1,4 +1,5 @@
import { ensureTwoParticipants, muteVideoAndCheck, unmuteVideoAndCheck } from '../../helpers/participants';
import { ensureTwoParticipants } from '../../helpers/participants';
import { muteVideoAndCheck, unmuteVideoAndCheck } from '../helpers/mute';
describe('Stop video', () => {
it('joining the meeting', () => ensureTwoParticipants());

View File

@@ -1,5 +1,6 @@
import type { Participant } from '../../helpers/Participant';
import { ensureThreeParticipants, muteAudioAndCheck } from '../../helpers/participants';
import { ensureThreeParticipants } from '../../helpers/participants';
import { muteAudioAndCheck } from '../helpers/mute';
describe('ActiveSpeaker', () => {
it('testActiveSpeaker', async () => {

View File

@@ -3,10 +3,9 @@ import { config } from '../../helpers/TestsConfig';
import {
ensureOneParticipant,
ensureThreeParticipants, ensureTwoParticipants,
hangupAllParticipants,
unmuteAudioAndCheck,
unmuteVideoAndCheck
hangupAllParticipants
} from '../../helpers/participants';
import { unmuteAudioAndCheck, unmuteVideoAndCheck } from '../helpers/mute';
describe('AVModeration', () => {
@@ -78,8 +77,9 @@ describe('AVModeration', () => {
});
it('hangup and change moderator', async () => {
// no moderator switching if jaas is available.
if (config.iframe.usesJaas) {
// The test below is only correct when the environment is configured to automatically elect a new moderator
// when the moderator leaves. For environments where this is not the case, the test is skipped.
if (!config.autoModerator) {
return;
}

View File

@@ -1,8 +1,5 @@
import {
ensureThreeParticipants,
ensureTwoParticipants,
unmuteVideoAndCheck
} from '../../helpers/participants';
import { ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants';
import { unmuteVideoAndCheck } from '../helpers/mute';
const EMAIL = 'support@jitsi.org';
const HASH = '38f014e4b7dde0f64f8157d26a8c812e';

View File

@@ -196,8 +196,9 @@ describe('Lobby', () => {
});
it('change of moderators in lobby', async () => {
// no moderator switching if jaas is available.
if (config.iframe.usesJaas) {
// The test below is only correct when the environment is configured to automatically elect a new moderator
// when the moderator leaves. For environments where this is not the case, the test is skipped.
if (!config.autoModerator) {
return;
}
await hangupAllParticipants();
@@ -288,8 +289,9 @@ describe('Lobby', () => {
});
it('moderator leaves while lobby enabled', async () => {
// no moderator switching if jaas is available.
if (config.iframe.usesJaas) {
// The test below is only correct when the environment is configured to automatically elect a new moderator
// when the moderator leaves. For environments where this is not the case, the test is skipped.
if (!config.autoModerator) {
return;
}
const { p1, p2, p3 } = ctx;

View File

@@ -4,9 +4,9 @@ import {
ensureTwoParticipants,
hangupAllParticipants,
joinSecondParticipant,
joinThirdParticipant,
unmuteVideoAndCheck
joinThirdParticipant
} from '../../helpers/participants';
import { unmuteVideoAndCheck } from '../helpers/mute';
describe('StartMuted', () => {
it('checkboxes test', async () => {

View File

@@ -1,5 +1,6 @@
import process from 'node:process';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureOneParticipant } from '../../helpers/participants';
import { cleanup, dialIn, isDialInEnabled, waitForAudioFromDialInParticipant } from '../helpers/DialIn';
@@ -12,9 +13,18 @@ describe('Dial-In', () => {
return;
}
await ensureOneParticipant({ preferGenerateToken: true });
// This is a temporary hack to avoid failing when running against a jaas env. The same cases are covered in
// jaas/dial/dialin.spec.ts.
if (testsConfig.jaas.enabled) {
ctx.skipSuiteTests = true;
return;
}
await ensureOneParticipant();
expect(await ctx.p1.isInMuc()).toBe(true);
// check dial-in is enabled
if (!await isDialInEnabled(ctx.p1)) {
ctx.skipSuiteTests = true;
}
@@ -41,7 +51,7 @@ describe('Dial-In', () => {
});
it('invite dial-in participant', async () => {
await dialIn(ctx.p1);
await dialIn(await ctx.p1.getDialInPin());
});
it('wait for audio from dial-in participant', async () => {

View File

@@ -1,85 +1,41 @@
import { Participant } from '../../helpers/Participant';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureOneParticipant } from '../../helpers/participants';
import { isDialInEnabled } from '../helpers/DialIn';
import { assertDialInDisplayed, assertUrlDisplayed, isDialInEnabled, verifyMoreNumbersPage } from '../helpers/DialIn';
describe('Invite', () => {
it('join participant', () => ensureOneParticipant({ preferGenerateToken: true }));
let p1: Participant;
let dialInEnabled: boolean;
it('url displayed', async () => {
const { p1 } = ctx;
const inviteDialog = p1.getInviteDialog();
it('setup', async () => {
// This is a temporary hack to avoid failing when running against a jaas env. The same cases are covered in
// jaas/dial/dialin.spec.ts.
if (testsConfig.jaas.enabled) {
ctx.skipSuiteTests = true;
await inviteDialog.open();
await inviteDialog.waitTillOpen();
return;
}
const driverUrl = await p1.driver.getUrl();
await ensureOneParticipant();
expect(driverUrl.includes(await inviteDialog.getMeetingURL())).toBe(true);
await inviteDialog.clickCloseButton();
await inviteDialog.waitTillOpen(true);
p1 = ctx.p1;
dialInEnabled = await isDialInEnabled(p1);
});
it('url displayed', () => assertUrlDisplayed(p1));
it('dial-in displayed', async () => {
const { p1 } = ctx;
if (!await isDialInEnabled(p1)) {
if (!dialInEnabled) {
return;
}
const inviteDialog = p1.getInviteDialog();
await inviteDialog.open();
await inviteDialog.waitTillOpen();
expect((await inviteDialog.getDialInNumber()).length > 0).toBe(true);
expect((await inviteDialog.getPinNumber()).length > 0).toBe(true);
await assertDialInDisplayed(p1);
});
it('view more numbers', async () => {
const { p1 } = ctx;
if (!await isDialInEnabled(p1)) {
it('view more numbers page', async () => {
if (!dialInEnabled) {
return;
}
const inviteDialog = p1.getInviteDialog();
await inviteDialog.open();
await inviteDialog.waitTillOpen();
const windows = await p1.driver.getWindowHandles();
expect(windows.length).toBe(1);
const meetingWindow = windows[0];
const displayedNumber = await inviteDialog.getDialInNumber();
const displayedPin = await inviteDialog.getPinNumber();
await inviteDialog.openDialInNumbersPage();
const newWindow = (await p1.driver.getWindowHandles()).filter(w => w !== meetingWindow);
expect(newWindow.length).toBe(1);
const moreNumbersWindow = newWindow[0];
await p1.driver.switchWindow(moreNumbersWindow);
await browser.pause(10000);
await p1.driver.$('.dial-in-numbers-list').waitForExist();
const conferenceIdMessage = p1.driver.$('//div[contains(@class, "pinLabel")]');
expect((await conferenceIdMessage.getText()).replace(/ /g, '').includes(displayedPin)).toBe(true);
const numbers = p1.driver.$$('.dial-in-number');
const nums = await numbers.filter(
async el => (await el.getText()).trim() === displayedNumber);
expect(nums.length).toBe(1);
await verifyMoreNumbersPage(p1);
});
});

View File

@@ -52,17 +52,10 @@ export async function isDialInEnabled(participant: Participant) {
/**
* Sends a request to the REST API to dial in the participant using the provided pin.
* @param participant
* @param pin the pin to use when dialing in
*/
export async function dialIn(participant: Participant) {
if (!await participant.isInMuc()) {
// local participant did not join abort
return;
}
const dialInPin = await participant.getDialInPin();
const restUrl = process.env.DIAL_IN_REST_URL?.replace('{0}', dialInPin);
export async function dialIn(pin: string) {
const restUrl = process.env.DIAL_IN_REST_URL?.replace('{0}', pin);
// we have already checked in the first test that DIAL_IN_REST_URL exist so restUrl cannot be ''
const responseData: string = await new Promise((resolve, reject) => {
@@ -88,3 +81,66 @@ export async function dialIn(participant: Participant) {
console.log(`dial-in.test.call_session_history_id:${JSON.parse(responseData).call_session_history_id}`);
console.log(`API response:${responseData}`);
}
export async function assertUrlDisplayed(p: Participant) {
const inviteDialog = p.getInviteDialog();
await inviteDialog.open();
await inviteDialog.waitTillOpen();
const driverUrl = await p.driver.getUrl();
expect(driverUrl.includes(await inviteDialog.getMeetingURL())).toBe(true);
await inviteDialog.clickCloseButton();
await inviteDialog.waitTillOpen(true);
}
export async function assertDialInDisplayed(p: Participant) {
const inviteDialog = p.getInviteDialog();
await inviteDialog.open();
await inviteDialog.waitTillOpen();
expect((await inviteDialog.getDialInNumber()).length > 0).toBe(true);
expect((await inviteDialog.getPinNumber()).length > 0).toBe(true);
}
export async function verifyMoreNumbersPage(p: Participant) {
const inviteDialog = p.getInviteDialog();
await inviteDialog.open();
await inviteDialog.waitTillOpen();
const windows = await p.driver.getWindowHandles();
expect(windows.length).toBe(1);
const meetingWindow = windows[0];
const displayedNumber = await inviteDialog.getDialInNumber();
const displayedPin = await inviteDialog.getPinNumber();
await inviteDialog.openDialInNumbersPage();
const newWindow = (await p.driver.getWindowHandles()).filter(w => w !== meetingWindow);
expect(newWindow.length).toBe(1);
const moreNumbersWindow = newWindow[0];
await p.driver.switchWindow(moreNumbersWindow);
await browser.pause(10000);
await p.driver.$('.dial-in-numbers-list').waitForExist();
const conferenceIdMessage = p.driver.$('//div[contains(@class, "pinLabel")]');
expect((await conferenceIdMessage.getText()).replace(/ /g, '').includes(displayedPin)).toBe(true);
const numbers = p.driver.$$('.dial-in-number');
const nums = await numbers.filter(
async el => (await el.getText()).trim() === displayedNumber);
expect(nums.length).toBe(1);
await p.driver.switchWindow(meetingWindow);
}

View File

@@ -0,0 +1,64 @@
import { Participant } from '../../helpers/Participant';
/**
* Toggles the mute state of a specific Meet conference participant and verifies that a specific other Meet
* conference participants sees a specific mute state for the former.
*
* @param {Participant} testee - The {@code Participant} which represents the Meet conference participant whose
* mute state is to be toggled.
* @param {Participant} observer - The {@code Participant} which represents the Meet conference participant to verify
* the mute state of {@code testee}.
* @returns {Promise<void>}
*/
export async function muteAudioAndCheck(testee: Participant, observer: Participant): Promise<void> {
await testee.getToolbar().clickAudioMuteButton();
await observer.getFilmstrip().assertAudioMuteIconIsDisplayed(testee);
await testee.getFilmstrip().assertAudioMuteIconIsDisplayed(testee);
await observer.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee);
await testee.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee);
}
/**
* Unmute audio, checks if the local UI has been updated accordingly and then does the verification from
* the other observer participant perspective.
* @param testee
* @param observer
*/
export async function unmuteAudioAndCheck(testee: Participant, observer: Participant) {
await testee.getNotifications().closeAskToUnmuteNotification(true);
await testee.getNotifications().closeAVModerationMutedNotification(true);
await testee.getToolbar().clickAudioUnmuteButton();
await testee.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, true);
await observer.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, true);
await testee.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee, true);
await observer.getParticipantsPane().assertAudioMuteIconIsDisplayed(testee, true);
}
/**
* Stop the video on testee and check on observer.
* @param testee
* @param observer
*/
export async function unmuteVideoAndCheck(testee: Participant, observer: Participant): Promise<void> {
await testee.getToolbar().clickVideoUnmuteButton();
await testee.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee, true);
await observer.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee, true);
}
/**
* Starts the video on testee and check on observer.
* @param testee
* @param observer
*/
export async function muteVideoAndCheck(testee: Participant, observer: Participant): Promise<void> {
await testee.getToolbar().clickVideoMuteButton();
await testee.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee);
await observer.getParticipantsPane().assertVideoMuteIconIsDisplayed(testee);
}

View File

@@ -2,20 +2,19 @@ import { expect } from '@wdio/globals';
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
import { fetchJson } from '../../helpers/utils';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { joinMuc } from '../../helpers/joinMuc';
setTestProperties(__filename, {
useIFrameApi: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Chat', () => {
it('joining the meeting', async () => {
await ensureTwoParticipants();
describe('iFrame API for Chat', () => {
let p1: Participant, p2: Participant;
const { p1, p2 } = ctx;
it('setup', async () => {
p1 = await joinMuc({ name: 'p1', iFrameApi: true, token: testsConfig.jwt.preconfiguredToken });
p2 = await joinMuc({ name: 'p2', iFrameApi: true });
if (await p1.execute(() => config.disableIframeAPI)) {
// skip the test if iframeAPI is disabled
@@ -24,19 +23,11 @@ describe('Chat', () => {
return;
}
// let's populate endpoint ids
await Promise.all([
p1.getEndpointId(),
p2.getEndpointId()
]);
await p1.switchToMainFrame();
await p2.switchToMainFrame();
});
it('send message', async () => {
const { p1, p2 } = ctx;
await p1.switchToMainFrame();
await p2.switchToMainFrame();
await p2.getIframeAPI().addEventListener('chatUpdated');
await p2.getIframeAPI().addEventListener('incomingMessage');
await p1.getIframeAPI().addEventListener('outgoingMessage');
@@ -65,7 +56,7 @@ describe('Chat', () => {
privateMessage: boolean;
} = await p2.getIframeAPI().getEventResult('incomingMessage');
expect(incomingMessageEvent).toEqual({
expect(incomingMessageEvent).toMatchObject({
from: await p1.getEndpointId(),
message: testMessage,
nick: p1.name,
@@ -88,8 +79,6 @@ describe('Chat', () => {
});
it('toggle chat', async () => {
const { p1, p2 } = ctx;
await p2.getIframeAPI().executeCommand('toggleChat');
await testSendGroupMessageWithChatOpen(p1, p2);
@@ -100,13 +89,10 @@ describe('Chat', () => {
});
it('private chat', async () => {
const { p1, p2 } = ctx;
const testMessage = 'Hello private world!';
const p2Id = await p2.getEndpointId();
const p1Id = await p1.getEndpointId();
await p1.getIframeAPI().executeCommand('initiatePrivateChat', p2Id);
await p1.getIframeAPI().executeCommand('sendChatMessage', testMessage, p2Id);
await p1.getIframeAPI().executeCommand('initiatePrivateChat', await p2.getEndpointId());
await p1.getIframeAPI().executeCommand('sendChatMessage', testMessage, await p2.getEndpointId());
const incomingMessageEvent = await p2.driver.waitUntil(
() => p2.getIframeAPI().getEventResult('incomingMessage'), {
@@ -114,8 +100,8 @@ describe('Chat', () => {
timeoutMsg: 'Chat was not received'
});
expect(incomingMessageEvent).toEqual({
from: p1Id,
expect(incomingMessageEvent).toMatchObject({
from: await p1.getEndpointId(),
message: testMessage,
nick: p1.name,
privateMessage: true
@@ -133,47 +119,22 @@ describe('Chat', () => {
await testSendGroupMessageWithChatOpen(p1, p2);
});
it('chat upload chat', async () => {
const { p1, p2, webhooksProxy } = ctx;
await p1.getIframeAPI().executeCommand('hangup');
await p2.getIframeAPI().executeCommand('hangup');
if (webhooksProxy) {
const event: {
data: {
preAuthenticatedLink: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('CHAT_UPLOADED');
expect('CHAT_UPLOADED').toBe(event.eventType);
expect(event.data.preAuthenticatedLink).toBeDefined();
const uploadedChat: any = await fetchJson(event.data.preAuthenticatedLink);
expect(uploadedChat.messageType).toBe('CHAT');
expect(uploadedChat.messages).toBeDefined();
expect(uploadedChat.messages.length).toBe(3);
}
});
});
/**
* Test sending a group message with the chat open.
* @param p1
* @param p2
* Send a group message from [sender], verify that it was received correctly by [receiver].
* @param sender the Participant that sends the message.
* @param receiver the Participant that receives the message.
*/
async function testSendGroupMessageWithChatOpen(p1: Participant, p2: Participant) {
async function testSendGroupMessageWithChatOpen(sender: Participant, receiver: Participant) {
const testMessage = 'Hello world again';
await p1.getIframeAPI().executeCommand('sendChatMessage', testMessage);
await sender.getIframeAPI().executeCommand('sendChatMessage', testMessage);
const chatUpdatedEvent: {
isOpen: boolean;
unreadCount: number;
} = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('chatUpdated'), {
} = await receiver.driver.waitUntil(() => receiver.getIframeAPI().getEventResult('chatUpdated'), {
timeout: 3000,
timeoutMsg: 'Chat was not updated'
});
@@ -183,16 +144,16 @@ async function testSendGroupMessageWithChatOpen(p1: Participant, p2: Participant
unreadCount: 0
});
const incomingMessageEvent = await p2.driver.waitUntil(
() => p2.getIframeAPI().getEventResult('incomingMessage'), {
const incomingMessageEvent = await receiver.driver.waitUntil(
() => receiver.getIframeAPI().getEventResult('incomingMessage'), {
timeout: 3000,
timeoutMsg: 'Chat was not received'
});
expect(incomingMessageEvent).toEqual({
from: await p1.getEndpointId(),
expect(incomingMessageEvent).toMatchObject({
from: await sender.getEndpointId(),
message: testMessage,
nick: p1.name,
nick: sender.name,
privateMessage: false
});
}

View File

@@ -1,214 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureOneParticipant } from '../../helpers/participants';
import {
cleanup,
dialIn,
isDialInEnabled,
waitForAudioFromDialInParticipant
} from '../helpers/DialIn';
setTestProperties(__filename, {
useIFrameApi: true,
useWebhookProxy: true
});
const customerId = testsConfig.iframe.customerId;
describe('Invite iframeAPI', () => {
let dialInDisabled: boolean;
let dialOutDisabled: boolean;
let sipJibriDisabled: 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;
}
dialOutDisabled = Boolean(!await p1.execute(() => config.dialOutAuthUrl));
sipJibriDisabled = Boolean(!await p1.execute(() => config.inviteServiceUrl));
// check dial-in is enabled
if (!await isDialInEnabled(ctx.p1) || !process.env.DIAL_IN_REST_URL) {
dialInDisabled = true;
}
});
it('dial-in', async () => {
if (dialInDisabled) {
return;
}
const { p1 } = ctx;
const dialInPin = await p1.getDialInPin();
expect(dialInPin.length >= 8).toBe(true);
await dialIn(p1);
if (!await p1.isInMuc()) {
// local participant did not join abort
return;
}
await waitForAudioFromDialInParticipant(p1);
await checkDialEvents(p1, 'in', 'DIAL_IN_STARTED', 'DIAL_IN_ENDED');
});
it('dial-out', async () => {
if (dialOutDisabled || !process.env.DIAL_OUT_URL) {
return;
}
const { p1 } = ctx;
await p1.switchToMainFrame();
await p1.getIframeAPI().invitePhone(process.env.DIAL_OUT_URL);
await p1.switchToIFrame();
await p1.waitForParticipants(1);
await waitForAudioFromDialInParticipant(p1);
await checkDialEvents(p1, 'out', 'DIAL_OUT_STARTED', 'DIAL_OUT_ENDED');
});
it('sip jibri', async () => {
if (sipJibriDisabled || !process.env.SIP_JIBRI_DIAL_OUT_URL) {
return;
}
const { p1 } = ctx;
await p1.switchToMainFrame();
await p1.getIframeAPI().inviteSIP(process.env.SIP_JIBRI_DIAL_OUT_URL);
await p1.switchToIFrame();
await p1.waitForParticipants(1);
await waitForAudioFromDialInParticipant(p1);
const { webhooksProxy } = ctx;
if (webhooksProxy) {
const sipCallOutStartedEvent: {
customerId: string;
data: {
participantFullJid: string;
participantId: string;
participantJid: string;
sipAddress: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_STARTED');
expect('SIP_CALL_OUT_STARTED').toBe(sipCallOutStartedEvent.eventType);
expect(sipCallOutStartedEvent.data.sipAddress).toBe(`sip:${process.env.SIP_JIBRI_DIAL_OUT_URL}`);
expect(sipCallOutStartedEvent.customerId).toBe(customerId);
const participantId = sipCallOutStartedEvent.data.participantId;
const participantJid = sipCallOutStartedEvent.data.participantJid;
const participantFullJid = sipCallOutStartedEvent.data.participantFullJid;
await cleanup(p1);
const sipCallOutEndedEvent: {
customerId: string;
data: {
direction: string;
participantFullJid: string;
participantId: string;
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_ENDED');
expect('SIP_CALL_OUT_ENDED').toBe(sipCallOutEndedEvent.eventType);
expect(sipCallOutEndedEvent.customerId).toBe(customerId);
expect(sipCallOutEndedEvent.data.participantFullJid).toBe(participantFullJid);
expect(sipCallOutEndedEvent.data.participantId).toBe(participantId);
expect(sipCallOutEndedEvent.data.participantJid).toBe(participantJid);
} else {
await cleanup(p1);
}
});
});
/**
* Checks the dial events for a participant and clean up at the end.
* @param participant
* @param startedEventName
* @param endedEventName
* @param direction
*/
async function checkDialEvents(participant: Participant, direction: string, startedEventName: string, endedEventName: string) {
const { webhooksProxy } = ctx;
if (webhooksProxy) {
const dialInStartedEvent: {
customerId: string;
data: {
direction: string;
participantFullJid: string;
participantId: string;
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent(startedEventName);
expect(startedEventName).toBe(dialInStartedEvent.eventType);
expect(dialInStartedEvent.data.direction).toBe(direction);
expect(dialInStartedEvent.customerId).toBe(customerId);
const participantId = dialInStartedEvent.data.participantId;
const participantJid = dialInStartedEvent.data.participantJid;
const participantFullJid = dialInStartedEvent.data.participantFullJid;
const usageEvent: {
customerId: string;
data: any;
eventType: string;
} = await webhooksProxy.waitForEvent('USAGE');
expect('USAGE').toBe(usageEvent.eventType);
expect(usageEvent.customerId).toBe(customerId);
expect(usageEvent.data.some((el: any) =>
el.participantId === participantId && el.callDirection === direction)).toBe(true);
await cleanup(participant);
const dialInEndedEvent: {
customerId: string;
data: {
direction: string;
participantFullJid: string;
participantId: string;
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent(endedEventName);
expect(endedEventName).toBe(dialInEndedEvent.eventType);
expect(dialInEndedEvent.customerId).toBe(customerId);
expect(dialInEndedEvent.data.participantFullJid).toBe(participantFullJid);
expect(dialInEndedEvent.data.participantId).toBe(participantId);
expect(dialInEndedEvent.data.participantJid).toBe(participantJid);
} else {
await cleanup(participant);
}
}

View File

@@ -1,64 +1,18 @@
import { isEqual } from 'lodash-es';
import { P1, P2, Participant } from '../../helpers/Participant';
import { P1, P2 } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureTwoParticipants, parseJid } from '../../helpers/participants';
setTestProperties(__filename, {
useIFrameApi: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
/**
* Tests PARTICIPANT_LEFT webhook.
*/
async function checkParticipantLeftHook(p: Participant, reason: string, checkId = false, conferenceJid: string) {
const { webhooksProxy } = ctx;
if (webhooksProxy) {
// PARTICIPANT_LEFT webhook
// @ts-ignore
const event: {
customerId: string;
data: {
conference: string;
disconnectReason: string;
group: string;
id: string;
isBreakout: boolean;
name: string;
participantId: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_LEFT');
expect('PARTICIPANT_LEFT').toBe(event.eventType);
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.disconnectReason).toBe(reason);
expect(event.data.isBreakout).toBe(false);
expect(event.data.participantId).toBe(await p.getEndpointId());
expect(event.data.name).toBe(p.name);
if (checkId) {
const jwtPayload = p.getToken()?.payload;
expect(event.data.id).toBe(jwtPayload?.context?.user?.id);
expect(event.data.group).toBe(jwtPayload?.context?.group);
expect(event.customerId).toBe(testsConfig.iframe.customerId);
}
}
}
describe('Participants presence', () => {
let conferenceJid: string = '';
it('joining the meeting', async () => {
// ensure 2 participants one moderator and one guest, we will load both with iframeAPI
await ensureTwoParticipants();
await ensureTwoParticipants({}, { name: 'p1', iFrameApi: true });
const { p1, p2, webhooksProxy } = ctx;
const { p1, p2 } = ctx;
if (await p1.execute(() => config.disableIframeAPI)) {
// skip the test if iframeAPI is disabled
@@ -67,44 +21,20 @@ describe('Participants presence', () => {
return;
}
// let's populate endpoint ids
await Promise.all([
p1.getEndpointId(),
p2.getEndpointId()
p1.switchToMainFrame(),
p2.switchToMainFrame()
]);
await p1.switchToMainFrame();
await p2.switchToMainFrame();
expect(await p1.getIframeAPI().getEventResult('isModerator')).toBe(true);
expect(await p2.getIframeAPI().getEventResult('isModerator')).toBe(false);
expect(await p1.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined();
expect(await p2.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined();
if (webhooksProxy) {
// USAGE webhook
// @ts-ignore
const event: {
data: [
{ participantId: string; }
];
eventType: string;
} = await webhooksProxy.waitForEvent('USAGE');
expect('USAGE').toBe(event.eventType);
const p1EpId = await p1.getEndpointId();
const p2EpId = await p2.getEndpointId();
expect(event.data.filter(d => d.participantId === p1EpId
|| d.participantId === p2EpId).length).toBe(2);
}
});
it('participants info',
async () => {
const { p1, roomName, webhooksProxy } = ctx;
const { p1, roomName } = ctx;
const roomsInfo = (await p1.getIframeAPI().getRoomsInfo()).rooms[0];
expect(roomsInfo).toBeDefined();
@@ -116,9 +46,6 @@ describe('Participants presence', () => {
expect(roomNode).toBe(roomName);
const { node, resource } = parseJid(roomsInfo.jid);
conferenceJid = roomsInfo.jid.substring(0, roomsInfo.jid.indexOf('/'));
const p1EpId = await p1.getEndpointId();
expect(node).toBe(roomName);
@@ -126,30 +53,12 @@ describe('Participants presence', () => {
expect(roomsInfo.participants.length).toBe(2);
expect(await p1.getIframeAPI().getNumberOfParticipants()).toBe(2);
if (webhooksProxy) {
// ROOM_CREATED webhook
// @ts-ignore
const event: {
data: {
conference: string;
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_CREATED');
expect('ROOM_CREATED').toBe(event.eventType);
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
}
}
);
it('participants pane', async () => {
const { p1 } = ctx;
await p1.switchToMainFrame();
expect(await p1.getIframeAPI().isParticipantsPaneOpen()).toBe(false);
await p1.getIframeAPI().addEventListener('participantsPaneToggled');
@@ -163,68 +72,7 @@ describe('Participants presence', () => {
expect((await p1.getIframeAPI().getEventResult('participantsPaneToggled'))?.open).toBe(false);
});
it('grant moderator', async () => {
const { p1, p2, webhooksProxy } = ctx;
const p2EpId = await p2.getEndpointId();
await p1.getIframeAPI().clearEventResults('participantRoleChanged');
await p2.getIframeAPI().clearEventResults('participantRoleChanged');
await p1.getIframeAPI().executeCommand('grantModerator', p2EpId);
await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('isModerator'), {
timeout: 3000,
timeoutMsg: 'Moderator role not granted'
});
type RoleChangedEvent = {
id: string;
role: string;
};
const event1: RoleChangedEvent = await p1.driver.waitUntil(
() => p1.getIframeAPI().getEventResult('participantRoleChanged'), {
timeout: 3000,
timeoutMsg: 'Role was not update on p1 side'
});
expect(event1?.id).toBe(p2EpId);
expect(event1?.role).toBe('moderator');
const event2: RoleChangedEvent = await p2.driver.waitUntil(
() => p2.getIframeAPI().getEventResult('participantRoleChanged'), {
timeout: 3000,
timeoutMsg: 'Role was not update on p2 side'
});
expect(event2?.id).toBe(p2EpId);
expect(event2?.role).toBe('moderator');
if (webhooksProxy) {
// ROLE_CHANGED webhook
// @ts-ignore
const event: {
data: {
grantedBy: {
participantId: string;
};
grantedTo: {
participantId: string;
};
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROLE_CHANGED');
expect('ROLE_CHANGED').toBe(event.eventType);
expect(event.data.role).toBe('moderator');
expect(event.data.grantedBy.participantId).toBe(await p1.getEndpointId());
expect(event.data.grantedTo.participantId).toBe(await p2.getEndpointId());
}
});
it('kick participant', async () => {
// we want to join second participant with token, so we can check info in webhook
await ctx.p2.getIframeAPI().clearEventResults('videoConferenceLeft');
await ctx.p2.getIframeAPI().addEventListener('videoConferenceLeft');
await ctx.p2.switchToMainFrame();
@@ -235,13 +83,9 @@ describe('Participants presence', () => {
timeoutMsg: 'videoConferenceLeft not received'
});
await ensureTwoParticipants({
preferGenerateToken: true
});
await ensureTwoParticipants({}, { name: 'p1', iFrameApi: true });
const { p1, p2, roomName, webhooksProxy } = ctx;
webhooksProxy?.clearCache();
const { p1, p2, roomName } = ctx;
const p1EpId = await p1.getEndpointId();
const p2EpId = await p2.getEndpointId();
@@ -252,10 +96,6 @@ describe('Participants presence', () => {
await p1.switchToMainFrame();
await p2.switchToMainFrame();
const roomsInfo = (await p1.getIframeAPI().getRoomsInfo()).rooms[0];
conferenceJid = roomsInfo.jid.substring(0, roomsInfo.jid.indexOf('/'));
await p1.getIframeAPI().addEventListener('participantKickedOut');
await p2.getIframeAPI().addEventListener('participantKickedOut');
@@ -273,8 +113,6 @@ describe('Participants presence', () => {
timeoutMsg: 'participantKickedOut event not received on p2 side'
});
await checkParticipantLeftHook(p2, 'kicked', true, conferenceJid);
expect(eventP1).toBeDefined();
expect(eventP2).toBeDefined();
@@ -314,40 +152,15 @@ describe('Participants presence', () => {
});
it('join after kick', async () => {
const { p1, webhooksProxy } = ctx;
const { p1 } = ctx;
await p1.getIframeAPI().addEventListener('participantJoined');
await p1.getIframeAPI().addEventListener('participantMenuButtonClick');
webhooksProxy?.clearCache();
// join again
await ensureTwoParticipants();
await ensureTwoParticipants({}, { name: 'p1', iFrameApi: true });
const { p2 } = ctx;
if (webhooksProxy) {
// PARTICIPANT_JOINED webhook
// @ts-ignore
const event: {
data: {
conference: string;
isBreakout: boolean;
moderator: boolean;
name: string;
participantId: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED');
expect('PARTICIPANT_JOINED').toBe(event.eventType);
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
expect(event.data.moderator).toBe(false);
expect(event.data.name).toBe(await p2.getLocalDisplayName());
expect(event.data.participantId).toBe(await p2.getEndpointId());
expect(event.data.name).toBe(p2.name);
}
await p1.switchToMainFrame();
const event = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('participantJoined'), {
@@ -411,8 +224,6 @@ describe('Participants presence', () => {
expect(eventConferenceLeftP2).toBeDefined();
expect(eventConferenceLeftP2.roomName).toBe(roomName);
await checkParticipantLeftHook(p2, 'left', false, conferenceJid);
const eventReadyToCloseP2 = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('readyToClose'), {
timeout: 2000,
timeoutMsg: 'readyToClose not received'
@@ -422,7 +233,7 @@ describe('Participants presence', () => {
});
it('dispose conference', async () => {
const { p1, roomName, webhooksProxy } = ctx;
const { p1, roomName } = ctx;
await p1.switchToMainFrame();
@@ -441,23 +252,6 @@ describe('Participants presence', () => {
expect(eventConferenceLeft).toBeDefined();
expect(eventConferenceLeft.roomName).toBe(roomName);
await checkParticipantLeftHook(p1, 'left', true, conferenceJid);
if (webhooksProxy) {
// ROOM_DESTROYED webhook
// @ts-ignore
const event: {
data: {
conference: string;
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_DESTROYED');
expect('ROOM_DESTROYED').toBe(event.eventType);
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
}
const eventReadyToClose = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('readyToClose'), {
timeout: 2000,
timeoutMsg: 'readyToClose not received'

View File

@@ -1,125 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
useIFrameApi: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Visitors', () => {
it('joining the meeting', async () => {
const { webhooksProxy } = ctx;
if (webhooksProxy) {
webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true
};
}
await ensureOneParticipant();
const { p1 } = ctx;
if (await p1.execute(() => config.disableIframeAPI)) {
// skip the test if iframeAPI is disabled or visitors are not supported
ctx.skipSuiteTests = true;
return;
}
await p1.driver.waitUntil(() => p1.execute(() => APP.conference._room.isVisitorsSupported()), {
timeout: 2000
}).then(async () => {
await p1.switchToMainFrame();
}).catch(() => {
ctx.skipSuiteTests = true;
});
});
it('visitor joins', async () => {
await ensureTwoParticipants({
preferGenerateToken: true,
tokenOptions: { visitor: true },
skipInMeetingChecks: true
});
const { p1, p2, webhooksProxy } = ctx;
await p2.waitForReceiveMedia(15_000, 'Visitor is not receiving media');
await p2.waitForRemoteStreams(1);
const p2Visitors = p2.getVisitors();
const p1Visitors = p1.getVisitors();
await p2.driver.waitUntil(() => p2Visitors.hasVisitorsDialog(), {
timeout: 5000,
timeoutMsg: 'Missing visitors dialog'
});
expect((await p1Visitors.getVisitorsCount()).trim()).toBe('1');
expect((await p1Visitors.getVisitorsHeaderFromParticipantsPane()).trim()).toBe('Viewers 1');
if (webhooksProxy) {
// PARTICIPANT_JOINED webhook
// @ts-ignore
const event: {
customerId: string;
data: {
avatar: string;
email: string;
group: string;
id: string;
name: string;
participantJid: string;
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED');
const jwtPayload = p2.getToken()?.payload;
expect('PARTICIPANT_JOINED').toBe(event.eventType);
expect(event.data.avatar).toBe(jwtPayload.context.user.avatar);
expect(event.data.email).toBe(jwtPayload.context.user.email);
expect(event.data.id).toBe(jwtPayload.context.user.id);
expect(event.data.group).toBe(jwtPayload.context.group);
expect(event.data.name).toBe(p2.name);
expect(event.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
expect(event.data.name).toBe(p2.name);
expect(event.data.role).toBe('visitor');
expect(event.customerId).toBe(testsConfig.iframe.customerId);
await p2.switchToMainFrame();
await p2.getIframeAPI().executeCommand('hangup');
// PARTICIPANT_LEFT webhook
// @ts-ignore
const eventLeft: {
customerId: string;
data: {
avatar: string;
email: string;
group: string;
id: string;
name: string;
participantJid: string;
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_LEFT');
expect('PARTICIPANT_LEFT').toBe(eventLeft.eventType);
expect(eventLeft.data.avatar).toBe(jwtPayload.context.user.avatar);
expect(eventLeft.data.email).toBe(jwtPayload.context.user.email);
expect(eventLeft.data.id).toBe(jwtPayload.context.user.id);
expect(eventLeft.data.group).toBe(jwtPayload.context.group);
expect(eventLeft.data.name).toBe(p2.name);
expect(eventLeft.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
expect(eventLeft.data.name).toBe(p2.name);
expect(eventLeft.data.role).toBe('visitor');
expect(eventLeft.customerId).toBe(testsConfig.iframe.customerId);
}
});
});

View File

@@ -0,0 +1,72 @@
import { expect } from '@wdio/globals';
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import WebhookProxy from '../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
import { fetchJson } from '../../helpers/utils';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('JaaS CHAT_UPLOADED webhook.', () => {
const tenant = testsConfig.jaas.tenant;
const customerId = tenant?.replace('vpaas-magic-cookie-', '');
let p1: Participant, p2: Participant;
let webhooksProxy: WebhookProxy;
let fqn: string;
it('setup', async () => {
const room = ctx.roomName;
webhooksProxy = ctx.webhooksProxy;
p1 = await joinJaasMuc({ name: 'p1', token: t({ room }) });
p2 = await joinJaasMuc({ name: 'p2', token: t({ room }) });
fqn = `${testsConfig.jaas.tenant}/${room}`;
});
it('test webhook', async () => {
await p1.getChatPanel().sendMessage('foo');
await p2.getChatPanel().sendMessage('bar');
await p1.getChatPanel().sendMessage('baz');
await p1.hangup();
await p2.hangup();
const event: {
appId: string;
customerId: string;
data: {
preAuthenticatedLink: string;
};
eventType: string;
fqn: string;
} = await webhooksProxy.waitForEvent('CHAT_UPLOADED');
expect(event.appId).toBe(tenant);
expect(event.customerId).toBe(customerId);
expect(event.data.preAuthenticatedLink).toBeDefined();
expect(event.eventType).toBe('CHAT_UPLOADED');
expect(event.fqn).toBe(fqn);
const uploadedChat: any = await fetchJson(event.data.preAuthenticatedLink);
expect(uploadedChat.meetingFqn).toBe(fqn);
expect(uploadedChat.messageType).toBe('CHAT');
const messages = uploadedChat.messages;
expect(messages).toBeDefined();
expect(messages.length).toBe(3);
expect(messages[0].content).toBe('foo');
expect(messages[0].name).toBe('p1');
expect(messages[1].content).toBe('bar');
expect(messages[1].name).toBe('p2');
expect(messages[2].content).toBe('baz');
expect(messages[2].name).toBe('p1');
});
});

View File

@@ -0,0 +1,64 @@
import type { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { config as testsConfig } from '../../../helpers/TestsConfig';
import WebhookProxy from '../../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
import {
assertDialInDisplayed, assertUrlDisplayed,
dialIn,
isDialInEnabled, verifyMoreNumbersPage,
} from '../../helpers/DialIn';
import { verifyEndedWebhook, verifyStartedWebhooks, waitForMedia } from './util';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true
});
describe('Dial-in', () => {
let p1: Participant, webhooksProxy: WebhookProxy;
const customerId: string = testsConfig.jaas.customerId || '';
it('setup', async () => {
const room = ctx.roomName;
if (!process.env.DIAL_IN_REST_URL) {
console.log('Dial-in test is disabled, set DIAL_IN_REST_URL to enable.');
ctx.skipSuiteTests = true;
return;
}
p1 = await joinJaasMuc({ name: 'p1', token: t({ room, moderator: true }) });
webhooksProxy = ctx.webhooksProxy;
expect(await p1.isInMuc()).toBe(true);
expect(await isDialInEnabled(p1)).toBe(true);
expect(customerId).toBeDefined();
});
it ('Invite UI', async () => {
await assertUrlDisplayed(p1);
await assertDialInDisplayed(p1);
await verifyMoreNumbersPage(p1);
});
it('dial-in', async () => {
const dialInPin = await p1.getDialInPin();
expect(dialInPin.length >= 8).toBe(true);
await dialIn(dialInPin);
await waitForMedia(p1);
const startedPayload
= await verifyStartedWebhooks(webhooksProxy, 'in', 'DIAL_IN_STARTED', customerId);
const endpointId = await p1.execute(() => APP?.conference?.listMembers()[0].getId());
await p1.getFilmstrip().kickParticipant(endpointId);
await verifyEndedWebhook(webhooksProxy, 'DIAL_IN_ENDED', customerId, startedPayload);
});
});

View File

@@ -0,0 +1,51 @@
import type { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { config as testsConfig } from '../../../helpers/TestsConfig';
import WebhookProxy from '../../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
import { verifyEndedWebhook, verifyStartedWebhooks, waitForMedia } from './util';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true
});
describe('Dial-out', () => {
let p1: Participant, webhooksProxy: WebhookProxy;
const dialOutUrl = process.env.DIAL_OUT_URL || '';
const customerId = testsConfig.jaas.customerId || '';
it('setup', async () => {
const room = ctx.roomName;
if (!dialOutUrl) {
console.log('Dial-out test is disabled, set DIAL_OUT_URL to enable.');
ctx.skipSuiteTests = true;
return;
}
webhooksProxy = ctx.webhooksProxy;
p1 = await joinJaasMuc({ name: 'p1', iFrameApi: true, token: t({ room, moderator: true }) });
expect(await p1.isInMuc()).toBe(true);
expect(Boolean(await p1.execute(() => config.dialOutAuthUrl))).toBe(true);
});
it('dial-out', async () => {
await p1.switchToMainFrame();
await p1.getIframeAPI().invitePhone(dialOutUrl);
await p1.switchToIFrame();
await p1.waitForParticipants(1);
await waitForMedia(p1);
const startedPayload
= await verifyStartedWebhooks(webhooksProxy, 'out', 'DIAL_OUT_STARTED', customerId);
const endpointId = await p1.execute(() => APP?.conference?.listMembers()[0].getId());
await p1.getFilmstrip().kickParticipant(endpointId);
await verifyEndedWebhook(webhooksProxy, 'DIAL_OUT_ENDED', customerId, startedPayload);
});
});

View File

@@ -0,0 +1,88 @@
import type { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { config as testsConfig } from '../../../helpers/TestsConfig';
import WebhookProxy from '../../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
import { waitForMedia } from './util';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true
});
describe('SIP jibri invite', () => {
let p1: Participant, webhooksProxy: WebhookProxy;
const customerId = testsConfig.jaas.customerId || '';
const dialOutUrl = process.env.SIP_JIBRI_DIAL_OUT_URL || '';
it('setup', async () => {
const room = ctx.roomName;
if (true) {
// This is temporary until we figure out how to fix it and configure it properly.
console.log('SIP jibri test is disabled.');
ctx.skipSuiteTests = true;
return;
}
if (!dialOutUrl) {
console.log('SIP jibri test is disabled, set SIP_JIBRI_DIAL_OUT_URL to enable.');
ctx.skipSuiteTests = true;
return;
}
p1 = await joinJaasMuc({ name: 'p1', iFrameApi: true, token: t({ room, moderator: true }) });
webhooksProxy = ctx.webhooksProxy;
expect(await p1.isInMuc()).toBe(true);
expect(Boolean(await p1.execute(() => config.inviteServiceUrl))).toBe(true);
});
it('sip jibri', async () => {
await p1.switchToMainFrame();
await p1.getIframeAPI().inviteSIP(dialOutUrl);
await p1.switchToIFrame();
await p1.waitForParticipants(1);
await waitForMedia(p1);
const startedEvent: {
customerId: string;
data: {
participantFullJid: string;
participantId: string;
participantJid: string;
sipAddress: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_STARTED');
expect('SIP_CALL_OUT_STARTED').toBe(startedEvent.eventType);
expect(startedEvent.data.sipAddress).toBe(`sip:${process.env.SIP_JIBRI_DIAL_OUT_URL}`);
expect(startedEvent.customerId).toBe(customerId);
const endpointId = await p1.execute(() => APP?.conference?.listMembers()[0].getId());
await p1.getFilmstrip().kickParticipant(endpointId);
const endedEvent: {
customerId: string;
data: {
direction: string;
participantFullJid: string;
participantId: string;
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_ENDED');
expect('SIP_CALL_OUT_ENDED').toBe(endedEvent.eventType);
expect(endedEvent.customerId).toBe(customerId);
expect(endedEvent.data.participantFullJid).toBe(startedEvent.data.participantFullJid);
expect(endedEvent.data.participantId).toBe(startedEvent.data.participantId);
expect(endedEvent.data.participantJid).toBe(startedEvent.data.participantJid);
});
});

View File

@@ -0,0 +1,81 @@
import { Participant } from '../../../helpers/Participant';
import WebhookProxy from '../../../helpers/WebhookProxy';
interface IStartedWebhookPayload {
direction: string;
participantFullJid: string;
participantId: string;
participantJid: string;
}
/**
* Checks the dial events for a participant and clean up at the end.
* @param webhooksProxy
* @param startedEventName
* @param direction
* @param customerId
*/
export async function verifyStartedWebhooks(
webhooksProxy: WebhookProxy,
direction: 'in' | 'out',
startedEventName: string,
customerId: string): Promise<IStartedWebhookPayload> {
const startedEvent: {
customerId: string;
data: IStartedWebhookPayload;
eventType: string;
} = await webhooksProxy.waitForEvent(startedEventName);
expect(startedEventName).toBe(startedEvent.eventType);
expect(startedEvent.data.direction).toBe(direction);
expect(startedEvent.customerId).toBe(customerId);
const usageEvent: {
customerId: string;
data: any;
eventType: string;
} = await webhooksProxy.waitForEvent('USAGE');
expect('USAGE').toBe(usageEvent.eventType);
expect(usageEvent.customerId).toBe(customerId);
expect(usageEvent.data.some((el: any) =>
el.participantId === startedEvent.data.participantId && el.callDirection === direction)).toBe(true);
return startedEvent.data;
}
export async function verifyEndedWebhook(
webhooksProxy: WebhookProxy,
endedEventName: string,
customerId: string,
startedPayload: IStartedWebhookPayload) {
const endedEvent: {
customerId: string;
data: {
direction: string;
participantFullJid: string;
participantId: string;
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent(endedEventName);
expect(endedEventName).toBe(endedEvent.eventType);
expect(endedEvent.customerId).toBe(customerId);
expect(endedEvent.data.participantFullJid).toBe(startedPayload.participantFullJid);
expect(endedEvent.data.participantId).toBe(startedPayload.participantId);
expect(endedEvent.data.participantJid).toBe(startedPayload.participantJid);
}
/**
* Wait until there is at least one remote participant, ICE is connected, the participant has a stream, and data is
* both received and sent.
*/
export async function waitForMedia(p: Participant) {
await p.waitForParticipants(1);
await p.waitForIceConnected();
await p.waitForRemoteStreams(1);
await p.waitForSendReceiveData(20_000);
}

View File

@@ -1,6 +1,6 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
import { TOKEN_AUTH_FAILED_TEST_ID, TOKEN_AUTH_FAILED_TITLE_TEST_ID } from '../../pageobjects/Notifications';
import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
setTestProperties(__filename, {
useJaas: 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({ token: t({ room: '*' }) });
const p = await joinJaasMuc({ 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({ token: t({ room: ctx.roomName }) });
const p = await joinJaasMuc({ 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({ token });
const p = await joinJaasMuc({ 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({ token: t({ exp: '-1m' }) });
const p = await joinJaasMuc({ 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({ token: t({ keyId: 'invalid-key-id' }) });
const p = await joinJaasMuc({ 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({ token: t({ room: ctx.roomName + 'different' }) });
const p = await joinJaasMuc({ 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({ token: t({ moderator: true }) });
const p = await joinJaasMuc({ 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();
const p = await joinJaasMuc();
expect(Boolean(await p.isInMuc())).toBe(false);

View File

@@ -1,5 +1,5 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -13,19 +13,19 @@ describe('MaxOccupants limit enforcement', () => {
maxOccupants: 2
};
const p1 = await joinMuc({ token: t({ room: ctx.roomName }) });
const p2 = await joinMuc({ name: 'p2', token: t({ room: ctx.roomName }) });
const p1 = await joinJaasMuc({ token: t({ room: ctx.roomName }) });
const p2 = await joinJaasMuc({ 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({ name: 'p3', token: t({ room: ctx.roomName, moderator: true }) });
let p3 = await joinJaasMuc({ name: 'p3', token: t({ room: ctx.roomName, moderator: true }) });
expect(Boolean(await p3.isInMuc())).toBe(false);
await p1.hangup();
p3 = await joinMuc({ name: 'p3', token: t({ room: ctx.roomName }) });
p3 = await joinJaasMuc({ name: 'p3', token: t({ room: ctx.roomName }) });
expect(await p3.isInMuc()).toBe(true);
});
});

View File

@@ -1,6 +1,6 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
import { IToken } from '../../helpers/token';
import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -31,7 +31,7 @@ describe('Setting passcode through settings provisioning', () => {
*/
async function joinWithPassword(instanceId: string, token: IToken) {
// @ts-ignore
const p = await joinMuc({ name: instanceId, token }, { roomName: ctx.roomName });
const p = await joinJaasMuc({ name: instanceId, token }, { roomName: ctx.roomName });
await p.waitForMucJoinedOrError();
expect(await p.isInMuc()).toBe(false);

View File

@@ -1,5 +1,5 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -14,7 +14,7 @@ describe('Setting passcode through settings provisioning', () => {
passcode: 'passcode-must-be-digits-only'
};
const p = await joinMuc({ token: t({ room: ctx.roomName }) }, { roomName: ctx.roomName });
const p = await joinJaasMuc({ 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.

View File

@@ -0,0 +1,178 @@
import { expect } from '@wdio/globals';
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import WebhookProxy from '../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
/**
* Tests the basic webhooks fired for participants joining, leaving, and creating/destroying a conference:
* PARTICIPANT_JOINED, PARTICIPANT_LEFT, ROOM_CREATED, ROOM_DESTROYED, ROLE_CHANGED, USAGE.
*/
describe('Create/destroy/join/leave webhooks', () => {
let conferenceJid: string = '';
let p1: Participant, p2: Participant;
let p1EpId: string, p2EpId: string;
let webhooksProxy: WebhookProxy;
let room: string;
async function checkParticipantJoinedHook(p: Participant) {
const event: {
data: {
conference: string;
isBreakout: boolean;
moderator: boolean;
name: string;
participantId: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED');
expect(event.eventType).toBe('PARTICIPANT_JOINED');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
expect(event.data.moderator).toBe(p.getToken()?.options?.moderator);
expect(event.data.name).toBe(await p.getLocalDisplayName());
expect(event.data.participantId).toBe(await p.getEndpointId());
expect(event.data.name).toBe(p.name);
}
async function checkParticipantLeftHook(p: Participant, reason: string) {
const event: {
customerId: string;
data: {
conference: string;
disconnectReason: string;
group: string;
id: string;
isBreakout: boolean;
name: string;
participantId: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_LEFT');
expect(event.eventType).toBe('PARTICIPANT_LEFT');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.disconnectReason).toBe(reason);
expect(event.data.isBreakout).toBe(false);
expect(event.data.participantId).toBe(await p.getEndpointId());
expect(event.data.name).toBe(p.name);
const jwtPayload = p.getToken()?.payload;
expect(event.data.id).toBe(jwtPayload?.context?.user?.id);
expect(event.data.group).toBe(jwtPayload?.context?.group);
expect(event.customerId).toBe(testsConfig.jaas.customerId);
}
it('setup', async () => {
room = ctx.roomName;
conferenceJid = `${room}@conference.${testsConfig.jaas.tenant}.${new URL(process.env.BASE_URL || '').hostname}`;
webhooksProxy = ctx.webhooksProxy;
p1 = await joinJaasMuc({ name: 'p1', iFrameApi: true, token: t({ room, moderator: true }) });
p1EpId = await p1.getEndpointId();
expect(await p1.isModerator()).toBe(true);
await checkParticipantJoinedHook(p1);
await p1.switchToMainFrame();
p2 = await joinJaasMuc({ name: 'p2', token: t({ room }) });
p2EpId = await p2.getEndpointId();
expect(await p2.isModerator()).toBe(false);
await checkParticipantJoinedHook(p2);
});
it('USAGE webhook', async () => {
const event: {
data: [
{ participantId: string; }
];
eventType: string;
} = await webhooksProxy.waitForEvent('USAGE');
expect(event.eventType).toBe('USAGE');
expect(event.data.some(d => d.participantId === p1EpId));
expect(event.data.some(d => d.participantId === p2EpId));
});
it('ROOM_CREATED webhook', async () => {
const event: {
data: {
conference: string;
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_CREATED');
expect(event.eventType).toBe('ROOM_CREATED');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
});
it('ROLE_CHANGED webhook', async () => {
await p1.getIframeAPI().executeCommand('grantModerator', p2EpId);
const event: {
data: {
grantedBy: {
participantId: string;
};
grantedTo: {
participantId: string;
};
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROLE_CHANGED');
expect(event.eventType).toBe('ROLE_CHANGED');
expect(event.data.role).toBe('moderator');
expect(event.data.grantedBy.participantId).toBe(p1EpId);
expect(event.data.grantedTo.participantId).toBe(p2EpId);
});
it('kick participant', async () => {
webhooksProxy.clearCache();
await p1.getIframeAPI().executeCommand('kickParticipant', p2EpId);
await checkParticipantLeftHook(p2, 'kicked');
});
it('join after kick', async () => {
webhooksProxy.clearCache();
// join again
p2 = await joinJaasMuc({ name: 'p2', token: t({ room }) });
p2EpId = await p2.getEndpointId();
await checkParticipantJoinedHook(p2);
});
it('hangup', async () => {
await p2.hangup();
await checkParticipantLeftHook(p2, 'left');
});
it('dispose conference', async () => {
await p1.getIframeAPI().executeCommand('hangup');
await checkParticipantLeftHook(p1, 'left');
const event: {
data: {
conference: string;
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_DESTROYED');
expect(event.eventType).toBe('ROOM_DESTROYED');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
});
});

View File

@@ -2,13 +2,10 @@ 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';
import { joinJaasMuc, 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
});
@@ -30,7 +27,7 @@ describe('Recording and Live Streaming', () => {
it('setup', async () => {
webhooksProxy = ctx.webhooksProxy;
p = await joinMuc({ iFrameApi: true, token: t({ moderator: true }) }, { roomName: ctx.roomName });
p = await joinJaasMuc({ 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)) {

View File

@@ -3,19 +3,27 @@ import { expect } from '@wdio/globals';
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import type WebhookProxy from '../../helpers/WebhookProxy';
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
useIFrameApi: true,
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Transcriptions', () => {
it('joining the meeting', async () => {
await ensureOneParticipant();
let p1: Participant, p2: Participant;
let webhooksProxy: WebhookProxy;
const { p1 } = ctx;
it('setup', async () => {
const room = ctx.roomName;
webhooksProxy = ctx.webhooksProxy;
p1 = await joinJaasMuc({
name: 'p1',
token: t({ room, moderator: true }),
iFrameApi: true });
if (await p1.execute(() => config.disableIframeAPI || !config.transcription?.enabled)) {
// skip the test if iframeAPI or transcriptions are disabled
@@ -24,32 +32,25 @@ describe('Transcriptions', () => {
return;
}
await p1.switchToMainFrame();
await ensureTwoParticipants({
p2 = await joinJaasMuc({
name: 'p2',
token: t({ room }),
iFrameApi: true }, {
configOverwrite: {
startWithAudioMuted: true
}
});
const { p2 } = ctx;
// let's populate endpoint ids
await Promise.all([
p1.getEndpointId(),
p2.getEndpointId()
p1.switchToMainFrame(),
p2.switchToMainFrame(),
]);
await p1.switchToMainFrame();
await p2.switchToMainFrame();
expect(await p1.getIframeAPI().getEventResult('isModerator')).toBe(true);
expect(await p1.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined();
});
it('toggle subtitles', async () => {
const { p1, p2, webhooksProxy } = ctx;
await p1.getIframeAPI().addEventListener('transcriptionChunkReceived');
await p2.getIframeAPI().addEventListener('transcriptionChunkReceived');
await p1.getIframeAPI().executeCommand('toggleSubtitles');
@@ -63,9 +64,7 @@ describe('Transcriptions', () => {
});
it('set subtitles on and off', async () => {
const { p1, p2, webhooksProxy } = ctx;
// we need to clear results or the last one will be used, form the previous time subtitles were on
// we need to clear results or the last one will be used, from the previous time subtitles were on
await p1.getIframeAPI().clearEventResults('transcriptionChunkReceived');
await p2.getIframeAPI().clearEventResults('transcriptionChunkReceived');
@@ -80,9 +79,7 @@ describe('Transcriptions', () => {
});
it('start/stop transcriptions via recording', async () => {
const { p1, p2, webhooksProxy } = ctx;
// we need to clear results or the last one will be used, form the previous time subtitles were on
// we need to clear results or the last one will be used, from the previous time subtitles were on
await p1.getIframeAPI().clearEventResults('transcriptionChunkReceived');
await p2.getIframeAPI().clearEventResults('transcriptionChunkReceived');
@@ -96,12 +93,12 @@ describe('Transcriptions', () => {
allTranscriptionStatusChanged.push(await p1.driver.waitUntil(() => p1.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 10000,
timeoutMsg: 'transcribingStatusChanged event not received on p1 side'
timeoutMsg: 'transcribingStatusChanged event not received on p1'
}));
allTranscriptionStatusChanged.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 10000,
timeoutMsg: 'transcribingStatusChanged event not received on p2 side'
timeoutMsg: 'transcribingStatusChanged event not received on p2'
}));
let result = await Promise.allSettled(allTranscriptionStatusChanged);
@@ -125,12 +122,12 @@ describe('Transcriptions', () => {
allTranscriptionStatusChanged.push(await p1.driver.waitUntil(() => p1.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 10000,
timeoutMsg: 'transcribingStatusChanged event not received on p1 side'
timeoutMsg: 'transcribingStatusChanged event not received on p1'
}));
allTranscriptionStatusChanged.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 10000,
timeoutMsg: 'transcribingStatusChanged event not received on p2 side'
timeoutMsg: 'transcribingStatusChanged event not received on p2'
}));
result = await Promise.allSettled(allTranscriptionStatusChanged);
@@ -149,17 +146,15 @@ describe('Transcriptions', () => {
// let's wait for destroy event before waiting for those that depends on it
await webhooksProxy.waitForEvent('ROOM_DESTROYED');
if (webhooksProxy) {
const event: {
data: {
preAuthenticatedLink: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('TRANSCRIPTION_UPLOADED');
const event: {
data: {
preAuthenticatedLink: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('TRANSCRIPTION_UPLOADED');
expect('TRANSCRIPTION_UPLOADED').toBe(event.eventType);
expect(event.data.preAuthenticatedLink).toBeDefined();
}
expect('TRANSCRIPTION_UPLOADED').toBe(event.eventType);
expect(event.data.preAuthenticatedLink).toBeDefined();
});
});
@@ -178,30 +173,28 @@ async function checkReceivingChunks(p1: Participant, p2: Participant, webhooksPr
timeoutMsg: 'transcriptionChunkReceived event not received on p2 side'
}));
if (webhooksProxy) {
// TRANSCRIPTION_CHUNK_RECEIVED webhook
allTranscripts.push((async () => {
const event: {
data: {
final: string;
language: string;
messageID: string;
participant: {
id: string;
name: string;
};
stable: string;
// TRANSCRIPTION_CHUNK_RECEIVED webhook
allTranscripts.push((async () => {
const event: {
data: {
final: string;
language: string;
messageID: string;
participant: {
id: string;
name: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('TRANSCRIPTION_CHUNK_RECEIVED');
stable: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('TRANSCRIPTION_CHUNK_RECEIVED');
expect('TRANSCRIPTION_CHUNK_RECEIVED').toBe(event.eventType);
expect('TRANSCRIPTION_CHUNK_RECEIVED').toBe(event.eventType);
event.data.stable = event.data.final;
event.data.stable = event.data.final;
return event;
})());
}
return event;
})());
const result = await Promise.allSettled(allTranscripts);

View File

@@ -1,5 +1,5 @@
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../../helpers/jaas';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -15,7 +15,7 @@ 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({
const m = await joinJaasMuc({
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
});
@@ -25,7 +25,7 @@ describe('Visitors triggered by reaching participantsSoftLimit', () => {
console.log('Moderator joined');
// Joining with a participant token before participantSoftLimit has been reached
const p = await joinMuc({
const p = await joinJaasMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Parti Cipant' })
});
@@ -36,7 +36,7 @@ describe('Visitors triggered by reaching participantsSoftLimit', () => {
console.log('Participant joined');
// Joining with a participant token after participantSoftLimit has been reached
const v = await joinMuc({
const v = await joinJaasMuc({
name: 'p3',
token: t({ room: ctx.roomName, displayName: 'Visi Tor' })
});

View File

@@ -1,5 +1,5 @@
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../../helpers/jaas';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -23,7 +23,7 @@ describe('Visitor receiving video from a single remote participant', () => {
enabled: false
}
};
const sender = await joinMuc({
const sender = await joinJaasMuc({
token: t({ room: ctx.roomName, displayName: 'Sender', moderator: true })
}, {
configOverwrite
@@ -31,7 +31,7 @@ describe('Visitor receiving video from a single remote participant', () => {
const senderEndpointId = await sender.getEndpointId();
const testVisitor = async function(instanceId: 'p1' | 'p2' | 'p3' | 'p4') {
const visitor = await joinMuc({
const visitor = await joinJaasMuc({
name: instanceId,
token: t({ room: ctx.roomName, displayName: 'Visitor', visitor: true })
}, {

View File

@@ -1,5 +1,8 @@
import { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../../helpers/jaas';
import { config as testsConfig } from '../../../helpers/TestsConfig';
import WebhookProxy from '../../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -8,51 +11,122 @@ setTestProperties(__filename, {
});
describe('Visitors triggered by visitor tokens', () => {
it('test visitor tokens', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
let webhooksProxy: WebhookProxy;
let room: string;
async function verifyJoinedWebhook(participant: Participant) {
const context = participant.getToken()?.payload.context;
const event: {
customerId: string;
data: {
avatar: string;
email: string;
group: string;
id: string;
name: string;
participantJid: string;
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED');
expect('PARTICIPANT_JOINED').toBe(event.eventType);
expect(event.data.avatar).toBe(context.user.avatar);
expect(event.data.email).toBe(context.user.email);
expect(event.data.id).toBe(context.user.id);
expect(event.data.group).toBe(context.group);
expect(event.data.name).toBe(context.user.name);
if (context.user.visitor) {
expect(event.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
expect(event.data.role).toBe('visitor');
}
expect(event.customerId).toBe(testsConfig.jaas.customerId);
}
async function verifyLeftWebhook(participant: Participant) {
const context = participant.getToken()?.payload.context;
const eventLeft: {
customerId: string;
data: {
avatar: string;
email: string;
group: string;
id: string;
name: string;
participantJid: string;
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_LEFT');
expect('PARTICIPANT_LEFT').toBe(eventLeft.eventType);
expect(eventLeft.data.avatar).toBe(context.user.avatar);
expect(eventLeft.data.email).toBe(context.user.email);
expect(eventLeft.data.id).toBe(context.user.id);
expect(eventLeft.data.group).toBe(context.group);
expect(eventLeft.data.name).toBe(context.user.name);
if (context.user.visitor) {
expect(eventLeft.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
expect(eventLeft.data.role).toBe('visitor');
}
expect(eventLeft.customerId).toBe(testsConfig.jaas.customerId);
}
it('setup', async () => {
webhooksProxy = ctx.webhooksProxy;
webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true
};
room = ctx.roomName;
});
const m = await joinMuc({
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
});
it('test visitor tokens', async () => {
expect(await m.isInMuc()).toBe(true);
expect(await m.isModerator()).toBe(true);
expect(await m.isVisitor()).toBe(false);
console.log('Moderator joined');
const moderatorToken = t({ room, displayName: 'Mo de Rator', moderator: true });
const moderator = await joinJaasMuc({ name: 'p1', token: moderatorToken });
expect(await moderator.isInMuc()).toBe(true);
expect(await moderator.isModerator()).toBe(true);
expect(await moderator.isVisitor()).toBe(false);
await verifyJoinedWebhook(moderator);
// Joining with a participant token before any visitors
const p = await joinMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Parti Cipant' })
});
const participantToken = t({ room, displayName: 'Parti Cipant' });
const participant = await joinJaasMuc({ name: 'p2', token: participantToken });
expect(await p.isInMuc()).toBe(true);
expect(await p.isModerator()).toBe(false);
expect(await p.isVisitor()).toBe(false);
console.log('Participant joined');
expect(await participant.isInMuc()).toBe(true);
expect(await participant.isModerator()).toBe(false);
expect(await participant.isVisitor()).toBe(false);
await verifyJoinedWebhook(participant);
// Joining with a visitor token
const v = await joinMuc({
name: 'p3',
token: t({ room: ctx.roomName, displayName: 'Visi Tor', visitor: true })
});
const visitorToken = t({ room, displayName: 'Visi Tor', visitor: true });
const visitor = await joinJaasMuc({ name: 'p3', token: visitorToken });
expect(await v.isInMuc()).toBe(true);
expect(await v.isModerator()).toBe(false);
expect(await v.isVisitor()).toBe(true);
console.log('Visitor joined');
expect(await visitor.isInMuc()).toBe(true);
expect(await visitor.isModerator()).toBe(false);
expect(await visitor.isVisitor()).toBe(true);
await verifyJoinedWebhook(visitor);
// Joining with a participant token after visitors...:mindblown:
const v2 = await joinMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Visi Tor 2' })
});
await participant.hangup();
await verifyLeftWebhook(participant);
expect(await v2.isInMuc()).toBe(true);
expect(await v2.isModerator()).toBe(false);
expect(await v2.isVisitor()).toBe(true);
console.log('Visitor2 joined');
// Joining with a participant token after visitors -> visitor
const participantToken2 = t({ room, displayName: 'Visi Tor 2' });
const visitor2 = await joinJaasMuc({ name: 'p2', token: participantToken2 });
expect(await visitor2.isInMuc()).toBe(true);
expect(await visitor2.isModerator()).toBe(false);
expect(await visitor2.isVisitor()).toBe(true);
await verifyJoinedWebhook(visitor2);
await visitor.hangup();
await verifyLeftWebhook(visitor);
await visitor2.hangup();
await verifyLeftWebhook(visitor2);
await moderator.hangup();
await verifyLeftWebhook(moderator);
});
});

View File

@@ -2,7 +2,7 @@ import { expect } from '@wdio/globals';
import { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../../helpers/jaas';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
@@ -19,7 +19,7 @@ describe('Visitors', () => {
visitorsLive: false
};
moderator = await joinMuc({
moderator = await joinJaasMuc({
name: 'p1',
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
});
@@ -32,7 +32,7 @@ describe('Visitors', () => {
ctx.skipSuiteTests = true;
});
visitor = await joinMuc({
visitor = await joinJaasMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Visi Tor', visitor: true })
}, {

View File

@@ -1,5 +1,7 @@
import { Participant } from '../../helpers/Participant';
import { ensureTwoParticipants } from '../../helpers/participants';
import { Participant } from '../helpers/Participant';
import { setTestProperties } from '../helpers/TestProperties';
import { config as testsConfig } from '../helpers/TestsConfig';
import { joinMuc } from '../helpers/joinMuc';
/**
* The CSS selector for local video when outside of tile view. It should
@@ -14,13 +16,16 @@ const FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '#filmstripLocalVideo #localVide
*/
const TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '.remote-videos #localVideoContainer';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('TileView', () => {
let p1: Participant, p2: Participant;
before('join the meeting', async () => {
await ensureTwoParticipants();
p1 = ctx.p1;
p2 = ctx.p2;
p1 = await joinMuc({ name: 'p1', token: testsConfig.jwt.preconfiguredToken });
p2 = await joinMuc({ name: 'p2' });
});
it('entering tile view', async () => {
await p1.getToolbar().clickEnterTileViewButton();

View File

@@ -242,13 +242,11 @@ export const config: WebdriverIO.MultiremoteConfig = {
globalAny.ctx.roomName = generateRoomName(testName);
console.log(`Using room name: ${globalAny.ctx.roomName}`);
// If we are running the iFrameApi tests, we need to mark it as such and if needed to create the proxy
// and connect to it.
if (testProperties.useWebhookProxy && testsConfig.webhooksProxy.enabled && !globalAny.ctx.webhooksProxy) {
let tenant = testsConfig.jaas.tenant;
const tenant = testsConfig.jaas.tenant;
if (!testProperties.useJaas) {
tenant = testsConfig.iframe.tenant;
throw new Error('The test tries to use WebhookProxy without JaaS.');
}
if (!tenant) {
console.log(`Can not configure WebhookProxy, missing tenant in config. Skipping ${testName}.`);