Compare commits

...

7 Commits

Author SHA1 Message Date
damencho
11a6e94541 debug: longer waits 2. 2025-08-19 16:59:38 -05:00
damencho
e1e262fb68 debug: longer wait. 2025-08-19 16:31:03 -05:00
damencho
b0a96b32d2 fix(jiconop): Fixes loading it under different virtual hosts. 2025-08-19 15:59:34 -05:00
bgrozev
dac9b5e244 test: Check for send/receive independently. (#16356)
This allows the logs to show which one definitely failed.
2025-08-19 15:31:52 -05:00
damencho
d15cfd845a fix(config): Drops legacy config prejoinPageEnabled. 2025-08-19 08:41:04 -05:00
bgrozev
91e4ac1665 ref: Extract test configuration code to TestsConfig.ts (#16329)
* ref: Move iFrameUsesJaas to TestsConfig.

* ref: Move room name prefix/suffix to config.

* ref: Move JaaS configuration to TestsConfig.

* ref: Move iframe config to TestsConfig.

* ref: Move webproxy config to TestsConfig.

* ref: Move JWT config to TestsConfig.

* doc: Document some of the IContext fields.

* Add a debug config option.
2025-08-18 13:32:41 -05:00
damencho
fda42e5230 fix: More fixes sending metadata to jicofo.
f1a0012 was not enough to address the issue.
2025-08-18 11:24:07 -05:00
29 changed files with 195 additions and 146 deletions

View File

@@ -791,7 +791,6 @@ var config = {
// Configs for prejoin page.
// prejoinConfig: {
// // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
// // This replaces `prejoinPageEnabled`. Defaults to true.
// enabled: true,
// // Hides the participant name editing field in the prejoin screen.
// // If requireDisplayName is also set as true, a name should still be provided through

View File

@@ -521,7 +521,6 @@ export interface IConfig {
preCallTestEnabled?: boolean;
preCallTestICEUrl?: string;
};
prejoinPageEnabled?: boolean;
raisedHands?: {
disableLowerHandByModerator?: boolean;
disableLowerHandNotification?: boolean;

View File

@@ -202,7 +202,6 @@ export default [
'prejoinConfig.enabled',
'prejoinConfig.hideDisplayName',
'prejoinConfig.hideExtraJoinButtons',
'prejoinPageEnabled',
'raisedHands',
'recordingService',
'requireDisplayName',

View File

@@ -375,8 +375,7 @@ export function setConfigFromURLParams(
// When not in an iframe, start without media if the pre-join page is not enabled.
if (!isEmbedded()
&& ('config.prejoinConfig.enabled' in params || 'config.prejoinPageEnabled' in params)
&& (config.prejoinConfig?.enabled === false || config.prejoinPageEnabled === false)) {
&& 'config.prejoinConfig.enabled' in params && config.prejoinConfig?.enabled === false) {
logger.warn('Using prejoinConfig.enabled config URL overwrite implies starting without media.');
config.disableInitialGUM = true;
}

View File

@@ -405,11 +405,6 @@ function _translateLegacyConfig(oldValue: IConfig) {
newValue.welcomePage.disabled = !oldValue.enableWelcomePage;
}
newValue.prejoinConfig = oldValue.prejoinConfig || {};
if (oldValue.hasOwnProperty('prejoinPageEnabled')) {
newValue.prejoinConfig.enabled = oldValue.prejoinPageEnabled;
}
newValue.disabledSounds = newValue.disabledSounds || [];
if (oldValue.disableJoinLeaveSounds) {

View File

@@ -7,6 +7,12 @@ local services_xml = ext_services.services_xml;
-- gathers needed information and pushes it with a message to clients
-- this way we skip 4 request responses during every client setup
local main_virtual_host = module:get_option_string('muc_mapper_domain_base');
if not main_virtual_host then
module:log('warn', 'No muc_mapper_domain_base option set.');
return;
end
local shard_name_config = module:get_option_string('shard_name');
if shard_name_config then
module:add_identity("server", "shard", shard_name_config);
@@ -33,7 +39,8 @@ module:hook("resource-bind", function (event)
-- disco info data / all identity and features
local query = st.stanza("query", { xmlns = "http://jabber.org/protocol/disco#info" });
local done = {};
for _,identity in ipairs(module:get_host_items("identity")) do
-- to lod this module in different virtual hosts than the main, make sure we query here for main
for _,identity in ipairs(module:context(main_virtual_host):get_host_items("identity")) do
local identity_s = identity.category.."\0"..identity.type;
if not done[identity_s] then
query:tag("identity", identity):up();

View File

@@ -317,8 +317,7 @@ end
-- Send a message update for metadata before sending the first self presence
function filter_stanza(stanza, session)
if not stanza.attr or not stanza.attr.to or stanza.name ~= 'presence'
or stanza.attr.type == 'unavailable' or ends_with(stanza.attr.from, '/focus') then
if not stanza.attr or not stanza.attr.to or stanza.name ~= 'presence' or stanza.attr.type == 'unavailable' then
return stanza;
end

View File

@@ -34,6 +34,9 @@
# The JaaS tenant, e.g. vpaas-magic-cookie-abcdabcd1234567890
#JAAS_TENANT=
# Enable debug logging. Note this includes private information from .env.
#JITSI_DEBUG=true
# An access token to use to create meetings (used for the first participant)
#JWT_ACCESS_TOKEN=

View File

@@ -27,6 +27,7 @@ import Toolbar from '../pageobjects/Toolbar';
import VideoQualityDialog from '../pageobjects/VideoQualityDialog';
import Visitors from '../pageobjects/Visitors';
import { config as testsConfig } from './TestsConfig';
import { LOG_PREFIX, logInfo } from './browserLogger';
import { IToken } from './token';
import { IParticipantJoinOptions, IParticipantOptions } from './types';
@@ -36,13 +37,6 @@ export const P2 = 'p2';
export const P3 = 'p3';
export const P4 = 'p4';
interface IWaitForSendReceiveDataOptions {
checkReceive?: boolean;
checkSend?: boolean;
msg?: string;
timeout?: number;
}
/**
* Participant.
*/
@@ -225,8 +219,8 @@ export class Participant {
// @ts-ignore
url = `${this.driver.iframePageBase}${url}&domain="${baseUrl.host}"&room="${options.roomName}"`;
if (process.env.IFRAME_TENANT) {
url = `${url}&tenant="${process.env.IFRAME_TENANT}"`;
if (testsConfig.iframe.tenant) {
url = `${url}&tenant="${testsConfig.iframe.tenant}"`;
} else if (baseUrl.pathname.length > 1) {
// remove leading slash
url = `${url}&tenant="${baseUrl.pathname.substring(1)}"`;
@@ -424,37 +418,50 @@ export class Participant {
}
/**
* Waits for send and receive data.
* Waits until the conference stats show positive upload and download bitrate (independently).
*
* @param {Object} options
* @param {boolean} options.checkSend - If true we will chec
* @returns {Promise<boolean>}
*/
waitForSendReceiveData({
checkSend = true,
checkReceive = true,
timeout = 15_000,
msg
} = {} as IWaitForSendReceiveDataOptions): Promise<boolean> {
if (!checkSend && !checkReceive) {
return Promise.resolve(true);
}
async waitForSendReceiveData(timeout = 15_000, msg?: string): Promise<boolean> {
const values = await Promise.all([
await this.waitForSendMedia(timeout, msg ? `${msg} (send)` : undefined),
await this.waitForReceiveMedia(timeout, msg ? `${msg} (receive)` : undefined)
]);
const lMsg = msg ?? `expected to ${
checkSend && checkReceive ? 'receive/send' : checkSend ? 'send' : 'receive'} data in 15s for ${this.name}`;
return values[0] && values[1];
}
return this.driver.waitUntil(() => this.execute((pCheckSend: boolean, pCheckReceive: boolean) => {
const stats = APP?.conference?.getStats();
const bitrateMap = stats?.bitrate || {};
const rtpStats = {
uploadBitrate: bitrateMap.upload || 0,
downloadBitrate: bitrateMap.download || 0
};
/**
* Waits until the conference stats show positive upload bitrate.
* @param timeout max time to wait in ms
* @param timeoutMsg the message to log if the timeout is reached
*/
async waitForSendMedia(
timeout = 15_000,
timeoutMsg = `expected to send media in ${timeout / 1000}s for ${this.name}`): Promise<boolean> {
return (rtpStats.uploadBitrate > 0 || !pCheckSend) && (rtpStats.downloadBitrate > 0 || !pCheckReceive);
}, checkSend, checkReceive), {
return this.driver.waitUntil(() => this.execute(() => {
return APP?.conference?.getStats()?.bitrate?.upload > 0;
}), {
timeout,
timeoutMsg: lMsg
timeoutMsg
});
}
/**
* Waits until the conference stats show positive upload bitrate.
* @param timeout max time to wait in ms
* @param timeoutMsg the message to log if the timeout is reached
*/
async waitForReceiveMedia(
timeout = 15_000,
timeoutMsg = `expected to receive media in ${timeout / 1000}s for ${this.name}`): Promise<boolean> {
return this.driver.waitUntil(() => this.execute(() => {
return APP?.conference?.getStats()?.bitrate?.download > 0;
}), {
timeout,
timeoutMsg
});
}

View File

@@ -0,0 +1,44 @@
/**
* Global configuration that the tests are run with. Loaded from environment variables.
*/
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-')),
},
jaas: {
/** 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),
/** The JaaS key ID, used to sign the tokens. */
kid: process.env.JAAS_KID?.trim(),
/** The path to the JaaS private key, used to sign JaaS tokens. */
privateKeyPath: process.env.JAAS_PRIVATE_KEY_PATH?.trim(),
/** The JaaS tenant (vpaas-magic-cookie-<ID>) . */
tenant: process.env.JAAS_TENANT?.trim(),
},
jwt: {
kid: process.env.JWT_KID?.trim(),
/** A pre-configured token used by some tests. */
preconfiguredToken: process.env.JWT_ACCESS_TOKEN?.trim(),
privateKeyPath: process.env.JWT_PRIVATE_KEY_PATH?.trim()
},
roomName: {
/** Optional prefix for room names used for tests. */
prefix: process.env.ROOM_NAME_PREFIX?.trim(),
/** Optional suffix for room names used for tests. */
suffix: process.env.ROOM_NAME_SUFFIX?.trim()
},
webhooksProxy: {
enabled: Boolean(process.env.WEBHOOKS_PROXY_URL && process.env.WEBHOOKS_PROXY_SHARED_SECRET),
sharedSecret: process.env.WEBHOOKS_PROXY_SHARED_SECRET?.trim(),
url: process.env.WEBHOOKS_PROXY_URL?.trim(),
}
};
if (config.debug) {
console.log('TestsConfig:', JSON.stringify(config, null, 2));
}

View File

@@ -1,6 +1,5 @@
import process from 'node:process';
import { P1, P2, P3, P4, Participant } from './Participant';
import { config } from './TestsConfig';
import { generateToken } from './token';
import { IJoinOptions, IParticipantOptions } from './types';
@@ -17,14 +16,14 @@ export async function ensureOneParticipant(options?: IJoinOptions): Promise<void
const participantOps = { name: P1 } as IParticipantOptions;
if (!options?.skipFirstModerator) {
const jwtPrivateKeyPath = process.env.JWT_PRIVATE_KEY_PATH;
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 (process.env.JWT_ACCESS_TOKEN
if (config.jwt.preconfiguredToken
&& ((jwtPrivateKeyPath && !ctx.testProperties.useIFrameApi && !options?.preferGenerateToken)
|| !jwtPrivateKeyPath)) {
participantOps.token = { jwt: process.env.JWT_ACCESS_TOKEN };
participantOps.token = { jwt: config.jwt.preconfiguredToken };
} else if (jwtPrivateKeyPath) {
participantOps.token = generateToken({
...options?.tokenOptions,
@@ -209,8 +208,8 @@ export async function joinParticipant( // eslint-disable-line max-params
let forceTenant = options?.forceTenant;
if (options?.preferGenerateToken && !ctx.testProperties.useIFrameApi
&& process.env.JWT_KID?.startsWith('vpaas-magic-cookie-') && process.env.IFRAME_TENANT) {
forceTenant = process.env.IFRAME_TENANT;
&& config.iframe.usesJaas && config.iframe.tenant) {
forceTenant = config.iframe.tenant;
}
await newParticipant.joinConference({
...options,

View File

@@ -2,6 +2,8 @@ import fs from 'fs';
import jwt from 'jsonwebtoken';
import { v4 as uuidv4 } from 'uuid';
import { config } from './TestsConfig';
export type ITokenOptions = {
displayName?: string;
/**
@@ -10,12 +12,12 @@ export type ITokenOptions = {
exp?: string;
/**
* The key ID to use for the token.
* If not provided, JWT_KID will be used from the environment variables.
* If not provided, the kid configured with environment variables will be used (see env.example).
*/
keyId?: string;
/**
* The path to the private key file used to sign the token.
* If not provided, JWT_PRIVATE_KEY_PATH will be used from the environment variables.
* If not provided, the path configured with environment variables will be used (see env.example).
*/
keyPath?: string;
/**
@@ -91,8 +93,8 @@ export function generatePayload(options: ITokenOptions): any {
* Generate a signed token.
*/
export function generateToken(options: ITokenOptions): IToken {
const keyId = options.keyId || process.env.JWT_KID;
const keyPath = options.keyPath || process.env.JWT_PRIVATE_KEY_PATH;
const keyId = options.keyId || config.jwt.kid;
const keyPath = options.keyPath || config.jwt.privateKeyPath;
const headers = {
algorithm: 'RS256',
noTimestamp: true,
@@ -101,11 +103,11 @@ export function generateToken(options: ITokenOptions): IToken {
};
if (!keyId) {
throw new Error('JWT_KID is not set');
throw new Error('No keyId provided (JWT_KID is not set?)');
}
if (!keyPath) {
throw new Error('JWT_PRIVATE_KEY_PATH is not set');
throw new Error('No keyPath provided (JWT_PRIVATE_KEY_PATH is not set?)');
}
const key = fs.readFileSync(keyPath);

View File

@@ -7,17 +7,26 @@ import { IToken, ITokenOptions } from './token';
export type IContext = {
/**
* Whether the configuration specifies a JaaS account for the iFrame API tests.
*/
iFrameUsesJaas: boolean;
* The up-to-four browser instances provided by the framework. These can be initialized using
* ensureOneParticipant, ensureTwoParticipants, etc. from participants.ts.
**/
p1: Participant;
p2: Participant;
p3: Participant;
p4: Participant;
/** A room name automatically generated by the framework for convenience. */
roomName: string;
/**
* A flag that tests can set, which signals to the framework that the (rest of the) test suite should be skipped.
*/
skipSuiteTests: boolean;
/**
* Test properties provided by the test file via TestProperties.setTestProperties. Used by the framework to
* set up the context appropriately.
**/
testProperties: ITestProperties;
times: any;
/** A WebhooksProxy instance generated by the framework and available for tests to use, if configured. */
webhooksProxy: WebhookProxy;
};

View File

@@ -1,16 +1,20 @@
import { config as testsConfig } from './TestsConfig';
const https = require('https');
/**
* Generates a random number between 1 and the specified maximum value (inclusive).
*
* @param {number} max - The maximum value for the random number (must be a positive integer).
* @param numberOfDigits - The number of digits to pad the random number with leading zeros.
* @return {string} The random number formatted with leading zeros if needed.
*/
export function getRandomNumberAsStr(max: number, numberOfDigits: number): string {
const randomNumber = Math.floor(Math.random() * max) + 1;
export function generateRoomName(testName: string) {
// XXX why chose between 1 and 40 and then always pad with an extra 0?
const rand = (Math.floor(Math.random() * 40) + 1).toString().padStart(3, '0');
let roomName = `${testName}-${rand}`;
return randomNumber.toString().padStart(numberOfDigits, '0');
if (testsConfig.roomName.prefix) {
roomName = `${testsConfig.roomName.prefix}_${roomName}`;
}
if (testsConfig.roomName.suffix) {
roomName += `_${testsConfig.roomName.suffix}`;
}
return roomName.toLowerCase();
}
/**

View File

@@ -116,7 +116,7 @@ async function muteP1BeforeP2JoinsAndScreenshare(p2p: boolean) {
await p2.waitForIceConnected();
}
await p2.waitForSendReceiveData({ checkReceive: false });
await p2.waitForSendMedia();
// Check if p1 appears video muted on p2.
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);

View File

@@ -1,12 +1,13 @@
import { multiremotebrowser } from '@wdio/globals';
import { config } from '../../helpers/TestsConfig';
import { ensureTwoParticipants } from '../../helpers/participants';
describe('URL Normalisation', () => {
it('joining the meeting', async () => {
// if we are running with token this becomes ugly to match the URL
if (process.env.JWT_ACCESS_TOKEN) {
if (config.jwt.preconfiguredToken) {
ctx.skipSuiteTests = true;
return;

View File

@@ -1,4 +1,5 @@
import { Participant } from '../../helpers/Participant';
import { config } from '../../helpers/TestsConfig';
import {
ensureOneParticipant,
ensureThreeParticipants, ensureTwoParticipants,
@@ -78,7 +79,7 @@ describe('AVModeration', () => {
it('hangup and change moderator', async () => {
// no moderator switching if jaas is available.
if (ctx.iFrameUsesJaas) {
if (config.iframe.usesJaas) {
return;
}

View File

@@ -1,4 +1,5 @@
import { P1, P3, Participant } from '../../helpers/Participant';
import { config } from '../../helpers/TestsConfig';
import {
ensureOneParticipant,
ensureThreeParticipants,
@@ -196,7 +197,7 @@ describe('Lobby', () => {
it('change of moderators in lobby', async () => {
// no moderator switching if jaas is available.
if (ctx.iFrameUsesJaas) {
if (config.iframe.usesJaas) {
return;
}
await hangupAllParticipants();
@@ -288,7 +289,7 @@ describe('Lobby', () => {
it('moderator leaves while lobby enabled', async () => {
// no moderator switching if jaas is available.
if (ctx.iFrameUsesJaas) {
if (config.iframe.usesJaas) {
return;
}
const { p1, p2, p3 } = ctx;

View File

@@ -53,7 +53,7 @@ describe('StartMuted', () => {
const p2EndpointId = await p2.getEndpointId();
await p2.waitForIceConnected();
await p2.waitForSendReceiveData({ checkSend: false });
await p2.waitForReceiveMedia();
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
@@ -88,7 +88,7 @@ describe('StartMuted', () => {
const { p3 } = ctx;
await p3.waitForIceConnected();
await p3.waitForSendReceiveData({ checkSend: false });
await p3.waitForReceiveMedia();
await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
@@ -118,7 +118,7 @@ describe('StartMuted', () => {
const { p2 } = ctx;
await p2.waitForIceConnected();
await p2.waitForSendReceiveData({ checkSend: false });
await p2.waitForReceiveMedia();
await joinThirdParticipant({
...options,
@@ -128,7 +128,7 @@ describe('StartMuted', () => {
const { p3 } = ctx;
await p3.waitForIceConnected();
await p3.waitForSendReceiveData({ checkSend: false });
await p3.waitForReceiveMedia();
const { p1 } = ctx;
@@ -240,7 +240,7 @@ describe('StartMuted', () => {
const { p1, p2 } = ctx;
await p2.waitForIceConnected();
await p2.waitForSendReceiveData({ checkReceive: false });
await p2.waitForSendMedia();
await p2.waitForAudioMuted(p1, true);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);

View File

@@ -23,7 +23,7 @@ describe('lastN', () => {
const { p3 } = ctx;
const p3Toolbar = p3.getToolbar();
await p3.waitForSendReceiveData({ checkReceive: false });
await p3.waitForSendMedia();
await ctx.p1.waitForRemoteVideo(await p3.getEndpointId());

View File

@@ -24,10 +24,7 @@ export async function waitForAudioFromDialInParticipant(participant: Participant
await participant.waitForIceConnected();
await participant.waitForRemoteStreams(1);
await participant.waitForSendReceiveData({
timeout: 20_000,
msg: 'dial-in.test.jigasi.participant.no.audio.after.join'
});
await participant.waitForSendReceiveData(20_000, 'dial-in.test.jigasi.participant.no.audio.after.join');
console.log(`dial-in.test.jigasi.participant.received.audio.after.join:${performance.now() - joinedTS} ms.`);
}

View File

@@ -1,23 +1,24 @@
import type { Participant } from '../../helpers/Participant';
import { config } from '../../helpers/TestsConfig';
import { joinParticipant } from '../../helpers/participants';
import { IToken, ITokenOptions, generateToken } from '../../helpers/token';
export function generateJaasToken(options: ITokenOptions): IToken {
if (!process.env.JAAS_PRIVATE_KEY_PATH || !process.env.JAAS_KID) {
throw new Error('JAAS_PRIVATE_KEY_PATH and JAAS_KID environment variables must be set');
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 || process.env.JAAS_KID,
keyPath: options.keyPath || process.env.JAAS_PRIVATE_KEY_PATH,
keyId: options.keyId || config.jaas.kid,
keyPath: options.keyPath || config.jaas.privateKeyPath
});
}
/**
* Creates a new Participant and joins the MUC with the given name. The jaas-specific properties must be set as
* environment variables: IFRAME_TENANT.
* environment variables (see env.example and TestsConfig.ts).
*
* @param instanceId This is the "name" passed to the Participant, I think it's used to match against one of the
* pre-configured browser instances in wdio? It must be one of 'p1', 'p2', 'p3', or 'p4'. TODO: figure out how this
@@ -28,15 +29,15 @@ export function generateJaasToken(options: ITokenOptions): IToken {
*/
export async function joinMuc(instanceId: 'p1' | 'p2' | 'p3' | 'p4', token?: IToken, roomName?: string):
Promise<Participant> {
if (!process.env.JAAS_TENANT) {
throw new Error('JAAS_TENANT environment variables must be set');
if (!config.jaas.enabled) {
throw new Error('JaaS is not configured.');
}
return await joinParticipant({
name: instanceId,
token
}, {
forceTenant: process.env.JAAS_TENANT,
forceTenant: config.jaas.tenant,
roomName
});
}

View File

@@ -146,7 +146,7 @@ describe('Chat', () => {
preAuthenticatedLink: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('CHAT_UPLOADED', 20000);
} = await webhooksProxy.waitForEvent('CHAT_UPLOADED', 120000);
expect('CHAT_UPLOADED').toBe(event.eventType);
expect(event.data.preAuthenticatedLink).toBeDefined();

View File

@@ -1,5 +1,6 @@
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,
@@ -13,6 +14,8 @@ setTestProperties(__filename, {
useWebhookProxy: true
});
const customerId = testsConfig.iframe.customerId;
describe('Invite iframeAPI', () => {
let dialInDisabled: boolean;
let dialOutDisabled: boolean;
@@ -102,7 +105,6 @@ describe('Invite iframeAPI', () => {
const { webhooksProxy } = ctx;
if (webhooksProxy) {
const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
const sipCallOutStartedEvent: {
customerId: string;
data: {
@@ -112,7 +114,7 @@ describe('Invite iframeAPI', () => {
sipAddress: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_STARTED');
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_STARTED', 120000);
expect('SIP_CALL_OUT_STARTED').toBe(sipCallOutStartedEvent.eventType);
expect(sipCallOutStartedEvent.data.sipAddress).toBe(`sip:${process.env.SIP_JIBRI_DIAL_OUT_URL}`);
@@ -157,7 +159,6 @@ async function checkDialEvents(participant: Participant, direction: string, star
const { webhooksProxy } = ctx;
if (webhooksProxy) {
const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
const dialInStartedEvent: {
customerId: string;
data: {
@@ -200,7 +201,7 @@ async function checkDialEvents(participant: Participant, direction: string, star
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent(endedEventName);
} = await webhooksProxy.waitForEvent(endedEventName, 120000);
expect(endedEventName).toBe(dialInEndedEvent.eventType);
expect(dialInEndedEvent.customerId).toBe(customerId);

View File

@@ -2,6 +2,7 @@ import { isEqual } from 'lodash-es';
import { P1, P2, Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureTwoParticipants, parseJid } from '../../helpers/participants';
setTestProperties(__filename, {
@@ -45,7 +46,7 @@ async function checkParticipantLeftHook(p: Participant, reason: string, checkId
expect(event.data.id).toBe(jwtPayload?.context?.user?.id);
expect(event.data.group).toBe(jwtPayload?.context?.group);
expect(event.customerId).toBe(process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', ''));
expect(event.customerId).toBe(testsConfig.iframe.customerId);
}
}
}

View File

@@ -1,4 +1,5 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureOneParticipant } from '../../helpers/participants';
setTestProperties(__filename, {
@@ -6,6 +7,8 @@ setTestProperties(__filename, {
useWebhookProxy: true
});
const { tenant, customerId } = testsConfig.iframe;
describe('Recording', () => {
let recordingDisabled: boolean;
let liveStreamingDisabled: boolean;
@@ -69,7 +72,6 @@ describe('Recording', () => {
});
if (webhooksProxy) {
const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
const liveStreamEvent: {
customerId: string;
eventType: string;
@@ -93,11 +95,10 @@ describe('Recording', () => {
await p1.getIframeAPI().executeCommand('stopRecording', 'stream');
if (webhooksProxy) {
const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
const liveStreamEvent: {
customerId: string;
eventType: string;
} = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED', 20000);
} = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED', 120000);
expect('LIVE_STREAM_ENDED').toBe(liveStreamEvent.eventType);
expect(liveStreamEvent.customerId).toBe(customerId);
@@ -132,7 +133,6 @@ async function testRecordingStarted(command: boolean) {
}
if (webhooksProxy) {
const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
const recordingEvent: {
customerId: string;
eventType: string;
@@ -152,7 +152,7 @@ async function testRecordingStarted(command: boolean) {
const linkEvent = (await p1.getIframeAPI().getEventResult('recordingLinkAvailable'));
expect(linkEvent.link.startsWith('https://')).toBe(true);
expect(linkEvent.link.includes(process.env.IFRAME_TENANT)).toBe(true);
expect(linkEvent.link.includes(tenant)).toBe(true);
expect(linkEvent.ttl > 0).toBe(true);
}
@@ -171,7 +171,6 @@ async function testRecordingStopped(command: boolean) {
}
if (webhooksProxy) {
const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
const liveStreamEvent: {
customerId: string;
eventType: string;

View File

@@ -1,4 +1,5 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
@@ -46,10 +47,8 @@ describe('Visitors', () => {
const { p1, p2, webhooksProxy } = ctx;
await p2.waitForSendReceiveData({
checkSend: false,
msg: 'Visitor is not receiving media'
}).then(() => p2.waitForRemoteStreams(1));
await p2.waitForReceiveMedia(15_000, 'Visitor is not receiving media');
await p2.waitForRemoteStreams(1);
const p2Visitors = p2.getVisitors();
const p1Visitors = p1.getVisitors();
@@ -90,7 +89,7 @@ describe('Visitors', () => {
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(process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', ''));
expect(event.customerId).toBe(testsConfig.iframe.customerId);
await p2.switchToAPI();
await p2.getIframeAPI().executeCommand('hangup');
@@ -120,7 +119,7 @@ describe('Visitors', () => {
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(process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', ''));
expect(eventLeft.customerId).toBe(testsConfig.iframe.customerId);
}
});
});

View File

@@ -67,11 +67,8 @@ describe('Visitors', () => {
await p1Visitors.goLive();
await p2.waitToJoinMUC();
await p2.waitForSendReceiveData({
checkSend: false,
msg: 'Visitor is not receiving media'
}).then(() => p2.waitForRemoteStreams(1));
await p2.waitForReceiveMedia(15000, 'Visitor is not receiving media');
await p2.waitForRemoteStreams(1);
await p2.driver.waitUntil(() => p2Visitors.hasVisitorsDialog(), {
timeout: 5000,

View File

@@ -7,10 +7,11 @@ import process from 'node:process';
import pretty from 'pretty';
import { getTestProperties, loadTestFiles } from './helpers/TestProperties';
import { config as testsConfig } from './helpers/TestsConfig';
import WebhookProxy from './helpers/WebhookProxy';
import { getLogs, initLogger, logInfo } from './helpers/browserLogger';
import { IContext } from './helpers/types';
import { getRandomNumberAsStr } from './helpers/utils';
import { generateRoomName } from './helpers/utils';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const allure = require('allure-commandline');
@@ -231,33 +232,19 @@ export const config: WebdriverIO.MultiremoteConfig = {
bInstance.iframePageBase = `file://${path.dirname(rpath)}`;
}));
globalAny.ctx.roomName = `${testName}-${getRandomNumberAsStr(40, 3)}`;
if (process.env.ROOM_NAME_PREFIX) {
globalAny.ctx.roomName = `${process.env.ROOM_NAME_PREFIX.trim()}_${globalAny.ctx.roomName}`;
}
if (process.env.ROOM_NAME_SUFFIX) {
globalAny.ctx.roomName += `_${process.env.ROOM_NAME_SUFFIX.trim()}`;
}
globalAny.ctx.roomName = globalAny.ctx.roomName.toLowerCase();
globalAny.ctx.iFrameUsesJaas = process.env.JWT_PRIVATE_KEY_PATH
&& process.env.JWT_KID?.startsWith('vpaas-magic-cookie-');
const isJaasConfigured = process.env.JAAS_TENANT && process.env.JAAS_PRIVATE_KEY_PATH && process.env.JAAS_KID;
globalAny.ctx.roomName = generateRoomName(testName);
// 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) {
if (!globalAny.ctx.webhooksProxy
&& process.env.WEBHOOKS_PROXY_URL && process.env.WEBHOOKS_PROXY_SHARED_SECRET) {
globalAny.ctx.webhooksProxy = new WebhookProxy(
`${process.env.WEBHOOKS_PROXY_URL}?tenant=${
isJaasConfigured ? process.env.JAAS_TENANT : process.env.IFRAME_TENANT
}&room=${globalAny.ctx.roomName}`,
process.env.WEBHOOKS_PROXY_SHARED_SECRET,
`${TEST_RESULTS_DIR}/webhooks-${cid}-${testName}.log`);
globalAny.ctx.webhooksProxy.connect();
}
if (testProperties.useWebhookProxy && testsConfig.webhooksProxy.enabled && !globalAny.ctx.webhooksProxy) {
// Note this prevents iframe and jaas test from running together.
const tenant = testsConfig.jaas.enabled ? testsConfig.jaas.tenant : testsConfig.iframe.tenant;
globalAny.ctx.webhooksProxy = new WebhookProxy(
`${testsConfig.webhooksProxy.url}?tenant=${tenant}&room=${globalAny.ctx.roomName}`,
testsConfig.webhooksProxy.sharedSecret!,
`${TEST_RESULTS_DIR}/webhooks-${cid}-${testName}.log`);
globalAny.ctx.webhooksProxy.connect();
}
if (testProperties.useWebhookProxy && !globalAny.ctx.webhooksProxy) {
@@ -265,9 +252,8 @@ export const config: WebdriverIO.MultiremoteConfig = {
globalAny.ctx.skipSuiteTests = true;
}
if (testProperties.useJaas && !isJaasConfigured) {
console.warn(`JaaS is not configured, skipping ${testName}. `
+ 'Set JAAS_TENANT, JAAS_KID, and JAAS_PRIVATE_KEY_PATH to enable.');
if (testProperties.useJaas && !testsConfig.jaas.enabled) {
console.warn(`JaaS is not configured, skipping ${testName}.`);
globalAny.ctx.skipSuiteTests = true;
}
},