diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx index d275f1564220606fd71ca0a3fa15785e0e6a1913..a03b7a9a992a0f487f89bb0bd6bb10d450cdd286 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx @@ -248,7 +248,8 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages.clearAllLabel)} description={intl.formatMessage(intlMessages.clearAllDesc)} onClick={toggleStatus} - />) : null + /> + ) : null ), (!meetingIsBreakout && isMeteorConnected ? ( <DropdownListItem @@ -257,7 +258,8 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages[isMeetingMuted ? 'unmuteAllLabel' : 'muteAllLabel'])} description={intl.formatMessage(intlMessages[isMeetingMuted ? 'unmuteAllDesc' : 'muteAllDesc'])} onClick={toggleMuteAllUsers} - />) : null + /> + ) : null ), (!meetingIsBreakout && !isMeetingMuted && isMeteorConnected ? ( <DropdownListItem @@ -266,7 +268,8 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages.muteAllExceptPresenterLabel)} description={intl.formatMessage(intlMessages.muteAllExceptPresenterDesc)} onClick={toggleMuteAllUsersExceptPresenter} - />) : null + /> + ) : null ), (amIModerator ? ( @@ -275,7 +278,8 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages.saveUserNames)} key={this.saveUsersNameId} onClick={this.onSaveUserNames} - />) + /> + ) : null ), (!meetingIsBreakout && isMeteorConnected ? ( @@ -285,16 +289,19 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages.lockViewersLabel)} description={intl.formatMessage(intlMessages.lockViewersDesc)} onClick={() => mountModal(<LockViewersContainer />)} - />) : null + /> + ) : null ), (!meetingIsBreakout && isMeteorConnected ? ( <DropdownListItem key={this.guestPolicyId} icon="user" label={intl.formatMessage(intlMessages.guestPolicyLabel)} + data-test="guestPolicyLabel" description={intl.formatMessage(intlMessages.guestPolicyDesc)} onClick={() => mountModal(<GuestPolicyContainer />)} - />) : null + /> + ) : null ), (isMeteorConnected ? <DropdownListSeparator key={_.uniqueId('list-separator-')} /> : null), (canCreateBreakout && isMeteorConnected ? ( @@ -305,7 +312,8 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages.createBreakoutRoom)} description={intl.formatMessage(intlMessages.createBreakoutRoomDesc)} onClick={this.onCreateBreakouts} - />) : null + /> + ) : null ), (canInviteUsers && isMeteorConnected ? ( <DropdownListItem @@ -314,7 +322,8 @@ class UserOptions extends PureComponent { label={intl.formatMessage(intlMessages.invitationItem)} key={this.createBreakoutId} onClick={this.onInvitationUsers} - />) : null + /> + ) : null ), (amIModerator && CaptionsService.isCaptionsEnabled() && isMeteorConnected ? ( diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/waiting-users/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/waiting-users/component.jsx index a4c55265589c333ffa5a3bb3cbdb4d120a91f5b0..2520a2fb66d9a832ae3100d58f962ccbf95a02ea 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/waiting-users/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/waiting-users/component.jsx @@ -23,7 +23,6 @@ const intlMessages = defineMessages({ }); class WaitingUsers extends PureComponent { - static toggleWaitingPanel() { Session.set( 'openPanel', @@ -49,9 +48,10 @@ class WaitingUsers extends PureComponent { <div className={styles.scrollableList}> <div className={styles.list}> <div - role='button' + role="button" tabIndex={0} className={styles.listItem} + data-test="waitingUsersBtn" onClick={WaitingUsers.toggleWaitingPanel} > <Icon iconName="user" /> diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/guest-policy/component.jsx b/bigbluebutton-html5/imports/ui/components/waiting-users/guest-policy/component.jsx index f98183d5b4b6d23144cbfc93bc9d9eb7c4d3966d..cd3a0731d0cd0152d5b2cd7ae8fa40d0c13c1733 100644 --- a/bigbluebutton-html5/imports/ui/components/waiting-users/guest-policy/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/waiting-users/guest-policy/component.jsx @@ -62,7 +62,10 @@ class GuestPolicyComponent extends PureComponent { hideBorder contentLabel={intl.formatMessage(intlMessages.ariaModalTitle)} > - <div className={styles.container}> + <div + className={styles.container} + data-test="guestPolicySettingsModal" + > <div className={styles.header}> <h2 className={styles.title}> {intl.formatMessage(intlMessages.guestPolicyTitle)} @@ -78,6 +81,7 @@ class GuestPolicyComponent extends PureComponent { className={styles.button} disabled={guestPolicy === ASK_MODERATOR} label={intl.formatMessage(intlMessages.askModerator)} + data-test="askModerator" onClick={() => { changeGuestPolicy(ASK_MODERATOR); closeModal(); @@ -88,6 +92,7 @@ class GuestPolicyComponent extends PureComponent { className={styles.button} disabled={guestPolicy === ALWAYS_ACCEPT} label={intl.formatMessage(intlMessages.alwaysAccept)} + data-test="alwaysAccept" onClick={() => { changeGuestPolicy(ALWAYS_ACCEPT); closeModal(); @@ -98,6 +103,7 @@ class GuestPolicyComponent extends PureComponent { className={styles.button} disabled={guestPolicy === ALWAYS_DENY} label={intl.formatMessage(intlMessages.alwaysDeny)} + data-test="alwaysDeny" onClick={() => { changeGuestPolicy(ALWAYS_DENY); closeModal(); diff --git a/bigbluebutton-html5/tests/puppeteer/breakout/create.js b/bigbluebutton-html5/tests/puppeteer/breakout/create.js index eef3b4e85ac3d61df6a326b0d8ab6cd5354c6b1e..83da8ab0cccf4b1dcb6a194811d480f0c4b4ccf0 100644 --- a/bigbluebutton-html5/tests/puppeteer/breakout/create.js +++ b/bigbluebutton-html5/tests/puppeteer/breakout/create.js @@ -21,12 +21,93 @@ class Create { this.page3 = new Page(); } - // Join BigBlueButton meeting + // Join BigBlueButton meeting with a Moderator and a Viewer async init(meetingId, testName) { await this.page1.init(Page.getArgs(), meetingId, { ...params, fullName: 'Moderator1' }, undefined, testName); await this.page2.init(Page.getArgs(), this.page1.meetingId, { ...params, fullName: 'Viewer1', moderatorPW: '' }, undefined, testName); } + // Join BigBlueButton meeting with a Viewer only + async initViewer(testName) { + await this.page3.init(Page.getArgs(), this.page1.meetingId, { ...params, fullName: 'Viewer2', moderatorPW: '' }, undefined, testName); + } + + async askModeratorGuestPolicy(testName) { + try { + await this.page1.screenshot(`${testName}`, `01-before-closing-audio-modal-[${this.page1.meetingId}]`); + await this.page1.closeAudioModal(); + await this.page2.closeAudioModal(); + await this.page1.screenshot(`${testName}`, `02-after-closing-audio-modal-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.manageUsers, ELEMENT_WAIT_TIME); + await this.page1.click(ue.manageUsers, true); + await this.page1.screenshot(`${testName}`, `03-opened-users-managing-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.guestPolicyLabel, ELEMENT_WAIT_TIME); + await this.page1.click(ue.guestPolicyLabel, true); + await this.page1.screenshot(`${testName}`, `04-opened-guest-policy-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.askModerator, ELEMENT_WAIT_TIME); + await this.page1.click(ue.askModerator, true); + await this.page1.screenshot(`${testName}`, `05-clicked-askModerator-[${this.page1.meetingId}]`); + await this.initViewer(testName); + const responseLoggedIn = await this.page1.page.evaluate(util.getTestElement, ue.waitingUsersBtn); + await this.page1.screenshot(`${testName}`, `06-after-viewer-acceptance-[${this.page1.meetingId}]`); + return responseLoggedIn; + } catch (e) { + console.log(e); + return false; + } + } + + async alwaysAcceptGuestPolicy(testName) { + try { + await this.page1.screenshot(`${testName}`, `01-before-closing-audio-modal-[${this.page1.meetingId}]`); + await this.page1.closeAudioModal(); + await this.page2.closeAudioModal(); + await this.page1.screenshot(`${testName}`, `02-after-closing-audio-modal-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.manageUsers, ELEMENT_WAIT_TIME); + await this.page1.click(ue.manageUsers, true); + await this.page1.screenshot(`${testName}`, `03-opened-users-managing-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.guestPolicyLabel, ELEMENT_WAIT_TIME); + await this.page1.click(ue.guestPolicyLabel, true); + await this.page1.screenshot(`${testName}`, `04-opened-guest-policy-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.alwaysAccept, ELEMENT_WAIT_TIME); + await this.page1.click(ue.alwaysAccept, true); + await this.page1.screenshot(`${testName}`, `05-clicked-alwaysAccept-[${this.page1.meetingId}]`); + await this.initViewer(testName); + await this.page3.closeAudioModal(); + const responseLoggedIn = await this.page3.page.evaluate(util.getTestElement, e.whiteboard); + await this.page3.screenshot(`${testName}`, `06-after-viewer-connection-[${this.page1.meetingId}]`); + return responseLoggedIn; + } catch (e) { + console.log(e); + return false; + } + } + + async alwaysDenyGuestPolicy(testName) { + try { + await this.page1.screenshot(`${testName}`, `01-before-closing-audio-modal-[${this.page1.meetingId}]`); + await this.page1.closeAudioModal(); + await this.page2.closeAudioModal(); + await this.page1.screenshot(`${testName}`, `02-after-closing-audio-modal-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.manageUsers, ELEMENT_WAIT_TIME); + await this.page1.click(ue.manageUsers, true); + await this.page1.screenshot(`${testName}`, `03-opened-users-managing-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.guestPolicyLabel, ELEMENT_WAIT_TIME); + await this.page1.click(ue.guestPolicyLabel, true); + await this.page1.screenshot(`${testName}`, `04-opened-guest-policy-[${this.page1.meetingId}]`); + await this.page1.waitForSelector(ue.alwaysAccept, ELEMENT_WAIT_TIME); + await this.page1.click(ue.alwaysAccept, true); + await this.page1.screenshot(`${testName}`, `05-clicked-alwaysAccept-[${this.page1.meetingId}]`); + await this.initViewer(testName); + const responseLoggedIn = await this.page3.page.evaluate(util.getTestElement, ue.joinMeetingDemoPage); + await this.page3.screenshot(`${testName}`, `06-after-viewer-gets-denied-[${this.page1.meetingId}]`); + return responseLoggedIn; + } catch (e) { + console.log(e); + return false; + } + } + // Create Breakoutrooms async create(testName) { await this.page1.closeAudioModal(); @@ -101,7 +182,6 @@ class Create { await this.page3.click(be.chatButton, true); await this.page3.click(be.breakoutRoomsItem, true); - await this.page3.waitForSelector(be.joinRoom1, ELEMENT_WAIT_TIME); await this.page3.click(be.joinRoom1, true); await this.page3.waitForSelector(be.alreadyConnected, ELEMENT_WAIT_LONGER_TIME); @@ -197,6 +277,11 @@ class Create { await this.page1.close(); await this.page2.close(); } + + // Close page + async closePage(page) { + await page.close(); + } } module.exports = exports = Create; diff --git a/bigbluebutton-html5/tests/puppeteer/core/page.js b/bigbluebutton-html5/tests/puppeteer/core/page.js index 229bc309e468207e3620e5b2cfdf3f1c8adfd6b5..72a55d7832667d1478e2baeb121144f71c0fd549 100644 --- a/bigbluebutton-html5/tests/puppeteer/core/page.js +++ b/bigbluebutton-html5/tests/puppeteer/core/page.js @@ -5,12 +5,12 @@ const fs = require('fs'); const fsExtra = require('fs-extra'); const moment = require('moment'); const path = require('path'); +const PuppeteerVideoRecorder = require('puppeteer-video-recorder'); const helper = require('./helper'); const params = require('../params'); -const { ELEMENT_WAIT_TIME } = require('../core/constants'); +const { ELEMENT_WAIT_TIME } = require('./constants'); const e = require('./elements'); const ue = require('../user/elements'); -const PuppeteerVideoRecorder = require('puppeteer-video-recorder'); const { NETWORK_PRESETS, USER_AGENTS, MOBILE_DEVICES } = require('./profiles'); class Page { @@ -540,7 +540,7 @@ class Page { const users = collection.default._collection.find({}, {}, {}, {}, {}, { loggedOut: 'false' }).count(); return users; }); - const totalNumberOfUsersDom = await this.page.evaluate(() => document.querySelectorAll('[data-test^="userListItem"]').length); + const totalNumberOfUsersDom = await this.page.evaluate(async () => await document.querySelectorAll('[data-test^="userListItem"]').length); this.logger({ totalNumberOfUsersDom, totalNumberOfUsersMongo }); const metric = await this.page.metrics(); pageMetricsObj.totalNumberOfUsersMongoObj = totalNumberOfUsersMongo; diff --git a/bigbluebutton-html5/tests/puppeteer/polling.obj.js b/bigbluebutton-html5/tests/puppeteer/polling.obj.js index ff9502672c049ae0e475d096edec100acea15252..7e035bf218d622fe60e5d80613a9894b981ca660 100644 --- a/bigbluebutton-html5/tests/puppeteer/polling.obj.js +++ b/bigbluebutton-html5/tests/puppeteer/polling.obj.js @@ -1,11 +1,11 @@ +const { toMatchImageSnapshot } = require('jest-image-snapshot'); const CustomParameters = require('./customparameters/customparameters'); const Multiusers = require('./user/multiusers'); const Polling = require('./polling/poll'); const Page = require('./core/page'); const Poll = require('./chat/poll'); const ce = require('./customparameters/constants'); -const { toMatchImageSnapshot } = require('jest-image-snapshot'); -const { MAX_POLLING_TEST_TIMEOUT } = require('./core/constants'); // core constants (Timeouts vars imported) +const { MAX_POLLING_TEST_TIMEOUT, TEST_DURATION_TIME } = require('./core/constants'); // core constants (Timeouts vars imported) expect.extend({ toMatchImageSnapshot }); @@ -25,7 +25,7 @@ const pollingTest = () => { await test.init(Page.getArgs(), undefined, undefined, undefined, testName); await test.startRecording(testName); await test.closeAudioModal(); - response = await test.test(); + response = await test.test(testName); await test.logger('end of ', testName); await test.stopRecording(); screenshot = await test.page.screenshot(); @@ -41,7 +41,7 @@ const pollingTest = () => { failureThresholdType: 'percent', }); } - }); + }, TEST_DURATION_TIME); // Check for Poll Results chat message and return true when it appears test('Poll Results chat message', async () => { @@ -69,7 +69,7 @@ const pollingTest = () => { failureThresholdType: 'percent', }); } - }); + }, TEST_DURATION_TIME); // This test spec sets the userdata-bbb_force_restore_presentation_on_new_events parameter to true // and checks that the viewers get the presentation restored forcefully when the Moderator zooms @@ -97,7 +97,7 @@ const pollingTest = () => { failureThresholdType: 'percent', }); } - }); + }, TEST_DURATION_TIME); // This Test chooses randomly a polling case, runs it // and expects having it answered by the other user @@ -126,6 +126,6 @@ const pollingTest = () => { failureThresholdType: 'percent', }); } - }); + }, TEST_DURATION_TIME); }; module.exports = exports = pollingTest; diff --git a/bigbluebutton-html5/tests/puppeteer/polling/poll.js b/bigbluebutton-html5/tests/puppeteer/polling/poll.js index 166730d4962221ee4279ca5f1aa7d66171612056..5f099c1bfb296e09d58b9957075bd8d34e972957 100644 --- a/bigbluebutton-html5/tests/puppeteer/polling/poll.js +++ b/bigbluebutton-html5/tests/puppeteer/polling/poll.js @@ -7,9 +7,12 @@ class Polling extends Page { super('polling-test'); } - async test() { + async test(testName) { try { await utilNotification.startPoll(this); + if (process.env.GENERATE_EVIDENCES === 'true') { + await this.screenshot(`${testName}`, `01-before-chat-message-send-[${this.meetingId}]`); + } const resp = this.page.evaluate(() => document.querySelectorAll('[data-test="pollMenuButton"]').length === 1); return resp; } catch (e) { diff --git a/bigbluebutton-html5/tests/puppeteer/user.obj.js b/bigbluebutton-html5/tests/puppeteer/user.obj.js index b320523e47139060eda9a41bd32a37a477502342..09f3e1d1e5458df05707426cfbbf28cfef873729 100644 --- a/bigbluebutton-html5/tests/puppeteer/user.obj.js +++ b/bigbluebutton-html5/tests/puppeteer/user.obj.js @@ -1,9 +1,10 @@ +const { toMatchImageSnapshot } = require('jest-image-snapshot'); const Page = require('./core/page'); const Status = require('./user/status'); +const Create = require('./breakout/create'); const MultiUsers = require('./user/multiusers'); -const { toMatchImageSnapshot } = require('jest-image-snapshot'); const { MAX_MULTIUSERS_TEST_TIMEOUT, TEST_DURATION_TIME } = require('./core/constants'); // core constants (Timeouts vars imported) -const { NETWORK_PRESETS, USER_AGENTS, MOBILE_DEVICES } = require('./core/profiles'); +const { NETWORK_PRESETS } = require('./core/profiles'); expect.extend({ toMatchImageSnapshot }); @@ -280,5 +281,101 @@ const userTest = () => { }); } }); + + // Set Guest policy to ASK_MODERATOR + // and expect user in guest wait list + test('Guest policy: ASK_MODERATOR', async () => { + const test = new Create(); + let response; + let screenshot; + try { + const testName = 'askModeratorGuestPolicy'; + await test.page1.logger('begin of ', testName); + await test.init(undefined, testName); + await test.page1.startRecording(testName); + await test.page2.startRecording(testName); + response = await test.askModeratorGuestPolicy(testName); + await test.page1.stopRecording(); + await test.page2.stopRecording(); + screenshot = await test.page1.page.screenshot(); + await test.page1.logger('end of ', testName); + } catch (err) { + await test.page1.logger(err); + } finally { + await test.close(test.page1, test.page2); + await test.closePage(test.page3); + } + expect(response).toBe(true); + if (process.env.REGRESSION_TESTING === 'true') { + expect(screenshot).toMatchImageSnapshot({ + failureThreshold: 19.93, + failureThresholdType: 'percent', + }); + } + }); + + // Set Guest policy to ALWAYS_ACCEPT + // and expect user to get accepted automatically + test('Guest policy: ALWAYS_ACCEPT', async () => { + const test = new Create(); + let response; + let screenshot; + try { + const testName = 'alwaysAcceptGuestPolicy'; + await test.page1.logger('begin of ', testName); + await test.init(undefined, testName); + await test.page1.startRecording(testName); + await test.page2.startRecording(testName); + response = await test.alwaysAcceptGuestPolicy(testName); + await test.page1.stopRecording(); + await test.page2.stopRecording(); + screenshot = await test.page1.page.screenshot(); + await test.page1.logger('end of ', testName); + } catch (err) { + await test.page1.logger(err); + } finally { + await test.close(test.page1, test.page2); + await test.closePage(test.page3); + } + expect(response).toBe(true); + if (process.env.REGRESSION_TESTING === 'true') { + expect(screenshot).toMatchImageSnapshot({ + failureThreshold: 19.93, + failureThresholdType: 'percent', + }); + } + }); + + // Set Guest policy to ALWAYS_DENY + // and expect user to get denied + test('Guest policy: ALWAYS_DENY', async () => { + const test = new Create(); + let response; + let screenshot; + try { + const testName = 'alwaysDenyGuestPolicy'; + await test.page1.logger('begin of ', testName); + await test.init(undefined, testName); + await test.page1.startRecording(testName); + await test.page2.startRecording(testName); + response = await test.alwaysDenyGuestPolicy(testName); + await test.page1.stopRecording(); + await test.page2.stopRecording(); + screenshot = await test.page1.page.screenshot(); + await test.page1.logger('end of ', testName); + } catch (err) { + await test.page1.logger(err); + } finally { + await test.close(test.page1, test.page2); + await test.closePage(test.page3); + } + expect(response).toBe(true); + if (process.env.REGRESSION_TESTING === 'true') { + expect(screenshot).toMatchImageSnapshot({ + failureThreshold: 19.93, + failureThresholdType: 'percent', + }); + } + }); }; module.exports = exports = userTest; diff --git a/bigbluebutton-html5/tests/puppeteer/user/elements.js b/bigbluebutton-html5/tests/puppeteer/user/elements.js index 7332d49beaabbe0652a0e911d15dc90305f7f637..2f9743e57a108f72c1ec43e6930c403d5d0c17ae 100644 --- a/bigbluebutton-html5/tests/puppeteer/user/elements.js +++ b/bigbluebutton-html5/tests/puppeteer/user/elements.js @@ -20,3 +20,11 @@ exports.connectionStatusItemUser = 'div[data-test="connectionStatusItemUser"]'; exports.connectionStatusOfflineUser = 'div[data-test="offlineUser"]'; exports.mobileUser = 'span[data-test="mobileUser"]'; exports.userList = '[aria-label="Users and messages toggle"]'; +exports.manageUsers = 'button[data-test="manageUsers"]'; +exports.guestPolicyLabel = 'li[data-test="guestPolicyLabel"]'; +exports.guestPolicySettingsModal = 'div[data-test="guestPolicySettingsModal"]'; +exports.askModerator = 'button[data-test="askModerator"]'; +exports.alwaysAccept = 'button[data-test="alwaysAccept"]'; +exports.alwaysDeny = 'button[data-test="alwaysDeny"]'; +exports.waitingUsersBtn = 'div[data-test="waitingUsersBtn"]'; +exports.joinMeetingDemoPage = 'div[class^="join-meeting"]';