diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index ebb2b90e..59a002db 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -27,17 +27,6 @@ jobs: run: | mkdir $HOME/.kube echo "${{ secrets.KUBE_CONFIG_DATA }}" > $HOME/.kube/config - - - name: Run Unit Test - run: | - cd react - npm install --include=dev - npm test - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -113,7 +102,7 @@ jobs: cp target/*.war /tmp/circle-webinar.war cd .. - - name: Build React application + - name: Build React application for conferencing env: CI: false NODE_OPTIONS: '--max-old-space-size=4096' @@ -129,7 +118,7 @@ jobs: rm -r webapp/src/main/webapp/static/* cp -a react/build/. webapp/src/main/webapp - - name: Build Maven project + - name: Build Maven project for conferencing run: | cd webapp mvn clean install -DskipTests -Dgpg.skip=true --quiet @@ -137,92 +126,261 @@ jobs: ls -alh target/ cp target/*.war /tmp/circle-conferencing.war cd .. - - - name: Install Test Tool + + - name: Create Test Tool run: | sudo apt-get update sudo apt-get install -y unzip iproute2 libva-drm2 libva-x11-2 libvdpau-dev ffmpeg git clone https://gitlab.com/Ant-Media/webrtc-test.git cd webrtc-test ./redeploy.sh - cp target/webrtc-load-test-tool-*.zip ../ cd .. - unzip webrtc-load-test-tool-*.zip - mkdir ~/test - mv webrtc-load-test ~/test - - - name: Install Selenium - run: | - wget https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.58/linux64/chromedriver-linux64.zip - unzip chromedriver-linux64.zip - sudo cp chromedriver-linux64/chromedriver /tmp - ls -al /tmp - pip3 install selenium==4.14 - pip3 install requests - pip3 show selenium - pip3 install psutil - - - name: Run Integration Test - run: | - cd test - python3 test_main.py ${{ secrets.STAGING_SERVER_URL }} ${{ secrets.USER_NAME }} ${{ secrets.PASSWORD }} /tmp/circle-conferencing.war false - - name: Run Integration Test for webinar - run: | - cd test - python3 test_main.py ${{ secrets.STAGING_SERVER_URL }} ${{ secrets.USER_NAME }} ${{ secrets.PASSWORD }} /tmp/circle-webinar.war true + - name: Cache Test Tool + uses: actions/cache@v3 + with: + path: webrtc-test/ + key: test-tool-${{ hashFiles('**/tool-source/**') }} + restore-keys: test-tool- - - name: Copy Logs From Pods - if: always() - run: | - for pod in $(kubectl -n circle get pods | grep "ant-media-server" | awk '{print $1}'); do - node_ip=$(kubectl -n circle get pods -o wide | grep "$pod" | awk '{print $6}') - kubectl -n circle cp $pod:/usr/local/antmedia/log/ant-media-server.log /tmp/ant-media-server-$node_ip.log - kubectl -n circle cp $pod:/usr/local/antmedia/log/antmedia-error.log /tmp/antmedia-error-$node_ip.log - done - - name: Upload PNG files as artifacts + - name: Upload conferencing war file as artifact if: always() uses: actions/upload-artifact@v4 with: - name: screenshots - path: ${{ github.workspace }}/test/**/*.png + name: circle-conferencing.war + path: /tmp/circle-conferencing.war - - name: Upload log files as artifacts + - name: Upload webinar war file as artifact if: always() uses: actions/upload-artifact@v4 with: - name: logs - path: /tmp/*.log + name: circle-webinar.war + path: /tmp/circle-webinar.war + + - name: Debug WAR file paths + run: ls -alh /tmp - - name: Upload war files as artifacts - if: always() - uses: actions/upload-artifact@v4 + unit-tests: + needs: [build] + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Run Unit Test + run: | + echo "pwd:" + pwd + echo "ls:" + ls -al + cd react + npm install --include=dev + npm test + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + conference-integration-tests: + needs: [build] + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore Cached Test Tool + uses: actions/cache@v3 + with: + path: webrtc-test/ + key: test-tool-${{ hashFiles('**/tool-source/**') }} + restore-keys: test-tool- + + - name: Locate Test Tool + run: | + pwd + ls + cp webrtc-test/target/webrtc-load-test-tool-*.zip ../ + cd .. + unzip webrtc-load-test-tool-*.zip + mkdir ~/test + mv webrtc-load-test ~/test + ls -al ~/test + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml + server-username: MAVEN_USERNAME # env variable for username in deploy + server-password: MAVEN_PASSWORD # env variable for token in deploy + gpg-private-key: ${{ secrets.MVN_GPG_KEY }} # Value of the GPG private key to import + gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase + + - name: Download circle-conferencing.war Artifact + uses: actions/download-artifact@v4 + with: + name: circle-conferencing.war + path: /tmp + + - name: Debug WAR file paths + run: ls -alh /tmp + + - name: Install Selenium + run: | + wget https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.58/linux64/chromedriver-linux64.zip + unzip chromedriver-linux64.zip + sudo cp chromedriver-linux64/chromedriver /tmp + ls -al /tmp + pip3 install selenium==4.14 + pip3 install requests + pip3 show selenium + pip3 install psutil + + - name: Run Integration Test + run: | + cd test + python3 test_main.py ${{ secrets.STAGING_SERVER_URL }} ${{ secrets.USER_NAME }} ${{ secrets.PASSWORD }} /tmp/circle-conferencing.war false + + - name: Upload PNG files as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: ${{ github.workspace }}/test/**/*.png + + webinar-integration-tests: + needs: [build] + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Restore Cached Test Tool + uses: actions/cache@v3 + with: + path: webrtc-test/ + key: test-tool-${{ hashFiles('**/tool-source/**') }} + restore-keys: test-tool- + + - name: Locate Test Tool + run: | + pwd + ls + cp webrtc-test/target/webrtc-load-test-tool-*.zip ../ + cd .. + unzip webrtc-load-test-tool-*.zip + mkdir ~/test + mv webrtc-load-test ~/test + + - name: Download circle-webinar.war Artifact + uses: actions/download-artifact@v4 + with: + name: circle-webinar.war + path: /tmp + + - name: Debug WAR file paths + run: ls -alh /tmp + + - name: Install Selenium + run: | + wget https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.58/linux64/chromedriver-linux64.zip + unzip chromedriver-linux64.zip + sudo cp chromedriver-linux64/chromedriver /tmp + ls -al /tmp + pip3 install selenium==4.14 + pip3 install requests + pip3 show selenium + pip3 install psutil + + - name: Run Integration Test for webinar + run: | + sleep 10 + cd test + python3 test_main.py ${{ secrets.STAGING_SERVER_URL }} ${{ secrets.USER_NAME }} ${{ secrets.PASSWORD }} /tmp/circle-webinar.war true + + + collect-and-upload-logs: + needs: [unit-tests, webinar-integration-tests , conference-integration-tests] + if: always() + runs-on: ubuntu-latest + + steps: + - name: Upload PNG files as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: ${{ github.workspace }}/test/**/*.png + + - name: Copy Logs From Pods + if: always() + run: | + for pod in $(kubectl -n circle get pods | grep "ant-media-server" | awk '{print $1}'); do + node_ip=$(kubectl -n circle get pods -o wide | grep "$pod" | awk '{print $6}') + kubectl -n circle cp $pod:/usr/local/antmedia/log/ant-media-server.log /tmp/ant-media-server-$node_ip.log + kubectl -n circle cp $pod:/usr/local/antmedia/log/antmedia-error.log /tmp/antmedia-error-$node_ip.log + done + + - name: Upload log files as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: logs + path: /tmp/*.log + + + deploy: + runs-on: ubuntu-latest + needs: collect-and-upload-logs + if: github.ref == 'refs/heads/main' + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: - name: wars - path: /tmp/*.war - + java-version: 17 + distribution: 'temurin' + server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml + server-username: MAVEN_USERNAME # env variable for username in deploy + server-password: MAVEN_PASSWORD # env variable for token in deploy + gpg-private-key: ${{ secrets.MVN_GPG_KEY }} # Value of the GPG private key to import + gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase + + - name: Build React application for conferencing + env: + CI: false + 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 + npm install + npm run build + cd .. + rm -r webapp/src/main/webapp/static/* + cp -a react/build/. webapp/src/main/webapp + - name: Publish to Maven Central - if: github.ref == 'refs/heads/main' run: | cd webapp ls -alh ls -alh target/ mvn -e deploy -DskipTests --quiet --settings ../mvn-settings.xml + sleep 60 env: MAVEN_USERNAME: ${{ secrets.MVN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MVN_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MVN_GPG_PASSPHRASE }} - - deploy: - runs-on: ubuntu-latest - needs: build - if: github.ref == 'refs/heads/main' - - steps: - - uses: actions/checkout@v3 - - name: Get versions data from repo run: wget -O maven-metadata.xml https://oss.sonatype.org/service/local/repositories/snapshots/content/io/antmedia/ant-media-server/maven-metadata.xml - name: Install jq diff --git a/react/.env.production b/react/.env.production index 1c31d149..983d5469 100644 --- a/react/.env.production +++ b/react/.env.production @@ -55,6 +55,7 @@ REACT_APP_TIME_ZONE_LIVE_TEXT_VISIBILITY=true # Video Overlay configurations REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED=false +REACT_APP_VIDEO_OVERLAY_ADMIN_CAMERA_CONTROL_ENABLED=false # Participant Tab configurations REACT_APP_PARTICIPANT_TAB_ADMIN_MODE_ENABLED=true diff --git a/react/.env.production.webinar b/react/.env.production.webinar index 4bf483ea..41eaf94e 100644 --- a/react/.env.production.webinar +++ b/react/.env.production.webinar @@ -55,6 +55,7 @@ REACT_APP_TIME_ZONE_LIVE_TEXT_VISIBILITY=true # Video Overlay configurations REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED=true +REACT_APP_VIDEO_OVERLAY_ADMIN_CAMERA_CONTROL_ENABLED=false # Speed Test configurations REACT_APP_SPEED_TEST_BEFORE_JOINING_THE_ROOM=true diff --git a/react/src/Components/Cards/VideoCard.js b/react/src/Components/Cards/VideoCard.js index bcd174d8..074ff7f8 100644 --- a/react/src/Components/Cards/VideoCard.js +++ b/react/src/Components/Cards/VideoCard.js @@ -113,7 +113,7 @@ function VideoCard(props) { return ( <> - {!useAvatar && ( + {(!useAvatar && process.env.REACT_APP_VIDEO_OVERLAY_ADMIN_MODE_ENABLED === "true") && ( diff --git a/react/src/Components/ParticipantTab.js b/react/src/Components/ParticipantTab.js index 71d73269..4ac7fcc9 100644 --- a/react/src/Components/ParticipantTab.js +++ b/react/src/Components/ParticipantTab.js @@ -63,7 +63,7 @@ function ParticipantTab(props) { onClick={() => { handleToggleMic(micMuted, streamId, name) } } > - + ) } diff --git a/react/src/__tests__/pages/AntMedia.test.js b/react/src/__tests__/pages/AntMedia.test.js index 42382280..03d61009 100644 --- a/react/src/__tests__/pages/AntMedia.test.js +++ b/react/src/__tests__/pages/AntMedia.test.js @@ -87,6 +87,7 @@ jest.mock('@antmedia/webrtc_adaptor', () => ({ enableEffect: jest.fn(), setSelectedVideoEffect: jest.fn(), setBlurEffectRange: jest.fn(), + getSubtrackCount: jest.fn(), } for (var key in params) { diff --git a/react/src/pages/AntMedia.js b/react/src/pages/AntMedia.js index 68caf7f7..5dd9d4ef 100644 --- a/react/src/pages/AntMedia.js +++ b/react/src/pages/AntMedia.js @@ -1575,6 +1575,7 @@ function AntMedia(props) { setIsPlayed(true); setIsNoSreamExist(false); webRTCAdaptor?.getBroadcastObject(roomName); + webRTCAdaptor?.getSubtrackCount(roomName, null, null); webRTCAdaptor?.getSubtracks(roomName, null, globals.participantListPagination.offset, globals.participantListPagination.pageSize); requestVideoTrackAssignmentsInterval(); @@ -2276,6 +2277,8 @@ function AntMedia(props) { let tempVideoTrackAssignmentsNew = []; + let tempAllParticipants = {...allParticipants}; + // This function checks the case 1 and case 2 currentVideoTrackAssignments.forEach(tempVideoTrackAssignment => { let assignment; @@ -2295,12 +2298,13 @@ function AntMedia(props) { } else { console.log("---> Removed video track assignment: " + tempVideoTrackAssignment.videoLabel); + delete tempAllParticipants[tempVideoTrackAssignment.streamId]; } }); - currentVideoTrackAssignments = [...tempVideoTrackAssignmentsNew]; + setAllParticipants(tempAllParticipants); - //let updateAllParticipants = {...allParticipants}; + currentVideoTrackAssignments = [...tempVideoTrackAssignmentsNew]; // update participants according to current assignments receivedVideoTrackAssignments.forEach(vta => { @@ -2310,23 +2314,9 @@ function AntMedia(props) { existingAssignment.isReserved = vta.reserved; } if (!allParticipants[vta.trackId]) { - /* - updateAllParticipants[vta.trackId] = { - streamId: vta.trackId, - name: vta.trackId, - isScreenShared: false, - isPinned: false, - isFake: false, - isMine: false, - status: "livestream" - metaData: "{\"isMicMuted\":true,\"isCameraOn\":false,\"isScreenShared\":false,\"playOnly\":false}" - }; - */ webRTCAdaptor?.getBroadcastObject(vta.trackId); } }); - - //setAllParticipants(updateAllParticipants); checkScreenSharingStatus(); diff --git a/react/src/styles/sprite.svg b/react/src/styles/sprite.svg index a0f984ea..ab5b7cb4 100644 --- a/react/src/styles/sprite.svg +++ b/react/src/styles/sprite.svg @@ -23,7 +23,7 @@ - + @@ -112,7 +112,7 @@ - + diff --git a/test/test_join_leave.py b/test/test_join_leave.py index b31241d5..61a035e4 100644 --- a/test/test_join_leave.py +++ b/test/test_join_leave.py @@ -48,8 +48,8 @@ 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 diff --git a/test/test_main.py b/test/test_main.py index 29be86d4..96a295eb 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -1,6 +1,8 @@ import test_join_leave import test_webinar from test_deployment import TestDeployment +from test_join_leave import TestJoinLeave + import unittest import sys @@ -12,21 +14,26 @@ os.environ['AMS_PASSWORD'] = sys.argv[3] os.environ['WAR_FILE'] = sys.argv[4] use_test_webinar = sys.argv[5].lower() == 'true' -os.environ['TEST_APP_NAME'] = "TestAPP"+str(random.randint(100, 999)) +app_name_prefix = "webinar" if use_test_webinar else "conference" +os.environ['TEST_APP_NAME'] = app_name_prefix + str(random.randint(100, 999)) + +#Keep it True to stop tests immediately after a failed test +fail_fast = True suite = unittest.TestSuite() suite.addTest(TestDeployment('test_install_app')) suite2 = None - if use_test_webinar: suite2 = unittest.TestLoader().loadTestsFromModule(test_webinar) else: suite2 = unittest.TestLoader().loadTestsFromModule(test_join_leave) suite.addTests(suite2) -suite.addTest(TestDeployment('test_delete_app')) -ret = not unittest.TextTestRunner(verbosity=2, failfast=True).run(suite).wasSuccessful() -sys.exit(ret) +#suite.addTest(TestJoinLeave("test_join_without_camera_mic")) + +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 diff --git a/test/test_webinar.py b/test/test_webinar.py index 7e350ab5..def55405 100644 --- a/test/test_webinar.py +++ b/test/test_webinar.py @@ -143,6 +143,8 @@ def join_room_as_player(self, participant, room): app = "" handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?playOnly=true&role=listener&streamName="+participant) + wait = self.chrome.get_wait() + #name_text_box = self.chrome.get_element_with_retry(By.ID,"participant_name") #self.chrome.write_to_element(name_text_box, participant) @@ -155,7 +157,7 @@ def join_room_as_player(self, participant, room): time.sleep(5) speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20) - assert(speedTestCircularProgress.is_displayed()) + wait.until(lambda x: speedTestCircularProgress.is_displayed()) time.sleep(5)