Merge branch 'v2.4.x-release' of github.com:bigbluebutton/bigbluebutton into merge-24

This commit is contained in:
Anton Georgiev 2021-11-03 14:04:32 +00:00
commit a7c98311fd
37 changed files with 308 additions and 163 deletions

View File

@ -11,7 +11,7 @@ stages:
# define which docker image to use for builds
default:
image: gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:v2021-10-25
image: gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:v2021-11-01
# This stage uses git to find out since when each package has been unmodified.
# it then checks an API endpoint on the package server to find out for which of

View File

@ -60,6 +60,7 @@ public class ApiParams {
public static final String WEBCAMS_ONLY_FOR_MODERATOR = "webcamsOnlyForModerator";
public static final String WELCOME = "welcome";
public static final String HTML5_INSTANCE_ID = "html5InstanceId";
public static final String ROLE = "role";
public static final String BREAKOUT_ROOMS_ENABLED = "breakoutRoomsEnabled";
public static final String BREAKOUT_ROOMS_RECORD = "breakoutRoomsRecord";

View File

@ -74,6 +74,10 @@ import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
public class MeetingService implements MessageListener {
private static Logger log = LoggerFactory.getLogger(MeetingService.class);
@ -1189,6 +1193,13 @@ public class MeetingService implements MessageListener {
log.info("Starting Meeting Service.");
try {
processMessage = true;
Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "cat /etc/bigbluebutton/bigbluebutton-release | cut -d '=' -f2"});
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String apiVersionFromFile = reader.readLine();
paramsProcessorUtil.setBbbVersion(apiVersionFromFile);
Runnable messageReceiver = new Runnable() {
public void run() {
while (processMessage) {

View File

@ -108,31 +108,34 @@ public class ParamsProcessorUtil {
private Long maxPresentationFileUpload = 30000000L; // 30MB
private Integer clientLogoutTimerInMinutes = 0;
private Integer meetingExpireIfNoUserJoinedInMinutes = 5;
private Integer meetingExpireWhenLastUserLeftInMinutes = 1;
private Integer userInactivityInspectTimerInMinutes = 120;
private Integer userInactivityThresholdInMinutes = 30;
private Integer meetingExpireIfNoUserJoinedInMinutes = 5;
private Integer meetingExpireWhenLastUserLeftInMinutes = 1;
private Integer userInactivityInspectTimerInMinutes = 120;
private Integer userInactivityThresholdInMinutes = 30;
private Integer userActivitySignResponseDelayInMinutes = 5;
private Boolean defaultAllowDuplicateExtUserid = true;
private Boolean defaultEndWhenNoModerator = false;
private Integer defaultEndWhenNoModeratorDelayInMinutes = 1;
private Integer defaultHtml5InstanceId = 1;
private Boolean defaultEndWhenNoModerator = false;
private Integer defaultEndWhenNoModeratorDelayInMinutes = 1;
private Integer defaultHtml5InstanceId = 1;
private String formatConfNum(String s) {
if (s.length() > 5) {
/* Reverse conference number.
* Put a whitespace every third char.
* Reverse it again to display it correctly.
* Trim leading whitespaces.
* */
String confNumReversed = new StringBuilder(s).reverse().toString();
String confNumSplit = confNumReversed.replaceAll("(.{3})", "$1 ");
String confNumL = new StringBuilder(confNumSplit).reverse().toString().trim();
return confNumL;
}
private String bbbVersion = "";
private Boolean allowRevealOfBBBVersion = false;
return s;
}
private String formatConfNum(String s) {
if (s.length() > 5) {
/* Reverse conference number.
* Put a whitespace every third char.
* Reverse it again to display it correctly.
* Trim leading whitespaces.
* */
String confNumReversed = new StringBuilder(s).reverse().toString();
String confNumSplit = confNumReversed.replaceAll("(.{3})", "$1 ");
String confNumL = new StringBuilder(confNumSplit).reverse().toString().trim();
return confNumL;
}
return s;
}
private String substituteKeywords(String message, String dialNumber, String telVoice, String meetingName) {
String welcomeMessage = message;
@ -659,6 +662,14 @@ public class ParamsProcessorUtil {
}
}
public String getBbbVersion() {
return bbbVersion;
}
public Boolean getAllowRevealOfBBBVersion() {
return allowRevealOfBBBVersion;
}
public String processWelcomeMessage(String message, Boolean isBreakout) {
String welcomeMessage = message;
if (StringUtils.isEmpty(message)) {
@ -1174,8 +1185,16 @@ public class ParamsProcessorUtil {
this.defaultEndWhenNoModerator = val;
}
public void setEndWhenNoModeratorDelayInMinutes(Integer value) {
this.defaultEndWhenNoModeratorDelayInMinutes = value;
}
public void setEndWhenNoModeratorDelayInMinutes(Integer value) {
this.defaultEndWhenNoModeratorDelayInMinutes = value;
}
public void setBbbVersion(String version) {
this.bbbVersion = this.allowRevealOfBBBVersion ? version : "";
}
public void setAllowRevealOfBBBVersion(Boolean allowVersion) {
this.allowRevealOfBBBVersion = allowVersion;
}
}

View File

@ -14,7 +14,8 @@ public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
PASSWORD("password"),
GUEST("guest"),
AUTH("auth"),
CREATE_TIME("createTime");
CREATE_TIME("createTime"),
ROLE("role");
private final String value;
@ -49,6 +50,8 @@ public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
private String createTimeString;
private Long createTime;
private String role;
public JoinMeeting(Checksum checksum) {
super(checksum);
}
@ -115,6 +118,14 @@ public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
this.createTime = createTime;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public void populateFromParamsMap(Map<String, String[]> params) {
if(params.containsKey(Params.MEETING_ID.getValue())) {
@ -127,6 +138,7 @@ public class JoinMeeting extends RequestWithChecksum<JoinMeeting.Params> {
if(params.containsKey(Params.GUEST.getValue())) setGuestString(params.get(Params.GUEST.getValue())[0]);
if(params.containsKey(Params.AUTH.getValue())) setAuthString(params.get(Params.AUTH.getValue())[0]);
if(params.containsKey(Params.CREATE_TIME.getValue())) setCreateTimeString(params.get(Params.CREATE_TIME.getValue())[0]);
if(params.containsKey(Params.ROLE.getValue())) setRole(params.get(Params.ROLE.getValue())[0]);
}
@Override

View File

@ -52,12 +52,13 @@ public class ResponseBuilder {
return new Date(timestamp).toString();
}
public String buildMeetingVersion(String version, String returnCode) {
public String buildMeetingVersion(String apiVersion, String bbbVersion, String returnCode) {
StringWriter xmlText = new StringWriter();
Map<String, Object> data = new HashMap<String, Object>();
data.put("returnCode", returnCode);
data.put("version", version);
data.put("apiVersion", apiVersion);
data.put("bbbVersion", bbbVersion);
processData(getTemplate("api-version.ftlx"), data, xmlText);

View File

@ -5,16 +5,16 @@
meteor-base@1.5.1
mobile-experience@1.1.0
mongo@1.12.0
mongo@1.13.0
reactive-var@1.0.11
standard-minifier-css@1.7.3
standard-minifier-js@2.6.1
standard-minifier-css@1.7.4
standard-minifier-js@2.7.1
es5-shim@4.8.0
ecmascript@0.15.3
ecmascript@0.16.0
shell-server@0.5.0
static-html
static-html@1.3.2
react-meteor-data
http@1.4.2
session@1.2.0

View File

@ -1 +1 @@
METEOR@2.3.6
METEOR@2.5

View File

@ -1,5 +1,5 @@
allow-deny@1.1.0
autoupdate@1.7.0
autoupdate@1.8.0
babel-compiler@7.7.0
babel-runtime@1.5.0
base64@1.0.12
@ -8,20 +8,20 @@ blaze-tools@1.1.2
boilerplate-generator@1.7.1
caching-compiler@1.2.2
caching-html-compiler@1.2.1
callback-hook@1.3.1
callback-hook@1.4.0
cfs:reactive-list@0.0.9
check@1.3.1
ddp@1.4.0
ddp-client@2.5.0
ddp-common@1.4.0
ddp-server@2.4.1
ddp-server@2.5.0
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.7.1
ecmascript@0.15.3
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.11.1
ecmascript-runtime-server@0.10.1
dynamic-import@0.7.2
ecmascript@0.16.0
ecmascript-runtime@0.8.0
ecmascript-runtime-client@0.12.1
ecmascript-runtime-server@0.11.0
ejson@1.1.1
es5-shim@4.8.0
fetch@0.1.1
@ -34,21 +34,21 @@ id-map@1.1.1
inter-process-messaging@0.1.1
launch-screen@1.3.0
lmieulet:meteor-coverage@3.2.0
logging@1.2.0
meteor@1.9.3
logging@1.3.1
meteor@1.10.0
meteor-base@1.5.1
meteortesting:browser-tests@1.3.4
meteortesting:mocha@2.0.2
meteortesting:mocha-core@8.0.1
minifier-css@1.5.4
minifier-js@2.6.1
minifier-css@1.6.0
minifier-js@2.7.1
minimongo@1.7.0
mobile-experience@1.1.0
mobile-status-bar@1.1.0
modern-browsers@0.1.5
modules@0.16.0
modern-browsers@0.1.7
modules@0.17.0
modules-runtime@0.12.0
mongo@1.12.0
mongo@1.13.0
mongo-decimal@0.1.2
mongo-dev-server@1.1.0
mongo-id@1.0.8
@ -57,7 +57,7 @@ npm-mongo@3.9.1
ordered-dict@1.1.0
promise@0.12.0
random@1.2.0
react-fast-refresh@0.1.1
react-fast-refresh@0.2.0
react-meteor-data@0.2.16
reactive-dict@1.3.0
reactive-var@1.0.11
@ -69,13 +69,13 @@ session@1.2.0
shell-server@0.5.0
socket-stream-client@0.4.0
spacebars-compiler@1.3.0
standard-minifier-css@1.7.3
standard-minifier-js@2.6.1
standard-minifier-css@1.7.4
standard-minifier-js@2.7.1
static-html@1.3.2
templating-tools@1.2.1
tmeasday:check-npm-versions@0.3.2
tracker@1.2.0
underscore@1.0.10
url@1.3.2
webapp@1.11.1
webapp@1.13.0
webapp-hashing@1.1.0

View File

@ -1,4 +1,4 @@
#!/bin/sh -e
#!/bin/sh -ex
# Please check bigbluebutton/bigbluebutton-html5/dev_local_deployment/README.md
@ -22,21 +22,18 @@ if [ -d "node_modules" ]; then
rm -r node_modules/
fi
meteor reset
meteor npm install --production
meteor npm ci --production
sudo chmod 777 /usr/share/meteor
METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build $UPPER_DESTINATION_DIR --architecture os.linux.x86_64 --allow-superuser
METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build $UPPER_DESTINATION_DIR --architecture os.linux.x86_64 --allow-superuser --directory
sudo chown -R meteor:meteor "$UPPER_DESTINATION_DIR"/
echo 'stage3'
tar -xzf $UPPER_DESTINATION_DIR/bigbluebutton-html5.tar.gz -C $UPPER_DESTINATION_DIR
cd "$DESTINATION_DIR"/programs/server/ || exit
sudo npm i --production
sudo npm i
echo "deployed to $DESTINATION_DIR/programs/server\n\n\n"
echo "writing $DESTINATION_DIR/mongod_start_pre.sh"

View File

@ -50,6 +50,7 @@ const PresentationOptionsContainer = ({
return (
<Styled.RestorePresentationButton
icon={`${buttonType}${isLayoutSwapped ? '_off' : ''}`}
data-test="restorePresentationButton"
label={intl.formatMessage(isLayoutSwapped ? intlMessages.restorePresentationLabel : intlMessages.minimizePresentationLabel)}
aria-label={intl.formatMessage(isLayoutSwapped ? intlMessages.restorePresentationLabel : intlMessages.minimizePresentationLabel)}
aria-describedby={intl.formatMessage(isLayoutSwapped ? intlMessages.restorePresentationDesc : intlMessages.minimizePresentationDesc)}

View File

@ -103,7 +103,7 @@ class ExternalVideoModal extends Component {
contentLabel={intl.formatMessage(intlMessages.title)}
hideBorder
>
<header data-test="videoModealHeader" className={styles.header}>
<header data-test="videoModalHeader" className={styles.header}>
<h3 className={styles.title}>{intl.formatMessage(intlMessages.title)}</h3>
</header>

View File

@ -11,7 +11,6 @@ const stressTest = require('./stress/stress.obj');
const userTest = require('./user/user.obj');
const virtualizedListTest = require('./virtualizedlist/virtualizedlist.obj');
const webcamTest = require('./webcam/webcam.obj');
const webcamLayout = require('./webcam/webcamlayout.obj');
const whiteboardTest = require('./whiteboard/whiteboard.obj');
process.setMaxListeners(Infinity);
@ -28,7 +27,6 @@ describe('Shared Notes ', sharedNotesTest);
describe('User', userTest);
describe('Virtualized List', virtualizedListTest);
describe('Webcam', webcamTest);
describe('Webcam Layout', webcamLayout);
describe('Whiteboard', whiteboardTest);
if (process.env.STRESS_TEST === 'true') {
describe('Stress', stressTest);

View File

@ -156,6 +156,7 @@ exports.stopScreenSharing = 'button[data-test="stopScreenShare"]';
exports.presentationToolbarWrapper = '#presentationToolbarWrapper';
exports.presentationTitle = '[class^="presentationTitle--"]';
exports.hidePresentation = 'button[data-test="hidePresentationButton"]';
exports.minimizePresentation = 'button[aria-label="Minimize presentation"]';
exports.restorePresentation = 'button[data-test="restorePresentationButton"]';
exports.nextSlide = '[data-test="nextSlide"]';
exports.prevSlide = '[data-test="prevSlide"]';
@ -173,6 +174,16 @@ exports.confirmManagePresentation = 'button[data-test="confirmManagePresentation
exports.allowPresentationDownload = 'button[data-test="allowPresentationDownload"]';
exports.disallowPresentationDownload = 'button[data-test="disallowPresentationDownload"]';
exports.uploadPresentationFileName = 'uploadTest.png';
exports.presentationContainer = 'div[class^="presentationContainer--"]';
exports.externalVideoBtn = 'li[data-test="external-video"]';
exports.externalVideoModalHeader = 'header[data-test="videoModalHeader"]';
exports.videoModalInput = 'input[id="video-modal-input"]';
exports.startShareVideoBtn = 'button[aria-label="Share a new video"]';
exports.videoPlayer = 'div[data-test="videoPlayer"]';
// YouTube frame
exports.youtubeLink = 'https://www.youtube.com/watch?v=Hso8yLzkqj8&ab_channel=BigBlueButton';
exports.youtubeFrame = 'iframe[title^="YouTube"]';
exports.ytFrameTitle = 'a[class^="ytp-title-link"]';
// User
exports.firstUser = '[data-test="userListItemCurrent"]';

View File

@ -202,6 +202,7 @@ class Page {
'--window-size=1150,980',
'--allow-file-access',
'--lang=en-US',
'--disable-features=IsolateOrigins,site-per-process',
];
return {
headless: false,

View File

@ -574,7 +574,7 @@ class CustomParameters {
await this.page2.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-page1-${testName}`);
await this.page2.screenshot(`${testName}`, `01-page2-${testName}`);
await this.page2.waitAndClick(e.hidePresentation);
await this.page2.waitAndClick(e.minimizePresentation);
await this.page2.screenshot(`${testName}`, `02-page2-${testName}`);
const zoomInCase = await util.zoomIn(this.page1);
await this.page1.screenshot(`${testName}`, `02-page1-${testName}`);
@ -619,7 +619,7 @@ class CustomParameters {
await this.page2.startRecording(testName);
await this.page1.screenshot(`${testName}`, `01-page1-${testName}`);
await this.page2.screenshot(`${testName}`, `01-page2-${testName}`);
await this.page2.waitAndClick(e.hidePresentation);
await this.page2.waitAndClick(e.minimizePresentation);
await this.page2.screenshot(`${testName}`, `02-page2-${testName}`);
const pollCase = await util.poll(this.page1, this.page2) === true;
await this.page2.waitForSelector(e.smallToastMsg);

View File

@ -793,9 +793,9 @@
}
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
@ -866,11 +866,11 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
},
"axios": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"requires": {
"follow-redirects": "^1.10.0"
"follow-redirects": "^1.14.0"
}
},
"babel-jest": {
@ -1996,9 +1996,9 @@
}
},
"follow-redirects": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz",
"integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA=="
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
},
"for-in": {
"version": "1.0.2",
@ -4972,9 +4972,9 @@
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"tmpl": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
"integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE="
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw=="
},
"to-fast-properties": {
"version": "2.0.0",

View File

@ -6,9 +6,9 @@
"verbose": false
},
"dependencies": {
"axios": "^0.21.1",
"child_process": "^1.0.2",
"axios": "^0.21.4",
"babel-jest": "^27.0.6",
"child_process": "^1.0.2",
"dotenv": "^6.0.0",
"fs-extra": "^9.1.0",
"jest": "^26.6.3",

View File

@ -100,9 +100,9 @@ class Polling {
e.receivedAnswer, e.answerMessage
);
await this.modPage.screenshot(testName, '04-success-to-receive-answer');
} catch (e) {
} catch (er) {
await this.modPage.screenshot(testName, '04-failed-to-receive-answer');
await this.modPage.logger(e);
await this.modPage.logger(er);
return false;
}

View File

@ -1,8 +1,8 @@
const Page = require('../core/page');
const e = require('../core/elements');
const util = require('./util');
const { ELEMENT_WAIT_LONGER_TIME, ELEMENT_WAIT_TIME } = require('../core/constants');
const { checkElement, checkElementTextIncludes, checkElementText } = require('../core/util');
const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
const { checkElement, checkElementText } = require('../core/util');
class Presentation {
constructor() {
@ -129,6 +129,59 @@ class Presentation {
return false;
}
}
async hideAndRestorePresentation(testName) {
try {
await this.modPage.waitForSelector(e.whiteboard);
await this.modPage.screenshot(testName, '01-after-close-audio-modal');
await this.modPage.waitAndClick(e.minimizePresentation);
const presentationWasRemoved = await this.modPage.wasRemoved(e.presentationContainer);
await this.modPage.screenshot(testName, '02-minimize-presentation');
await this.modPage.waitAndClick(e.restorePresentation);
const presentationWasRestored = await this.modPage.hasElement(e.presentationContainer);
await this.modPage.screenshot(testName, '03-restore-presentation');
return presentationWasRemoved && presentationWasRestored;
} catch (err) {
await this.modPage.logger(err);
return false;
}
}
async startExternalVideo(testName) {
try {
await this.modPage.waitForSelector(e.whiteboard);
await this.modPage.screenshot(testName, '01-after-close-audio-modal');
await this.modPage.waitAndClick(e.actions);
await this.modPage.waitAndClick(e.externalVideoBtn);
await this.modPage.waitForSelector(e.externalVideoModalHeader);
await this.modPage.type(e.videoModalInput, e.youtubeLink);
await this.modPage.screenshot(testName, '02-before-start-sharing-video');
await this.modPage.waitAndClick(e.startShareVideoBtn);
const modFrame = await this.getFrame(this.modPage, e.youtubeFrame);
await this.modPage.screenshot(testName, '03-modPage-after-rendering-frame');
const userFrame = await this.getFrame(this.userPage, e.youtubeFrame);
await this.userPage.screenshot(testName, '03-userPage-after-rendering-frame');
const resp = (await modFrame.hasElement('video')) && (await userFrame.hasElement('video'));
return resp === true;
} catch (err) {
await this.modPage.logger(err);
return false;
}
}
async getFrame(page, frameSelector) {
await page.waitForSelector(frameSelector);
const handleFrame = await page.page.$(frameSelector);
const contentFrame = await handleFrame.contentFrame();
const frame = new Page(contentFrame);
await frame.waitForSelector(e.ytFrameTitle);
return frame;
}
}
module.exports = exports = Presentation;

View File

@ -98,5 +98,50 @@ const presentationTest = () => {
expect(response).toBe(true);
Page.checkRegression(24.62, screenshot);
});
test('Hide/Restore presentation', async () => {
const test = new Presentation();
let response;
let screenshot;
try {
const testName = 'hideAndRestorePresentation';
await test.modPage.logger('begin of ', testName);
await test.initModPage(testName);
await test.modPage.startRecording(testName);
response = await test.hideAndRestorePresentation(testName);
await test.modPage.stopRecording();
screenshot = await test.modPage.page.screenshot();
await test.modPage.logger('end of ', testName);
} catch (e) {
await test.modPage.logger(e);
} finally {
await test.modPage.close();
}
expect(response).toBe(true);
Page.checkRegression(24.62, screenshot);
});
test('Start external video', async () => {
const test = new Presentation();
let response;
let screenshot;
try {
const testName = 'startExternalVideo';
await test.modPage.logger('begin of ', testName);
await test.initPages(testName);
await test.modPage.startRecording(testName);
response = await test.startExternalVideo(testName);
await test.modPage.stopRecording();
screenshot = await test.modPage.page.screenshot();
await test.modPage.logger('end of ', testName);
} catch (e) {
await test.modPage.logger(e);
} finally {
await closePages(test.modPage, test.userPage);
}
expect(response).toBe(true);
Page.checkRegression(24.62, screenshot);
});
};
module.exports = exports = presentationTest;

View File

@ -6,7 +6,7 @@ usage() {
BBB Health Check
OPTIONS:
-t <test name: webcamlayout/whiteboard/webcam/virtualizedlist/user/trigger/sharednotes/screenshare/presentation/polling/notifications/customparameters/chat/breakout/audio/all>
-t <test name: whiteboard/webcam/virtualizedlist/user/trigger/sharednotes/screenshare/presentation/polling/notifications/customparameters/chat/breakout/audio/all>
-u Print usage
HERE

View File

@ -8,14 +8,12 @@ class ShareScreen extends Page {
super();
}
async test() {
async startSharing() {
try {
await util.startScreenshare(this);
await this.waitForSelector(e.screenshareConnecting);
await this.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
const response = await this.hasElement(e.isSharingScreen, true);
const resp = await this.hasElement(e.isSharingScreen);
return response === true;
return resp === true;
} catch (err) {
await this.logger(err);
return false;

View File

@ -22,7 +22,7 @@ const screenShareTest = () => {
await test.logger('begin of ', testName);
await test.init(true, true, testName)
await test.startRecording(testName);
response = await test.test();
response = await test.startSharing();
await test.logger('end of ', testName);
await test.stopRecording();
screenshot = await test.page.screenshot();

View File

@ -3,9 +3,6 @@ const { VIDEO_LOADING_WAIT_TIME } = require('../core/constants');
async function startScreenshare(test) {
await test.waitAndClick(e.startScreenSharing);
}
async function waitForScreenshareContainer(test) {
await test.waitForSelector(e.screenshareConnecting);
await test.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
}
@ -16,5 +13,4 @@ async function getScreenShareBreakoutContainer(test) {
}
exports.getScreenShareBreakoutContainer = getScreenShareBreakoutContainer;
exports.startScreenshare = startScreenshare;
exports.waitForScreenshareContainer = waitForScreenshareContainer;
exports.startScreenshare = startScreenshare;

View File

@ -246,9 +246,9 @@ class MultiUsers {
);
await this.userPage.screenshot(testName, '04-connection-network-success');
return true;
} catch (e) {
} catch (er) {
await this.userPage.screenshot(testName, '04-connection-network-failed');
this.userPage.logger(e);
this.userPage.logger(er);
return false;
}
} catch (err) {
@ -280,8 +280,8 @@ class MultiUsers {
await this.page1.waitAndClick(e.userListButton);
await this.page2.waitAndClick(e.userListButton);
await this.page2.waitAndClick(e.chatButtonKey);
const onUserListPanel = await this.page1.wasRemoved(e.hidePresentation);
const onChatPanel = await this.page2.wasRemoved(e.hidePresentation);
const onUserListPanel = await this.page1.wasRemoved(e.minimizePresentation);
const onChatPanel = await this.page2.wasRemoved(e.minimizePresentation);
return onUserListPanel && onChatPanel;
} catch (err) {

View File

@ -53,7 +53,6 @@ class Status extends Page {
async disableScreenshareFromConnectionStatus() {
try {
await utilScreenshare.startScreenshare(this);
await utilScreenshare.waitForScreenshareContainer(this);
await util.connectionStatus(this);
await this.waitAndClickElement(e.dataSavingScreenshare);
await this.waitAndClickElement(e.closeConnectionStatusModal);
@ -72,7 +71,6 @@ class Status extends Page {
await this.joinMicrophone();
await this.shareWebcam(true, ELEMENT_WAIT_LONGER_TIME);
await utilScreenshare.startScreenshare(this);
await utilScreenshare.waitForScreenshareContainer(this);
await util.connectionStatus(this);
await sleep(5000);
const connectionStatusItemEmpty = await this.page.evaluate(checkElementLengthEqualTo, e.connectionStatusItemEmpty, 0);

View File

@ -54,5 +54,28 @@ const webcamTest = () => {
expect(response).toBe(true);
Page.checkRegression(7.5, screenshot);
});
test('Checks webcam talking indicator', async () => {
const test = new Share();
let response;
let screenshot;
try {
const testName = 'joinWebcamAndMicrophone';
await test.logger('begin of ', testName);
await test.init(true, false, testName);
await test.startRecording(testName);
await test.webcamLayoutStart();
response = await test.webcamLayoutTest(testName);
await test.logger('end of ', testName);
await test.stopRecording();
screenshot = await test.page.screenshot();
} catch (err) {
await test.logger(err);
} finally {
await test.close();
}
expect(response).toBe(true);
Page.checkRegression(10.83, screenshot);
});
};
module.exports = exports = webcamTest;

View File

@ -1,36 +0,0 @@
const Page = require('../core/page');
const Share = require('./share');
const { toMatchImageSnapshot } = require('jest-image-snapshot');
const { MAX_WEBCAM_LAYOUT_TEST_TIMEOUT } = require('../core/constants');
expect.extend({ toMatchImageSnapshot });
const webcamLayoutTest = () => {
beforeEach(() => {
jest.setTimeout(MAX_WEBCAM_LAYOUT_TEST_TIMEOUT);
});
test('Join Webcam and microphone', async () => {
const test = new Share();
let response;
let screenshot;
try {
const testName = 'joinWebcamAndMicrophone';
await test.logger('begin of ', testName);
await test.init(true, false, testName);
await test.startRecording(testName);
await test.webcamLayoutStart();
response = await test.webcamLayoutTest(testName);
await test.logger('end of ', testName);
await test.stopRecording();
screenshot = await test.page.screenshot();
} catch (err) {
await test.logger(err);
} finally {
await test.close();
}
expect(response).toBe(true);
Page.checkRegression(10.83, screenshot);
});
};
module.exports = exports = webcamLayoutTest;

View File

@ -1,3 +0,0 @@
const webcamLayoutTest = require('./webcamlayout.obj');
describe('Webcams Layout', webcamLayoutTest);

View File

@ -407,3 +407,6 @@ endWhenNoModerator=false
# Number of minutes to wait for moderator rejoin before end meeting (if `endWhenNoModerator` enabled)
endWhenNoModeratorDelayInMinutes=1
# Allow endpoint with current BigBlueButton version
allowRevealOfBBBVersion=false

View File

@ -186,6 +186,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="endWhenNoModerator" value="${endWhenNoModerator}"/>
<property name="endWhenNoModeratorDelayInMinutes" value="${endWhenNoModeratorDelayInMinutes}"/>
<property name="defaultKeepEvents" value="${defaultKeepEvents}"/>
<property name="allowRevealOfBBBVersion" value="${allowRevealOfBBBVersion}"/>
</bean>
<import resource="doc-conversion.xml"/>

View File

@ -83,7 +83,7 @@ class ApiController {
withFormat {
xml {
render(text: responseBuilder.buildMeetingVersion(paramsProcessorUtil.getApiVersion(), RESP_CODE_SUCCESS), contentType: "text/xml")
render(text: responseBuilder.buildMeetingVersion(paramsProcessorUtil.getApiVersion(), paramsProcessorUtil.getBbbVersion(), RESP_CODE_SUCCESS), contentType: "text/xml")
}
}
}
@ -126,6 +126,8 @@ class ApiController {
Meeting newMeeting = paramsProcessorUtil.processCreateParams(params)
ApiErrors errors = new ApiErrors()
if (meetingService.createMeeting(newMeeting)) {
// See if the request came with pre-uploading of presentation.
uploadDocuments(newMeeting); //
@ -183,6 +185,11 @@ class ApiController {
request.getQueryString()
)
HashMap<String, String> roles = new HashMap<String, String>();
roles.put("moderator", ROLE_MODERATOR);
roles.put("viewer", ROLE_ATTENDEE);
if(!(validationResponse == null)) {
invalid(validationResponse.getKey(), validationResponse.getValue(), REDIRECT_RESPONSE)
return
@ -247,6 +254,10 @@ class ApiController {
role = Meeting.ROLE_ATTENDEE
}
if (!StringUtils.isEmpty(params.role) && roles.containsKey(params.role.toLowerCase())) {
role = roles.get(params.role.toLowerCase());
}
// We preprend "w_" to our internal meeting Id to indicate that this is a web user.
// For users joining using the phone, we will prepend "v_" so it will be easier
// to distinguish users who doesn't have a web client. (ralam june 12, 2017)
@ -358,13 +369,10 @@ class ApiController {
session[sessionToken] = sessionToken
meetingService.addUserSession(sessionToken, us)
logSessionInfo()
//Identify which of these to logs should be used. sessionToken or user-token
log.info("Session sessionToken for " + us.fullname + " [" + session[sessionToken] + "]")
log.info("Session user-token for " + us.fullname + " [" + session['user-token'] + "]")
logSession()
log.info("Session token: ${sessionToken}")
// Process if we send the user directly to the client or
@ -792,8 +800,6 @@ class ApiController {
String API_CALL = 'enter'
log.debug CONTROLLER_NAME + "#${API_CALL}"
logSessionInfo()
String respMessage = "Session not found."
boolean reject = false;
@ -835,7 +841,6 @@ class ApiController {
logoutUrl = us.logoutUrl
}
logSession()
log.info("Session token: ${sessionToken}")
response.addHeader("Cache-Control", "no-cache")
@ -1412,8 +1417,6 @@ class ApiController {
return false
}
logSession()
if (!session[token]) {
log.info("Session for token ${token} not found")

View File

@ -3,6 +3,7 @@
<response>
<#-- Where code is a 'SUCCESS' String -->
<returncode>${returnCode}</returncode>
<version>${version}</version>
<apiVersion>${apiVersion}</apiVersion>
<bbbVersion>${bbbVersion}</bbbVersion>
</response>
</#compress>

View File

@ -45,7 +45,7 @@ export ROOT_URL=http://127.0.0.1/html5client
export MONGO_OPLOG_URL=mongodb://127.0.1.1/local
export MONGO_URL=mongodb://127.0.1.1/meteor
export NODE_ENV=production
export NODE_VERSION=node-v14.17.6-linux-x64
export NODE_VERSION=node-v14.18.1-linux-x64
export SERVER_WEBSOCKET_COMPRESSION=0
export BIND_IP=127.0.0.1
PORT=$PORT /usr/share/$NODE_VERSION/bin/node --max-old-space-size=2048 --max_semi_space_size=128 main.js NODEJS_BACKEND_INSTANCE_ID=$INSTANCE_ID

View File

@ -45,7 +45,7 @@ export ROOT_URL=http://127.0.0.1/html5client
export MONGO_OPLOG_URL=mongodb://127.0.1.1/local
export MONGO_URL=mongodb://127.0.1.1/meteor
export NODE_ENV=production
export NODE_VERSION=node-v14.17.6-linux-x64
export NODE_VERSION=node-v14.18.1-linux-x64
export SERVER_WEBSOCKET_COMPRESSION=0
export BIND_IP=127.0.0.1
PORT=$PORT /usr/share/$NODE_VERSION/bin/node --max-old-space-size=2048 --max_semi_space_size=128 main.js

View File

@ -35,6 +35,7 @@ mkdir -p staging/usr/share/meteor
rm -rf /tmp/html5-build
mkdir -p /tmp/html5-build
npm -v
meteor npm -v
meteor node -v
cat .meteor/release
@ -42,19 +43,29 @@ cat .meteor/release
# meteor version control was moved to the Dockerfile of the image used in .gitlab-ci.yml
# meteor update --allow-superuser --release 2.3.6
# build the HTML5 client
# Install the npm dependencies needed for the HTML5 client.
# Argument 'c' means package-lock.json will be respected
# --production means we won't be installing devDependencies
meteor npm ci --production
METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build /tmp/html5-build --architecture os.linux.x86_64 --allow-superuser
# Build the HTML5 client https://guide.meteor.com/deployment.html#custom-deployment
# https://docs.meteor.com/environment-variables.html#METEOR-DISABLE-OPTIMISTIC-CACHING - disable caching because we're only building once
# --allow-superuser
# --directory - instead of creating tar.gz and then extracting (which is the default option)
METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build /tmp/html5-build --architecture os.linux.x86_64 --allow-superuser --directory
# extract, install the npm dependencies, then copy to staging
tar xfz /tmp/html5-build/bbb-html5_${VERSION}_${DISTRO}.tar.gz -C /tmp/html5-build/
# Install the npm dependencies, then copy to staging
cd /tmp/html5-build/bundle/programs/server/
npm i --production
# Install Meteor related dependencies
# Note that we don't use "c" argument as there is no package-lock.json here
# only package.json. The dependencies for bbb-html5 are already installed in
# /usr/share/meteor/bundle/programs/server/npm/node_modules/ and not in
# /usr/share/meteor/bundle/programs/server/node_modules
npm i
cd -
cp -r /tmp/html5-build/bundle staging/usr/share/meteor
cp $DISTRO/systemd_start.sh staging/usr/share/meteor/bundle
chmod +x staging/usr/share/meteor/bundle/systemd_start.sh
@ -81,11 +92,11 @@ cp $DISTRO/bbb-html5-frontend@.service staging/usr/lib/systemd/system
mkdir -p staging/usr/share
if [ ! -f node-v14.17.6-linux-x64.tar.gz ]; then
wget https://nodejs.org/dist/v14.17.6/node-v14.17.6-linux-x64.tar.gz
if [ ! -f node-v14.18.1-linux-x64.tar.gz ]; then
wget https://nodejs.org/dist/v14.18.1/node-v14.18.1-linux-x64.tar.gz
fi
cp node-v14.17.6-linux-x64.tar.gz staging/usr/share
cp node-v14.18.1-linux-x64.tar.gz staging/usr/share
if [ -f staging/usr/share/meteor/bundle/programs/web.browser/head.html ]; then
sed -i "s/VERSION/$(($BUILD))/" staging/usr/share/meteor/bundle/programs/web.browser/head.html