diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
index 59a002db..04780ec3 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy.yml
@@ -359,7 +359,6 @@ jobs:
NODE_OPTIONS: '--max-old-space-size=4096'
run: |
cd react
- mv .env.production.conferencing .env.production
sed -i "s#^REACT_APP_TURN_SERVER_URL=.*#REACT_APP_TURN_SERVER_URL=\"turn:${{ secrets.STAGING_TURN_URL }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_USERNAME=.*#REACT_APP_TURN_SERVER_USERNAME=\"${{ secrets.STAGING_TURN_USERNAME }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_CREDENTIAL=.*#REACT_APP_TURN_SERVER_CREDENTIAL=\"${{ secrets.STAGING_TURN_PASSWORD }}\"#" .env.production
diff --git a/.gitignore b/.gitignore
index 01aea159..458aa1fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@ webapp/src/main/webapp/
react/.env.development
react/.idea/workspace.xml
react/.env.fakeeh
+test/run.sh
+test/runw.sh
diff --git a/react/.env.development.conference b/react/.env.development.conference
new file mode 100644
index 00000000..8387365c
--- /dev/null
+++ b/react/.env.development.conference
@@ -0,0 +1,77 @@
+## Ant Media Server WebRTC Conference App Environment Variables
+# Path: .env.production
+
+# IMPORTANT: This file is for production environment.
+# If you wanted to make local tests, you can copy this file to .env.development and change the values.
+# When you run the application with npm start, .env.development file will be used.
+# When you run the application with npm run build, .env.production file will be used.
+
+# We don't suggest to set REACT_APP_WEBSOCKET_URL and REACT_APP_REST_BASE_URL in production environment.
+# When you create a war file and deploy it to the Ant Media Server, these variables will be set automatically.
+
+# Ant Media Server URL configurations
+REACT_APP_WEBSOCKET_URL="ws://localhost:5080/Conference/websocket"
+#REACT_APP_WEBSOCKET_URL="wss://circle.antmedia.io/Conference/websocket"
+#REACT_APP_WEBSOCKET_URL="ws://localhost:7080/demo/websocket"
+
+# Turn Server URL configurations
+REACT_APP_TURN_SERVER_URL="turn:ovh36.antmedia.io:3478"
+REACT_APP_TURN_SERVER_USERNAME="ovh36"
+REACT_APP_TURN_SERVER_CREDENTIAL="ovh36"
+
+# Footer buttons configurations
+REACT_APP_FOOTER_OPTION_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_CAMERA_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_MIC_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_SCREEN_SHARE_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_REACTIONS_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_MESSAGE_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_PARTICIPANT_LIST_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_END_CALL_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_CLOCK_VISIBILITY=true
+REACT_APP_FOOTER_PUBLISHER_REQUEST_BUTTON_VISIBILITY=false
+
+# Option menu buttons configurations
+REACT_APP_OPTION_MENU_GENERAL_SETTINGS_BUTTON_VISIBILITY=true
+REACT_APP_OPTION_MENU_CHANGE_LAYOUT_BUTTON_VISIBILITY=true
+REACT_APP_OPTION_MENU_CALL_SETTINGS_BUTTON_VISIBILITY=true
+REACT_APP_OPTION_MENU_REPORT_PROBLEM_BUTTON_VISIBILITY=true
+
+# General settings configurations
+REACT_APP_GENERAL_SETTINGS_LANGUAGE_VISIBILITY=true
+REACT_APP_GENERAL_SETTINGS_THEME_VISIBILITY=true
+
+# Call settings configurations
+REACT_APP_CALL_SETTINGS_VIRTUAL_BACKGROUND_MODE_VISIBILITY=true
+
+# Waiting room configurations
+REACT_APP_WAITING_ROOM_PARTICIPANT_NAME_READONLY=false
+
+# Layout configurations
+# If you make REACT_APP_LAYOUT_OTHERS_CARD_VISIBILITY false, you won't see other participants' video in case of number of participants is more than the max participant number of the layout
+REACT_APP_LAYOUT_OTHERS_CARD_VISIBILITY=true
+
+# Time Zone Widget configurations
+REACT_APP_TIME_ZONE_LIVE_TEXT_VISIBILITY=true
+
+# Video Overlay configurations
+REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED=false
+
+# Participant Tab configurations
+REACT_APP_PARTICIPANT_TAB_ADMIN_MODE_ENABLED=false
+
+# Meeting Recording configuration
+REACT_APP_RECORDING_MANAGED_BY_ADMIN=false
+
+# Used to force the theme
+#REACT_APP_FORCE_THEME="white"
+
+# Speed Test configurations
+REACT_APP_SPEED_TEST_BEFORE_JOINING_THE_ROOM=false
+
+# Auto Pin configurations for screen share
+REACT_APP_AUTO_PIN_WHEN_SCREEN_SHARE=true
+
+# URL configurations
+REACT_APP_FOOTER_LOGO_ON_CLICK_URL="https://antmedia.io/circle"
+REACT_APP_REPORT_PROBLEM_URL="https://github.com/ant-media/conference-call-application/issues"
diff --git a/react/.env.development.webinar b/react/.env.development.webinar
new file mode 100644
index 00000000..a0a40ed1
--- /dev/null
+++ b/react/.env.development.webinar
@@ -0,0 +1,76 @@
+## Ant Media Server WebRTC Conference App Environment Variables
+# Path: .env.production
+
+# IMPORTANT: This file is for production environment.
+# If you wanted to make local tests, you can copy this file to .env.development and change the values.
+# When you run the application with npm start, .env.development file will be used.
+# When you run the application with npm run build, .env.production file will be used.
+
+# We don't suggest to set REACT_APP_WEBSOCKET_URL and REACT_APP_REST_BASE_URL in production environment.
+# When you create a war file and deploy it to the Ant Media Server, these variables will be set automatically.
+
+# Ant Media Server URL configurations
+#REACT_APP_WEBSOCKET_URL="ws://localhost:5080/Conference/websocket"
+
+# Turn Server URL configurations
+REACT_APP_TURN_SERVER_URL="turn:ovh36.antmedia.io:3478"
+REACT_APP_TURN_SERVER_USERNAME="ovh36"
+REACT_APP_TURN_SERVER_CREDENTIAL="ovh36"
+
+# Footer buttons configurations
+REACT_APP_FOOTER_APP_LOGO_VISIBILITY=false
+REACT_APP_FOOTER_OPTION_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_CAMERA_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_MIC_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_SCREEN_SHARE_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_REACTIONS_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_MESSAGE_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_PARTICIPANT_LIST_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_END_CALL_BUTTON_VISIBILITY=true
+REACT_APP_FOOTER_CLOCK_VISIBILITY=true
+REACT_APP_FOOTER_PUBLISHER_REQUEST_BUTTON_VISIBILITY=false
+
+# Option menu buttons configurations
+REACT_APP_OPTION_MENU_GENERAL_SETTINGS_BUTTON_VISIBILITY=false
+REACT_APP_OPTION_MENU_CHANGE_LAYOUT_BUTTON_VISIBILITY=false
+REACT_APP_OPTION_MENU_CALL_SETTINGS_BUTTON_VISIBILITY=true
+REACT_APP_OPTION_MENU_REPORT_PROBLEM_BUTTON_VISIBILITY=true
+
+# General settings configurations
+REACT_APP_GENERAL_SETTINGS_LANGUAGE_VISIBILITY=true
+REACT_APP_GENERAL_SETTINGS_THEME_VISIBILITY=true
+
+# Call settings configurations
+REACT_APP_CALL_SETTINGS_VIRTUAL_BACKGROUND_MODE_VISIBILITY=true
+
+# Waiting room configurations
+REACT_APP_WAITING_ROOM_PARTICIPANT_NAME_READONLY=true
+
+# Layout configurations
+# If you make REACT_APP_LAYOUT_OTHERS_CARD_VISIBILITY false, you won't see other participants' video in case of number of participants is more than the max participant number of the layout
+REACT_APP_LAYOUT_OTHERS_CARD_VISIBILITY=false
+
+# Time Zone Widget configurations
+REACT_APP_TIME_ZONE_LIVE_TEXT_VISIBILITY=true
+
+# Video Overlay configurations
+REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED=true
+
+# Speed Test configurations
+REACT_APP_SPEED_TEST_BEFORE_JOINING_THE_ROOM=true
+
+# Participant Tab configurations
+REACT_APP_PARTICIPANT_TAB_ADMIN_MODE_ENABLED=true
+
+# Meeting Recording configuration
+REACT_APP_RECORDING_MANAGED_BY_ADMIN=true
+
+# Used to force the theme
+REACT_APP_FORCE_THEME="white"
+
+# Speed Test configurations
+REACT_APP_SPEED_TEST_BEFORE_JOINING_THE_ROOM=true
+
+# URL configurations
+REACT_APP_FOOTER_LOGO_ON_CLICK_URL=""
+REACT_APP_REPORT_PROBLEM_URL=""
diff --git a/react/src/Components/Cards/VideoCard.js b/react/src/Components/Cards/VideoCard.js
index 074ff7f8..003c5207 100644
--- a/react/src/Components/Cards/VideoCard.js
+++ b/react/src/Components/Cards/VideoCard.js
@@ -82,9 +82,9 @@ function VideoCard(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [conference.isPublished]);
- const OverlayButton = ({ title, icon, color, onClick }) => (
+ const OverlayButton = ({ title, icon, color, onClick, label }) => (
-
+
@@ -113,11 +113,12 @@ function VideoCard(props) {
return (
<>
- {(!useAvatar && process.env.REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED === "true") && (
+ {(process.env.REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED === "true") && (
)}
@@ -125,6 +126,7 @@ function VideoCard(props) {
title={`Microphone ${micMuted ? "on" : "off"} ${props.name}`}
icon={micMuted ? "muted-microphone" : "microphone"}
color={micMuted ? "error" : "primary"}
+ label={micMuted ? "unmute" : "mute"}
onClick={handleToggleMic}
/>
>
@@ -136,6 +138,7 @@ function VideoCard(props) {
title={`${props.pinned ? t("unpin") : t("pin")} ${props.name}`}
icon={props.pinned ? "unpin" : "pin"}
color="primary"
+ label={props.pinned ? "unpin" : "pin"}
onClick={() => conference.pinVideo(props.trackAssignment.streamId)}
/>
);
@@ -166,7 +169,7 @@ function VideoCard(props) {
{!isMobile && !isTablet && }
- {isAdministrativeButtonsVisible && }
+ {isAdministrativeButtonsVisible && }
@@ -187,6 +190,7 @@ function VideoCard(props) {
height: "100%",
transform: isMine ? "rotateY(180deg)" : "none",
}}
+
>
switchLanguage(e.target.value)}
@@ -112,7 +112,7 @@ export function GeneralSettingsDialog(props) {
switchTheme(e.target.value)}
diff --git a/react/src/Components/Footer/Components/LayoutSettingsDialog.js b/react/src/Components/Footer/Components/LayoutSettingsDialog.js
index ea718506..fa3ede90 100644
--- a/react/src/Components/Footer/Components/LayoutSettingsDialog.js
+++ b/react/src/Components/Footer/Components/LayoutSettingsDialog.js
@@ -93,14 +93,8 @@ export function LayoutSettingsDialog(props) {
}
});
} else if (mode === "sidebar") {
- const participants = document.querySelectorAll(
- ".single-video-container.not-pinned video"
- );
- const firstParticipant =
- participants.length > 1 ? participants[1] : participants[0];
-
- //pin the first participant
- conference.pinVideo(firstParticipant?.id ? firstParticipant.streamId : "localVideo");
+ //pins your video
+ conference.pinVideo(conference.videoTrackAssignments[0]?.streamId);
}
};
const radioLabel = (label, icon) => {
diff --git a/react/src/Components/Footer/Components/OptionButton.js b/react/src/Components/Footer/Components/OptionButton.js
index 61d69bbd..873c5603 100644
--- a/react/src/Components/Footer/Components/OptionButton.js
+++ b/react/src/Components/Footer/Components/OptionButton.js
@@ -147,7 +147,7 @@ function OptionButton({footer, ...props}) {
{conference.isPlayOnly === false
&& process.env.REACT_APP_OPTION_MENU_CALL_SETTINGS_BUTTON_VISIBILITY === 'true' ?
- handleDialogOpen()}>
+ handleDialogOpen()} id="call-settings">
@@ -157,7 +157,8 @@ function OptionButton({footer, ...props}) {
{conference.isPlayOnly === false
&& process.env.REACT_APP_CALL_SETTINGS_VIRTUAL_BACKGROUND_MODE_VISIBILITY === 'true' ?
- { conference.handleEffectsOpen(!conference.effectsDrawerOpen); handleClose(); }}>
+ { conference.handleEffectsOpen(!conference.effectsDrawerOpen); handleClose(); }}
+ id="virtual-effects">
diff --git a/react/src/Components/Footer/Components/SettingsDialog.js b/react/src/Components/Footer/Components/SettingsDialog.js
index d507da14..6540ea7c 100644
--- a/react/src/Components/Footer/Components/SettingsDialog.js
+++ b/react/src/Components/Footer/Components/SettingsDialog.js
@@ -67,7 +67,7 @@ export default function SettingsDialog(props) {
}, [conference.devices]);
return (
-
+
{t('Set Camera and Microphone')}
@@ -80,7 +80,7 @@ export default function SettingsDialog(props) {
switchVideoMode(e.target.value)}
@@ -108,7 +108,12 @@ export default function SettingsDialog(props) {
- conference.setVideoSendResolution(e.target.value)} sx={{ color: '#fff' }}>
+ conference.setVideoSendResolution(e.target.value)}
+ sx={{ color: '#fff' }}
+ id="setting-dialog-resolution-select"
+ >
{t('Auto')}
@@ -136,7 +141,13 @@ export default function SettingsDialog(props) {
- switchAudioMode(e.target.value)} sx={{ color: '#fff' }}>
+ switchAudioMode(e.target.value)}
+ sx={{ color: '#fff' }}
+ id="setting-dialog-mic-select"
+ >
{conference.devices && conference.devices?.length > 0 && conference.devices
.filter(device => device.kind === 'audioinput')
.map(device => (
diff --git a/react/src/__tests__/pages/AntMedia.test.js b/react/src/__tests__/pages/AntMedia.test.js
index a85bb381..885b8ed1 100644
--- a/react/src/__tests__/pages/AntMedia.test.js
+++ b/react/src/__tests__/pages/AntMedia.test.js
@@ -92,6 +92,7 @@ jest.mock('@antmedia/webrtc_adaptor', () => ({
updateBroadcastRole: jest.fn(),
showInfoSnackbarWithLatency: jest.fn(),
getSubtrackCount: jest.fn(),
+ setVolumeLevel: jest.fn(),
}
for (var key in params) {
@@ -1285,6 +1286,30 @@ describe('AntMedia Component', () => {
});
});
+ it('audio level setting test', async () => {
+ const { container } = render(
+
+
+
+
+ );
+
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
+
+ await act(async () => {
+ currentConference.setMicAudioLevel(10);
+ });
+ expect(webRTCAdaptorConstructor.setVolumeLevel).toHaveBeenCalledWith(10);
+
+ consoleSpy.mockRestore();
+
+ });
+
it('checks connection quality and displays warning for poor network connection for publish', async () => {
const { container } = render(
@@ -1675,10 +1700,6 @@ describe('AntMedia Component', () => {
});
-
-
-
-
consoleWarnSpy.mockRestore();
});
@@ -1695,23 +1716,54 @@ describe('AntMedia Component', () => {
expect(webRTCAdaptorConstructor).not.toBe(undefined);
});
+ expect(currentConference.isScreenShared).toBe(false);
+
+ await act(async () => {
+ currentConference.handleStartScreenShare();
+ });
+
+ await waitFor(() => {
+ expect(webRTCAdaptorScreenConstructor).not.toBe(undefined);
+ });
+
+ act(() => {
+ webRTCAdaptorScreenConstructor.callback("publish_started");
+ });
+
+
+ await waitFor(() => {
+ expect(currentConference.isScreenShared).toBe(true);
+ });
+
+ console.log(currentConference);
+
+ expect(currentConference.isScreenShared).toBe(true);
+
webRTCAdaptorConstructor.reconnectIfRequired = jest.fn();
webRTCAdaptorConstructor.requestVideoTrackAssignments = jest.fn();
webRTCAdaptorConstructor.iceConnectionState = () => "mock1";
+ webRTCAdaptorScreenConstructor.reconnectIfRequired = jest.fn();
+ webRTCAdaptorScreenConstructor.requestVideoTrackAssignments = jest.fn();
+ webRTCAdaptorScreenConstructor.iceConnectionState = () => "mock1";
+
await act(async () => {
expect(webRTCAdaptorConstructor.iceConnectionState()).toBe("mock1");
+ expect(webRTCAdaptorScreenConstructor.iceConnectionState()).toBe("mock1");
});
await act(async () => {
jest.useFakeTimers();
currentConference.fakeReconnect();
expect(webRTCAdaptorConstructor.iceConnectionState()).toBe("disconnected");
+ expect(webRTCAdaptorScreenConstructor.iceConnectionState()).toBe("disconnected");
jest.runAllTimers();
});
await waitFor(() => {
expect(webRTCAdaptorConstructor.iceConnectionState()).toBe("mock1");
+ expect(webRTCAdaptorScreenConstructor.iceConnectionState()).toBe("mock1");
+
});
jest.useRealTimers();
diff --git a/react/src/pages/AntMedia.js b/react/src/pages/AntMedia.js
index 11175973..62f4c5a0 100644
--- a/react/src/pages/AntMedia.js
+++ b/react/src/pages/AntMedia.js
@@ -1214,12 +1214,20 @@ function AntMedia(props) {
console.log("************* fake reconnect");
let orginal = webRTCAdaptor.iceConnectionState;
webRTCAdaptor.iceConnectionState = () => "disconnected";
-
webRTCAdaptor.reconnectIfRequired();
+ if (isScreenShared && screenShareWebRtcAdaptor.current != null) {
+ screenShareWebRtcAdaptor.current.iceConnectionState = () => "disconnected";
+ screenShareWebRtcAdaptor.current.reconnectIfRequired();
+ }
+
setTimeout(() => {
webRTCAdaptor.iceConnectionState = orginal;
+ if (isScreenShared && screenShareWebRtcAdaptor.current != null) {
+ screenShareWebRtcAdaptor.current.iceConnectionState = orginal;
+ }
}, 5000);
+
}
function addFakeParticipant() {
@@ -1863,6 +1871,12 @@ function AntMedia(props) {
allParticipants[streamId] = broadcastObject;
handleNotifyUnpinUser(streamId !== publishStreamId ? streamId : publishStreamId);
setParticipantUpdated(!participantUpdated);
+
+ let vta = videoTrackAssignments.find(el => el.streamId == streamId);
+ if (vta) {
+ webRTCAdaptor?.assignVideoTrack(vta.videoLabel, streamId, false);
+ }
+
return;
}
@@ -2395,15 +2409,17 @@ function AntMedia(props) {
}
} else if (eventType === "AUDIO_TRACK_ASSIGNMENT") {
- // xxx to be able to reduce render
+ // FIXME: to be able to reduce render
if (role === WebinarRoles.Host || role === WebinarRoles.ActiveHost) {
return;
}
+ /*
clearInterval(timeoutRef.current);
timeoutRef.current = setTimeout(() => {
setTalkers([]);
}, 1000);
//console.log(JSON.stringify(notificationEvent.payload));
+ */
setTalkers((oldTalkers) => {
const newTalkers = notificationEvent.payload
.filter((p) => p.trackId !== "" && p.audioLevel < 60)
@@ -2943,6 +2959,8 @@ function AntMedia(props) {
sendMessage(JSON.stringify(jsCmd));
}
+
+
const showReactions = React.useCallback((streamId, streamName, reactionRequest, allParticipants) => {
let reaction = '😀';
@@ -2988,6 +3006,10 @@ function AntMedia(props) {
handleSendNotificationEvent("MIC_UNMUTED", publishStreamId);
}
+ function setMicAudioLevel(audioLevel) {
+ webRTCAdaptor.setVolumeLevel(audioLevel);
+ }
+
const setAudioLevelListener = (listener, period) => {
if (audioListenerIntervalJob == null) {
audioListenerIntervalJob = setInterval(() => {
@@ -3236,6 +3258,7 @@ function AntMedia(props) {
isBroadcasting,
playStats,
checkAndSetIsPinned,
+ setMicAudioLevel,
updateAllParticipantsPagination,
pagedParticipants,
participantCount,
diff --git a/react/src/pages/Home.js b/react/src/pages/Home.js
index dadb31cd..f6e19d52 100755
--- a/react/src/pages/Home.js
+++ b/react/src/pages/Home.js
@@ -38,7 +38,7 @@ function Home(props) {
const handleCreateMeeting = () => {
console.log("handleCreateMeeting is called");
- if (settings.roomCreationPasswordEnabled) {
+ if (typeof settings !== 'undefined' && settings.roomCreationPasswordEnabled) {
setCreateRoomPasswordDialogOpen(true);
}
else {
diff --git a/react/src/pages/MeetingRoom.js b/react/src/pages/MeetingRoom.js
index c6172e7d..9cc13a0f 100644
--- a/react/src/pages/MeetingRoom.js
+++ b/react/src/pages/MeetingRoom.js
@@ -12,6 +12,7 @@ import {t} from "i18next";
import {isComponentMode} from "../utils";
import BecomePublisherConfirmationDialog from "../Components/BecomePublisherConfirmationDialog";
import RecordingButton from "../Components/RecordingButton";
+import {Container} from "@mui/material";
function debounce(fn, ms) {
let timer;
@@ -107,7 +108,9 @@ const MeetingRoom = React.memo((props) => {
const pinLayout = (typeof firstPinnedParticipant !== "undefined");
return (
- <>
+
{conference?.isRecordPluginActive === true ?
: null
}
@@ -152,7 +155,7 @@ const MeetingRoom = React.memo((props) => {
)
}
- >
+
);
});
diff --git a/react/src/pages/WaitingRoom.js b/react/src/pages/WaitingRoom.js
index 397e8043..016061d4 100755
--- a/react/src/pages/WaitingRoom.js
+++ b/react/src/pages/WaitingRoom.js
@@ -228,7 +228,9 @@ function WaitingRoom(props) {
}
return (
-
+
handleDialogOpen()}
+ id="waiting-room-more-options"
>
diff --git a/test/browser.py b/test/browser.py
index 1af02e60..22523f91 100644
--- a/test/browser.py
+++ b/test/browser.py
@@ -17,11 +17,14 @@
import subprocess
class Browser:
- def init(self, is_headless):
+ def init(self, is_headless=True, is_fake_camera=True, mic_file=None):
browser_options = Options()
browser_options.add_experimental_option("detach", True)
- browser_options.add_argument("--use-fake-ui-for-media-stream")
- browser_options.add_argument("--use-fake-device-for-media-stream")
+ if is_fake_camera:
+ browser_options.add_argument("--use-fake-ui-for-media-stream")
+ browser_options.add_argument("--use-fake-device-for-media-stream")
+ if mic_file is not None:
+ browser_options.add_argument(f"--use-file-for-fake-audio-capture={mic_file}")
browser_options.add_argument('--log-level=0')
browser_options.add_argument('--no-sandbox')
browser_options.add_argument('--disable-extensions')
@@ -30,14 +33,11 @@ def init(self, is_headless):
browser_options.add_argument('--disable-setuid-sandbox')
browser_options.add_argument('--enable-logging')
browser_options.add_argument('--v=1')
-
- #is_headless = False #for local testing in windows
if is_headless:
browser_options.add_argument("--headless")
- service = Service(executable_path='/tmp/chromedriver', service_args=["--verbose","--log-path=/tmp/chromedriver.log"])
- else:
- service = Service(executable_path='C:/WebDriver/chromedriver.exe')
+
+ service = Service(executable_path='/tmp/chromedriver', service_args=["--verbose","--log-path=/tmp/chromedriver.log"])
browser_options.set_capability( "goog:loggingPrefs", { 'browser':'ALL' } )
self.driver = webdriver.Chrome(service=service, options=browser_options)
@@ -53,9 +53,9 @@ def switch_to_tab(self,tab_id):
def get_current_tab_id(self):
return self.driver.current_window_handle
- def execute_script(self, script):
+ def execute_script(self, script, *args):
try:
- return self.driver.execute_script(script)
+ return self.driver.execute_script(script, *args)
except StaleElementReferenceException as e:
return None
@@ -87,7 +87,6 @@ def execute_script_with_retry(self, script, retries=3, wait_time=2):
time.sleep(wait_time)
else:
print(f"Script {script} failed after {retries} attempts: {e}")
- print("SS as base64: \n"+self.driver.get_screenshot_as_base64())
raise
def get_element_with_retry(self, by, value, retries=5, wait_time=2):
@@ -103,7 +102,7 @@ def get_element_with_retry(self, by, value, retries=5, wait_time=2):
time.sleep(wait_time)
else:
print(f"Element not found by {by} with value {value} after {retries} attempts: {e}")
- print("SS as base64: \n"+self.driver.get_screenshot_as_base64())
+ #print("SS as base64: \n"+self.driver.get_screenshot_as_base64())
raise
def get_all_elements(self, by, value):
@@ -122,12 +121,12 @@ def get_element(self, by, value, timeout=15):
WebDriverWait(self.driver, timeout).until(element_present)
except TimeoutException:
print("Timed out waiting for element to be clickable by "+str(by)+" with value "+str(value))
- print("SS as base64: \n"+self.driver.get_screenshot_as_base64())
+ #print("SS as base64: \n"+self.driver.get_screenshot_as_base64())
return self.driver.find_element(by, value)
- def get_element_in_element(self, element, by, value, timeout=15):
+ def get_all_elements_in_element(self, element, by, value, timeout=15):
try:
element_present = EC.element_to_be_clickable((by, value))
WebDriverWait(element, timeout).until(element_present)
@@ -135,6 +134,16 @@ def get_element_in_element(self, element, by, value, timeout=15):
print("Timed out waiting for nested element to be clickable by "+str(by)+" with value "+str(value))
return element.find_elements(by, value)
+
+ def get_element_in_element(self, element, by, value, timeout=15, wait_until_clickable=True):
+ if wait_until_clickable:
+ try:
+ element_present = EC.element_to_be_clickable((by, value))
+ WebDriverWait(element, timeout).until(element_present)
+ except TimeoutException:
+ print("Timed out waiting for nested element to be clickable by "+str(by)+" with value "+str(value))
+
+ return element.find_element(by, value)
def is_element_displayed(self, by, value):
try:
@@ -177,6 +186,10 @@ def move_slider_to(self, element, value):
move = ActionChains(self.driver)
move.click_and_hold(element).move_by_offset(value, 0).release().perform()
+ def move_to_element(self, element):
+ move = ActionChains(self.driver)
+ move.move_to_element(element).perform()
+
def get_wait(self, wait_time=25, poll_frequency=1):
return WebDriverWait(self.driver, wait_time, poll_frequency)
diff --git a/test/camera.mp4 b/test/camera.mp4
new file mode 100644
index 00000000..c86b5daf
Binary files /dev/null and b/test/camera.mp4 differ
diff --git a/test/fake_mic.wav b/test/fake_mic.wav
new file mode 100644
index 00000000..ae59c948
Binary files /dev/null and b/test/fake_mic.wav differ
diff --git a/test/rest_helper.py b/test/rest_helper.py
index 12096936..73b56dd9 100644
--- a/test/rest_helper.py
+++ b/test/rest_helper.py
@@ -75,7 +75,9 @@ def start_broadcast(self, streamId):
def getVoDFor(self, streamId):
resp = self.session.get(self.rest_url +"/request?_path="+self.app_name+"/rest/v2/vods/list/0/5")
+ print("get_vod_for "+str(streamId+":"+str(resp.text)))
json_data = json.loads(resp.text)
+
for item in json_data:
if item["streamId"] == streamId:
diff --git a/test/test_join_leave.py b/test/test_join_leave.py
index 61a035e4..bf741f68 100644
--- a/test/test_join_leave.py
+++ b/test/test_join_leave.py
@@ -11,22 +11,28 @@
import json
import time
import psutil
+import re
class TestJoinLeave(unittest.TestCase):
def setUp(self):
print("----------------\n", self._testMethodName, " starting...")
+ self.is_local = False
+ #self.is_local = True
+ self.verbose = False
self.url = os.environ.get('SERVER_URL')
self.test_app_name = os.environ.get('TEST_APP_NAME')
self.user = os.environ.get('AMS_USER')
self.password = os.environ.get('AMS_PASSWORD')
self.chrome = Browser()
- self.chrome.init(True)
+ self.chrome.init(not self.is_local)
+ self.chrome.makeFullScreen()
self.rest_helper = RestHelper(self.url, self.user, self.password, self.test_app_name)
self.rest_helper.login()
+
wait = self.chrome.get_wait()
- time.sleep(15)
+ #time.sleep(15)
#wait.until(lambda x: len(self.rest_helper.get_broadcasts()) == 0)
#print("broadcasts are empty")
@@ -35,10 +41,15 @@ def tearDown(self):
print(self._testMethodName, " ending...\n","----------------")
def create_participants_with_test_tool(self, participant_name, room, count):
+
directory = os.path.expanduser("~/test/webrtc-load-test")
script = "run.sh"
ws_url = self.url.replace("https://", "").replace("http://", "")
- parameters = ["-m", "publisher", "-s", ws_url, "-p", "443", "-q", "true", "-f", "test.mp4", "-r", "true", "-a", self.test_app_name, "-i", participant_name, "-t", room, "-n", str(count)]
+
+ if self.is_local:
+ parameters = ["-m", "publisher", "-f", "test.mp4", "-r", "true", "-a", self.test_app_name, "-i", participant_name, "-t", room, "-n", str(count)]
+ else:
+ parameters = ["-m", "publisher", "-s", ws_url, "-p", "443", "-q", "true", "-f", "test.mp4", "-r", "true", "-a", self.test_app_name, "-i", participant_name, "-t", room, "-n", str(count)]
print("test tool is running with parameters: "+str(parameters))
# Full path to the script
@@ -48,22 +59,43 @@ def create_participants_with_test_tool(self, participant_name, room, count):
process = subprocess.Popen(
["bash", script_path] + parameters,
cwd=directory,
- #stdout=subprocess.DEVNULL,
- #stderr=subprocess.DEVNULL
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL
)
return process
def kill_participants_with_test_tool(self, process):
- process.kill()
-
-
- def join_room_in_new_tab(self, participant, room):
+ print(f"Before killing process: {process.pid}")
+ try:
+ # Kill the given process and its children
+ parent = psutil.Process(process.pid)
+ for child in parent.children(recursive=True):
+ child.kill()
+ parent.kill()
+ except psutil.NoSuchProcess:
+ print("Process already terminated.")
+
+ # Call `pkill java` to ensure no stray Java processes are left running
+ try:
+ subprocess.run(["pkill", "java"], check=True)
+ print("pkill java executed successfully.")
+ except subprocess.CalledProcessError as e:
+ print(f"Error executing pkill java: {e}")
+ except FileNotFoundError:
+ print("pkill command not found on the system.")
+
+ print(f"After killing process: {process.pid}")
+
+
+
+ def join_room_in_new_tab(self, participant, room, play_only=False):
print("url: "+self.url+"/"+self.test_app_name+"/"+room)
app = "/"+self.test_app_name
if self.url.endswith("localhost:3000"):
app = ""
- handle = self.chrome.open_in_new_tab(self.url+app+"/"+room)
+
+ handle = self.chrome.open_in_new_tab(self.url + app + "/" + room + ("?playOnly=true" if play_only else ""))
name_text_box = self.chrome.get_element_with_retry(By.ID, "participant_name")
@@ -91,18 +123,14 @@ def get_videoTrackAssignments(self, expected_value=None):
#self.chrome.print_console_logs()
vtas = result_json["videoTrackAssignments"]
if expected_value is not None and len(vtas) != expected_value:
- print("\n ++++++++++ start trial ++++++++++")
print("VTA expected: "+str(expected_value) + " but got: "+str(len(vtas)))
- self.call_debugme()
- print("\n")
- self.print_message()
-
- #print("\n screen shot")
- #self.chrome.print_ss_as_base64()
-
- print("++++++++++ end trial ++++++++++\n")
-
+ if self.verbose:
+ self.call_debugme()
+ print("\n")
+ self.print_message()
+ #print("\n screen shot")
+ #self.chrome.print_ss_as_base64()
cpu_usage = psutil.cpu_percent(interval=0)
print(f"Instant CPU Usage: {cpu_usage}%")
@@ -116,6 +144,18 @@ def get_track_stats(self):
return result_json
+ def fake_reconnect(self):
+ script = "return window.conference.fakeReconnect();"
+ result_json = self.chrome.execute_script_with_retry(script)
+ if result_json is None:
+ return []
+
+ return result_json
+
+ def set_audio_level(self, audioLevel):
+ script = "return window.conference.setMicAudioLevel("+str(audioLevel)+");"
+ self.chrome.execute_script_with_retry(script)
+
def get_conference(self):
script = "return window.conference;"
result_json = self.chrome.execute_script_with_retry(script)
@@ -125,14 +165,29 @@ def get_conference(self):
#print(result_json)
return result_json
- def get_video_track_limit(self):
+ def get_tile_count(self):
script = "return window.conference;"
result_json = self.chrome.execute_script_with_retry(script)
if result_json is None:
return -1
return result_json["globals"]["desiredTileCount"]
- def change_video_track_count(self, count):
+ def set_layout(self, type):
+ settings_button = self.chrome.get_element_with_retry(By.ID, "settings-button")
+ self.chrome.click_element(settings_button)
+
+ time.sleep(1)
+
+ change_layout_button = self.chrome.get_element_with_retry(By.ID, "change-layout-button")
+ self.chrome.click_element(change_layout_button)
+
+ change_layout_button = self.chrome.get_element_with_retry(By.XPATH, "//input[@type='radio' and @value='"+type+"']")
+ self.chrome.click_element(change_layout_button)
+
+ layout_dialog_close_button = self.chrome.get_element_with_retry(By.ID, "layout-dialog-close-button")
+ self.chrome.click_element(layout_dialog_close_button)
+
+ def change_tile_count(self, count):
index = 0
if count == 2:
index = 0
@@ -156,7 +211,7 @@ def change_video_track_count(self, count):
time.sleep(1)
tile_count_slider = self.chrome.get_element_with_retry(By.ID, "tile-count-slider")
- points = self.chrome.get_element_in_element(tile_count_slider, By.CLASS_NAME, "MuiSlider-mark")
+ points = self.chrome.get_all_elements_in_element(tile_count_slider, By.CLASS_NAME, "MuiSlider-mark")
self.chrome.mouse_click_on(points[index])
layout_dialog_close_button = self.chrome.get_element_with_retry(By.ID, "layout-dialog-close-button")
@@ -170,11 +225,13 @@ def get_start_recording_button(self):
return start_recording_button
def get_share_screen_button(self):
- settings_button = self.chrome.get_element(By.ID, "settings-button")
- self.chrome.click_element(settings_button)
-
- share_screen_button = self.chrome.get_element(By.ID, "share-screen-button")
- return share_screen_button
+ if(self.chrome.is_element_exist(By.ID, "share-screen-button")):
+ ss_button = self.chrome.get_element(By.ID, "share-screen-button")
+ else:
+ more_button = self.chrome.get_element_with_retry(By.ID, "more-button")
+ self.chrome.click_element(more_button)
+ ss_button = self.chrome.get_element_with_retry(By.ID, "more-options-share-screen-button")
+ return ss_button
def get_stop_recording_button(self):
settings_button = self.chrome.get_element(By.ID, "settings-button")
@@ -182,31 +239,181 @@ def get_stop_recording_button(self):
stop_recording_button = self.chrome.get_element(By.ID, "stop-recording-button")
return stop_recording_button
+
+ def test_home_page_create_room(self):
+ room = "room"+str(random.randint(100, 999))
+ app = "/"+self.test_app_name
+ if self.url.endswith("localhost:3000"):
+ app = ""
+ handle = self.chrome.open_in_new_tab(self.url+app)
+ room_name_text_box = self.chrome.get_element_with_retry(By.ID, "room_name")
+
+ self.chrome.write_to_element(room_name_text_box, room)
+
+ join_button = self.chrome.get_element_with_retry(By.ID, "room_join_button")
+ self.chrome.click_element(join_button)
+ waiting_gallery = self.chrome.get_element_with_retry(By.ID, "waiting-room")
+
+ assert(waiting_gallery.is_displayed())
+
+ self.chrome.close_all()
+
+ def test_home_page_create_random_room(self):
+ app = "/"+self.test_app_name
+ if self.url.endswith("localhost:3000"):
+ app = ""
+ handle = self.chrome.open_in_new_tab(self.url+app)
+ link = self.chrome.get_element_with_retry(By.XPATH, "//p[text()='Create Meeting']")
+ link.click()
+ waiting_gallery = self.chrome.get_element_with_retry(By.ID, "waiting-room")
+ assert(waiting_gallery.is_displayed())
+
+ self.chrome.close_all()
+
+ def test_camera_mic_setting_in_waiting_room(self):
+ room = "room"+str(random.randint(100, 999))
+ app = "/"+self.test_app_name
+ if self.url.endswith("localhost:3000"):
+ app = ""
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room)
+ more_options_button = self.chrome.get_element_with_retry(By.ID, "waiting-room-more-options")
+ more_options_button.click()
+
+ camera_select = self.chrome.get_element_with_retry(By.ID, "setting-dialog-camera-select")
+ self.chrome.mouse_click_on(camera_select)
+
+ time.sleep(1)
+
+ camera = self.chrome.get_element_with_retry(By.XPATH, "//li[contains(text(), 'fake_device_0')]")
+ self.chrome.mouse_click_on(camera)
+
+
+ resolution_select = self.chrome.get_element_with_retry(By.ID, "setting-dialog-resolution-select")
+ self.chrome.mouse_click_on(resolution_select)
+
+ time.sleep(1)
+
+ resolution = self.chrome.get_element_with_retry(By.XPATH, "//li[contains(text(), 'Low definition (180p)')]")
+ self.chrome.mouse_click_on(resolution)
+
+ mic_select = self.chrome.get_element_with_retry(By.ID, "setting-dialog-mic-select")
+ self.chrome.mouse_click_on(mic_select)
+
+ time.sleep(1)
+
+ self.chrome.save_ss_as_file("test_camera_mic_setting_in_waiting_room-1.png")
+
+ #TODO: sometimes audio input 2 is not available, check it.
+ if self.chrome.is_element_exist(By.XPATH, "//li[contains(text(), 'Fake Audio Input 2')]"):
+ mic = self.chrome.get_element_with_retry(By.XPATH, "//li[contains(text(), 'Fake Audio Input 2')]")
+ self.chrome.mouse_click_on(mic)
+
+ close_button = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "button[aria-label='close']")
+ close_button.click()
+
+
+ name_text_box = self.chrome.get_element_with_retry(By.ID, "participant_name")
+
+ self.chrome.write_to_element(name_text_box, "participant1")
+
+ join_button = self.chrome.get_element_with_retry(By.ID, "room_join_button")
+ self.chrome.click_element(join_button)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.ID, "meeting-gallery")
+ assert(meeting_gallery.is_displayed())
+ self.chrome.close_all()
+
+ def test_join_as_camera_mic_off(self):
+ room = "room"+str(random.randint(100, 999))
+ app = "/"+self.test_app_name
+ if self.url.endswith("localhost:3000"):
+ app = ""
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room)
+
+ camera_button = self.chrome.get_element_with_retry(By.ID, "camera-button")
+ camera_button.click()
+
+ min_button = self.chrome.get_element_with_retry(By.ID, "mic-button")
+ min_button.click()
+
+ name_text_box = self.chrome.get_element_with_retry(By.ID, "participant_name")
+
+ self.chrome.write_to_element(name_text_box, "participant1")
+
+ join_button = self.chrome.get_element_with_retry(By.ID, "room_join_button")
+ self.chrome.click_element(join_button)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.ID, "meeting-gallery")
+ assert(meeting_gallery.is_displayed())
+ self.chrome.close_all()
+
+ #this test will not work on local since we have camera and mic in local
+ def test_join_without_camera_mic(self):
+ self.chrome.close_all()
+ self.chrome = Browser()
+ self.chrome.init(True, False)
+
+ self.chrome.makeFullScreen()
+ room = "room"+str(random.randint(100, 999))
+ app = "/"+self.test_app_name
+ if self.url.endswith("localhost:3000"):
+ app = ""
+
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room)
+ more_options_button = self.chrome.get_element_with_retry(By.ID, "waiting-room-more-options")
+ more_options_button.click()
+
+ self.chrome.save_ss_as_file("settings-1.png")
+
+ time.sleep(2)
+
+ close_button = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "button[aria-label='close']")
+ close_button.click()
+
+ time.sleep(2)
+
+ camera_button = self.chrome.get_element_with_retry(By.ID, "camera-button")
+ camera_button.click()
+
+ min_button = self.chrome.get_element_with_retry(By.ID, "mic-button")
+ min_button.click()
+
+ name_text_box = self.chrome.get_element_with_retry(By.ID, "participant_name")
+
+ self.chrome.write_to_element(name_text_box, "participant1")
+
+ join_button = self.chrome.get_element_with_retry(By.ID, "room_join_button")
+ self.chrome.click_element(join_button)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.ID, "meeting-gallery")
+ assert(meeting_gallery.is_displayed())
+ self.chrome.close_all()
+
def test_join_room(self):
room = "room"+str(random.randint(100, 999))
self.join_room_in_new_tab("participantA", room)
self.chrome.close_all()
- def set_and_test_track_limit(self, limit):
+ def set_and_test_tile_count(self, limit):
print("set_track_limit -> count: "+str(limit))
- self.change_video_track_count(limit)
+ self.change_tile_count(limit)
wait = self.chrome.get_wait()
- wait.until(lambda x: self.get_video_track_limit() == limit)
+ wait.until(lambda x: self.get_tile_count() == limit)
print("video_track_limit: "+str(limit))
- def _test_video_track_count(self):
+ def test_tile_count(self):
#self.chrome.makeFullScreen()
room = "room"+str(random.randint(100, 999))
self.join_room_in_new_tab("participantA", room)
- self.set_and_test_track_limit(2)
+ self.set_and_test_tile_count(2)
time.sleep(5)
- self.set_and_test_track_limit(4)
+ self.set_and_test_tile_count(4)
time.sleep(5)
- self.set_and_test_track_limit(6)
+ self.set_and_test_tile_count(6)
time.sleep(5)
- self.set_and_test_track_limit(12)
+ self.set_and_test_tile_count(12)
self.chrome.close_all()
@@ -233,18 +440,42 @@ def open_close_chat_drawer(self):
self.chrome.click_element_as_script(messages_button)
- def call_debugme(self):
+ def send_message(self, message):
if not self.chrome.is_element_displayed(By.ID, "message-input"):
self.open_close_chat_drawer()
- time.sleep(2)
+
+ wait = self.chrome.get_wait()
+ wait.until(lambda x: self.chrome.is_element_exist(By.ID, "message-send-button"))
message_input = self.chrome.get_element_with_retry(By.ID, "message-input")
- self.chrome.write_to_element(message_input, "debugme")
+ time.sleep(1)
+ self.chrome.write_to_element(message_input, message)
send_button = self.chrome.get_element_with_retry(By.ID, "message-send-button")
self.chrome.click_element_as_script(send_button)
+ def call_debugme(self):
+ self.send_message("debugme")
+
+ def call_clearme_debugme(self):
+ self.send_message("clearme")
+ self.send_message("debugme")
+
+ wait = self.chrome.get_wait()
+ wait.until(lambda x: "Client Debug Info" in self.get_debugme_response())
+
+ return self.get_debugme_response()
+
+ def get_debugme_response(self):
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ content = last_message.get_attribute("innerHTML")
+
+ return content if content is not None else ""
+
+
+
def print_message(self):
messages = self.chrome.get_all_elements(By.ID, "message")
print(">>>>>>>\nlast 2 messages:"+str(len(messages)))
@@ -253,6 +484,24 @@ def print_message(self):
print("message:" + message.get_attribute("innerHTML"))
print("<<<<<<<\n")
+ def parse_backend_video_trackAssignments(self, debug_info):
+ pattern = re.compile(r"{label:(\w+),assigned stream id:(\w+),reserved:(\w+),")
+ matches = pattern.findall(debug_info)
+
+ video_track_assignments = [
+ {"label": match[0], "assigned_stream_id": match[1], "reserved": match[2] == 'true'}
+ for match in matches
+ ]
+
+ print("video_track_assignments:"+str(video_track_assignments))
+ return video_track_assignments
+
+ def send_reaction(self, reaction):
+ reaction_button = self.chrome.get_element(By.XPATH, "//button[@type='button' and @aria-label='Emoji reactions']")
+ reaction_button.click()
+
+ reaction_button = self.chrome.get_element(By.XPATH, "//div[text()='" + reaction + "']")
+ self.chrome.mouse_click_on(reaction_button)
def test_others_tile(self):
@@ -271,13 +520,7 @@ def test_others_tile(self):
time.sleep(3)
- if(self.chrome.is_element_exist(By.ID, "share-screen-button")):
- ss_button = self.chrome.get_element(By.ID, "share-screen-button")
- else:
- more_button = self.chrome.get_element_with_retry(By.ID, "more-button")
- self.chrome.click_element(more_button)
- ss_button = self.chrome.get_element_with_retry(By.ID, "more-options-share-screen-button")
-
+ ss_button = self.get_share_screen_button()
self.chrome.click_element(ss_button)
time.sleep(3)
@@ -289,7 +532,7 @@ def test_others_tile(self):
assert(not self.chrome.is_element_exist(By.CLASS_NAME, 'others-tile-inner'))
- self.set_and_test_track_limit(2)
+ self.set_and_test_tile_count(2)
others_tile = self.chrome.get_element_with_retry(By.CLASS_NAME, 'others-tile-inner', retries=10, wait_time=3)
@@ -345,7 +588,7 @@ def test_join_room_2_participants(self):
self.chrome.close_all()
- def _test_with_stats(self):
+ def test_with_stats(self):
room = "room"+str(random.randint(100, 999))
handle_1 = self.join_room_in_new_tab("participantA", room)
handle_2 = self.join_room_in_new_tab("participantB", room)
@@ -382,8 +625,21 @@ def is_first_participant_pinned(self):
conference = self.get_conference()
videoTrackAssignments = conference["videoTrackAssignments"]
return videoTrackAssignments[1]["streamId"] == conference["pinnedVideoId"]
+
+ def open_close_participant_list_drawer(self):
+ participant_list_button = None
+
+ if(self.chrome.is_element_exist(By.ID, "participant-list-button")):
+ participant_list_button = self.chrome.get_element(By.ID, "participant-list-button")
+ else:
+ more_button = self.chrome.get_element_with_retry(By.ID, "more-button")
+ self.chrome.click_element(more_button)
+ participant_list_button = self.chrome.get_element_with_retry(By.ID, "more-options-participant-list-button")
+
+ self.chrome.click_element(participant_list_button)
+ time.sleep(2)
- def _test_screen_share(self):
+ def test_screen_share(self):
room = "room"+str(random.randint(100, 999))
handle_1 = self.join_room_in_new_tab("participantA", room)
handle_2 = self.join_room_in_new_tab("participantB", room)
@@ -398,57 +654,148 @@ def _test_screen_share(self):
wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+ participantA_stream_id = self.get_videoTrackAssignments()[1]["streamId"]
+
self.chrome.switch_to_tab(handle_1)
wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+ participantB_stream_id = self.get_videoTrackAssignments()[1]["streamId"]
+
share_screen_button = self.get_share_screen_button()
+ self.chrome.click_element(share_screen_button)
- if(share_screen_button.is_displayed()):
- ss_button = self.chrome.get_element(By.ID, "share-screen-button")
- else:
- more_button = self.chrome.get_element(By.ID, "more-button")
- self.chrome.click_element(more_button)
- ss_button = self.chrome.get_element(By.ID, "more-options-share-screen-button")
+ self.chrome.switch_to_tab(handle_2)
- self.chrome.click_element(ss_button)
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 3)
+
+ participantA_share_stream_id = participantA_stream_id+"_presentation"
+
+
+ #check first vide track is assigned to A's screen
+ wait.until(lambda x:
+ (backend_assignments := self.parse_backend_video_trackAssignments(self.call_clearme_debugme()))
+ and
+ (2 == len(backend_assignments))
+ and
+ (backend_assignments[0]["assigned_stream_id"] == participantA_share_stream_id)
+ and
+ (backend_assignments[0]["reserved"])
+ and
+ (backend_assignments[1]["assigned_stream_id"] == participantA_stream_id)
+ and
+ (not backend_assignments[1]["reserved"])
+ )
+
+ #now share your video
+ share_screen_button = self.get_share_screen_button()
+ self.chrome.click_element(share_screen_button)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 4)
+
+ participantB_share_stream_id = participantB_stream_id+"_presentation"
+
+ wait.until(lambda x:
+ (backend_assignments := self.parse_backend_video_trackAssignments(self.call_clearme_debugme()))
+ and
+ (3 == len(backend_assignments))
+ and
+ (backend_assignments[0]["assigned_stream_id"] == participantA_share_stream_id)
+ and
+ (backend_assignments[0]["reserved"])
+ and
+ (backend_assignments[1]["assigned_stream_id"] == participantB_share_stream_id)
+ and
+ (backend_assignments[1]["reserved"])
+ and
+ (backend_assignments[2]["assigned_stream_id"] == participantA_stream_id)
+ and
+ (not backend_assignments[2]["reserved"])
+ )
+
+
+
+
+ self.chrome.close_all()
+
+ def test_reconnection_while_screen_sharing(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+
+ print("current: "+self.chrome.get_current_tab_id())
+
+ assert(handle_2 == self.chrome.get_current_tab_id())
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ participantA_stream_id = self.get_videoTrackAssignments()[1]["streamId"]
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ participantB_stream_id = self.get_videoTrackAssignments()[1]["streamId"]
+
+ share_screen_button = self.get_share_screen_button()
+ self.chrome.click_element(share_screen_button)
self.chrome.switch_to_tab(handle_2)
wait.until(lambda x: len(self.get_videoTrackAssignments()) == 3)
+
+ participantA_share_stream_id = participantA_stream_id+"_presentation"
+
+
+ #check first vide track is assigned to A's screen
+ wait.until(lambda x:
+ (backend_assignments := self.parse_backend_video_trackAssignments(self.call_clearme_debugme()))
+ and
+ (2 == len(backend_assignments))
+ and
+ (backend_assignments[0]["assigned_stream_id"] == participantA_share_stream_id)
+ and
+ (backend_assignments[0]["reserved"])
+ and
+ (backend_assignments[1]["assigned_stream_id"] == participantA_stream_id)
+ and
+ (not backend_assignments[1]["reserved"])
+ )
- if(share_screen_button.is_displayed()):
- ss_button2 = self.chrome.get_element(By.ID, "share-screen-button")
- else:
- more_button = self.chrome.get_element(By.ID, "more-button")
- self.chrome.click_element(more_button)
- ss_button2 = self.chrome.get_element(By.ID, "more-options-share-screen-button")
+ self.chrome.switch_to_tab(handle_1)
+ self.fake_reconnect()
- self.chrome.click_element(ss_button2)
+ wait.until(lambda x: self.chrome.is_element_exist(By.XPATH, "//span[text()='Reconnecting...']"))
- wait.until(lambda x: len(self.get_videoTrackAssignments()) == 4)
+ wait.until(lambda x: not self.chrome.is_element_exist(By.XPATH, "//span[text()='Reconnecting...']"))
-
- conference = self.get_conference()
- allParticipants = conference["allParticipants"]
- videoTrackAssignments = conference["videoTrackAssignments"]
+ self.chrome.switch_to_tab(handle_2)
- presenter2Exists = videoTrackAssignments[1]["streamId"] + "_presentation" in allParticipants
+ print("------------------")
+
+
- streamIdOfPresenter = videoTrackAssignments[2]["streamId"]
- print("streamIdOfPresenter: "+str(streamIdOfPresenter))
- broadcastObjectOfPresenter = allParticipants[streamIdOfPresenter]
- print("broadcastObjectOfPresenter: "+str(broadcastObjectOfPresenter))
- presenterPinned = broadcastObjectOfPresenter.get('isPinned') == True
- presenter1Exists = videoTrackAssignments[2]["streamId"] in allParticipants
+ for i in range(20):
+ stats = self.get_track_stats()
+ assert stats is not None
- print("presenter1Exists: "+str(presenter1Exists)+" presenter2Exists: "+str(presenter2Exists)+" presenterPinned: "+str(presenterPinned))
+ for track_stat in stats['inboundRtpList']: # Access 'inboundRtpList' properly
+ if 'trackIdentifier' in track_stat and track_stat['trackIdentifier'] == "ARDAMSvvideoTrack0":
+ print("* framesReceived: " + str(track_stat['framesReceived']))
+ assert track_stat['framesReceived'] > 0
+ time.sleep(2)
- assert(presenter1Exists and presenter2Exists and presenterPinned)
self.chrome.close_all()
+
+
+
def test_join_room_N_participants(self):
self.chrome.makeFullScreen()
N = 5
@@ -480,7 +827,7 @@ def test_join_room_N_participants(self):
print("Files in Directory:", files)
- self.set_and_test_track_limit(4)
+ self.set_and_test_tile_count(4)
wait.until(lambda x: len(self.get_videoTrackAssignments(3)) == 3)
@@ -489,7 +836,7 @@ def test_join_room_N_participants(self):
self.chrome.save_ss_as_file("shot-2.png")
print("screen shot 2 end: default")
- self.set_and_test_track_limit(6)
+ self.set_and_test_tile_count(6)
wait.until(lambda x: len(self.get_videoTrackAssignments(5)) == N)
@@ -610,6 +957,23 @@ def get_snackbar_content(self):
if snackbar is not None:
return snackbar.get_attribute("innerHTML")
return "not found"
+
+
+ def get_video_container_by_stream_name(self, stream_name):
+ # Get all video card elements
+ video_cards = self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container")
+
+ # Loop through each video card
+ for video_card in video_cards:
+ # Get the innerHTML of the video card
+ inner_html = video_card.get_attribute("innerHTML")
+
+ # Check if the stream_name is present in the innerHTML
+ if stream_name in inner_html:
+ return video_card
+
+ # If no matching video card is found, return None
+ return None
@@ -656,14 +1020,877 @@ def _test_recording(self):
wait.until(lambda x: "Recording is stopped successfully" in self.get_snackbar_content())
- wait.until(lambda x: self.rest_helper.getVoDFor(room+"_composite") is not None)
+ wait.until(lambda x: self.rest_helper.get_vod_for(room+"_composite") is not None)
+
+ self.chrome.close_all()
+
+
+ def test_tiled_layout_test(self):
+ self.chrome.makeFullScreen()
+ room = "room"+str(random.randint(100, 999))
+ wait = self.chrome.get_wait(30, 3)
+
+ self.join_room_in_new_tab("participant"+str(random.randint(100, 999)), room)
+ self.assertLocalVideoAvailable()
+
+ #add 3 participants, check video track assignments size is 4=1+3, then remove
+ process = self.create_participants_with_test_tool("participant", room, 3)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(4)) == 4)
+ self.kill_participants_with_test_tool(process)
+
+ #check video track assignments size is 1
+ wait.until(lambda x: len(self.get_videoTrackAssignments(1)) == 1)
+
+ #set video tile count 4
+ self.set_and_test_tile_count(4)
+
+ #add 3 participants, check video track assignments size is 4=1+3, then remove
+ process = self.create_participants_with_test_tool("participant", room, 3)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(4)) == 4)
+ self.kill_participants_with_test_tool(process)
+
+ #add 4 participants, check video track assignments size is 3=1+2 one tile is for others
+ process = self.create_participants_with_test_tool("participant", room, 4)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(3)) == 3)
+
+ #set video tile count 6
+ self.set_and_test_tile_count(6)
+ #add 3 participants, check video track assignments size is 4=1+3, then remove
+ wait.until(lambda x: len(self.get_videoTrackAssignments(5)) == 5)
+
+ #remove participants
+ self.kill_participants_with_test_tool(process)
+
+
self.chrome.close_all()
+ def test_pinned_layout_test(self):
+ self.chrome.makeFullScreen()
+ room = "room"+str(random.randint(100, 999))
+ wait = self.chrome.get_wait(30, 3)
+
+ self.join_room_in_new_tab("participant"+str(random.randint(100, 999)), room)
+ self.assertLocalVideoAvailable()
+
+ self.set_layout("sidebar")
+
+ #add 4 participants, check video track assignments size is 5=1+4, then remove
+ process = self.create_participants_with_test_tool("participant", room, 4)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(5)) == 5)
+ self.kill_participants_with_test_tool(process)
+
+ #check video track assignments size is 1
+ wait.until(lambda x: len(self.get_videoTrackAssignments(1)) == 1)
+
+ #add 5 participants, check video track assignments size is 4=1+3, one tile is for others then remove
+ process = self.create_participants_with_test_tool("participant", room, 5)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(4)) == 4)
+ self.kill_participants_with_test_tool(process)
+
+ #set video tile count 4
+ self.set_and_test_tile_count(4)
+
+ #add 3 participants, check video track assignments size is 4=1+3, then remove
+ process = self.create_participants_with_test_tool("participant", room, 3)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(4)) == 4)
+ self.kill_participants_with_test_tool(process)
+
+ #add 4 participants, check video track assignments size is 3=1+2 one tile is for others
+ process = self.create_participants_with_test_tool("participant", room, 4)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(3)) == 3)
+
+ #set video tile count 6
+ self.set_and_test_tile_count(6)
+
+ #add 3 participants, check video track assignments size is 3=1+4 then remove
+ wait.until(lambda x: len(self.get_videoTrackAssignments(5)) == 5)
+
+ self.set_layout("tiled")
+ wait.until(lambda x: len(self.get_videoTrackAssignments(5)) == 5)
+ #remove participants
+ self.kill_participants_with_test_tool(process)
+
+
+ self.chrome.close_all()
+ #FIXME uncomment test
+ def _test_pin_on_video_card(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+
+ print("current: "+self.chrome.get_current_tab_id())
+
+ assert(handle_2 == self.chrome.get_current_tab_id())
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ wait.until(lambda x: len(self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container.not-pinned")) == 2)
+
+ #pin yourself
+ print("pin participantA")
+ participantA_video_card = self.get_video_container_by_stream_name("participantA")
+ participantA_pin_button = self.chrome.get_element_in_element(participantA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='pin']", wait_until_clickable=False)
+ participantA_pin_button.click()
+
+
+ wait.until(lambda x: self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantA" in inner_html)
+
+ #unpin yourself
+ print("unpin participantA")
+ participantA_video_card = self.get_video_container_by_stream_name("participantA")
+ self.chrome.move_to_element(participantA_video_card)
+ participantA_unpin_button = self.chrome.get_element_in_element(participantA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='unpin']")
+ participantA_unpin_button.click()
+
+ wait.until(lambda x: not self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ #pin participantB
+ print("pin participantB")
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+ print("participantB_video_card:"+participantB_video_card.get_attribute("innerHTML"))
+ self.chrome.move_to_element(participantB_video_card)
+ participantB_pin_button = self.chrome.get_element_in_element(participantB_video_card, By.XPATH, ".//button[@type='button' and @aria-label='pin']")
+ self.chrome.move_to_element(participantB_video_card)
+ self.chrome.save_ss_as_file("test_pin_on_video_card-1.png")
+ time.sleep(1)
+ participantB_pin_button.click()
+
+ wait.until(lambda x: self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantB" in inner_html)
+
+ #check if reserved true
+ debug_info = self.call_clearme_debugme()
+ backend_assignments = self.parse_backend_video_trackAssignments(debug_info)
+ assert(1 == len(backend_assignments))
+ assert(backend_assignments[0]["reserved"])
+
+ #unpin participantB
+ print("unpin participantB")
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+ self.chrome.move_to_element(participantB_video_card)
+ participantB_unpin_button = self.chrome.get_element_in_element(participantB_video_card, By.XPATH, ".//button[@type='button' and @aria-label='unpin']")
+ participantB_unpin_button.click()
+
+ wait.until(lambda x: not self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ #check if reserved false
+ debug_info = self.call_clearme_debugme()
+ print("debug info:"+debug_info)
+ backend_assignments = self.parse_backend_video_trackAssignments(debug_info)
+ assert(1 == len(backend_assignments))
+ assert(False == backend_assignments[0]["reserved"])
+
+ #pin yourself
+ participantA_video_card = self.get_video_container_by_stream_name("participantA")
+ self.chrome.move_to_element(participantA_video_card)
+ participantA_pin_button = self.chrome.get_element_in_element(participantA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='pin']")
+ participantA_pin_button.click()
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantA" in inner_html)
+
+ #pin participantB
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+ self.chrome.move_to_element(participantB_video_card)
+ participantB_pin_button = self.chrome.get_element_in_element(participantB_video_card, By.XPATH, ".//button[@type='button' and @aria-label='pin']")
+ participantB_pin_button.click()
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantB" in inner_html)
+
+
+ self.chrome.close_all()
+
+
+
+ def test_pin_on_participant_list(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+
+ print("current: "+self.chrome.get_current_tab_id())
+
+ assert(handle_2 == self.chrome.get_current_tab_id())
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ wait.until(lambda x: len(self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container.not-pinned")) == 2)
+
+ self.open_close_participant_list_drawer()
+
+ #pin yourself
+ participantA_pin_button = self.chrome.get_element_with_retry(By.XPATH, "//button[starts-with(@id, 'pin-participantA_')]")
+ participantA_pin_button.click()
+ wait.until(lambda x: self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantA" in inner_html)
+
+ #unpin yourself
+ participantA_unpin_button = self.chrome.get_element_with_retry(By.XPATH, "//button[starts-with(@id, 'unpin-participantA_')]")
+ participantA_unpin_button.click()
+
+ wait.until(lambda x: not self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+
+ #pin participantB
+ participantB_pin_button = self.chrome.get_element_with_retry(By.XPATH, "//button[starts-with(@id, 'pin-participantB_')]")
+ participantB_pin_button.click()
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantB" in inner_html)
+
+ #check if reserved true
+ debug_info = self.call_clearme_debugme()
+ backend_assignments = self.parse_backend_video_trackAssignments(debug_info)
+ assert(1 == len(backend_assignments))
+ assert(backend_assignments[0]["reserved"])
+
+ self.open_close_participant_list_drawer()
+
+
+ #unpin participantB
+ participantB_unpin_button = self.chrome.get_element_with_retry(By.XPATH, "//button[starts-with(@id, 'unpin-participantB_')]")
+ participantB_unpin_button.click()
+
+
+ wait.until(lambda x: not self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ #check if reserved false
+ debug_info = self.call_clearme_debugme()
+ print("debug info:"+debug_info)
+ backend_assignments = self.parse_backend_video_trackAssignments(debug_info)
+ assert(1 == len(backend_assignments))
+ assert(False == backend_assignments[0]["reserved"])
+
+ #### Now we will test pinning other while I am pinned
+
+ self.open_close_participant_list_drawer()
+
+ #pin yourself
+ participantA_unpin_button = self.chrome.get_element_with_retry(By.XPATH, "//button[starts-with(@id, 'pin-participantA_')]")
+ participantA_pin_button.click()
+ wait.until(lambda x: self.chrome.is_element_exist(By.CSS_SELECTOR, "div.single-video-container.pinned"))
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantA" in inner_html)
+
+
+ #pin participantB
+ participantB_pin_button = self.chrome.get_element_with_retry(By.XPATH, "//button[starts-with(@id, 'pin-participantB_')]")
+ participantB_pin_button.click()
+
+ pinned_video_card = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "div.single-video-container.pinned")
+ inner_html = pinned_video_card.get_attribute("innerHTML")
+ assert ("participantB" in inner_html)
+
+ self.chrome.close_all()
+
+
+ def test_mute_on_video_card(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+
+ print("current: "+self.chrome.get_current_tab_id())
+
+ assert(handle_2 == self.chrome.get_current_tab_id())
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ wait.until(lambda x: len(self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container.not-pinned")) == 2)
+
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+
+ #check muted icon is not visible
+ assert(not self.chrome.is_nested_element_exist(participantB_video_card, By.XPATH, ".//div[@aria-label='mic is muted']"))
+
+ #move mouse to make overlay buttons visible
+ self.chrome.move_to_element(participantB_video_card)
+
+ #mute participantB
+ mute_button = self.chrome.get_element_in_element(participantB_video_card, By.XPATH, ".//button[@type='button' and @aria-label='mute']")
+ mute_button.click()
+
+ '''
+ #accept mute
+ wait.until(lambda x: self.chrome.is_element_exist(By.XPATH, "//button[text()='Mute']"))
+ mute_accept_button = self.chrome.get_element_with_retry(By.XPATH, "//button[text()='Mute']")
+ mute_accept_button.click()
+ '''
+
+ #check muted icon will be visible
+ wait.until(lambda x: self.chrome.get_element_in_element(participantB_video_card, By.XPATH, ".//div[@aria-label='mic is muted']") is not None)
+
+ #switch participantB tab and open mic
+ self.chrome.switch_to_tab(handle_2)
+ mic = self.chrome.get_element(By.ID, "mic-button")
+ self.chrome.click_element(mic)
+
+ #switch participantA tab and check muted icon is not visible
+ self.chrome.switch_to_tab(handle_1)
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+ wait.until(lambda x: not self.chrome.is_nested_element_exist(participantB_video_card, By.XPATH, "//div[@aria-label='mic is muted']"))
+
+ self.chrome.close_all()
+
+ def test_talking_people_frame(self):
+ self.chrome.close_all()
+ current_dir = os.path.dirname(os.path.abspath(__file__))
+ fake_audio_file_path = os.path.join(current_dir, "fake_mic.wav")
+ self.chrome = Browser()
+ self.chrome.init(not self.is_local, mic_file=fake_audio_file_path)
+
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+
+ print("current: "+self.chrome.get_current_tab_id())
+
+ assert(handle_2 == self.chrome.get_current_tab_id())
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.set_audio_level(1)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ wait.until(lambda x: len(self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container.not-pinned")) == 2)
+
+
+ #switch participantA tab check green frame appear
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+ talking_indicator = self.chrome.get_element_in_element(participantB_video_card, By.CLASS_NAME, "talking-indicator-light", wait_until_clickable=False)
+ wait.until(lambda x: talking_indicator.is_displayed())
+
+ #switch participantB tab and mute
+ self.chrome.switch_to_tab(handle_2)
+ mic = self.chrome.get_element(By.ID, "mic-button")
+ self.chrome.click_element(mic)
+
+ #switch participantA tab check green frame disappear
+ self.chrome.switch_to_tab(handle_1)
+ participantB_video_card = self.get_video_container_by_stream_name("participantB")
+ talking_indicator = self.chrome.get_element_in_element(participantB_video_card, By.CLASS_NAME, "talking-indicator-light", wait_until_clickable=False)
+ wait.until(lambda x: not talking_indicator.is_displayed())
+
+ self.chrome.close_all()
+
+
+ def test_video_track_assignment(self):
+ self.chrome.close_all()
+ current_dir = os.path.dirname(os.path.abspath(__file__))
+ fake_audio_file_path = os.path.join(current_dir, "fake_mic.wav")
+ self.chrome = Browser()
+ self.chrome.init(not self.is_local, mic_file=fake_audio_file_path)
+
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+ self.change_tile_count(2)
+ handle_3 = self.join_room_in_new_tab("participantC", room)
+ self.change_tile_count(2)
+ handle_4 = self.join_room_in_new_tab("participantD", room)
+ self.change_tile_count(2)
+ self.set_audio_level(0.1)
+ handle_5 = self.join_room_in_new_tab("participantE", room)
+ self.change_tile_count(2)
+ self.set_audio_level(0.1)
+
+
+ wait = self.chrome.get_wait()
+ self.chrome.switch_to_tab(handle_1)
+ wait.until(lambda x: len(self.get_videoTrackAssignments(5)) == 5)
+
+ self.change_tile_count(4)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments(3)) == 3) #1(you)+2(videos)+1(other)=4 tiles => 3 video tracks (don't count others)
+
+ #check 2 video track assigned to B or C (because D and E have silent audio)
+ wait.until(lambda x:
+ (backend_assignments := self.parse_backend_video_trackAssignments(self.call_clearme_debugme()))
+ and
+ (2 == len(backend_assignments))
+ and
+ (
+ backend_assignments[0]["assigned_stream_id"].startswith("participantB")
+ or
+ backend_assignments[0]["assigned_stream_id"].startswith("participantC")
+ )
+ and
+ (
+ backend_assignments[1]["assigned_stream_id"].startswith("participantB")
+ or
+ backend_assignments[1]["assigned_stream_id"].startswith("participantC")
+ )
+ )
+
+ #change audio levels: B and C are 0.1 ; D and E are 1
+ self.chrome.switch_to_tab(handle_2)
+ self.set_audio_level(0.1)
+ self.chrome.switch_to_tab(handle_3)
+ self.set_audio_level(0.1)
+ self.chrome.switch_to_tab(handle_4)
+ self.set_audio_level(1)
+ self.chrome.switch_to_tab(handle_5)
+ self.set_audio_level(1)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ #check 2 video track assigned to D or E (because B and C have silent audio)
+ wait.until(lambda x:
+ (backend_assignments := self.parse_backend_video_trackAssignments(self.call_clearme_debugme()))
+ and
+ (2 == len(backend_assignments))
+ and
+ (
+ backend_assignments[0]["assigned_stream_id"].startswith("participantD")
+ or
+ backend_assignments[0]["assigned_stream_id"].startswith("participantE")
+ )
+ and
+ (
+ backend_assignments[1]["assigned_stream_id"].startswith("participantD")
+ or
+ backend_assignments[1]["assigned_stream_id"].startswith("participantE")
+ )
+ )
+
+ self.chrome.close_all()
+
+
+ def test_camera_mic_setting_in_meeting_room(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+
+ more_options_button = self.chrome.get_element_with_retry(By.ID, "settings-button")
+ more_options_button.click()
+
+ time.sleep(1)
+
+ call_settings_button = self.chrome.get_element(By.ID, "call-settings")
+ self.chrome.mouse_click_on(call_settings_button)
+
+ camera_select = self.chrome.get_element_with_retry(By.ID, "setting-dialog-camera-select")
+ self.chrome.mouse_click_on(camera_select)
+
+ camera = self.chrome.get_element_with_retry(By.XPATH, "//li[contains(text(), 'fake_device_0')]")
+ self.chrome.mouse_click_on(camera)
+
+ resolution_select = self.chrome.get_element_with_retry(By.ID, "setting-dialog-resolution-select")
+ self.chrome.mouse_click_on(resolution_select)
+
+ time.sleep(1)
+
+ resolution = self.chrome.get_element_with_retry(By.XPATH, "//li[contains(text(), 'Low definition (180p)')]")
+ self.chrome.mouse_click_on(resolution)
+
+ mic_select = self.chrome.get_element_with_retry(By.ID, "setting-dialog-mic-select")
+ self.chrome.mouse_click_on(mic_select)
+
+ time.sleep(1)
+
+ self.chrome.save_ss_as_file("test_camera_mic_setting_in_meeting_room-1.png")
+
+ if self.chrome.is_element_exist(By.XPATH, "//li[contains(text(), 'Fake Audio Input 2')]"):
+ mic = self.chrome.get_element_with_retry(By.XPATH, "//li[contains(text(), 'Fake Audio Input 2')]")
+ self.chrome.mouse_click_on(mic)
+
+ close_button = self.chrome.get_element_with_retry(By.CSS_SELECTOR, "button[aria-label='close']")
+ close_button.click()
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.ID, "meeting-gallery")
+ assert(meeting_gallery.is_displayed())
+
+ self.chrome.close_all()
+
+ def _test_chat_messages(self):
+ message_A = "hello from A"
+ message_B = "hello from B"
+ message_C = "hello from C"
+
+
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+ handle_3 = self.join_room_in_new_tab("participantC", room, play_only=True)
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+ #VTA counts should be 2, because C is play only
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_2)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+
+ #send message from A
+ self.send_message(message_A)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_A in last_message.get_attribute("innerHTML"))
+
+ #check messages on B and C
+ self.chrome.switch_to_tab(handle_2)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_A in last_message.get_attribute("innerHTML"))
+
+ self.chrome.switch_to_tab(handle_3)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_A in last_message.get_attribute("innerHTML"))
+
+ #send message from B
+ self.chrome.switch_to_tab(handle_2)
+ self.send_message(message_B)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_B in last_message.get_attribute("innerHTML"))
+
+ #check messages on A and C
+ self.chrome.switch_to_tab(handle_2)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_B in last_message.get_attribute("innerHTML"))
+
+ self.chrome.switch_to_tab(handle_3)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_B in last_message.get_attribute("innerHTML"))
+
+ #send message from C
+ self.send_message(message_C)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_C in last_message.get_attribute("innerHTML"))
+
+ #check messages on A and B
+ self.chrome.switch_to_tab(handle_1)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_C in last_message.get_attribute("innerHTML"))
+
+ self.chrome.switch_to_tab(handle_2)
+ messages = self.chrome.get_all_elements(By.ID, "message")
+ last_message = messages[-1]
+ assert(message_C in last_message.get_attribute("innerHTML"))
+
+
+ self.chrome.close_all()
+
+ def test_reactions(self):
+ reaction_A = "💖"
+ reaction_B = "👍🏼"
+ reaction_C = "🎉"
+
+
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+ handle_2 = self.join_room_in_new_tab("participantB", room)
+ handle_3 = self.join_room_in_new_tab("participantC", room, play_only=True)
+
+ self.assertLocalVideoAvailable()
+
+ wait = self.chrome.get_wait()
+ #VTA counts should be 2, because C is play only
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_2)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+ self.chrome.switch_to_tab(handle_1)
+
+ wait.until(lambda x: len(self.get_videoTrackAssignments()) == 2)
+
+
+ #send reaction from A
+ self.send_reaction(reaction_A)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_A}']/br/following-sibling::span[text()='You']"))
+
+ #check messages on B and C
+ self.chrome.switch_to_tab(handle_2)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_A}']/br/following-sibling::span[text()='participantA']"))
+ self.chrome.switch_to_tab(handle_3)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_A}']/br/following-sibling::span[text()='participantA']"))
+
+
+ #send reaction from B
+ self.chrome.switch_to_tab(handle_2)
+ self.send_reaction(reaction_B)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_B}']/br/following-sibling::span[text()='You']"))
+
+ #check messages on A and C
+ self.chrome.switch_to_tab(handle_1)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_B}']/br/following-sibling::span[text()='participantB']"))
+ self.chrome.switch_to_tab(handle_3)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_B}']/br/following-sibling::span[text()='participantB']"))
+
+
+ #send reaction from C
+ self.send_reaction(reaction_C)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_C}']/br/following-sibling::span[text()='You']"))
+
+ #check messages on A and B
+ self.chrome.switch_to_tab(handle_1)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_C}']/br/following-sibling::span[text()='participantC']"))
+ self.chrome.switch_to_tab(handle_2)
+ wait.until(lambda x: self.chrome.is_element_displayed(By.XPATH, f"//div[text()='{reaction_C}']/br/following-sibling::span[text()='participantC']"))
+
+ self.chrome.close_all()
+
+
+ def test_background_replacement(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+
+ more_options_button = self.chrome.get_element_with_retry(By.ID, "settings-button")
+ more_options_button.click()
+
+ time.sleep(1)
+
+ virtual_effects_button = self.chrome.get_element(By.ID, "virtual-effects")
+ self.chrome.mouse_click_on(virtual_effects_button)
+
+ time.sleep(1)
+
+ wait = self.chrome.get_wait()
+ wait.until(lambda x: self.chrome.is_element_exist(By.ID, "slight-blur-button"))
+
+ slight_blur_button = self.chrome.get_element(By.ID, "slight-blur-button")
+ slight_blur_button.click()
+
+ time.sleep(5)
+
+ blur_button = self.chrome.get_element(By.ID, "blur-button")
+ blur_button.click()
+
+ time.sleep(5)
+
+ custom_background_button = self.chrome.get_element(By.ID, "custom-virtual-background-button")
+ custom_background_button.click()
+
+ time.sleep(5)
+
+ remove_effect_button = self.chrome.get_element(By.ID, "remove-effect-button")
+ remove_effect_button.click()
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.ID, "meeting-gallery")
+ assert(meeting_gallery.is_displayed())
+
+ self.chrome.close_all()
+
+ def rgb_to_hex(self, rgb_string):
+ # Extract RGB values from the string
+ rgb_values = [int(x) for x in rgb_string.replace("rgb(", "").replace(")", "").split(",")]
+ # Convert to hex and return
+ color = "#{:02X}{:02X}{:02X}".format(*rgb_values)
+ print ("background_color:"+color)
+ return color
+
+
+ def test_theme(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+
+ more_options_button = self.chrome.get_element_with_retry(By.ID, "settings-button")
+ more_options_button.click()
+
+ time.sleep(1)
+
+ general_button = self.chrome.get_element(By.ID, "general-button")
+ self.chrome.mouse_click_on(general_button)
+
+ theme_select = self.chrome.get_element(By.ID, "theme-select")
+ self.chrome.mouse_click_on(theme_select)
+
+ green = self.chrome.get_element(By.XPATH, "//li[@data-value='green']")
+ self.chrome.mouse_click_on(green)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.TAG_NAME, "body")
+ background_color = self.chrome.execute_script("return window.getComputedStyle(arguments[0]).backgroundColor;", meeting_gallery)
+ assert("#001D1A" == self.rgb_to_hex(background_color))
+
+ theme_select = self.chrome.get_element(By.ID, "theme-select")
+ self.chrome.mouse_click_on(theme_select)
+
+ time.sleep(1)
+
+ blue = self.chrome.get_element(By.XPATH, "//li[@data-value='blue']")
+ self.chrome.mouse_click_on(blue)
+
+ time.sleep(1)
+
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.TAG_NAME, "body")
+ background_color = self.chrome.execute_script("return window.getComputedStyle(arguments[0]).backgroundColor;", meeting_gallery)
+ assert("#00838F" == self.rgb_to_hex(background_color))
+
+ theme_select = self.chrome.get_element(By.ID, "theme-select")
+ self.chrome.mouse_click_on(theme_select)
+
+ time.sleep(1)
+
+ gray = self.chrome.get_element(By.XPATH, "//li[@data-value='gray']")
+ self.chrome.mouse_click_on(gray)
+
+ time.sleep(1)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.TAG_NAME, "body")
+ background_color = self.chrome.execute_script("return window.getComputedStyle(arguments[0]).backgroundColor;", meeting_gallery)
+ assert("#424242" == self.rgb_to_hex(background_color))
+
+ theme_select = self.chrome.get_element(By.ID, "theme-select")
+ self.chrome.mouse_click_on(theme_select)
+
+ time.sleep(1)
+
+ white = self.chrome.get_element(By.XPATH, "//li[@data-value='white']")
+ self.chrome.mouse_click_on(white)
+
+ time.sleep(1)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.TAG_NAME, "body")
+ background_color = self.chrome.execute_script("return window.getComputedStyle(arguments[0]).backgroundColor;", meeting_gallery)
+ assert("#FFFFFF" == self.rgb_to_hex(background_color))
+
+ theme_select = self.chrome.get_element(By.ID, "theme-select")
+ self.chrome.mouse_click_on(theme_select)
+
+ time.sleep(1)
+
+ green = self.chrome.get_element(By.XPATH, "//li[@data-value='green']")
+ self.chrome.mouse_click_on(green)
+
+ time.sleep(1)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.TAG_NAME, "body")
+ background_color = self.chrome.execute_script("return window.getComputedStyle(arguments[0]).backgroundColor;", meeting_gallery)
+ assert("#001D1A" == self.rgb_to_hex(background_color))
+
+
+ self.chrome.close_all()
+
+
+ def test_language(self):
+ room = "room"+str(random.randint(100, 999))
+ handle_1 = self.join_room_in_new_tab("participantA", room)
+
+ more_options_button = self.chrome.get_element_with_retry(By.ID, "settings-button")
+ more_options_button.click()
+
+ time.sleep(3)
+
+ general_button = self.chrome.get_element(By.ID, "general-button")
+ self.chrome.mouse_click_on(general_button)
+
+ lang_select = self.chrome.get_element(By.ID, "language-select")
+ self.chrome.mouse_click_on(lang_select)
+
+ time.sleep(3)
+
+ en = self.chrome.get_element(By.XPATH, "//li[@data-value='en']")
+ self.chrome.mouse_click_on(en)
+
+ assert(self.chrome.is_element_exist(By.XPATH, "//label[text()='Language']"))
+
+ lang_select = self.chrome.get_element(By.ID, "language-select")
+ self.chrome.mouse_click_on(lang_select)
+
+ time.sleep(3)
+
+ tr = self.chrome.get_element(By.XPATH, "//li[@data-value='tr']")
+ self.chrome.mouse_click_on(tr)
+
+ assert(self.chrome.is_element_exist(By.XPATH, "//label[text()='Dil']"))
+
+ lang_select = self.chrome.get_element(By.ID, "language-select")
+ self.chrome.mouse_click_on(lang_select)
+
+ time.sleep(3)
+
+ es = self.chrome.get_element(By.XPATH, "//li[@data-value='es']")
+ self.chrome.mouse_click_on(es)
+
+ assert(self.chrome.is_element_exist(By.XPATH, "//label[text()='Idioma']"))
+
+ lang_select = self.chrome.get_element(By.ID, "language-select")
+ self.chrome.mouse_click_on(lang_select)
+
+ time.sleep(3)
+
+ fr = self.chrome.get_element(By.XPATH, "//li[@data-value='fr']")
+ self.chrome.mouse_click_on(fr)
+
+ assert(self.chrome.is_element_exist(By.XPATH, "//label[text()='Langue']"))
+
+ lang_select = self.chrome.get_element(By.ID, "language-select")
+ self.chrome.mouse_click_on(lang_select)
+
+ time.sleep(3)
+
+ en = self.chrome.get_element(By.XPATH, "//li[@data-value='en']")
+ self.chrome.mouse_click_on(en)
+
+ assert(self.chrome.is_element_exist(By.XPATH, "//label[text()='Language']"))
+
+
+
+ self.chrome.close_all()
if __name__ == '__main__':
unittest.main()
diff --git a/test/test_main.py b/test/test_main.py
index 96a295eb..d8f301cf 100644
--- a/test/test_main.py
+++ b/test/test_main.py
@@ -29,6 +29,7 @@
else:
suite2 = unittest.TestLoader().loadTestsFromModule(test_join_leave)
+
suite.addTests(suite2)
#suite.addTest(TestJoinLeave("test_join_without_camera_mic"))
@@ -36,4 +37,5 @@
suite.addTest(TestDeployment('test_delete_app'))
ret = not unittest.TextTestRunner(verbosity=2, failfast=fail_fast).run(suite).wasSuccessful()
-sys.exit(ret)
\ No newline at end of file
+sys.exit(ret)
+
diff --git a/test/test_webinar.py b/test/test_webinar.py
index def55405..e425ae89 100644
--- a/test/test_webinar.py
+++ b/test/test_webinar.py
@@ -15,12 +15,15 @@
class TestWebinarScenario(unittest.TestCase):
def setUp(self):
+ self.is_local = False
+ #self.is_local = True
print(self._testMethodName, " starting...")
self.url = os.environ.get('SERVER_URL')
self.test_app_name = os.environ.get('TEST_APP_NAME')
self.user = os.environ.get('AMS_USER')
self.password = os.environ.get('AMS_PASSWORD')
self.chrome = Browser()
+ self.chrome.init(not self.is_local)
self.chrome.init(True)
self.rest_helper = RestHelper(self.url, self.user, self.password, self.test_app_name)
self.rest_helper.login()
@@ -39,95 +42,92 @@ def start_load_test():
load_test_thread = threading.Thread(target=start_load_test)
load_test_thread.start()
- def join_room_as_admin(self, participant, room):
+ def join_room_as_admin(self, participant, room, skip_speed_test=False):
print("url: "+self.url+"/"+self.test_app_name+"/"+room)
app = "/"+self.test_app_name
if self.url.endswith("localhost:3000"):
app = ""
- handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?role=host&streamName="+participant)
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?role=host&streamName=" + participant + ("&enterDirectly=true" if skip_speed_test else ""))
#name_text_box = self.chrome.get_element_with_retry(By.ID,"participant_name")
#self.chrome.write_to_element(name_text_box, participant)
join_button = self.chrome.get_element_with_retry(By.ID,"room_join_button")
-
- time.sleep(5)
-
self.chrome.click_element(join_button)
- time.sleep(5)
-
- speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
- assert(speedTestCircularProgress.is_displayed())
- time.sleep(5)
+ if not skip_speed_test:
+ time.sleep(5)
+ speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
+ assert(speedTestCircularProgress.is_displayed())
- timeoutCounter = 0
+ time.sleep(5)
- isSpeedTestFinished = False
- isSpeedTestFailed = False
+ timeoutCounter = 0
- while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
- time.sleep(1)
- timeoutCounter += 1
- script = "return window.conference.speedTestObject;"
- result_json = self.chrome.execute_script(script)
- if result_json is not None:
- isSpeedTestFinished = result_json["isfinished"]
- isSpeedTestFailed = result_json["isfailed"]
+ isSpeedTestFinished = False
+ isSpeedTestFailed = False
- speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
+ while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
+ time.sleep(1)
+ timeoutCounter += 1
+ script = "return window.conference.speedTestObject;"
+ result_json = self.chrome.execute_script(script)
+ if result_json is not None:
+ isSpeedTestFinished = result_json["isfinished"]
+ isSpeedTestFailed = result_json["isfailed"]
- self.chrome.print_ss_as_base64()
+ speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
- self.chrome.click_element(speedTestModalJoinButton)
+ self.chrome.print_ss_as_base64()
+ self.chrome.click_element(speedTestModalJoinButton)
+
meeting_gallery = self.chrome.get_element_with_retry(By.ID,"meeting-gallery")
assert(meeting_gallery.is_displayed())
return handle
- def join_room_as_presenter(self, participant, room):
+ def join_room_as_presenter(self, participant, room, skip_speed_test=False):
print("url: "+self.url+"/"+self.test_app_name+"/"+room)
app = "/"+self.test_app_name
if self.url.endswith("localhost:3000"):
app = ""
- handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?role=speaker&streamName="+participant)
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?role=speaker&streamName=" + participant + ("&enterDirectly=true" if skip_speed_test else ""))
#name_text_box = self.chrome.get_element_with_retry(By.ID,"participant_name")
#self.chrome.write_to_element(name_text_box, participant)
join_button = self.chrome.get_element_with_retry(By.ID,"room_join_button")
- time.sleep(5)
-
self.chrome.click_element(join_button)
- time.sleep(5)
+ if not skip_speed_test:
+ time.sleep(5)
- speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
- assert(speedTestCircularProgress.is_displayed())
+ speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
+ assert(speedTestCircularProgress.is_displayed())
- time.sleep(5)
+ time.sleep(5)
- timeoutCounter = 0
+ timeoutCounter = 0
- isSpeedTestFinished = False
- isSpeedTestFailed = False
+ isSpeedTestFinished = False
+ isSpeedTestFailed = False
- while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
- time.sleep(1)
- timeoutCounter += 1
- script = "return window.conference.speedTestObject;"
- result_json = self.chrome.execute_script(script)
- if result_json is not None:
- isSpeedTestFinished = result_json["isfinished"]
- isSpeedTestFailed = result_json["isfailed"]
+ while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
+ time.sleep(1)
+ timeoutCounter += 1
+ script = "return window.conference.speedTestObject;"
+ result_json = self.chrome.execute_script(script)
+ if result_json is not None:
+ isSpeedTestFinished = result_json["isfinished"]
+ isSpeedTestFailed = result_json["isfailed"]
- speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
+ speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
- self.chrome.click_element(speedTestModalJoinButton)
+ self.chrome.click_element(speedTestModalJoinButton)
meeting_gallery = self.chrome.get_element_with_retry(By.ID,"meeting-gallery")
@@ -136,12 +136,12 @@ def join_room_as_presenter(self, participant, room):
return handle
- def join_room_as_player(self, participant, room):
+ def join_room_as_player(self, participant, room, skip_speed_test=False):
print("url: "+self.url+"/"+self.test_app_name+"/"+room)
app = "/"+self.test_app_name
if self.url.endswith("localhost:3000"):
app = ""
- handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?playOnly=true&role=listener&streamName="+participant)
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?playOnly=true&role=listener&streamName=" + participant + ("&enterDirectly=true" if skip_speed_test else ""))
wait = self.chrome.get_wait()
@@ -150,35 +150,33 @@ def join_room_as_player(self, participant, room):
join_button = self.chrome.get_element_with_retry(By.ID,"room_join_button")
- time.sleep(5)
-
self.chrome.click_element(join_button)
- time.sleep(5)
+ if not skip_speed_test:
+ time.sleep(5)
- speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
- wait.until(lambda x: speedTestCircularProgress.is_displayed())
+ speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
+ wait.until(lambda x: speedTestCircularProgress.is_displayed())
- time.sleep(5)
-
- timeoutCounter = 0
+ time.sleep(5)
- isSpeedTestFinished = False
- isSpeedTestFailed = False
+ timeoutCounter = 0
- while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
- time.sleep(1)
- timeoutCounter += 1
- script = "return window.conference.speedTestObject;"
- result_json = self.chrome.execute_script(script)
- if result_json is not None:
- isSpeedTestFinished = result_json["isfinished"]
- isSpeedTestFailed = result_json["isfailed"]
+ isSpeedTestFinished = False
+ isSpeedTestFailed = False
- speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
+ while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
+ time.sleep(1)
+ timeoutCounter += 1
+ script = "return window.conference.speedTestObject;"
+ result_json = self.chrome.execute_script(script)
+ if result_json is not None:
+ isSpeedTestFinished = result_json["isfinished"]
+ isSpeedTestFailed = result_json["isfailed"]
- self.chrome.click_element(speedTestModalJoinButton)
+ speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
+ self.chrome.click_element(speedTestModalJoinButton)
meeting_gallery = self.chrome.get_element_with_retry(By.ID,"meeting-gallery")
@@ -266,6 +264,22 @@ def get_track_stats(self):
return result_json
+ def get_video_container_by_stream_name(self, stream_name):
+ # Get all video card elements
+ video_cards = self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container")
+
+ # Loop through each video card
+ for video_card in video_cards:
+ # Get the innerHTML of the video card
+ inner_html = video_card.get_attribute("innerHTML")
+
+ # Check if the stream_name is present in the innerHTML
+ if stream_name in inner_html:
+ return video_card
+
+ # If no matching video card is found, return None
+ return None
+
def get_conference(self):
script = "return window.conference;"
result_json = self.chrome.execute_script_with_retry(script)
@@ -764,6 +778,70 @@ def test_request_to_speak(self):
self.chrome.switch_to_tab(handle_player_A)
wait.until(lambda x: len(self.get_participants()) == 1)
+ self.chrome.close_all()
+
+ def test_admin_video_card_controls(self):
+ # create a room and join as admin and presenter
+ room = "room"+str(random.randint(100, 999))
+ handle_admin = self.join_room_as_admin("adminA", room, skip_speed_test=True)
+ handle_presenter = self.join_room_as_presenter("presenterA", room, skip_speed_test=True)
+
+ assert(handle_presenter == self.chrome.get_current_tab_id())
+
+ presenterId = self.get_publishStreamId()
+
+ assert(self.chrome.get_element_with_retry(By.ID,presenterId).is_displayed())
+
+ wait = self.chrome.get_wait()
+
+ # check if both participants are in the room and see each other
+ wait.until(lambda x: len(self.get_participants()) == 2)
+
+ self.chrome.switch_to_tab(handle_admin)
+
+ wait.until(lambda x: len(self.get_participants()) == 2)
+
+
+ wait.until(lambda x: len(self.chrome.get_all_elements(By.CSS_SELECTOR, "div.single-video-container.not-pinned")) == 2)
+
+ presenterA_video_card = self.get_video_container_by_stream_name("presenterA")
+
+ #check muted icon is not visible
+ assert(not self.chrome.is_nested_element_exist(presenterA_video_card, By.XPATH, ".//div[@aria-label='mic is muted']"))
+
+ #mute presenterA
+ mute_button = self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='mute']", wait_until_clickable=False)
+ mute_button.click()
+
+ #check muted icon will be visible
+ wait.until(lambda x: self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//div[@aria-label='mic is muted']") is not None)
+
+ #mute presenterA
+ unmute_button = self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='unmute']", wait_until_clickable=False)
+ unmute_button.click()
+
+ #check muted icon will be disappeared
+ wait.until(lambda x: not self.chrome.is_nested_element_exist(presenterA_video_card, By.XPATH, ".//div[@aria-label='mic is muted']"))
+
+ #turn off presenterA camera
+ turn_off_button = self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='turn-off-camera']", wait_until_clickable=False)
+ turn_off_button.click()
+
+ #check turn off button returned turn on
+ wait.until(lambda x: self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='turn-on-camera']") is not None)
+
+
+ self.chrome.switch_to_tab(handle_presenter)
+ camera_button = self.chrome.get_element_with_retry(By.ID, "camera-button")
+ camera_button.click()
+
+
+ self.chrome.switch_to_tab(handle_admin)
+
+ #check turn off button returned turn on
+ wait.until(lambda x: self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='turn-off-camera']") is not None)
+
+
self.chrome.close_all()
if __name__ == '__main__':