Compare commits

..

2 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
24 changed files with 79 additions and 271 deletions

View File

@@ -25,15 +25,9 @@ import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.facebook.react.modules.core.PermissionListener;
@@ -93,28 +87,6 @@ public class JitsiMeetActivity extends AppCompatActivity
launch(context, options);
}
public static void addTopBottomInsets(@NonNull Window w, @NonNull View v) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) return;
View decorView = w.getDecorView();
decorView.post(() -> {
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(decorView);
if (insets != null) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
params.topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
params.bottomMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
v.setLayoutParams(params);
decorView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
view.setBackgroundColor(JitsiMeetView.BACKGROUND_COLOR);
return windowInsets;
});
}
});
}
// Overrides
//
@@ -135,7 +107,6 @@ public class JitsiMeetActivity extends AppCompatActivity
JitsiMeetActivityDelegate.onHostResume(this);
setContentView(R.layout.activity_jitsi_meet);
addTopBottomInsets(getWindow(),findViewById(android.R.id.content));
this.jitsiView = findViewById(R.id.jitsiView);
registerForBroadcastMessages();

View File

@@ -36,7 +36,7 @@ public class JitsiMeetView extends FrameLayout {
/**
* Background color. Should match the background color set in JS.
*/
public static final int BACKGROUND_COLOR = 0xFF040404;
private static final int BACKGROUND_COLOR = 0xFF040404;
/**
* React Native root view.

31
package-lock.json generated
View File

@@ -51,7 +51,6 @@
"clipboard-copy": "4.0.1",
"clsx": "1.1.1",
"dayjs": "1.11.13",
"dompurify": "3.2.6",
"dropbox": "10.7.0",
"focus-visible": "5.1.0",
"glob": "11.0.3",
@@ -7958,13 +7957,6 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true
},
"node_modules/@types/unorm": {
"version": "1.3.28",
"resolved": "https://registry.npmjs.org/@types/unorm/-/unorm-1.3.28.tgz",
@@ -12445,15 +12437,6 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -32406,12 +32389,6 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
"@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"optional": true
},
"@types/unorm": {
"version": "1.3.28",
"resolved": "https://registry.npmjs.org/@types/unorm/-/unorm-1.3.28.tgz",
@@ -35554,14 +35531,6 @@
"domelementtype": "^2.2.0"
}
},
"dompurify": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
"requires": {
"@types/trusted-types": "^2.0.7"
}
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",

View File

@@ -57,7 +57,6 @@
"clipboard-copy": "4.0.1",
"clsx": "1.1.1",
"dayjs": "1.11.13",
"dompurify": "3.2.6",
"dropbox": "10.7.0",
"focus-visible": "5.1.0",
"glob": "11.0.3",

View File

@@ -2,7 +2,6 @@ import { IReduxState } from '../app/types';
import { IStateful } from '../base/app/types';
import { toState } from '../base/redux/functions';
import { cleanSvg } from './functions';
import logger from './logger';
/**
@@ -83,7 +82,7 @@ export const fetchCustomIcons = async (customIcons: Record<string, string>) => {
if (response.ok) {
const svgXml = await response.text();
localCustomIcons[key] = cleanSvg(svgXml);
localCustomIcons[key] = svgXml;
} else {
logger.error(`Failed to fetch ${url}. Status: ${response.status}`);
}

View File

@@ -1,9 +0,0 @@
/**
* Sanitizes the given SVG by removing dangerous elements.
*
* @param {string} svg - The SVG string to clean.
* @returns {string} The sanitized SVG string.
*/
export function cleanSvg(svg: string): string {
return svg;
}

View File

@@ -1,22 +1,11 @@
import { Theme } from '@mui/material';
import { adaptV4Theme, createTheme } from '@mui/material/styles';
import DOMPurify from 'dompurify';
import { breakpoints, colorMap, font, shape, spacing, typography } from '../base/ui/Tokens';
import { createColorTokens } from '../base/ui/utils';
const DEFAULT_FONT_SIZE = 16;
/**
* Sanitizes the given SVG by removing dangerous elements.
*
* @param {string} svg - The SVG string to clean.
* @returns {string} The sanitized SVG string.
*/
export function cleanSvg(svg: string): string {
return DOMPurify.sanitize(svg);
}
/**
* Converts unitless fontSize and lineHeight values in a typography style object to rem units.
* Backward compatibility: This conversion supports custom themes that may still override

View File

@@ -7,7 +7,6 @@ import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
import { getSoundFileSrc } from '../base/media/functions';
import { getLocalParticipant, getRemoteParticipants } from '../base/participants/functions';
import { registerSound, unregisterSound } from '../base/sounds/actions';
import { isEmbedded } from '../base/util/embedUtils';
import { isSpotTV } from '../base/util/spot';
import { isInBreakoutRoom as isInBreakoutRoomF } from '../breakout-rooms/functions';
import { isEnabled as isDropboxEnabled } from '../dropbox/functions';
@@ -152,7 +151,7 @@ export function getSessionStatusToShow(state: IReduxState, mode: string): string
* @returns {boolean} - Whether local recording is supported or not.
*/
export function supportsLocalRecording() {
return LocalRecordingManager.isSupported() && !isEmbedded();
return LocalRecordingManager.isSupported();
}
/**

View File

@@ -188,7 +188,7 @@ export class Participant {
* @param {IParticipantJoinOptions} options - Options for joining.
* @returns {Promise<void>}
*/
async joinConference(options: IParticipantJoinOptions): Promise<Participant> {
async joinConference(options: IParticipantJoinOptions): Promise<void> {
const config = {
room: options.roomName,
configOverwrite: {
@@ -254,8 +254,6 @@ export class Participant {
}
await this.postLoadProcess();
return this;
}
/**
@@ -468,7 +466,7 @@ export class Participant {
}
/**
* Waits until there are at least [number] participants that have at least one track.
* Waits for remote streams.
*
* @param {number} number - The number of remote streams to wait for.
* @returns {Promise<boolean>}
@@ -874,46 +872,26 @@ export class Participant {
}
}
/**
* Checks if video is currently received for the given remote endpoint ID (there is a track, it's not muted,
* and it's streaming status according to the connection-indicator is active).
*/
async isRemoteVideoReceived(endpointId: string): Promise<boolean> {
return this.execute(e => JitsiMeetJS.app.testing.isRemoteVideoReceived(e), endpointId);
}
/**
* Checks if the remove video is displayed for the given remote endpoint ID.
* @param endpointId
*/
async isRemoteVideoDisplayed(endpointId: string): Promise<boolean> {
return this.driver.$(
`//span[@id="participant_${endpointId}" and contains(@class, "display-video")]`).isExisting();
}
/**
* Check if remote video for a specific remote endpoint is both received and displayed.
* @param endpointId
*/
async isRemoteVideoReceivedAndDisplayed(endpointId: string): Promise<boolean> {
return await this.isRemoteVideoReceived(endpointId) && await this.isRemoteVideoDisplayed(endpointId);
}
/**
* Waits for remote video state - receiving and displayed.
* @param endpointId
* @param reverse if true, waits for the remote video to NOT be received AND NOT displayed.
* @param reverse
*/
async waitForRemoteVideo(endpointId: string, reverse = false) {
if (reverse) {
await this.driver.waitUntil(async () =>
!await this.isRemoteVideoReceived(endpointId) && !await this.isRemoteVideoDisplayed(endpointId), {
!await this.execute(epId => JitsiMeetJS.app.testing.isRemoteVideoReceived(`${epId}`),
endpointId) && !await this.driver.$(
`//span[@id="participant_${endpointId}" and contains(@class, "display-video")]`).isExisting(), {
timeout: 15_000,
timeoutMsg: `expected remote video for ${endpointId} to not be received 15s by ${this.name}`
});
} else {
await this.driver.waitUntil(async () =>
await this.isRemoteVideoReceivedAndDisplayed(endpointId), {
await this.execute(epId => JitsiMeetJS.app.testing.isRemoteVideoReceived(`${epId}`),
endpointId) && await this.driver.$(
`//span[@id="participant_${endpointId}" and contains(@class, "display-video")]`).isExisting(), {
timeout: 15_000,
timeoutMsg: `expected remote video for ${endpointId} to be received 15s by ${this.name}`
});

View File

@@ -30,7 +30,6 @@ export default class WebhookProxy {
* Connects.
*/
connect() {
console.log('Connecting...' + this.url);
this.ws = new WebSocket(this.url, {
headers: {
Authorization: this.secret

View File

@@ -28,7 +28,6 @@ export async function ensureOneParticipant(options?: IJoinOptions): Promise<void
participantOps.token = generateToken({
...options?.tokenOptions,
displayName: participantOps.name,
moderator: true
});
}
}
@@ -141,7 +140,7 @@ export async function ensureTwoParticipants(options?: IJoinOptions): Promise<voi
await ensureOneParticipant(options);
const participantOptions = { name: P2 } as IParticipantOptions;
console.log('1111', options?.preferGenerateToken)
if (options?.preferGenerateToken) {
participantOptions.token = generateToken({
...options?.tokenOptions,
@@ -149,10 +148,7 @@ console.log('1111', options?.preferGenerateToken)
});
}
await joinParticipant({
...participantOptions,
name: P2
}, options);
await joinParticipant({ name: P2 }, options);
if (options?.skipInMeetingChecks) {
return Promise.resolve();
@@ -169,16 +165,14 @@ console.log('1111', options?.preferGenerateToken)
}
/**
* Creates a new participant instance, or returns an existing one if it is already joined.
* Creates a participant instance or prepares one for re-joining.
* @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.
* @returns {Promise<Participant>} - The participant instance.
*/
async function joinParticipant( // eslint-disable-line max-params
export async function joinParticipant( // eslint-disable-line max-params
participantOptions: IParticipantOptions,
options?: IJoinOptions
): Promise<Participant> {
options?: IJoinOptions): Promise<Participant> {
participantOptions.iFrameApi = ctx.testProperties.useIFrameApi;
@@ -201,6 +195,8 @@ async function joinParticipant( // eslint-disable-line max-params
// Change the page so we can reload same url if we need to, base.html is supposed to be empty or close to empty
await p.driver.url('/base.html');
// we want the participant instance re-recreated so we clear any kept state, like endpoint ID
}
const newParticipant = new Participant(participantOptions);
@@ -215,12 +211,13 @@ async function joinParticipant( // eslint-disable-line max-params
&& config.iframe.usesJaas && config.iframe.tenant) {
forceTenant = config.iframe.tenant;
}
return await newParticipant.joinConference({
await newParticipant.joinConference({
...options,
forceTenant,
roomName: options?.roomName || ctx.roomName,
});
return newParticipant;
}
/**

View File

@@ -77,8 +77,7 @@ export function generatePayload(options: ITokenOptions): any {
},
'room': options.room || '*'
};
console.log('4444', options.moderator);
console.log('5555', options.visitor);
if (options.moderator) {
// @ts-ignore
payload.context.user.moderator = true;
@@ -94,7 +93,6 @@ export function generatePayload(options: ITokenOptions): any {
* Generate a signed token.
*/
export function generateToken(options: ITokenOptions): IToken {
console.log('2222', JSON.stringify(options));
const keyId = options.keyId || config.jwt.kid;
const keyPath = options.keyPath || config.jwt.privateKeyPath;
const headers = {

View File

@@ -26,11 +26,7 @@ export type IContext = {
**/
testProperties: ITestProperties;
times: any;
/**
* A WebhooksProxy instance generated by the framework and available for tests to use, if configured.
* Note that this is only configured for roomName, if a test wishes to use a different room name it can set up
* a WebhooksProxy instance itself.
*/
/** A WebhooksProxy instance generated by the framework and available for tests to use, if configured. */
webhooksProxy: WebhookProxy;
};

View File

@@ -1,7 +1,7 @@
import { Participant } from '../../helpers/Participant';
import type { Participant } from '../../helpers/Participant';
import { config } from '../../helpers/TestsConfig';
import { joinParticipant } from '../../helpers/participants';
import { IToken, ITokenOptions, generateToken } from '../../helpers/token';
import { IParticipantJoinOptions } from '../../helpers/types';
export function generateJaasToken(options: ITokenOptions): IToken {
if (!config.jaas.enabled) {
@@ -17,46 +17,27 @@ export function generateJaasToken(options: ITokenOptions): IToken {
}
/**
* 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 name. The jaas-specific properties must be set as
* 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
* should be used.
* @param token the token to use, if any.
* @param joinOptions options to use when joining the MUC.
* @param roomName the name of the room to join, if any. If not provided, the ctx generated one will be used.
* @returns {Promise<Participant>} The Participant that has joined the MUC.
*/
export async function joinMuc(
instanceId: 'p1' | 'p2' | 'p3' | 'p4',
token?: IToken,
joinOptions?: Partial<IParticipantJoinOptions>): Promise<Participant> {
export async function joinMuc(instanceId: 'p1' | 'p2' | 'p3' | 'p4', token?: IToken, roomName?: string):
Promise<Participant> {
if (!config.jaas.enabled) {
throw new Error('JaaS is not configured.');
}
// @ts-ignore
const p = ctx[instanceId] as Participant;
if (p) {
// Load a blank page to make sure the page is reloaded (in case the new participant uses the same URL). Using
// 'about:blank' was causing problems in the past, if we notice any issues we can change to "base.html".
await p.driver.url('about:blank');
}
const newParticipant = new Participant({
return await joinParticipant({
name: instanceId,
token
});
// @ts-ignore
ctx[instanceId] = newParticipant;
return await newParticipant.joinConference({
...joinOptions,
}, {
forceTenant: config.jaas.tenant,
roomName: joinOptions?.roomName || ctx.roomName,
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

@@ -114,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}`);
@@ -168,7 +168,7 @@ async function checkDialEvents(participant: Participant, direction: string, star
participantJid: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent(startedEventName, 60000);
} = await webhooksProxy.waitForEvent(startedEventName);
expect(startedEventName).toBe(dialInStartedEvent.eventType);
expect(dialInStartedEvent.data.direction).toBe(direction);
@@ -201,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

@@ -90,7 +90,7 @@ describe('Participants presence', () => {
{ participantId: string; }
];
eventType: string;
} = await webhooksProxy.waitForEvent('USAGE', 60000);
} = await webhooksProxy.waitForEvent('USAGE');
expect('USAGE').toBe(event.eventType);
@@ -141,7 +141,7 @@ describe('Participants presence', () => {
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_CREATED', 60000);
} = await webhooksProxy.waitForEvent('ROOM_CREATED');
expect('ROOM_CREATED').toBe(event.eventType);
expect(event.data.conference).toBe(conferenceJid);

View File

@@ -98,7 +98,7 @@ describe('Recording', () => {
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);

View File

@@ -13,6 +13,8 @@ describe('XMPP login and MUC join test', () => {
expect(await p.isInMuc()).toBe(true);
expect(await p.isModerator()).toBe(false);
await p.hangup();
});
it('with a valid token (specific room)', async () => {
@@ -21,6 +23,8 @@ describe('XMPP login and MUC join test', () => {
expect(await p.isInMuc()).toBe(true);
expect(await p.isModerator()).toBe(false);
await p.hangup();
});
it('with a token with bad signature', async () => {
@@ -37,6 +41,8 @@ describe('XMPP login and MUC join test', () => {
|| await p.getNotifications().getNotificationText(TOKEN_AUTH_FAILED_TITLE_TEST_ID);
expect(errorText).toContain('not allowed to join');
await p.hangup();
});
it('with an expired token', async () => {
@@ -48,6 +54,8 @@ describe('XMPP login and MUC join test', () => {
const errorText = await p.getNotifications().getNotificationText(TOKEN_AUTH_FAILED_TITLE_TEST_ID);
expect(errorText).toContain('Token is expired');
await p.hangup();
});
it('with a token using the wrong key ID', async () => {
@@ -59,6 +67,8 @@ describe('XMPP login and MUC join test', () => {
const errorText = await p.getNotifications().getNotificationText(TOKEN_AUTH_FAILED_TEST_ID);
expect(errorText).toContain('not allowed to join');
await p.hangup();
});
it('with a token for a different room', async () => {
@@ -70,6 +80,8 @@ describe('XMPP login and MUC join test', () => {
const errorText = await p.getNotifications().getNotificationText(TOKEN_AUTH_FAILED_TEST_ID);
expect(errorText).toContain('not allowed to join');
await p.hangup();
});
it('with a moderator token', async () => {
@@ -78,6 +90,8 @@ describe('XMPP login and MUC join test', () => {
expect(await p.isInMuc()).toBe(true);
expect(await p.isModerator()).toBe(true);
await p.hangup();
});
// This is dependent on jaas account configuration. All tests under jaas/ expect that "unauthenticated access" is
@@ -91,6 +105,8 @@ describe('XMPP login and MUC join test', () => {
const errorText = await p.getNotifications().getNotificationText(TOKEN_AUTH_FAILED_TEST_ID);
expect(errorText).toContain('not allowed to join');
await p.hangup();
});
// it('without sending a conference-request', async () => {

View File

@@ -4,8 +4,7 @@ import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
useWebhookProxy: true
});
const passcode = '1234';
@@ -17,11 +16,22 @@ describe('Setting passcode through settings provisioning', () => {
visitorsEnabled: true
};
// We want to keep the room from getting destroyed, because the visitors queue has a timeout and causes
// problems. We could use different rooms instead, but the webhooksProxy is only configured for the default room.
await joinWithPassword('p1', t({ room: ctx.roomName }));
await joinWithPassword('p2', t({ room: ctx.roomName, moderator: true }));
await joinWithPassword('p2', t({ room: ctx.roomName, visitor: true }));
await joinWithPassword('p1', t({ room: ctx.roomName, moderator: true }));
await joinWithPassword('p1', t({ room: ctx.roomName, visitor: true }));
});
it('With an invalid passcode', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
passcode: 'passcode-must-be-digits-only'
};
const roomName = ctx.roomName + '-2';
const p = await joinMuc('p1', t({ room: roomName }), roomName);
// Setting the passcode should fail, resulting in the room being accessible without a password
await p.waitToJoinMUC();
expect(await p.isInMuc()).toBe(true);
expect(await p.getPasswordDialog().isOpen()).toBe(false);
});
});
@@ -31,7 +41,7 @@ describe('Setting passcode through settings provisioning', () => {
*/
async function joinWithPassword(instanceId: string, token: IToken) {
// @ts-ignore
const p = await joinMuc(instanceId, token, ctx.roomName);
const p = await joinMuc(instanceId, token);
await p.waitForMucJoinedOrError();
expect(await p.isInMuc()).toBe(false);
@@ -47,5 +57,7 @@ async function joinWithPassword(instanceId: string, token: IToken) {
expect(await p.isInMuc()).toBe(true);
expect(await p.getPasswordDialog().isOpen()).toBe(false);
await p.hangup();
}

View File

@@ -1,25 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true
});
// This test is separate from passcode.spec.ts, because it needs to use a different room name, and webhooksProxy is only
// setup for the default room name.
describe('Setting passcode through settings provisioning', () => {
it('With an invalid passcode', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
passcode: 'passcode-must-be-digits-only'
};
const p = await joinMuc('p1', t({ room: ctx.roomName }), ctx.roomName);
// The settings provisioning contains an invalid passcode, the expected result is that the room is not
// configured to require a passcode.
await p.waitToJoinMUC();
expect(await p.isInMuc()).toBe(true);
expect(await p.getPasswordDialog().isOpen()).toBe(false);
});
});

View File

@@ -1,61 +0,0 @@
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2', 'p3', 'p4' ]
});
/**
* This is a case which fails if jitsi-videobridge doesn't properly forward PLIs from visitors.
*/
describe('Visitor receiving video from a single remote participant', () => {
it('joining the meeting', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true,
visitorsLive: true,
};
// Force a connection via JVB.
const configOverwrite = {
p2p: {
enabled: false
}
};
const sender = await joinMuc(
'p1',
t({ room: ctx.roomName, displayName: 'Sender', moderator: true }), {
configOverwrite
}
);
const senderEndpointId = await sender.getEndpointId();
const testVisitor = async function(instanceId: 'p1' | 'p2' | 'p3' | 'p4') {
const visitor = await joinMuc(
instanceId,
t({ room: ctx.roomName, displayName: 'Visitor', visitor: true }), {
configOverwrite
}
);
await visitor.waitForIceConnected();
const iceConnected = performance.now();
await visitor.driver.waitUntil(
() => visitor.isRemoteVideoReceivedAndDisplayed(senderEndpointId), {
timeout: 10_000,
timeoutMsg: `Visitor (${instanceId}) is not receiving video from the sender`
});
const duration = performance.now() - iceConnected;
console.log(`Video displayed after ${duration} ms after ICE connected (${instanceId})`);
};
await testVisitor('p2');
await testVisitor('p3');
await testVisitor('p4');
});
});

View File

@@ -45,6 +45,8 @@ describe('Visitors triggered by visitor tokens', () => {
expect(await v.isVisitor()).toBe(true);
console.log('Visitor joined');
await p.hangup();
// Joining with a participant token after visitors...:mindblown:
const v2 = await joinMuc(
'p2',

View File

@@ -58,8 +58,7 @@ const chromePreferences = {
};
const specs = [
'specs/iframe/visitors.spec.ts',
// 'specs/iframe/participantsPresence.spec.ts'
'specs/**/*.spec.ts'
];
/**
@@ -234,7 +233,6 @@ 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.