From 2a9fdebd618b9475bcde36021c64f36f8b7622c6 Mon Sep 17 00:00:00 2001
From: Mohamed Amine Ben Salah <damineone@gmail.com>
Date: Tue, 25 May 2021 13:05:11 -0300
Subject: [PATCH] Mobile/Tablet devices automated tests for mobile/tablet on
 ios/android devices (#12173)

* adds unability to see screenshare button on mobile devices test specs

* simplify code in testMobileDevice()

* userlist and chat panels should not appear at page load in mobile devices

* lint

* updates outdated audio specs due to leaveAudio changes

* correct clicks on disconnectAudio elements

* whiteboard not visible on userlistPanel or on chatPanel

* reworks mobile devices/usersagents

* fixes screenshare mobile/tablet specs

* adds whiteboardNotAppearOnMobile spec

* adds Chat Panel specification to Mobile-Tablet specs

* simplify getArgs() functions for all devices
---
 .../input-stream-live-selector/component.jsx  |   1 +
 .../ui/components/panel-manager/component.jsx |   2 +
 .../tests/puppeteer/audio.obj.js              |   4 +-
 .../tests/puppeteer/audio/elements.js         |   3 +-
 .../tests/puppeteer/breakout/create.js        |   4 +-
 .../tests/puppeteer/core/devices.js           |  59 +++++
 .../tests/puppeteer/core/elements.js          |   4 +-
 .../tests/puppeteer/core/page.js              | 204 ++++--------------
 .../tests/puppeteer/core/profiles.js          |  43 ----
 .../tests/puppeteer/customparameters.obj.js   |  20 +-
 .../puppeteer/notifications/notifications.js  |   2 +-
 .../tests/puppeteer/screenshare.obj.js        |  87 +++++++-
 .../puppeteer/screenshare/screenshare.js      |  29 ++-
 .../tests/puppeteer/screenshare/util.js       |   1 +
 .../tests/puppeteer/user.obj.js               | 199 ++++++++++-------
 .../tests/puppeteer/user/elements.js          |   4 +
 .../tests/puppeteer/user/multiusers.js        |  60 +++++-
 .../tests/puppeteer/webcam.obj.js             |   4 +-
 .../tests/puppeteer/webcamlayout.obj.js       |   2 +-
 19 files changed, 415 insertions(+), 317 deletions(-)
 create mode 100644 bigbluebutton-html5/tests/puppeteer/core/devices.js

diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
index 94c2186c4e..f384f1f5a7 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
@@ -298,6 +298,7 @@ class InputStreamLiveSelector extends Component {
         <DropdownListItem
           key="leaveAudioButtonKey"
           className={styles.stopButton}
+          data-test="disconnectAudio"
           label={intl.formatMessage(intlMessages.leaveAudio)}
           onClick={() => handleLeaveAudio()}
           accessKey={shortcuts.leaveaudio}
diff --git a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
index 4fdb2321b4..662bcf55c8 100755
--- a/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/panel-manager/component.jsx
@@ -316,6 +316,7 @@ class PanelManager extends Component {
 
     return (
       <div
+        data-test="userListPanel"
         className={styles.userList}
         aria-label={intl.formatMessage(intlMessages.userListLabel)}
         key={enableResize ? null : this.userlistKey}
@@ -364,6 +365,7 @@ class PanelManager extends Component {
     return (
       <section
         id="chatPanel"
+        data-test="chatPanel"
         className={styles.chat}
         aria-label={intl.formatMessage(intlMessages.chatLabel)}
         key={enableResize ? null : this.chatKey}
diff --git a/bigbluebutton-html5/tests/puppeteer/audio.obj.js b/bigbluebutton-html5/tests/puppeteer/audio.obj.js
index fcb5989fdd..7afe84b0fb 100644
--- a/bigbluebutton-html5/tests/puppeteer/audio.obj.js
+++ b/bigbluebutton-html5/tests/puppeteer/audio.obj.js
@@ -17,7 +17,7 @@ const audioTest = () => {
     try {
       const testName = 'joinWithListenOnly';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithAudio(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       response = await test.test();
       await test.stopRecording();
@@ -44,7 +44,7 @@ const audioTest = () => {
     try {
       const testName = 'joinWithMicrophone';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithAudio(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       response = await test.microphone();
       await test.stopRecording();
diff --git a/bigbluebutton-html5/tests/puppeteer/audio/elements.js b/bigbluebutton-html5/tests/puppeteer/audio/elements.js
index 5604689e63..6e2d6e2136 100644
--- a/bigbluebutton-html5/tests/puppeteer/audio/elements.js
+++ b/bigbluebutton-html5/tests/puppeteer/audio/elements.js
@@ -1,10 +1,11 @@
 exports.joinAudio = 'button[aria-label="Join audio"]';
+exports.leaveAudio = 'button[aria-label="Change/Leave audio"]';
+exports.disconnectAudio = 'li[data-test="disconnectAudio"]';
 exports.listen = 'button[aria-label="Listen only"]';
 exports.connectingStatus = 'div[class^="connecting-"]';
 exports.connecting = 'span[data-test="connecting"]';
 exports.connectingToEchoTest = 'span[data-test="connectingToEchoTest"]';
 exports.audioAudible = 'button[aria-label="Echo is audible"]';
-exports.leaveAudio = 'button[aria-label="Leave audio"]';
 exports.microphone = 'button[aria-label="Microphone"]';
 exports.muteMicrophoneBtn = 'button[aria-label="Mute"]';
 exports.whiteboard = 'svg[data-test="whiteboard"]';
diff --git a/bigbluebutton-html5/tests/puppeteer/breakout/create.js b/bigbluebutton-html5/tests/puppeteer/breakout/create.js
index 83da8ab0cc..2f7f16d7db 100644
--- a/bigbluebutton-html5/tests/puppeteer/breakout/create.js
+++ b/bigbluebutton-html5/tests/puppeteer/breakout/create.js
@@ -172,7 +172,7 @@ class Create {
   // Initialize a Moderator session
   async joinWithUser3(testName) {
     if (testName === 'joinBreakoutroomsWithAudio') {
-      await this.page3.init(Page.getArgsWithAudio(), this.page1.meetingId, { ...params, fullName: 'Moderator3' }, undefined, testName);
+      await this.page3.init(Page.getArgs(), this.page1.meetingId, { ...params, fullName: 'Moderator3' }, undefined, testName);
       await this.page3.closeAudioModal();
       await this.page3.waitForSelector(be.breakoutRoomsButton, ELEMENT_WAIT_TIME);
       await this.page3.click(be.breakoutRoomsButton, true);
@@ -205,7 +205,7 @@ class Create {
         await page3[2].screenshot({ path: path.join(__dirname, `../${process.env.TEST_FOLDER}/test-${today}-${testName}/screenshots/00-breakout-page03-user-joined-with-mic-before-check-${testName}.png`) });
       }
     } else if (testName === 'joinBreakoutroomsWithVideo') {
-      await this.page3.init(Page.getArgsWithVideo(), this.page1.meetingId, { ...params, fullName: 'Moderator3' }, undefined, testName);
+      await this.page3.init(Page.getArgs(), this.page1.meetingId, { ...params, fullName: 'Moderator3' }, undefined, testName);
       await this.page3.closeAudioModal();
       await this.page3.waitForSelector(be.breakoutRoomsButton, ELEMENT_WAIT_TIME);
       await this.page3.click(be.breakoutRoomsButton, true);
diff --git a/bigbluebutton-html5/tests/puppeteer/core/devices.js b/bigbluebutton-html5/tests/puppeteer/core/devices.js
new file mode 100644
index 0000000000..1aa8480e32
--- /dev/null
+++ b/bigbluebutton-html5/tests/puppeteer/core/devices.js
@@ -0,0 +1,59 @@
+const devices = [
+    {
+        name: 'iPhone X',
+        userAgent:
+          'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
+        viewport: {
+          width: 375,
+          height: 812,
+          deviceScaleFactor: 3,
+          isMobile: true,
+          hasTouch: true,
+          isLandscape: false,
+        },
+    },
+    {
+        name: 'iPad Pro',
+        userAgent:
+          'Mozilla/5.0 (iPad; CPU OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
+        viewport: {
+          width: 1024,
+          height: 720,
+          deviceScaleFactor: 2,
+          isMobile: true,
+          hasTouch: true,
+          isLandscape: false,
+        },
+    },
+    {
+        name: 'Galaxy Note 3',
+        userAgent:
+          'Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-N900V 4G Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4408.2 Mobile Safari/537.36',
+        viewport: {
+          width: 360,
+          height: 640,
+          deviceScaleFactor: 3,
+          isMobile: true,
+          hasTouch: true,
+          isLandscape: false,
+        },
+    },
+    {
+        name: 'Linux Desktop',
+        userAgent:
+          'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
+        viewport: {
+          width: 1024,
+          height: 720,    
+          deviceScaleFactor: 1,
+          isMobile: false,
+          hasTouch: false,
+          isLandscape: true,
+        },
+    },
+]
+
+const devicesMap = {};
+for (const device of devices)
+    devicesMap[device.name] = device;
+module.exports = devicesMap;
diff --git a/bigbluebutton-html5/tests/puppeteer/core/elements.js b/bigbluebutton-html5/tests/puppeteer/core/elements.js
index d752fe9b24..7ff20a8b51 100644
--- a/bigbluebutton-html5/tests/puppeteer/core/elements.js
+++ b/bigbluebutton-html5/tests/puppeteer/core/elements.js
@@ -9,14 +9,14 @@ exports.alerts = '.toastify-content';
 exports.isTalking = '[data-test="isTalking"]';
 exports.wasTalking = '[data-test="wasTalking"]';
 exports.joinAudio = 'button[data-test="joinAudio"]';
-exports.leaveAudio = 'button[data-test="leaveAudio"]';
+exports.leaveAudio = 'button[aria-label="Change/Leave audio"]';
+exports.disconnectAudio = 'li[data-test="disconnectAudio"]';
 
 exports.actions = 'button[aria-label="Actions"]';
 exports.options = 'button[aria-label="Options"]';
 exports.userList = 'button[aria-label="Users and Messages Toggle"]';
 exports.joinAudio = 'button[aria-label="Join Audio"]';
 exports.connectingStatus = 'div[class^="connecting--"]';
-exports.leaveAudio = 'button[aria-label="Leave Audio"]';
 exports.videoMenu = 'button[aria-label="Open video menu dropdown"]';
 exports.screenShare = 'button[aria-label="Share your screen"]';
 exports.screenshareConnecting = 'div[data-test="screenshareConnecting"]';
diff --git a/bigbluebutton-html5/tests/puppeteer/core/page.js b/bigbluebutton-html5/tests/puppeteer/core/page.js
index 72a55d7832..fcfaeea282 100644
--- a/bigbluebutton-html5/tests/puppeteer/core/page.js
+++ b/bigbluebutton-html5/tests/puppeteer/core/page.js
@@ -11,7 +11,11 @@ const params = require('../params');
 const { ELEMENT_WAIT_TIME } = require('./constants');
 const e = require('./elements');
 const ue = require('../user/elements');
-const { NETWORK_PRESETS, USER_AGENTS, MOBILE_DEVICES } = require('./profiles');
+const { NETWORK_PRESETS } = require('./profiles');
+const audioCapture = `--use-file-for-fake-audio-capture=${path.join(__dirname, '../media/audio.wav')}`;
+const videoCapture = `--use-file-for-fake-video-capture=${path.join(__dirname, '../media/video_rgb.y4m')}`;
+const devices = require('./devices');
+const linuxDesktop = devices['Linux Desktop'];
 
 class Page {
   constructor(name) {
@@ -38,7 +42,7 @@ class Page {
   }
 
   // Join BigBlueButton meeting
-  async init(args, meetingId, newParams, customParameter, testFolderName, connectionPreset) {
+  async init(args, meetingId, newParams, customParameter, testFolderName, connectionPreset, deviceX) {
     try {
       this.effectiveParams = newParams || params;
       const isModerator = this.effectiveParams.moderatorPW;
@@ -50,6 +54,8 @@ class Page {
         this.browser = await puppeteer.launch(args);
       }
       this.page = await this.browser.newPage();
+      this.page.emulate(deviceX || linuxDesktop);
+      await this.getUserAgent(this);
 
       // Connect to Chrome DevTools
       const client = await this.page.target().createCDPSession();
@@ -57,9 +63,9 @@ class Page {
       // Set throttling property
       await client.send('Network.emulateNetworkConditions', connectionPreset || NETWORK_PRESETS.WiFi);
 
-      if (process.env.DEVICE_NAME === 'Desktop') {
-        await this.page.setViewport({ width: 1280, height: 720 });
-      }
+      // if (process.env.DEVICE_NAME === 'Desktop') {
+      //   await this.page.setViewport({ width: 1024, height: 720 });
+      // }
 
       this.page.setDefaultTimeout(3600000);
 
@@ -67,6 +73,7 @@ class Page {
       // this.page.on('console', async msg => console[msg._type](
       //   ...await Promise.all(msg.args().map(arg => arg.jsonValue()))
       // ));
+
       await this.page.setExtraHTTPHeaders({
         'Accept-Language': 'en-US',
       });
@@ -76,7 +83,6 @@ class Page {
 
       const joinURL = helper.getJoinURL(this.meetingId, this.effectiveParams, isModerator, customParameter);
       await this.page.goto(joinURL, { waitUntil: 'networkidle2' });
-      await this.getUserAgent();
 
       if (process.env.BBB_COLLECT_METRICS === 'true' && process.env.IS_MOBILE !== 'true') {
         await this.waitForSelector(ue.anyUser, ELEMENT_WAIT_TIME);
@@ -107,12 +113,17 @@ class Page {
     const parsedSettings = await this.getSettingsYaml();
     const listenOnlyCallTimeout = parseInt(parsedSettings.public.media.listenOnlyCallTimeout);
     await this.waitForSelector(e.leaveAudio, listenOnlyCallTimeout);
+    await this.click(e.leaveAudio, ELEMENT_WAIT_TIME);
+    await this.waitForSelector(e.disconnectAudio, ELEMENT_WAIT_TIME);
+    await this.click(e.disconnectAudio, true);
   }
 
   // Leave audio
   async leaveAudio() {
     await this.waitForSelector(e.leaveAudio, ELEMENT_WAIT_TIME);
     await this.click(e.leaveAudio, true);
+    await this.waitForSelector(e.disconnectAudio, ELEMENT_WAIT_TIME);
+    await this.click(e.disconnectAudio, true);
     await this.waitForSelector(e.joinAudio, ELEMENT_WAIT_TIME);
   }
 
@@ -163,42 +174,20 @@ class Page {
     return await document.querySelectorAll(element)[0];
   }
 
-  async getUserAgent() {
-    const useragent = await this.page.evaluate('navigator.userAgent');
+  async getUserAgent(test) {
+    const useragent = await test.page.evaluate('navigator.userAgent');
     console.log({ useragent });
     return useragent;
   }
 
   // Get the default arguments for creating a page
   static getArgs() {
-    const args = [
-      '--no-sandbox',
-      '--use-fake-ui-for-media-stream',
-      '--use-fake-device-for-media-stream',
-      '--no-default-browser-check',
-      '--window-size=1280,1000',
-      '--lang=en-US',
-    ];
-    return {
-      headless: false,
-      args,
-      defaultViewport: {
-        width: 1280,
-        height: 805,
-      },
-      ignoreDefaultArgs: [
-        '--enable-automation',
-      ],
-    };
-  }
-
-  static getArgsWithAudio() {
     if (process.env.BROWSERLESS_ENABLED === 'true') {
       const args = [
         '--no-sandbox',
         '--use-fake-ui-for-media-stream',
         '--use-fake-device-for-media-stream',
-        '--window-size=1280,720',
+        '--window-size=1024,720',
         '--lang=en-US',
       ];
       return {
@@ -211,8 +200,9 @@ class Page {
       '--use-fake-ui-for-media-stream',
       '--use-fake-device-for-media-stream',
       '--no-default-browser-check',
-      '--window-size=1280,1000',
-      `--use-file-for-fake-audio-capture=${path.join(__dirname, '../media/audio.wav')}`,
+      '--window-size=1150,980',
+      audioCapture,
+      videoCapture,
       '--allow-file-access',
       '--lang=en-US',
     ];
@@ -220,8 +210,8 @@ class Page {
       headless: false,
       args,
       defaultViewport: {
-        width: 1280,
-        height: 805,
+        width: 1250,
+        height: 850,
       },
       ignoreDefaultArgs: [
         '--enable-automation',
@@ -229,139 +219,23 @@ class Page {
     };
   }
 
-  static getArgsWithVideo() {
-    if (process.env.BROWSERLESS_ENABLED === 'true') {
-      const args = [
-        '--no-sandbox',
-        '--use-fake-ui-for-media-stream',
-        '--use-fake-device-for-media-stream',
-        '--window-size=1280,720',
-        '--lang=en-US',
-      ];
-      return {
-        headless: true,
-        args,
-      };
-    }
-    const args = [
-      '--no-sandbox',
-      '--use-fake-ui-for-media-stream',
-      '--use-fake-device-for-media-stream',
-      '--no-default-browser-check',
-      '--window-size=1280,1000',
-      `--use-file-for-fake-video-capture=${path.join(__dirname, '../media/video_rgb.y4m')}`,
-      '--allow-file-access',
-      '--lang=en-US',
-    ];
-    return {
-      headless: false,
-      args,
-      defaultViewport: {
-        width: 1280,
-        height: 805,
-      },
-      ignoreDefaultArgs: [
-        '--enable-automation',
-      ],
-    };
+  static checkRegression(numb) {
+    if (process.env.REGRESSION_TESTING === 'true') {
+      expect(screenshot).toMatchImageSnapshot({
+        failureThreshold: numb,
+        failureThresholdType: 'percent',
+      });
+    }  
   }
 
-  static getArgsWithAudioAndVideo() {
-    if (process.env.BROWSERLESS_ENABLED === 'true') {
-      const args = [
-        '--no-sandbox',
-        '--use-fake-ui-for-media-stream',
-        '--use-fake-device-for-media-stream',
-        '--window-size=1280,720',
-        '--lang=en-US',
-      ];
-      return {
-        headless: true,
-        args,
-      };
+  async isNotVisible(el, timeout) {
+    try {
+      await this.page.waitForSelector(el, {visible: false, timeout: timeout});
+      return true;
+    } catch(e) {
+      console.log(e);
+      return false;
     }
-    const args = [
-      '--no-sandbox',
-      '--use-fake-ui-for-media-stream',
-      '--use-fake-device-for-media-stream',
-      '--no-default-browser-check',
-      '--window-size=1280,1000',
-      `--use-file-for-fake-audio-capture=${path.join(__dirname, '../media/audio.wav')}`,
-      `--use-file-for-fake-video-capture=${path.join(__dirname, '../media/video_rgb.y4m')}`,
-      '--allow-file-access',
-      '--lang=en-US',
-    ];
-    return {
-      headless: false,
-      args,
-      defaultViewport: {
-        width: 1280,
-        height: 805,
-      },
-      ignoreDefaultArgs: [
-        '--enable-automation',
-      ],
-    };
-  }
-
-  static iPhoneXArgs() {
-    const args = [
-      '--no-sandbox',
-      '--use-fake-ui-for-media-stream',
-      '--use-fake-device-for-media-stream',
-      `--user-agent=${USER_AGENTS.iPhoneX}`,
-      `--window-size=${MOBILE_DEVICES.iPhoneX.defaultViewport.width + 250},${MOBILE_DEVICES.iPhoneX.defaultViewport.height}`,
-      `--use-file-for-fake-audio-capture=${path.join(__dirname, '../media/audio.wav')}`,
-      `--use-file-for-fake-video-capture=${path.join(__dirname, '../media/video_rgb.y4m')}`,
-      '--allow-file-access',
-      '--lang=en-US',
-    ];
-    const mobileArgs = MOBILE_DEVICES.iPhoneX;
-    return {
-      headless: false,
-      args,
-      ...mobileArgs,
-    };
-  }
-
-  static iPadArgs() {
-    const args = [
-      '--no-sandbox',
-      '--use-fake-ui-for-media-stream',
-      '--use-fake-device-for-media-stream',
-      `--user-agent=${USER_AGENTS.iPad}`,
-      `--window-size=${MOBILE_DEVICES.iPad.defaultViewport.width},${MOBILE_DEVICES.iPad.defaultViewport.height}`,
-      `--use-file-for-fake-audio-capture=${path.join(__dirname, '../media/audio.wav')}`,
-      `--use-file-for-fake-video-capture=${path.join(__dirname, '../media/video_rgb.y4m')}`,
-      '--allow-file-access',
-      '--lang=en-US',
-    ];
-    const mobileArgs = MOBILE_DEVICES.iPad;
-    return {
-      headless: false,
-      args,
-      ...mobileArgs,
-    };
-  }
-
-  static galaxyNote3Args() {
-    const args = [
-      '--no-sandbox',
-      '--use-fake-ui-for-media-stream',
-      '--use-fake-device-for-media-stream',
-      `--user-agent=${USER_AGENTS.GalaxyNote3}`,
-      `--window-size=${MOBILE_DEVICES.GalaxyNote3.defaultViewport.width + 250},${MOBILE_DEVICES.GalaxyNote3.defaultViewport.height}`,
-      `--use-file-for-fake-audio-capture=${path.join(__dirname, '../media/audio.wav')}`,
-      `--use-file-for-fake-video-capture=${path.join(__dirname, '../media/video_rgb.y4m')}`,
-      '--allow-file-access',
-      '--lang=en-US',
-    ];
-    const mobileArgs = MOBILE_DEVICES.GalaxyNote3;
-    return {
-      headless: false,
-      args,
-      ...mobileArgs,
-    };
   }
 
   // async emulateMobile(userAgent) {
diff --git a/bigbluebutton-html5/tests/puppeteer/core/profiles.js b/bigbluebutton-html5/tests/puppeteer/core/profiles.js
index 8013322ac8..7d817e7e7e 100644
--- a/bigbluebutton-html5/tests/puppeteer/core/profiles.js
+++ b/bigbluebutton-html5/tests/puppeteer/core/profiles.js
@@ -49,46 +49,3 @@ exports.NETWORK_PRESETS = {
     latency: 2,
   },
 };
-
-// Mobile Devices (iPhoneX, GalaxyA31, iPad)
-exports.MOBILE_DEVICES = {
-  iPhoneX: {
-    devtools: true,
-    ignoreHTTPSErrors: true,
-    defaultViewport: {
-      width: 375,
-      height: 812,
-      isMobile: true,
-    },
-  },
-  GalaxyNote3: {
-    devtools: true,
-    ignoreHTTPSErrors: true,
-    defaultViewport: {
-      width: 360,
-      height: 640,
-      isMobile: true,
-    },
-  },
-  iPad: {
-    devtools: true,
-    ignoreHTTPSErrors: true,
-    defaultViewport: {
-      width: 765,
-      height: 850,
-      isMobile: true,
-    },
-  },
-};
-
-// User Agents (iPhoneX, GalaxyNote3, iPad, Desktop)
-exports.USER_AGENTS = {
-  iPhoneX:
-    'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
-  GalaxyNote3:
-    'Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-N900V 4G Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4408.2 Mobile Safari/537.36',
-  iPad:
-    'Mozilla/5.0 (iPad; CPU OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
-  Desktop:
-    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
-};
diff --git a/bigbluebutton-html5/tests/puppeteer/customparameters.obj.js b/bigbluebutton-html5/tests/puppeteer/customparameters.obj.js
index f1f264d2fc..15396b1599 100644
--- a/bigbluebutton-html5/tests/puppeteer/customparameters.obj.js
+++ b/bigbluebutton-html5/tests/puppeteer/customparameters.obj.js
@@ -50,7 +50,7 @@ const customParametersTest = () => {
     try {
       const testName = 'listenOnlyMode';
       page.logger('before ', testName);
-      response = await test.listenOnlyMode(testName, Page.getArgsWithAudio(), undefined, c.listenOnlyMode);
+      response = await test.listenOnlyMode(testName, Page.getArgs(), undefined, c.listenOnlyMode);
       await test.page1.stopRecording();
       screenshot = await test.page1.page.screenshot();
       page.logger('after ', testName);
@@ -78,7 +78,7 @@ const customParametersTest = () => {
     try {
       const testName = 'forceListenOnly';
       page.logger('before ', testName);
-      response = await test.forceListenOnly(testName, Page.getArgsWithAudio(), undefined, c.forceListenOnly);
+      response = await test.forceListenOnly(testName, Page.getArgs(), undefined, c.forceListenOnly);
       await test.page2.stopRecording();
       screenshot = await test.page2.page.screenshot();
       page.logger('after ', testName);
@@ -106,7 +106,7 @@ const customParametersTest = () => {
     try {
       const testName = 'skipCheck';
       page.logger('before ', testName);
-      response = await test.skipCheck(testName, Page.getArgsWithAudio(), undefined, c.skipCheck);
+      response = await test.skipCheck(testName, Page.getArgs(), undefined, c.skipCheck);
       await test.page1.stopRecording();
       screenshot = await test.page1.page.screenshot();
       page.logger('after ', testName);
@@ -134,7 +134,7 @@ const customParametersTest = () => {
     try {
       const testName = 'skipCheckOnFirstJoin';
       page.logger('before ', testName);
-      response = await test.skipCheckOnFirstJoin(testName, Page.getArgsWithAudio(), undefined, c.skipCheckOnFirstJoin);
+      response = await test.skipCheckOnFirstJoin(testName, Page.getArgs(), undefined, c.skipCheckOnFirstJoin);
       await test.page1.stopRecording();
       screenshot = await test.page1.page.screenshot();
       page.logger('after ', testName);
@@ -303,7 +303,7 @@ const customParametersTest = () => {
     try {
       const testName = 'enableVideo';
       page.logger('before ', testName);
-      response = await test.enableVideo(testName, Page.getArgsWithVideo(), undefined, c.enableVideo);
+      response = await test.enableVideo(testName, Page.getArgs(), undefined, c.enableVideo);
       await test.page1.stopRecording();
       screenshot = await test.page1.page.screenshot();
       page.logger('after ', testName);
@@ -331,7 +331,7 @@ const customParametersTest = () => {
     try {
       const testName = 'autoShareWebcam';
       page.logger('before ', testName);
-      response = await test.autoShareWebcam(testName, Page.getArgsWithVideo(), undefined, c.autoShareWebcam);
+      response = await test.autoShareWebcam(testName, Page.getArgs(), undefined, c.autoShareWebcam);
       await test.page1.stopRecording();
       screenshot = await test.page1.page.screenshot();
       page.logger('after ', testName);
@@ -692,7 +692,7 @@ const customParametersTest = () => {
     try {
       const testName = 'skipVideoPreview';
       page.logger('before ', testName);
-      response = await test.skipVideoPreview(testName, Page.getArgsWithVideo(), undefined, `${c.skipVideoPreview}`);
+      response = await test.skipVideoPreview(testName, Page.getArgs(), undefined, `${c.skipVideoPreview}`);
       await test.page1.stopRecording();
       page.logger('after ', testName);
     } catch (e) {
@@ -712,7 +712,7 @@ const customParametersTest = () => {
     try {
       const testName = 'skipVideoPreviewOnFirstJoin';
       page.logger('before ', testName);
-      response = await test.skipVideoPreviewOnFirstJoin(testName, Page.getArgsWithVideo(), undefined, `${c.skipVideoPreviewOnFirstJoin}`);
+      response = await test.skipVideoPreviewOnFirstJoin(testName, Page.getArgs(), undefined, `${c.skipVideoPreviewOnFirstJoin}`);
       await test.page1.stopRecording();
       page.logger('after ', testName);
     } catch (e) {
@@ -733,7 +733,7 @@ const customParametersTest = () => {
     try {
       const testName = 'mirrorOwnWebcam';
       page.logger('before ', testName);
-      response = await test.mirrorOwnWebcam(testName, Page.getArgsWithVideo(), undefined, `${c.mirrorOwnWebcam}`);
+      response = await test.mirrorOwnWebcam(testName, Page.getArgs(), undefined, `${c.mirrorOwnWebcam}`);
       await test.page1.stopRecording();
       page.logger('after ', testName);
     } catch (e) {
@@ -753,7 +753,7 @@ const customParametersTest = () => {
     try {
       const testName = 'showParticipantsOnLogin';
       page.logger('before ', testName);
-      response = await test.showParticipantsOnLogin(testName, Page.getArgsWithVideo(), undefined, `${c.showParticipantsOnLogin}`);
+      response = await test.showParticipantsOnLogin(testName, Page.getArgs(), undefined, `${c.showParticipantsOnLogin}`);
       await test.page1.stopRecording();
       page.logger('after ', testName);
     } catch (e) {
diff --git a/bigbluebutton-html5/tests/puppeteer/notifications/notifications.js b/bigbluebutton-html5/tests/puppeteer/notifications/notifications.js
index a6bc43e94d..c72e5aba20 100644
--- a/bigbluebutton-html5/tests/puppeteer/notifications/notifications.js
+++ b/bigbluebutton-html5/tests/puppeteer/notifications/notifications.js
@@ -168,7 +168,7 @@ class Notifications extends MultiUsers {
   }
 
   async audioNotification(testName) {
-    await this.initUser3(Page.getArgsWithAudio(), undefined, testName);
+    await this.initUser3(Page.getArgs(), undefined, testName);
     await this.page3.startRecording(testName);
     await this.page3.screenshot(`${testName}`, `01-page03-initialized-${testName}`);
     await this.page3.joinMicrophone();
diff --git a/bigbluebutton-html5/tests/puppeteer/screenshare.obj.js b/bigbluebutton-html5/tests/puppeteer/screenshare.obj.js
index 33676d4472..5e453fa4c2 100644
--- a/bigbluebutton-html5/tests/puppeteer/screenshare.obj.js
+++ b/bigbluebutton-html5/tests/puppeteer/screenshare.obj.js
@@ -1,8 +1,11 @@
+const { toMatchImageSnapshot } = require('jest-image-snapshot');
 const ShareScreen = require('./screenshare/screenshare');
 const Page = require('./core/page');
-const { toMatchImageSnapshot } = require('jest-image-snapshot');
 const { MAX_SCREENSHARE_TEST_TIMEOUT } = require('./core/constants'); // core constants (Timeouts vars imported)
-
+const devices = require('./core/devices');
+const iPhonex = devices['iPhone X'];
+const galaxyNote3 = devices['Galaxy Note 3'];
+const ipadPro = devices['iPad Pro'];
 expect.extend({ toMatchImageSnapshot });
 
 const screenShareTest = () => {
@@ -17,7 +20,7 @@ const screenShareTest = () => {
     try {
       const testName = 'shareScreen';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithVideo(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       await test.closeAudioModal();
       response = await test.test();
@@ -37,5 +40,83 @@ const screenShareTest = () => {
       });
     }
   });
+
+  test('Share screen unvailable on Mobile Android', async () => {
+    process.env.IS_MOBILE = true;
+    const test = new ShareScreen();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'shareScreenAndroidMobile';
+      await test.logger('begin of ', testName);
+      response = await test.testMobileDevice(Page.getArgs(), testName, galaxyNote3);
+      await test.logger('end of ', testName);
+      await test.stopRecording();
+      screenshot = await test.page.screenshot();
+    } catch (e) {
+      await test.logger(e);
+    } finally {
+      await test.close();
+    }
+    expect(response).toBe(true);
+    if (process.env.REGRESSION_TESTING === 'true') {
+      expect(screenshot).toMatchImageSnapshot({
+        failureThreshold: 1.37,
+        failureThresholdType: 'percent',
+      });
+    }
+  });
+
+  test('Share screen unvailable on Mobile iPhone', async () => {
+    process.env.IS_MOBILE = true;
+    const test = new ShareScreen();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'shareScreenIphoneMobile';
+      await test.logger('begin of ', testName);
+      response = await test.testMobileDevice(Page.getArgs(), testName, iPhonex);
+      await test.logger('end of ', testName);
+      await test.stopRecording();
+      screenshot = await test.page.screenshot();
+    } catch (e) {
+      await test.logger(e);
+    } finally {
+      await test.close();
+    }
+    expect(response).toBe(true);
+    if (process.env.REGRESSION_TESTING === 'true') {
+      expect(screenshot).toMatchImageSnapshot({
+        failureThreshold: 1.37,
+        failureThresholdType: 'percent',
+      });
+    }
+  });
+
+  test('Share screen unvailable on Tablet iPad', async () => {
+    process.env.IS_MOBILE = true;
+    const test = new ShareScreen();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'shareScreenTabletIpad';
+      await test.logger('begin of ', testName);
+      response = await test.testMobileDevice(Page.getArgs(), testName, ipadPro);
+      await test.logger('end of ', testName);
+      await test.stopRecording();
+      screenshot = await test.page.screenshot();
+    } catch (e) {
+      await test.logger(e);
+    } finally {
+      await test.close();
+    }
+    expect(response).toBe(true);
+    if (process.env.REGRESSION_TESTING === 'true') {
+      expect(screenshot).toMatchImageSnapshot({
+        failureThreshold: 1.37,
+        failureThresholdType: 'percent',
+      });
+    }
+  });
 };
 module.exports = exports = screenShareTest;
diff --git a/bigbluebutton-html5/tests/puppeteer/screenshare/screenshare.js b/bigbluebutton-html5/tests/puppeteer/screenshare/screenshare.js
index 7a43dcd764..d96f7abc74 100644
--- a/bigbluebutton-html5/tests/puppeteer/screenshare/screenshare.js
+++ b/bigbluebutton-html5/tests/puppeteer/screenshare/screenshare.js
@@ -10,13 +10,30 @@ class ShareScreen extends Page {
   }
 
   async test() {
-    await util.startScreenshare(this.page);
+    try {
+      await util.startScreenshare(this);
+      await this.page.waitForSelector(e.screenshareConnecting, ELEMENT_WAIT_TIME);
+      await this.page.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
+      await sleep(5000);
+      const response = await util.getScreenShareContainer(this);
+      return response;
+    } catch (e) {
+      console.log(e);
+      return false;
+    }
+  }
 
-    await this.page.waitForSelector(e.screenshareConnecting, ELEMENT_WAIT_TIME);
-    await this.page.waitForSelector(e.screenShareVideo, VIDEO_LOADING_WAIT_TIME);
-    await sleep(5000);
-    const response = await util.getScreenShareContainer(this.page);
-    return response;
+  async testMobileDevice(args, testName, deviceX) {
+    await this.init(args, undefined, undefined, undefined, testName, undefined, deviceX);
+    await this.startRecording(testName);
+    await this.closeAudioModal();
+    try {
+      const screenshareBtn = await this.page.evaluate(() => document.querySelectorAll('button[aria-label="Share your screen"]').length === 0) === true;
+      return screenshareBtn;
+    } catch (e) {
+      console.log(e);
+      return false;
+    }
   }
 }
 
diff --git a/bigbluebutton-html5/tests/puppeteer/screenshare/util.js b/bigbluebutton-html5/tests/puppeteer/screenshare/util.js
index 802c722afd..cc86e391c1 100644
--- a/bigbluebutton-html5/tests/puppeteer/screenshare/util.js
+++ b/bigbluebutton-html5/tests/puppeteer/screenshare/util.js
@@ -21,6 +21,7 @@ async function getScreenShareContainer(test) {
   const response = screenShareContainer !== null;
   return response;
 }
+
 async function getScreenShareBreakoutContainer(test) {
   await test.waitForSelector(e.screenshareConnecting, { timeout: VIDEO_LOADING_WAIT_TIME });
   await test.waitForSelector(e.screenShareVideo, { timeout: VIDEO_LOADING_WAIT_TIME });
diff --git a/bigbluebutton-html5/tests/puppeteer/user.obj.js b/bigbluebutton-html5/tests/puppeteer/user.obj.js
index c3758ef6cd..b2b96fb8b8 100644
--- a/bigbluebutton-html5/tests/puppeteer/user.obj.js
+++ b/bigbluebutton-html5/tests/puppeteer/user.obj.js
@@ -5,6 +5,9 @@ const Create = require('./breakout/create');
 const MultiUsers = require('./user/multiusers');
 const { MAX_MULTIUSERS_TEST_TIMEOUT, TEST_DURATION_TIME } = require('./core/constants'); // core constants (Timeouts vars imported)
 const { NETWORK_PRESETS } = require('./core/profiles');
+const devices = require('./core/devices');
+const iPhonex = devices['iPhone X'];
+const galaxyNote3 = devices['Galaxy Note 3'];
 
 expect.extend({ toMatchImageSnapshot });
 
@@ -21,7 +24,7 @@ const userTest = () => {
     try {
       const testName = 'mobileTagName';
       await test.logger('begin of ', testName);
-      await test.init(Page.iPhoneXArgs(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, iPhonex);
       await test.startRecording(testName);
       await test.closeAudioModal();
       response = await test.mobileTagName();
@@ -34,12 +37,7 @@ const userTest = () => {
       await test.close();
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Change user status icon and check if it has changed
@@ -63,12 +61,7 @@ const userTest = () => {
       await test.close();
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Connect with 2 users and check if User1 sees User2
@@ -95,12 +88,7 @@ const userTest = () => {
       await test.close(test.page1, test.page2);
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Open Connection Status Modal and check if appears
@@ -124,12 +112,7 @@ const userTest = () => {
       await test.close();
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Open Connection Status Modal, start Webcam Share, disable Webcams in
@@ -141,7 +124,7 @@ const userTest = () => {
     try {
       const testName = 'disableWebcamsFromConnectionStatus';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithVideo(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       response = await test.disableWebcamsFromConnectionStatus();
       await test.stopRecording();
@@ -153,12 +136,7 @@ const userTest = () => {
       await test.close();
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Open Connection Status Modal, start Screenshare, disable Screenshare in
@@ -182,12 +160,7 @@ const userTest = () => {
       await test.close();
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Connect with a Good3G NETWORK_PRESET profil,  Open Connection Status Modal
@@ -199,7 +172,7 @@ const userTest = () => {
     try {
       const testName = 'reportUserInConnectionIssues';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithAudioAndVideo(), undefined, undefined, undefined, testName, NETWORK_PRESETS.Regular4G);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName, NETWORK_PRESETS.Regular4G);
       await test.startRecording(testName);
       response = await test.reportUserInConnectionIssues();
       await test.stopRecording();
@@ -211,12 +184,7 @@ const userTest = () => {
       await test.close();
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   }, TEST_DURATION_TIME);
 
   // Force bad connection profile, force disconnection
@@ -240,12 +208,7 @@ const userTest = () => {
       await test.closePage(test.page1);
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   }, TEST_DURATION_TIME);
 
   // Raise and Lower Hand and make sure that the User2 Avatar color
@@ -274,12 +237,7 @@ const userTest = () => {
       await test.close(test.page1, test.page2);
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Set Guest policy to ASK_MODERATOR
@@ -306,12 +264,7 @@ const userTest = () => {
       await test.closePage(test.page3);
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Set Guest policy to ALWAYS_ACCEPT
@@ -338,12 +291,7 @@ const userTest = () => {
       await test.closePage(test.page3);
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
-    }
+    await Page.checkRegression(2.0);
   });
 
   // Set Guest policy to ALWAYS_DENY
@@ -370,12 +318,115 @@ const userTest = () => {
       await test.closePage(test.page3);
     }
     expect(response).toBe(true);
-    if (process.env.REGRESSION_TESTING === 'true') {
-      expect(screenshot).toMatchImageSnapshot({
-        failureThreshold: 2.0,
-        failureThresholdType: 'percent',
-      });
+    await Page.checkRegression(2.0);
+  });
+
+  // Whiteboard shouldn't be accessible when
+  // chat panel or userlist are active
+  test('Whiteboard should not be accessible when chat panel or userlist are active on mobile devices', async () => {
+    const test = new MultiUsers();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'whiteboardNotAppearOnMobile';
+      await test.page1.logger('begin of ', testName);
+      await test.page1.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, iPhonex);
+      await test.page2.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, galaxyNote3);
+      await test.page1.startRecording(testName);
+      await test.page2.startRecording(testName);
+      response = await test.whiteboardNotAppearOnMobile(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);
     }
+    expect(response).toBe(true);
+    await Page.checkRegression(2.0);
+  });
+
+  // Userlist and chat panel should not appear at page
+  // load in iPhone and Android Mobile devices
+  test('Userlist does not appear at page load on iPhone and Android', async () => {
+    const test = new MultiUsers();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'userlistNotAppearOnMobile';
+      await test.page1.logger('begin of ', testName);
+      await test.page1.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, iPhonex);
+      await test.page2.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, galaxyNote3);
+      await test.page1.startRecording(testName);
+      await test.page2.startRecording(testName);
+      response = await test.userlistNotAppearOnMobile(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);
+    }
+    expect(response).toBe(true);
+    await Page.checkRegression(2.0);
+  });
+
+  // Userslist shouldn't appear when Chat Panel or Whiteboard
+  // are active on small mobile devices
+  test('Userslist should not appear when Chat Panel or Whiteboard are active on small mobile devices', async () => {
+    const test = new MultiUsers();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'userlistNotAppearOnMobile';
+      await test.page1.logger('begin of ', testName);
+      await test.page1.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, iPhonex);
+      await test.page2.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, galaxyNote3);
+      await test.page1.startRecording(testName);
+      await test.page2.startRecording(testName);
+      response = await test.userlistNotAppearOnMobile();
+      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);
+    }
+    expect(response).toBe(true);
+    await Page.checkRegression(2.0);
+  });
+
+  // Chat Panel shouldn't appear when Userlist or Whiteboard
+  // are active on small mobile devices
+  test('Chat Panel should not appear when Userlist or Whiteboard are active on small mobile devices', async () => {
+    const test = new MultiUsers();
+    let response;
+    let screenshot;
+    try {
+      const testName = 'chatPanelNotAppearOnMobile';
+      await test.page1.logger('begin of ', testName);
+      await test.page1.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, iPhonex);
+      await test.page2.init(Page.getArgs(), undefined, undefined, undefined, testName, undefined, galaxyNote3);
+      await test.page1.startRecording(testName);
+      await test.page2.startRecording(testName);
+      response = await test.chatPanelNotAppearOnMobile();
+      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);
+    }
+    expect(response).toBe(true);
+    await Page.checkRegression(2.0);
   });
 };
 module.exports = exports = userTest;
diff --git a/bigbluebutton-html5/tests/puppeteer/user/elements.js b/bigbluebutton-html5/tests/puppeteer/user/elements.js
index 2f9743e57a..6323052b40 100644
--- a/bigbluebutton-html5/tests/puppeteer/user/elements.js
+++ b/bigbluebutton-html5/tests/puppeteer/user/elements.js
@@ -28,3 +28,7 @@ exports.alwaysAccept = 'button[data-test="alwaysAccept"]';
 exports.alwaysDeny = 'button[data-test="alwaysDeny"]';
 exports.waitingUsersBtn = 'div[data-test="waitingUsersBtn"]';
 exports.joinMeetingDemoPage = 'div[class^="join-meeting"]';
+exports.chatButton = '[accesskey="P"]';
+exports.chatPanel = 'section[data-test="chatPanel"]';
+exports.userListButton = '[accesskey="U"]';
+exports.userListPanel = 'div[data-test="userListPanel"]';
diff --git a/bigbluebutton-html5/tests/puppeteer/user/multiusers.js b/bigbluebutton-html5/tests/puppeteer/user/multiusers.js
index eb5151c5f2..66b5cecdad 100644
--- a/bigbluebutton-html5/tests/puppeteer/user/multiusers.js
+++ b/bigbluebutton-html5/tests/puppeteer/user/multiusers.js
@@ -1,13 +1,15 @@
 const Page = require('../core/page');
 const params = require('../params');
 const util = require('../chat/util');
-const utilUser = require('../user/util');
+const utilUser = require('./util');
 const utilCustomParams = require('../customparameters/util');
 const pe = require('../core/elements');
 const ne = require('../notifications/elements');
 const ple = require('../polling/elemens');
 const we = require('../whiteboard/elements');
-const ue = require('../user/elements');
+const ue = require('./elements');
+const cu = require('../customparameters/elements');
+const pre = require('../presentation/elements');
 const { ELEMENT_WAIT_TIME, ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
 const { sleep } = require('../core/helper');
 
@@ -190,17 +192,17 @@ class MultiUsers {
 
   // Get Avatars Colors from Userlist and Notification toast
   async getAvatarColorAndCompareWithUserListItem() {
-    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 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);
     return avatarInToastElementColor === avatarInUserListColor;
   }
 
-
   async userOfflineWithInternetProblem() {
     try {
       await this.page1.closeAudioModal();
       await this.page2.closeAudioModal();
       await this.page2.page.evaluate(() => window.dispatchEvent(new CustomEvent('socketstats', { detail: { rtt: 2000 } })));
+      await this.page2.page.setOfflineMode(true);
       await sleep(3000);
       await this.page2.close();
       await sleep(5000);
@@ -215,6 +217,54 @@ class MultiUsers {
     }
   }
 
+  async userlistNotAppearOnMobile() {
+    try {
+      await this.page1.closeAudioModal();
+      await this.page2.closeAudioModal();
+      const userlistPanel = await this.page1.page.evaluate(utilUser.countTestElements, ue.chatButton) === false;
+      const chatPanel = await this.page2.page.evaluate(utilUser.countTestElements, ue.chatButton) === false;
+      return userlistPanel && chatPanel;
+    } catch (e) {
+      console.log(e);
+      return false;
+    }
+  }
+
+  async whiteboardNotAppearOnMobile() {
+    try {
+      await this.page1.closeAudioModal();
+      await this.page2.closeAudioModal();
+      await this.page1.click(ue.userListButton, true);
+      await this.page2.click(ue.userListButton, true);
+      await this.page2.click(ue.chatButton, 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;
+      console.log({onUserListPanel, onChatPanel});
+      await sleep(2000);
+      return onUserListPanel && onChatPanel;
+    } catch (e) {
+      console.log(e);
+      return false;
+    }
+  }
+
+  async chatPanelNotAppearOnMobile() {
+    try {
+      await this.page1.closeAudioModal();
+      await this.page2.closeAudioModal();
+      await this.page2.click(ue.userListButton, true);
+      await this.page2.click(ue.chatButton, true);
+      const whiteboard = await this.page1.page.evaluate(utilUser.countTestElements, ue.chatButton) === false;
+      const onChatPanel = await this.page2.isNotVisible(ue.chatButton, ELEMENT_WAIT_TIME) === true;
+      console.log({whiteboard, onChatPanel});
+      await sleep(2000);
+      return whiteboard && onChatPanel;
+    } catch (e) {
+      console.log(e);
+      return false;
+    }
+  }
+
   // Close all Pages
   async close(page1, page2) {
     await page1.close();
diff --git a/bigbluebutton-html5/tests/puppeteer/webcam.obj.js b/bigbluebutton-html5/tests/puppeteer/webcam.obj.js
index a085cd7ede..fd767cdeb9 100644
--- a/bigbluebutton-html5/tests/puppeteer/webcam.obj.js
+++ b/bigbluebutton-html5/tests/puppeteer/webcam.obj.js
@@ -18,7 +18,7 @@ const webcamTest = () => {
     try {
       const testName = 'shareWebcam';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithVideo(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       response = await test.test();
       await test.stopRecording();
@@ -45,7 +45,7 @@ const webcamTest = () => {
     try {
       const testName = 'checkWebcamContent';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithVideo(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       response = await test.test();
       await test.stopRecording();
diff --git a/bigbluebutton-html5/tests/puppeteer/webcamlayout.obj.js b/bigbluebutton-html5/tests/puppeteer/webcamlayout.obj.js
index 2041f10d01..7bf443d5a2 100644
--- a/bigbluebutton-html5/tests/puppeteer/webcamlayout.obj.js
+++ b/bigbluebutton-html5/tests/puppeteer/webcamlayout.obj.js
@@ -17,7 +17,7 @@ const webcamLayoutTest = () => {
     try {
       const testName = 'joinWebcamAndMicrophone';
       await test.logger('begin of ', testName);
-      await test.init(Page.getArgsWithAudioAndVideo(), undefined, undefined, undefined, testName);
+      await test.init(Page.getArgs(), undefined, undefined, undefined, testName);
       await test.startRecording(testName);
       await test.webcamLayoutStart();
       response = await test.webcamLayoutTest(testName);
-- 
GitLab