mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-03-21 02:50:19 +00:00
Compare commits
9 Commits
8836
...
debug-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ff3219935 | ||
|
|
386bdbfc22 | ||
|
|
a45453e391 | ||
|
|
07554a156b | ||
|
|
70c3c8db13 | ||
|
|
9bb1c36508 | ||
|
|
a93ca9d7c4 | ||
|
|
d2f20c49af | ||
|
|
c5f82d4f20 |
@@ -19,8 +19,6 @@ import {
|
||||
endConference,
|
||||
sendTones,
|
||||
setAssumedBandwidthBps,
|
||||
setFollowMe,
|
||||
setFollowMeRecorder,
|
||||
setLocalSubject,
|
||||
setPassword,
|
||||
setSubject
|
||||
@@ -91,6 +89,7 @@ import {
|
||||
togglePinStageParticipant
|
||||
} from '../../react/features/filmstrip/actions.web';
|
||||
import { getPinnedActiveParticipants, isStageFilmstripAvailable } from '../../react/features/filmstrip/functions.web';
|
||||
import { setFollowMe, setFollowMeRecorder } from '../../react/features/follow-me/actions';
|
||||
import { invite } from '../../react/features/invite/actions.any';
|
||||
import {
|
||||
selectParticipantInLargeVideo
|
||||
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -66,7 +66,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2097.0.0+58646fc3/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2099.0.0+89536686/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
@@ -18260,8 +18260,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2097.0.0+58646fc3/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-CViaK78aH8jmlmUkx+J3StpYFDDWyd5ry2CIoBEJx9uZtSnqczVjOBkbx/9VFifd8ZTr+VClfDRM/ZpkJye8rg==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2099.0.0+89536686/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0FYPvOFSdg9L4ocH8bJw8doUE0rM55JnqRijXMOLS3ZOphbpeBg8tBTH33jwb+bqgo5jjmjTrvJkmkvGNF5/Jg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jitsi/js-utils": "2.4.6",
|
||||
@@ -39715,8 +39715,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2097.0.0+58646fc3/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-CViaK78aH8jmlmUkx+J3StpYFDDWyd5ry2CIoBEJx9uZtSnqczVjOBkbx/9VFifd8ZTr+VClfDRM/ZpkJye8rg==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2099.0.0+89536686/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-0FYPvOFSdg9L4ocH8bJw8doUE0rM55JnqRijXMOLS3ZOphbpeBg8tBTH33jwb+bqgo5jjmjTrvJkmkvGNF5/Jg==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.4.6",
|
||||
"@jitsi/logger": "2.1.1",
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2097.0.0+58646fc3/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2099.0.0+89536686/lib-jitsi-meet.tgz",
|
||||
"lodash-es": "4.17.21",
|
||||
"null-loader": "4.0.1",
|
||||
"optional-require": "1.0.3",
|
||||
|
||||
@@ -260,28 +260,6 @@ export const P2P_STATUS_CHANGED = 'P2P_STATUS_CHANGED';
|
||||
*/
|
||||
export const SEND_TONES = 'SEND_TONES';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which updates the current known status of the
|
||||
* Follow Me feature.
|
||||
*
|
||||
* {
|
||||
* type: SET_FOLLOW_ME,
|
||||
* enabled: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which updates the current known status of the
|
||||
* Follow Me feature that is used only by the recorder.
|
||||
*
|
||||
* {
|
||||
* type: SET_FOLLOW_ME_RECORDER,
|
||||
* enabled: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_FOLLOW_ME_RECORDER = 'SET_FOLLOW_ME_RECORDER';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which sets the obfuscated room name.
|
||||
*
|
||||
|
||||
@@ -58,8 +58,6 @@ import {
|
||||
P2P_STATUS_CHANGED,
|
||||
SEND_TONES,
|
||||
SET_ASSUMED_BANDWIDTH_BPS,
|
||||
SET_FOLLOW_ME,
|
||||
SET_FOLLOW_ME_RECORDER,
|
||||
SET_OBFUSCATED_ROOM,
|
||||
SET_PASSWORD,
|
||||
SET_PASSWORD_FAILED,
|
||||
@@ -853,38 +851,6 @@ export function sendTones(tones: string, duration: number, pause: number) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the Follow Me feature.
|
||||
*
|
||||
* @param {boolean} enabled - Whether or not Follow Me should be enabled.
|
||||
* @returns {{
|
||||
* type: SET_FOLLOW_ME,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setFollowMe(enabled: boolean) {
|
||||
return {
|
||||
type: SET_FOLLOW_ME,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the Follow Me feature used only for the recorder.
|
||||
*
|
||||
* @param {boolean} enabled - Whether Follow Me should be enabled and used only by the recorder.
|
||||
* @returns {{
|
||||
* type: SET_FOLLOW_ME_RECORDER,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setFollowMeRecorder(enabled: boolean) {
|
||||
return {
|
||||
type: SET_FOLLOW_ME_RECORDER,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the Mute reaction sounds feature.
|
||||
*
|
||||
|
||||
@@ -210,7 +210,7 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
case JitsiConferenceErrors.CONFERENCE_MAX_USERS: {
|
||||
let retryAsVisitor = false;
|
||||
|
||||
if (error.params?.length && error.params[0]?.visitorsSupported) {
|
||||
if (error.params?.length && error.params[0]?.visitorsSupported === 'true') {
|
||||
// visitors are supported, so let's try joining that way
|
||||
retryAsVisitor = true;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ import {
|
||||
LOCK_STATE_CHANGED,
|
||||
P2P_STATUS_CHANGED,
|
||||
SET_ASSUMED_BANDWIDTH_BPS,
|
||||
SET_FOLLOW_ME,
|
||||
SET_FOLLOW_ME_RECORDER,
|
||||
SET_OBFUSCATED_ROOM,
|
||||
SET_PASSWORD,
|
||||
SET_PENDING_SUBJECT_CHANGE,
|
||||
@@ -178,8 +176,6 @@ export interface IConferenceState {
|
||||
dataChannelOpen?: boolean;
|
||||
e2eeSupported?: boolean;
|
||||
error?: Error;
|
||||
followMeEnabled?: boolean;
|
||||
followMeRecorderEnabled?: boolean;
|
||||
joining?: IJitsiConference;
|
||||
leaving?: IJitsiConference;
|
||||
lobbyError?: boolean;
|
||||
@@ -274,14 +270,6 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
|
||||
|
||||
return set(state, 'assumedBandwidthBps', assumedBandwidthBps);
|
||||
}
|
||||
case SET_FOLLOW_ME:
|
||||
return set(state, 'followMeEnabled', action.enabled);
|
||||
|
||||
case SET_FOLLOW_ME_RECORDER:
|
||||
return { ...state,
|
||||
followMeRecorderEnabled: action.enabled,
|
||||
followMeEnabled: action.enabled
|
||||
};
|
||||
|
||||
case SET_START_REACTIONS_MUTED:
|
||||
return set(state, 'startReactionsMuted', action.muted);
|
||||
|
||||
@@ -744,6 +744,11 @@ function _shouldSendPrivateMessageTo(state: IReduxState, action: AnyAction) {
|
||||
}
|
||||
|
||||
if (lastMessage.privateMessage) {
|
||||
if (!lastMessage.participantId) {
|
||||
// this is a system message we can ignore
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We show the notice if the last received message was private.
|
||||
return {
|
||||
id: lastMessage.participantId,
|
||||
|
||||
@@ -19,3 +19,25 @@ export const SET_FOLLOW_ME_MODERATOR = 'SET_FOLLOW_ME_MODERATOR';
|
||||
* }
|
||||
*/
|
||||
export const SET_FOLLOW_ME_STATE = 'SET_FOLLOW_ME_STATE';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which updates the current known status of the
|
||||
* Follow Me feature.
|
||||
*
|
||||
* {
|
||||
* type: SET_FOLLOW_ME,
|
||||
* enabled: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_FOLLOW_ME = 'SET_FOLLOW_ME';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which updates the current known status of the
|
||||
* Follow Me feature that is used only by the recorder.
|
||||
*
|
||||
* {
|
||||
* type: SET_FOLLOW_ME_RECORDER,
|
||||
* enabled: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_FOLLOW_ME_RECORDER = 'SET_FOLLOW_ME_RECORDER';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {
|
||||
SET_FOLLOW_ME,
|
||||
SET_FOLLOW_ME_MODERATOR,
|
||||
SET_FOLLOW_ME_RECORDER,
|
||||
SET_FOLLOW_ME_STATE
|
||||
} from './actionTypes';
|
||||
|
||||
@@ -37,3 +39,35 @@ export function setFollowMeState(state?: Object) {
|
||||
state
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the Follow Me feature.
|
||||
*
|
||||
* @param {boolean} enabled - Whether or not Follow Me should be enabled.
|
||||
* @returns {{
|
||||
* type: SET_FOLLOW_ME,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setFollowMe(enabled: boolean) {
|
||||
return {
|
||||
type: SET_FOLLOW_ME,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the Follow Me feature used only for the recorder.
|
||||
*
|
||||
* @param {boolean} enabled - Whether Follow Me should be enabled and used only by the recorder.
|
||||
* @returns {{
|
||||
* type: SET_FOLLOW_ME_RECORDER,
|
||||
* enabled: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setFollowMeRecorder(enabled: boolean) {
|
||||
return {
|
||||
type: SET_FOLLOW_ME_RECORDER,
|
||||
enabled
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ import ReducerRegistry from '../base/redux/ReducerRegistry';
|
||||
import { set } from '../base/redux/functions';
|
||||
|
||||
import {
|
||||
SET_FOLLOW_ME,
|
||||
SET_FOLLOW_ME_MODERATOR,
|
||||
SET_FOLLOW_ME_RECORDER,
|
||||
SET_FOLLOW_ME_STATE
|
||||
} from './actionTypes';
|
||||
|
||||
export interface IFollowMeState {
|
||||
followMeEnabled?: boolean;
|
||||
followMeRecorderEnabled?: boolean;
|
||||
moderator?: string;
|
||||
recorder?: boolean;
|
||||
state?: {
|
||||
@@ -21,7 +25,8 @@ ReducerRegistry.register<IFollowMeState>(
|
||||
'features/follow-me',
|
||||
(state = {}, action): IFollowMeState => {
|
||||
switch (action.type) {
|
||||
|
||||
case SET_FOLLOW_ME:
|
||||
return set(state, 'followMeEnabled', action.enabled);
|
||||
case SET_FOLLOW_ME_MODERATOR: {
|
||||
let newState = set(state, 'moderator', action.id);
|
||||
|
||||
@@ -35,6 +40,11 @@ ReducerRegistry.register<IFollowMeState>(
|
||||
|
||||
return newState;
|
||||
}
|
||||
case SET_FOLLOW_ME_RECORDER:
|
||||
return { ...state,
|
||||
followMeRecorderEnabled: action.enabled,
|
||||
followMeEnabled: action.enabled
|
||||
};
|
||||
case SET_FOLLOW_ME_STATE: {
|
||||
return set(state, 'state', action.state);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { FOLLOW_ME_COMMAND } from './constants';
|
||||
* notify all listeners.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/conference'].followMeEnabled,
|
||||
/* selector */ state => state['features/follow-me'].followMeEnabled,
|
||||
/* listener */ (newSelectedValue, store) => _sendFollowMeCommand(newSelectedValue || 'off', store));
|
||||
|
||||
/**
|
||||
@@ -88,7 +88,7 @@ function _getFollowMeState(state: IReduxState) {
|
||||
const stageFilmstrip = isStageFilmstripEnabled(state);
|
||||
|
||||
return {
|
||||
recorder: state['features/base/conference'].followMeRecorderEnabled,
|
||||
recorder: state['features/follow-me'].followMeRecorderEnabled,
|
||||
filmstripVisible: state['features/filmstrip'].visible,
|
||||
maxStageParticipants: stageFilmstrip ? state['features/base/settings'].maxStageParticipants : undefined,
|
||||
nextOnStage: pinnedParticipant?.id,
|
||||
@@ -130,7 +130,7 @@ function _sendFollowMeCommand(
|
||||
);
|
||||
|
||||
return;
|
||||
} else if (!state['features/base/conference'].followMeEnabled) {
|
||||
} else if (!state['features/follow-me'].followMeEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,8 @@ import JitsiMeetJS, { RTCStatsEvents } from '../base/lib-jitsi-meet';
|
||||
|
||||
import logger from './logger';
|
||||
import {
|
||||
DominantSpeakerData,
|
||||
E2ERTTData,
|
||||
FaceLandmarksData,
|
||||
VideoTypeData
|
||||
FaceLandmarksData
|
||||
} from './types';
|
||||
|
||||
// TODO(saghul): expose these in libn-jitsi-meet?
|
||||
@@ -46,16 +44,6 @@ class RTCStats {
|
||||
JitsiMeetJS.rtcstats.sendStatsEntry('logs', logEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send dominant speaker data, the data will be processed by rtcstats-server and saved in the dump file.
|
||||
*
|
||||
* @param {Object} dominantSpeakerData - Dominant speaker data to be saved in the rtcstats dump.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendDominantSpeakerData(dominantSpeakerData: DominantSpeakerData) {
|
||||
JitsiMeetJS.rtcstats.sendStatsEntry('dominantSpeaker', dominantSpeakerData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send e2e rtt data, the data will be processed by rtcstats-server and saved in the dump file.
|
||||
*
|
||||
@@ -87,16 +75,6 @@ class RTCStats {
|
||||
JitsiMeetJS.rtcstats.sendStatsEntry('conferenceStartTimestamp', timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send videoType data, the data will be processed by rtcstats-server and saved in the dump file.
|
||||
*
|
||||
* @param {Object} videoTypeData - The object that holds the videoType data.
|
||||
* @returns {void}
|
||||
*/
|
||||
sendVideoTypeData(videoTypeData: VideoTypeData) {
|
||||
JitsiMeetJS.rtcstats.sendStatsEntry('setVideoType', videoTypeData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send face landmarks data, the data will be processed by rtcstats-server and saved in the dump file.
|
||||
*
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
CONFERENCE_JOINED,
|
||||
E2E_RTT_CHANGED
|
||||
} from '../base/conference/actionTypes';
|
||||
import { DOMINANT_SPEAKER_CHANGED } from '../base/participants/actionTypes';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
import { TRACK_ADDED, TRACK_UPDATED } from '../base/tracks/actionTypes';
|
||||
import { TRACK_UPDATED } from '../base/tracks/actionTypes';
|
||||
import { ADD_FACE_LANDMARKS } from '../face-landmarks/actionTypes';
|
||||
import { FaceLandmarks } from '../face-landmarks/types';
|
||||
import { sendGetCustomerIdRequest } from '../jaas/functions';
|
||||
@@ -47,25 +46,10 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TRACK_ADDED: {
|
||||
if (isRTCStatsEnabled(state)) {
|
||||
const jitsiTrack = action?.track?.jitsiTrack;
|
||||
const { ssrc, videoType } = jitsiTrack || { };
|
||||
|
||||
// Remote tracks store their ssrc in the jitsiTrack object. Local tracks don't. See getSsrcByTrack.
|
||||
if (videoType && ssrc && !jitsiTrack.isLocal() && !jitsiTrack.isAudioTrack()) {
|
||||
RTCStats.sendVideoTypeData({
|
||||
ssrc,
|
||||
videoType
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TRACK_UPDATED: {
|
||||
if (isRTCStatsEnabled(state)) {
|
||||
const { videoType, jitsiTrack, muted } = action?.track || { };
|
||||
const { ssrc, isLocal, videoType: trackVideoType, conference } = jitsiTrack || { };
|
||||
const { jitsiTrack, muted } = action?.track || { };
|
||||
const { isLocal, videoType: trackVideoType, conference } = jitsiTrack || { };
|
||||
|
||||
if (trackVideoType === 'camera' && conference && isLocal()) {
|
||||
RTCStats.sendFaceLandmarksData({
|
||||
@@ -74,27 +58,6 @@ MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyA
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
// if the videoType of the remote track has changed we expect to find it in track.videoType. grep for
|
||||
// trackVideoTypeChanged.
|
||||
if (videoType && ssrc && !jitsiTrack.isLocal() && !jitsiTrack.isAudioTrack()) {
|
||||
|
||||
RTCStats.sendVideoTypeData({
|
||||
ssrc,
|
||||
videoType
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DOMINANT_SPEAKER_CHANGED: {
|
||||
if (isRTCStatsEnabled(state)) {
|
||||
const { id, previousSpeakers, silence } = action.participant;
|
||||
|
||||
RTCStats.sendDominantSpeakerData({
|
||||
dominantSpeakerEndpoint: silence ? null : id,
|
||||
previousSpeakers
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
export type VideoTypeData = {
|
||||
ssrc: number;
|
||||
videoType: string;
|
||||
};
|
||||
|
||||
export type DominantSpeakerData = {
|
||||
dominantSpeakerEndpoint: string;
|
||||
previousSpeakers: string[];
|
||||
};
|
||||
|
||||
// Types for RTC stats entries.
|
||||
export type E2ERTTData = {
|
||||
remoteEndpointId: string;
|
||||
remoteRegion: string;
|
||||
|
||||
@@ -4,8 +4,6 @@ import { IStore } from '../app/types';
|
||||
import { setTokenAuthUrlSuccess } from '../authentication/actions.web';
|
||||
import { isTokenAuthEnabled } from '../authentication/functions';
|
||||
import {
|
||||
setFollowMe,
|
||||
setFollowMeRecorder,
|
||||
setStartMutedPolicy,
|
||||
setStartReactionsMuted
|
||||
} from '../base/conference/actions';
|
||||
@@ -19,6 +17,7 @@ import { updateSettings } from '../base/settings/actions';
|
||||
import { IAudioSettings } from '../base/settings/reducer';
|
||||
import { getLocalVideoTrack } from '../base/tracks/functions.web';
|
||||
import { appendURLHashParam } from '../base/util/uri';
|
||||
import { setFollowMe, setFollowMeRecorder } from '../follow-me/actions';
|
||||
import { disableKeyboardShortcuts, enableKeyboardShortcuts } from '../keyboard-shortcuts/actions';
|
||||
import { toggleBackgroundEffect } from '../virtual-background/actions';
|
||||
import virtualBackgroundLogger from '../virtual-background/logger';
|
||||
|
||||
@@ -3,13 +3,12 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import {
|
||||
setFollowMe,
|
||||
setFollowMeRecorder,
|
||||
setStartMutedPolicy,
|
||||
setStartReactionsMuted
|
||||
} from '../../../base/conference/actions';
|
||||
import { updateSettings } from '../../../base/settings/actions';
|
||||
import Switch from '../../../base/ui/components/native/Switch';
|
||||
import { setFollowMe, setFollowMeRecorder } from '../../../follow-me/actions';
|
||||
import { getModeratorTabProps } from '../../functions.native';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
|
||||
@@ -151,12 +151,11 @@ export function getModeratorTabProps(stateful: IStateful) {
|
||||
const state = toState(stateful);
|
||||
const {
|
||||
conference,
|
||||
followMeEnabled,
|
||||
followMeRecorderEnabled,
|
||||
startAudioMutedPolicy,
|
||||
startVideoMutedPolicy,
|
||||
startReactionsMuted
|
||||
} = state['features/base/conference'];
|
||||
const { followMeEnabled, followMeRecorderEnabled } = state['features/follow-me'];
|
||||
const { groupChatWithPermissions } = state['features/chat'];
|
||||
const { disableReactionsModeration } = state['features/base/config'];
|
||||
const followMeActive = isFollowMeActive(state);
|
||||
|
||||
@@ -17,6 +17,7 @@ local filters = require 'util.filters';
|
||||
local array = require 'util.array';
|
||||
local set = require 'util.set';
|
||||
local json = require 'cjson.safe';
|
||||
local datetime = require 'util.datetime';
|
||||
|
||||
local util = module:require 'util';
|
||||
local is_admin = util.is_admin;
|
||||
@@ -476,6 +477,46 @@ local function message_handler(event)
|
||||
end
|
||||
end
|
||||
|
||||
-- Receives history messages from the main prosody and adds them to the local history
|
||||
-- this happens before the first participant joins and that participant gets the history using the standard flow on join
|
||||
local function history_message_handler(event)
|
||||
local origin, stanza = event.origin, event.stanza;
|
||||
|
||||
local delay_elem = stanza:get_child('delay', 'urn:xmpp:delay');
|
||||
if not delay_elem then
|
||||
return;
|
||||
end
|
||||
|
||||
-- now let's add it history, we do not use the event as we want to keep
|
||||
-- the delay element
|
||||
local room = get_room_from_jid(room_jid_match_rewrite(
|
||||
jid.bare(stanza.attr.from):sub(1, -(main_domain:len() + 1))..local_domain));
|
||||
|
||||
if not room then
|
||||
return;
|
||||
end
|
||||
|
||||
if room:get_historylength() == 0 then
|
||||
return;
|
||||
end
|
||||
|
||||
local history = room._history;
|
||||
if not history then history = {}; room._history = history; end
|
||||
|
||||
local history_stanza = st.clone(stanza);
|
||||
history_stanza.attr.to = '';
|
||||
|
||||
local node, host, resource = jid.split(room_jid_match_rewrite(history_stanza.attr.from));
|
||||
history_stanza.attr.from = jid.join(node, local_muc_domain, resource);
|
||||
-- the from in the delay extension is still the main prosody jid, not used for now
|
||||
local entry = { stanza = history_stanza, timestamp = datetime.parse(delay_elem.attr.stamp) };
|
||||
table.insert(history, entry);
|
||||
while #history > room:get_historylength() do table.remove(history, 1) end
|
||||
|
||||
return false;
|
||||
end
|
||||
module:hook('message/host', history_message_handler);
|
||||
|
||||
process_host_module(local_domain, function(host_module, host)
|
||||
host_module:hook('iq/host', stanza_handler, 10);
|
||||
host_module:hook('message/full', message_handler);
|
||||
|
||||
@@ -17,6 +17,7 @@ local is_admin = util.is_admin;
|
||||
local presence_check_status = util.presence_check_status;
|
||||
local process_host_module = util.process_host_module;
|
||||
local is_transcriber_jigasi = util.is_transcriber_jigasi;
|
||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||
local json = require 'cjson.safe';
|
||||
|
||||
-- Debug flag
|
||||
@@ -167,6 +168,20 @@ local function connect_vnode(event)
|
||||
-- send update initially so we can report the moderators that will join
|
||||
send_visitors_iq(conference_service, room, 'update');
|
||||
|
||||
-- let's send message history
|
||||
local event = {
|
||||
room = room;
|
||||
to = conference_service;
|
||||
next_stanza = function() end; -- muc-get-history should define this iterator
|
||||
};
|
||||
module:context(main_muc_component_config):fire_event("muc-get-history", event);
|
||||
for msg in event.next_stanza, event do
|
||||
-- the messages stored in history has been stored before domain_mapper and
|
||||
-- contain the virtual jid for a from
|
||||
msg.attr.from = room_jid_match_rewrite(msg.attr.from);
|
||||
room:route_stanza(msg);
|
||||
end
|
||||
|
||||
for _, o in room:each_occupant() do
|
||||
if not is_admin(o.bare_jid) then
|
||||
local fmuc_pr = filter_stanza_nick_if_needed(st.clone(o:get_presence()), room);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import {
|
||||
checkForScreensharingTile,
|
||||
ensureOneParticipant,
|
||||
ensureTwoParticipants,
|
||||
hangupAllParticipants,
|
||||
joinSecondParticipant,
|
||||
joinThirdParticipant,
|
||||
unmuteVideoAndCheck
|
||||
joinThirdParticipant
|
||||
} from '../../helpers/participants';
|
||||
|
||||
describe('StartMuted', () => {
|
||||
@@ -144,134 +142,134 @@ describe('StartMuted', () => {
|
||||
await p1.waitForAudioMuted(p3, false /* unmuted */);
|
||||
});
|
||||
|
||||
it('startWithVideoMuted=true can unmute', async () => {
|
||||
// Maybe disable if there is FF or Safari participant.
|
||||
|
||||
await hangupAllParticipants();
|
||||
|
||||
// Explicitly enable P2P due to a regression with unmute not updating
|
||||
// large video while in P2P.
|
||||
const options = {
|
||||
configOverwrite: {
|
||||
p2p: {
|
||||
enabled: true
|
||||
},
|
||||
startWithVideoMuted: true
|
||||
}
|
||||
};
|
||||
|
||||
await ensureTwoParticipants(options);
|
||||
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
|
||||
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
|
||||
|
||||
await Promise.all([
|
||||
p1.getLargeVideo().waitForSwitchTo(await p2.getEndpointId()),
|
||||
p2.getLargeVideo().waitForSwitchTo(await p1.getEndpointId())
|
||||
]);
|
||||
|
||||
await unmuteVideoAndCheck(p2, p1);
|
||||
await p1.getLargeVideo().assertPlaying();
|
||||
});
|
||||
|
||||
it('startWithAudioMuted=true can unmute', async () => {
|
||||
await hangupAllParticipants();
|
||||
|
||||
const options = {
|
||||
configOverwrite: {
|
||||
startWithAudioMuted: true,
|
||||
testing: {
|
||||
testMode: true,
|
||||
debugAudioLevels: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await ensureTwoParticipants(options);
|
||||
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, true) ]);
|
||||
await p1.getToolbar().clickAudioUnmuteButton();
|
||||
await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, false) ]);
|
||||
});
|
||||
|
||||
it('startWithAudioVideoMuted=true can unmute', async () => {
|
||||
await hangupAllParticipants();
|
||||
|
||||
const options = {
|
||||
configOverwrite: {
|
||||
startWithAudioMuted: true,
|
||||
startWithVideoMuted: true,
|
||||
p2p: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await ensureOneParticipant(options);
|
||||
await joinSecondParticipant({
|
||||
configOverwrite: {
|
||||
testing: {
|
||||
testMode: true,
|
||||
debugAudioLevels: true
|
||||
},
|
||||
p2p: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p2.waitForIceConnected();
|
||||
await p2.waitForSendMedia();
|
||||
|
||||
await p2.waitForAudioMuted(p1, true);
|
||||
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
|
||||
|
||||
// Unmute p1's both audio and video and check on p2.
|
||||
await p1.getToolbar().clickAudioUnmuteButton();
|
||||
await p2.waitForAudioMuted(p1, false);
|
||||
|
||||
await unmuteVideoAndCheck(p1, p2);
|
||||
await p2.getLargeVideo().assertPlaying();
|
||||
});
|
||||
|
||||
|
||||
it('test p2p JVB switch and switch back', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
// Mute p2's video just before p3 joins.
|
||||
await p2.getToolbar().clickVideoMuteButton();
|
||||
|
||||
await joinThirdParticipant({
|
||||
configOverwrite: {
|
||||
p2p: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { p3 } = ctx;
|
||||
|
||||
// Unmute p2 and check if its video is being received by p1 and p3.
|
||||
await unmuteVideoAndCheck(p2, p3);
|
||||
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
|
||||
|
||||
// Mute p2's video just before p3 leaves.
|
||||
await p2.getToolbar().clickVideoMuteButton();
|
||||
|
||||
await p3.hangup();
|
||||
|
||||
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
|
||||
|
||||
await p2.getToolbar().clickVideoUnmuteButton();
|
||||
|
||||
// Check if p2's video is playing on p1.
|
||||
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
|
||||
await p1.getLargeVideo().assertPlaying();
|
||||
});
|
||||
// it('startWithVideoMuted=true can unmute', async () => {
|
||||
// // Maybe disable if there is FF or Safari participant.
|
||||
//
|
||||
// await hangupAllParticipants();
|
||||
//
|
||||
// // Explicitly enable P2P due to a regression with unmute not updating
|
||||
// // large video while in P2P.
|
||||
// const options = {
|
||||
// configOverwrite: {
|
||||
// p2p: {
|
||||
// enabled: true
|
||||
// },
|
||||
// startWithVideoMuted: true
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// await ensureTwoParticipants(options);
|
||||
//
|
||||
// const { p1, p2 } = ctx;
|
||||
//
|
||||
// await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
|
||||
// await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
|
||||
//
|
||||
// await Promise.all([
|
||||
// p1.getLargeVideo().waitForSwitchTo(await p2.getEndpointId()),
|
||||
// p2.getLargeVideo().waitForSwitchTo(await p1.getEndpointId())
|
||||
// ]);
|
||||
//
|
||||
// await unmuteVideoAndCheck(p2, p1);
|
||||
// await p1.getLargeVideo().assertPlaying();
|
||||
// });
|
||||
//
|
||||
// it('startWithAudioMuted=true can unmute', async () => {
|
||||
// await hangupAllParticipants();
|
||||
//
|
||||
// const options = {
|
||||
// configOverwrite: {
|
||||
// startWithAudioMuted: true,
|
||||
// testing: {
|
||||
// testMode: true,
|
||||
// debugAudioLevels: true
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// await ensureTwoParticipants(options);
|
||||
//
|
||||
// const { p1, p2 } = ctx;
|
||||
//
|
||||
// await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, true) ]);
|
||||
// await p1.getToolbar().clickAudioUnmuteButton();
|
||||
// await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, false) ]);
|
||||
// });
|
||||
//
|
||||
// it('startWithAudioVideoMuted=true can unmute', async () => {
|
||||
// await hangupAllParticipants();
|
||||
//
|
||||
// const options = {
|
||||
// configOverwrite: {
|
||||
// startWithAudioMuted: true,
|
||||
// startWithVideoMuted: true,
|
||||
// p2p: {
|
||||
// enabled: true
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// await ensureOneParticipant(options);
|
||||
// await joinSecondParticipant({
|
||||
// configOverwrite: {
|
||||
// testing: {
|
||||
// testMode: true,
|
||||
// debugAudioLevels: true
|
||||
// },
|
||||
// p2p: {
|
||||
// enabled: true
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// const { p1, p2 } = ctx;
|
||||
//
|
||||
// await p2.waitForIceConnected();
|
||||
// await p2.waitForSendMedia();
|
||||
//
|
||||
// await p2.waitForAudioMuted(p1, true);
|
||||
// await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
|
||||
//
|
||||
// // Unmute p1's both audio and video and check on p2.
|
||||
// await p1.getToolbar().clickAudioUnmuteButton();
|
||||
// await p2.waitForAudioMuted(p1, false);
|
||||
//
|
||||
// await unmuteVideoAndCheck(p1, p2);
|
||||
// await p2.getLargeVideo().assertPlaying();
|
||||
// });
|
||||
//
|
||||
//
|
||||
// it('test p2p JVB switch and switch back', async () => {
|
||||
// const { p1, p2 } = ctx;
|
||||
//
|
||||
// // Mute p2's video just before p3 joins.
|
||||
// await p2.getToolbar().clickVideoMuteButton();
|
||||
//
|
||||
// await joinThirdParticipant({
|
||||
// configOverwrite: {
|
||||
// p2p: {
|
||||
// enabled: true
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// const { p3 } = ctx;
|
||||
//
|
||||
// // Unmute p2 and check if its video is being received by p1 and p3.
|
||||
// await unmuteVideoAndCheck(p2, p3);
|
||||
// await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
|
||||
//
|
||||
// // Mute p2's video just before p3 leaves.
|
||||
// await p2.getToolbar().clickVideoMuteButton();
|
||||
//
|
||||
// await p3.hangup();
|
||||
//
|
||||
// await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
|
||||
//
|
||||
// await p2.getToolbar().clickVideoUnmuteButton();
|
||||
//
|
||||
// // Check if p2's video is playing on p1.
|
||||
// await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
|
||||
// await p1.getLargeVideo().assertPlaying();
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
@@ -58,7 +58,7 @@ const chromePreferences = {
|
||||
};
|
||||
|
||||
const specs = [
|
||||
'specs/**/*.spec.ts'
|
||||
'specs/**/startMuted.spec.ts'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -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}.`);
|
||||
@@ -314,6 +312,14 @@ export const config: WebdriverIO.MultiremoteConfig = {
|
||||
* @param {Object} context - The context object.
|
||||
*/
|
||||
beforeTest(test, context) {
|
||||
// Use the directory under 'tests/specs' as the parent suite
|
||||
const match = test.file.match(/.*\/tests\/specs\/([^\/]+)\//);
|
||||
const dir = match ? match[1] : false;
|
||||
|
||||
if (dir) {
|
||||
AllureReporter.addParentSuite(dir);
|
||||
}
|
||||
|
||||
if (ctx.skipSuiteTests) {
|
||||
context.skip();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user