mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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' ]
|
||||
|
||||
@@ -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
41
tests/helpers/jaas.ts
Normal 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
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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());
|
||||
@@ -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());
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
64
tests/specs/helpers/mute.ts
Normal file
64
tests/specs/helpers/mute.ts
Normal 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);
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
72
tests/specs/jaas/chat.spec.ts
Normal file
72
tests/specs/jaas/chat.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
64
tests/specs/jaas/dial/dialin.spec.ts
Normal file
64
tests/specs/jaas/dial/dialin.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
|
||||
51
tests/specs/jaas/dial/dialout.spec.ts
Normal file
51
tests/specs/jaas/dial/dialout.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
88
tests/specs/jaas/dial/sipjibri.spec.ts
Normal file
88
tests/specs/jaas/dial/sipjibri.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
81
tests/specs/jaas/dial/util.ts
Normal file
81
tests/specs/jaas/dial/util.ts
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
178
tests/specs/jaas/presence.spec.ts
Normal file
178
tests/specs/jaas/presence.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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' })
|
||||
});
|
||||
|
||||
@@ -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 })
|
||||
}, {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 })
|
||||
}, {
|
||||
|
||||
@@ -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();
|
||||
@@ -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}.`);
|
||||
|
||||
Reference in New Issue
Block a user