Merge pull request #13209 from ramonlsouza/v24-develop

chore: Merge 2.4 into develop
This commit is contained in:
Anton Georgiev 2021-09-13 13:52:11 -04:00 committed by GitHub
commit d7df8dd048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 14199 additions and 561 deletions

View File

@ -4,6 +4,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.models.{ Users2x, Roles }
trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait { trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
this: MeetingActor => this: MeetingActor =>
@ -19,15 +20,22 @@ trait RequestBreakoutJoinURLReqMsgHdlr extends RightsManagementTrait {
for { for {
model <- state.breakout model <- state.breakout
room <- model.find(msg.body.breakoutId) room <- model.find(msg.body.breakoutId)
requesterUser <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
} yield { } yield {
BreakoutHdlrHelpers.sendJoinURL( if (requesterUser.role == Roles.MODERATOR_ROLE || room.freeJoin) {
liveMeeting, BreakoutHdlrHelpers.sendJoinURL(
outGW, liveMeeting,
msg.body.userId, outGW,
room.externalId, msg.body.userId,
room.sequence.toString(), room.externalId,
room.id room.sequence.toString(),
) room.id
)
} else {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to request breakout room URL for meeting."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
}
} }
} }

View File

@ -45,7 +45,6 @@ class App extends React.Component {
.then((response) => response.json()) .then((response) => response.json())
.then((json) => { .then((json) => {
this.setState({ activitiesJson: json }); this.setState({ activitiesJson: json });
document.title = `Learning Dashboard - ${json.name}`;
}); });
} }
} }
@ -54,6 +53,8 @@ class App extends React.Component {
const { activitiesJson, tab } = this.state; const { activitiesJson, tab } = this.state;
const { intl } = this.props; const { intl } = this.props;
document.title = `${intl.formatMessage({ id: 'app.learningDashboard.dashboardTitle', defaultMessage: 'Learning Dashboard' })} - ${activitiesJson.name}`;
function totalOfRaiseHand() { function totalOfRaiseHand() {
if (activitiesJson && activitiesJson.users) { if (activitiesJson && activitiesJson.users) {
return Object.values(activitiesJson.users) return Object.values(activitiesJson.users)

View File

@ -1 +1 @@
BIGBLUEBUTTON_RELEASE=2.4-beta-4 BIGBLUEBUTTON_RELEASE=2.4-rc-1

View File

@ -74,6 +74,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
[hidden]:not([hidden="false"]) { [hidden]:not([hidden="false"]) {
display: none !important; display: none !important;
} }
textarea::-webkit-input-placeholder,
input::-webkit-input-placeholder {
color: var(--palette-placeholder-text);
opacity: 1;
}
</style> </style>
<script> <script>
document.addEventListener('gesturestart', function (e) { document.addEventListener('gesturestart', function (e) {

View File

@ -253,7 +253,6 @@ class BreakoutRoom extends PureComponent {
roomList.removeEventListener('keydown', this.handleMoveEvent, true); roomList.removeEventListener('keydown', this.handleMoveEvent, true);
} }
} }
this.handleDismiss();
} }
handleShiftUser(activeListSibling) { handleShiftUser(activeListSibling) {
@ -373,7 +372,7 @@ class BreakoutRoom extends PureComponent {
return; return;
} }
this.setState({ preventClosing: false }); this.handleDismiss();
const rooms = _.range(1, numberOfRooms + 1).map((seq) => ({ const rooms = _.range(1, numberOfRooms + 1).map((seq) => ({
users: this.getUserByRoom(seq).map((u) => u.userId), users: this.getUserByRoom(seq).map((u) => u.userId),
@ -403,7 +402,7 @@ class BreakoutRoom extends PureComponent {
breakoutUsers.forEach((user) => sendInvitation(breakoutId, user.userId)); breakoutUsers.forEach((user) => sendInvitation(breakoutId, user.userId));
}); });
this.setState({ preventClosing: false }); this.handleDismiss();
} }
onAssignRandomly() { onAssignRandomly() {

View File

@ -47,6 +47,7 @@ import ConnectionStatusService from '/imports/ui/components/connection-status/se
import { NAVBAR_HEIGHT, LARGE_NAVBAR_HEIGHT } from '/imports/ui/components/layout/defaultValues'; import { NAVBAR_HEIGHT, LARGE_NAVBAR_HEIGHT } from '/imports/ui/components/layout/defaultValues';
import Settings from '/imports/ui/services/settings'; import Settings from '/imports/ui/services/settings';
import LayoutService from '/imports/ui/components/layout/service'; import LayoutService from '/imports/ui/components/layout/service';
import { registerTitleView } from '/imports/utils/dom-utils';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)'; const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
const APP_CONFIG = Meteor.settings.public.app; const APP_CONFIG = Meteor.settings.public.app;
@ -99,6 +100,10 @@ const intlMessages = defineMessages({
id: 'app.whiteboard.annotations.poll', id: 'app.whiteboard.annotations.poll',
description: 'message displayed when a poll is published', description: 'message displayed when a poll is published',
}, },
defaultViewLabel: {
id: 'app.title.defaultViewLabel',
description: 'view name apended to document title',
},
}); });
const propTypes = { const propTypes = {
@ -153,12 +158,14 @@ class App extends Component {
const { browserName } = browserInfo; const { browserName } = browserInfo;
const { osName } = deviceInfo; const { osName } = deviceInfo;
registerTitleView(intl.formatMessage(intlMessages.defaultViewLabel));
layoutContextDispatch({ layoutContextDispatch({
type: ACTIONS.SET_IS_RTL, type: ACTIONS.SET_IS_RTL,
value: isRTL, value: isRTL,
}); });
MediaService.setSwapLayout(); MediaService.setSwapLayout(layoutContextDispatch);
Modal.setAppElement('#app'); Modal.setAppElement('#app');
const fontSize = isMobile() ? MOBILE_FONT_SIZE : DESKTOP_FONT_SIZE; const fontSize = isMobile() ? MOBILE_FONT_SIZE : DESKTOP_FONT_SIZE;

View File

@ -60,6 +60,7 @@ const AppContainer = (props) => {
settingsLayout, settingsLayout,
pushLayoutToEveryone, pushLayoutToEveryone,
currentUserId, currentUserId,
shouldShowPresentation: propsShouldShowPresentation,
...otherProps ...otherProps
} = props; } = props;
const { const {
@ -68,12 +69,14 @@ const AppContainer = (props) => {
layoutType, layoutType,
deviceType, deviceType,
} = layoutContextState; } = layoutContextState;
const { sidebarContent, sidebarNavigation } = input; const { sidebarContent, sidebarNavigation, presentation } = input;
const { actionBar: actionsBarStyle, captions: captionsStyle } = output; const { actionBar: actionsBarStyle, captions: captionsStyle } = output;
const { sidebarNavPanel } = sidebarNavigation; const { sidebarNavPanel } = sidebarNavigation;
const { sidebarContentPanel } = sidebarContent; const { sidebarContentPanel } = sidebarContent;
const sidebarNavigationIsOpen = sidebarNavigation.isOpen; const sidebarNavigationIsOpen = sidebarNavigation.isOpen;
const sidebarContentIsOpen = sidebarContent.isOpen; const sidebarContentIsOpen = sidebarContent.isOpen;
const presentationIsOpen = presentation.isOpen;
const shouldShowPresentation = propsShouldShowPresentation && presentationIsOpen;
return currentUserId return currentUserId
? ( ? (
@ -93,6 +96,7 @@ const AppContainer = (props) => {
sidebarNavigationIsOpen, sidebarNavigationIsOpen,
sidebarContentPanel, sidebarContentPanel,
sidebarContentIsOpen, sidebarContentIsOpen,
shouldShowPresentation,
}} }}
{...otherProps} {...otherProps}
/> />

View File

@ -79,6 +79,7 @@ class AudioControls extends PureComponent {
hideLabel hideLabel
aria-label={intl.formatMessage(intlMessages.joinAudio)} aria-label={intl.formatMessage(intlMessages.joinAudio)}
label={intl.formatMessage(intlMessages.joinAudio)} label={intl.formatMessage(intlMessages.joinAudio)}
data-test="joinAudio"
color="default" color="default"
ghost ghost
icon="audio_off" icon="audio_off"

View File

@ -290,6 +290,7 @@ class InputStreamLiveSelector extends Component {
aria-label={intl.formatMessage(intlMessages.leaveAudio)} aria-label={intl.formatMessage(intlMessages.leaveAudio)}
label={intl.formatMessage(intlMessages.leaveAudio)} label={intl.formatMessage(intlMessages.leaveAudio)}
accessKey={shortcuts.leaveaudio} accessKey={shortcuts.leaveaudio}
data-test="leaveAudio"
hideLabel hideLabel
color="primary" color="primary"
icon={isListenOnly ? 'listen' : 'audio_on'} icon={isListenOnly ? 'listen' : 'audio_on'}

View File

@ -187,7 +187,7 @@ class AudioModal extends Component {
if (autoplayBlocked !== prevProps.autoplayBlocked) { if (autoplayBlocked !== prevProps.autoplayBlocked) {
if (autoplayBlocked) { if (autoplayBlocked) {
this.setContent('autoplayBlocked'); this.setContent({ content: 'autoplayBlocked' });
} else { } else {
closeModal(); closeModal();
} }

View File

@ -5,6 +5,8 @@ import _ from 'lodash';
import BBBMenu from "/imports/ui/components/menu/component"; import BBBMenu from "/imports/ui/components/menu/component";
import Button from '/imports/ui/components/button/component'; import Button from '/imports/ui/components/button/component';
import { alertScreenReader } from '/imports/utils/dom-utils';
import ChatService from '../service'; import ChatService from '../service';
const intlMessages = defineMessages({ const intlMessages = defineMessages({
@ -20,6 +22,14 @@ const intlMessages = defineMessages({
id: 'app.chat.dropdown.copy', id: 'app.chat.dropdown.copy',
description: 'Copy button label', description: 'Copy button label',
}, },
copySuccess: {
id: 'app.chat.copySuccess',
description: 'aria success alert',
},
copyErr: {
id: 'app.chat.copyErr',
description: 'aria error alert',
},
options: { options: {
id: 'app.chat.dropdown.options', id: 'app.chat.dropdown.options',
description: 'Chat Options', description: 'Chat Options',
@ -92,7 +102,11 @@ class ChatDropdown extends PureComponent {
label: intl.formatMessage(intlMessages.copy), label: intl.formatMessage(intlMessages.copy),
onClick: () => { onClick: () => {
let chatHistory = ChatService.exportChat(timeWindowsValues, users, intl); let chatHistory = ChatService.exportChat(timeWindowsValues, users, intl);
navigator.clipboard.writeText(chatHistory); navigator.clipboard.writeText(chatHistory).then(() => {
alertScreenReader(intl.formatMessage(intlMessages.copySuccess));
}).catch(() => {
alertScreenReader(intl.formatMessage(intlMessages.copyErr));
});
} }
} }
) )
@ -121,6 +135,7 @@ class ChatDropdown extends PureComponent {
if (!amIModerator && !ENABLE_SAVE_AND_COPY_PUBLIC_CHAT) return null; if (!amIModerator && !ENABLE_SAVE_AND_COPY_PUBLIC_CHAT) return null;
return ( return (
<>
<BBBMenu <BBBMenu
trigger={ trigger={
<Button <Button
@ -148,6 +163,7 @@ class ChatDropdown extends PureComponent {
}} }}
actions={this.getAvailableActions()} actions={this.getAvailableActions()}
/> />
</>
); );
} }
} }

View File

@ -125,7 +125,7 @@
flex-shrink: 0; flex-shrink: 0;
flex-grow: 0; flex-grow: 0;
flex-basis: 3.5rem; flex-basis: 3.5rem;
color: var(--color-gray-light); color: var(--palette-placeholder-text);
text-transform: uppercase; text-transform: uppercase;
font-size: 75%; font-size: 75%;
margin: 0 0 0 calc(var(--line-height-computed) / 2); margin: 0 0 0 calc(var(--line-height-computed) / 2);

View File

@ -321,7 +321,7 @@ class ConnectionStatusComponent extends PureComponent {
{conn.offline ? ` (${intl.formatMessage(intlMessages.offline)})` : null} {conn.offline ? ` (${intl.formatMessage(intlMessages.offline)})` : null}
</div> </div>
</div> </div>
<div className={styles.status}> <div aria-label={`${intl.formatMessage(intlMessages.title)} ${conn.level}`} className={styles.status}>
<div className={styles.icon}> <div className={styles.icon}>
<Icon level={conn.level} /> <Icon level={conn.level} />
</div> </div>

View File

@ -147,6 +147,7 @@ class CustomLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -174,6 +175,7 @@ class CustomLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -426,6 +428,7 @@ class CustomLayout extends Component {
const { isOpen } = presentation; const { isOpen } = presentation;
const { camerasMargin } = DEFAULT_VALUES; const { camerasMargin } = DEFAULT_VALUES;
const sidebarSize = sidebarNavWidth + sidebarContentWidth; const sidebarSize = sidebarNavWidth + sidebarContentWidth;
const bannerAreaHeight = this.bannerAreaHeight();
const cameraDockBounds = {}; const cameraDockBounds = {};
@ -462,7 +465,7 @@ class CustomLayout extends Component {
); );
} }
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight; cameraDockBounds.top = DEFAULT_VALUES.navBarHeight + bannerAreaHeight;
cameraDockBounds.left = cameraDockLeft; cameraDockBounds.left = cameraDockLeft;
cameraDockBounds.right = isRTL ? sidebarSize : null; cameraDockBounds.right = isRTL ? sidebarSize : null;
cameraDockBounds.minWidth = mediaAreaBounds.width; cameraDockBounds.minWidth = mediaAreaBounds.width;
@ -490,7 +493,7 @@ class CustomLayout extends Component {
); );
} }
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight; cameraDockBounds.top = DEFAULT_VALUES.navBarHeight + bannerAreaHeight;
const sizeValue = input.presentation.isOpen const sizeValue = input.presentation.isOpen
? (mediaAreaBounds.left + mediaAreaBounds.width) - cameraDockWidth ? (mediaAreaBounds.left + mediaAreaBounds.width) - cameraDockWidth
: mediaAreaBounds.left; : mediaAreaBounds.left;
@ -527,7 +530,7 @@ class CustomLayout extends Component {
} }
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight cameraDockBounds.top = DEFAULT_VALUES.navBarHeight
+ mediaAreaBounds.height - cameraDockHeight; + mediaAreaBounds.height - cameraDockHeight + bannerAreaHeight;
cameraDockBounds.left = cameraDockLeft; cameraDockBounds.left = cameraDockLeft;
cameraDockBounds.right = isRTL ? sidebarSize : null; cameraDockBounds.right = isRTL ? sidebarSize : null;
cameraDockBounds.minWidth = mediaAreaBounds.width; cameraDockBounds.minWidth = mediaAreaBounds.width;
@ -555,7 +558,7 @@ class CustomLayout extends Component {
); );
} }
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight; cameraDockBounds.top = DEFAULT_VALUES.navBarHeight + bannerAreaHeight;
cameraDockBounds.left = mediaAreaBounds.left + camerasMargin; cameraDockBounds.left = mediaAreaBounds.left + camerasMargin;
cameraDockBounds.right = isRTL ? sidebarSize + (camerasMargin * 2) : null; cameraDockBounds.right = isRTL ? sidebarSize + (camerasMargin * 2) : null;
cameraDockBounds.minWidth = DEFAULT_VALUES.cameraDockMinWidth; cameraDockBounds.minWidth = DEFAULT_VALUES.cameraDockMinWidth;
@ -629,8 +632,9 @@ class CustomLayout extends Component {
const { presentation } = input; const { presentation } = input;
const { isOpen } = presentation; const { isOpen } = presentation;
const { height: actionBarHeight } = this.calculatesActionbarHeight(); const { height: actionBarHeight } = this.calculatesActionbarHeight();
const bannerAreaHeight = this.bannerAreaHeight();
const mediaAreaHeight = windowHeight() const mediaAreaHeight = windowHeight()
- (DEFAULT_VALUES.navBarHeight + actionBarHeight); - (DEFAULT_VALUES.navBarHeight + actionBarHeight + bannerAreaHeight);
const mediaAreaWidth = windowWidth() - (sidebarNavWidth + sidebarContentWidth); const mediaAreaWidth = windowWidth() - (sidebarNavWidth + sidebarContentWidth);
const mediaBounds = {}; const mediaBounds = {};
const { element: fullscreenElement } = fullscreen; const { element: fullscreenElement } = fullscreen;
@ -663,7 +667,7 @@ class CustomLayout extends Component {
case CAMERADOCK_POSITION.CONTENT_TOP: { case CAMERADOCK_POSITION.CONTENT_TOP: {
mediaBounds.width = mediaAreaWidth; mediaBounds.width = mediaAreaWidth;
mediaBounds.height = mediaAreaHeight - cameraDockBounds.height - camerasMargin; mediaBounds.height = mediaAreaHeight - cameraDockBounds.height - camerasMargin;
mediaBounds.top = navBarHeight + cameraDockBounds.height + camerasMargin; mediaBounds.top = navBarHeight + cameraDockBounds.height + camerasMargin + bannerAreaHeight;
mediaBounds.left = !isRTL ? sidebarSize : null; mediaBounds.left = !isRTL ? sidebarSize : null;
mediaBounds.right = isRTL ? sidebarSize : null; mediaBounds.right = isRTL ? sidebarSize : null;
break; break;
@ -671,7 +675,7 @@ class CustomLayout extends Component {
case CAMERADOCK_POSITION.CONTENT_RIGHT: { case CAMERADOCK_POSITION.CONTENT_RIGHT: {
mediaBounds.width = mediaAreaWidth - cameraDockBounds.width - camerasMargin; mediaBounds.width = mediaAreaWidth - cameraDockBounds.width - camerasMargin;
mediaBounds.height = mediaAreaHeight; mediaBounds.height = mediaAreaHeight;
mediaBounds.top = navBarHeight; mediaBounds.top = navBarHeight + bannerAreaHeight;
mediaBounds.left = !isRTL ? sidebarSize : null; mediaBounds.left = !isRTL ? sidebarSize : null;
mediaBounds.right = isRTL ? sidebarSize - (camerasMargin * 2) : null; mediaBounds.right = isRTL ? sidebarSize - (camerasMargin * 2) : null;
break; break;
@ -679,7 +683,7 @@ class CustomLayout extends Component {
case CAMERADOCK_POSITION.CONTENT_BOTTOM: { case CAMERADOCK_POSITION.CONTENT_BOTTOM: {
mediaBounds.width = mediaAreaWidth; mediaBounds.width = mediaAreaWidth;
mediaBounds.height = mediaAreaHeight - cameraDockBounds.height - camerasMargin; mediaBounds.height = mediaAreaHeight - cameraDockBounds.height - camerasMargin;
mediaBounds.top = navBarHeight - camerasMargin; mediaBounds.top = navBarHeight - camerasMargin + bannerAreaHeight;
mediaBounds.left = !isRTL ? sidebarSize : null; mediaBounds.left = !isRTL ? sidebarSize : null;
mediaBounds.right = isRTL ? sidebarSize : null; mediaBounds.right = isRTL ? sidebarSize : null;
break; break;
@ -687,7 +691,7 @@ class CustomLayout extends Component {
case CAMERADOCK_POSITION.CONTENT_LEFT: { case CAMERADOCK_POSITION.CONTENT_LEFT: {
mediaBounds.width = mediaAreaWidth - cameraDockBounds.width - camerasMargin; mediaBounds.width = mediaAreaWidth - cameraDockBounds.width - camerasMargin;
mediaBounds.height = mediaAreaHeight; mediaBounds.height = mediaAreaHeight;
mediaBounds.top = navBarHeight; mediaBounds.top = navBarHeight + bannerAreaHeight;
const sizeValue = sidebarNavWidth const sizeValue = sidebarNavWidth
+ sidebarContentWidth + mediaAreaWidth - mediaBounds.width; + sidebarContentWidth + mediaAreaWidth - mediaBounds.width;
mediaBounds.left = !isRTL ? sizeValue : null; mediaBounds.left = !isRTL ? sizeValue : null;
@ -697,7 +701,7 @@ class CustomLayout extends Component {
case CAMERADOCK_POSITION.SIDEBAR_CONTENT_BOTTOM: { case CAMERADOCK_POSITION.SIDEBAR_CONTENT_BOTTOM: {
mediaBounds.width = mediaAreaWidth; mediaBounds.width = mediaAreaWidth;
mediaBounds.height = mediaAreaHeight; mediaBounds.height = mediaAreaHeight;
mediaBounds.top = navBarHeight; mediaBounds.top = navBarHeight + bannerAreaHeight;
mediaBounds.left = !isRTL ? sidebarSize : null; mediaBounds.left = !isRTL ? sidebarSize : null;
mediaBounds.right = isRTL ? sidebarSize : null; mediaBounds.right = isRTL ? sidebarSize : null;
break; break;
@ -710,7 +714,7 @@ class CustomLayout extends Component {
} else { } else {
mediaBounds.width = mediaAreaWidth; mediaBounds.width = mediaAreaWidth;
mediaBounds.height = mediaAreaHeight; mediaBounds.height = mediaAreaHeight;
mediaBounds.top = DEFAULT_VALUES.navBarHeight + this.bannerAreaHeight(); mediaBounds.top = DEFAULT_VALUES.navBarHeight + bannerAreaHeight;
mediaBounds.left = !isRTL ? sidebarSize : null; mediaBounds.left = !isRTL ? sidebarSize : null;
mediaBounds.right = isRTL ? sidebarSize : null; mediaBounds.right = isRTL ? sidebarSize : null;
} }

View File

@ -81,6 +81,7 @@ class PresentationFocusLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -108,6 +109,7 @@ class PresentationFocusLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -276,6 +278,8 @@ class PresentationFocusLayout extends Component {
calculatesSidebarContentHeight() { calculatesSidebarContentHeight() {
const { layoutContextState } = this.props; const { layoutContextState } = this.props;
const { deviceType, input } = layoutContextState; const { deviceType, input } = layoutContextState;
const { presentation } = input;
const { isOpen } = presentation;
const { const {
navBarHeight, navBarHeight,
sidebarContentMinHeight, sidebarContentMinHeight,
@ -288,7 +292,7 @@ class PresentationFocusLayout extends Component {
height = windowHeight() - navBarHeight - this.bannerAreaHeight(); height = windowHeight() - navBarHeight - this.bannerAreaHeight();
minHeight = height; minHeight = height;
maxHeight = height; maxHeight = height;
} else if (input.cameraDock.numCameras > 0) { } else if (input.cameraDock.numCameras > 0 && isOpen) {
if (input.sidebarContent.height === 0) { if (input.sidebarContent.height === 0) {
height = (windowHeight() * 0.75) - this.bannerAreaHeight(); height = (windowHeight() * 0.75) - this.bannerAreaHeight();
} else { } else {
@ -368,57 +372,70 @@ class PresentationFocusLayout extends Component {
const { const {
deviceType, input, fullscreen, isRTL, deviceType, input, fullscreen, isRTL,
} = layoutContextState; } = layoutContextState;
const { presentation } = input;
const { isOpen } = presentation;
const cameraDockBounds = {}; const cameraDockBounds = {};
const sidebarSize = sidebarNavWidth + sidebarContentWidth;
if (input.cameraDock.numCameras > 0) { if (input.cameraDock.numCameras > 0) {
let cameraDockHeight = 0; if (!isOpen) {
if (fullscreen.group === 'webcams') {
cameraDockBounds.width = windowWidth();
cameraDockBounds.minWidth = windowWidth();
cameraDockBounds.maxWidth = windowWidth();
cameraDockBounds.height = windowHeight();
cameraDockBounds.minHeight = windowHeight();
cameraDockBounds.maxHeight = windowHeight();
cameraDockBounds.top = 0;
cameraDockBounds.left = 0;
cameraDockBounds.right = 0;
cameraDockBounds.zIndex = 99;
return cameraDockBounds;
}
if (deviceType === DEVICE_TYPE.MOBILE) {
cameraDockBounds.top = mediaAreaBounds.top + mediaBounds.height;
cameraDockBounds.left = 0;
cameraDockBounds.right = 0;
cameraDockBounds.minWidth = mediaAreaBounds.width;
cameraDockBounds.width = mediaAreaBounds.width; cameraDockBounds.width = mediaAreaBounds.width;
cameraDockBounds.maxWidth = mediaAreaBounds.width; cameraDockBounds.maxWidth = mediaAreaBounds.width;
cameraDockBounds.minHeight = DEFAULT_VALUES.cameraDockMinHeight; cameraDockBounds.height = mediaAreaBounds.height;
cameraDockBounds.height = mediaAreaBounds.height - mediaBounds.height; cameraDockBounds.maxHeight = mediaAreaBounds.height;
cameraDockBounds.maxHeight = mediaAreaBounds.height - mediaBounds.height; cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : 0;
cameraDockBounds.right = isRTL ? sidebarSize : null;
} else { } else {
if (input.cameraDock.height === 0) { let cameraDockHeight = 0;
cameraDockHeight = min(
max((windowHeight() - sidebarContentHeight), DEFAULT_VALUES.cameraDockMinHeight), if (fullscreen.group === 'webcams') {
(windowHeight() - DEFAULT_VALUES.cameraDockMinHeight), cameraDockBounds.width = windowWidth();
); cameraDockBounds.minWidth = windowWidth();
} else { cameraDockBounds.maxWidth = windowWidth();
cameraDockHeight = min( cameraDockBounds.height = windowHeight();
max(input.cameraDock.height, DEFAULT_VALUES.cameraDockMinHeight), cameraDockBounds.minHeight = windowHeight();
(windowHeight() - DEFAULT_VALUES.cameraDockMinHeight), cameraDockBounds.maxHeight = windowHeight();
); cameraDockBounds.top = 0;
cameraDockBounds.left = 0;
cameraDockBounds.right = 0;
cameraDockBounds.zIndex = 99;
return cameraDockBounds;
}
if (deviceType === DEVICE_TYPE.MOBILE) {
cameraDockBounds.top = mediaAreaBounds.top + mediaBounds.height;
cameraDockBounds.left = 0;
cameraDockBounds.right = 0;
cameraDockBounds.minWidth = mediaAreaBounds.width;
cameraDockBounds.width = mediaAreaBounds.width;
cameraDockBounds.maxWidth = mediaAreaBounds.width;
cameraDockBounds.minHeight = DEFAULT_VALUES.cameraDockMinHeight;
cameraDockBounds.height = mediaAreaBounds.height - mediaBounds.height;
cameraDockBounds.maxHeight = mediaAreaBounds.height - mediaBounds.height;
} else {
if (input.cameraDock.height === 0) {
cameraDockHeight = min(
max((windowHeight() - sidebarContentHeight), DEFAULT_VALUES.cameraDockMinHeight),
(windowHeight() - DEFAULT_VALUES.cameraDockMinHeight),
);
} else {
cameraDockHeight = min(
max(input.cameraDock.height, DEFAULT_VALUES.cameraDockMinHeight),
(windowHeight() - DEFAULT_VALUES.cameraDockMinHeight),
);
}
cameraDockBounds.top = windowHeight() - cameraDockHeight;
cameraDockBounds.left = !isRTL ? sidebarNavWidth : 0;
cameraDockBounds.right = isRTL ? sidebarNavWidth : 0;
cameraDockBounds.minWidth = sidebarContentWidth;
cameraDockBounds.width = sidebarContentWidth;
cameraDockBounds.maxWidth = sidebarContentWidth;
cameraDockBounds.minHeight = DEFAULT_VALUES.cameraDockMinHeight;
cameraDockBounds.height = cameraDockHeight;
cameraDockBounds.maxHeight = windowHeight() - sidebarContentHeight;
cameraDockBounds.zIndex = 1;
} }
cameraDockBounds.top = windowHeight() - cameraDockHeight;
cameraDockBounds.left = !isRTL ? sidebarNavWidth : 0;
cameraDockBounds.right = isRTL ? sidebarNavWidth : 0;
cameraDockBounds.minWidth = sidebarContentWidth;
cameraDockBounds.width = sidebarContentWidth;
cameraDockBounds.maxWidth = sidebarContentWidth;
cameraDockBounds.minHeight = DEFAULT_VALUES.cameraDockMinHeight;
cameraDockBounds.height = cameraDockHeight;
cameraDockBounds.maxHeight = windowHeight() - sidebarContentHeight;
cameraDockBounds.zIndex = 1;
} }
} else { } else {
cameraDockBounds.width = 0; cameraDockBounds.width = 0;

View File

@ -81,6 +81,7 @@ class SmartLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -108,6 +109,7 @@ class SmartLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,

View File

@ -83,6 +83,7 @@ class VideoFocusLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -113,6 +114,7 @@ class VideoFocusLayout extends Component {
isOpen: false, isOpen: false,
}, },
presentation: { presentation: {
isOpen: input.presentation.isOpen,
slidesLength: input.presentation.slidesLength, slidesLength: input.presentation.slidesLength,
currentSlide: { currentSlide: {
...input.presentation.currentSlide, ...input.presentation.currentSlide,
@ -374,42 +376,52 @@ class VideoFocusLayout extends Component {
const { const {
deviceType, input, fullscreen, isRTL, deviceType, input, fullscreen, isRTL,
} = layoutContextState; } = layoutContextState;
const { cameraDock } = input; const { cameraDock, presentation } = input;
const { isOpen } = presentation;
const { numCameras } = cameraDock; const { numCameras } = cameraDock;
const cameraDockBounds = {}; const cameraDockBounds = {};
if (numCameras > 0) { if (numCameras > 0) {
if (deviceType === DEVICE_TYPE.MOBILE) { if (!isOpen) {
cameraDockBounds.minHeight = mediaAreaBounds.height * 0.7; cameraDockBounds.width = mediaAreaBounds.width;
cameraDockBounds.height = mediaAreaBounds.height * 0.7; cameraDockBounds.maxWidth = mediaAreaBounds.width;
cameraDockBounds.maxHeight = mediaAreaBounds.height * 0.7;
} else {
cameraDockBounds.minHeight = mediaAreaBounds.height;
cameraDockBounds.height = mediaAreaBounds.height; cameraDockBounds.height = mediaAreaBounds.height;
cameraDockBounds.maxHeight = mediaAreaBounds.height; cameraDockBounds.maxHeight = mediaAreaBounds.height;
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : 0;
cameraDockBounds.right = isRTL ? sidebarSize : null;
} else {
if (deviceType === DEVICE_TYPE.MOBILE) {
cameraDockBounds.minHeight = mediaAreaBounds.height * 0.7;
cameraDockBounds.height = mediaAreaBounds.height * 0.7;
cameraDockBounds.maxHeight = mediaAreaBounds.height * 0.7;
} else {
cameraDockBounds.minHeight = mediaAreaBounds.height;
cameraDockBounds.height = mediaAreaBounds.height;
cameraDockBounds.maxHeight = mediaAreaBounds.height;
}
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : null;
cameraDockBounds.right = isRTL ? sidebarSize : null;
cameraDockBounds.minWidth = mediaAreaBounds.width;
cameraDockBounds.width = mediaAreaBounds.width;
cameraDockBounds.maxWidth = mediaAreaBounds.width;
cameraDockBounds.zIndex = 1;
if (fullscreen.group === 'webcams') {
cameraDockBounds.width = windowWidth();
cameraDockBounds.minWidth = windowWidth();
cameraDockBounds.maxWidth = windowWidth();
cameraDockBounds.height = windowHeight();
cameraDockBounds.minHeight = windowHeight();
cameraDockBounds.maxHeight = windowHeight();
cameraDockBounds.top = 0;
cameraDockBounds.left = 0;
cameraDockBounds.zIndex = 99;
}
} }
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : null;
cameraDockBounds.right = isRTL ? sidebarSize : null;
cameraDockBounds.minWidth = mediaAreaBounds.width;
cameraDockBounds.width = mediaAreaBounds.width;
cameraDockBounds.maxWidth = mediaAreaBounds.width;
cameraDockBounds.zIndex = 1;
if (fullscreen.group === 'webcams') {
cameraDockBounds.width = windowWidth();
cameraDockBounds.minWidth = windowWidth();
cameraDockBounds.maxWidth = windowWidth();
cameraDockBounds.height = windowHeight();
cameraDockBounds.minHeight = windowHeight();
cameraDockBounds.maxHeight = windowHeight();
cameraDockBounds.top = 0;
cameraDockBounds.left = 0;
cameraDockBounds.zIndex = 99;
}
return cameraDockBounds; return cameraDockBounds;
} }

View File

@ -47,9 +47,14 @@ const swapLayout = {
tracker: new Tracker.Dependency(), tracker: new Tracker.Dependency(),
}; };
const setSwapLayout = () => { const setSwapLayout = (layoutContextDispatch) => {
swapLayout.value = getFromUserSettings('bbb_auto_swap_layout', LAYOUT_CONFIG.autoSwapLayout); swapLayout.value = getFromUserSettings('bbb_auto_swap_layout', LAYOUT_CONFIG.autoSwapLayout);
swapLayout.tracker.changed(); swapLayout.tracker.changed();
layoutContextDispatch({
type: ACTIONS.SET_PRESENTATION_IS_OPEN,
value: !swapLayout.value,
});
}; };
const toggleSwapLayout = (layoutContextDispatch) => { const toggleSwapLayout = (layoutContextDispatch) => {

View File

@ -117,6 +117,7 @@ class BBBMenu extends React.Component {
open={Boolean(anchorEl)} open={Boolean(anchorEl)}
onClose={this.handleClose} onClose={this.handleClose}
className={menuClasses.join(' ')} className={menuClasses.join(' ')}
style={{ zIndex: 9999 }}
> >
{actionsItems} {actionsItems}
{anchorEl && window.innerWidth < MAX_WIDTH && {anchorEl && window.innerWidth < MAX_WIDTH &&

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ReactModal from 'react-modal'; import ReactModal from 'react-modal';
import { styles } from './styles.scss'; import { styles } from './styles.scss';
import { registerTitleView, unregisterTitleView } from '/imports/utils/dom-utils';
const propTypes = { const propTypes = {
overlayClassName: PropTypes.string.isRequired, overlayClassName: PropTypes.string.isRequired,
@ -19,6 +20,15 @@ const defaultProps = {
}; };
export default class ModalBase extends Component { export default class ModalBase extends Component {
componentDidMount() {
registerTitleView(this.props.contentLabel);
}
componentWillUnmount() {
unregisterTitleView();
}
render() { render() {
if (!this.props.isOpen) return null; if (!this.props.isOpen) return null;
@ -27,8 +37,8 @@ export default class ModalBase extends Component {
{...this.props} {...this.props}
parentSelector={() => { parentSelector={() => {
if (document.fullscreenElement && if (document.fullscreenElement &&
document.fullscreenElement.nodeName && document.fullscreenElement.nodeName &&
document.fullscreenElement.nodeName.toLowerCase() === 'div') document.fullscreenElement.nodeName.toLowerCase() === 'div')
return document.fullscreenElement; return document.fullscreenElement;
else return document.body; else return document.body;
}} }}
@ -55,12 +65,12 @@ export const withModalState = ComponentToWrap =>
this.show = this.show.bind(this); this.show = this.show.bind(this);
} }
hide(cb = () => {}) { hide(cb = () => { }) {
Promise.resolve(cb()) Promise.resolve(cb())
.then(() => this.setState({ isOpen: false })); .then(() => this.setState({ isOpen: false }));
} }
show(cb = () => {}) { show(cb = () => { }) {
Promise.resolve(cb()) Promise.resolve(cb())
.then(() => this.setState({ isOpen: true })); .then(() => this.setState({ isOpen: true }));
} }

View File

@ -11,6 +11,7 @@ import LiveResult from './live-result/component';
import { styles } from './styles.scss'; import { styles } from './styles.scss';
import { PANELS, ACTIONS } from '../layout/enums'; import { PANELS, ACTIONS } from '../layout/enums';
import DragAndDrop from './dragAndDrop/component'; import DragAndDrop from './dragAndDrop/component';
import { alertScreenReader } from '/imports/utils/dom-utils';
const intlMessages = defineMessages({ const intlMessages = defineMessages({
pollPaneTitle: { pollPaneTitle: {
@ -185,6 +186,14 @@ const intlMessages = defineMessages({
id: 'app.switch.offLabel', id: 'app.switch.offLabel',
description: 'label for toggle switch off state', description: 'label for toggle switch off state',
}, },
removePollOpt: {
id: 'app.poll.removePollOpt',
description: 'screen reader alert for removed poll option',
},
emptyPollOpt: {
id: 'app.poll.emptyPollOpt',
description: 'screen reader for blank poll option',
},
}); });
const POLL_SETTINGS = Meteor.settings.public.poll; const POLL_SETTINGS = Meteor.settings.public.poll;
@ -295,10 +304,15 @@ class Poll extends Component {
} }
handleRemoveOption(index) { handleRemoveOption(index) {
const { intl } = this.props;
const { optList } = this.state; const { optList } = this.state;
const list = [...optList]; const list = [...optList];
const removed = list[index];
list.splice(index, 1); list.splice(index, 1);
this.setState({ optList: list }); this.setState({ optList: list }, () => {
alertScreenReader(`${intl.formatMessage(intlMessages.removePollOpt,
{ 0: removed.val || intl.formatMessage(intlMessages.emptyPollOpt) })}`);
});
} }
handleAddOption() { handleAddOption() {
@ -395,7 +409,10 @@ class Poll extends Component {
this.handleRemoveOption(i); this.handleRemoveOption(i);
}} }}
/> />
<span className="sr-only" id={`option-${i}`}>{intl.formatMessage(intlMessages.deleteRespDesc, { 0: o.val })}</span> <span className="sr-only" id={`option-${i}`}>
{intl.formatMessage(intlMessages.deleteRespDesc,
{ 0: (o.val || intl.formatMessage(intlMessages.emptyPollOpt)) })}
</span>
</> </>
) )
: <div style={{ width: '40px' }} />} : <div style={{ width: '40px' }} />}

View File

@ -13,6 +13,7 @@ import logger from '/imports/startup/client/logger';
import { notify } from '/imports/ui/services/notification'; import { notify } from '/imports/ui/services/notification';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import _ from 'lodash'; import _ from 'lodash';
import { registerTitleView, unregisterTitleView } from '/imports/utils/dom-utils';
import { styles } from './styles'; import { styles } from './styles';
const { isMobile } = deviceInfo; const { isMobile } = deviceInfo;
@ -214,6 +215,10 @@ const intlMessages = defineMessages({
id: 'app.presentationUploder.clearErrorsDesc', id: 'app.presentationUploder.clearErrorsDesc',
description: 'aria description for button clearing upload error', description: 'aria description for button clearing upload error',
}, },
uploadViewTitle: {
id: 'app.presentationUploder.uploadViewTitle',
description: 'view name apended to document title',
}
}); });
class PresentationUploader extends Component { class PresentationUploader extends Component {
@ -251,10 +256,16 @@ class PresentationUploader extends Component {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { isOpen, presentations: propPresentations } = this.props; const { isOpen, presentations: propPresentations, intl } = this.props;
const { presentations } = this.state; const { presentations } = this.state;
if (!isOpen && prevProps.isOpen) {
unregisterTitleView();
}
// Updates presentation list when chat modal opens to avoid missing presentations // Updates presentation list when chat modal opens to avoid missing presentations
if (isOpen && !prevProps.isOpen) { if (isOpen && !prevProps.isOpen) {
registerTitleView(intl.formatMessage(intlMessages.uploadViewTitle));
const focusableElements = const focusableElements =
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const modal = document.getElementById('upload-modal'); const modal = document.getElementById('upload-modal');

View File

@ -22,6 +22,10 @@ const intlMessages = defineMessages({
id: 'app.guest-policy.description', id: 'app.guest-policy.description',
description: 'Guest policy description', description: 'Guest policy description',
}, },
policyBtnDesc: {
id: 'app.guest-policy.policyBtnDesc',
description: 'aria description for guest policy button',
},
askModerator: { askModerator: {
id: 'app.guest-policy.button.askModerator', id: 'app.guest-policy.button.askModerator',
description: 'Ask moderator button label', description: 'Ask moderator button label',
@ -84,9 +88,11 @@ class GuestPolicyComponent extends PureComponent {
<div className={styles.content}> <div className={styles.content}>
<Button <Button
color="primary" color="primary"
className={styles.button} className={[styles.button, guestPolicy === ASK_MODERATOR && styles.active].join(' ')}
disabled={guestPolicy === ASK_MODERATOR} disabled={guestPolicy === ASK_MODERATOR}
label={intl.formatMessage(intlMessages.askModerator)} label={intl.formatMessage(intlMessages.askModerator)}
aria-describedby={guestPolicy === ASK_MODERATOR ? 'selected-btn-desc' : 'policy-btn-desc'}
aria-pressed={guestPolicy === ASK_MODERATOR}
data-test="askModerator" data-test="askModerator"
onClick={() => { onClick={() => {
changeGuestPolicy(ASK_MODERATOR); changeGuestPolicy(ASK_MODERATOR);
@ -95,9 +101,11 @@ class GuestPolicyComponent extends PureComponent {
/> />
<Button <Button
color="primary" color="primary"
className={styles.button} className={[styles.button, guestPolicy === ALWAYS_ACCEPT && styles.active].join(' ')}
disabled={guestPolicy === ALWAYS_ACCEPT} disabled={guestPolicy === ALWAYS_ACCEPT}
label={intl.formatMessage(intlMessages.alwaysAccept)} label={intl.formatMessage(intlMessages.alwaysAccept)}
aria-describedby={guestPolicy === ALWAYS_ACCEPT ? 'selected-btn-desc' : 'policy-btn-desc'}
aria-pressed={guestPolicy === ALWAYS_ACCEPT}
data-test="alwaysAccept" data-test="alwaysAccept"
onClick={() => { onClick={() => {
changeGuestPolicy(ALWAYS_ACCEPT); changeGuestPolicy(ALWAYS_ACCEPT);
@ -106,9 +114,11 @@ class GuestPolicyComponent extends PureComponent {
/> />
<Button <Button
color="primary" color="primary"
className={styles.button} className={[styles.button, guestPolicy === ALWAYS_DENY && styles.active].join(' ')}
disabled={guestPolicy === ALWAYS_DENY} disabled={guestPolicy === ALWAYS_DENY}
label={intl.formatMessage(intlMessages.alwaysDeny)} label={intl.formatMessage(intlMessages.alwaysDeny)}
aria-describedby={guestPolicy === ALWAYS_DENY ? 'selected-btn-desc' : 'policy-btn-desc'}
aria-pressed={guestPolicy === ALWAYS_DENY}
data-test="alwaysDeny" data-test="alwaysDeny"
onClick={() => { onClick={() => {
changeGuestPolicy(ALWAYS_DENY); changeGuestPolicy(ALWAYS_DENY);
@ -116,6 +126,9 @@ class GuestPolicyComponent extends PureComponent {
}} }}
/> />
</div> </div>
<div id="policy-btn-desc" aria-hidden className="sr-only">
{intl.formatMessage(intlMessages.policyBtnDesc)}
</div>
</div> </div>
</Modal> </Modal>
); );

View File

@ -66,3 +66,9 @@
box-sizing: border-box; box-sizing: border-box;
margin: 5px; margin: 5px;
} }
.active {
span {
text-decoration: underline;
}
}

View File

@ -98,7 +98,7 @@ const WebcamComponent = ({
); );
Storage.setItem('webcamSize', { width: newCameraMaxWidth, height: lastHeight }); Storage.setItem('webcamSize', { width: newCameraMaxWidth, height: lastHeight });
} }
}, [cameraDock.position, isPresenter, displayPresentation]); }, [cameraDock.position, cameraDock.maxWidth, isPresenter, displayPresentation]);
const onResizeHandle = (deltaWidth, deltaHeight) => { const onResizeHandle = (deltaWidth, deltaHeight) => {
if (cameraDock.resizableEdge.top || cameraDock.resizableEdge.bottom) { if (cameraDock.resizableEdge.top || cameraDock.resizableEdge.bottom) {

View File

@ -1,4 +1,6 @@
:root { :root {
--palette-placeholder-text: #787675;
--color-white: #FFF; --color-white: #FFF;
--color-off-white: #F3F6F9; --color-off-white: #F3F6F9;

View File

@ -0,0 +1,40 @@
const TITLE_WITH_VIEW = 3;
const ARIA_ALERT_TIMEOUT = 3000;
const getTitleData = () => {
const title = document.getElementsByTagName('title')[0];
return { title, data: title?.text?.split(' - ') };
}
export const registerTitleView = (v) => {
const { title, data } = getTitleData();
if (data.length < TITLE_WITH_VIEW) data.push(`${v}`);
else data.splice(TITLE_WITH_VIEW - 1, TITLE_WITH_VIEW, v);
title.text = data.join(' - ');
};
export const unregisterTitleView = () => {
const { title, data } = getTitleData();
if (data.length === TITLE_WITH_VIEW) {
data.splice(TITLE_WITH_VIEW - 1, TITLE_WITH_VIEW, 'Default');
}
title.text = data.join(' - ');
};
export const alertScreenReader = (s = '') => {
const app = document.getElementById('app');
const ariaAlert = document.createElement("div");
ariaAlert.setAttribute("id", "aria-alert");
ariaAlert.setAttribute("role", "alert");
ariaAlert.setAttribute("aria-hidden", false);
ariaAlert.setAttribute("className", "sr-only");
ariaAlert.textContent = s;
app.appendChild(ariaAlert);
setTimeout(() => {
document.getElementById('aria-alert').remove();
}, ARIA_ALERT_TIMEOUT);
};
export default { registerTitleView, unregisterTitleView, alertScreenReader };

View File

@ -25,6 +25,8 @@
"app.chat.multi.typing": "Multiple users are typing", "app.chat.multi.typing": "Multiple users are typing",
"app.chat.one.typing": "{0} is typing", "app.chat.one.typing": "{0} is typing",
"app.chat.two.typing": "{0} and {1} are typing", "app.chat.two.typing": "{0} and {1} are typing",
"app.chat.copySuccess": "Copied chat transcript",
"app.chat.copyErr": "Copy chat transcript failed",
"app.captions.label": "Captions", "app.captions.label": "Captions",
"app.captions.menu.close": "Close", "app.captions.menu.close": "Close",
"app.captions.menu.start": "Start", "app.captions.menu.start": "Start",
@ -51,6 +53,7 @@
"app.captions.pad.dictationOffDesc": "Turns speech recognition off", "app.captions.pad.dictationOffDesc": "Turns speech recognition off",
"app.captions.pad.speechRecognitionStop": "Speech recognition stopped due to the browser incompatibility or some time of silence", "app.captions.pad.speechRecognitionStop": "Speech recognition stopped due to the browser incompatibility or some time of silence",
"app.textInput.sendLabel": "Send", "app.textInput.sendLabel": "Send",
"app.title.defaultViewLabel": "Default presentation view",
"app.note.title": "Shared Notes", "app.note.title": "Shared Notes",
"app.note.label": "Note", "app.note.label": "Note",
"app.note.hideNoteLabel": "Hide note", "app.note.hideNoteLabel": "Hide note",
@ -230,6 +233,7 @@
"app.presentationUploder.itemPlural" : "items", "app.presentationUploder.itemPlural" : "items",
"app.presentationUploder.clearErrors": "Clear errors", "app.presentationUploder.clearErrors": "Clear errors",
"app.presentationUploder.clearErrorsDesc": "Clears failed presentation uploads", "app.presentationUploder.clearErrorsDesc": "Clears failed presentation uploads",
"app.presentationUploder.uploadViewTitle": "Upload Presentation",
"app.poll.pollPaneTitle": "Polling", "app.poll.pollPaneTitle": "Polling",
"app.poll.quickPollTitle": "Quick Poll", "app.poll.quickPollTitle": "Quick Poll",
"app.poll.hidePollDesc": "Hides the poll menu pane", "app.poll.hidePollDesc": "Hides the poll menu pane",
@ -288,6 +292,8 @@
"app.poll.liveResult.usersTitle": "Users", "app.poll.liveResult.usersTitle": "Users",
"app.poll.liveResult.responsesTitle": "Response", "app.poll.liveResult.responsesTitle": "Response",
"app.poll.liveResult.secretLabel": "This is an anonymous poll. Individual responses are not shown.", "app.poll.liveResult.secretLabel": "This is an anonymous poll. Individual responses are not shown.",
"app.poll.removePollOpt": "Removed Poll option {0}",
"app.poll.emptyPollOpt": "Blank",
"app.polling.pollingTitle": "Polling options", "app.polling.pollingTitle": "Polling options",
"app.polling.pollQuestionTitle": "Polling Question", "app.polling.pollQuestionTitle": "Polling Question",
"app.polling.submitLabel": "Submit", "app.polling.submitLabel": "Submit",
@ -662,6 +668,7 @@
"app.guest-policy.button.askModerator": "Ask moderator", "app.guest-policy.button.askModerator": "Ask moderator",
"app.guest-policy.button.alwaysAccept": "Always accept", "app.guest-policy.button.alwaysAccept": "Always accept",
"app.guest-policy.button.alwaysDeny": "Always deny", "app.guest-policy.button.alwaysDeny": "Always deny",
"app.guest-policy.policyBtnDesc": "Sets meeting guest policy",
"app.connection-status.ariaTitle": "Connection status modal", "app.connection-status.ariaTitle": "Connection status modal",
"app.connection-status.title": "Connection status", "app.connection-status.title": "Connection status",
"app.connection-status.description": "View users' connection status", "app.connection-status.description": "View users' connection status",

View File

@ -7,11 +7,11 @@ class Audio extends Page {
} }
async test() { async test() {
return await util.joinAudio(this); return util.joinAudio(this);
} }
async microphone() { async microphone() {
return await util.joinMicrophone(this); return util.joinMicrophone(this);
} }
} }

View File

@ -1,26 +1,27 @@
const ae = require('./elements'); const ae = require('./elements');
const { clickElement, getElementLength } = require('../core/util');
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
async function joinAudio(test) { async function joinAudio(test) {
await test.waitForSelector(ae.joinAudio, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.joinAudio, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ae.joinAudio); await test.page.evaluate(clickElement, ae.joinAudio);
await test.waitForSelector(ae.listen, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.listen, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ae.listen); await test.page.evaluate(clickElement, ae.listen);
await test.waitForSelector(ae.connectingStatus, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.connectingStatus, ELEMENT_WAIT_TIME);
await test.waitForElementHandleToBeRemoved(ae.connectingStatus, ELEMENT_WAIT_LONGER_TIME); await test.waitForElementHandleToBeRemoved(ae.connectingStatus, ELEMENT_WAIT_LONGER_TIME);
const parsedSettings = await test.getSettingsYaml(); const parsedSettings = await test.getSettingsYaml();
const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout); const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout);
await test.waitForSelector(ae.leaveAudio, listenOnlyCallTimeout); await test.waitForSelector(ae.leaveAudio, listenOnlyCallTimeout);
await test.waitForSelector(ae.whiteboard, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.whiteboard, ELEMENT_WAIT_TIME);
const resp = await test.page.evaluate(getTestElement, ae.leaveAudio); const resp = await test.page.evaluate(getElementLength, ae.leaveAudio) >= 1;
return resp; return resp;
} }
async function joinMicrophone(test) { async function joinMicrophone(test) {
await test.waitForSelector(ae.joinAudio, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.joinAudio, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ae.joinAudio); await test.page.evaluate(clickElement, ae.joinAudio);
await test.waitForSelector(ae.microphone, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.microphone, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ae.microphone); await test.page.evaluate(clickElement, ae.microphone);
await test.waitForSelector(ae.connectingStatus, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.connectingStatus, ELEMENT_WAIT_TIME);
await test.waitForElementHandleToBeRemoved(ae.connectingStatus, ELEMENT_WAIT_LONGER_TIME); await test.waitForElementHandleToBeRemoved(ae.connectingStatus, ELEMENT_WAIT_LONGER_TIME);
const parsedSettings = await test.getSettingsYaml(); const parsedSettings = await test.getSettingsYaml();
@ -28,17 +29,9 @@ async function joinMicrophone(test) {
await test.waitForSelector(ae.audioAudible, listenOnlyCallTimeout); await test.waitForSelector(ae.audioAudible, listenOnlyCallTimeout);
await test.click(ae.audioAudible, true); await test.click(ae.audioAudible, true);
await test.waitForSelector(ae.whiteboard, ELEMENT_WAIT_TIME); await test.waitForSelector(ae.whiteboard, ELEMENT_WAIT_TIME);
const resp = await test.page.evaluate(getTestElement, ae.audioAudible); const resp = await test.page.evaluate(getElementLength, ae.audioAudible) >= 1;
return resp; return resp;
} }
async function clickTestElement(element) {
document.querySelectorAll(element)[0].click();
}
async function getTestElement(element) {
return document.querySelectorAll(element).length >= 1 === true;
}
exports.joinAudio = joinAudio; exports.joinAudio = joinAudio;
exports.joinMicrophone = joinMicrophone; exports.joinMicrophone = joinMicrophone;

View File

@ -2,14 +2,13 @@ const moment = require('moment');
const path = require('path'); const path = require('path');
const Page = require('../core/page'); const Page = require('../core/page');
const params = require('../params'); const params = require('../params');
const util = require('./util');
const be = require('./elements'); // breakout elements const be = require('./elements'); // breakout elements
const we = require('../webcam/elements'); // webcam elements const we = require('../webcam/elements'); // webcam elements
const ae = require('../audio/elements'); // audio elements const ae = require('../audio/elements'); // audio elements
const ue = require('../user/elements'); // user elements const ue = require('../user/elements'); // user elements
const ce = require('../customparameters/elements'); // customparameters elements const ce = require('../customparameters/elements'); // customparameters elements
const e = require('../core/elements'); // page base elements const e = require('../core/elements'); // page base elements
// core constants (Timeouts vars imported) const { checkElement, clickElement } = require('../core/util');
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const today = moment().format('DD-MM-YYYY'); const today = moment().format('DD-MM-YYYY');
@ -48,7 +47,7 @@ class Create {
await this.page1.click(ue.askModerator, true); await this.page1.click(ue.askModerator, true);
await this.page1.screenshot(`${testName}`, `05-clicked-askModerator-[${this.page1.meetingId}]`); await this.page1.screenshot(`${testName}`, `05-clicked-askModerator-[${this.page1.meetingId}]`);
await this.initViewer(testName); await this.initViewer(testName);
const responseLoggedIn = await this.page1.page.evaluate(util.getTestElement, ue.waitingUsersBtn); const responseLoggedIn = await this.page1.page.evaluate(checkElement, ue.waitingUsersBtn);
await this.page1.screenshot(`${testName}`, `06-after-viewer-acceptance-[${this.page1.meetingId}]`); await this.page1.screenshot(`${testName}`, `06-after-viewer-acceptance-[${this.page1.meetingId}]`);
return responseLoggedIn; return responseLoggedIn;
} catch (err) { } catch (err) {
@ -74,7 +73,7 @@ class Create {
await this.page1.screenshot(`${testName}`, `05-clicked-alwaysAccept-[${this.page1.meetingId}]`); await this.page1.screenshot(`${testName}`, `05-clicked-alwaysAccept-[${this.page1.meetingId}]`);
await this.initViewer(testName); await this.initViewer(testName);
await this.page3.closeAudioModal(); await this.page3.closeAudioModal();
const responseLoggedIn = await this.page3.page.evaluate(util.getTestElement, e.whiteboard); const responseLoggedIn = await this.page3.page.evaluate(checkElement, e.whiteboard);
await this.page3.screenshot(`${testName}`, `06-after-viewer-connection-[${this.page1.meetingId}]`); await this.page3.screenshot(`${testName}`, `06-after-viewer-connection-[${this.page1.meetingId}]`);
return responseLoggedIn; return responseLoggedIn;
} catch (err) { } catch (err) {
@ -99,7 +98,7 @@ class Create {
await this.page1.click(ue.alwaysAccept, true); await this.page1.click(ue.alwaysAccept, true);
await this.page1.screenshot(`${testName}`, `05-clicked-alwaysAccept-[${this.page1.meetingId}]`); await this.page1.screenshot(`${testName}`, `05-clicked-alwaysAccept-[${this.page1.meetingId}]`);
await this.initViewer(testName); await this.initViewer(testName);
const responseLoggedIn = await this.page3.page.evaluate(util.getTestElement, ue.joinMeetingDemoPage); const responseLoggedIn = await this.page3.page.evaluate(checkElement, ue.joinMeetingDemoPage);
await this.page3.screenshot(`${testName}`, `06-after-viewer-gets-denied-[${this.page1.meetingId}]`); await this.page3.screenshot(`${testName}`, `06-after-viewer-gets-denied-[${this.page1.meetingId}]`);
return responseLoggedIn; return responseLoggedIn;
} catch (err) { } catch (err) {
@ -116,20 +115,20 @@ class Create {
await this.page1.screenshot(`${testName}`, `01-page01-initialized-${testName}`); await this.page1.screenshot(`${testName}`, `01-page01-initialized-${testName}`);
await this.page2.screenshot(`${testName}`, `01-page02-initialized-${testName}`); await this.page2.screenshot(`${testName}`, `01-page02-initialized-${testName}`);
await this.page1.page.evaluate(util.clickTestElement, be.manageUsers); await this.page1.page.evaluate(clickElement, be.manageUsers);
await this.page1.page.evaluate(util.clickTestElement, be.createBreakoutRooms); await this.page1.page.evaluate(clickElement, be.createBreakoutRooms);
await this.page1.screenshot(`${testName}`, `02-page01-creating-breakoutrooms-${testName}`); await this.page1.screenshot(`${testName}`, `02-page01-creating-breakoutrooms-${testName}`);
await this.page1.waitForSelector(be.randomlyAssign, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(be.randomlyAssign, ELEMENT_WAIT_TIME);
await this.page1.page.evaluate(util.clickTestElement, be.randomlyAssign); await this.page1.page.evaluate(clickElement, be.randomlyAssign);
await this.page1.screenshot(`${testName}`, `03-page01-randomly-assign-user-${testName}`); await this.page1.screenshot(`${testName}`, `03-page01-randomly-assign-user-${testName}`);
await this.page1.waitForSelector(be.modalConfirmButton, ELEMENT_WAIT_LONGER_TIME); await this.page1.waitForSelector(be.modalConfirmButton, ELEMENT_WAIT_LONGER_TIME);
await this.page1.page.evaluate(util.clickTestElement, be.modalConfirmButton); await this.page1.page.evaluate(clickElement, be.modalConfirmButton);
await this.page1.screenshot(`${testName}`, `04-page01-confirm-breakoutrooms-creation-${testName}`); await this.page1.screenshot(`${testName}`, `04-page01-confirm-breakoutrooms-creation-${testName}`);
await this.page2.waitForSelector(be.modalConfirmButton, ELEMENT_WAIT_LONGER_TIME); await this.page2.waitForSelector(be.modalConfirmButton, ELEMENT_WAIT_LONGER_TIME);
await this.page2.page.evaluate(util.clickTestElement, be.modalConfirmButton); await this.page2.page.evaluate(clickElement, be.modalConfirmButton);
await this.page2.screenshot(`${testName}`, `02-page02-accept-invite-breakoutrooms-${testName}`); await this.page2.screenshot(`${testName}`, `02-page02-accept-invite-breakoutrooms-${testName}`);
await this.page2.page.bringToFront(); await this.page2.page.bringToFront();
@ -157,7 +156,7 @@ class Create {
// Check if Breakoutrooms have been created // Check if Breakoutrooms have been created
async testCreatedBreakout(testName) { async testCreatedBreakout(testName) {
try { try {
const resp = await this.page1.page.evaluate(() => document.querySelectorAll('div[data-test="breakoutRoomsItem"]').length !== 0); const resp = await this.page1.page.evaluate(checkElement, be.breakoutRoomsItem);
if (resp === true) { if (resp === true) {
await this.page1.screenshot(`${testName}`, `05-page01-success-${testName}`); await this.page1.screenshot(`${testName}`, `05-page01-success-${testName}`);

View File

@ -1,13 +1,12 @@
const path = require('path'); const path = require('path');
const moment = require('moment'); const moment = require('moment');
const Page = require('../core/page');
const Create = require('./create'); const Create = require('./create');
const util = require('./util');
const utilScreenShare = require('../screenshare/util'); const utilScreenShare = require('../screenshare/util');
const e = require('./elements'); const e = require('./elements');
const pe = require('../core/elements'); const pe = require('../core/elements');
const we = require('../webcam/elements'); const we = require('../webcam/elements');
const ae = require('../audio/elements'); const ae = require('../audio/elements');
const { checkElement } = require('../core/util');
const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported) const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported)
const today = moment().format('DD-MM-YYYY'); const today = moment().format('DD-MM-YYYY');
@ -41,7 +40,7 @@ class Join extends Create {
} }
await this.page3.logger('before pages check'); await this.page3.logger('before pages check');
const resp = await page2[2].evaluate(util.getTestElement, pe.isTalking); const resp = await page2[2].evaluate(checkElement, pe.isTalking);
if (process.env.GENERATE_EVIDENCES === 'true') { if (process.env.GENERATE_EVIDENCES === 'true') {
await page2[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/06-breakout-page02-user-joined-with-audio-after-check-${testName}.png`) }); await page2[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/06-breakout-page02-user-joined-with-audio-after-check-${testName}.png`) });
@ -58,7 +57,7 @@ class Join extends Create {
} }
await this.page3.logger('before pages check'); await this.page3.logger('before pages check');
const resp = await page2[2].evaluate(util.getTestElement, we.videoContainer); const resp = await page2[2].evaluate(checkElement, we.videoContainer);
if (process.env.GENERATE_EVIDENCES === 'true') { if (process.env.GENERATE_EVIDENCES === 'true') {
await page2[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/06-breakout-page02-user-joined-webcam-before-check-${testName}.png`) }); await page2[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/06-breakout-page02-user-joined-webcam-before-check-${testName}.png`) });
@ -80,7 +79,7 @@ class Join extends Create {
await page2[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/06-breakout-page02-user-joined-screenshare-after-check-${testName}.png`) }); await page2[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/06-breakout-page02-user-joined-screenshare-after-check-${testName}.png`) });
} }
await this.page3.logger('after pages check'); this.page2.logger('after pages check');
return resp === true; return resp === true;
} else { } else {
await this.page3.page.bringToFront(); await this.page3.page.bringToFront();
@ -88,7 +87,8 @@ class Join extends Create {
await this.page3.waitForSelector(e.chatButton, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(e.chatButton, ELEMENT_WAIT_TIME);
await this.page3.click(e.chatButton, true); await this.page3.click(e.chatButton, true);
await this.page3.click(e.breakoutRoomsItem, true); await this.page3.click(e.breakoutRoomsItem, true);
const resp = await this.page3.page.evaluate(async () => await document.querySelectorAll('span[class^="alreadyConnected--"]') !== null); const resp = await this.page3.page.evaluate(checkElement, e.alreadyConnected);
return resp === true; return resp === true;
} }
} catch (err) { } catch (err) {

View File

@ -1,5 +1,4 @@
const e = require('./elements'); const e = require('./elements');
const pe = require('../core/elements');
const { ELEMENT_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported) const { ELEMENT_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported)
async function createBreakoutRooms(page1, page2) { async function createBreakoutRooms(page1, page2) {
@ -13,14 +12,4 @@ async function createBreakoutRooms(page1, page2) {
await page2.click(e.modalConfirmButton, true); await page2.click(e.modalConfirmButton, true);
} }
async function getTestElement(element) {
return document.querySelectorAll(element)[0] !== null;
}
async function clickTestElement(element) {
await document.querySelectorAll(element)[0].click();
}
exports.getTestElement = getTestElement;
exports.createBreakoutRooms = createBreakoutRooms; exports.createBreakoutRooms = createBreakoutRooms;
exports.clickTestElement = clickTestElement;

View File

@ -3,7 +3,7 @@
const Page = require('../core/page'); const Page = require('../core/page');
const e = require('./elements'); const e = require('./elements');
const util = require('./util'); const util = require('./util');
const { chatPushAlerts } = require('../notifications/elements'); const { checkElementLengthEqualTo } = require('../core/util');
const { ELEMENT_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME } = require('../core/constants');
class Clear extends Page { class Clear extends Page {
@ -23,7 +23,7 @@ class Clear extends Page {
await this.screenshot(`${testName}`, `02-after-chat-message-send-[${this.meetingId}]`); await this.screenshot(`${testName}`, `02-after-chat-message-send-[${this.meetingId}]`);
const chat0 = await this.page.evaluate(() => document.querySelectorAll('p[data-test="chatClearMessageText"]').length === 0); const chat0 = await this.page.evaluate(checkElementLengthEqualTo, e.chatClearMessageText, 0);
// clear // clear
await this.click(e.chatOptions, true); await this.click(e.chatOptions, true);

View File

@ -1,8 +1,8 @@
// Test: Sending a chat message // Test: Sending a chat message
const Notifications = require('../notifications/notifications'); const Notifications = require('../notifications/notifications');
const Page = require('../core/page');
const e = require('./elements'); const e = require('./elements');
const { checkElementLengthEqualTo } = require('../core/util');
const { ELEMENT_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME } = require('../core/constants');
class Poll extends Notifications { class Poll extends Notifications {
@ -13,7 +13,7 @@ class Poll extends Notifications {
async test(testName) { async test(testName) {
try { try {
// 0 messages // 0 messages
const chat0 = await this.page3.page.evaluate(() => document.querySelectorAll('p[data-test="chatPollMessageText"]').length === 0); const chat0 = await this.page3.page.evaluate(checkElementLengthEqualTo, e.chatPollMessageText, 0);
await this.page3.screenshot(`${testName}`, `01-before-chat-message-send-[${this.page3.meetingId}]`); await this.page3.screenshot(`${testName}`, `01-before-chat-message-send-[${this.page3.meetingId}]`);
await this.publishPollResults(testName); await this.publishPollResults(testName);
@ -23,7 +23,7 @@ class Poll extends Notifications {
await this.page3.waitForSelector(e.chatPollMessageText, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(e.chatPollMessageText, ELEMENT_WAIT_TIME);
// 1 message // 1 message
const chat1 = await this.page3.page.evaluate(() => document.querySelectorAll('p[data-test="chatPollMessageText"]').length === 1); const chat1 = await this.page3.page.evaluate(checkElementLengthEqualTo, e.chatPollMessageText, 1);
return chat0 === chat1; return chat0 === chat1;
} catch (err) { } catch (err) {
await this.page3.logger(err); await this.page3.logger(err);

View File

@ -3,6 +3,7 @@
const Page = require('../core/page'); const Page = require('../core/page');
const e = require('./elements'); const e = require('./elements');
const util = require('./util'); const util = require('./util');
const { checkElementLengthEqualTo } = require('../core/util');
class Send extends Page { class Send extends Page {
constructor() { constructor() {
@ -14,7 +15,7 @@ class Send extends Page {
await util.openChat(this); await util.openChat(this);
// 0 messages // 0 messages
const chat0 = await this.page.evaluate((chatSelector) => document.querySelectorAll(chatSelector).length === 0, e.chatUserMessageText); const chat0 = await this.page.evaluate(checkElementLengthEqualTo, e.chatUserMessageText, 0);
await this.screenshot(`${testName}`, `01-before-chat-message-send-[${this.meetingId}]`); await this.screenshot(`${testName}`, `01-before-chat-message-send-[${this.meetingId}]`);
// send a message // send a message
@ -27,7 +28,7 @@ class Send extends Page {
await this.waitForSelector(e.chatUserMessageText); await this.waitForSelector(e.chatUserMessageText);
// 1 message // 1 message
const chat1 = await this.page.evaluate((chatSelector) => document.querySelectorAll(chatSelector).length === 1, e.chatUserMessageText); const chat1 = await this.page.evaluate(checkElementLengthEqualTo, e.chatUserMessageText, 1);
return chat0 === chat1; return chat0 === chat1;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);

View File

@ -1,5 +1,6 @@
const e = require('./elements'); const e = require('./elements');
const ule = require('../user/elements'); const ule = require('../user/elements');
const { clickElement } = require('../core/util');
const { ELEMENT_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME } = require('../core/constants');
async function openChat(test) { async function openChat(test) {
@ -20,12 +21,12 @@ async function sendPublicChatMessage(page1, page2) {
async function openPrivateChatMessage(page1, page2) { async function openPrivateChatMessage(page1, page2) {
// Open private Chat with the other User // Open private Chat with the other User
Object.values(arguments).forEach(async argument => await argument.waitForSelector(ule.userListItem, ELEMENT_WAIT_TIME)); Object.values(arguments).forEach(async argument => await argument.waitForSelector(ule.userListItem, ELEMENT_WAIT_TIME));
await page1.page.evaluate(clickOnTheOtherUser, ule.userListItem); await page1.page.evaluate(clickElement, ule.userListItem);
await page2.page.evaluate(clickOnTheOtherUser, ule.userListItem); await page2.page.evaluate(clickElement, ule.userListItem);
await page1.page.waitForSelector(e.activeChat, ELEMENT_WAIT_TIME); await page1.page.waitForSelector(e.activeChat, ELEMENT_WAIT_TIME);
await page1.page.evaluate(clickThePrivateChatButton, e.activeChat); await page1.page.evaluate(clickElement, e.activeChat);
await page2.page.waitForSelector(e.activeChat, ELEMENT_WAIT_TIME); await page2.page.waitForSelector(e.activeChat, ELEMENT_WAIT_TIME);
await page2.page.evaluate(clickThePrivateChatButton, e.activeChat); await page2.page.evaluate(clickElement, e.activeChat);
} }
async function sendPrivateChatMessage(page1, page2) { async function sendPrivateChatMessage(page1, page2) {
@ -41,14 +42,6 @@ async function sendPrivateChatMessage(page1, page2) {
await page2.page.screenshot(true); await page2.page.screenshot(true);
} }
async function clickOnTheOtherUser(element) {
await document.querySelectorAll(element)[0].click();
}
async function clickThePrivateChatButton(element) {
await document.querySelectorAll(element)[0].click();
}
async function checkForPublicMessageReception(page1, page2) { async function checkForPublicMessageReception(page1, page2) {
const publicChat1 = await page1.page.$$(`${e.chatUserMessage} ${e.chatMessageText}`); const publicChat1 = await page1.page.$$(`${e.chatUserMessage} ${e.chatMessageText}`);
const publicChat2 = await page2.page.$$(`${e.chatUserMessage} ${e.chatMessageText}`); const publicChat2 = await page2.page.$$(`${e.chatUserMessage} ${e.chatMessageText}`);

View File

@ -6,18 +6,18 @@ exports.echoYes = 'button[aria-label="Echo is audible"]';
exports.title = '._imports_ui_components_nav_bar__styles__presentationTitle'; exports.title = '._imports_ui_components_nav_bar__styles__presentationTitle';
exports.alerts = '.toastify-content'; exports.alerts = '.toastify-content';
exports.presenterClassName = 'presenter--'; exports.presenterClassName = 'presenter--';
exports.zoomIn = 'button[aria-label="Zoom in"]';
exports.pdfFileName = '100PagesFile'; exports.pdfFileName = '100PagesFile';
exports.isTalking = '[data-test="isTalking"]'; exports.isTalking = '[data-test="isTalking"]';
exports.wasTalking = '[data-test="wasTalking"]'; exports.wasTalking = '[data-test="wasTalking"]';
exports.joinAudio = 'button[data-test="joinAudio"]'; exports.joinAudio = 'button[data-test="joinAudio"]';
exports.leaveAudio = 'button[aria-label="Leave audio"]'; exports.leaveAudio = 'button[data-test="leaveAudio"]';
exports.disconnectAudio = 'li[data-test="disconnectAudio"]'; exports.disconnectAudio = 'li[data-test="disconnectAudio"]';
exports.actions = 'button[aria-label="Actions"]'; exports.actions = 'button[aria-label="Actions"]';
exports.options = 'button[aria-label="Options"]'; exports.options = 'button[aria-label="Options"]';
exports.userList = 'button[aria-label="Users and Messages Toggle"]'; exports.userList = 'button[aria-label="Users and Messages Toggle"]';
exports.joinAudio = 'button[aria-label="Join Audio"]';
exports.connectingStatus = 'div[class^="connecting--"]'; exports.connectingStatus = 'div[class^="connecting--"]';
exports.videoMenu = 'button[aria-label="Open video menu dropdown"]'; exports.videoMenu = 'button[aria-label="Open video menu dropdown"]';
exports.screenShare = 'button[aria-label="Share your screen"]'; exports.screenShare = 'button[aria-label="Share your screen"]';
@ -28,5 +28,5 @@ exports.logout = 'li[data-test="logout"]';
exports.meetingEndedModal = 'div[data-test="meetingEndedModal"]'; exports.meetingEndedModal = 'div[data-test="meetingEndedModal"]';
exports.rating = 'div[data-test="rating"]'; exports.rating = 'div[data-test="rating"]';
exports.whiteboard = 'svg[data-test="whiteboard"]'; exports.whiteboard = 'svg[data-test="whiteboard"]';
exports.pollMenuButton = 'button[data-test="pollMenuButton"]'; exports.pollMenuButton = 'div[data-test="pollMenuButton"]';
exports.unauthorized = 'h1[data-test="unauthorized"]'; exports.unauthorized = 'h1[data-test="unauthorized"]';

View File

@ -9,6 +9,7 @@ const PuppeteerVideoRecorder = require('puppeteer-video-recorder');
const helper = require('./helper'); const helper = require('./helper');
const params = require('../params'); const params = require('../params');
const { ELEMENT_WAIT_TIME } = require('./constants'); const { ELEMENT_WAIT_TIME } = require('./constants');
const { getElementLength } = require('./util');
const e = require('./elements'); const e = require('./elements');
const ue = require('../user/elements'); const ue = require('../user/elements');
const { NETWORK_PRESETS } = require('./profiles'); const { NETWORK_PRESETS } = require('./profiles');
@ -169,7 +170,7 @@ class Page {
} }
async returnElement(element) { async returnElement(element) {
return await document.querySelectorAll(element)[0]; return document.querySelectorAll(element)[0];
} }
async getUserAgent(test) { async getUserAgent(test) {
@ -410,7 +411,7 @@ class Page {
const users = collection.default._collection.find({}, {}, {}, {}, {}, { loggedOut: 'false' }).count(); const users = collection.default._collection.find({}, {}, {}, {}, {}, { loggedOut: 'false' }).count();
return users; return users;
}); });
const totalNumberOfUsersDom = await this.page.evaluate(async () => await document.querySelectorAll('[data-test^="userListItem"]').length); const totalNumberOfUsersDom = await this.page.evaluate(getElementLength, '[data-test^="userListItem"]');
await this.logger({ totalNumberOfUsersDom, totalNumberOfUsersMongo }); await this.logger({ totalNumberOfUsersDom, totalNumberOfUsersMongo });
const metric = await this.page.metrics(); const metric = await this.page.metrics();
pageMetricsObj.totalNumberOfUsersMongoObj = totalNumberOfUsersMongo; pageMetricsObj.totalNumberOfUsersMongoObj = totalNumberOfUsersMongo;

View File

@ -0,0 +1,44 @@
// Common
function checkElement(element, index = 0) {
return document.querySelectorAll(element)[index] !== undefined;
}
function clickElement(element, index = 0) {
document.querySelectorAll(element)[index].click();
}
// Text
function checkElementText(element, param, index = 0) {
return document.querySelectorAll(element)[index].innerText === param;
}
function checkElementTextIncludes(element, param, index = 0) {
return document.querySelectorAll(element)[index].innerText.includes(param);
}
function getElementText(element, index = 0) {
return document.querySelectorAll(element)[index].innerText;
}
// Length
function checkElementLengthEqualTo(element, param) {
return document.querySelectorAll(element).length === param;
}
function checkElementLengthDifferentTo(element, param) {
return document.querySelectorAll(element).length !== param;
}
// use this for other operations
function getElementLength(element) {
return document.querySelectorAll(element).length;
}
exports.checkElement = checkElement;
exports.clickElement = clickElement;
exports.checkElementText = checkElementText;
exports.checkElementTextIncludes = checkElementTextIncludes;
exports.getElementText = getElementText;
exports.checkElementLengthEqualTo = checkElementLengthEqualTo;
exports.checkElementLengthDifferentTo = checkElementLengthDifferentTo;
exports.getElementLength = getElementLength;

View File

@ -1,4 +1,3 @@
const path = require('path');
const Page = require('../core/page'); const Page = require('../core/page');
const params = require('../params'); const params = require('../params');
const ne = require('../notifications/elements'); const ne = require('../notifications/elements');
@ -6,9 +5,11 @@ const pe = require('../core/elements');
const cpe = require('./elements'); const cpe = require('./elements');
const we = require('../webcam/elements'); const we = require('../webcam/elements');
const ae = require('../audio/elements'); const ae = require('../audio/elements');
const ce = require('../chat/elements');
const util = require('./util'); const util = require('./util');
const c = require('./constants'); const c = require('./constants');
const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants'); // core constants (Timeouts vars imported) const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants'); // core constants (Timeouts vars imported)
const { checkElementLengthEqualTo, checkElementLengthDifferentTo } = require('../core/util');
class CustomParameters { class CustomParameters {
constructor() { constructor() {
@ -30,12 +31,13 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.waitForSelector('div[data-test="chatMessages"]', ELEMENT_WAIT_TIME); await this.page1.waitForSelector('div[data-test="chatMessages"]', ELEMENT_WAIT_TIME);
if (await this.page1.page.evaluate(util.getTestElement, cpe.audioModal) === false) { await this.page1.waitForSelector(ce.chatMessages, ELEMENT_WAIT_TIME);
const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.audioModal, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `02-fail-${testName}`); await this.page1.screenshot(`${testName}`, `02-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.audioModal) === true;
await this.page1.screenshot(`${testName}`, `02-success-${testName}`); await this.page1.screenshot(`${testName}`, `02-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -46,26 +48,22 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.waitForSelector(pe.audioDialog, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.audioDialog, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `01-page1-${testName}`); await this.page1.screenshot(`${testName}`, `01-page1-${testName}`);
const audioOptionsButton = await this.page1.page.evaluate(async () => { const audioOptionsButton = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.audioOptionsButtons, 1);
const countFoundElements = await document.querySelectorAll('[class^="audioOptions"] > button').length; if (!audioOptionsButton) {
return countFoundElements;
});
if (audioOptionsButton === 1) {
await this.page1.screenshot(`${testName}`, `04-success-${testName}`);
this.page1.logger(testName, ' passed');
return true;
} if (audioOptionsButton !== 1) {
await this.page1.screenshot(`${testName}`, `04-fail-${testName}`); await this.page1.screenshot(`${testName}`, `04-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
await this.page1.screenshot(`${testName}`, `04-success-${testName}`);
this.page1.logger(testName, ' passed');
return true;
} }
async forceListenOnly(testName, args, meetingId, customParameter) { async forceListenOnly(testName, args, meetingId, customParameter) {
await this.page2.init(args, meetingId, { ...params, fullName: 'Attendee', moderatorPW: '' }, customParameter, testName); await this.page2.init(args, meetingId, { ...params, fullName: 'Attendee', moderatorPW: '' }, customParameter, testName);
await this.page2.startRecording(testName); await this.page2.startRecording(testName);
await this.page2.screenshot(`${testName}`, `01-${testName}`); await this.page2.screenshot(`${testName}`, `01-${testName}`);
if (await this.page2.page.$('[data-test="audioModalHeader"]')) { if (await this.page2.page.$(cpe.audioModalHeader)) {
await this.page2.screenshot(`${testName}`, `02-fail-${testName}`); await this.page2.screenshot(`${testName}`, `02-fail-${testName}`);
this.page2.logger(testName, ' failed'); this.page2.logger(testName, ' failed');
return false; return false;
@ -87,32 +85,30 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
await this.page1.waitForElementHandleToBeRemoved(ae.connectingStatus, ELEMENT_WAIT_LONGER_TIME); await this.page1.waitForElementHandleToBeRemoved(ae.connectingStatus, ELEMENT_WAIT_LONGER_TIME);
await this.page1.screenshot(`${testName}`, `03-${testName}`); await this.page1.screenshot(`${testName}`, `03-${testName}`);
if (await this.page1.page.evaluate(util.countTestElements, cpe.echoTestYesButton) === true) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.echoTestYesButton, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `04-fail-${testName}`); await this.page1.screenshot(`${testName}`, `04-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.echoTestYesButton) === false;
await this.page1.screenshot(`${testName}`, `04-success-${testName}`); await this.page1.screenshot(`${testName}`, `04-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
} }
async skipCheckOnFirstJoin(testName, args, meetingId, customParameter) { async skipCheckOnFirstJoin(testName, args, meetingId, customParameter) {
const parsedSettings = await this.page1.getSettingsYaml();
const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout);
await this.page1.init(args, meetingId, { ...params, fullName: 'Moderator' }, customParameter, testName); await this.page1.init(args, meetingId, { ...params, fullName: 'Moderator' }, customParameter, testName);
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.click(ae.microphone, true); await this.page1.click(ae.microphone, true);
const firstCheck = await this.page1.page.evaluate(util.getTestElement, ae.connecting) === false; const firstCheck = await this.page1.page.evaluate(checkElementLengthDifferentTo, ae.connecting, 0);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
await this.page1.leaveAudio(); await this.page1.leaveAudio();
await this.page1.screenshot(`${testName}`, `03-${testName}`); await this.page1.screenshot(`${testName}`, `03-${testName}`);
await this.page1.waitForSelector(pe.joinAudio, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.joinAudio, ELEMENT_WAIT_TIME);
await this.page1.click(pe.joinAudio, true); await this.page1.click(pe.joinAudio, true);
await this.page1.click(ae.microphone, true); await this.page1.click(ae.microphone, true);
const secondCheck = await this.page1.page.evaluate(util.getTestElement, ae.connectingToEchoTest) === false; const secondCheck = await this.page1.page.evaluate(checkElementLengthDifferentTo, ae.connectingToEchoTest, 0);
if (firstCheck !== secondCheck) { if (firstCheck !== secondCheck) {
await this.page1.screenshot(`${testName}`, `04-fail-${testName}`); await this.page1.screenshot(`${testName}`, `04-fail-${testName}`);
@ -130,12 +126,12 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.waitForSelector(pe.whiteboard, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.whiteboard, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
if (await !(await this.page1.page.title()).includes(c.docTitle)) { const resp = await (await this.page1.page.title()).includes(c.docTitle);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await (await this.page1.page.title()).includes(c.docTitle);
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -152,12 +148,12 @@ class CustomParameters {
await this.page1.waitForSelector(cpe.meetingEndedModal, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.meetingEndedModal, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `04-${testName}`); await this.page1.screenshot(`${testName}`, `04-${testName}`);
this.page1.logger('audio modal closed'); this.page1.logger('audio modal closed');
if (await this.page1.page.evaluate(util.countTestElements, cpe.rating) === false) { const resp = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.rating, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `05-fail-${testName}`); await this.page1.screenshot(`${testName}`, `05-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.rating) === true;
await this.page1.screenshot(`${testName}`, `05-success-${testName}`); await this.page1.screenshot(`${testName}`, `05-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -171,12 +167,12 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
this.page1.logger('audio modal closed'); this.page1.logger('audio modal closed');
await this.page1.waitForSelector(cpe.userListContent, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.userListContent, ELEMENT_WAIT_TIME);
if (await this.page1.page.evaluate(util.countTestElements, cpe.brandingAreaLogo) === false) { const resp = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.brandingAreaLogo, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.brandingAreaLogo) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -192,12 +188,12 @@ class CustomParameters {
await this.page1.waitForSelector(pe.options, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.options, ELEMENT_WAIT_TIME);
await this.page1.page.keyboard.down('Alt'); await this.page1.page.keyboard.down('Alt');
await this.page1.page.keyboard.press('O'); await this.page1.page.keyboard.press('O');
if (await this.page1.page.evaluate(util.getTestElement, cpe.verticalListOptions) === false) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.verticalListOptions, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.verticalListOptions) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -208,12 +204,12 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
if (await this.page1.page.evaluate(util.getTestElement, cpe.screenShareButton) === false) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.screenShareButton, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `02-fail-${testName}`); await this.page1.screenshot(`${testName}`, `02-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.screenShareButton) === true;
await this.page1.screenshot(`${testName}`, `02-success-${testName}`); await this.page1.screenshot(`${testName}`, `02-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -224,12 +220,12 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
if (await this.page1.page.evaluate(util.getTestElement, cpe.shareWebcamButton) === false) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.shareWebcamButton, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `02-fail-${testName}`); await this.page1.screenshot(`${testName}`, `02-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.shareWebcamButton) === true;
await this.page1.screenshot(`${testName}`, `02-success-${testName}`); await this.page1.screenshot(`${testName}`, `02-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -241,12 +237,12 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
if (await this.page1.page.evaluate(util.getTestElement, cpe.webcamSettingsModal) === true) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.webcamSettingsModal, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.webcamSettingsModal) === false;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -269,12 +265,14 @@ class CustomParameters {
await this.page2.waitForSelector(cpe.tools, ELEMENT_WAIT_TIME); await this.page2.waitForSelector(cpe.tools, ELEMENT_WAIT_TIME);
await this.page2.click(cpe.tools, true); await this.page2.click(cpe.tools, true);
await this.page2.screenshot(`${testName}`, `04-page2-${testName}`); await this.page2.screenshot(`${testName}`, `04-page2-${testName}`);
if (await this.page2.page.evaluate(async () => await document.querySelectorAll('[aria-label="Tools"]')[0].parentElement.childElementCount === 2)) { const resp = await this.page2.page.evaluate((toolsElement) => {
return document.querySelectorAll(toolsElement)[0].parentElement.childElementCount === 1;
}, cpe.tools);
if (!resp) {
await this.page2.screenshot(`${testName}`, `05-page2-fail-${testName}`); await this.page2.screenshot(`${testName}`, `05-page2-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page2.page.evaluate(async () => await document.querySelectorAll('[aria-label="Tools"]')[0].parentElement.childElementCount === 1);
await this.page2.screenshot(`${testName}`, `05-page2-success-${testName}`); await this.page2.screenshot(`${testName}`, `05-page2-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -289,12 +287,14 @@ class CustomParameters {
await this.page1.waitForSelector(cpe.tools, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.tools, ELEMENT_WAIT_TIME);
await this.page1.click(cpe.tools, true); await this.page1.click(cpe.tools, true);
await this.page1.screenshot(`${testName}`, `03-${testName}`); await this.page1.screenshot(`${testName}`, `03-${testName}`);
if (await this.page1.page.evaluate(async () => await document.querySelectorAll('[aria-label="Tools"]')[0].parentElement.querySelector('[class^="toolbarList--"]').childElementCount === 7)) { const resp = await this.page1.page.evaluate((toolsElement, toolbarListSelector) => {
return document.querySelectorAll(toolsElement)[0].parentElement.querySelector(toolbarListSelector).childElementCount === 2;
}, cpe.tools, cpe.toolbarListClass);
if (!resp) {
await this.page1.screenshot(`${testName}`, `04-fail-${testName}`); await this.page1.screenshot(`${testName}`, `04-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(async () => await document.querySelectorAll('[aria-label="Tools"]')[0].parentElement.querySelector('[class^="toolbarList--"]').childElementCount === 2);
await this.page1.screenshot(`${testName}`, `04-success-${testName}`); await this.page1.screenshot(`${testName}`, `04-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -317,12 +317,14 @@ class CustomParameters {
await this.page2.waitForSelector(cpe.tools, ELEMENT_WAIT_TIME); await this.page2.waitForSelector(cpe.tools, ELEMENT_WAIT_TIME);
await this.page2.click(cpe.tools, true); await this.page2.click(cpe.tools, true);
await this.page2.screenshot(`${testName}`, `04-page2-${testName}`); await this.page2.screenshot(`${testName}`, `04-page2-${testName}`);
if (await this.page2.page.evaluate(async () => await document.querySelectorAll('[aria-label="Tools"]')[0].parentElement.querySelector('[class^="toolbarList--"]').childElementCount === 7)) { const resp = await this.page2.page.evaluate((toolsElement, toolbarListSelector) => {
return document.querySelectorAll(toolsElement)[0].parentElement.querySelector(toolbarListSelector).childElementCount === 2;
}, cpe.tools, cpe.toolbarListClass);
if (!resp) {
await this.page2.screenshot(`${testName}`, `05-page2-fail-${testName}`); await this.page2.screenshot(`${testName}`, `05-page2-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page2.page.evaluate(async () => await document.querySelectorAll('[aria-label="Tools"]')[0].parentElement.querySelector('[class^="toolbarList--"]').childElementCount === 2);
await this.page2.screenshot(`${testName}`, `05-page2-success-${testName}`); await this.page2.screenshot(`${testName}`, `05-page2-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -335,17 +337,16 @@ class CustomParameters {
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.whiteboard, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.whiteboard, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
const isHidden = await this.page1.page.$eval('[class="presentationTitle--1LT79g"]', elem => elem.offsetHeight == 0); const isHidden = await this.page1.page.$eval(cpe.presentationTitle, elem => elem.offsetHeight == 0);
if (isHidden === false) { if (isHidden !== true) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} if (isHidden === true) {
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
const resp = isHidden;
this.page1.logger(testName, ' passed');
return resp === true;
} }
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
const resp = isHidden;
this.page1.logger(testName, ' passed');
return resp === true;
} }
async customStyleUrl(testName, args, meetingId, customParameter) { async customStyleUrl(testName, args, meetingId, customParameter) {
@ -355,17 +356,16 @@ class CustomParameters {
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.whiteboard, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.whiteboard, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
const isHidden = await this.page1.page.$eval('[class="presentationTitle--1LT79g"]', elem => elem.offsetHeight == 0); const isHidden = await this.page1.page.$eval(cpe.presentationTitle, elem => elem.offsetHeight == 0);
if (isHidden === false) { if (isHidden !== true) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} if (isHidden === true) {
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
const resp = isHidden;
this.page1.logger(testName, ' passed');
return resp === true;
} }
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
const resp = isHidden;
this.page1.logger(testName, ' passed');
return resp === true;
} }
async autoSwapLayout(testName, args, meetingId, customParameter) { async autoSwapLayout(testName, args, meetingId, customParameter) {
@ -373,19 +373,18 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.container, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.actions, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
const isNotHidden = await this.page1.page.$eval(cpe.restorePresentation, elem => elem.offsetHeight !== 0); const isNotHidden = await this.page1.page.$eval(cpe.restorePresentation, elem => elem.offsetHeight !== 0);
if (isNotHidden === false) { if (isNotHidden !== true) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} if (isNotHidden === true) {
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
const resp = isNotHidden;
this.page1.logger(testName, ' passed');
return resp === true;
} }
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
const resp = isNotHidden;
this.page1.logger(testName, ' passed');
return resp === true;
} }
async hidePresentation(testName, args, meetingId, customParameter) { async hidePresentation(testName, args, meetingId, customParameter) {
@ -395,12 +394,12 @@ class CustomParameters {
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.actions, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.actions, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
if (await this.page1.page.evaluate(util.countTestElements, cpe.defaultContent) === false) { const resp = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.defaultContent, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.defaultContent) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -413,12 +412,12 @@ class CustomParameters {
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.actions, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.actions, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
if (await this.page1.page.evaluate(util.countTestElements, cpe.notificationBar) === false) { const resp = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.notificationBar, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.notificationBar) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -436,11 +435,10 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} if (notificationBarColor === colorToRGB) {
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed');
return true;
} }
await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed');
return true;
} }
async hideAndSwapPresentation(testName, args, meetingId, customParameter) { async hideAndSwapPresentation(testName, args, meetingId, customParameter) {
@ -448,13 +446,13 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.container, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.actions, ELEMENT_WAIT_TIME);
if (await this.page1.page.evaluate(util.countTestElements, cpe.restorePresentation) === false && await this.page1.page.evaluate(util.countTestElements, cpe.defaultContent) === false) { const resp = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.restorePresentation, 0) && await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.defaultContent, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.restorePresentation) === true && await this.page1.page.evaluate(util.countTestElements, cpe.defaultContent) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -465,13 +463,13 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.waitForSelector(cpe.container, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(pe.actions, ELEMENT_WAIT_TIME);
if (await this.page1.page.evaluate(util.countTestElements, cpe.chat) === true) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.chat, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.countTestElements, cpe.chat) === false;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -488,7 +486,6 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `02-page1-${testName}`); await this.page1.screenshot(`${testName}`, `02-page1-${testName}`);
await this.page2.closeAudioModal(); await this.page2.closeAudioModal();
await this.page2.screenshot(`${testName}`, `02-page2-${testName}`); await this.page2.screenshot(`${testName}`, `02-page2-${testName}`);
await this.page1.waitForSelector(cpe.container, ELEMENT_WAIT_TIME);
await this.page2.waitForSelector(cpe.hidePresentation, ELEMENT_WAIT_TIME); await this.page2.waitForSelector(cpe.hidePresentation, ELEMENT_WAIT_TIME);
await this.page2.click(cpe.hidePresentation, true); await this.page2.click(cpe.hidePresentation, true);
await this.page2.screenshot(`${testName}`, `03-page2-${testName}`); await this.page2.screenshot(`${testName}`, `03-page2-${testName}`);
@ -510,13 +507,15 @@ class CustomParameters {
const annotationCase = await util.annotation(this.page1); const annotationCase = await util.annotation(this.page1);
await this.page1.screenshot(`${testName}`, `06-page1-${testName}`); await this.page1.screenshot(`${testName}`, `06-page1-${testName}`);
await this.page2.screenshot(`${testName}`, `07-page2-${testName}`); await this.page2.screenshot(`${testName}`, `07-page2-${testName}`);
if (zoomInCase === true && zoomOutCase === true && pollCase === true && previousSlideCase === true && nextSlideCase === true && annotationCase === true
&& await this.page2.page.evaluate(util.countTestElements, cpe.restorePresentation) === true) { const test = await this.page2.page.evaluate(checkElementLengthDifferentTo, cpe.restorePresentation, 0);
const resp = (zoomInCase && zoomOutCase && pollCase && previousSlideCase && nextSlideCase && annotationCase && test);
if (resp) {
await this.page2.screenshot(`${testName}`, `08-page2-fail-${testName}`); await this.page2.screenshot(`${testName}`, `08-page2-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
await this.page2.page.evaluate(util.countTestElements, cpe.restorePresentation) === false; await this.page2.page.evaluate(checkElementLengthEqualTo, cpe.restorePresentation, 0);
await this.page2.screenshot(`${testName}`, `08-page2-success-${testName}`); await this.page2.screenshot(`${testName}`, `08-page2-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return true; return true;
@ -534,17 +533,17 @@ class CustomParameters {
await this.page2.screenshot(`${testName}`, `02-page2-${testName}`); await this.page2.screenshot(`${testName}`, `02-page2-${testName}`);
await this.page2.click(cpe.hidePresentation, true); await this.page2.click(cpe.hidePresentation, true);
await this.page2.screenshot(`${testName}`, `03-page2-${testName}`); await this.page2.screenshot(`${testName}`, `03-page2-${testName}`);
const pollCase = await util.poll(this.page1, this.page2); const pollCase = await util.poll(this.page1, this.page2) === true;
await this.page2.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME); await this.page2.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME);
await this.page1.screenshot(`${testName}`, `03-page1-${testName}`); await this.page1.screenshot(`${testName}`, `03-page1-${testName}`);
await this.page2.screenshot(`${testName}`, `04-page2-${testName}`); await this.page2.screenshot(`${testName}`, `04-page2-${testName}`);
if (pollCase === true
&& await this.page2.page.evaluate(util.countTestElements, cpe.restorePresentation) === true) { const test = await this.page2.page.evaluate(checkElementLengthDifferentTo, cpe.restorePresentation, 0);
if (pollCase && test) {
await this.page2.screenshot(`${testName}`, `05-page2-fail-${testName}`); await this.page2.screenshot(`${testName}`, `05-page2-fail-${testName}`);
await this.page1.logger(testName, ' failed'); await this.page1.logger(testName, ' failed');
return false; return false;
} }
await this.page2.page.evaluate(util.countTestElements, cpe.restorePresentation) === false;
await this.page2.screenshot(`${testName}`, `05-page2-success-${testName}`); await this.page2.screenshot(`${testName}`, `05-page2-success-${testName}`);
await this.page1.logger(testName, ' passed'); await this.page1.logger(testName, ' passed');
return true; return true;
@ -555,12 +554,12 @@ class CustomParameters {
await this.page1.startRecording(testName); await this.page1.startRecording(testName);
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.screenshot(`${testName}`, `01-${testName}`); await this.page1.screenshot(`${testName}`, `01-${testName}`);
if (await this.page1.page.evaluate(util.getTestElement, cpe.recordingIndicator) === false) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.recordingIndicator, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `02-fail-${testName}`); await this.page1.screenshot(`${testName}`, `02-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.recordingIndicator) === true;
await this.page1.screenshot(`${testName}`, `02-success-${testName}`); await this.page1.screenshot(`${testName}`, `02-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -574,12 +573,12 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
await this.page1.waitForSelector(cpe.shareWebcamButton, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.shareWebcamButton, ELEMENT_WAIT_TIME);
await this.page1.click(cpe.shareWebcamButton, true); await this.page1.click(cpe.shareWebcamButton, true);
if (await this.page1.page.evaluate(util.getTestElement, cpe.webcamSettingsModal) === false) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.webcamSettingsModal, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.webcamSettingsModal) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -593,7 +592,7 @@ class CustomParameters {
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
await this.page1.waitForSelector(we.joinVideo, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(we.joinVideo, ELEMENT_WAIT_TIME);
await this.page1.click(we.joinVideo, true); await this.page1.click(we.joinVideo, true);
const firstCheck = await this.page1.page.evaluate(util.getTestElement, cpe.webcamSettingsModal) === true; const firstCheck = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.webcamSettingsModal, 0);
await this.page1.waitForSelector(we.leaveVideo, VIDEO_LOADING_WAIT_TIME); await this.page1.waitForSelector(we.leaveVideo, VIDEO_LOADING_WAIT_TIME);
await this.page1.click(we.leaveVideo, true); await this.page1.click(we.leaveVideo, true);
await this.page1.waitForElementHandleToBeRemoved(we.webcamVideo), ELEMENT_WAIT_LONGER_TIME; await this.page1.waitForElementHandleToBeRemoved(we.webcamVideo), ELEMENT_WAIT_LONGER_TIME;
@ -605,7 +604,7 @@ class CustomParameters {
const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout); const videoPreviewTimeout = parseInt(parsedSettings.public.kurento.gUMTimeout);
await this.page1.waitForSelector(cpe.webcamVideoPreview, videoPreviewTimeout); await this.page1.waitForSelector(cpe.webcamVideoPreview, videoPreviewTimeout);
await this.page1.waitForSelector(cpe.startSharingWebcamButton, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.startSharingWebcamButton, ELEMENT_WAIT_TIME);
const secondCheck = await this.page1.page.evaluate(util.getTestElement, cpe.webcamSettingsModal) === false; const secondCheck = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.webcamSettingsModal, 0);
await this.page1.click(cpe.startSharingWebcamButton, true); await this.page1.click(cpe.startSharingWebcamButton, true);
await this.page1.waitForSelector(we.webcamConnecting, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(we.webcamConnecting, ELEMENT_WAIT_TIME);
@ -630,12 +629,12 @@ class CustomParameters {
await this.page1.waitForSelector(cpe.webcamMirroredVideoPreview, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.webcamMirroredVideoPreview, ELEMENT_WAIT_TIME);
await this.page1.waitForSelector(cpe.startSharingWebcamButton, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.startSharingWebcamButton, ELEMENT_WAIT_TIME);
await this.page1.click(cpe.startSharingWebcamButton, true); await this.page1.click(cpe.startSharingWebcamButton, true);
if (await this.page1.page.evaluate(util.getTestElement, cpe.webcamMirroredVideoContainer) === true) { const resp = await this.page1.page.evaluate(checkElementLengthDifferentTo, cpe.webcamMirroredVideoContainer, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.webcamMirroredVideoContainer) === false;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;
@ -648,12 +647,12 @@ class CustomParameters {
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page1.screenshot(`${testName}`, `02-${testName}`); await this.page1.screenshot(`${testName}`, `02-${testName}`);
await this.page1.waitForSelector(cpe.whiteboard, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(cpe.whiteboard, ELEMENT_WAIT_TIME);
if (await this.page1.page.evaluate(util.getTestElement, cpe.userslistContainer) === false) { const resp = await this.page1.page.evaluate(checkElementLengthEqualTo, cpe.userslistContainer, 0);
if (!resp) {
await this.page1.screenshot(`${testName}`, `03-fail-${testName}`); await this.page1.screenshot(`${testName}`, `03-fail-${testName}`);
this.page1.logger(testName, ' failed'); this.page1.logger(testName, ' failed');
return false; return false;
} }
const resp = await this.page1.page.evaluate(util.getTestElement, cpe.userslistContainer) === true;
await this.page1.screenshot(`${testName}`, `03-success-${testName}`); await this.page1.screenshot(`${testName}`, `03-success-${testName}`);
this.page1.logger(testName, ' passed'); this.page1.logger(testName, ' passed');
return resp === true; return resp === true;

View File

@ -1,5 +1,7 @@
exports.audioModal = 'div[aria-label="Join audio modal"]'; exports.audioModal = 'div[aria-label="Join audio modal"]';
exports.audioOverlay = 'div[class^="ReactModal__Overlay"]'; exports.audioOverlay = 'div[class^="ReactModal__Overlay"]';
exports.audioModalHeader = '[data-test="audioModalHeader"]';
exports.audioOptionsButtons = '[class^="audioOptions"] > button';
exports.whiteboard = 'svg[data-test="whiteboard"]'; exports.whiteboard = 'svg[data-test="whiteboard"]';
exports.echoTestYesButton = 'button[aria-label="Echo is audible"]'; exports.echoTestYesButton = 'button[aria-label="Echo is audible"]';
exports.echoTestNoButton = 'button[aria-label="Echo is inaudible"]'; exports.echoTestNoButton = 'button[aria-label="Echo is inaudible"]';
@ -17,11 +19,12 @@ exports.stopWebcamButton = 'button[data-test="leaveVideo"]';
exports.webcamSettingsModal = 'div[aria-label="Webcam settings"]'; exports.webcamSettingsModal = 'div[aria-label="Webcam settings"]';
exports.startWebcamSharingConfirm = 'button[aria-label="Start sharing"]'; exports.startWebcamSharingConfirm = 'button[aria-label="Start sharing"]';
exports.multiUsersWhiteboard = 'button[aria-label="Turn multi-user whiteboard on"]'; exports.multiUsersWhiteboard = 'button[aria-label="Turn multi-user whiteboard on"]';
exports.toolbarListClass = '[class^="toolbarList--"]';
exports.tools = 'button[aria-label="Tools"]'; exports.tools = 'button[aria-label="Tools"]';
exports.actions = 'button[aria-label="Actions"]'; exports.actions = 'button[aria-label="Actions"]';
exports.hidePresentation = 'button[data-test="hidePresentationButton"]'; exports.hidePresentation = 'button[data-test="hidePresentationButton"]';
exports.restorePresentation = 'button[data-test="restorePresentationButton"]'; exports.restorePresentation = 'button[data-test="restorePresentationButton"]';
exports.container = 'div[id="container"]'; exports.presentationTitle = '[class^="presentationTitle--"]';
exports.defaultContent = 'div[class^="defaultContent--"]'; exports.defaultContent = 'div[class^="defaultContent--"]';
exports.notificationBar = 'div[class^="notificationsBar--"]'; exports.notificationBar = 'div[class^="notificationsBar--"]';
exports.chat = 'section[aria-label="Chat"]'; exports.chat = 'section[aria-label="Chat"]';

View File

@ -1,28 +1,31 @@
const path = require('path'); const path = require('path');
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const ne = require('../notifications/elements'); const ne = require('../notifications/elements');
const pe = require('../presentation/elements'); const pe = require('../presentation/elements');
const ce = require('../customparameters/elements'); const ce = require('../customparameters/elements');
const we = require('../whiteboard/elements'); const we = require('../whiteboard/elements');
const poe = require('../polling/elemens'); const poe = require('../polling/elemens');
const e = require('../core/elements'); const e = require('../core/elements');
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const { checkElementLengthEqualTo, checkElementLengthDifferentTo, checkElementText } = require('../core/util');
async function autoJoinTest(test) { async function autoJoinTest(test) {
const resp = await test.page.evaluate(async () => { try {
const rep = await document.querySelectorAll('div[aria-label="Join audio modal"]').length === 0; const resp = await test.page.evaluate(checkElementLengthEqualTo, e.audioDialog, 0);
return rep !== false; return resp === true;
}); } catch (err) {
return resp; console.log(err);
return false;
}
} }
async function listenOnlyMode(test) { async function listenOnlyMode(test) {
// maybe not used
try { try {
const resp = await test.page.evaluate(async () => { const resp = await test.page.evaluate(async (connectionSelector, echoYes) => {
await document.querySelectorAll('div[class^="connecting--"]')[0]; await document.querySelectorAll(connectionSelector)[0];
const audibleButton = await document.querySelectorAll('button[aria-label="Echo is audible"]').length !== 0; return document.querySelectorAll(echoYes).length !== 0;
return audibleButton !== false; }, e.connectingStatus, e.echoYes);
}); return resp === true;
return resp;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -30,40 +33,31 @@ async function listenOnlyMode(test) {
async function forceListenOnly(test) { async function forceListenOnly(test) {
try { try {
const resp = await test.page.evaluate(async () => { const checkEchoYes = await test.page.evaluate(checkElementLengthEqualTo, e.echoYes, 0);
await document.querySelectorAll('div[class^="connecting--"]')[0]; if (!checkEchoYes) return false;
if (await document.querySelectorAll('button[aria-label="Echo is audible"]').length > 0) { const resp = await test.page.evaluate(checkElementText, ce.toastContainer, 'You have joined the audio conference');
return false;
} return resp === true;
const audibleNotification = await document.querySelectorAll('div[class^="toastContainer--"]')[0].innerText === 'You have joined the audio conference';
return audibleNotification !== false;
});
return resp;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
return false
} }
} }
async function skipCheck(test) { async function skipCheck(test) {
// maybe not used
try { try {
await test.waitForSelector(ce.toastContainer, ELEMENT_WAIT_TIME); await test.waitForSelector(ce.toastContainer, ELEMENT_WAIT_TIME);
const resp1 = await test.page.evaluate(async () => await document.querySelectorAll('div[class^="toastContainer--"]').length !== 0); const resp1 = await test.page.evaluate(checkElementLengthDifferentTo, e.toastContainer, 0);
await test.waitForSelector(ce.muteBtn, ELEMENT_WAIT_TIME); await test.waitForSelector(ce.muteBtn, ELEMENT_WAIT_TIME);
const resp2 = await test.page.evaluate(async () => await document.querySelectorAll('button[aria-label="Mute"]').length !== 0); const resp2 = await test.page.evaluate(checkElementLengthDifferentTo, ce.muteBtn, 0);
return resp1 === true && resp2 === true; return resp1 === true && resp2 === true;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
return false;
} }
} }
async function countTestElements(element) {
return document.querySelectorAll(element).length !== 0;
}
async function getTestElement(element) {
return document.querySelectorAll(element).length === 0;
}
function hexToRgb(hex) { function hexToRgb(hex) {
const bigint = parseInt(hex, 16); const bigint = parseInt(hex, 16);
const r = (bigint >> 16) & 255; const r = (bigint >> 16) & 255;
@ -74,11 +68,11 @@ function hexToRgb(hex) {
async function zoomIn(test) { async function zoomIn(test) {
try { try {
await test.page.evaluate(() => { await test.page.evaluate((zoomIn) => {
setInterval(() => { setInterval(() => {
document.querySelector('button[aria-label="Zoom in"]').scrollBy(0, 10); document.querySelector(zoomIn).scrollBy(0, 10);
}, 100); }, 100);
}); }, e.zoomIn);
return true; return true;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
@ -88,11 +82,12 @@ async function zoomIn(test) {
async function zoomOut(test) { async function zoomOut(test) {
try { try {
await test.page.evaluate(() => { await test.page.evaluate((zoomIn) => {
setInterval(() => { setInterval(() => {
document.querySelector('button[aria-label="Zoom in"]').scrollBy(10, 0); document.querySelector(zoomIn).scrollBy(10, 0);
}, 100); }, 100);
}); return true; }, e.zoomIn);
return true;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
return false; return false;
@ -102,7 +97,7 @@ async function zoomOut(test) {
async function poll(page1, page2) { async function poll(page1, page2) {
try { try {
await page1.page.waitForSelector(ce.whiteboard, { visible: true, timeout: ELEMENT_WAIT_LONGER_TIME }); await page1.page.waitForSelector(ce.whiteboard, { visible: true, timeout: ELEMENT_WAIT_LONGER_TIME });
await page1.page.evaluate(async () => await document.querySelectorAll('button[aria-label="Actions"]')[0].click()); await page1.click(e.actions);
await page1.waitForSelector(ne.polling, ELEMENT_WAIT_TIME); await page1.waitForSelector(ne.polling, ELEMENT_WAIT_TIME);
await page1.click(ne.polling, true); await page1.click(ne.polling, true);
await page1.waitForSelector(ne.pollYesNoAbstentionBtn, ELEMENT_WAIT_TIME); await page1.waitForSelector(ne.pollYesNoAbstentionBtn, ELEMENT_WAIT_TIME);
@ -149,8 +144,10 @@ async function annotation(test) {
await test.waitForSelector(we.pencil, ELEMENT_WAIT_TIME); await test.waitForSelector(we.pencil, ELEMENT_WAIT_TIME);
await test.click(we.pencil, true); await test.click(we.pencil, true);
await test.click(ce.whiteboard, true); await test.click(ce.whiteboard, true);
const annoted = await test.page.evaluate(async () => await document.querySelectorAll('[data-test="whiteboard"] > g > g')[1].innerHTML !== ''); const annoted = await test.page.evaluate((whiteboard) => {
return annoted; return document.querySelectorAll(`${whiteboard} > g > g`)[1].innerHTML !== '';
}, e.whiteboard);
return annoted === true;
} }
async function presetationUpload(test) { async function presetationUpload(test) {
@ -159,7 +156,7 @@ async function presetationUpload(test) {
await test.click(ce.actions, true); await test.click(ce.actions, true);
await test.waitForSelector(pe.uploadPresentation, ELEMENT_WAIT_TIME); await test.waitForSelector(pe.uploadPresentation, ELEMENT_WAIT_TIME);
await test.click(pe.uploadPresentation, true); await test.click(pe.uploadPresentation, true);
const elementHandle = await test.page.$('input[type=file]'); const elementHandle = await test.page.$(pe.fileUpload);
await elementHandle.uploadFile(path.join(__dirname, `../media/${e.pdfFileName}.pdf`)); await elementHandle.uploadFile(path.join(__dirname, `../media/${e.pdfFileName}.pdf`));
await test.click(ce.confirmBtn, true); await test.click(ce.confirmBtn, true);
return true; return true;
@ -187,8 +184,6 @@ exports.nextSlide = nextSlide;
exports.annotation = annotation; exports.annotation = annotation;
exports.presetationUpload = presetationUpload; exports.presetationUpload = presetationUpload;
exports.hexToRgb = hexToRgb; exports.hexToRgb = hexToRgb;
exports.getTestElement = getTestElement;
exports.countTestElements = countTestElements;
exports.autoJoinTest = autoJoinTest; exports.autoJoinTest = autoJoinTest;
exports.listenOnlyMode = listenOnlyMode; exports.listenOnlyMode = listenOnlyMode;
exports.forceListenOnly = forceListenOnly; exports.forceListenOnly = forceListenOnly;

View File

@ -1,9 +1,9 @@
const Page = require('../core/page'); const Page = require('../core/page');
const e = require('../core/elements');
const util = require('../customparameters/util');
const { exec } = require("child_process"); const { exec } = require("child_process");
const { CLIENT_RECONNECTION_TIMEOUT } = require('../core/constants'); // core constants (Timeouts vars imported) const { CLIENT_RECONNECTION_TIMEOUT } = require('../core/constants'); // core constants (Timeouts vars imported)
const { sleep } = require('../core/helper'); const { sleep } = require('../core/helper');
const e = require('../core/elements');
const { checkElementLengthDifferentTo } = require('../core/util');
class Trigger extends Page { class Trigger extends Page {
constructor() { constructor() {
@ -33,7 +33,7 @@ class Trigger extends Page {
await sleep(3000); await sleep(3000);
await this.screenshot(`${testName}`, `04-after-meteor-reconnection-[${this.meetingId}]`); await this.screenshot(`${testName}`, `04-after-meteor-reconnection-[${this.meetingId}]`);
const findUnauthorized = await this.page.evaluate(util.countTestElements, e.unauthorized) === true; const findUnauthorized = await this.page.evaluate(checkElementLengthDifferentTo, e.unauthorized, 0) === true;
await this.logger('Check if Unauthorized message appears => ', findUnauthorized); await this.logger('Check if Unauthorized message appears => ', findUnauthorized);
return meteorStatusConfirm && getAudioButton && findUnauthorized; return meteorStatusConfirm && getAudioButton && findUnauthorized;
} catch (err) { } catch (err) {
@ -85,12 +85,12 @@ class Trigger extends Page {
await this.page.reload(); await this.page.reload();
await this.closeAudioModal(); await this.closeAudioModal();
const getAudioButton = await this.page.evaluate(() => const getAudioButton = await this.page.evaluate((joinAudioSelector) => {
document.querySelectorAll('button[aria-label="Join audio"]')[0] return document.querySelectorAll(joinAudioSelector)[0].getAttribute('aria-disabled') === "true";
.getAttribute('aria-disabled') === "true"); }, e.joinAudio)
await this.logger('Check if Connections Buttons are disabled => ', getAudioButton); await this.logger('Check if Connections Buttons are disabled => ', getAudioButton);
await sleep(3000); await sleep(3000);
const findUnauthorized = await this.page.evaluate(util.countTestElements, e.unauthorized) === true; const findUnauthorized = await this.page.evaluate(checkElementLengthDifferentTo, e.unauthorized, 0) === true;
await this.logger('Check if Unauthorized message appears => ', findUnauthorized); await this.logger('Check if Unauthorized message appears => ', findUnauthorized);
return meteorStatusConfirm && getAudioButton && findUnauthorized; return meteorStatusConfirm && getAudioButton && findUnauthorized;
} catch (err) { } catch (err) {

View File

@ -12,8 +12,13 @@ class SharedNotes extends Create {
} }
async close() { async close() {
await this.page1.close(); try {
await this.page2.close(); await this.page1.close();
await this.page2.close();
} catch (e) {
await this.page1.logger(e);
}
} }
} }
module.exports = exports = SharedNotes; module.exports = exports = SharedNotes;

View File

@ -1,19 +1,19 @@
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const se = require('./elements'); const se = require('./elements');
const { getElementLength } = require('../core/util');
async function startSharedNotes(test) { async function startSharedNotes(test) {
await test.waitForSelector(se.sharedNotes, ELEMENT_WAIT_TIME); try {
await test.click(se.sharedNotes, true); await test.waitForSelector(se.sharedNotes, ELEMENT_WAIT_TIME);
await test.waitForSelector(se.hideNoteLabel, ELEMENT_WAIT_LONGER_TIME); await test.click(se.sharedNotes, true);
const resp = await test.page.evaluate(getTestElement, se.etherpad); await test.waitForSelector(se.hideNoteLabel, ELEMENT_WAIT_LONGER_TIME);
await test.waitForSelector(se.etherpad, ELEMENT_WAIT_TIME); const resp = await test.page.evaluate(getElementLength, se.etherpad) >= 1;
return resp; await test.waitForSelector(se.etherpad, ELEMENT_WAIT_TIME);
return resp === true;
} catch (e) {
await test.logger(e);
return false;
}
} }
async function getTestElement(element) {
const response = await document.querySelectorAll(element).length >= 1;
return response;
}
exports.getTestElement = getTestElement;
exports.startSharedNotes = startSharedNotes; exports.startSharedNotes = startSharedNotes;

View File

@ -9,6 +9,7 @@ const ne = require('./elements');
const pe = require('../presentation/elements'); const pe = require('../presentation/elements');
const we = require('../whiteboard/elements'); const we = require('../whiteboard/elements');
const { ELEMENT_WAIT_TIME, UPLOAD_PDF_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, UPLOAD_PDF_WAIT_TIME } = require('../core/constants');
const { checkElementTextIncludes } = require('../core/util');
class Notifications extends MultiUsers { class Notifications extends MultiUsers {
constructor() { constructor() {
@ -127,8 +128,8 @@ class Notifications extends MultiUsers {
await this.initUser4(testName); await this.initUser4(testName);
await this.page4.closeAudioModal(); await this.page4.closeAudioModal();
await this.page3.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME);
await this.page3.page.waitForFunction( await this.page3.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("User joined the session")', 'body', 'User joined the session'
); );
await this.page3.screenshot(`${testName}`, `04-page03-user-join-toast-${testName}`); await this.page3.screenshot(`${testName}`, `04-page03-user-join-toast-${testName}`);
return true; return true;
@ -151,20 +152,20 @@ class Notifications extends MultiUsers {
await this.page3.waitForSelector(pe.fileUpload, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(pe.fileUpload, ELEMENT_WAIT_TIME);
const fileUpload = await this.page3.page.$(pe.fileUpload); const fileUpload = await this.page3.page.$(pe.fileUpload);
await fileUpload.uploadFile(path.join(__dirname, `../media/${e.pdfFileName}.pdf`)); await fileUpload.uploadFile(path.join(__dirname, `../media/${e.pdfFileName}.pdf`));
await this.page3.page.waitForFunction( await this.page3.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("To be uploaded ...")', 'body', 'To be uploaded ...'
); );
await this.page3.waitForSelector(pe.upload, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(pe.upload, ELEMENT_WAIT_TIME);
await this.page3.click(pe.upload, true); await this.page3.click(pe.upload, true);
await this.page3.page.waitForFunction( await this.page3.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("Converting file")', 'body', 'Converting file'
); );
await this.page3.screenshot(`${testName}`, `04-page03-file-uploaded-and-ready-${testName}`); await this.page3.screenshot(`${testName}`, `04-page03-file-uploaded-and-ready-${testName}`);
await this.page3.waitForSelector(ne.smallToastMsg, UPLOAD_PDF_WAIT_TIME); await this.page3.waitForSelector(ne.smallToastMsg, UPLOAD_PDF_WAIT_TIME);
await this.page3.waitForSelector(we.whiteboard, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(we.whiteboard, ELEMENT_WAIT_TIME);
await this.page3.screenshot(`${testName}`, `05-page03-presentation-changed-${testName}`); await this.page3.screenshot(`${testName}`, `05-page03-presentation-changed-${testName}`);
await this.page3.page.waitForFunction( await this.page3.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("Current presentation")', 'body', 'Current presentation'
); );
await this.page3.screenshot(`${testName}`, `06-page03-presentation-change-toast-${testName}`); await this.page3.screenshot(`${testName}`, `06-page03-presentation-change-toast-${testName}`);
return true; return true;
@ -186,7 +187,7 @@ class Notifications extends MultiUsers {
await this.page3.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME); await this.page3.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME);
const resp = await util.getLastToastValue(this.page3); const resp = await util.getLastToastValue(this.page3);
await this.page3.screenshot(`${testName}`, `04-page03-poll-toast-${testName}`); await this.page3.screenshot(`${testName}`, `04-page03-poll-toast-${testName}`);
return resp === true; return resp;
} catch (err) { } catch (err) {
await this.page3.logger(err); await this.page3.logger(err);
return false; return false;
@ -202,7 +203,7 @@ class Notifications extends MultiUsers {
await this.page3.screenshot(`${testName}`, `02-page03-joined-microphone-${testName}`); await this.page3.screenshot(`${testName}`, `02-page03-joined-microphone-${testName}`);
const resp = await util.getLastToastValue(this.page3) === ne.joinAudioToast; const resp = await util.getLastToastValue(this.page3) === ne.joinAudioToast;
await this.page3.screenshot(`${testName}`, `03-page03-audio-toast-${testName}`); await this.page3.screenshot(`${testName}`, `03-page03-audio-toast-${testName}`);
return resp === true; return resp;
} catch (err) { } catch (err) {
await this.page3.logger(err); await this.page3.logger(err);
return false; return false;
@ -220,7 +221,7 @@ class Notifications extends MultiUsers {
await this.page3.screenshot(`${testName}`, `03-page03-screenshare-started-${testName}`); await this.page3.screenshot(`${testName}`, `03-page03-screenshare-started-${testName}`);
const response = await util.getLastToastValue(this.page3); const response = await util.getLastToastValue(this.page3);
await this.page3.screenshot(`${testName}`, `04-page03-screenshare-toast-${testName}`); await this.page3.screenshot(`${testName}`, `04-page03-screenshare-toast-${testName}`);
return response === true; return response;
} catch (err) { } catch (err) {
await this.page3.logger(err); await this.page3.logger(err);
return false; return false;

View File

@ -3,28 +3,25 @@ const ule = require('../user/elements');
const ce = require('../chat/elements'); const ce = require('../chat/elements');
const e = require('../core/elements'); const e = require('../core/elements');
const { ELEMENT_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME } = require('../core/constants');
const { clickElement, getElementText, checkElement, checkElementLengthEqualTo } = require('../core/util');
async function clickTestElement(element) {
await document.querySelectorAll(element)[0].click();
}
async function popupMenu(test) { async function popupMenu(test) {
await test.page.evaluate(clickTestElement, e.options); await test.page.evaluate(clickElement, e.options);
await test.page.evaluate(clickTestElement, ne.settings); await test.page.evaluate(clickElement, ne.settings);
} }
async function enableChatPopup(test) { async function enableChatPopup(test) {
await test.waitForSelector(ne.notificationsTab, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.notificationsTab, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ne.notificationsTab); await test.page.evaluate(clickElement, ne.notificationsTab);
await test.waitForSelector(ne.chatPushAlerts, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.chatPushAlerts, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ne.chatPushAlerts); await test.page.evaluate(clickElement, ne.chatPushAlerts);
} }
async function enableUserJoinPopup(test) { async function enableUserJoinPopup(test) {
await test.waitForSelector(ne.notificationsTab, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.notificationsTab, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ne.notificationsTab); await test.page.evaluate(clickElement, ne.notificationsTab);
await test.waitForSelector(ne.userJoinPushAlerts, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.userJoinPushAlerts, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, ne.userJoinPushAlerts); await test.page.evaluate(clickElement, ne.userJoinPushAlerts);
} }
async function saveSettings(page) { async function saveSettings(page) {
@ -34,44 +31,26 @@ async function saveSettings(page) {
async function waitForToast(test) { async function waitForToast(test) {
await test.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME);
const resp = await test.page.evaluate(getTestElement, ne.smallToastMsg) !== null; const resp = await test.page.evaluate(checkElement, ne.smallToastMsg, 1);
return resp; return resp;
} }
async function getLastToastValue(test) { async function getLastToastValue(test) {
await test.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME);
const toast = test.page.evaluate(async (toastMsgSelector) => { const toast = test.page.evaluate(getElementText, ne.smallToastMsg);
const lastToast = await document.querySelectorAll(toastMsgSelector)[0].innerText;
return lastToast;
}, ne.smallToastMsg);
return toast; return toast;
} }
async function getOtherToastValue(test) { async function getOtherToastValue(test) {
await test.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.smallToastMsg, ELEMENT_WAIT_TIME);
const toast = test.page.evaluate(async (toastMsgSelector) => { const toast = test.page.evaluate(getElementText, ne.smallToastMsg, 1);
const lastToast = await document.querySelectorAll(toastMsgSelector)[1].innerText;
return lastToast;
}, ne.smallToastMsg);
return toast; return toast;
} }
async function getTestElement(element) {
await document.querySelectorAll(element)[1];
}
async function clickOnElement(element) {
await document.querySelectorAll(element)[0].click();
}
async function clickThePrivateChatButton(element) {
await document.querySelectorAll(element)[0].click();
}
async function publicChatMessageToast(page1, page2) { async function publicChatMessageToast(page1, page2) {
// Open private Chat with the other User // Open private Chat with the other User
await page1.page.evaluate(clickOnElement, ule.userListItem); await page1.page.evaluate(clickElement, ule.userListItem);
await page1.page.evaluate(clickThePrivateChatButton, ce.activeChat); await page1.page.evaluate(clickElement, ce.activeChat);
// send a public message // send a public message
await page2.page.type(ce.publicChat, ce.publicMessage1); await page2.page.type(ce.publicChat, ce.publicMessage1);
await page2.click(ce.sendButton, true); await page2.click(ce.sendButton, true);
@ -80,13 +59,13 @@ async function publicChatMessageToast(page1, page2) {
async function privateChatMessageToast(page2) { async function privateChatMessageToast(page2) {
// Open private Chat with the other User // Open private Chat with the other User
await page2.page.evaluate(clickOnElement, ule.userListItem); await page2.page.evaluate(clickElement, ule.userListItem);
await page2.page.evaluate(clickThePrivateChatButton, ce.activeChat); await page2.page.evaluate(clickElement, ce.activeChat);
// wait for the private chat to be ready // wait for the private chat to be ready
await page2.page.waitForFunction( await page2.page.waitForFunction(
(chatSelector) => document.querySelectorAll(chatSelector).length == 2, checkElementLengthEqualTo,
{ timeout: ELEMENT_WAIT_TIME }, { timeout: ELEMENT_WAIT_TIME },
ce.chatButton ce.chatButton, 2
); );
// send a private message // send a private message
await page2.page.type(ce.privateChat, ce.message1); await page2.page.type(ce.privateChat, ce.message1);
@ -100,27 +79,20 @@ async function uploadFileMenu(test) {
await test.click(ne.uploadPresentation); await test.click(ne.uploadPresentation);
} }
async function getFileItemStatus(element, value) {
document.querySelectorAll(element)[1].innerText.includes(value);
}
async function startPoll(test) { async function startPoll(test) {
await test.waitForSelector(e.actions);
await test.click(e.actions); await test.click(e.actions);
await test.waitForSelector(ne.polling);
await test.click(ne.polling); await test.click(ne.polling);
await test.waitForSelector(ne.hidePollDesc, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.hidePollDesc, ELEMENT_WAIT_TIME);
await test.waitForSelector(ne.polling, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.polling, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickOnElement, ne.polling); await test.page.evaluate(clickElement, ne.polling);
await test.waitForSelector(ne.pollYesNoAbstentionBtn, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.pollYesNoAbstentionBtn, ELEMENT_WAIT_TIME);
await test.click(ne.pollYesNoAbstentionBtn, true); await test.click(ne.pollYesNoAbstentionBtn, true);
await test.waitForSelector(ne.startPoll, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.startPoll, ELEMENT_WAIT_TIME);
await test.click(ne.startPoll, true); await test.click(ne.startPoll, true);
await test.waitForSelector(ne.publishLabel, ELEMENT_WAIT_TIME); await test.waitForSelector(ne.publishLabel, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickOnElement, ne.publishLabel); await test.page.evaluate(clickElement, ne.publishLabel);
} }
exports.getFileItemStatus = getFileItemStatus;
exports.privateChatMessageToast = privateChatMessageToast; exports.privateChatMessageToast = privateChatMessageToast;
exports.publicChatMessageToast = publicChatMessageToast; exports.publicChatMessageToast = publicChatMessageToast;
exports.enableUserJoinPopup = enableUserJoinPopup; exports.enableUserJoinPopup = enableUserJoinPopup;
@ -128,10 +100,7 @@ exports.getOtherToastValue = getOtherToastValue;
exports.getLastToastValue = getLastToastValue; exports.getLastToastValue = getLastToastValue;
exports.enableChatPopup = enableChatPopup; exports.enableChatPopup = enableChatPopup;
exports.uploadFileMenu = uploadFileMenu; exports.uploadFileMenu = uploadFileMenu;
exports.getTestElement = getTestElement;
exports.saveSettings = saveSettings; exports.saveSettings = saveSettings;
exports.waitForToast = waitForToast; exports.waitForToast = waitForToast;
exports.popupMenu = popupMenu; exports.popupMenu = popupMenu;
exports.clickTestElement = clickTestElement;
exports.startPoll = startPoll; exports.startPoll = startPoll;
exports.clickOnElement = clickOnElement;

View File

@ -5289,9 +5289,9 @@
} }
}, },
"ws": { "ws": {
"version": "7.3.1", "version": "7.5.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.4.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" "integrity": "sha512-zP9z6GXm6zC27YtspwH99T3qTG7bBFv2VIkeHstMLrLlDJuzA7tQ5ls3OJ1hOGGCzTQPniNJoHXIAOS0Jljohg=="
}, },
"xml-name-validator": { "xml-name-validator": {
"version": "3.0.0", "version": "3.0.0",

View File

@ -1,7 +1,8 @@
exports.pollingContainer = 'div[data-test="pollingContainer"]'; exports.pollingContainer = 'div[data-test="pollingContainer"]';
exports.pollQuestionArea = 'textarea[data-test="pollQuestionArea"]'; exports.pollQuestionArea = 'textarea[data-test="pollQuestionArea"]';
exports.pollQuestion = 'Are we good ?'; exports.pollQuestion = 'Are we good ?';
exports.responseTypes = 'div[data-test="responseTypesLabel"]'; exports.responseTypes = 'div[data-test="responseTypes"]';
exports.responseTypesLabel = 'div[data-test="responseTypesLabel"]';
exports.responseChoices = 'div[data-test="responseChoices"]'; exports.responseChoices = 'div[data-test="responseChoices"]';
exports.addItem = 'button[data-test="addItem"]'; exports.addItem = 'button[data-test="addItem"]';
exports.pollOptionItem = 'input[data-test="pollOptionItem"]'; exports.pollOptionItem = 'input[data-test="pollOptionItem"]';

View File

@ -1,6 +1,7 @@
const Page = require('../core/page'); const Page = require('../core/page');
const e = require('../core/elements');
const utilNotification = require('../notifications/util'); const utilNotification = require('../notifications/util');
const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported) const { checkElementLengthEqualTo } = require('../core/util');
class Polling extends Page { class Polling extends Page {
constructor() { constructor() {
@ -12,7 +13,7 @@ class Polling extends Page {
await utilNotification.startPoll(this); await utilNotification.startPoll(this);
await this.screenshot(`${testName}`, `01-before-chat-message-send-[${this.meetingId}]`); await this.screenshot(`${testName}`, `01-before-chat-message-send-[${this.meetingId}]`);
const resp = this.page.evaluate(() => document.querySelectorAll('[data-test="pollMenuButton"]').length === 1); const resp = this.page.evaluate(checkElementLengthEqualTo, e.pollMenuButton, 1);
return resp; return resp;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);

View File

@ -2,6 +2,7 @@ const Page = require('../core/page');
const e = require('./elements'); const e = require('./elements');
const we = require('../whiteboard/elements'); const we = require('../whiteboard/elements');
const { ELEMENT_WAIT_LONGER_TIME, ELEMENT_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_LONGER_TIME, ELEMENT_WAIT_TIME } = require('../core/constants');
const util = require('./util');
class Slide extends Page { class Slide extends Page {
constructor() { constructor() {
@ -13,21 +14,21 @@ class Slide extends Page {
await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_LONGER_TIME); await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await this.waitForSelector(e.presentationToolbarWrapper, ELEMENT_WAIT_TIME); await this.waitForSelector(e.presentationToolbarWrapper, ELEMENT_WAIT_TIME);
const svg0 = await this.page.evaluate(async () => await document.querySelector('svg g g g').outerHTML.indexOf('/svg/1') !== -1); const svg0 = await this.page.evaluate(util.checkSvgIndex, '/svg/1');
await this.waitForSelector(e.nextSlide, ELEMENT_WAIT_TIME); await this.waitForSelector(e.nextSlide, ELEMENT_WAIT_TIME);
await this.click(e.nextSlide, true); await this.click(e.nextSlide, true);
await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_TIME); await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_TIME);
await this.page.waitFor(1000); await this.page.waitForTimeout(1000);
const svg1 = await this.page.evaluate(async () => await document.querySelector('svg g g g').outerHTML.indexOf('/svg/2') !== -1); const svg1 = await this.page.evaluate(util.checkSvgIndex, '/svg/2');
await this.waitForSelector(e.prevSlide, ELEMENT_WAIT_TIME); await this.waitForSelector(e.prevSlide, ELEMENT_WAIT_TIME);
await this.click(e.prevSlide, true); await this.click(e.prevSlide, true);
await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_TIME); await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_TIME);
await this.page.waitFor(1000); await this.page.waitForTimeout(1000);
const svg2 = await this.page.evaluate(async () => await document.querySelector('svg g g g').outerHTML.indexOf('/svg/1') !== -1); const svg2 = await this.page.evaluate(util.checkSvgIndex, '/svg/1');
return svg0 === true && svg1 === true && svg2 === true; return svg0 === true && svg1 === true && svg2 === true;
} catch (err) { } catch (err) {

View File

@ -3,6 +3,8 @@ const e = require('./elements');
const we = require('../whiteboard/elements'); const we = require('../whiteboard/elements');
const ce = require('../core/elements'); const ce = require('../core/elements');
const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const { checkElementTextIncludes } = require('../core/util');
const util = require('./util');
class Upload extends Page { class Upload extends Page {
constructor() { constructor() {
@ -14,7 +16,7 @@ class Upload extends Page {
await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_LONGER_TIME); await this.waitForSelector(we.whiteboard, ELEMENT_WAIT_LONGER_TIME);
await this.waitForSelector(e.skipSlide, ELEMENT_WAIT_TIME); await this.waitForSelector(e.skipSlide, ELEMENT_WAIT_TIME);
const slides0 = await this.page.evaluate(async () => await document.querySelector('svg g g g').outerHTML); const slides0 = await this.page.evaluate(util.getSvgOuterHtml);
await this.click(ce.actions, true); await this.click(ce.actions, true);
await this.click(e.uploadPresentation, true); await this.click(e.uploadPresentation, true);
@ -24,19 +26,19 @@ class Upload extends Page {
await this.waitForSelector(e.fileUpload, ELEMENT_WAIT_TIME); await this.waitForSelector(e.fileUpload, ELEMENT_WAIT_TIME);
const fileUpload = await this.page.$(e.fileUpload); const fileUpload = await this.page.$(e.fileUpload);
await fileUpload.uploadFile(`${__dirname}/upload-test.png`); await fileUpload.uploadFile(`${__dirname}/upload-test.png`);
await this.page.waitForFunction( await this.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("To be uploaded ...")', 'body', 'To be uploaded ...'
); );
await this.page.waitForSelector(e.upload, ELEMENT_WAIT_TIME); await this.page.waitForSelector(e.upload, ELEMENT_WAIT_TIME);
await this.page.click(e.upload, true); await this.page.click(e.upload, true);
await this.logger('\nWaiting for the new presentation to upload...'); await this.logger('\nWaiting for the new presentation to upload...');
await this.page.waitForFunction( await this.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("Converting file")', 'body', 'Converting file'
); );
await this.logger('\nPresentation uploaded!'); await this.logger('\nPresentation uploaded!');
await this.page.waitForFunction( await this.page.waitForFunction(checkElementTextIncludes, {},
'document.querySelector("body").innerText.includes("Current presentation")', 'body', 'Current presentation'
); );
await this.screenshot(`${testName}`, `02-after-presentation-upload-[${testName}]`); await this.screenshot(`${testName}`, `02-after-presentation-upload-[${testName}]`);
@ -53,12 +55,6 @@ class Upload extends Page {
return false; return false;
} }
} }
async getTestElements() {
const svg = await this.page.evaluate(async () => await document.querySelector('svg g g g').outerHTML);
await this.logger(svg);
return svg;
}
} }
module.exports = exports = Upload; module.exports = exports = Upload;

View File

@ -0,0 +1,10 @@
function checkSvgIndex (element) {
return document.querySelector('svg g g g').outerHTML.indexOf(element) !== -1;
}
function getSvgOuterHtml() {
return document.querySelector('svg g g g').outerHTML;
}
exports.checkSvgIndex = checkSvgIndex;
exports.getSvgOuterHtml = getSvgOuterHtml;

View File

@ -3,6 +3,7 @@ const util = require('./util');
const e = require('../core/elements'); const e = require('../core/elements');
const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants');
const { sleep } = require('../core/helper'); const { sleep } = require('../core/helper');
const { checkElementLengthEqualTo } = require('../core/util');
class ShareScreen extends Page { class ShareScreen extends Page {
constructor() { constructor() {
@ -28,7 +29,7 @@ class ShareScreen extends Page {
await this.init(args, undefined, undefined, undefined, testName, undefined, deviceX); await this.init(args, undefined, undefined, undefined, testName, undefined, deviceX);
await this.startRecording(testName); await this.startRecording(testName);
await this.closeAudioModal(); await this.closeAudioModal();
const screenshareBtn = await this.page.evaluate(() => document.querySelectorAll('button[aria-label="Share your screen"]').length === 0) === true; const screenshareBtn = await this.page.evaluate(checkElementLengthEqualTo, e.screenShare, 1);
return screenshareBtn; return screenshareBtn;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);

View File

@ -1,15 +1,12 @@
const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants');
const e = require('../core/elements'); const e = require('../core/elements');
const { checkElement } = require('../core/util');
async function startScreenshare(test) { async function startScreenshare(test) {
await test.waitForSelector(e.screenShare, ELEMENT_WAIT_TIME); await test.waitForSelector(e.screenShare, ELEMENT_WAIT_TIME);
await test.click(e.screenShare, true); await test.click(e.screenShare, true);
} }
async function getTestElement(element) {
(await document.querySelectorAll(element)[0]) !== null;
}
async function waitForScreenshareContainer(test) { async function waitForScreenshareContainer(test) {
await test.waitForSelector(e.screenshareConnecting, ELEMENT_WAIT_TIME); await test.waitForSelector(e.screenshareConnecting, ELEMENT_WAIT_TIME);
await test.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME); await test.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
@ -17,21 +14,16 @@ async function waitForScreenshareContainer(test) {
async function getScreenShareContainer(test) { async function getScreenShareContainer(test) {
await test.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME); await test.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
const screenShareContainer = await test.page.evaluate(getTestElement, e.screenShareVideo); return test.page.evaluate(checkElement, e.screenShareVideo);
const response = screenShareContainer !== null;
return response;
} }
async function getScreenShareBreakoutContainer(test) { async function getScreenShareBreakoutContainer(test) {
await test.waitForSelector(e.screenshareConnecting, { timeout: VIDEO_LOADING_WAIT_TIME }); await test.waitForSelector(e.screenshareConnecting, { timeout: VIDEO_LOADING_WAIT_TIME });
await test.waitForSelector(e.screenShareVideo, { timeout: VIDEO_LOADING_WAIT_TIME }); await test.waitForSelector(e.screenShareVideo, { timeout: VIDEO_LOADING_WAIT_TIME });
const screenShareContainer = await test.evaluate(getTestElement, e.screenShareVideo); return test.evaluate(checkElement, e.screenShareVideo);
const response = screenShareContainer !== null;
return response;
} }
exports.getScreenShareBreakoutContainer = getScreenShareBreakoutContainer; exports.getScreenShareBreakoutContainer = getScreenShareBreakoutContainer;
exports.getScreenShareContainer = getScreenShareContainer; exports.getScreenShareContainer = getScreenShareContainer;
exports.getTestElement = getTestElement;
exports.startScreenshare = startScreenshare; exports.startScreenshare = startScreenshare;
exports.waitForScreenshareContainer = waitForScreenshareContainer; exports.waitForScreenshareContainer = waitForScreenshareContainer;

View File

@ -1,10 +1,11 @@
const Page = require('../core/page'); const Page = require('../core/page');
const { checkIncludeClass, checkUniqueElement } = require('./util');
const c = require('../core/constants');
const params = require('../params'); const params = require('../params');
const c = require('../core/constants');
const ne = require('../notifications/elements'); const ne = require('../notifications/elements');
const ue = require('../user/elements'); const ue = require('../user/elements');
const e = require('../core/elements'); const e = require('../core/elements');
const util = require('./util');
const { checkElementLengthEqualTo } = require('../core/util');
class Stress extends Page { class Stress extends Page {
constructor() { constructor() {
@ -19,9 +20,9 @@ class Stress extends Page {
await this.init(Page.getArgs(), undefined, { ...params, fullName: `Moderator-${i}` }, undefined, testName); await this.init(Page.getArgs(), undefined, { ...params, fullName: `Moderator-${i}` }, undefined, testName);
await this.closeAudioModal(); await this.closeAudioModal();
await this.page.waitForSelector(ue.statusIcon, { timeout: c.ELEMENT_WAIT_TIME }); await this.page.waitForSelector(ue.statusIcon, { timeout: c.ELEMENT_WAIT_TIME });
const hasPresenterClass = await this.page.evaluate(checkIncludeClass, ue.statusIcon, e.presenterClassName); const hasPresenterClass = await this.page.evaluate(util.checkIncludeClass, ue.statusIcon, e.presenterClassName);
await this.click(e.actions); await this.click(e.actions);
const canStartPoll = await this.page.evaluate(checkUniqueElement, ne.polling); const canStartPoll = await this.page.evaluate(checkElementLengthEqualTo, ne.polling, 1);
if (!hasPresenterClass || !canStartPoll) { if (!hasPresenterClass || !canStartPoll) {
failureCount++; failureCount++;
await this.screenshot(`${testName}`, `loop-${i}-failure-${testName}`); await this.screenshot(`${testName}`, `loop-${i}-failure-${testName}`);

View File

@ -1,9 +1,5 @@
function checkIncludeClass(selector, className) { function checkIncludeClass(selector, className) {
return document.querySelectorAll(selector)[0]?.className.includes(className); return document.querySelectorAll(selector)[0]?.className.includes(className);
} }
function checkUniqueElement(selector) {
return document.querySelectorAll(selector).length === 1;
}
exports.checkIncludeClass = checkIncludeClass; exports.checkIncludeClass = checkIncludeClass;
exports.checkUniqueElement = checkUniqueElement;

View File

@ -32,3 +32,5 @@ exports.chatButton = '[accesskey="P"]';
exports.chatPanel = 'section[data-test="chatPanel"]'; exports.chatPanel = 'section[data-test="chatPanel"]';
exports.userListButton = '[accesskey="U"]'; exports.userListButton = '[accesskey="U"]';
exports.userListPanel = 'div[data-test="userListPanel"]'; exports.userListPanel = 'div[data-test="userListPanel"]';
exports.multiWhiteboardTool = 'span[data-test="multiWhiteboardTool"]'
exports.connectionStatusBtn = 'button[data-test="connectionStatusButton"]';

View File

@ -2,15 +2,16 @@ const Page = require('../core/page');
const params = require('../params'); const params = require('../params');
const util = require('../chat/util'); const util = require('../chat/util');
const utilUser = require('./util'); const utilUser = require('./util');
const utilCustomParams = require('../customparameters/util');
const pe = require('../core/elements'); const pe = require('../core/elements');
const ne = require('../notifications/elements'); const ne = require('../notifications/elements');
const ple = require('../polling/elemens'); const ple = require('../polling/elemens');
const we = require('../whiteboard/elements'); const we = require('../whiteboard/elements');
const ue = require('./elements'); const ue = require('./elements');
const ce = require('../chat/elements');
const cu = require('../customparameters/elements'); const cu = require('../customparameters/elements');
const { ELEMENT_WAIT_TIME } = require('../core/constants'); const { ELEMENT_WAIT_TIME } = require('../core/constants');
const { sleep } = require('../core/helper'); const { sleep } = require('../core/helper');
const { getElementLength, checkElementLengthEqualTo, checkElementLengthDifferentTo } = require('../core/util');
class MultiUsers { class MultiUsers {
constructor() { constructor() {
@ -32,8 +33,8 @@ class MultiUsers {
// Run the test for the page // Run the test for the page
async checkForOtherUser() { async checkForOtherUser() {
const firstCheck = await this.page1.page.evaluate(() => document.querySelectorAll('[data-test="userListItem"]').length > 0); const firstCheck = await this.page1.page.evaluate(getElementLength, ue.userListItem) > 0;
const secondCheck = await this.page2.page.evaluate(() => document.querySelectorAll('[data-test="userListItem"]').length > 0); const secondCheck = await this.page1.page.evaluate(getElementLength, ue.userListItem) > 0;
return { return {
firstCheck, firstCheck,
secondCheck, secondCheck,
@ -42,9 +43,9 @@ class MultiUsers {
async multiUsersPublicChat() { async multiUsersPublicChat() {
try { try {
const chat0 = await this.page1.page.evaluate(() => document.querySelectorAll('p[data-test="chatUserMessageText"]').length); const chat0 = await this.page1.page.evaluate(getElementLength, ce.chatUserMessageText);
await util.sendPublicChatMessage(this.page1, this.page2); await util.sendPublicChatMessage(this.page1, this.page2);
const chat1 = await this.page1.page.evaluate(() => document.querySelectorAll('p[data-test="chatUserMessageText"]').length); const chat1 = await this.page1.page.evaluate(getElementLength, ce.chatUserMessageText);
return chat0 !== chat1; return chat0 !== chat1;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -55,10 +56,10 @@ class MultiUsers {
async multiUsersPrivateChat() { async multiUsersPrivateChat() {
try { try {
await util.openPrivateChatMessage(this.page1, this.page2); await util.openPrivateChatMessage(this.page1, this.page2);
const chat0 = await this.page1.page.evaluate(() => document.querySelectorAll('p[data-test="chatUserMessageText"]').length); const chat0 = await this.page1.page.evaluate(getElementLength, ce.chatUserMessageText);
await util.sendPrivateChatMessage(this.page1, this.page2); await util.sendPrivateChatMessage(this.page1, this.page2);
await sleep(2000); await sleep(2000);
const chat1 = await this.page1.page.evaluate(() => document.querySelectorAll('p[data-test="chatUserMessageText"]').length); const chat1 = await this.page1.page.evaluate(getElementLength, ce.chatUserMessageText);
return chat0 !== chat1; return chat0 !== chat1;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -90,15 +91,15 @@ class MultiUsers {
await this.page1.page.focus(ple.pollQuestionArea); await this.page1.page.focus(ple.pollQuestionArea);
await this.page1.page.keyboard.type(ple.pollQuestion); await this.page1.page.keyboard.type(ple.pollQuestion);
const chosenRandomNb = await this.page1.page.evaluate(() => { const chosenRandomNb = await this.page1.page.evaluate((responseTypes) => {
const responseTypesDiv = document.querySelector('div[data-test="responseTypes"]'); const responseTypesDiv = document.querySelector(responseTypes);
const buttons = responseTypesDiv.querySelectorAll('button'); const buttons = responseTypesDiv.querySelectorAll('button');
const countButtons = buttons.length; const countButtons = buttons.length;
const randomNb = Math.floor(Math.random() * countButtons) + 1; const randomNb = Math.floor(Math.random() * countButtons) + 1;
const chosenRandomNb = randomNb - 1; const chosenRandomNb = randomNb - 1;
responseTypesDiv.querySelectorAll('button')[chosenRandomNb].click(); responseTypesDiv.querySelectorAll('button')[chosenRandomNb].click();
return chosenRandomNb; return chosenRandomNb;
}); }, ple.responseTypes);
const customs = { const customs = {
0: ple.uncertain, 0: ple.uncertain,
@ -158,7 +159,7 @@ class MultiUsers {
await this.page1.waitForSelector(ple.publishLabel, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(ple.publishLabel, ELEMENT_WAIT_TIME);
await this.page1.click(ple.publishLabel, true); await this.page1.click(ple.publishLabel, true);
await this.page1.waitForSelector(ple.restartPoll, ELEMENT_WAIT_TIME); await this.page1.waitForSelector(ple.restartPoll, ELEMENT_WAIT_TIME);
const receivedAnswerFound = await this.page1.page.evaluate(utilCustomParams.countTestElements, ple.receivedAnswer); const receivedAnswerFound = await this.page1.page.evaluate(checkElementLengthDifferentTo, ple.receivedAnswer, 0);
return receivedAnswerFound; return receivedAnswerFound;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -175,7 +176,9 @@ class MultiUsers {
await this.page1.clickNItem(we.userListItem, true, 1); await this.page1.clickNItem(we.userListItem, true, 1);
await this.page1.clickNItem(we.changeWhiteboardAccess, true, 1); await this.page1.clickNItem(we.changeWhiteboardAccess, true, 1);
await sleep(2000); await sleep(2000);
const resp = await this.page1.page.evaluate(async () => await document.querySelector('[data-test="multiWhiteboardTool"]').children[0].innerText === '1'); const resp = await this.page1.page.evaluate((multiWhiteboardTool) => {
return document.querySelector(multiWhiteboardTool).children[0].innerText === '1';
}, ue.multiWhiteboardTool);
return resp === true; return resp === true;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -191,7 +194,7 @@ class MultiUsers {
await this.page2.waitForSelector(we.raiseHandLabel, ELEMENT_WAIT_TIME); await this.page2.waitForSelector(we.raiseHandLabel, ELEMENT_WAIT_TIME);
await this.page2.click(we.raiseHandLabel, true); await this.page2.click(we.raiseHandLabel, true);
await sleep(2000); await sleep(2000);
const resp = await this.page2.page.evaluate(utilCustomParams.countTestElements, we.lowerHandLabel); const resp = await this.page2.page.evaluate(checkElementLengthDifferentTo, we.lowerHandLabel, 0);
return resp === true; return resp === true;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -205,7 +208,7 @@ class MultiUsers {
await this.page2.waitForSelector(we.lowerHandLabel, ELEMENT_WAIT_TIME); await this.page2.waitForSelector(we.lowerHandLabel, ELEMENT_WAIT_TIME);
await this.page2.click(we.lowerHandLabel, true); await this.page2.click(we.lowerHandLabel, true);
await sleep(2000); await sleep(2000);
const resp = await this.page2.page.evaluate(utilCustomParams.countTestElements, we.raiseHandLabel); const resp = await this.page2.page.evaluate(checkElementLengthDifferentTo, we.raiseHandLabel, 0);
return resp === true; return resp === true;
} catch (err) { } catch (err) {
await this.page2.logger(err); await this.page2.logger(err);
@ -217,7 +220,7 @@ class MultiUsers {
async getAvatarColorAndCompareWithUserListItem() { async getAvatarColorAndCompareWithUserListItem() {
try { try {
const avatarInToastElementColor = await this.page1.page.$eval(we.avatarsWrapperAvatar, (elem) => getComputedStyle(elem).backgroundColor); const avatarInToastElementColor = await this.page1.page.$eval(we.avatarsWrapperAvatar, (elem) => getComputedStyle(elem).backgroundColor);
const avatarInUserListColor = await this.page1.page.$eval('[data-test="userListItem"] > div [data-test="userAvatar"]', (elem) => getComputedStyle(elem).backgroundColor); const avatarInUserListColor = await this.page1.page.$eval(`${ue.userListItem} > div ${ue.statusIcon}`, (elem) => getComputedStyle(elem).backgroundColor);
return avatarInToastElementColor === avatarInUserListColor; return avatarInToastElementColor === avatarInUserListColor;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -236,8 +239,8 @@ class MultiUsers {
await sleep(5000); await sleep(5000);
await utilUser.connectionStatus(this.page1); await utilUser.connectionStatus(this.page1);
await sleep(5000); await sleep(5000);
const connectionStatusItemEmpty = await this.page1.page.evaluate(utilUser.countTestElements, ue.connectionStatusItemEmpty) === false; const connectionStatusItemEmpty = await this.page1.page.evaluate(checkElementLengthEqualTo, ue.connectionStatusItemEmpty, 0);
const connectionStatusOfflineUser = await this.page1.page.evaluate(utilUser.countTestElements, ue.connectionStatusOfflineUser) === true; const connectionStatusOfflineUser = await this.page1.page.evaluate(checkElementLengthDifferentTo, ue.connectionStatusOfflineUser, 0) === true;
return connectionStatusOfflineUser && connectionStatusItemEmpty; return connectionStatusOfflineUser && connectionStatusItemEmpty;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -249,8 +252,8 @@ class MultiUsers {
try { try {
await this.page1.closeAudioModal(); await this.page1.closeAudioModal();
await this.page2.closeAudioModal(); await this.page2.closeAudioModal();
const userlistPanel = await this.page1.page.evaluate(utilUser.countTestElements, ue.chatButton) === false; const userlistPanel = await this.page1.page.evaluate(checkElementLengthEqualTo, ue.chatButton, 0);
const chatPanel = await this.page2.page.evaluate(utilUser.countTestElements, ue.chatButton) === false; const chatPanel = await this.page2.page.evaluate(checkElementLengthEqualTo, ue.chatButton, 0);
return userlistPanel && chatPanel; return userlistPanel && chatPanel;
} catch (err) { } catch (err) {
await this.page1.logger(err); await this.page1.logger(err);
@ -266,7 +269,7 @@ class MultiUsers {
await this.page2.click(ue.userListButton, true); await this.page2.click(ue.userListButton, true);
await this.page2.click(ue.chatButton, true); await this.page2.click(ue.chatButton, true);
const onUserListPanel = await this.page1.isNotVisible(cu.hidePresentation, ELEMENT_WAIT_TIME) === true; const onUserListPanel = await this.page1.isNotVisible(cu.hidePresentation, ELEMENT_WAIT_TIME) === true;
const onChatPanel = await this.page2.page.evaluate(utilUser.countTestElements, cu.hidePresentation) === false; const onChatPanel = await this.page2.page.evaluate(checkElementLengthEqualTo, cu.hidePresentation, 0);
await sleep(2000); await sleep(2000);
return onUserListPanel && onChatPanel; return onUserListPanel && onChatPanel;
} catch (err) { } catch (err) {
@ -281,7 +284,7 @@ class MultiUsers {
await this.page2.closeAudioModal(); await this.page2.closeAudioModal();
await this.page2.click(ue.userListButton, true); await this.page2.click(ue.userListButton, true);
await this.page2.click(ue.chatButton, true); await this.page2.click(ue.chatButton, true);
const whiteboard = await this.page1.page.evaluate(utilUser.countTestElements, ue.chatButton) === false; const whiteboard = await this.page1.page.evaluate(checkElementLengthEqualTo, ue.chatButton, 0);
const onChatPanel = await this.page2.isNotVisible(ue.chatButton, ELEMENT_WAIT_TIME) === true; const onChatPanel = await this.page2.isNotVisible(ue.chatButton, ELEMENT_WAIT_TIME) === true;
await sleep(2000); await sleep(2000);
return whiteboard && onChatPanel; return whiteboard && onChatPanel;

View File

@ -4,8 +4,8 @@ const e = require('./elements');
const util = require('./util'); const util = require('./util');
const utilWebcam = require('../webcam/util'); const utilWebcam = require('../webcam/util');
const utilScreenshare = require('../screenshare/util'); const utilScreenshare = require('../screenshare/util');
const utilB = require('../breakout/util');
const { sleep } = require('../core/helper'); const { sleep } = require('../core/helper');
const { clickElement, checkElementLengthEqualTo, checkElementLengthDifferentTo } = require('../core/util');
class Status extends Page { class Status extends Page {
constructor() { constructor() {
@ -15,9 +15,9 @@ class Status extends Page {
async test() { async test() {
try { try {
await util.setStatus(this, e.applaud); await util.setStatus(this, e.applaud);
const resp1 = await this.page.evaluate(util.countTestElements, e.applauseIcon); const resp1 = await this.page.evaluate(checkElementLengthDifferentTo, e.applauseIcon, 0);
await util.setStatus(this, e.away); await util.setStatus(this, e.away);
const resp2 = await this.page.evaluate(util.countTestElements, e.awayIcon); const resp2 = await this.page.evaluate(checkElementLengthDifferentTo, e.awayIcon, 0);
await this.click(e.firstUser, true); await this.click(e.firstUser, true);
await this.waitForSelector(e.clearStatus, ELEMENT_WAIT_TIME); await this.waitForSelector(e.clearStatus, ELEMENT_WAIT_TIME);
@ -35,7 +35,7 @@ class Status extends Page {
await this.page.click(e.userList, true); await this.page.click(e.userList, true);
await this.page.waitForSelector(e.firstUser, ELEMENT_WAIT_TIME); await this.page.waitForSelector(e.firstUser, ELEMENT_WAIT_TIME);
const response = await this.page.evaluate(util.countTestElements, e.mobileUser) === true; const response = await this.page.evaluate(checkElementLengthDifferentTo, e.mobileUser, 0);
return response === true; return response === true;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);
@ -46,7 +46,7 @@ class Status extends Page {
async findConnectionStatusModal() { async findConnectionStatusModal() {
try { try {
await util.connectionStatus(this.page); await util.connectionStatus(this.page);
const resp = await this.page.evaluate(util.countTestElements, e.connectionStatusModal) === true; const resp = await this.page.evaluate(checkElementLengthDifferentTo, e.connectionStatusModal, 0);
return resp === true; return resp === true;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);
@ -60,11 +60,11 @@ class Status extends Page {
await utilWebcam.enableWebcam(this, ELEMENT_WAIT_LONGER_TIME); await utilWebcam.enableWebcam(this, ELEMENT_WAIT_LONGER_TIME);
await util.connectionStatus(this); await util.connectionStatus(this);
await this.waitForSelector(e.dataSavingWebcams, ELEMENT_WAIT_TIME); await this.waitForSelector(e.dataSavingWebcams, ELEMENT_WAIT_TIME);
await this.page.evaluate(utilB.clickTestElement, e.dataSavingWebcams); await this.page.evaluate(clickElement, e.dataSavingWebcams);
await this.waitForSelector(e.closeConnectionStatusModal, ELEMENT_WAIT_TIME); await this.waitForSelector(e.closeConnectionStatusModal, ELEMENT_WAIT_TIME);
await this.page.evaluate(utilB.clickTestElement, e.closeConnectionStatusModal); await this.page.evaluate(clickElement, e.closeConnectionStatusModal);
await sleep(2000); await sleep(2000);
const webcamsIsDisabledInDataSaving = await this.page.evaluate(util.countTestElements, e.webcamsIsDisabledInDataSaving) === true; const webcamsIsDisabledInDataSaving = await this.page.evaluate(checkElementLengthDifferentTo, e.webcamsIsDisabledInDataSaving, 0);
return webcamsIsDisabledInDataSaving === true; return webcamsIsDisabledInDataSaving === true;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);
@ -79,11 +79,11 @@ class Status extends Page {
await utilScreenshare.waitForScreenshareContainer(this); await utilScreenshare.waitForScreenshareContainer(this);
await util.connectionStatus(this); await util.connectionStatus(this);
await this.waitForSelector(e.dataSavingScreenshare, ELEMENT_WAIT_TIME); await this.waitForSelector(e.dataSavingScreenshare, ELEMENT_WAIT_TIME);
await this.page.evaluate(utilB.clickTestElement, e.dataSavingScreenshare); await this.page.evaluate(clickElement, e.dataSavingScreenshare);
await this.waitForSelector(e.closeConnectionStatusModal, ELEMENT_WAIT_TIME); await this.waitForSelector(e.closeConnectionStatusModal, ELEMENT_WAIT_TIME);
await this.page.evaluate(utilB.clickTestElement, e.closeConnectionStatusModal); await this.page.evaluate(clickElement, e.closeConnectionStatusModal);
await sleep(2000); await sleep(2000);
const webcamsIsDisabledInDataSaving = await this.page.evaluate(util.countTestElements, e.screenshareLocked) === true; const webcamsIsDisabledInDataSaving = await this.page.evaluate(checkElementLengthEqualTo, e.screenshareLocked, 0);
return webcamsIsDisabledInDataSaving === true; return webcamsIsDisabledInDataSaving === true;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);
@ -100,8 +100,8 @@ class Status extends Page {
await utilScreenshare.waitForScreenshareContainer(this); await utilScreenshare.waitForScreenshareContainer(this);
await util.connectionStatus(this); await util.connectionStatus(this);
await sleep(5000); await sleep(5000);
const connectionStatusItemEmpty = await this.page.evaluate(util.countTestElements, e.connectionStatusItemEmpty) === false; const connectionStatusItemEmpty = await this.page.evaluate(checkElementLengthEqualTo, e.connectionStatusItemEmpty, 0);
const connectionStatusItemUser = await this.page.evaluate(util.countTestElements, e.connectionStatusItemUser) === true; const connectionStatusItemUser = await this.page.evaluate(checkElementLengthDifferentTo, e.connectionStatusItemUser, 0);
return connectionStatusItemUser && connectionStatusItemEmpty; return connectionStatusItemUser && connectionStatusItemEmpty;
} catch (err) { } catch (err) {
await this.logger(err); await this.logger(err);

View File

@ -10,15 +10,10 @@ async function setStatus(test, status) {
await test.click(status, true); await test.click(status, true);
} }
async function countTestElements(element) {
return document.querySelectorAll(element).length !== 0;
}
async function connectionStatus(test) { async function connectionStatus(test) {
await test.click('button[data-test="connectionStatusButton"]', true); await test.click(e.connectionStatusBtn, true);
await test.waitForSelector('div[aria-label="Connection status modal"]', ELEMENT_WAIT_TIME); await test.waitForSelector(e.connectionStatusModal, ELEMENT_WAIT_TIME);
} }
exports.countTestElements = countTestElements;
exports.setStatus = setStatus; exports.setStatus = setStatus;
exports.connectionStatus = connectionStatus; exports.connectionStatus = connectionStatus;

View File

@ -2,6 +2,7 @@ const Page = require('../core/page');
const params = require('../params'); const params = require('../params');
const { USER_LIST_VLIST_BOTS_LISTENING, ELEMENT_WAIT_TIME } = require('../core/constants'); const { USER_LIST_VLIST_BOTS_LISTENING, ELEMENT_WAIT_TIME } = require('../core/constants');
const ue = require('../user/elements'); const ue = require('../user/elements');
const { getElementLength } = require('../core/util')
class VirtualizeList { class VirtualizeList {
constructor() { constructor() {
@ -31,7 +32,7 @@ class VirtualizeList {
async test() { async test() {
try { try {
const USER_LIST_VLIST_VISIBLE_USERS = await this.page1.page.evaluate(async () => await document.querySelectorAll('[data-test^="userListItem"]').length); const USER_LIST_VLIST_VISIBLE_USERS = await this.page1.page.evaluate(getElementLength, ue.anyUser);
const totalNumberOfUsersMongo = await this.page1.page.evaluate(() => { const totalNumberOfUsersMongo = await this.page1.page.evaluate(() => {
const collection = require('/imports/api/users/index.js'); const collection = require('/imports/api/users/index.js');
const users = collection.default._collection.find().count(); const users = collection.default._collection.find().count();

View File

@ -1,8 +1,9 @@
const Page = require('../core/page'); const Page = require('../core/page');
const util = require('./util'); const util = require('./util');
const wle = require('./elements'); const wle = require('./elements');
const { checkElementLengthDifferentTo } = require('../core/util');
const { VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported)
const e = require('../core/elements'); const e = require('../core/elements');
const { ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME } = require('../core/constants'); // core constants (Timeouts vars imported)
class Share extends Page { class Share extends Page {
constructor() { constructor() {
@ -37,7 +38,7 @@ class Share extends Page {
await this.waitForSelector(wle.webcamVideo, VIDEO_LOADING_WAIT_TIME); await this.waitForSelector(wle.webcamVideo, VIDEO_LOADING_WAIT_TIME);
await this.waitForSelector(wle.stopSharingWebcam, VIDEO_LOADING_WAIT_TIME); await this.waitForSelector(wle.stopSharingWebcam, VIDEO_LOADING_WAIT_TIME);
await this.waitForSelector(e.isTalking); await this.waitForSelector(e.isTalking);
const foundTestElement = await this.page.evaluate(util.countTestElements, wle.webcamItemTalkingUser) !== 0; const foundTestElement = await this.page.evaluate(checkElementLengthDifferentTo, wle.webcamItemTalkingUser, 0);
if (foundTestElement === true) { if (foundTestElement === true) {
await this.screenshot(`${testName}`, `success-${testName}`); await this.screenshot(`${testName}`, `success-${testName}`);
this.logger(testName, ' passed'); this.logger(testName, ' passed');

View File

@ -1,32 +1,29 @@
const we = require('./elements'); const we = require('./elements');
const { sleep } = require('../core/helper'); const { sleep } = require('../core/helper');
const { checkElement, clickElement , checkElementLengthDifferentTo } = require('../core/util');
const { const {
LOOP_INTERVAL, ELEMENT_WAIT_TIME, VIDEO_LOADING_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME, LOOP_INTERVAL,
ELEMENT_WAIT_TIME,
VIDEO_LOADING_WAIT_TIME,
ELEMENT_WAIT_LONGER_TIME,
} = require('../core/constants'); } = require('../core/constants');
async function enableWebcam(test, videoPreviewTimeout) { async function enableWebcam(test, videoPreviewTimeout) {
// Enabling webcam // Enabling webcam
await test.waitForSelector(we.joinVideo, ELEMENT_WAIT_TIME); await test.waitForSelector(we.joinVideo, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, we.joinVideo); await test.page.evaluate(clickElement, we.joinVideo);
await test.waitForSelector(we.videoPreview, videoPreviewTimeout); await test.waitForSelector(we.videoPreview, videoPreviewTimeout);
await test.waitForSelector(we.startSharingWebcam, ELEMENT_WAIT_TIME); await test.waitForSelector(we.startSharingWebcam, ELEMENT_WAIT_TIME);
await test.page.evaluate(clickTestElement, we.startSharingWebcam); await test.page.evaluate(clickElement, we.startSharingWebcam);
await test.waitForSelector(we.webcamConnecting, ELEMENT_WAIT_TIME); await test.waitForSelector(we.webcamConnecting, ELEMENT_WAIT_TIME);
await test.waitForSelector(we.webcamVideo, VIDEO_LOADING_WAIT_TIME); await test.waitForSelector(we.webcamVideo, VIDEO_LOADING_WAIT_TIME);
await test.waitForSelector(we.leaveVideo, VIDEO_LOADING_WAIT_TIME); await test.waitForSelector(we.leaveVideo, VIDEO_LOADING_WAIT_TIME);
const resp = await test.page.evaluate(countTestElements, we.webcamVideo) !== 0; return test.page.evaluate(checkElementLengthDifferentTo, we.webcamVideo, 0);
return resp;
}
async function getFullScreenWebcamButton(element) {
return await document.querySelectorAll(element)[1] !== null;
} }
async function evaluateCheck(test) { async function evaluateCheck(test) {
await test.waitForSelector(we.videoContainer, ELEMENT_WAIT_TIME); await test.waitForSelector(we.videoContainer, ELEMENT_WAIT_TIME);
const videoContainer = await test.page.evaluate(getFullScreenWebcamButton, we.presentationFullscreenButton); return test.page.evaluate(checkElement, we.presentationFullscreenButton, 1);
const response = videoContainer !== false;
return response;
} }
async function startAndCheckForWebcams(test) { async function startAndCheckForWebcams(test) {
@ -72,11 +69,6 @@ async function webcamContentCheck(test) {
return check === true; return check === true;
} }
async function clickTestElement(element) {
document.querySelectorAll(element)[0].click();
}
async function countTestElements(element) { async function countTestElements(element) {
const respCount = await document.querySelectorAll(element).length; const respCount = await document.querySelectorAll(element).length;
return respCount; return respCount;
@ -85,6 +77,5 @@ async function countTestElements(element) {
exports.startAndCheckForWebcams = startAndCheckForWebcams; exports.startAndCheckForWebcams = startAndCheckForWebcams;
exports.webcamContentCheck = webcamContentCheck; exports.webcamContentCheck = webcamContentCheck;
exports.evaluateCheck = evaluateCheck; exports.evaluateCheck = evaluateCheck;
exports.getFullScreenWebcamButton = getFullScreenWebcamButton;
exports.enableWebcam = enableWebcam; exports.enableWebcam = enableWebcam;
exports.countTestElements = countTestElements; exports.countTestElements = countTestElements;

2
record-and-playback/core/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.bundle
vendor/bundle

View File

@ -34,7 +34,9 @@ gem 'rubyzip', '~> 2.0'
gem 'trollop', '2.1.3' gem 'trollop', '2.1.3'
gem 'resque', '~> 2.0.0' gem 'resque', '~> 2.0.0'
gem 'bbbevents', '~> 1.2' gem 'bbbevents', '~> 1.2'
gem 'rake', '>= 12.3', '<14'
group :test, optional: true do group :test, optional: true do
gem 'rubocop', '~> 0.79.0' gem 'rubocop', '~> 0.79.0'
gem 'minitest', '~> 5.14.1'
end end

View File

@ -45,6 +45,7 @@ GEM
rack-protection (2.0.8.1) rack-protection (2.0.8.1)
rack rack
rainbow (3.0.0) rainbow (3.0.0)
rake (13.0.6)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
redis (4.1.3) redis (4.1.3)
@ -93,8 +94,10 @@ DEPENDENCIES
jwt (~> 2.2) jwt (~> 2.2)
locale (~> 2.1) locale (~> 2.1)
loofah (~> 2.3) loofah (~> 2.3)
minitest (~> 5.14.1)
nokogiri (~> 1.11) nokogiri (~> 1.11)
open4 (~> 1.3) open4 (~> 1.3)
rake (>= 12.3, < 14)
rb-inotify (~> 0.10) rb-inotify (~> 0.10)
redis (~> 4.1) redis (~> 4.1)
resque (~> 2.0.0) resque (~> 2.0.0)

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'resque/tasks' require 'resque/tasks'
require 'rake/testtask'
task 'resque:setup' => :environment do task 'resque:setup' => :environment do
props = BigBlueButton.read_props props = BigBlueButton.read_props
@ -17,3 +18,9 @@ task :environment do
require_relative 'lib/boot' require_relative 'lib/boot'
require 'recordandplayback/workers' require 'recordandplayback/workers'
end end
Rake::TestTask.new(:test) do |t|
t.libs << 'test'
t.libs << 'lib'
t.test_files = FileList['test/**/test_*.rb']
end

View File

@ -21,13 +21,14 @@
require 'rubygems' require 'rubygems'
require 'time'
require 'nokogiri' require 'nokogiri'
require 'loofah' require 'loofah'
require 'set' require 'set'
module BigBlueButton module BigBlueButton
module Events module Events
# Get the total number of participants # Get the total number of participants
def self.get_num_participants(events) def self.get_num_participants(events)
participants_ids = Set.new participants_ids = Set.new
@ -75,31 +76,31 @@ module BigBlueButton
external_meeting_id = metadata['meetingId'] if !metadata['meetingId'].nil? external_meeting_id = metadata['meetingId'] if !metadata['meetingId'].nil?
external_meeting_id external_meeting_id
end end
# Get the timestamp of the first event. # Get the timestamp of the first event.
def self.first_event_timestamp(events) def self.first_event_timestamp(events)
first_event = events.at_xpath('/recording/event[position() = 1]') first_event = events.at_xpath('/recording/event[position() = 1]')
first_event['timestamp'].to_i if first_event && first_event.key?('timestamp') first_event['timestamp'].to_i if first_event && first_event.key?('timestamp')
end end
# Get the timestamp of the last event. # Get the timestamp of the last event.
def self.last_event_timestamp(events) def self.last_event_timestamp(events)
last_event = events.at_xpath('/recording/event[position() = last()]') last_event = events.at_xpath('/recording/event[position() = last()]')
last_event['timestamp'].to_i if last_event && last_event.key?('timestamp') last_event['timestamp'].to_i if last_event && last_event.key?('timestamp')
end end
# Determine if the start and stop event matched. # Determine if the start and stop event matched.
def self.find_video_event_matched(start_events, stop) def self.find_video_event_matched(start_events, stop)
BigBlueButton.logger.info("Task: Finding video events that match") BigBlueButton.logger.info("Task: Finding video events that match")
start_events.each do |start| start_events.each do |start|
if (start[:stream] == stop[:stream]) if (start[:stream] == stop[:stream])
return start return start
end end
end end
return nil return nil
end end
# Get start video events # Get start video events
def self.get_start_video_events(events) def self.get_start_video_events(events)
start_events = [] start_events = []
events.xpath("/recording/event[@eventname='StartWebcamShareEvent']").each do |start_event| events.xpath("/recording/event[@eventname='StartWebcamShareEvent']").each do |start_event|
@ -235,7 +236,7 @@ module BigBlueButton
end end
def self.get_stop_deskshare_events(events) def self.get_stop_deskshare_events(events)
BigBlueButton.logger.info("Task: Getting stop DESKSHARE events") BigBlueButton.logger.info("Task: Getting stop DESKSHARE events")
stop_events = [] stop_events = []
events.xpath('/recording/event[@module="Deskshare" or (@module="bbb-webrtc-sfu" and @eventname="StopWebRTCDesktopShareEvent")]').each do |stop_event| events.xpath('/recording/event[@module="Deskshare" or (@module="bbb-webrtc-sfu" and @eventname="StopWebRTCDesktopShareEvent")]').each do |stop_event|
case stop_event['eventname'] case stop_event['eventname']
@ -476,12 +477,134 @@ module BigBlueButton
node['href'] = node['href'][6..-1] if node.name == 'a' && node['href'] && node['href'].start_with?('event:') node['href'] = node['href'][6..-1] if node.name == 'a' && node['href'] && node['href'].start_with?('event:')
end end
def self.linkify( text ) def self.linkify(text)
html = Loofah.fragment(text) html = Loofah.fragment(text)
html.scrub!(@remove_link_event_prefix).scrub!(:strip).scrub!(:nofollow).scrub!(:unprintable) html.scrub!(@remove_link_event_prefix).scrub!(:strip).scrub!(:nofollow).scrub!(:unprintable)
html.to_html html.to_html
end end
# Build a map of internal user IDs to anonymized names. This can be used to anonymize users in
# chat, cursor overlays, etc.
def self.anonymous_user_map(events, moderators: false)
viewer_count = 0
moderator_count = 0
external_map = {}
map = {}
events.xpath('/recording/event[@module="PARTICIPANT" and @eventname="ParticipantJoinEvent"]').each do |event|
internal_id = event.at_xpath('./userId')&.content
next if internal_id.nil?
external_id = event.at_xpath('./externalUserId')&.content || internal_id
name = external_map.fetch(external_id) do
role = event.at_xpath('./role').content
new_name = \
if role == 'MODERATOR' && moderators
moderator_count += 1
"Moderator #{moderator_count}"
elsif role == 'MODERATOR'
event.at_xpath('./name')&.content
else
viewer_count += 1
"Viewer #{viewer_count}"
end
external_map[external_id] = new_name unless new_name.nil?
end
map[internal_id] = name unless name.nil?
end
map
end
# Get a list of chat events, with start/end time for segments and recording marks applied.
# Optionally anonymizes chat participant names.
# Reads the keys 'anonymize_chat' and 'anonymize_chat_moderators' from bbb_props, but allows
# per-meeting override using the create meta params 'meta_bbb-anonymize-chat' and
# 'meta_bbb-anonymize-chat-moderators'
# Each event in the return value has the following properties:
# in: 0-based milliseconds timestamp of when chat was sent
# out: 0-based milliseconds timestamp of when chat was cleared (or nil if chat was never cleared)
# sender_id: The internal user id of the sender (can be nil on really old BBB versions)
# sender: The display name of the sender
# message: The chat message, with link cleanup already applied
# date: The real time of when the message was sent (if available) as a DateTime
# text_color: The RGB color value of the chat message text as an integer (old BBB versions only)
# avatar_color: The color of the user's avatar (initials) box (newer BBB versions only)
def self.get_chat_events(events, start_time, end_time, bbb_props = {})
BigBlueButton.logger.info('Getting chat events')
initial_timestamp = first_event_timestamp(events)
start_time -= initial_timestamp
end_time -= initial_timestamp
last_stop_timestamp = start_time
offset = start_time
# Recordings without status events are assumed to have been recorded from the beginning
record = events.at_xpath('/recording/event[@eventname="RecordStatusEvent"]').nil?
# Load the anonymize settings; defaults from bigbluebutton.yml, override with meta params
metadata = events.at_xpath('/recording/metadata')
anonymize_senders = metadata['bbb-anonymize-chat'] unless metadata.nil?
anonymize_senders = bbb_props['anonymize_chat'] if anonymize_senders.nil?
anonymize_senders = anonymize_senders.to_s.casecmp?('true')
anonymize_moderators = metadata['bbb-anonymize-chat-moderators'] unless metadata.nil?
anonymize_moderators = bbb_props['anonymize_chat_moderators'] if anonymize_moderators.nil?
anonymize_moderators = anonymize_moderators.to_s.casecmp?('true')
user_map = anonymize_senders ? anonymous_user_map(events, moderators: anonymize_moderators) : {}
chats = []
events.xpath('/recording/event').each do |event|
timestamp = event[:timestamp].to_i - initial_timestamp
break if timestamp >= end_time
case [event[:module], event[:eventname]]
when %w[CHAT PublicChatEvent]
next if timestamp < start_time || !record
date = event.at_xpath('./date')&.content
date = DateTime.iso8601(date) unless date.nil?
sender_id = event.at_xpath('./senderId')&.content
color = event.at_xpath('./color')&.content
if color&.start_with?('#')
avatar_color = color
else
text_color = color.to_i
end
chats << {
in: timestamp - offset,
out: nil,
sender_id: sender_id,
sender: user_map.fetch(sender_id) { event.at_xpath('./sender').content },
message: linkify(event.at_xpath('./message').content.strip),
avatar_color: avatar_color,
text_color: text_color,
date: date,
}
when %w[CHAT ClearPublicChatEvent]
next if timestamp < start_time
clear_timestamp = (record ? timestamp : last_stop_timestamp) - offset
chats.each do |chat|
chat[:out] = clear_timestamp if chat[:out].nil?
end
when %w[PARTICIPANT RecordStatusEvent]
record = event.at_xpath('status').content == 'true'
next if timestamp < start_time
if record
offset += timestamp - last_stop_timestamp
else
last_stop_timestamp = timestamp
end
end
end
chats
end
def self.get_record_status_events(events_xml) def self.get_record_status_events(events_xml)
BigBlueButton.logger.info "Getting record status events" BigBlueButton.logger.info "Getting record status events"
rec_events = [] rec_events = []
@ -496,8 +619,8 @@ module BigBlueButton
BigBlueButton.logger.info "Getting external video events" BigBlueButton.logger.info "Getting external video events"
external_videos_events = [] external_videos_events = []
events_xml.xpath("//event[@eventname='StartExternalVideoRecordEvent']").each do |event| events_xml.xpath("//event[@eventname='StartExternalVideoRecordEvent']").each do |event|
s = { s = {
:timestamp => event['timestamp'].to_i, :timestamp => event['timestamp'].to_i,
:external_video_url => event.at_xpath("externalVideoUrl").text :external_video_url => event.at_xpath("externalVideoUrl").text
} }
external_videos_events << s external_videos_events << s
@ -523,7 +646,7 @@ module BigBlueButton
end end
rec_events.sort_by {|a| a[:timestamp]} rec_events.sort_by {|a| a[:timestamp]}
end end
# Get events when the moderator wants the recording to start or stop # Get events when the moderator wants the recording to start or stop
def self.get_start_and_stop_external_video_events(events_xml) def self.get_start_and_stop_external_video_events(events_xml)
BigBlueButton.logger.info "Getting start and stop externalvideo events" BigBlueButton.logger.info "Getting start and stop externalvideo events"
@ -534,7 +657,7 @@ module BigBlueButton
end end
external_video_events.sort_by {|a| a[:timestamp]} external_video_events.sort_by {|a| a[:timestamp]}
end end
# Match recording start and stop events # Match recording start and stop events
def self.match_start_and_stop_rec_events(rec_events) def self.match_start_and_stop_rec_events(rec_events)
BigBlueButton.logger.info ("Matching record events") BigBlueButton.logger.info ("Matching record events")

View File

@ -0,0 +1,422 @@
<?xml version="1.0" encoding="UTF-8"?>
<recording meeting_id="2a1de53edf0543d950056bf3c0d4d357eba3383f-1630607370684" bbb_version="2.1.0">
<meeting id="2a1de53edf0543d950056bf3c0d4d357eba3383f-1630607370684" externalId="random-9678161_calvin_" name="random-9678161" breakout="false"/>
<metadata bbb-anonymize-chat="true" isBreakout="false" meetingId="random-9678161_calvin_" meetingName="random-9678161"/>
<event timestamp="1131799575" module="PRESENTATION" eventname="CreatePresentationPodEvent">
<currentPresenter/>
<timestampUTC>1630607370704</timestampUTC>
<podId>DEFAULT_PRESENTATION_POD</podId>
<date>2021-09-02T14:29:30.704-04</date>
</event>
<event timestamp="1131799900" module="PAD" eventname="AddPadEvent">
<timestampUTC>1630607371029</timestampUTC>
<date>2021-09-02T14:29:31.029-04</date>
<padId>[2]cfe107fd5780210103d9d4017f8c751d5594</padId>
</event>
<event timestamp="1131803050" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>User 7520456</name>
<timestampUTC>1630607374179</timestampUTC>
<role>MODERATOR</role>
<date>2021-09-02T14:29:34.179-04</date>
<externalUserId>w_jw2fcjeovwa6</externalUserId>
<userId>w_jw2fcjeovwa6</userId>
</event>
<event timestamp="1131803051" module="PARTICIPANT" eventname="AssignPresenterEvent">
<name>User 7520456</name>
<timestampUTC>1630607374180</timestampUTC>
<userid>w_jw2fcjeovwa6</userid>
<assignedBy>w_jw2fcjeovwa6</assignedBy>
<date>2021-09-02T14:29:34.180-04</date>
</event>
<event timestamp="1131803066" module="PARTICIPANT" eventname="AssignPresenterEvent">
<name>User 7520456</name>
<timestampUTC>1630607374195</timestampUTC>
<userid>w_jw2fcjeovwa6</userid>
<assignedBy>w_jw2fcjeovwa6</assignedBy>
<date>2021-09-02T14:29:34.195-04</date>
</event>
<event timestamp="1131803067" module="PRESENTATION" eventname="SetPresenterInPodEvent">
<timestampUTC>1630607374196</timestampUTC>
<podId>DEFAULT_PRESENTATION_POD</podId>
<date>2021-09-02T14:29:34.196-04</date>
<nextPresenterId>w_jw2fcjeovwa6</nextPresenterId>
</event>
<event timestamp="1131803071" module="PARTICIPANT" eventname="AssignPresenterEvent">
<name>User 7520456</name>
<timestampUTC>1630607374200</timestampUTC>
<userid>w_jw2fcjeovwa6</userid>
<assignedBy>w_jw2fcjeovwa6</assignedBy>
<date>2021-09-02T14:29:34.200-04</date>
</event>
<event timestamp="1131803072" module="PRESENTATION" eventname="SetPresenterInPodEvent">
<timestampUTC>1630607374201</timestampUTC>
<podId>DEFAULT_PRESENTATION_POD</podId>
<date>2021-09-02T14:29:34.201-04</date>
<nextPresenterId>w_jw2fcjeovwa6</nextPresenterId>
</event>
<event timestamp="1131805474" module="PRESENTATION" eventname="ConversionCompletedEvent">
<originalFilename>default.pdf</originalFilename>
<timestampUTC>1630607376603</timestampUTC>
<presentationName>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentationName>
<podId>DEFAULT_PRESENTATION_POD</podId>
<date>2021-09-02T14:29:36.603-04</date>
</event>
<event timestamp="1131805477" module="PRESENTATION" eventname="SharePresentationEvent">
<timestampUTC>1630607376606</timestampUTC>
<presentationName>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentationName>
<podId>DEFAULT_PRESENTATION_POD</podId>
<share>true</share>
<date>2021-09-02T14:29:36.606-04</date>
</event>
<event timestamp="1131805479" module="PRESENTATION" eventname="SetPresentationDownloadable">
<timestampUTC>1630607376608</timestampUTC>
<presentationName>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentationName>
<podId>DEFAULT_PRESENTATION_POD</podId>
<date>2021-09-02T14:29:36.608-04</date>
<downloadable>false</downloadable>
</event>
<event timestamp="1131824162" module="VOICE" eventname="ParticipantJoinedEvent">
<participant>w_jw2fcjeovwa6</participant>
<timestampUTC>1630607395291</timestampUTC>
<bridge>578447737</bridge>
<callername>User 7520456</callername>
<talking>false</talking>
<callernumber>w_jw2fcjeovwa6_1-bbbID-User 7520456</callernumber>
<date>2021-09-02T14:29:55.291-04</date>
<muted>false</muted>
</event>
<event timestamp="1131824186" module="VOICE" eventname="StartRecordingEvent">
<filename>/var/freeswitch/meetings/2a1de53edf0543d950056bf3c0d4d357eba3383f-1630607370684-1131824162.opus</filename>
<bridge>578447737</bridge>
<timestampUTC>1630607395314</timestampUTC>
<date>2021-09-02T14:29:55.314-04</date>
<recordingTimestamp>1131824184</recordingTimestamp>
</event>
<event timestamp="1131824226" module="VOICE" eventname="ParticipantTalkingEvent">
<participant>w_jw2fcjeovwa6</participant>
<timestampUTC>1630607395355</timestampUTC>
<bridge>578447737</bridge>
<talking>true</talking>
<date>2021-09-02T14:29:55.355-04</date>
</event>
<event timestamp="1131825122" module="VOICE" eventname="ParticipantMutedEvent">
<participant>w_jw2fcjeovwa6</participant>
<timestampUTC>1630607396250</timestampUTC>
<bridge>578447737</bridge>
<date>2021-09-02T14:29:56.250-04</date>
<muted>true</muted>
</event>
<event timestamp="1131825327" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607396456</timestampUTC>
<xOffset>42.81167030334473</xOffset>
<date>2021-09-02T14:29:56.456-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>87.12742558232061</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131825473" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607396602</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:29:56.602-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131834887" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607406015</timestampUTC>
<xOffset>34.58885828653971</xOffset>
<date>2021-09-02T14:30:06.015-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>75.1026690447772</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131835039" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607406168</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:30:06.168-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131839891" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607411020</timestampUTC>
<xOffset>40.557028452555336</xOffset>
<date>2021-09-02T14:30:11.020-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>0.8321106875384296</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131840041" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607411169</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:30:11.169-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131841537" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607412665</timestampUTC>
<xOffset>50.63660303751628</xOffset>
<date>2021-09-02T14:30:12.665-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>9.084394949453849</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131841691" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607412819</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:30:12.819-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131841840" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607412969</timestampUTC>
<xOffset>36.57824834187826</xOffset>
<date>2021-09-02T14:30:12.969-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>33.60546818485967</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131842003" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607413131</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:30:13.131-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131882243" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>(guest) Calvin</name>
<timestampUTC>1630607453372</timestampUTC>
<role>VIEWER</role>
<date>2021-09-02T14:30:53.372-04</date>
<externalUserId>3ff859e3-1c2e-48e9-860d-cf230648cfca</externalUserId>
<userId>w_tvumguamxhhs</userId>
</event>
<event timestamp="1131885821" module="VOICE" eventname="ParticipantJoinedEvent">
<participant>w_tvumguamxhhs</participant>
<timestampUTC>1630607456950</timestampUTC>
<bridge>578447737</bridge>
<callername>(guest) Calvin</callername>
<talking>false</talking>
<callernumber>(guest) Calvin</callernumber>
<date>2021-09-02T14:30:56.950-04</date>
<muted>true</muted>
</event>
<event timestamp="1131895190" module="CHAT" eventname="PublicChatEvent">
<timestampUTC>1630607466319</timestampUTC>
<color>#6a1b9a</color>
<senderId>w_jw2fcjeovwa6</senderId>
<date>2021-09-02T14:31:06.319-04</date>
<sender>User 7520456</sender>
<message>Hello, moderator chat message!</message>
</event>
<event timestamp="1131905701" module="CHAT" eventname="PublicChatEvent">
<timestampUTC>1630607476829</timestampUTC>
<color>#4a148c</color>
<senderId>w_tvumguamxhhs</senderId>
<date>2021-09-02T14:31:16.829-04</date>
<sender>(guest) Calvin</sender>
<message>Hello, guest chat message!</message>
</event>
<event timestamp="1131908051" module="CHAT" eventname="PublicChatEvent">
<timestampUTC>1630607479180</timestampUTC>
<color>#4a148c</color>
<senderId>w_tvumguamxhhs</senderId>
<date>2021-09-02T14:31:19.180-04</date>
<sender>(guest) Calvin</sender>
<message>yay!</message>
</event>
<event timestamp="1131949647" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607520776</timestampUTC>
<xOffset>10.185674826304119</xOffset>
<date>2021-09-02T14:32:00.776-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>96.79438838252314</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131949798" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607520927</timestampUTC>
<xOffset>14.297080039978027</xOffset>
<date>2021-09-02T14:32:00.927-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>90.1925602665654</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131949893" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607521022</timestampUTC>
<xOffset>14.297080039978027</xOffset>
<date>2021-09-02T14:32:01.022-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>89.95677806712963</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131950258" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607521387</timestampUTC>
<xOffset>14.297080039978027</xOffset>
<date>2021-09-02T14:32:01.387-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>90.1925602665654</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131950408" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607521537</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:32:01.537-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131955635" module="CHAT" eventname="ClearPublicChatEvent">
<timestampUTC>1630607526763</timestampUTC>
<date>2021-09-02T14:32:06.763-04</date>
</event>
<event timestamp="1131964123" module="CHAT" eventname="PublicChatEvent">
<timestampUTC>1630607535252</timestampUTC>
<color>#6a1b9a</color>
<senderId>w_jw2fcjeovwa6</senderId>
<date>2021-09-02T14:32:15.252-04</date>
<sender>User 7520456</sender>
<message>Chat was cleared</message>
</event>
<event timestamp="1131994645" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607565774</timestampUTC>
<xOffset>43.34217389424642</xOffset>
<date>2021-09-02T14:32:45.774-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>96.55861183449073</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131994767" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607565896</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:32:45.896-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131997197" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607568326</timestampUTC>
<xOffset>32.20158894856771</xOffset>
<date>2021-09-02T14:32:48.326-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>96.55861183449073</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131997346" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607568475</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:32:48.475-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1131999450" module="PARTICIPANT" eventname="RecordStatusEvent">
<timestampUTC>1630607570579</timestampUTC>
<date>2021-09-02T14:32:50.579-04</date>
<status>true</status>
<userId>w_jw2fcjeovwa6</userId>
</event>
<event timestamp="1131999466" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607570595</timestampUTC>
<xOffset>13.501324653625488</xOffset>
<date>2021-09-02T14:32:50.595-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>59.77699562355324</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1132001481" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607572610</timestampUTC>
<xOffset>13.103446960449219</xOffset>
<date>2021-09-02T14:32:52.610-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>59.77699562355324</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1132001799" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607572927</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:32:52.927-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1132011085" module="CHAT" eventname="PublicChatEvent">
<timestampUTC>1630607582214</timestampUTC>
<color>#4a148c</color>
<senderId>w_tvumguamxhhs</senderId>
<date>2021-09-02T14:33:02.214-04</date>
<sender>(guest) Calvin</sender>
<message>whoops, forgot to start recording…</message>
</event>
<event timestamp="1132049253" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607620382</timestampUTC>
<xOffset>36.180369059244796</xOffset>
<date>2021-09-02T14:33:40.382-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>97.9732824254919</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1132049403" module="WHITEBOARD" eventname="WhiteboardCursorMoveEvent">
<timestampUTC>1630607620531</timestampUTC>
<xOffset>-62.891248067220054</xOffset>
<date>2021-09-02T14:33:40.531-04</date>
<whiteboardId>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702/1</whiteboardId>
<pageNumber>0</pageNumber>
<userId>w_jw2fcjeovwa6</userId>
<yOffset>-60.94213415075232</yOffset>
<presentation>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1630607370702</presentation>
</event>
<event timestamp="1132053637" module="PARTICIPANT" eventname="EndAndKickAllEvent">
<timestampUTC>1630607624766</timestampUTC>
<reason>ENDED_AFTER_USER_LOGGED_OUT</reason>
<date>2021-09-02T14:33:44.766-04</date>
</event>
</recording>

View File

@ -0,0 +1,344 @@
<?xml version="1.0" encoding="UTF-8"?>
<recording meeting_id="afa22bf4e2a55835006a0016776f700dcf8e981e-1630339972856" bbb_version="0.9.0">
<meeting
id="afa22bf4e2a55835006a0016776f700dcf8e981e-1630339972856"
externalId="chat_0_9" name="Chat 0.9 Test"
breakout="false"/>
<metadata
meetingName="Chat 0.9 Test"
meetingId="chat_0_9"
isBreakout="false"/>
<event timestamp="1061316615" module="PRESENTATION" eventname="SharePresentationEvent">
<presentationName>d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1503524575790</presentationName>
<share>true</share>
<originalFilename>default.pdf</originalFilename>
</event>
<event timestamp="1061329979" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>Marinda Collins</name>
<role>MODERATOR</role>
<userId>9izxq660i7vr_1</userId>
<externalUserId>1000</externalUserId>
</event>
<event timestamp="1061397065" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<userId>vvyha6umxoyt_1</userId>
<externalUserId>1001</externalUserId>
<name>Phelix Fishman</name>
<role>VIEWER</role>
</event>
<event timestamp="1061511972" module="PARTICIPANT" eventname="RecordStatusEvent">
<userId>9izxq660i7vr_1</userId>
<status>true</status>
</event>
<event timestamp="1061536072" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<role>VIEWER</role>
<externalUserId>1002</externalUserId>
<userId>hs7iskkr7xrt_1</userId>
<name>Isaías Seelen</name>
</event>
<event timestamp="1061574575" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<role>VIEWER</role>
<userId>7m940cic73r3_1</userId>
<name>Mireia Castell</name>
<externalUserId>1003</externalUserId>
</event>
<event timestamp="1061629575" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<role>VIEWER</role>
<externalUserId>1004</externalUserId>
<userId>tgfbj6f828sp_1</userId>
<name>Liborius Hayes</name>
</event>
<event timestamp="1061703579" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<role>VIEWER</role>
<externalUserId>1005</externalUserId>
<userId>bepguk6d7dza_1</userId>
<name>Eva Aquino</name>
</event>
<event timestamp="1061749302" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<role>VIEWER</role>
<externalUserId>1006</externalUserId>
<userId>66ntqzexswc2_1</userId>
<name>Rodge Palazzo</name>
</event>
<event timestamp="1061825270" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<externalUserId>1007</externalUserId>
<role>VIEWER</role>
<name>Elias Stablum</name>
<userId>0q1hkmla9asu_1</userId>
</event>
<event timestamp="1061881172" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<userId>yyynfpyca09g_1</userId>
<externalUserId>1008</externalUserId>
<role>VIEWER</role>
<name>Evelina Keller</name>
</event>
<event timestamp="1061933105" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<userId>dmsj3897dwss_1</userId>
<role>VIEWER</role>
<name>Xhesika De Lange</name>
<externalUserId>1009</externalUserId>
</event>
<event timestamp="1061952342" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>Nimue Harlan</name>
<role>VIEWER</role>
<externalUserId>1010</externalUserId>
<userId>42dnty7rovjt_1</userId>
</event>
<event timestamp="1061962737" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>Elpidio O'Gorman</name>
<role>VIEWER</role>
<externalUserId>1011</externalUserId>
<userId>7ur69btts657_1</userId>
</event>
<event timestamp="1061998179" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>dmsj3897dwss_1</userId>
</event>
<event timestamp="1062008158" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>66ntqzexswc2_1</userId>
</event>
<event timestamp="1062013094" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<externalUserId>1012</externalUserId>
<role>VIEWER</role>
<userId>23uydbo9nauq_1</userId>
<name>Asa Darby</name>
</event>
<event timestamp="1062017073" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<externalUserId>1006</externalUserId>
<userId>12ipastd9pw1_1</userId>
<role>VIEWER</role>
<name>Rodge Palazzo</name>
</event>
<event timestamp="1062026555" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>Xhesika De Lange</name>
<role>VIEWER</role>
<externalUserId>1009</externalUserId>
<userId>ainnu65fiycz_1</userId>
</event>
<event timestamp="1062058796" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<externalUserId>1013</externalUserId>
<role>VIEWER</role>
<name>Arethusa Mann</name>
<userId>j73nq5k8xcaa_1</userId>
</event>
<event timestamp="1062142085" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<name>Ninel Mac Ruaidhrí</name>
<role>VIEWER</role>
<userId>nfuklna24flg_1</userId>
<externalUserId>1014</externalUserId>
</event>
<event timestamp="1062159696" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>ainnu65fiycz_1</userId>
</event>
<event timestamp="1062170527" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<externalUserId>1009</externalUserId>
<name>Xhesika De Lange</name>
<role>VIEWER</role>
<userId>2dc8jctma0nj_1</userId>
</event>
<event timestamp="1062260997" module="CHAT" eventname="PublicChatEvent">
<sender>Xhesika De Lange</sender>
<senderId>2dc8jctma0nj_1</senderId>
<message>
<![CDATA[Public chat 1]]>
</message>
<color>0</color>
</event>
<event timestamp="1062483994" module="CHAT" eventname="PublicChatEvent">
<color>0</color>
<senderId>23uydbo9nauq_1</senderId>
<message>
<![CDATA[Public chat 2]]>
</message>
<sender>Asa Darby</sender>
</event>
<event timestamp="1062500000" module="CHAT" eventname="ClearPublicChatEvent">
</event>
<event timestamp="1062914576" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>j73nq5k8xcaa_1</userId>
</event>
<event timestamp="1063007465" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<userId>fzlsahcijxo4_1</userId>
<externalUserId>1013</externalUserId>
<role>VIEWER</role>
<name>Arethusa Mann</name>
</event>
<event timestamp="1063309296" module="CHAT" eventname="PublicChatEvent">
<color>0</color>
<senderId>hs7iskkr7xrt_1</senderId>
<message>
<![CDATA[Public chat 3]]>
</message>
<sender>Isaías Seelen</sender>
</event>
<event timestamp="1064118099" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>fzlsahcijxo4_1</userId>
</event>
<event timestamp="1064118099" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<userId>fzlsahcijxo4_1</userId>
<externalUserId>1013</externalUserId>
<name>Arethusa Mann</name>
<role>VIEWER</role>
</event>
<event timestamp="1064123456" module="PARTICIPANT" eventname="RecordStatusEvent">
<userId>9izxq660i7vr_1</userId>
<status>false</status>
</event>
<event timestamp="1064181418" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>hs7iskkr7xrt_1</userId>
</event>
<event timestamp="1064370077" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<externalUserId>1002</externalUserId>
<role>VIEWER</role>
<userId>y096bmb53yu5_1</userId>
<name>Isaías Seelen</name>
</event>
<event timestamp="1064621935" module="CHAT" eventname="PublicChatEvent">
<color>0</color>
<senderId>nfuklna24flg_1</senderId>
<message>
<![CDATA[Public chat 4]]>
</message>
<sender>Ninel Mac Ruaidhrí</sender>
</event>
<event timestamp="1064635147" module="CHAT" eventname="PublicChatEvent">
<senderId>bepguk6d7dza_1</senderId>
<sender>Eva Aquino</sender>
<message>
<![CDATA[Public chat 5]]>
</message>
<color>0</color>
</event>
<event timestamp="1064645678" module="PARTICIPANT" eventname="RecordStatusEvent">
<userId>9izxq660i7vr_1</userId>
<status>true</status>
</event>
<event timestamp="1064656291" module="CHAT" eventname="PublicChatEvent">
<sender>Elias Stablum</sender>
<senderId>0q1hkmla9asu_1</senderId>
<color>0</color>
<message>
<![CDATA[Public chat 6]]>
</message>
</event>
<event timestamp="1064660118" module="CHAT" eventname="PublicChatEvent">
<senderId>fzlsahcijxo4_1</senderId>
<message>
<![CDATA[Public chat 7]]>
</message>
<sender>Arethusa Mann</sender>
<color>0</color>
</event>
<event timestamp="1064669521" module="CHAT" eventname="PublicChatEvent">
<message>
<![CDATA[Public chat 8]]>
</message>
<senderId>nfuklna24flg_1</senderId>
<sender>Ninel Mac Ruaidhrí</sender>
<color>0</color>
</event>
<event timestamp="1064671034" module="CHAT" eventname="PublicChatEvent">
<message>
<![CDATA[Public chat 9]]>
</message>
<color>0</color>
<sender>Mireia Castell</sender>
<senderId>7m940cic73r3_1</senderId>
</event>
<event timestamp="1064679602" module="CHAT" eventname="PublicChatEvent">
<sender>Ninel Mac Ruaidhrí</sender>
<message>
<![CDATA[Public chat 10]]>
</message>
<color>0</color>
<senderId>nfuklna24flg_1</senderId>
</event>
<event timestamp="1066752701" module="CHAT" eventname="PublicChatEvent">
<sender>Xhesika De Lange</sender>
<color>0</color>
<senderId>2dc8jctma0nj_1</senderId>
<message>
<![CDATA[Public chat 11]]>
</message>
</event>
<event timestamp="1066777963" module="CHAT" eventname="PublicChatEvent">
<sender>Arethusa Mann</sender>
<senderId>fzlsahcijxo4_1</senderId>
<color>0</color>
<message>
<![CDATA[Public chat 12]]>
</message>
</event>
<event timestamp="1066909573" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<userId>y096bmb53yu5_2</userId>
<role>VIEWER</role>
<name>Isaías Seelen</name>
<externalUserId>1002</externalUserId>
</event>
<event timestamp="1066966371" module="CHAT" eventname="PublicChatEvent">
<message>
<![CDATA[Public chat 13]]>
</message>
<color>0</color>
<senderId>2dc8jctma0nj_1</senderId>
<sender>Xhesika De Lange</sender>
</event>
<event timestamp="1069500636" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>0q1hkmla9asu_1</userId>
</event>
<event timestamp="1069502806" module="PARTICIPANT" eventname="ParticipantJoinEvent">
<role>VIEWER</role>
<externalUserId>1008</externalUserId>
<userId>yyynfpyca09g_1</userId>
<name>Evelina Keller</name>
</event>
<event timestamp="1069502545" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>nfuklna24flg_1</userId>
</event>
<event timestamp="1069502806" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>yyynfpyca09g_1</userId>
</event>
<event timestamp="1069504995" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>2dc8jctma0nj_1</userId>
</event>
<event timestamp="1069505733" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>bepguk6d7dza_1</userId>
</event>
<event timestamp="1069507663" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>fzlsahcijxo4_1</userId>
</event>
<event timestamp="1069508668" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>7ur69btts657_1</userId>
</event>
<event timestamp="1069509022" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>23uydbo9nauq_1</userId>
</event>
<event timestamp="1069512636" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>12ipastd9pw1_1</userId>
</event>
<event timestamp="1069514445" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>7m940cic73r3_1</userId>
</event>
<event timestamp="1069595144" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>42dnty7rovjt_1</userId>
</event>
<event timestamp="1069686925" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>vvyha6umxoyt_1</userId>
</event>
<event timestamp="1069700912" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>y096bmb53yu5_2</userId>
</event>
<event timestamp="1069717589" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>tgfbj6f828sp_1</userId>
</event>
<event timestamp="1069722053" module="PARTICIPANT" eventname="RecordStatusEvent">
<userId>9izxq660i7vr_1</userId>
<status>false</status>
</event>
<event timestamp="1069746114" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>9izxq660i7vr_1</userId>
</event>
<event timestamp="1069802390" module="PARTICIPANT" eventname="ParticipantLeftEvent">
<userId>yyynfpyca09g_1</userId>
</event>
<event timestamp="1069897986" module="PARTICIPANT" eventname="EndAndKickAllEvent">
</event>
</recording>

View File

@ -28,6 +28,16 @@ redis_port: 6379
# and have another script process it. # and have another script process it.
store_recording_status: false store_recording_status: false
# Whether to anonymize the sender of chat messages in the processed
# recordings. The settings here are the defaults; they can be overridden
# by passing meta parameters on the meeting create call.
# meta param: meta_bbb-anonymize-chat (true/false)
anonymize_chat: false
# By default only names of viewers are anonymized - if you would also
# like to anonymize moderators, you can set this to true:
# meta param: meta_bbb-anonymize-chat-moderators (true/false)
anonymize_chat_moderators: false
# Sequence of recording steps. Keys are the current step, values # Sequence of recording steps. Keys are the current step, values
# are the next step(s). Examples: # are the next step(s). Examples:
# current_step: next_step # current_step: next_step

View File

@ -0,0 +1,273 @@
# frozen_string_literal: true
require 'minitest/autorun'
require 'nokogiri'
require 'recordandplayback'
class TestEvents < Minitest::Test
def setup
@events_legacy = File.open('resources/raw/1b199e88-7df7-4842-a5f1-0e84b781c5c8/events.xml') do |io|
Nokogiri::XML(io)
end
@events_chat09 = File.open('resources/raw/chat_0_9.xml') do |io|
Nokogiri::XML(io)
end
@events_devcall = File.open('resources/raw/183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1630430006889/events.xml') do |io|
Nokogiri::XML(io)
end
@events_meta_edt = File.open('resources/raw/2a1de53edf0543d950056bf3c0d4d357eba3383f-1630607370684/events.xml') do |io|
Nokogiri::XML(io)
end
end
def test_anonymous_user_map_legacy
map = BigBlueButton::Events.anonymous_user_map(@events_legacy)
assert_empty(map)
end
def test_anonymous_user_map_legacy_no_viewer_only
map = BigBlueButton::Events.anonymous_user_map(@events_legacy, moderators: true)
assert_equal(1, map.length)
assert_equal('Moderator 1', map['1'])
end
def test_anonymous_user_map_bbb_0_9
map = BigBlueButton::Events.anonymous_user_map(@events_chat09)
assert_equal(21, map.length)
assert_equal('Marinda Collins', map['9izxq660i7vr_1']) # Moderator
assert_equal('Viewer 1', map['vvyha6umxoyt_1'])
assert_equal('Viewer 2', map['hs7iskkr7xrt_1'])
assert_equal('Viewer 3', map['7m940cic73r3_1'])
assert_equal('Viewer 4', map['tgfbj6f828sp_1'])
assert_equal('Viewer 5', map['bepguk6d7dza_1'])
assert_equal('Viewer 6', map['66ntqzexswc2_1'])
assert_equal('Viewer 7', map['0q1hkmla9asu_1'])
assert_equal('Viewer 8', map['yyynfpyca09g_1'])
assert_equal('Viewer 9', map['dmsj3897dwss_1'])
assert_equal('Viewer 10', map['42dnty7rovjt_1'])
assert_equal('Viewer 11', map['7ur69btts657_1'])
assert_equal('Viewer 12', map['23uydbo9nauq_1'])
assert_equal('Viewer 6', map['12ipastd9pw1_1'])
assert_equal('Viewer 9', map['ainnu65fiycz_1'])
assert_equal('Viewer 13', map['j73nq5k8xcaa_1'])
assert_equal('Viewer 14', map['nfuklna24flg_1'])
assert_equal('Viewer 9', map['2dc8jctma0nj_1'])
assert_equal('Viewer 13', map['fzlsahcijxo4_1'])
assert_equal('Viewer 2', map['y096bmb53yu5_1'])
assert_equal('Viewer 2', map['y096bmb53yu5_2'])
assert_equal('Viewer 8', map['yyynfpyca09g_1'])
end
def test_anonymous_user_map_bbb_0_9_no_viewer_only
map = BigBlueButton::Events.anonymous_user_map(@events_chat09, moderators: true)
assert_equal(21, map.length)
assert_equal('Moderator 1', map['9izxq660i7vr_1'])
assert_equal('Viewer 1', map['vvyha6umxoyt_1'])
assert_equal('Viewer 2', map['hs7iskkr7xrt_1'])
assert_equal('Viewer 3', map['7m940cic73r3_1'])
assert_equal('Viewer 4', map['tgfbj6f828sp_1'])
assert_equal('Viewer 5', map['bepguk6d7dza_1'])
assert_equal('Viewer 6', map['66ntqzexswc2_1'])
assert_equal('Viewer 7', map['0q1hkmla9asu_1'])
assert_equal('Viewer 8', map['yyynfpyca09g_1'])
assert_equal('Viewer 9', map['dmsj3897dwss_1'])
assert_equal('Viewer 10', map['42dnty7rovjt_1'])
assert_equal('Viewer 11', map['7ur69btts657_1'])
assert_equal('Viewer 12', map['23uydbo9nauq_1'])
assert_equal('Viewer 6', map['12ipastd9pw1_1'])
assert_equal('Viewer 9', map['ainnu65fiycz_1'])
assert_equal('Viewer 13', map['j73nq5k8xcaa_1'])
assert_equal('Viewer 14', map['nfuklna24flg_1'])
assert_equal('Viewer 9', map['2dc8jctma0nj_1'])
assert_equal('Viewer 13', map['fzlsahcijxo4_1'])
assert_equal('Viewer 2', map['y096bmb53yu5_1'])
assert_equal('Viewer 2', map['y096bmb53yu5_2'])
assert_equal('Viewer 8', map['yyynfpyca09g_1'])
end
def test_get_chat_events_legacy
start_time = BigBlueButton::Events.first_event_timestamp(@events_legacy)
end_time = BigBlueButton::Events.last_event_timestamp(@events_legacy)
bbb_props = { 'anonymize_chat' => true, 'anonymize_chat_moderators' => true }
chats_enum = BigBlueButton::Events.get_chat_events(@events_legacy, start_time, end_time, bbb_props).each
chat = chats_enum.next
assert_equal(34_876, chat[:in])
assert_nil(chat.fetch(:out))
assert_nil(chat.fetch(:sender_id))
# Anonymization doesn't work on really old recordings since there's no connection between
# chat user names and user ids
assert_equal('FRED', chat[:sender])
assert_equal('hello', chat[:message])
assert_nil(chat.fetch(:date))
assert_equal(0, chat[:text_color])
chat = chats_enum.next
assert_equal(42_388, chat[:in])
assert_nil(chat.fetch(:out))
assert_equal('FRED', chat[:sender])
assert_equal('how are you?', chat[:message])
chat = chats_enum.next
assert_equal(90_561, chat[:in])
assert_nil(chat.fetch(:out))
assert_equal('FRED', chat[:sender])
assert_equal('hi fred', chat[:message])
assert_raises(StopIteration) { chats_enum.next }
end
def test_get_chat_events_0_9
start_time = BigBlueButton::Events.first_event_timestamp(@events_chat09)
end_time = BigBlueButton::Events.last_event_timestamp(@events_chat09)
chats_enum = BigBlueButton::Events.get_chat_events(@events_chat09, start_time, end_time).each
chat = chats_enum.next
assert_equal(749_025, chat[:in])
assert_equal(988_028, chat[:out])
assert_equal('2dc8jctma0nj_1', chat[:sender_id])
assert_equal('Xhesika De Lange', chat[:sender])
assert_equal('Public chat 1', chat[:message])
assert_nil(chat.fetch(:date))
assert_equal(0, chat[:text_color])
chat = chats_enum.next
assert_equal(972_022, chat[:in])
assert_equal(988_028, chat[:out])
assert_equal('23uydbo9nauq_1', chat[:sender_id])
assert_equal('Asa Darby', chat[:sender])
assert_equal('Public chat 2', chat[:message])
chat = chats_enum.next
assert_equal(1_797_324, chat[:in])
assert_nil(chat.fetch(:out))
assert_equal('hs7iskkr7xrt_1', chat[:sender_id])
assert_equal('Isaías Seelen', chat[:sender])
assert_equal('Public chat 3', chat[:message])
chat = chats_enum.next
assert_equal(3_144_319 - 522_222, chat[:in])
assert_equal('0q1hkmla9asu_1', chat[:sender_id])
assert_equal('Elias Stablum', chat[:sender])
assert_equal('Public chat 6', chat[:message])
chat = chats_enum.next
assert_equal(3_148_146 - 522_222, chat[:in])
assert_equal('fzlsahcijxo4_1', chat[:sender_id])
assert_equal('Arethusa Mann', chat[:sender])
assert_equal('Public chat 7', chat[:message])
chat = chats_enum.next
assert_equal(3_157_549 - 522_222, chat[:in])
assert_equal('nfuklna24flg_1', chat[:sender_id])
assert_equal('Ninel Mac Ruaidhrí', chat[:sender])
assert_equal('Public chat 8', chat[:message])
chat = chats_enum.next
assert_equal(3_159_062 - 522_222, chat[:in])
assert_equal('7m940cic73r3_1', chat[:sender_id])
assert_equal('Mireia Castell', chat[:sender])
assert_equal('Public chat 9', chat[:message])
chat = chats_enum.next
assert_equal(3_167_630 - 522_222, chat[:in])
assert_equal('nfuklna24flg_1', chat[:sender_id])
assert_equal('Ninel Mac Ruaidhrí', chat[:sender])
assert_equal('Public chat 10', chat[:message])
chat = chats_enum.next
assert_equal(5_240_729 - 522_222, chat[:in])
assert_equal('2dc8jctma0nj_1', chat[:sender_id])
assert_equal('Xhesika De Lange', chat[:sender])
assert_equal('Public chat 11', chat[:message])
chat = chats_enum.next
assert_equal(5_265_991 - 522_222, chat[:in])
assert_equal('fzlsahcijxo4_1', chat[:sender_id])
assert_equal('Arethusa Mann', chat[:sender])
assert_equal('Public chat 12', chat[:message])
chat = chats_enum.next
assert_equal(5_454_399 - 522_222, chat[:in])
assert_equal('2dc8jctma0nj_1', chat[:sender_id])
assert_equal('Xhesika De Lange', chat[:sender])
assert_equal('Public chat 13', chat[:message])
assert_raises(StopIteration) { chats_enum.next }
end
def test_get_chat_events_0_9_anonymized
start_time = BigBlueButton::Events.first_event_timestamp(@events_chat09)
end_time = BigBlueButton::Events.last_event_timestamp(@events_chat09)
bbb_props = { 'anonymize_chat' => true }
chats_enum = BigBlueButton::Events.get_chat_events(@events_chat09, start_time, end_time, bbb_props).each
assert_equal('Viewer 9', chats_enum.next[:sender])
assert_equal('Viewer 12', chats_enum.next[:sender])
assert_equal('Viewer 2', chats_enum.next[:sender])
assert_equal('Viewer 7', chats_enum.next[:sender])
assert_equal('Viewer 13', chats_enum.next[:sender])
assert_equal('Viewer 14', chats_enum.next[:sender])
assert_equal('Viewer 3', chats_enum.next[:sender])
assert_equal('Viewer 14', chats_enum.next[:sender])
assert_equal('Viewer 9', chats_enum.next[:sender])
assert_equal('Viewer 13', chats_enum.next[:sender])
assert_equal('Viewer 9', chats_enum.next[:sender])
assert_raises(StopIteration) { chats_enum.next }
end
def test_get_chat_events_0_9_start_time
end_time = BigBlueButton::Events.last_event_timestamp(@events_chat09)
chats = BigBlueButton::Events.get_chat_events(@events_chat09, 1_063_007_465, end_time)
chat = chats.first
assert_equal(301_831, chat[:in])
assert_equal('hs7iskkr7xrt_1', chat[:sender_id])
assert_equal('Isaías Seelen', chat[:sender])
assert_equal('Public chat 3', chat[:message])
end
def test_get_chat_events_0_9_end_time
start_time = BigBlueButton::Events.first_event_timestamp(@events_chat09)
chats = BigBlueButton::Events.get_chat_events(@events_chat09, start_time, 1_062_490_000)
chat = chats.last
assert_equal(972_022, chat[:in])
assert_nil(chat.fetch(:out))
assert_equal('23uydbo9nauq_1', chat[:sender_id])
assert_equal('Asa Darby', chat[:sender])
assert_equal('Public chat 2', chat[:message])
end
def test_get_chat_events_devcall
start_time = BigBlueButton::Events.first_event_timestamp(@events_devcall)
end_time = BigBlueButton::Events.last_event_timestamp(@events_devcall)
chats = BigBlueButton::Events.get_chat_events(@events_devcall, start_time, end_time)
assert_equal(11, chats.length)
chat = chats[0]
assert_equal(17_013, chat[:in])
assert_equal(148_701, chat[:out])
assert_equal('w_kmm96j1as24f', chat[:sender_id])
assert_equal('Mario', chat[:sender])
assert_equal('#7b1fa2', chat[:avatar_color])
assert_nil(chat.fetch(:text_color))
assert_equal(DateTime.rfc3339('2021-08-31T18:06:14.330+00:00'), chat[:date])
# rubocop:disable Layout/LineLength
assert_equal(
"i get logs of these: \n\n ERROR: clientLogger: Camera VIEWER failed. Reconnecting. <a href=\"https://develop.bigbluebutton.org/html5client/8fb14b479570f65105c7ff9a2960b679501f34ff.js?meteor_js_resource=true:348:579447\" rel=\"nofollow\"><u>https://develop.bigbluebutton.org/html5client/8fb14b479570f65105c7ff9a2960b679501f34ff.js?meteor_js_resource=true:348:579447</u></a>",
chat[:message]
)
# rubocop:enable Layout/LineLength
chat = chats[7]
assert_equal(1_241_014, chat[:in])
assert_nil(chat.fetch(:out))
assert_equal('w_vk0ebqjxox9d', chat[:sender_id])
assert_equal('Anton G', chat[:sender])
assert_equal('#0277bd', chat[:avatar_color])
assert_nil(chat.fetch(:text_color))
assert_equal(DateTime.rfc3339('2021-08-31T18:26:38.332+00:00'), chat[:date])
assert_equal('Nice!', chat[:message])
end
def test_get_chat_events_meta_edt
start_time = BigBlueButton::Events.first_event_timestamp(@events_meta_edt)
end_time = BigBlueButton::Events.last_event_timestamp(@events_meta_edt)
chats = BigBlueButton::Events.get_chat_events(@events_meta_edt, start_time, end_time)
assert_equal(1, chats.length)
chat = chats[0]
assert_equal(11_635, chat[:in])
assert_nil(chat.fetch(:out))
assert_equal('w_tvumguamxhhs', chat[:sender_id])
# This recording has the meta_bbb-anonymize-chat param set
assert_equal('Viewer 1', chat[:sender])
assert_equal(DateTime.rfc3339('2021-09-02T14:33:02.214-04:00'), chat[:date])
assert_equal('whoops, forgot to start recording…', chat[:message])
end
end

View File

@ -1056,33 +1056,21 @@ def processPresentation(package_dir)
File.write("#{package_dir}/#{$cursor_xml_filename}", cursors_doc.to_xml) File.write("#{package_dir}/#{$cursor_xml_filename}", cursors_doc.to_xml)
end end
def processChatMessages def processChatMessages(events, bbb_props)
BigBlueButton.logger.info("Processing chat events") BigBlueButton.logger.info("Processing chat events")
# Create slides.xml and chat. # Create slides.xml and chat.
$slides_doc = Nokogiri::XML::Builder.new do |xml| Nokogiri::XML::Builder.new do |xml|
$xml = xml xml.popcorn {
$xml.popcorn { BigBlueButton::Events.get_chat_events(events, $meeting_start.to_i, $meeting_end.to_i, bbb_props).each do |chat|
# Process chat events. chattimeline = {
current_time = 0 in: (chat[:in] / 1000.0).round(1),
$rec_events.each do |re| direction: 'down',
$chat_events.each do |node| name: chat[:sender],
if (node[:timestamp].to_i >= re[:start_timestamp] and node[:timestamp].to_i <= re[:stop_timestamp]) message: chat[:message],
chat_timestamp = node[:timestamp] target: 'chat'
chat_sender = node.xpath(".//sender")[0].text() }
chat_message = BigBlueButton::Events.linkify(node.xpath(".//message")[0].text()) chattimeline[:out] = (chat[:out] / 1000.0).round(1) unless chat[:out].nil?
chat_start = ( translateTimestamp(chat_timestamp) / 1000).to_i xml.chattimeline(**chattimeline)
# Creates a list of the clear timestamps that matter for this message
next_clear_timestamps = $clear_chat_timestamps.select{ |e| e >= node[:timestamp] }
# If there is none we skip it, or else we add the out time that will remove a message
if next_clear_timestamps.empty?
$xml.chattimeline(:in => chat_start, :direction => :down, :name => chat_sender, :message => chat_message, :target => :chat )
else
chat_end = ( translateTimestamp( next_clear_timestamps.first ) / 1000).to_i
$xml.chattimeline(:in => chat_start, :out => chat_end, :direction => :down, :name => chat_sender, :message => chat_message, :target => :chat )
end
end
end
current_time += re[:stop_timestamp] - re[:start_timestamp]
end end
} }
end end
@ -1216,7 +1204,7 @@ def processExternalVideoEvents(events, package_dir)
timestamp = (translateTimestamp(event[:start_timestamp]) / 1000).to_i timestamp = (translateTimestamp(event[:start_timestamp]) / 1000).to_i
# do not add same external_video twice # do not add same external_video twice
if (external_videos.find {|ev| ev[:timestamp] == timestamp}.nil?) if (external_videos.find {|ev| ev[:timestamp] == timestamp}.nil?)
if ((event[:start_timestamp] >= re[:start_timestamp] and event[:start_timestamp] <= re[:stop_timestamp]) || if ((event[:start_timestamp] >= re[:start_timestamp] and event[:start_timestamp] <= re[:stop_timestamp]) ||
(event[:start_timestamp] < re[:start_timestamp] and event[:stop_timestamp] >= re[:start_timestamp])) (event[:start_timestamp] < re[:start_timestamp] and event[:stop_timestamp] >= re[:start_timestamp]))
external_videos << { external_videos << {
:timestamp => timestamp, :timestamp => timestamp,
@ -1414,18 +1402,11 @@ begin
#Create slides.xml #Create slides.xml
BigBlueButton.logger.info("Generating xml for slides and chat") BigBlueButton.logger.info("Generating xml for slides and chat")
# Gathering all the events from the events.xml
$chat_events = @doc.xpath("//event[@eventname='PublicChatEvent']")
# Create a list of timestamps when the moderator cleared the public chat
$clear_chat_timestamps = [ ]
clear_chat_events = @doc.xpath("//event[@eventname='ClearPublicChatEvent']")
clear_chat_events.each { |clear| $clear_chat_timestamps << clear[:timestamp] }
$clear_chat_timestamps.sort!
calculateRecordEventsOffset() calculateRecordEventsOffset()
processChatMessages() # Write slides.xml to file
slides_doc = processChatMessages(@doc, bbb_props)
File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts slides_doc.to_xml }
processPresentation(package_dir) processPresentation(package_dir)
@ -1435,9 +1416,6 @@ begin
processExternalVideoEvents(@doc, package_dir) processExternalVideoEvents(@doc, package_dir)
# Write slides.xml to file
File.open("#{package_dir}/slides_new.xml", 'w') { |f| f.puts $slides_doc.to_xml }
# Write deskshare.xml to file # Write deskshare.xml to file
File.open("#{package_dir}/#{$deskshare_xml_filename}", 'w') { |f| f.puts $deskshare_xml.to_xml } File.open("#{package_dir}/#{$deskshare_xml_filename}", 'w') { |f| f.puts $deskshare_xml.to_xml }