Compare commits

..

No commits in common. "master" and "v3.0.0-beta2" have entirely different histories.

359 changed files with 28107 additions and 65306 deletions

113
.github/workflows/openvidu-ce-test.yml vendored Normal file
View File

@ -0,0 +1,113 @@
name: OpenVidu CE Tests
on:
push:
branches:
- master
paths-ignore:
- ".github/workflows/openvidu-components-angular-E2E.yml"
- "openvidu-components-angular/**"
- "openvidu-server/docker/**"
- "openvidu-server/deployments/**"
pull_request:
branches:
- master
workflow_dispatch:
inputs:
TEST_IMAGE:
description: "Docker image where to run the tests"
required: true
default: "openvidu/openvidu-test-e2e:22.04"
KURENTO_JAVA_COMMIT:
description: 'Commit to use in kurento-java dependencies. If "default" the release version declared in property "version.kurento" of openvidu-parent/pom.xml will be used'
required: true
default: "default"
KURENTO_MEDIA_SERVER_IMAGE:
description: "Docker image of kurento-media-server"
required: true
default: "kurento/kurento-media-server:7.0.1"
DOCKER_RECORDING_VERSION:
description: "Force version of openvidu/openvidu-recording container"
required: true
default: "default"
CHROME_VERSION:
description: "Version of Chrome to use. Must be a valid image tag from https://hub.docker.com/r/selenium/standalone-chrome/tags"
required: true
default: "latest"
FIREFOX_VERSION:
description: "Version of Firefox to use. Must be a valid image tag from https://hub.docker.com/r/selenium/standalone-firefox/tags"
required: true
default: "latest"
EDGE_VERSION:
description: "Version of Edge to use. Must be a valid image tag from https://hub.docker.com/r/selenium/standalone-edge/tags"
required: true
default: "latest"
jobs:
main:
runs-on: ubuntu-latest
container:
image: ${{ inputs.TEST_IMAGE || 'openvidu/openvidu-test-e2e:22.04' }}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/openvidu:/opt/openvidu
env:
TEST_IMAGE: ${{ inputs.TEST_IMAGE || 'openvidu/openvidu-test-e2e:22.04' }}
KURENTO_SNAPSHOTS_URL: ${{ secrets.KURENTO_SNAPSHOTS_URL }}
KURENTO_MEDIA_SERVER_IMAGE: ${{ inputs.KURENTO_MEDIA_SERVER_IMAGE || 'kurento/kurento-media-server:7.0.1' }}
KURENTO_JAVA_COMMIT: ${{ inputs.KURENTO_JAVA_COMMIT || 'default' }}
DOCKER_RECORDING_VERSION: ${{ inputs.DOCKER_RECORDING_VERSION || 'default' }}
CHROME_VERSION: ${{ inputs.CHROME_VERSION || 'latest' }}
FIREFOX_VERSION: ${{ inputs.FIREFOX_VERSION || 'latest' }}
EDGE_VERSION: ${{ inputs.EDGE_VERSION || 'latest' }}
steps:
- uses: actions/checkout@v3
- name: Setup scripts
run: |
curl -sOJ --output-dir /opt https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/commons/build.sh
curl -sOJ --output-dir /opt https://raw.githubusercontent.com/OpenVidu/openvidu/master/ci-scripts/commons/test-utils.sh
cp ci-scripts/openvidu-e2e-tests.sh /opt/openvidu-e2e-tests.sh
find /opt/*.sh -type f -print0 | xargs -0 chmod u+x
- name: Clean environment
run: /opt/build.sh --clean-environment
- name: Prepare test environment
run: /opt/test-utils.sh --prepare-test-environment "${TEST_IMAGE}"
- name: Check and prepare kurento snapshots
run: /opt/build.sh --check-and-prepare-kurento-snapshot
- name: Use specific kurento-java commit
if: ${{ env.KURENTO_JAVA_COMMIT != 'default'}}
run: /opt/test-utils.sh --use-specific-kurento-java-commit
- name: Build openvidu-browser
run: /opt/build.sh --build-openvidu-browser
- name: Build openvidu-node-client
run: /opt/build.sh --build-openvidu-node-client
- name: Build openvidu-java-client
run: /opt/build.sh --build-openvidu-java-client
- name: Build openvidu-parent
run: /opt/build.sh --build-openvidu-parent
- name: Build openvidu-testapp
run: /opt/build.sh --build-openvidu-testapp
- name: Build openvidu-server dashboard
run: /opt/build.sh --build-openvidu-server-dashboard true
- name: Build openvidu-server
run: /opt/build.sh --build-openvidu-server
- name: openvidu-server unit tests
run: /opt/openvidu-e2e-tests.sh --openvidu-server-unit-tests
- name: openvidu-server integration tests
run: /opt/openvidu-e2e-tests.sh --openvidu-server-integration-tests
- name: Environment launch Kurento
run: /opt/openvidu-e2e-tests.sh --environment-launch-kurento
- name: Serve openvidu-testapp
run: /opt/test-utils.sh --serve-openvidu-testapp
- name: OpenVidu E2E Tests Kurento
run: /opt/openvidu-e2e-tests.sh --openvidu-e2e-tests-kurento
- name: Test reports
uses: mikepenz/action-junit-report@v3
if: always() # always run even if the previous step fails
with:
report_paths: "**/target/surefire-reports/TEST-*.xml"
- name: Upload logs
uses: actions/upload-artifact@v3
if: always() # always run even if the previous step fails
with:
name: Logs
path: |
/opt/openvidu/*.log

View File

@ -0,0 +1,158 @@
name: openvidu-components-angular E2E
on:
push:
paths:
- 'openvidu-components-angular/**'
- 'openvidu-browser/**'
- 'openvidu-node-client/**'
- '.github/workflows/openvidu-components-angular-E2E.yml'
pull_request:
branches:
- master
workflow_dispatch:
inputs:
commit_sha:
description: 'Commit SHA'
required: false
default: ''
jobs:
test_setup:
name: Test setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Commit URL
run: echo https://github.com/OpenVidu/openvidu/commit/${{ inputs.commit_sha || github.sha }}
- name: Send repository dispatch event
env:
GITHUB_TOKEN: ${{ secrets.OPENVIDU_DISPATCH_EVENT_GA }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message || 'Manually' }}
COMMIT_URL: ${{ github.event.commits[0].url || 'Manually' }}
BRANCH_NAME: ${{ github.ref_name }}
run: |
curl \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
https://api.github.com/repos/OpenVidu/openvidu-call/dispatches \
-d '{"event_type":"openvidu-components-angular","client_payload":{"commit-message":"'"$COMMIT_MESSAGE"'","commit-ref":"'"$COMMIT_URL"'", "branch-name":"'"$BRANCH_NAME"'"}}'
- name: Build openvidu-browser
run: |
cd openvidu-browser
npm install
npm run build && \
npm pack
- uses: actions/upload-artifact@v4
with:
name: openvidu-browser
path: openvidu-browser/openvidu-browser-*.tgz
openvidu_angular_e2e:
needs: test_setup
name: OpenVidu Angular E2E tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/download-artifact@v4
with:
name: openvidu-browser
path: openvidu-components-angular
- name: Run Browserless Chrome
run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run openvidu-server-kms
run: |
docker run -p 4443:4443 --rm -d \
-e OPENVIDU_SECRET=MY_SECRET \
openvidu/openvidu-dev:latest
- name: Install openvidu-browser and dependencies
run: |
cd openvidu-components-angular
npm install openvidu-browser-*.tgz
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-angular-testapp
run: npm run build --prefix openvidu-components-angular
- name: Serve openvidu-angular-testapp
run: npm run start-prod --prefix openvidu-components-angular &
- name: Run openvidu-angular E2E
run: npm run lib:e2e-ci --prefix openvidu-components-angular
webcomponent_e2e:
needs: test_setup
name: Webcomponent E2E CE tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/download-artifact@v4
with:
name: openvidu-browser
path: openvidu-components-angular
- name: Run Browserless Chrome
run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run openvidu-server-kms
run: |
docker run -p 4443:4443 --rm -d \
-e OPENVIDU_SECRET=MY_SECRET \
openvidu/openvidu-dev:latest
- name: Install openvidu-browser and dependencies
run: |
cd openvidu-components-angular
npm install openvidu-browser-*.tgz
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-webcomponent
run: npm run webcomponent:build --prefix openvidu-components-angular
- name: Serve Webcomponent Testapp
run: npm run webcomponent:serve-testapp --prefix openvidu-components-angular &
- name: Run Webcomponent E2E
run: npm run webcomponent:e2e-ci --prefix openvidu-components-angular
webcomponent_e2e_pro:
if: false #Skip PRO test because infra is unstable
needs: test_setup
name: Webcomponent E2E PRO tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- uses: actions/download-artifact@v4
with:
name: openvidu-browser
path: openvidu-components-angular
- name: Run Browserless Chrome
run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Install openvidu-browser and dependencies
run: |
cd openvidu-components-angular
npm install openvidu-browser-*.tgz
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-webcomponent
run: npm run webcomponent:build --prefix openvidu-components-angular
- name: Serve Webcomponent Testapp
run: npm run webcomponent:serve-testapp --prefix openvidu-components-angular &
- name: Run Webcomponent E2E PRO
env:
OPENVIDU_SERVER_URL: ${{ secrets.OPENVIDU_CALL_NEXT_URL }}
OPENVIDU_SECRET: ${{ secrets.OPENVIDU_CALL_NEXT_SECRET }}
run: npm run webcomponent:e2e-pro-ci --prefix openvidu-components-angular

View File

@ -1,445 +0,0 @@
name: openvidu-components-angular Tests
on:
push:
branches:
- master
paths:
- 'openvidu-components-angular/**'
- '.github/workflows/openvidu-components-angular-tests.yml'
pull_request:
branches:
- master
workflow_dispatch:
inputs:
commit_sha:
description: 'Commit SHA'
required: false
default: ''
jobs:
test_setup:
name: Test setup
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Commit URL
run: echo https://github.com/OpenVidu/openvidu/commit/${{ inputs.commit_sha || github.sha }}
- name: Send Dispatch Event
env:
GITHUB_TOKEN: ${{ secrets.OPENVIDU_DISPATCH_EVENT_GA }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message || 'Manually' }}
COMMIT_URL: ${{ github.event.commits[0].url || 'Manually' }}
BRANCH_NAME: ${{ github.ref_name }}
run: |
curl -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
https://api.github.com/repos/OpenVidu/openvidu-call/dispatches \
-d '{"event_type":"openvidu-components-angular","client_payload":{"commit-message":"'"$COMMIT_MESSAGE"'","commit-ref":"'"$COMMIT_URL"'", "branch-name":"'"$BRANCH_NAME"'"}}'
nested_events:
needs: test_setup
name: Nested events
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Install dependencies
run: |
cd openvidu-components-angular
npm install
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run nested components E2E event tests
env:
LAUNCH_MODE: CI
run: npm run e2e:nested-events --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
nested_structural_directives:
needs: test_setup
name: Nested Structural Directives
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run nested structural directives tests
env:
LAUNCH_MODE: CI
run: npm run e2e:nested-structural-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
nested_attribute_directives:
needs: test_setup
name: Nested Attribute Directives
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run nested attribute directives tests
env:
LAUNCH_MODE: CI
run: npm run e2e:nested-attribute-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_directives:
needs: test_setup
name: API Directives Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_internal_directives:
needs: test_setup
name: Internal Directives Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-internal-directives --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_chat:
needs: test_setup
name: Chat E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-chat --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_events:
needs: test_setup
name: Events E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-events --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_media_devices:
needs: test_setup
name: Media devices E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-media-devices --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_panels:
needs: test_setup
name: Panels E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-panels --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_screen_sharing:
needs: test_setup
name: Screen sharing E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-screensharing --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_stream:
needs: test_setup
name: Stream E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -v $(pwd)/openvidu-components-angular/e2e/assets:/e2e-assets selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Tests
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-stream --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main
e2e_toolbar:
needs: test_setup
name: Toolbar E2E
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ inputs.commit_sha || github.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install wait-on package
run: npm install -g wait-on
# - name: Run Browserless Chrome
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
- name: Run Chrome
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
- name: Run openvidu-local-deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
- name: Start OpenVidu Call backend
uses: OpenVidu/actions/start-openvidu-call@main
- name: Build and Serve openvidu-components-angular Testapp
uses: OpenVidu/actions/start-openvidu-components-testapp@main
- name: Run Webcomponent E2E
env:
LAUNCH_MODE: CI
run: npm run e2e:lib-toolbar --prefix openvidu-components-angular
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main

View File

@ -1,56 +0,0 @@
name: OpenVidu integration tests
on:
push:
branches:
- master
paths:
- "openvidu-test-integration/**"
- ".github/workflows/openvidu-integration-tests.yml"
workflow_dispatch:
jobs:
integration-tests:
name: Integration tests
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- name: Configure OpenVidu Local Deployment
uses: OpenVidu/actions/start-openvidu-local-deployment@main
with:
ref-openvidu-local-deployment: development
pre_startup_commands: |
sed -i 's/interval: 10s/interval: 1s/' livekit.yaml
sed -i '/interval: 1s/a \ fixer_interval: 10s' livekit.yaml
- name: Install LiveKit CLI
run: |
curl -sSL https://get.livekit.io/cli | bash
- name: Checkout current repository
uses: actions/checkout@v4
with:
path: openvidu
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
working-directory: ./openvidu/openvidu-test-integration
run: npm ci
- name: Run tests
working-directory: ./openvidu/openvidu-test-integration
run: npm run test:ci
- name: Upload report
uses: actions/upload-artifact@v4
if: always()
with:
name: openvidu-integration-tests-report
path: ./openvidu/openvidu-test-integration/test-results.json
retention-days: 7
- name: Cleanup
if: always()
uses: OpenVidu/actions/cleanup@main

View File

@ -39,7 +39,7 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
OpenVidu has been supported under project "CPP2021-008720 NewGenVidu: An elastic, user-friendly and privacy-friendly videoconferencing platform", funded by MCIN/AEI/10.13039/501100011033 and by the European Union-NextGenerationEU/PRTR. OpenVidu has been supported under project "CPP2021-008720 NewGenVidu: An elastic, user-friendly and privacy-friendly videoconferencing platform", funded by MCIN/AEI/10.13039/501100011033 and by the European Union-NextGenerationEU/PRTR.
<img height="75px" src="https://docs.openvidu.io/en/stable/img/logos/support.jpg"> <img height="75px" src="https://openvidu.io/img/logos/support.jpg">
## Sponsors ## Sponsors

View File

@ -10,4 +10,9 @@
node_modules node_modules
dist/ dist/
docs/ docs/
openvidu-webcomponent/
coverage/** coverage/**
e2e/webcomponent-app/openvidu-webcomponent-*.css
e2e/webcomponent-app/openvidu-webcomponent-*.js
e2e/assets/*

View File

@ -18,8 +18,7 @@
"builder": "@angular-devkit/build-angular:application", "builder": "@angular-devkit/build-angular:application",
"options": { "options": {
"outputPath": { "outputPath": {
"base": "dist/openvidu-components-testapp", "base": "dist/openvidu-components-testapp"
"browser": ""
}, },
"index": "src/index.html", "index": "src/index.html",
"polyfills": ["zone.js"], "polyfills": ["zone.js"],
@ -149,6 +148,81 @@
} }
} }
} }
},
"openvidu-webcomponent": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/openvidu-webcomponent-rc",
"index": "src/index.html",
"main": "src/app/openvidu-webcomponent/openvidu-webcomponent.main.ts",
"polyfills": ["zone.js"],
"tsConfig": "src/app/openvidu-webcomponent/tsconfig.openvidu-webcomponent.json",
"aot": true,
"assets": ["src/favicon.ico"],
"styles": ["src/app/openvidu-webcomponent/openvidu-webcomponent.component.scss"],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "none",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "1mb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
},
"testing": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.testing.ts"
}
],
"optimization": true,
"outputHashing": "none",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "1mb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
}
}
}
}
} }
}, },
"cli": { "cli": {

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
export const LAUNCH_MODE = process.env.LAUNCH_MODE || 'DEV'; export const LAUNCH_MODE = process.env.LAUNCH_MODE || 'DEV';
export const OPENVIDU_CALL_SERVER = process.env.OPENVIDU_CALL_SERVER || 'http://localhost:5000';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;

View File

@ -1,84 +0,0 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { OpenViduComponentsPO } from './utils.po.test';
import { TestAppConfig } from './selenium.conf';
let url = '';
describe('Testing Internal Directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(TestAppConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
url = `${TestAppConfig.appUrl}&roomName=INTERNAL_DIRECTIVES_${Math.floor(Math.random() * 1000)}`;
});
afterEach(async () => {
try {
} catch (error) {}
await browser.sleep(500);
await browser.quit();
});
it('should show/hide toolbar view recording button with toolbarViewRecordingsButton directive', async () => {
await browser.get(`${url}&prejoin=false&toolbarViewRecordingsButton=true`);
await utils.checkSessionIsPresent();
await utils.toggleToolbarMoreOptions();
expect(await utils.isPresent('#view-recordings-btn')).toBeTrue();
await browser.get(`${url}&prejoin=false`);
await browser.navigate().refresh();
await utils.checkSessionIsPresent();
await utils.toggleToolbarMoreOptions();
expect(await utils.isPresent('#view-recordings-btn')).toBeFalse();
});
it('should show/hide participant name in prejoin with prejoinDisplayParticipantName directive', async () => {
await browser.get(`${url}&prejoin=true`);
await utils.checkPrejoinIsPresent();
expect(await utils.isPresent('.participant-name-container')).toBeTrue();
await browser.get(`${url}&prejoin=true&prejoinDisplayParticipantName=false`);
await browser.navigate().refresh();
await utils.checkPrejoinIsPresent();
expect(await utils.isPresent('.participant-name-container')).toBeFalse();
});
it('should show/hide view recordings button with recordingActivityViewRecordingsButton directive', async () => {
await browser.get(`${url}&prejoin=false&recordingActivityViewRecordingsButton=true`);
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#view-recordings-btn')).toBeTrue();
await browser.get(`${url}&prejoin=false`);
await browser.navigate().refresh();
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#view-recordings-btn')).toBeFalse();
});
it('should show/hide start/stop recording buttons with recordingActivityStartStopRecordingButton directive', async () => {
await browser.get(`${url}&prejoin=false&recordingActivityStartStopRecordingButton=false`);
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#start-recording-btn')).toBeFalse();
await browser.sleep(3000);
await browser.get(`${url}&prejoin=false`);
await browser.navigate().refresh();
await utils.checkSessionIsPresent();
await utils.togglePanel('activities');
await utils.clickOn('#recording-activity');
expect(await utils.isPresent('#start-recording-btn')).toBeTrue();
});
});

View File

@ -1,380 +0,0 @@
import { Builder, By, WebDriver } from 'selenium-webdriver';
import { NestedConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = NestedConfig.appUrl;
describe('OpenVidu Components ATTRIBUTE toolbar directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the CHAT PANEL BUTTON', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#chatPanelButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Check if chat button does not exist
expect(await utils.isPresent('chat-panel-btn')).toBeFalse();
});
it('should HIDE the PARTICIPANTS PANEL BUTTON', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#participantsPanelButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Check if participants button does not exist
expect(await utils.isPresent('participants-panel-btn')).toBeFalse();
});
it('should HIDE the ACTIVITIES PANEL BUTTON', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#activitiesPanelButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Check if participants button does not exist
expect(await utils.isPresent('activities-panel-btn')).toBeFalse();
});
it('should HIDE the DISPLAY LOGO', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#displayLogo-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('branding-logo')).toBeFalse();
});
it('should HIDE the DISPLAY ROOM name', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#displayRoomName-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('session-name')).toBeFalse();
});
it('should HIDE the FULLSCREEN button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#fullscreenButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
await utils.waitForElement('#more-options-menu');
// Checking if fullscreen button is not present
expect(await utils.isPresent('#fullscreen-btn')).toBeFalse();
});
it('should HIDE the STREAMING button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#broadcastingButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
await utils.waitForElement('#more-options-menu');
// Checking if fullscreen button is not present
expect(await utils.isPresent('#broadcasting-btn')).toBeFalse();
});
it('should HIDE the LEAVE button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#leaveButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('leave-btn')).toBeFalse();
});
it('should HIDE the SCREENSHARE button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#screenshareButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
expect(await utils.isPresent('screenshare-btn')).toBeFalse();
});
});
describe('OpenVidu Components ATTRIBUTE stream directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the AUDIO detector', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#displayAudioDetection-checkbox');
await utils.clickOn('#apply-btn');
await utils.waitForElement('#session-container');
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('audio-wave-container')).toBeFalse();
});
it('should HIDE the PARTICIPANT NAME', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#displayParticipantName-checkbox');
await utils.clickOn('#apply-btn');
await utils.waitForElement('#session-container');
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('participant-name-container')).toBeFalse();
});
it('should HIDE the SETTINGS button', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#settingsButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('settings-container')).toBeFalse();
});
});
describe('OpenVidu Components ATTRIBUTE participant panels directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the participant MUTE button', async () => {
const fixedSession = `${url}?sessionId=fixedNameTesting`;
await browser.get(`${fixedSession}`);
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#muteButton-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.clickOn('#participants-panel-btn');
await utils.waitForElement('#participants-container');
// Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedSession}")`;
await browser.executeScript(newTabScript);
// Get tabs opened
const tabs = await browser.getAllWindowHandles();
// Focus on the last tab
browser.switchTo().window(tabs[1]);
await utils.clickOn('#apply-btn');
// Switch to first tab
await browser.switchTo().window(tabs[0]);
await utils.waitForElement('#remote-participant-item');
expect(await utils.isPresent('mute-btn')).toBeFalse();
});
});
describe('OpenVidu Components ATTRIBUTE activity panel directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should HIDE the RECORDING activity', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#recordingActivity-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#activities-panel-btn');
await browser.sleep(500);
await utils.waitForElement('#custom-activities-panel');
expect(await utils.isPresent('ov-recording-activity')).toBeFalse();
});
it('should HIDE the STREAMING activity', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#broadcastingActivity-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#activities-panel-btn');
await browser.sleep(500);
await utils.waitForElement('#custom-activities-panel');
await utils.waitForElement('ov-recording-activity');
expect(await utils.isPresent('ov-broadcasting-activity')).toBeFalse();
});
});

View File

@ -1,186 +0,0 @@
import { Builder, By, WebDriver } from 'selenium-webdriver';
import { NestedConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = NestedConfig.appUrl;
describe('OpenVidu Components EVENTS', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should receive the onParticipantLeft event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
// Clicking to leave button
await utils.clickOn('#leave-btn');
// Checking if onLeaveButtonClicked has been received
await utils.waitForElement('#onParticipantLeft');
expect(await utils.isPresent('#onParticipantLeft')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#camera-btn');
await utils.waitForElement('#onVideoEnabledChanged');
expect(await utils.isPresent('#onVideoEnabledChanged')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#mic-btn');
await utils.waitForElement('#onAudioEnabledChanged');
expect(await utils.isPresent('#onAudioEnabledChanged')).toBeTrue();
});
it('should receive the onScreenShareEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.clickOn('#screenshare-btn');
await utils.waitForElement('#onScreenShareEnabledChanged');
expect(await utils.isPresent('#onScreenShareEnabledChanged')).toBeTrue();
});
it('should receive the onFullscreenEnabledChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.toggleFullscreenFromToolbar();
await browser.sleep(1000);
await utils.waitForElement('#onFullscreenEnabledChanged');
expect(await utils.isPresent('#onFullscreenEnabledChanged')).toBeTrue();
});
it('should receive the onRecordingStartRequested event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.toggleRecordingFromToolbar();
await utils.waitForElement('#onRecordingStartRequested');
expect(await utils.isPresent('#onRecordingStartRequested')).toBeTrue();
});
it('should receive the onParticipantsPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('participants');
await utils.waitForElement('#onParticipantsPanelStatusChanged');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged')).toBeTrue();
});
it('should receive the onChatPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('chat');
await utils.waitForElement('#onChatPanelStatusChanged');
expect(await utils.isPresent('#onChatPanelStatusChanged')).toBeTrue();
});
it('should receive the onActivitiesPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
await utils.waitForElement('#onActivitiesPanelStatusChanged');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged')).toBeTrue();
});
it('should receive the onSettingsPanelStatusChanged event', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await utils.waitForElement('#onSettingsPanelStatusChanged');
expect(await utils.isPresent('#onSettingsPanelStatusChanged')).toBeTrue();
});
});

View File

@ -1,806 +0,0 @@
import { Builder, By, WebDriver } from 'selenium-webdriver';
import { NestedConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = NestedConfig.appUrl;
describe('E2E: Toolbar structural directive scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should render only the custom toolbar (no additional buttons, no default toolbar)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom toolbar is present in DOM
await utils.waitForElement('#custom-toolbar');
expect(await utils.isPresent('#custom-toolbar')).toBeTrue();
// Check if additional buttons element has not been rendered
expect(await utils.isPresent('#custom-toolbar-additional-buttons')).toBeFalse();
// Check if default toolbar is not present
expect(await utils.isPresent('#default-toolbar')).toBeFalse();
});
it('should render the custom toolbar with additional custom buttons and hide the default toolbar', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#ovToolbarAdditionalButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom toolbar is present in DOM
await utils.waitForElement('#custom-toolbar');
expect(await utils.isPresent('#custom-toolbar')).toBeTrue();
// Check if additional buttons element has been rendered;
await utils.waitForElement('#custom-toolbar-additional-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-buttons')).toBeTrue();
const element = await browser.findElements(By.id('toolbar-additional-btn'));
expect(element.length).toEqual(2);
// Check if default toolbar is not present
expect(await utils.isPresent('#default-toolbar')).toBeFalse();
});
it('should render the custom toolbar with additional custom panel buttons and hide the default toolbar', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovToolbar-checkbox');
await utils.clickOn('#ovToolbarAdditionalPanelButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom toolbar is present in DOM
await utils.waitForElement('#custom-toolbar');
expect(await utils.isPresent('#custom-toolbar')).toBeTrue();
// Check if additional buttons element has been rendered;
await utils.waitForElement('#custom-toolbar-additional-panel-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-panel-buttons')).toBeTrue();
const element = await browser.findElements(By.id('toolbar-additional-panel-btn'));
expect(element.length).toEqual(1);
// Check if default toolbar is not present
expect(await utils.isPresent('#default-toolbar')).toBeFalse();
});
it('should render only additional toolbar buttons (default toolbar visible, no custom toolbar)', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovToolbarAdditionalButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if default toolbar is present
await utils.waitForElement('#default-toolbar');
expect(await utils.isPresent('#default-toolbar')).toBeTrue();
// Check if additional buttons are present
await utils.waitForElement('#custom-toolbar-additional-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-buttons')).toBeTrue();
element = await browser.findElements(By.id('toolbar-additional-btn'));
expect(element.length).toEqual(3);
// Check if custom toolbar not is present
expect(await utils.isPresent('#custom-toolbar')).toBeFalse();
});
it('should render only additional toolbar panel buttons (default toolbar visible, no custom toolbar)', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovToolbarAdditionalPanelButtons-checkbox');
await utils.clickOn('#apply-btn');
// Check if default toolbar is present
await utils.waitForElement('#default-toolbar');
expect(await utils.isPresent('#default-toolbar')).toBeTrue();
// Check if additional buttons are present
await utils.waitForElement('#custom-toolbar-additional-panel-buttons');
expect(await utils.isPresent('#custom-toolbar-additional-panel-buttons')).toBeTrue();
element = await browser.findElements(By.id('toolbar-additional-panel-btn'));
expect(element.length).toEqual(1);
// Check if custom toolbar not is present
expect(await utils.isPresent('#custom-toolbar')).toBeFalse();
});
});
describe('E2E: Panel structural directive scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should render an additional custom panel with default panels', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovAdditionalPanels-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Open additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
await browser.sleep(500);
// Check if custom panel is present
expect(await utils.isPresent('#custom-additional-panel')).toBeTrue();
element = await utils.waitForElement('#additional-panel-title');
expect(await element.getAttribute('innerText')).toEqual('NEW PANEL');
// Open the participants panel
await utils.clickOn('#participants-panel-btn');
await browser.sleep(500);
// Check if default panel is present
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Open additional panel again
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeTrue();
// Close the additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeFalse();
});
it('should render only the custom panel container (no children, no default panels)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
// Click on button for opening panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render the custom panel container with an additional panel only', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovAdditionalPanels-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
// Check if custom panel is present
element = await browser.findElements(By.id('custom-additional-panel'));
expect(element.length).toEqual(1);
element = await utils.waitForElement('#additional-panel-title');
expect(await utils.isPresent('#additional-panel-title')).toBeTrue();
expect(await element.getAttribute('innerText')).toEqual('NEW PANEL');
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeFalse();
});
it('should render the custom panel container with a custom chat panel only', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovChatPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is not present
await utils.waitForElement('#custom-chat-panel');
expect(await utils.isPresent('#custom-chat-panel')).toBeTrue();
});
it('should render the custom panel container with a custom activities panel only', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening chat panel
await utils.clickOn('#activities-panel-btn');
// Check if default activities panel is not present
expect(await utils.isPresent('#default-activities-panel')).toBeFalse();
// Check if custom chat panel is not present
element = await utils.waitForElement('#custom-activities-panel');
expect(await utils.isPresent('#custom-activities-panel')).toBeTrue();
element = await utils.waitForElement('#activities-panel-title');
expect(await element.getAttribute('innerText')).toEqual('CUSTOM ACTIVITIES PANEL');
});
it('should render the custom panel container with a custom participants panel only (no children)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render the custom panel container with a custom participants panel and a custom participant item', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Check if custom participant panel item is present
await utils.waitForElement('#custom-participants-panel-item');
expect(await utils.isPresent('#custom-participants-panel-item')).toBeTrue();
// Check if default participant panel item is not present
expect(await utils.isPresent('#default-participant-panel-item')).toBeFalse();
});
it('should render the custom panel container with a custom participants panel, custom participant item, and custom item element', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#ovParticipantPanelItemElements-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Check if custom participant panel item is present
await utils.waitForElement('#custom-participants-panel-item');
expect(await utils.isPresent('#custom-participants-panel-item')).toBeTrue();
// Check if custom participant panel item element is present
await utils.waitForElement('#custom-participants-panel-item-element');
expect(await utils.isPresent('#custom-participants-panel-item-element')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if default participant panel item is not present
expect(await utils.isPresent('#default-participant-panel-item')).toBeFalse();
});
it('should render only a custom activities panel (no default panel)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovActivitiesPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening additional panel
await utils.clickOn('#activities-panel-btn');
// Check if default panel is not present
expect(await utils.isPresent('#default-activities-panel')).toBeFalse();
// Check if custom panel is present
await utils.waitForElement('#custom-activities-panel');
expect(await utils.isPresent('#custom-activities-panel')).toBeTrue();
// Check if activities panel is has content
await utils.waitForElement('#activities-container');
expect(await utils.isPresent('#activities-container')).toBeTrue();
});
it('should render only a custom additional panel (no default panel)', async () => {
let element;
await browser.get(`${url}`);
await utils.clickOn('#ovAdditionalPanels-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening additional panel
await utils.clickOn('#toolbar-additional-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-additional-panel');
expect(await utils.isPresent('#custom-additional-panel')).toBeTrue();
element = await utils.waitForElement('#additional-panel-title');
expect(await element.getAttribute('innerText')).toEqual('NEW PANEL');
await utils.clickOn('#toolbar-additional-panel-btn');
expect(await utils.isPresent('#custom-additional-panel')).toBeFalse();
});
it('should render only a custom chat panel (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovChatPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
expect(await utils.isPresent('#custom-panels')).toBeFalse();
// Check if default panel is not present
await utils.waitForElement('#default-panel');
expect(await utils.isPresent('#default-panel')).toBeTrue();
// Check if default participant panel is not present
await utils.waitForElement('#default-participants-panel');
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is present
await utils.waitForElement('#custom-chat-panel');
expect(await utils.isPresent('#custom-chat-panel')).toBeTrue();
});
it('should render only a custom participants panel (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
expect(await utils.isPresent('#custom-panels')).toBeFalse();
// Check if default panel is not present
await utils.waitForElement('#default-panel');
expect(await utils.isPresent('#default-panel')).toBeTrue();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is present
await utils.waitForElement('#default-chat-panel');
expect(await utils.isPresent('#default-chat-panel')).toBeTrue();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render only a custom participant panel item (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovParticipantPanelItem-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
expect(await utils.isPresent('#custom-panels')).toBeFalse();
// Check if default panel is not present
await utils.waitForElement('#default-panel');
expect(await utils.isPresent('#default-panel')).toBeTrue();
// Check if default participant panel is not present
await utils.waitForElement('#default-participants-panel');
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
await utils.waitForElement('#custom-participants-panel-item');
expect(await utils.isPresent('#custom-participants-panel-item')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is present
await utils.waitForElement('#default-chat-panel');
expect(await utils.isPresent('#default-chat-panel')).toBeTrue();
// Check if custom chat panel is not present
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render only a custom participant panel item element (no custom panel container)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovParticipantPanelItemElements-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if default participant panel is not present
await utils.waitForElement('#default-participants-panel');
expect(await utils.isPresent('#default-participants-panel')).toBeTrue();
// Check if custom participant panel is not present
expect(await utils.isPresent('#custom-participants-panel')).toBeFalse();
expect(await utils.isPresent('#custom-participants-panel-item')).toBeFalse();
expect(await utils.isPresent('#custom-participants-panel-item')).toBeFalse();
await utils.waitForElement('#custom-participants-panel-item-element');
expect(await utils.isPresent('#custom-participants-panel-item-element')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is present
await utils.waitForElement('#default-chat-panel');
expect(await utils.isPresent('#default-chat-panel')).toBeTrue();
// Check if custom chat panel is not present;
expect(await utils.isPresent('#custom-chat-panel')).toBeFalse();
});
it('should render the custom panel container with both custom chat and participants panels', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovPanel-checkbox');
await utils.clickOn('#ovChatPanel-checkbox');
await utils.clickOn('#ovParticipantsPanel-checkbox');
await utils.clickOn('#apply-btn');
// Check if toolbar panel buttons are present
await utils.checkToolbarIsPresent();
// Click on button for opening participants panel
await utils.clickOn('#participants-panel-btn');
// Check if custom panel is present
await utils.waitForElement('#custom-panels');
expect(await utils.isPresent('#custom-panels')).toBeTrue();
// Check if default panel is not present
expect(await utils.isPresent('#default-panel')).toBeFalse();
// Check if default participant panel is not present
expect(await utils.isPresent('#default-participant-panel')).toBeFalse();
// Check if custom participant panel is present
await utils.waitForElement('#custom-participants-panel');
expect(await utils.isPresent('#custom-participants-panel')).toBeTrue();
// Click on button for opening chat panel
await utils.clickOn('#chat-panel-btn');
// Check if default chat panel is not present
expect(await utils.isPresent('#default-chat-panel')).toBeFalse();
// Check if custom chat panel is present
await utils.waitForElement('#custom-chat-panel');
expect(await utils.isPresent('#custom-chat-panel')).toBeTrue();
});
});
describe('E2E: Layout and stream structural directive scenarios', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(NestedConfig.browserName)
.withCapabilities(NestedConfig.browserCapabilities)
.setChromeOptions(NestedConfig.browserOptions)
.usingServer(NestedConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should render only the custom layout (no stream, no default layout)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovLayout-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom layout is present
await utils.waitForElement('#custom-layout');
expect(await utils.isPresent('#custom-layout')).toBeTrue();
// Check if default layout is not present
expect(await utils.isPresent('#default-layout')).toBeFalse();
// Check if custom stream is not present
expect(await utils.isPresent('#custom-stream')).toBeFalse();
// Check if video is not present
expect(await utils.isPresent('video')).toBeFalse();
});
it('should render the custom layout with a custom stream (no default layout/stream)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovLayout-checkbox');
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#apply-btn');
// Check if custom layout is present
await utils.waitForElement('#custom-layout');
expect(await utils.isPresent('#custom-layout')).toBeTrue();
// Check if default layout is not present
expect(await utils.isPresent('default-layout')).toBeFalse();
// Check if custom stream is present
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('#custom-stream')).toBeTrue();
// Check if default stream is not present
expect(await utils.isPresent('default-stream')).toBeFalse();
// Check if video is present
await utils.waitForElement('video');
expect(await utils.isPresent('video')).toBeTrue();
});
it('should render only a custom stream (no custom layout, no default stream)', async () => {
await browser.get(`${url}`);
await utils.clickOn('#ovStream-checkbox');
await utils.clickOn('#apply-btn');
// Check if default layout is not present
await utils.waitForElement('#default-layout');
expect(await utils.isPresent('#default-layout')).toBeTrue();
// Check if custom stream is present
await utils.waitForElement('#custom-stream');
expect(await utils.isPresent('#custom-stream')).toBeTrue();
// Check if custom layout is not present
expect(await utils.isPresent('#custom-layout')).toBeFalse();
// Check if default stream is not present
expect(await utils.isPresent('default-stream')).toBeFalse();
// Check if video is present
await utils.waitForElement('video');
expect(await utils.isPresent('video')).toBeTrue();
});
});

View File

@ -10,14 +10,12 @@ interface BrowserConfig {
browserName: string; browserName: string;
} }
const audioPath = LAUNCH_MODE === 'CI' ? `e2e-assets/audio_test.wav` : 'e2e/assets/audio_test.wav';
const chromeArguments = [ const chromeArguments = [
'--window-size=1300,1000', '--window-size=1300,1000',
// '--headless', '--headless',
'--use-fake-ui-for-media-stream', '--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream', '--use-fake-device-for-media-stream',
`--use-file-for-fake-audio-capture=${audioPath}` '--use-file-for-fake-audio-capture=e2e/assets/audio.wav'
]; ];
const chromeArgumentsCI = [ const chromeArgumentsCI = [
'--window-size=1300,1000', '--window-size=1300,1000',
@ -31,10 +29,7 @@ const chromeArgumentsCI = [
'--disable-background-networking', '--disable-background-networking',
'--disable-default-apps', '--disable-default-apps',
'--use-fake-ui-for-media-stream', '--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream', '--use-fake-device-for-media-stream'
`--use-file-for-fake-audio-capture=${audioPath}`,
'--autoplay-policy=no-user-gesture-required',
'--allow-file-access-from-files'
]; ];
const chromeArgumentsWithoutMediaDevices = ['--headless', '--window-size=1300,900', '--deny-permission-prompts']; const chromeArgumentsWithoutMediaDevices = ['--headless', '--window-size=1300,900', '--deny-permission-prompts'];
const chromeArgumentsWithoutMediaDevicesCI = [ const chromeArgumentsWithoutMediaDevicesCI = [
@ -51,17 +46,17 @@ const chromeArgumentsWithoutMediaDevicesCI = [
'--deny-permission-prompts' '--deny-permission-prompts'
]; ];
export const TestAppConfig: BrowserConfig = { export const WebComponentConfig: BrowserConfig = {
appUrl: 'http://localhost:4200/#/call?staticVideos=false', appUrl: 'http://localhost:8080/',
seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:4444/wd/hub' : '', seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:3000/webdriver' : '',
browserName: 'chrome', browserName: 'chrome',
browserCapabilities: Capabilities.chrome().set('acceptInsecureCerts', true), browserCapabilities: Capabilities.chrome().set('acceptInsecureCerts', true),
browserOptions: new chrome.Options().addArguments(...(LAUNCH_MODE === 'CI' ? chromeArgumentsCI : chromeArguments)) browserOptions: new chrome.Options().addArguments(...(LAUNCH_MODE === 'CI' ? chromeArgumentsCI : chromeArguments))
}; };
export const NestedConfig: BrowserConfig = { export const AngularConfig: BrowserConfig = {
appUrl: 'http://localhost:4200/#/testing', appUrl: 'http://localhost:4200/#/testing',
seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:4444/wd/hub' : '', seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:3000/webdriver' : '',
browserName: 'Chrome', browserName: 'Chrome',
browserCapabilities: Capabilities.chrome().set('acceptInsecureCerts', true), browserCapabilities: Capabilities.chrome().set('acceptInsecureCerts', true),
browserOptions: new chrome.Options().addArguments(...(LAUNCH_MODE === 'CI' ? chromeArgumentsCI : chromeArguments)) browserOptions: new chrome.Options().addArguments(...(LAUNCH_MODE === 'CI' ? chromeArgumentsCI : chromeArguments))

View File

@ -6,7 +6,7 @@
"strict": true, "strict": true,
"outDir": "./dist", "outDir": "./dist",
"lib": ["es6"], "lib": ["es6"],
"types": [ "jasmine", "node" ], "types": [ "mocha", "node" ],
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true "emitDecoratorMetadata": true
}, },

View File

@ -1,4 +1,5 @@
import { By, until, WebDriver, WebElement } from 'selenium-webdriver'; import { expect } from 'chai';
import { By, Origin, until, WebDriver, WebElement } from 'selenium-webdriver';
export class OpenViduComponentsPO { export class OpenViduComponentsPO {
private TIMEOUT = 10 * 1000; private TIMEOUT = 10 * 1000;
@ -30,44 +31,44 @@ export class OpenViduComponentsPO {
async checkPrejoinIsPresent(): Promise<void> { async checkPrejoinIsPresent(): Promise<void> {
await this.waitForElement('#prejoin-container'); await this.waitForElement('#prejoin-container');
expect(await this.isPresent('#prejoin-container')).toBe(true); expect(await this.isPresent('#prejoin-container')).to.be.true;
} }
async checkSessionIsPresent() { async checkSessionIsPresent() {
await this.waitForElement('#call-container'); await this.waitForElement('#call-container');
expect(await this.isPresent('#call-container')).toBe(true); expect(await this.isPresent('#call-container')).to.be.true;
await this.waitForElement('#session-container'); await this.waitForElement('#session-container');
expect(await this.isPresent('#session-container')).toBe(true); expect(await this.isPresent('#session-container')).to.be.true;
} }
async checkLayoutPresent(): Promise<void> { async checkLayoutPresent(): Promise<void> {
await this.waitForElement('#layout-container'); await this.waitForElement('#layout-container');
expect(await this.isPresent('#layout-container')).toBe(true); expect(await this.isPresent('#layout-container')).to.be.true;
await this.waitForElement('#layout'); await this.waitForElement('#layout');
expect(await this.isPresent('#layout')).toBe(true); expect(await this.isPresent('#layout')).to.be.true;
} }
async checkStreamIsPresent(): Promise<void> { async checkStreamIsPresent(): Promise<void> {
await this.waitForElement('.OV_stream'); await this.waitForElement('.OV_stream');
expect(await this.isPresent('.OV_stream')).toBe(true); expect(await this.isPresent('.OV_stream')).to.be.true;
} }
async checkVideoElementIsPresent(): Promise<void> { async checkVideoElementIsPresent(): Promise<void> {
await this.waitForElement('video'); await this.waitForElement('video');
expect(await this.isPresent('video')).toBe(true); expect(await this.isPresent('video')).to.be.true;
} }
async checkToolbarIsPresent(): Promise<void> { async checkToolbarIsPresent(): Promise<void> {
await this.waitForElement('#toolbar'); await this.waitForElement('#toolbar');
await this.waitForElement('#media-buttons-container'); await this.waitForElement('#media-buttons-container');
expect(await this.isPresent('#media-buttons-container')).toBe(true); expect(await this.isPresent('#media-buttons-container')).to.be.true;
} }
async chceckProFeatureAlertIsPresent(): Promise<void> { async chceckProFeatureAlertIsPresent(): Promise<void> {
await this.waitForElement('ov-pro-feature-template'); await this.waitForElement('ov-pro-feature-template');
expect(await this.isPresent('ov-pro-feature-template')).toBe(true); expect(await this.isPresent('ov-pro-feature-template')).to.be.true;
} }
async clickOn(selector: string): Promise<void> { async clickOn(selector: string): Promise<void> {
@ -103,27 +104,18 @@ export class OpenViduComponentsPO {
async toggleToolbarMoreOptions(): Promise<void> { async toggleToolbarMoreOptions(): Promise<void> {
await this.waitForElement('#more-options-btn'); await this.waitForElement('#more-options-btn');
expect(await this.isPresent('#more-options-btn')).toBe(true); expect(await this.isPresent('#more-options-btn')).to.be.true;
await this.clickOn('#more-options-btn'); await this.clickOn('#more-options-btn');
await this.browser.sleep(500); await this.browser.sleep(500);
await this.waitForElement('#more-options-menu'); await this.waitForElement('#more-options-menu');
} }
async disableScreenShare(): Promise<void> {
await this.waitForElement('#screenshare-btn');
await this.clickOn('#screenshare-btn');
await this.browser.sleep(500);
await this.waitForElement('#screenshare-menu');
await this.clickOn('#disable-screen-button');
await this.browser.sleep(1000);
}
async toggleRecordingFromToolbar() { async toggleRecordingFromToolbar() {
// Open more options menu // Open more options menu
await this.toggleToolbarMoreOptions(); await this.toggleToolbarMoreOptions();
await this.waitForElement('#recording-btn'); await this.waitForElement('#recording-btn');
expect(await this.isPresent('#recording-btn')).toBe(true); expect(await this.isPresent('#recording-btn')).to.be.true;
await this.clickOn('#recording-btn'); await this.clickOn('#recording-btn');
} }
@ -131,52 +123,16 @@ export class OpenViduComponentsPO {
// Open more options menu // Open more options menu
await this.toggleToolbarMoreOptions(); await this.toggleToolbarMoreOptions();
await this.waitForElement('#fullscreen-btn'); const fullscreenButton = await this.waitForElement('#fullscreen-btn');
expect(await this.isPresent('#fullscreen-btn')).toBe(true); expect(await this.isPresent('#fullscreen-btn')).to.be.true;
await this.clickOn('#fullscreen-btn'); await fullscreenButton.click();
}
async leaveRoom() {
try {
// Close any open panels or menus clicking on the body
await this.clickOn('body');
await this.browser.sleep(300);
// Verify that the leave button is present
await this.waitForElement('#leave-btn');
// Click on the leave button
await this.clickOn('#leave-btn');
// Verify that the session container is no longer present
await this.browser.wait(
async () => {
return !(await this.isPresent('#session-container'));
},
this.TIMEOUT,
'Session container should disappear after leaving room'
);
// Wait for the prejoin container to be present again
await this.browser.sleep(500);
// Verify that there are no video elements left in the DOM
const videoCount = await this.getNumberOfElements('video');
if (videoCount > 0) {
console.warn(`Warning: ${videoCount} video elements still present after leaving room`);
}
} catch (error) {
console.error('Error during leaveRoom:', error);
throw error;
}
} }
async togglePanel(panelName: string) { async togglePanel(panelName: string) {
switch (panelName) { switch (panelName) {
case 'activities': case 'activities':
await this.waitForElement('#activities-panel-btn'); await this.waitForElement('#activities-panel-btn');
expect(await this.isPresent('#activities-panel-btn')).toBe(true); expect(await this.isPresent('#activities-panel-btn')).to.be.true;
await this.clickOn('#activities-panel-btn'); await this.clickOn('#activities-panel-btn');
break; break;
@ -195,7 +151,5 @@ export class OpenViduComponentsPO {
await this.clickOn('#toolbar-settings-btn'); await this.clickOn('#toolbar-settings-btn');
break; break;
} }
await this.browser.sleep(500);
} }
} }

View File

@ -0,0 +1,292 @@
import monkeyPatchMediaDevices from './utils/media-devices.js';
var MINIMAL;
var LANG;
var CAPTIONS_LANG;
var CUSTOM_LANG_OPTIONS;
var CUSTOM_CAPTIONS_LANG_OPTIONS;
var PREJOIN;
var VIDEO_ENABLED;
var AUDIO_ENABLED;
var SCREENSHARE_BUTTON;
var FULLSCREEN_BUTTON;
var ACTIVITIES_PANEL_BUTTON;
var RECORDING_BUTTON;
var BROADCASTING_BUTTON;
var CHAT_PANEL_BUTTON;
var DISPLAY_LOGO;
var DISPLAY_ROOM_NAME;
var DISPLAY_PARTICIPANT_NAME;
var DISPLAY_AUDIO_DETECTION;
var VIDEO_CONTROLS;
var LEAVE_BUTTON;
var PARTICIPANT_MUTE_BUTTON;
var PARTICIPANTS_PANEL_BUTTON;
var ACTIVITIES_RECORDING_ACTIVITY;
var ACTIVITIES_BROADCASTING_ACTIVITY;
var RECORDING_ERROR;
var BROADCASTING_ERROR;
var TOOLBAR_SETTINGS_BUTTON;
var CAPTIONS_BUTTON;
var ROOM_NAME;
var FAKE_DEVICES;
var FAKE_RECORDINGS;
var PARTICIPANT_NAME;
var OPENVIDU_CALL_SERVER_URL;
// var OPENVIDU_SECRET;
document.addEventListener('DOMContentLoaded', () => {
var url = new URL(window.location.href);
OPENVIDU_CALL_SERVER_URL = url.searchParams.get('OV_URL');
// OPENVIDU_SECRET = url.searchParams.get('OV_SECRET');
FAKE_DEVICES = url.searchParams.get('fakeDevices') === null ? false : url.searchParams.get('fakeDevices') === 'true';
FAKE_RECORDINGS = url.searchParams.get('fakeRecordings') === null ? false : url.searchParams.get('fakeRecordings') === 'true';
// Directives
MINIMAL = url.searchParams.get('minimal') === null ? false : url.searchParams.get('minimal') === 'true';
LANG = url.searchParams.get('lang') || 'en';
CUSTOM_LANG_OPTIONS = url.searchParams.get('langOptions') === null ? false : url.searchParams.get('langOptions') === 'true';
// CAPTIONS_LANG = url.searchParams.get('captionsLang') || 'en-US';
// CUSTOM_CAPTIONS_LANG_OPTIONS = url.searchParams.get('captionsLangOptions') === null ? false : url.searchParams.get('captionsLangOptions') === 'true';
PARTICIPANT_NAME =
url.searchParams.get('participantName') === null
? 'TEST_USER' + Math.random().toString(36).substr(2, 9)
: url.searchParams.get('participantName');
PREJOIN = url.searchParams.get('prejoin') === null ? true : url.searchParams.get('prejoin') === 'true';
VIDEO_ENABLED = url.searchParams.get('videoEnabled') === null ? true : url.searchParams.get('videoEnabled') === 'true';
AUDIO_ENABLED = url.searchParams.get('audioEnabled') === null ? true : url.searchParams.get('audioEnabled') === 'true';
SCREENSHARE_BUTTON = url.searchParams.get('screenshareBtn') === null ? true : url.searchParams.get('screenshareBtn') === 'true';
RECORDING_BUTTON =
url.searchParams.get('toolbarRecordingButton') === null ? true : url.searchParams.get('toolbarRecordingButton') === 'true';
FULLSCREEN_BUTTON = url.searchParams.get('fullscreenBtn') === null ? true : url.searchParams.get('fullscreenBtn') === 'true';
BROADCASTING_BUTTON =
url.searchParams.get('toolbarBroadcastingButton') === null ? true : url.searchParams.get('toolbarBroadcastingButton') === 'true';
if (url.searchParams.get('broadcastingError') !== null) {
BROADCASTING_ERROR = url.searchParams.get('broadcastingError');
}
TOOLBAR_SETTINGS_BUTTON =
url.searchParams.get('toolbarSettingsBtn') === null ? true : url.searchParams.get('toolbarSettingsBtn') === 'true';
CAPTIONS_BUTTON = url.searchParams.get('toolbarCaptionsBtn') === null ? true : url.searchParams.get('toolbarCaptionsBtn') === 'true';
LEAVE_BUTTON = url.searchParams.get('leaveBtn') === null ? true : url.searchParams.get('leaveBtn') === 'true';
ACTIVITIES_PANEL_BUTTON =
url.searchParams.get('activitiesPanelBtn') === null ? true : url.searchParams.get('activitiesPanelBtn') === 'true';
CHAT_PANEL_BUTTON = url.searchParams.get('chatPanelBtn') === null ? true : url.searchParams.get('chatPanelBtn') === 'true';
PARTICIPANTS_PANEL_BUTTON =
url.searchParams.get('participantsPanelBtn') === null ? true : url.searchParams.get('participantsPanelBtn') === 'true';
ACTIVITIES_BROADCASTING_ACTIVITY =
url.searchParams.get('activitiesPanelBroadcastingActivity') === null
? true
: url.searchParams.get('activitiesPanelBroadcastingActivity') === 'true';
ACTIVITIES_RECORDING_ACTIVITY =
url.searchParams.get('activitiesPanelRecordingActivity') === null
? true
: url.searchParams.get('activitiesPanelRecordingActivity') === 'true';
if (url.searchParams.get('recordingError') !== null) {
RECORDING_ERROR = url.searchParams.get('recordingError');
}
DISPLAY_LOGO = url.searchParams.get('displayLogo') === null ? true : url.searchParams.get('displayLogo') === 'true';
DISPLAY_ROOM_NAME = url.searchParams.get('displayRoomName') === null ? true : url.searchParams.get('displayRoomName') === 'true';
DISPLAY_PARTICIPANT_NAME =
url.searchParams.get('displayParticipantName') === null ? true : url.searchParams.get('displayParticipantName') === 'true';
DISPLAY_AUDIO_DETECTION =
url.searchParams.get('displayAudioDetection') === null ? true : url.searchParams.get('displayAudioDetection') === 'true';
VIDEO_CONTROLS = url.searchParams.get('videoControls') === null ? true : url.searchParams.get('videoControls') === 'true';
PARTICIPANT_MUTE_BUTTON =
url.searchParams.get('participantMuteBtn') === null ? true : url.searchParams.get('participantMuteBtn') === 'true';
ROOM_NAME = url.searchParams.get('roomName') === null ? `E2ESession${Math.floor(Date.now())}` : url.searchParams.get('roomName');
var webComponent = document.querySelector('openvidu-webcomponent');
webComponent.addEventListener('onTokenRequested', (event) => {
appendElement('onTokenRequested');
console.log('Token ready', event.detail);
joinSession(ROOM_NAME, event.detail);
});
webComponent.addEventListener('onReadyToJoin', (event) => appendElement('onReadyToJoin'));
webComponent.addEventListener('onRoomDisconnected', (event) => appendElement('onRoomDisconnected'));
webComponent.addEventListener('onVideoEnabledChanged', (event) => appendElement('onVideoEnabledChanged-' + event.detail));
webComponent.addEventListener('onVideoDeviceChanged', (event) => appendElement('onVideoDeviceChanged'));
webComponent.addEventListener('onAudioEnabledChanged', (eSESSIONvent) => appendElement('onAudioEnabledChanged-' + event.detail));
webComponent.addEventListener('onAudioDeviceChanged', (event) => appendElement('onAudioDeviceChanged'));
webComponent.addEventListener('onScreenShareEnabledChanged', (event) => appendElement('onScreenShareEnabledChanged'));
webComponent.addEventListener('onParticipantsPanelStatusChanged', (event) =>
appendElement('onParticipantsPanelStatusChanged-' + event.detail.isOpened)
);
webComponent.addEventListener('onLangChanged', (event) => appendElement('onLangChanged-' + event.detail.lang));
webComponent.addEventListener('onChatPanelStatusChanged', (event) =>
appendElement('onChatPanelStatusChanged-' + event.detail.isOpened)
);
webComponent.addEventListener('onActivitiesPanelStatusChanged', (event) =>
appendElement('onActivitiesPanelStatusChanged-' + event.detail.isOpened)
);
webComponent.addEventListener('onSettingsPanelStatusChanged', (event) =>
appendElement('onSettingsPanelStatusChanged-' + event.detail.isOpened)
);
webComponent.addEventListener('onFullscreenEnabledChanged', (event) => appendElement('onFullscreenEnabledChanged-' + event.detail));
webComponent.addEventListener('onRecordingStartRequested', async (event) => {
appendElement('onRecordingStartRequested-' + event.detail.roomName);
// Can't test the recording
// RECORDING_ID = await startRecording(SESSION_NAME);
});
// Can't test the recording
// webComponent.addEventListener('onRecordingStopRequested', async (event) => {
// appendElement('onRecordingStopRequested-' + event.detail.roomName);
// await stopRecording(RECORDING_ID);
// });
webComponent.addEventListener('onRecordingStopRequested', async (event) => {
appendElement('onRecordingStopRequested-' + event.detail.roomName);
});
// Can't test the recording
// webComponent.addEventListener('onActivitiesPanelStopRecordingClicked', async (event) => {
// appendElement('onActivitiesPanelStopRecordingClicked');
// await stopRecording(RECORDING_ID);
// });
webComponent.addEventListener('onRecordingDeleteRequested', (event) => {
const { roomName, recordingId } = event.detail;
appendElement(`onRecordingDeleteRequested-${roomName}-${recordingId}`);
});
webComponent.addEventListener('onBroadcastingStartRequested', async (event) => {
const { roomName, broadcastUrl } = event.detail;
appendElement(`onBroadcastingStartRequested-${roomName}-${broadcastUrl}`);
});
webComponent.addEventListener('onActivitiesPanelStopBroadcastingClicked', async (event) => {
appendElement('onActivitiesPanelStopBroadcastingClicked');
});
webComponent.addEventListener('onRoomCreated', (event) => {
var room = event.detail;
appendElement('onRoomCreated');
room.on('disconnected', (e) => {
appendElement('roomDisconnected');
});
});
webComponent.addEventListener('onParticipantCreated', (event) => {
var participant = event.detail;
appendElement(`${participant.name}-onParticipantCreated`);
});
setWebcomponentAttributes();
});
function setWebcomponentAttributes() {
var webComponent = document.querySelector('openvidu-webcomponent');
webComponent.participantName = PARTICIPANT_NAME;
webComponent.minimal = MINIMAL;
webComponent.lang = LANG;
if (CUSTOM_LANG_OPTIONS) {
webComponent.langOptions = [
{ name: 'Esp', lang: 'es' },
{ name: 'Eng', lang: 'en' }
];
}
// TODO: Uncomment when the captions are implemented
// webComponent.captionsLang = CAPTIONS_LANG;
// if (CUSTOM_CAPTIONS_LANG_OPTIONS) {
// webComponent.captionsLangOptions = [
// { name: 'Esp', lang: 'es-ES' },
// { name: 'Eng', lang: 'en-US' }
// ];
// }
if (FAKE_DEVICES) {
console.warn('Using fake devices');
monkeyPatchMediaDevices();
}
if (FAKE_RECORDINGS) {
console.warn('Using fake recordings');
webComponent.recordingActivityRecordingsList = [{ status: 'ready', filename: 'fakeRecording' }];
}
if (BROADCASTING_ERROR) {
webComponent.broadcastingActivityBroadcastingError = { message: BROADCASTING_ERROR, broadcastAvailable: true };
}
webComponent.prejoin = PREJOIN;
webComponent.videoEnabled = VIDEO_ENABLED;
webComponent.audioEnabled = AUDIO_ENABLED;
webComponent.toolbarScreenshareButton = SCREENSHARE_BUTTON;
webComponent.toolbarFullscreenButton = FULLSCREEN_BUTTON;
webComponent.toolbarSettingsButton = TOOLBAR_SETTINGS_BUTTON;
// webComponent.toolbarCaptionsButton = CAPTIONS_BUTTON;
webComponent.toolbarLeaveButton = LEAVE_BUTTON;
webComponent.toolbarRecordingButton = RECORDING_BUTTON;
webComponent.toolbarBroadcastingButton = BROADCASTING_BUTTON;
webComponent.toolbarActivitiesPanelButton = ACTIVITIES_PANEL_BUTTON;
webComponent.toolbarChatPanelButton = CHAT_PANEL_BUTTON;
webComponent.toolbarParticipantsPanelButton = PARTICIPANTS_PANEL_BUTTON;
webComponent.toolbarDisplayLogo = DISPLAY_LOGO;
webComponent.toolbarDisplayRoomName = DISPLAY_ROOM_NAME;
webComponent.streamDisplayParticipantName = DISPLAY_PARTICIPANT_NAME;
webComponent.streamDisplayAudioDetection = DISPLAY_AUDIO_DETECTION;
webComponent.streamVideoControls = VIDEO_CONTROLS;
webComponent.participantPanelItemMuteButton = PARTICIPANT_MUTE_BUTTON;
webComponent.activitiesPanelRecordingActivity = ACTIVITIES_RECORDING_ACTIVITY;
webComponent.activitiesPanelBroadcastingActivity = ACTIVITIES_BROADCASTING_ACTIVITY;
webComponent.recordingActivityRecordingError = RECORDING_ERROR;
}
function appendElement(id) {
var eventsDiv = document.getElementById('events');
eventsDiv.setAttribute('style', 'position: absolute;');
var element = document.createElement('div');
element.setAttribute('id', id);
element.setAttribute('style', 'height: 1px;');
eventsDiv.appendChild(element);
}
async function joinSession(roomName, participantName) {
var webComponent = document.querySelector('openvidu-webcomponent');
console.log('Joining session', roomName, participantName);
try {
webComponent.token = await getToken(roomName, participantName);
} catch (error) {
webComponent.tokenError = error;
}
}
async function getToken(roomName, participantName) {
try {
const response = await fetch(OPENVIDU_CALL_SERVER_URL + '/call/api/rooms', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
// 'Authorization': 'Basic ' + btoa('OPENVIDUAPP:' + OPENVIDU_SECRET),
},
body: JSON.stringify({
participantName,
roomName
})
});
if (!response.ok) {
throw new Error('Failed to fetch token');
}
const data = await response.json();
return data.token;
} catch (error) {
console.error(error);
throw error;
}
}

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<title>openvidu-web-component</title>
<script type="module" src="utils/filter-stream.js"></script>
<!-- <script type="module" src="utils/shader-renderer.js"></script> -->
<script type="module" src="utils/media-devices.js"></script>
<script type="module" src="app.js"></script>
<script src="openvidu-webcomponent-dev.js"></script>
<link rel="stylesheet" href="openvidu-webcomponent-dev.css" />
<style>
:root {
--ov-primary-color: #303030;
--ov-secondary-color: #3e3f3f;
--ov-tertiary-color: #598eff;
--ov-warn-color: #eb5144;
--ov-accent-color: #ffae35;
--ov-light-color: #e6e6e6;
--ov-logo-background-color: #3a3d3d;
--ov-text-color: #ffffff;
--ov-panel-text-color: #1d1d1d;
--ov-panel-background: #ffffff;
--ov-buttons-radius: 50%;
--ov-leave-button-radius: 10px;
--ov-video-radius: 5px;
--ov-panel-radius: 5px;
}
</style>
</head>
<body>
<div id="events"></div>
<!-- OpenVidu Web Component -->
<openvidu-webcomponent></openvidu-webcomponent>
</body>
</html>

View File

@ -1,21 +1,21 @@
// Ideally we'd use an editor or import shaders directly from the API. // Ideally we'd use an editor or import shaders directly from the API.
import { FilterStream } from './filter-stream.js'; import { FilterStream } from './filter-stream.js';
export const monkeyPatchMediaDevices = () => { export default function monkeyPatchMediaDevices() {
const enumerateDevicesFn = MediaDevices.prototype.enumerateDevices; const enumerateDevicesFn = MediaDevices.prototype.enumerateDevices;
const getUserMediaFn = MediaDevices.prototype.getUserMedia; const getUserMediaFn = MediaDevices.prototype.getUserMedia;
const getDisplayMediaFn = MediaDevices.prototype.getDisplayMedia; const getDisplayMediaFn = MediaDevices.prototype.getDisplayMedia;
const fakeVideoDevice = { const fakeVideoDevice = {
deviceId: 'virtual_video', deviceId: 'virtual',
groupId: '', groupID: '',
kind: 'videoinput', kind: 'videoinput',
label: 'custom_fake_video_1' label: 'custom_fake_video_1'
}; };
const fakeAudioDevice = { const fakeAudioDevice = {
deviceId: 'virtual_audio', deviceId: 'virtual',
groupId: '', groupID: '',
kind: 'audioinput', kind: 'audioinput',
label: 'custom_fake_audio_1' label: 'custom_fake_audio_1'
}; };
@ -29,21 +29,8 @@ export const monkeyPatchMediaDevices = () => {
const getUserMediaMonkeyPatch = async function () { const getUserMediaMonkeyPatch = async function () {
const args = arguments[0]; const args = arguments[0];
if (args.audio && (args.audio.deviceId === 'virtual_audio' || args.audio.deviceId?.exact === 'virtual_audio')) {
const constraints = {
audio: {
facingMode: args.facingMode,
advanced: args.audio.advanced,
deviceId: fakeAudioDevice.deviceId
},
video: false
};
const res = await getUserMediaFn.call(navigator.mediaDevices, constraints);
return res;
} else if (args.video && (args.video.deviceId === 'virtual_video' || args.video.deviceId?.exact === 'virtual_video')) {
const { deviceId, advanced, width, height } = args.video; const { deviceId, advanced, width, height } = args.video;
if (deviceId === 'virtual' || deviceId?.exact === 'virtual') {
const constraints = { const constraints = {
video: { video: {
facingMode: args.facingMode, facingMode: args.facingMode,

View File

@ -1,35 +1,30 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
let url = ''; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Testing API Directives', () => { describe('Testing API Directives', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
beforeEach(async () => { beforeEach(async () => {
browser = await createChromeBrowser(); browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser); utils = new OpenViduComponentsPO(browser);
url = `${TestAppConfig.appUrl}&roomName=API_DIRECTIVES_${Math.floor(Math.random() * 1000)}`;
}); });
afterEach(async () => { afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot()); // console.log('data:image/png;base64,' + await browser.takeScreenshot());
try {
if (await utils.isPresent('#session-container')) {
await utils.leaveRoom();
}
} catch (error) {}
await browser.sleep(500);
await browser.quit(); await browser.quit();
}); });
@ -39,7 +34,7 @@ describe('Testing API Directives', () => {
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
// Checking if audio detection is not displayed // Checking if audio detection is not displayed
expect(await utils.isPresent('#audio-wave-container')).toBeFalse(); expect(await utils.isPresent('#audio-wave-container')).to.be.false;
const joinButton = await utils.waitForElement('#join-button'); const joinButton = await utils.waitForElement('#join-button');
await joinButton.click(); await joinButton.click();
@ -57,31 +52,31 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if screenshare button is not present // Checking if screenshare button is not present
expect(await utils.isPresent('#screenshare-btn')).toBeFalse(); expect(await utils.isPresent('#screenshare-btn')).to.be.false;
// Checking if more options button is not present // Checking if more options button is not present
expect(await utils.isPresent('#more-options-btn')).toBeFalse(); expect(await utils.isPresent('#more-options-btn')).to.be.false;
// Checking if participants panel button is not present // Checking if participants panel button is not present
expect(await utils.isPresent('#participants-panel-btn')).toBeFalse(); expect(await utils.isPresent('#participants-panel-btn')).to.be.false;
// Checking if activities panel button is not present // Checking if activities panel button is not present
expect(await utils.isPresent('#activities-panel-btn')).toBeFalse(); expect(await utils.isPresent('#activities-panel-btn')).to.be.false;
// Checking if logo is not displayed // Checking if logo is not displayed
expect(await utils.isPresent('#branding-logo')).toBeFalse(); expect(await utils.isPresent('#branding-logo')).to.be.false;
// Checking if session name is not displayed // Checking if session name is not displayed
expect(await utils.isPresent('#session-name')).toBeFalse(); expect(await utils.isPresent('#session-name')).to.be.false;
// Checking if nickname is not displayed // Checking if nickname is not displayed
expect(await utils.getNumberOfElements('#participant-name-container')).toEqual(0); expect(await utils.getNumberOfElements('#participant-name-container')).equals(0);
// Checking if audio detection is not displayed // Checking if audio detection is not displayed
expect(await utils.isPresent('#audio-wave-container')).toBeFalse(); expect(await utils.isPresent('#audio-wave-container')).to.be.false;
// Checking if settings button is not displayed // Checking if settings button is not displayed
expect(await utils.isPresent('#settings-container')).toBeFalse(); expect(await utils.isPresent('#settings-container')).to.be.false;
}); });
it('should change the UI LANG in prejoin page', async () => { it('should change the UI LANG in prejoin page', async () => {
@ -92,7 +87,7 @@ describe('Testing API Directives', () => {
await utils.waitForElement('#lang-btn-compact'); await utils.waitForElement('#lang-btn-compact');
const element = await utils.waitForElement('#join-button'); const element = await utils.waitForElement('#join-button');
expect(await element.getText()).toEqual('Unirme ahora'); expect(await element.getText()).equal('Unirme ahora');
}); });
it('should change the UI LANG in room page', async () => { it('should change the UI LANG in room page', async () => {
@ -104,12 +99,12 @@ describe('Testing API Directives', () => {
await utils.togglePanel('settings'); await utils.togglePanel('settings');
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('#default-settings-panel')).toBeTrue(); expect(await utils.isPresent('#default-settings-panel')).to.be.true;
const panelTitle = await utils.waitForElement('.panel-title'); const panelTitle = await utils.waitForElement('.panel-title');
expect(await panelTitle.getText()).toEqual('Configuración'); expect(await panelTitle.getText()).equal('Configuración');
const element = await utils.waitForElement('#lang-selected-name'); const element = await utils.waitForElement('#lang-selected-name');
expect(await element.getAttribute('innerText')).toEqual('Español'); expect(await element.getAttribute('innerText')).equal('Español');
}); });
it('should override the LANG OPTIONS', async () => { it('should override the LANG OPTIONS', async () => {
@ -119,7 +114,7 @@ describe('Testing API Directives', () => {
await utils.waitForElement('#lang-btn-compact'); await utils.waitForElement('#lang-btn-compact');
await utils.clickOn('#lang-btn-compact'); await utils.clickOn('#lang-btn-compact');
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.lang-menu-opt')).toEqual(2); expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2);
await utils.clickOn('.lang-menu-opt'); await utils.clickOn('.lang-menu-opt');
await browser.sleep(500); await browser.sleep(500);
@ -141,7 +136,7 @@ describe('Testing API Directives', () => {
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.lang-menu-opt')).toEqual(2); expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2);
}); });
it('should show the PREJOIN page', async () => { it('should show the PREJOIN page', async () => {
@ -170,11 +165,11 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if screenshare button is not present // Checking if screenshare button is not present
expect(await utils.isPresent('#screenshare-btn')).toBeTrue(); expect(await utils.isPresent('#screenshare-btn')).to.be.true;
}); });
it('should show the token error WITH prejoin page', async () => { it('should show the token error WITH prejoin page', async () => {
const fixedUrl = `${TestAppConfig.appUrl}&roomName=TEST_TOKEN&participantName=PNAME`; const fixedUrl = `${url}&roomName=TEST_TOKEN&participantName=PNAME`;
await browser.get(`${fixedUrl}`); await browser.get(`${fixedUrl}`);
// Checking if prejoin page exist // Checking if prejoin page exist
@ -200,11 +195,11 @@ describe('Testing API Directives', () => {
// Checking if token error is displayed // Checking if token error is displayed
await utils.waitForElement('#token-error'); await utils.waitForElement('#token-error');
expect(await utils.isPresent('#token-error')).toBeTrue(); expect(await utils.isPresent('#token-error')).to.be.true;
}); });
it('should show the token error WITHOUT prejoin page', async () => { it('should show the token error WITHOUT prejoin page', async () => {
const fixedUrl = `${TestAppConfig.appUrl}&roomName=TOKEN_ERROR&prejoin=false&participantName=PNAME`; const fixedUrl = `${url}&roomName=TOKEN_ERROR&prejoin=false&participantName=PNAME`;
await browser.get(`${fixedUrl}`); await browser.get(`${fixedUrl}`);
// Checking if session container is present // Checking if session container is present
@ -220,7 +215,7 @@ describe('Testing API Directives', () => {
// Checking if token error is displayed // Checking if token error is displayed
await utils.waitForElement('#openvidu-dialog'); await utils.waitForElement('#openvidu-dialog');
expect(await utils.isPresent('#openvidu-dialog')).toBeTrue(); expect(await utils.isPresent('#openvidu-dialog')).to.be.true;
}); });
it('should run the app with VIDEO DISABLED in prejoin page', async () => { it('should run the app with VIDEO DISABLED in prejoin page', async () => {
@ -229,20 +224,21 @@ describe('Testing API Directives', () => {
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
// Checking if video is displayed // Checking if video is displayed
await utils.waitForElement('#video-poster'); expect(await utils.getNumberOfElements('video')).equals(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
// Checking if virtual background button is disabled
// const button = await utils.waitForElement('#background-effects-btn');
// expect(await button.isEnabled()).to.be.false;
await utils.waitForElement('#videocam_off'); await utils.waitForElement('#videocam_off');
await utils.clickOn('#join-button'); await utils.clickOn('#join-button');
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
await utils.waitForElement('#videocam_off'); expect(await utils.getNumberOfElements('video')).equals(1);
expect(await utils.isPresent('#videocam_off')).toBeTrue();
await utils.waitForElement('#video-poster'); await utils.waitForElement('#videocam_off');
expect(await utils.getNumberOfElements('video')).toEqual(0); expect(await utils.isPresent('#videocam_off')).to.be.true;
}); });
it('should run the app with VIDEO DISABLED and WITHOUT PREJOIN page', async () => { it('should run the app with VIDEO DISABLED and WITHOUT PREJOIN page', async () => {
@ -253,74 +249,58 @@ describe('Testing API Directives', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Checking if video is displayed // Checking if video is displayed
await utils.waitForElement('#video-poster'); expect(await utils.getNumberOfElements('video')).equals(1);
expect(await utils.getNumberOfElements('video')).toEqual(0); expect(await utils.getNumberOfElements('#video-poster')).equals(1);
expect(await utils.getNumberOfElements('#video-poster')).toEqual(1);
await utils.waitForElement('#videocam_off'); await utils.waitForElement('#videocam_off');
expect(await utils.isPresent('#videocam_off')).toBeTrue(); expect(await utils.isPresent('#videocam_off')).to.be.true;
}); });
it('should run the app with AUDIO DISABLED in prejoin page', async () => { it('should run the app with AUDIO DISABLED in prejoin page', async () => {
// let isAudioEnabled;
// const script = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;';
await browser.get(`${url}&audioEnabled=false`); await browser.get(`${url}&audioEnabled=false`);
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
// Checking if video is displayed // Checking if video is displayed
await utils.checkVideoElementIsPresent(); await utils.checkVideoElementIsPresent();
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0); // Checking if audio track is disabled/muted
// isAudioEnabled = await browser.executeScript(script);
// expect(isAudioEnabled).to.be.false;
await utils.waitForElement('#mic_off'); await utils.waitForElement('#mic_off');
expect(await utils.isPresent('#mic_off')).toBeTrue(); expect(await utils.isPresent('#mic_off')).to.be.true;
await utils.clickOn('#join-button'); await utils.clickOn('#join-button');
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
// isAudioEnabled = await browser.executeScript(script);
// expect(isAudioEnabled).to.be.false;
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await utils.waitForElement('#mic_off'); await utils.waitForElement('#mic_off');
expect(await utils.isPresent('#mic_off')).toBeTrue(); expect(await utils.isPresent('#mic_off')).to.be.true;
}); });
it('should run the app with AUDIO DISABLED and WITHOUT PREJOIN page', async () => { it('should run the app with AUDIO DISABLED and WITHOUT PREJOIN page', async () => {
// let isAudioEnabled;
// const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;';
await browser.get(`${url}&prejoin=false&audioEnabled=false`); await browser.get(`${url}&prejoin=false&audioEnabled=false`);
await browser.sleep(1000);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
// Checking if video is displayed // Checking if video is displayed
await utils.checkVideoElementIsPresent(); await utils.checkVideoElementIsPresent();
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0); // Checking if audio track is disabled/muted
// isAudioEnabled = await browser.executeScript(audioEnableScript);
// expect(isAudioEnabled).to.be.false;
await utils.waitForElement('#mic_off'); await utils.waitForElement('#mic_off');
expect(await utils.isPresent('#mic_off')).toBeTrue(); expect(await utils.isPresent('#mic_off')).to.be.true;
});
it('should run the app without camera button', async () => {
await browser.get(`${url}&prejoin=false&cameraBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if camera button is not present
expect(await utils.isPresent('#camera-btn')).toBeFalse();
});
it('should run the app without microphone button', async () => {
await browser.get(`${url}&prejoin=false&microphoneBtn=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Checking if microphone button is not present
expect(await utils.isPresent('#microphone-btn')).toBeFalse();
}); });
it('should HIDE the SCREENSHARE button', async () => { it('should HIDE the SCREENSHARE button', async () => {
@ -332,7 +312,7 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if screenshare button is not present // Checking if screenshare button is not present
expect(await utils.isPresent('#screenshare-btn')).toBeFalse(); expect(await utils.isPresent('#screenshare-btn')).to.be.false;
}); });
it('should HIDE the FULLSCREEN button', async () => { it('should HIDE the FULLSCREEN button', async () => {
@ -344,10 +324,10 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions(); await utils.toggleToolbarMoreOptions();
expect(await utils.getNumberOfElements('#fullscreen-btn')).toEqual(0); expect(await utils.getNumberOfElements('#fullscreen-btn')).equals(0);
}); });
xit('should HIDE the CAPTIONS button', async () => { it('should HIDE the CAPTIONS button', async () => {
await browser.get(`${url}&prejoin=false&toolbarCaptionsBtn=false`); await browser.get(`${url}&prejoin=false&toolbarCaptionsBtn=false`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
@ -358,16 +338,16 @@ describe('Testing API Directives', () => {
await utils.toggleToolbarMoreOptions(); await utils.toggleToolbarMoreOptions();
// Checking if captions button is not present // Checking if captions button is not present
expect(await utils.isPresent('#captions-btn')).toBeFalse(); expect(await utils.isPresent('#captions-btn')).to.be.false;
await utils.clickOn('#toolbar-settings-btn'); await utils.clickOn('#toolbar-settings-btn');
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('.settings-container'); await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).toBeTrue(); expect(await utils.isPresent('.settings-container')).to.be.true;
expect(await utils.isPresent('#captions-opt')).toBeFalse(); expect(await utils.isPresent('#captions-opt')).to.be.false;
}); });
it('should HIDE the TOOLBAR RECORDING button', async () => { it('should HIDE the TOOLBAR RECORDING button', async () => {
@ -381,7 +361,7 @@ describe('Testing API Directives', () => {
await utils.toggleToolbarMoreOptions(); await utils.toggleToolbarMoreOptions();
// Checking if recording button is not present // Checking if recording button is not present
expect(await utils.isPresent('#recording-btn')).toBeFalse(); expect(await utils.isPresent('#recording-btn')).to.be.false;
}); });
it('should HIDE the TOOLBAR BROADCASTING button', async () => { it('should HIDE the TOOLBAR BROADCASTING button', async () => {
@ -395,7 +375,7 @@ describe('Testing API Directives', () => {
await utils.toggleToolbarMoreOptions(); await utils.toggleToolbarMoreOptions();
// Checking if broadcasting button is not present // Checking if broadcasting button is not present
expect(await utils.isPresent('#broadcasting-btn')).toBeFalse(); expect(await utils.isPresent('#broadcasting-btn')).to.be.false;
}); });
it('should HIDE the TOOLBAR SETTINGS button', async () => { it('should HIDE the TOOLBAR SETTINGS button', async () => {
@ -409,7 +389,7 @@ describe('Testing API Directives', () => {
// Open more options menu // Open more options menu
await utils.toggleToolbarMoreOptions(); await utils.toggleToolbarMoreOptions();
expect(await utils.isPresent('#toolbar-settings-btn')).toBeFalse(); expect(await utils.isPresent('#toolbar-settings-btn')).to.be.false;
}); });
it('should HIDE the LEAVE button', async () => { it('should HIDE the LEAVE button', async () => {
@ -421,7 +401,7 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if leave button is not present // Checking if leave button is not present
expect(await utils.getNumberOfElements('#leave-btn')).toEqual(0); expect(await utils.getNumberOfElements('#leave-btn')).equals(0);
}); });
it('should HIDE the ACTIVITIES PANEL button', async () => { it('should HIDE the ACTIVITIES PANEL button', async () => {
@ -433,7 +413,7 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if activities panel button is not present // Checking if activities panel button is not present
expect(await utils.isPresent('#activities-panel-btn')).toBeFalse(); expect(await utils.isPresent('#activities-panel-btn')).to.be.false;
}); });
it('should HIDE the CHAT PANEL button', async () => { it('should HIDE the CHAT PANEL button', async () => {
@ -445,7 +425,7 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if chat panel button is not present // Checking if chat panel button is not present
expect(await utils.isPresent('#chat-panel-btn')).toBeFalse(); expect(await utils.isPresent('#chat-panel-btn')).to.be.false;
}); });
it('should HIDE the PARTICIPANTS PANEL button', async () => { it('should HIDE the PARTICIPANTS PANEL button', async () => {
@ -457,7 +437,7 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if participants panel button is not present // Checking if participants panel button is not present
expect(await utils.isPresent('#participants-panel-btn')).toBeFalse(); expect(await utils.isPresent('#participants-panel-btn')).to.be.false;
}); });
it('should HIDE the LOGO', async () => { it('should HIDE the LOGO', async () => {
@ -470,13 +450,13 @@ describe('Testing API Directives', () => {
// Checking if toolbar is present // Checking if toolbar is present
await utils.waitForElement('#info-container'); await utils.waitForElement('#info-container');
expect(await utils.isPresent('#info-container')).toBeTrue(); expect(await utils.isPresent('#info-container')).to.be.true;
// Checking if logo is not displayed // Checking if logo is not displayed
expect(await utils.isPresent('#branding-logo')).toBeFalse(); expect(await utils.isPresent('#branding-logo')).to.be.false;
}); });
it('should HIDE the ROOM NAME', async () => { it('should HIDE the SESSION NAME', async () => {
await browser.get(`${url}&prejoin=false&displayRoomName=false`); await browser.get(`${url}&prejoin=false&displayRoomName=false`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
@ -486,10 +466,10 @@ describe('Testing API Directives', () => {
// Checking if toolbar is present // Checking if toolbar is present
await utils.waitForElement('#info-container'); await utils.waitForElement('#info-container');
expect(await utils.isPresent('#info-container')).toBeTrue(); expect(await utils.isPresent('#info-container')).to.be.true;
// Checking if session name is not displayed // Checking if session name is not displayed
expect(await utils.isPresent('#session-name')).toBeFalse(); expect(await utils.isPresent('#session-name')).to.be.false;
}); });
it('should HIDE the PARTICIPANT NAME', async () => { it('should HIDE the PARTICIPANT NAME', async () => {
@ -504,7 +484,7 @@ describe('Testing API Directives', () => {
await utils.checkStreamIsPresent(); await utils.checkStreamIsPresent();
// Checking if nickname is not present // Checking if nickname is not present
expect(await utils.isPresent('#participant-name-container')).toBeFalse(); expect(await utils.isPresent('#participant-name-container')).to.be.false;
}); });
it('should HIDE the AUDIO DETECTION element', async () => { it('should HIDE the AUDIO DETECTION element', async () => {
@ -519,7 +499,7 @@ describe('Testing API Directives', () => {
await utils.checkStreamIsPresent(); await utils.checkStreamIsPresent();
// Checking if audio detection is not present // Checking if audio detection is not present
expect(await utils.isPresent('#audio-wave-container')).toBeFalse(); expect(await utils.isPresent('#audio-wave-container')).to.be.false;
}); });
it('should HIDE the STREAM VIDEO CONTROLS button', async () => { it('should HIDE the STREAM VIDEO CONTROLS button', async () => {
@ -534,12 +514,12 @@ describe('Testing API Directives', () => {
await utils.checkStreamIsPresent(); await utils.checkStreamIsPresent();
// Checking if settings button is not present // Checking if settings button is not present
expect(await utils.isPresent('.stream-video-controls')).toBeFalse(); expect(await utils.isPresent('.stream-video-controls')).to.be.false;
}); });
it('should HIDE the MUTE button in participants panel', async () => { it('should HIDE the MUTE button in participants panel', async () => {
const roomName = 'e2etest'; const roomName = 'e2etest';
const fixedUrl = `${TestAppConfig.appUrl}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`; const fixedUrl = `${url}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
@ -552,15 +532,14 @@ describe('Testing API Directives', () => {
// Checking if participatns panel is displayed // Checking if participatns panel is displayed
await utils.waitForElement('#participants-container'); await utils.waitForElement('#participants-container');
expect(await utils.isPresent('#participants-container')).toBeTrue(); expect(await utils.isPresent('#participants-container')).to.be.true;
// Checking remote participants item // Checking remote participants item
expect(await utils.isPresent('#remote-participant-item')).toBeFalse(); expect(await utils.isPresent('#remote-participant-item')).to.be.false;
// Starting new browser for adding a new participant // Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedUrl}&participantName=SecondParticipant")`; const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript); await browser.executeScript(newTabScript);
await browser.sleep(10000);
// Go to first tab // Go to first tab
const tabs = await browser.getAllWindowHandles(); const tabs = await browser.getAllWindowHandles();
@ -568,9 +547,9 @@ describe('Testing API Directives', () => {
// Checking if mute button is not displayed in participant item // Checking if mute button is not displayed in participant item
await utils.waitForElement('#remote-participant-item'); await utils.waitForElement('#remote-participant-item');
expect(await utils.isPresent('#remote-participant-item')).toBeTrue(); expect(await utils.isPresent('#remote-participant-item')).to.be.true;
expect(await utils.isPresent('#mute-btn')).toBeFalse(); expect(await utils.isPresent('#mute-btn')).to.be.false;
}); });
it('should HIDE the RECORDING ACTIVITY in activities panel', async () => { it('should HIDE the RECORDING ACTIVITY in activities panel', async () => {
@ -588,13 +567,86 @@ describe('Testing API Directives', () => {
// Checking if participatns panel is displayed // Checking if participatns panel is displayed
await utils.waitForElement('#default-activities-panel'); await utils.waitForElement('#default-activities-panel');
expect(await utils.isPresent('#default-activities-panel')).toBeTrue(); expect(await utils.isPresent('#default-activities-panel')).to.be.true;
// await browser.sleep(1000); // await browser.sleep(1000);
// Checking if recording activity exists // Checking if recording activity exists
await utils.waitForElement('.activities-body-container'); await utils.waitForElement('.activities-body-container');
expect(await utils.isPresent('ov-recording-activity')).toBeFalse(); expect(await utils.isPresent('ov-recording-activity')).to.be.false;
});
it('should SHOW a RECORDING ERROR in activities panel', async () => {
let element;
const fixedUrl = `${url}&prejoin=false&recordingError=TEST_ERROR`;
await browser.get(fixedUrl);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
element = await utils.waitForElement('#activities-panel-btn');
await element.click();
// Checking if participatns panel is displayed
await utils.waitForElement('#default-activities-panel');
expect(await utils.isPresent('#default-activities-panel')).to.be.true;
// Checking if recording activity exists
await utils.waitForElement('#activities-container');
await utils.waitForElement('.activities-body-container');
await utils.waitForElement('ov-recording-activity');
expect(await utils.isPresent('ov-recording-activity')).to.be.true;
await utils.waitForElement('.failed');
expect(await utils.isPresent('.failed')).to.be.true;
// Open recording
await browser.sleep(500);
await utils.waitForElement('ov-recording-activity');
await utils.clickOn('ov-recording-activity');
await browser.sleep(500);
element = await utils.waitForElement('.recording-error');
expect(await element.getAttribute('innerText')).equal('"TEST_ERROR"');
expect(await utils.isPresent('.recording-error')).to.be.true;
});
it('should SHOW a BROADCASTING ERROR in activities panel', async () => {
let element;
const fixedUrl = `${url}&prejoin=false&broadcastingError=TEST_ERROR`;
await browser.get(fixedUrl);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
element = await utils.waitForElement('#activities-panel-btn');
await element.click();
// Checking if participatns panel is displayed
await utils.waitForElement('#default-activities-panel');
expect(await utils.isPresent('#default-activities-panel')).to.be.true;
// Checking if broadcasting activity exists
await utils.waitForElement('#activities-container');
await utils.waitForElement('.activities-body-container');
await utils.waitForElement('ov-broadcasting-activity');
expect(await utils.isPresent('ov-broadcasting-activity')).to.be.true;
const status = await utils.waitForElement('#broadcasting-status');
expect(await status.getAttribute('innerText')).equals('FAILED');
// Open broadcasting
await browser.sleep(500);
await utils.clickOn('ov-broadcasting-activity');
await browser.sleep(500);
element = await utils.waitForElement('#broadcasting-error');
expect(await element.getAttribute('innerText')).equal('TEST_ERROR');
}); });
it('should HIDE the BROADCASTING ACTIVITY in activities panel', async () => { it('should HIDE the BROADCASTING ACTIVITY in activities panel', async () => {
@ -610,12 +662,12 @@ describe('Testing API Directives', () => {
// Checking if participatns panel is displayed // Checking if participatns panel is displayed
await utils.waitForElement('#default-activities-panel'); await utils.waitForElement('#default-activities-panel');
expect(await utils.isPresent('#default-activities-panel')).toBeTrue(); expect(await utils.isPresent('#default-activities-panel')).to.be.true;
// await browser.sleep(1000); // await browser.sleep(1000);
// Checking if recording activity exists // Checking if recording activity exists
await utils.waitForElement('.activities-body-container'); await utils.waitForElement('.activities-body-container');
expect(await utils.isPresent('ov-broadcasting-activity')).toBeFalse(); expect(await utils.isPresent('ov-broadcasting-activity')).to.be.false;
}); });
}); });

View File

@ -1,8 +1,10 @@
import { expect } from 'chai';
import { Builder, Key, WebDriver } from 'selenium-webdriver'; import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
//TODO: Uncomment when captions are implemented //TODO: Uncomment when captions are implemented
// describe('Testing captions features', () => { // describe('Testing captions features', () => {
@ -10,10 +12,10 @@ const url = TestAppConfig.appUrl;
// let utils: OpenViduComponentsPO; // let utils: OpenViduComponentsPO;
// async function createChromeBrowser(): Promise<WebDriver> { // async function createChromeBrowser(): Promise<WebDriver> {
// return await new Builder() // return await new Builder()
// .forBrowser(TestAppConfig.browserName) // .forBrowser(WebComponentConfig.browserName)
// .withCapabilities(TestAppConfig.browserCapabilities) // .withCapabilities(WebComponentConfig.browserCapabilities)
// .setChromeOptions(TestAppConfig.browserOptions) // .setChromeOptions(WebComponentConfig.browserOptions)
// .usingServer(TestAppConfig.seleniumAddress) // .usingServer(WebComponentConfig.seleniumAddress)
// .build(); // .build();
// } // }
@ -41,11 +43,11 @@ const url = TestAppConfig.appUrl;
// // Checking if button panel is present // // Checking if button panel is present
// await utils.waitForElement('#more-options-menu'); // await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue(); // expect(await utils.isPresent('#more-options-menu')).to.be.true;
// // Checking if captions button is present // // Checking if captions button is present
// await utils.waitForElement('#captions-btn'); // await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue(); // expect(await utils.isPresent('#captions-btn')).to.be.true;
// await utils.clickOn('#captions-btn'); // await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container'); // await utils.waitForElement('.captions-container');
@ -66,11 +68,11 @@ const url = TestAppConfig.appUrl;
// // Checking if button panel is present // // Checking if button panel is present
// await utils.waitForElement('#more-options-menu'); // await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue(); // expect(await utils.isPresent('#more-options-menu')).to.be.true;
// // Checking if captions button is present // // Checking if captions button is present
// await utils.waitForElement('#captions-btn'); // await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue(); // expect(await utils.isPresent('#captions-btn')).to.be.true;
// await utils.clickOn('#captions-btn'); // await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container'); // await utils.waitForElement('.captions-container');
@ -80,12 +82,12 @@ const url = TestAppConfig.appUrl;
// await browser.sleep(500); // await browser.sleep(500);
// await utils.waitForElement('.settings-container'); // await utils.waitForElement('.settings-container');
// expect(await utils.isPresent('.settings-container')).toBeTrue(); // expect(await utils.isPresent('.settings-container')).to.be.true;
// await utils.waitForElement('ov-captions-settings'); // await utils.waitForElement('ov-captions-settings');
// // Expect caption button is not present // // Expect caption button is not present
// expect(await utils.isPresent('#caption-settings-btn')).toBeFalse(); // expect(await utils.isPresent('#caption-settings-btn')).to.be.false;
// }); // });
// it('should TOGGLE the CAPTIONS container from settings panel', async () => { // it('should TOGGLE the CAPTIONS container from settings panel', async () => {
@ -103,11 +105,11 @@ const url = TestAppConfig.appUrl;
// // Checking if button panel is present // // Checking if button panel is present
// await utils.waitForElement('#more-options-menu'); // await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue(); // expect(await utils.isPresent('#more-options-menu')).to.be.true;
// // Checking if captions button is present // // Checking if captions button is present
// await utils.waitForElement('#captions-btn'); // await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue(); // expect(await utils.isPresent('#captions-btn')).to.be.true;
// await utils.clickOn('#captions-btn'); // await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container'); // await utils.waitForElement('.captions-container');
@ -117,18 +119,18 @@ const url = TestAppConfig.appUrl;
// await browser.sleep(500); // await browser.sleep(500);
// await utils.waitForElement('.settings-container'); // await utils.waitForElement('.settings-container');
// expect(await utils.isPresent('.settings-container')).toBeTrue(); // expect(await utils.isPresent('.settings-container')).to.be.true;
// await utils.waitForElement('ov-captions-settings'); // await utils.waitForElement('ov-captions-settings');
// expect(await utils.isPresent('.captions-container')).toBeTrue(); // expect(await utils.isPresent('.captions-container')).to.be.true;
// await utils.clickOn('#captions-toggle-slide'); // await utils.clickOn('#captions-toggle-slide');
// expect(await utils.isPresent('.captions-container')).toBeFalse(); // expect(await utils.isPresent('.captions-container')).to.be.false;
// await browser.sleep(200); // await browser.sleep(200);
// await utils.clickOn('#captions-toggle-slide'); // await utils.clickOn('#captions-toggle-slide');
// expect(await utils.isPresent('.captions-container')).toBeTrue(); // expect(await utils.isPresent('.captions-container')).to.be.true;
// }); // });
// it('should change the CAPTIONS language', async () => { // it('should change the CAPTIONS language', async () => {
@ -146,11 +148,11 @@ const url = TestAppConfig.appUrl;
// // Checking if button panel is present // // Checking if button panel is present
// await utils.waitForElement('#more-options-menu'); // await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).toBeTrue(); // expect(await utils.isPresent('#more-options-menu')).to.be.true;
// // Checking if captions button is present // // Checking if captions button is present
// await utils.waitForElement('#captions-btn'); // await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).toBeTrue(); // expect(await utils.isPresent('#captions-btn')).to.be.true;
// await utils.clickOn('#captions-btn'); // await utils.clickOn('#captions-btn');
// await utils.waitForElement('.captions-container'); // await utils.waitForElement('.captions-container');
@ -160,11 +162,11 @@ const url = TestAppConfig.appUrl;
// await browser.sleep(500); // await browser.sleep(500);
// await utils.waitForElement('.settings-container'); // await utils.waitForElement('.settings-container');
// expect(await utils.isPresent('.settings-container')).toBeTrue(); // expect(await utils.isPresent('.settings-container')).to.be.true;
// await utils.waitForElement('ov-captions-settings'); // await utils.waitForElement('ov-captions-settings');
// expect(await utils.isPresent('.captions-container')).toBeTrue(); // expect(await utils.isPresent('.captions-container')).to.be.true;
// await utils.clickOn('.lang-button'); // await utils.clickOn('.lang-button');
// await browser.sleep(500); // await browser.sleep(500);
@ -173,7 +175,7 @@ const url = TestAppConfig.appUrl;
// await utils.clickOn('.panel-close-button'); // await utils.clickOn('.panel-close-button');
// const button = await utils.waitForElement('#caption-settings-btn'); // const button = await utils.waitForElement('#caption-settings-btn');
// expect(await button.getText()).toEqual('settingsEspañol'); // expect(await button.getText()).equals('settingsEspañol');
// }); // });
// }); // });

View File

@ -1,8 +1,10 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Testing CHAT features', () => { describe('Testing CHAT features', () => {
let browser: WebDriver; let browser: WebDriver;
@ -10,10 +12,10 @@ describe('Testing CHAT features', () => {
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -23,10 +25,6 @@ describe('Testing CHAT features', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
// leaving room if connected
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -40,7 +38,7 @@ describe('Testing CHAT features', () => {
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container'); await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue(); expect(await utils.isPresent('.input-container')).to.be.true;
const input = await utils.waitForElement('#chat-input'); const input = await utils.waitForElement('#chat-input');
await input.sendKeys('Test message'); await input.sendKeys('Test message');
@ -49,13 +47,13 @@ describe('Testing CHAT features', () => {
await utils.waitForElement('.message'); await utils.waitForElement('.message');
await utils.getNumberOfElements('.message'); await utils.getNumberOfElements('.message');
expect(await utils.isPresent('.message')).toBeTrue(); expect(await utils.isPresent('.message')).to.be.true;
expect(await utils.getNumberOfElements('.message')).toEqual(1); expect(await utils.getNumberOfElements('.message')).equals(1);
await input.sendKeys('Test message'); await input.sendKeys('Test message');
await utils.clickOn('#send-btn'); await utils.clickOn('#send-btn');
expect(await utils.getNumberOfElements('.message')).toEqual(2); expect(await utils.getNumberOfElements('.message')).equals(2);
}); });
it('should receive a message', async () => { it('should receive a message', async () => {
@ -63,7 +61,6 @@ describe('Testing CHAT features', () => {
let pName = `participant${Math.floor(Math.random() * 1000)}`; let pName = `participant${Math.floor(Math.random() * 1000)}`;
const fixedUrl = `${url}&prejoin=false&roomName=${roomName}`; const fixedUrl = `${url}&prejoin=false&roomName=${roomName}`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
await browser.sleep(1000);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Starting new browser for adding a new participant // Starting new browser for adding a new participant
@ -79,7 +76,7 @@ describe('Testing CHAT features', () => {
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container'); await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue(); expect(await utils.isPresent('.input-container')).to.be.true;
const input = await utils.waitForElement('#chat-input'); const input = await utils.waitForElement('#chat-input');
await input.sendKeys('test message'); await input.sendKeys('test message');
@ -93,8 +90,8 @@ describe('Testing CHAT features', () => {
await browser.sleep(1000); await browser.sleep(1000);
await utils.waitForElement('.message'); await utils.waitForElement('.message');
const participantName = await utils.waitForElement('.participant-name-container>p'); const participantName = await utils.waitForElement('.participant-name-container>p');
expect(await utils.getNumberOfElements('.message')).toEqual(1); expect(await utils.getNumberOfElements('.message')).equals(1);
expect(await participantName.getText()).toEqual(pName); expect(await participantName.getText()).equals(pName);
}); });
it('should send an url message and converts in a link', async () => { it('should send an url message and converts in a link', async () => {
@ -107,14 +104,14 @@ describe('Testing CHAT features', () => {
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container'); await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue(); expect(await utils.isPresent('.input-container')).to.be.true;
const input = await utils.waitForElement('#chat-input'); const input = await utils.waitForElement('#chat-input');
await input.sendKeys('demos.openvidu.io'); await input.sendKeys('demos.openvidu.io');
await utils.clickOn('#send-btn'); await utils.clickOn('#send-btn');
await utils.waitForElement('.chat-message a'); await utils.waitForElement('.msg-content a');
expect(await utils.isPresent('.chat-message a')).toBeTrue(); expect(await utils.isPresent('.msg-content a')).to.be.true;
}); });
}); });

View File

@ -1,19 +1,21 @@
import { expect } from 'chai';
import { Builder, Key, WebDriver } from 'selenium-webdriver'; import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Testing videoconference EVENTS', () => { describe('Testing videoconference EVENTS', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
const isHeadless: boolean = (TestAppConfig.browserOptions as any).options_.args.includes('--headless'); const isHeadless: boolean = (WebComponentConfig.browserOptions as any).options_.args.includes('--headless');
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -23,10 +25,6 @@ describe('Testing videoconference EVENTS', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
// leaving room if connected
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -34,7 +32,7 @@ describe('Testing videoconference EVENTS', () => {
await browser.get(`${url}`); await browser.get(`${url}`);
await utils.waitForElement('#prejoin-container'); await utils.waitForElement('#prejoin-container');
expect(await utils.isPresent('#prejoin-container')).toBeTrue(); expect(await utils.isPresent('#prejoin-container')).to.be.true;
// Clicking to join button // Clicking to join button
await utils.waitForElement('#join-button'); await utils.waitForElement('#join-button');
@ -42,14 +40,14 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onReadyToJoin has been received // Checking if onReadyToJoin has been received
await utils.waitForElement('#onReadyToJoin'); await utils.waitForElement('#onReadyToJoin');
expect(await utils.isPresent('#onReadyToJoin')).toBeTrue(); expect(await utils.isPresent('#onReadyToJoin')).to.be.true;
}); });
it('should receive the onTokenRequested event', async () => { it('should receive the onTokenRequested event', async () => {
await browser.get(`${url}`); await browser.get(`${url}`);
await utils.waitForElement('#prejoin-container'); await utils.waitForElement('#prejoin-container');
expect(await utils.isPresent('#prejoin-container')).toBeTrue(); expect(await utils.isPresent('#prejoin-container')).to.be.true;
// Clicking to join button // Clicking to join button
await utils.waitForElement('#join-button'); await utils.waitForElement('#join-button');
@ -57,7 +55,24 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onTokenRequested has been received // Checking if onTokenRequested has been received
await utils.waitForElement('#onTokenRequested'); await utils.waitForElement('#onTokenRequested');
expect(await utils.isPresent('#onTokenRequested')).toBeTrue(); expect(await utils.isPresent('#onTokenRequested')).to.be.true;
});
it('should receive the onRoomDisconnected event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
const leaveButton = await utils.waitForElement('#leave-btn');
expect(await utils.isPresent('#leave-btn')).to.be.true;
await leaveButton.click();
// Checking if onRoomDisconnected has been received
await utils.waitForElement('#onRoomDisconnected');
expect(await utils.isPresent('#onRoomDisconnected')).to.be.true;
}); });
it('should receive the onVideoEnabledChanged event when clicking on the prejoin', async () => { it('should receive the onVideoEnabledChanged event when clicking on the prejoin', async () => {
@ -69,7 +84,7 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onVideoEnabledChanged has been received // Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false'); await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onVideoEnabledChanged-false')).to.be.true;
}); });
it('should receive the onVideoEnabledChanged event when clicking on the toolbar', async () => { it('should receive the onVideoEnabledChanged event when clicking on the toolbar', async () => {
@ -85,11 +100,11 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onVideoEnabledChanged has been received // Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false'); await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onVideoEnabledChanged-false')).to.be.true;
await utils.clickOn('#camera-btn'); await utils.clickOn('#camera-btn');
await utils.waitForElement('#onVideoEnabledChanged-true'); await utils.waitForElement('#onVideoEnabledChanged-true');
expect(await utils.isPresent('#onVideoEnabledChanged-true')).toBeTrue(); expect(await utils.isPresent('#onVideoEnabledChanged-true')).to.be.true;
}); });
it('should receive the onVideoEnabledChanged event when clicking on the settings panel', async () => { it('should receive the onVideoEnabledChanged event when clicking on the settings panel', async () => {
@ -108,11 +123,11 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('ov-video-devices-select #camera-button'); await utils.clickOn('ov-video-devices-select #camera-button');
// Checking if onVideoEnabledChanged has been received // Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false'); await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onVideoEnabledChanged-false')).to.be.true;
await utils.clickOn('ov-video-devices-select #camera-button'); await utils.clickOn('ov-video-devices-select #camera-button');
await utils.waitForElement('#onVideoEnabledChanged-true'); await utils.waitForElement('#onVideoEnabledChanged-true');
expect(await utils.isPresent('#onVideoEnabledChanged-true')).toBeTrue(); expect(await utils.isPresent('#onVideoEnabledChanged-true')).to.be.true;
}); });
it('should receive the onVideoDeviceChanged event on prejoin', async () => { it('should receive the onVideoDeviceChanged event on prejoin', async () => {
@ -126,7 +141,7 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('#option-custom_fake_video_1'); await utils.clickOn('#option-custom_fake_video_1');
await utils.waitForElement('#onVideoDeviceChanged'); await utils.waitForElement('#onVideoDeviceChanged');
expect(await utils.isPresent('#onVideoDeviceChanged')).toBeTrue(); expect(await utils.isPresent('#onVideoDeviceChanged')).to.be.true;
}); });
it('should receive the onVideoDeviceChanged event on settings panel', async () => { it('should receive the onVideoDeviceChanged event on settings panel', async () => {
@ -149,7 +164,7 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('#option-custom_fake_video_1'); await utils.clickOn('#option-custom_fake_video_1');
await utils.waitForElement('#onVideoDeviceChanged'); await utils.waitForElement('#onVideoDeviceChanged');
expect(await utils.isPresent('#onVideoDeviceChanged')).toBeTrue(); expect(await utils.isPresent('#onVideoDeviceChanged')).to.be.true;
}); });
it('should receive the onAudioEnabledChanged event when clicking on the prejoin', async () => { it('should receive the onAudioEnabledChanged event when clicking on the prejoin', async () => {
@ -161,7 +176,7 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onAudioEnabledChanged has been received // Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false'); await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onAudioEnabledChanged-false')).to.be.true;
}); });
it('should receive the onAudioEnabledChanged event when clicking on the toolbar', async () => { it('should receive the onAudioEnabledChanged event when clicking on the toolbar', async () => {
@ -177,11 +192,11 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onAudioEnabledChanged has been received // Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false'); await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onAudioEnabledChanged-false')).to.be.true;
await utils.clickOn('#mic-btn'); await utils.clickOn('#mic-btn');
await utils.waitForElement('#onAudioEnabledChanged-true'); await utils.waitForElement('#onAudioEnabledChanged-true');
expect(await utils.isPresent('#onAudioEnabledChanged-true')).toBeTrue(); expect(await utils.isPresent('#onAudioEnabledChanged-true')).to.be.true;
}); });
it('should receive the onAudioEnabledChanged event when clicking on the settings panel', async () => { it('should receive the onAudioEnabledChanged event when clicking on the settings panel', async () => {
@ -200,11 +215,11 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('ov-audio-devices-select #microphone-button'); await utils.clickOn('ov-audio-devices-select #microphone-button');
// Checking if onAudioEnabledChanged has been received // Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false'); await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onAudioEnabledChanged-false')).to.be.true;
await utils.clickOn('ov-audio-devices-select #microphone-button'); await utils.clickOn('ov-audio-devices-select #microphone-button');
await utils.waitForElement('#onAudioEnabledChanged-true'); await utils.waitForElement('#onAudioEnabledChanged-true');
expect(await utils.isPresent('#onAudioEnabledChanged-true')).toBeTrue(); expect(await utils.isPresent('#onAudioEnabledChanged-true')).to.be.true;
}); });
it('should receive the onAudioDeviceChanged event on prejoin', async () => { it('should receive the onAudioDeviceChanged event on prejoin', async () => {
@ -218,7 +233,7 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('#option-custom_fake_audio_1'); await utils.clickOn('#option-custom_fake_audio_1');
await utils.waitForElement('#onAudioDeviceChanged'); await utils.waitForElement('#onAudioDeviceChanged');
expect(await utils.isPresent('#onAudioDeviceChanged')).toBeTrue(); expect(await utils.isPresent('#onAudioDeviceChanged')).to.be.true;
}); });
it('should receive the onAudioDeviceChanged event on settings panel', async () => { it('should receive the onAudioDeviceChanged event on settings panel', async () => {
@ -241,7 +256,7 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('#option-custom_fake_audio_1'); await utils.clickOn('#option-custom_fake_audio_1');
await utils.waitForElement('#onAudioDeviceChanged'); await utils.waitForElement('#onAudioDeviceChanged');
expect(await utils.isPresent('#onAudioDeviceChanged')).toBeTrue(); expect(await utils.isPresent('#onAudioDeviceChanged')).to.be.true;
}); });
it('should receive the onLangChanged event on prejoin', async () => { it('should receive the onLangChanged event on prejoin', async () => {
@ -256,7 +271,7 @@ describe('Testing videoconference EVENTS', () => {
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('#onLangChanged-es'); await utils.waitForElement('#onLangChanged-es');
expect(await utils.isPresent('#onLangChanged-es')).toBeTrue(); expect(await utils.isPresent('#onLangChanged-es')).to.be.true;
}); });
it('should receive the onLangChanged event on settings panel', async () => { it('should receive the onLangChanged event on settings panel', async () => {
@ -277,7 +292,7 @@ describe('Testing videoconference EVENTS', () => {
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('#onLangChanged-es'); await utils.waitForElement('#onLangChanged-es');
expect(await utils.isPresent('#onLangChanged-es')).toBeTrue(); expect(await utils.isPresent('#onLangChanged-es')).to.be.true;
}); });
it('should receive the onScreenShareEnabledChanged event', async () => { it('should receive the onScreenShareEnabledChanged event', async () => {
@ -289,16 +304,16 @@ describe('Testing videoconference EVENTS', () => {
// Clicking to leave button // Clicking to leave button
const screenshareButton = await utils.waitForElement('#screenshare-btn'); const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await utils.isPresent('#screenshare-btn')).toBeTrue(); expect(await utils.isPresent('#screenshare-btn')).to.be.true;
await screenshareButton.click(); await screenshareButton.click();
// Checking if onScreenShareEnabledChanged has been received // Checking if onScreenShareEnabledChanged has been received
await utils.waitForElement('#onScreenShareEnabledChanged'); await utils.waitForElement('#onScreenShareEnabledChanged');
expect(await utils.isPresent('#onScreenShareEnabledChanged')).toBeTrue(); expect(await utils.isPresent('#onScreenShareEnabledChanged')).to.be.true;
}); });
// With headless mode, the Fullscreen API doesn't work // With headless mode, the Fullscreen API doesn't work
it('should receive the onFullscreenEnabledChanged event', async () => { (isHeadless ? it.skip : it)('should receive the onFullscreenEnabledChanged event', async () => {
let element; let element;
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
@ -311,13 +326,13 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onFullscreenEnabledChanged has been received // Checking if onFullscreenEnabledChanged has been received
await utils.waitForElement('#onFullscreenEnabledChanged-true'); await utils.waitForElement('#onFullscreenEnabledChanged-true');
expect(await utils.isPresent('#onFullscreenEnabledChanged-true')).toBeTrue(); expect(await utils.isPresent('#onFullscreenEnabledChanged-true')).to.be.true;
await (await utils.waitForElement('html')).sendKeys(Key.F11); await (await utils.waitForElement('html')).sendKeys(Key.F11);
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('#onFullscreenEnabledChanged-false'); await utils.waitForElement('#onFullscreenEnabledChanged-false');
expect(await utils.isPresent('#onFullscreenEnabledChanged-false')).toBeTrue(); expect(await utils.isPresent('#onFullscreenEnabledChanged-false')).to.be.true;
}); });
it('should receive the onChatPanelStatusChanged event', async () => { it('should receive the onChatPanelStatusChanged event', async () => {
@ -331,13 +346,13 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onChatPanelStatusChanged has been received // Checking if onChatPanelStatusChanged has been received
await utils.waitForElement('#onChatPanelStatusChanged-true'); await utils.waitForElement('#onChatPanelStatusChanged-true');
expect(await utils.isPresent('#onChatPanelStatusChanged-true')).toBeTrue(); expect(await utils.isPresent('#onChatPanelStatusChanged-true')).to.be.true;
await utils.togglePanel('chat'); await utils.togglePanel('chat');
// Checking if onChatPanelStatusChanged has been received // Checking if onChatPanelStatusChanged has been received
await utils.waitForElement('#onChatPanelStatusChanged-false'); await utils.waitForElement('#onChatPanelStatusChanged-false');
expect(await utils.isPresent('#onChatPanelStatusChanged-false')).toBeTrue(); expect(await utils.isPresent('#onChatPanelStatusChanged-false')).to.be.true;
}); });
it('should receive the onParticipantsPanelStatusChanged event', async () => { it('should receive the onParticipantsPanelStatusChanged event', async () => {
@ -351,13 +366,13 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onParticipantsPanelStatusChanged has been received // Checking if onParticipantsPanelStatusChanged has been received
await utils.waitForElement('#onParticipantsPanelStatusChanged-true'); await utils.waitForElement('#onParticipantsPanelStatusChanged-true');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged-true')).toBeTrue(); expect(await utils.isPresent('#onParticipantsPanelStatusChanged-true')).to.be.true;
await utils.togglePanel('participants'); await utils.togglePanel('participants');
// Checking if onParticipantsPanelStatusChanged has been received // Checking if onParticipantsPanelStatusChanged has been received
await utils.waitForElement('#onParticipantsPanelStatusChanged-false'); await utils.waitForElement('#onParticipantsPanelStatusChanged-false');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged-false')).toBeTrue(); expect(await utils.isPresent('#onParticipantsPanelStatusChanged-false')).to.be.true;
}); });
it('should receive the onActivitiesPanelStatusChanged event', async () => { it('should receive the onActivitiesPanelStatusChanged event', async () => {
@ -370,13 +385,13 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onActivitiesPanelStatusChanged has been received // Checking if onActivitiesPanelStatusChanged has been received
await utils.waitForElement('#onActivitiesPanelStatusChanged-true'); await utils.waitForElement('#onActivitiesPanelStatusChanged-true');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged-true')).toBeTrue(); expect(await utils.isPresent('#onActivitiesPanelStatusChanged-true')).to.be.true;
await utils.togglePanel('activities'); await utils.togglePanel('activities');
// Checking if onActivitiesPanelStatusChanged has been received // Checking if onActivitiesPanelStatusChanged has been received
await utils.waitForElement('#onActivitiesPanelStatusChanged-false'); await utils.waitForElement('#onActivitiesPanelStatusChanged-false');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged-false')).toBeTrue(); expect(await utils.isPresent('#onActivitiesPanelStatusChanged-false')).to.be.true;
}); });
it('should receive the onSettingsPanelStatusChanged event', async () => { it('should receive the onSettingsPanelStatusChanged event', async () => {
@ -389,13 +404,13 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onSettingsPanelStatusChanged has been received // Checking if onSettingsPanelStatusChanged has been received
await utils.waitForElement('#onSettingsPanelStatusChanged-true'); await utils.waitForElement('#onSettingsPanelStatusChanged-true');
expect(await utils.isPresent('#onSettingsPanelStatusChanged-true')).toBeTrue(); expect(await utils.isPresent('#onSettingsPanelStatusChanged-true')).to.be.true;
await utils.togglePanel('settings'); await utils.togglePanel('settings');
// Checking if onSettingsPanelStatusChanged has been received // Checking if onSettingsPanelStatusChanged has been received
await utils.waitForElement('#onSettingsPanelStatusChanged-false'); await utils.waitForElement('#onSettingsPanelStatusChanged-false');
expect(await utils.isPresent('#onSettingsPanelStatusChanged-false')).toBeTrue(); expect(await utils.isPresent('#onSettingsPanelStatusChanged-false')).to.be.true;
}); });
it('should receive the onRecordingStartRequested event when clicking toolbar button', async () => { it('should receive the onRecordingStartRequested event when clicking toolbar button', async () => {
@ -409,7 +424,7 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onRecordingStartRequested has been received // Checking if onRecordingStartRequested has been received
await utils.waitForElement(`#onRecordingStartRequested-${roomName}`); await utils.waitForElement(`#onRecordingStartRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).toBeTrue(); expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).to.be.true;
}); });
xit('should receive the onRecordingStopRequested event when clicking toolbar button', async () => {}); xit('should receive the onRecordingStopRequested event when clicking toolbar button', async () => {});
@ -443,7 +458,7 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onBroadcastingStopRequested has been received // Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested'); await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue(); expect(await utils.isPresent('#onBroadcastingStopRequested')).to.be.true;
}); });
it('should receive the onRecordingStartRequested when clicking from activities panel', async () => { it('should receive the onRecordingStartRequested when clicking from activities panel', async () => {
@ -469,7 +484,7 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onRecordingStartRequested has been received // Checking if onRecordingStartRequested has been received
await utils.waitForElement(`#onRecordingStartRequested-${roomName}`); await utils.waitForElement(`#onRecordingStartRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).toBeTrue(); expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).to.be.true;
}); });
xit('should receive the onRecordingStopRequested when clicking from activities panel', async () => {}); xit('should receive the onRecordingStopRequested when clicking from activities panel', async () => {});
@ -485,7 +500,7 @@ describe('Testing videoconference EVENTS', () => {
// Clicking to activities button // Clicking to activities button
const activitiesButton = await utils.waitForElement('#activities-panel-btn'); const activitiesButton = await utils.waitForElement('#activities-panel-btn');
expect(await utils.isPresent('#activities-panel-btn')).toBeTrue(); expect(await utils.isPresent('#activities-panel-btn')).to.be.true;
await activitiesButton.click(); await activitiesButton.click();
await browser.sleep(1500); await browser.sleep(1500);
@ -497,15 +512,15 @@ describe('Testing videoconference EVENTS', () => {
// Delete event // Delete event
element = await utils.waitForElement('#delete-recording-btn'); element = await utils.waitForElement('#delete-recording-btn');
expect(await utils.isPresent('#delete-recording-btn')).toBeTrue(); expect(await utils.isPresent('#delete-recording-btn')).to.be.true;
await element.click(); await element.click();
element = await utils.waitForElement('#delete-recording-confirm-btn'); element = await utils.waitForElement('#delete-recording-confirm-btn');
expect(await utils.isPresent('#delete-recording-confirm-btn')).toBeTrue(); expect(await utils.isPresent('#delete-recording-confirm-btn')).to.be.true;
await element.click(); await element.click();
await utils.waitForElement(`#onRecordingDeleteRequested-${roomName}-fakeRecording`); await utils.waitForElement(`#onRecordingDeleteRequested-${roomName}-fakeRecording`);
expect(await utils.isPresent(`#onRecordingDeleteRequested-${roomName}-fakeRecording`)).toBeTrue(); expect(await utils.isPresent(`#onRecordingDeleteRequested-${roomName}-fakeRecording`)).to.be.true;
}); });
it('should receive the onBroadcastingStartRequested event when clicking from panel', async () => { it('should receive the onBroadcastingStartRequested event when clicking from panel', async () => {
@ -525,7 +540,7 @@ describe('Testing videoconference EVENTS', () => {
await browser.sleep(1000); await browser.sleep(1000);
const button = await utils.waitForElement('#broadcasting-btn'); const button = await utils.waitForElement('#broadcasting-btn');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
const input = await utils.waitForElement('#broadcast-url-input'); const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys(broadcastUrl); await input.sendKeys(broadcastUrl);
@ -534,7 +549,7 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onBroadcastingStartRequested has been received // Checking if onBroadcastingStartRequested has been received
await utils.waitForElement(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`); await utils.waitForElement(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`);
expect(await utils.isPresent(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`)).toBeTrue(); expect(await utils.isPresent(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`)).to.be.true;
}); });
xit('should receive the onBroadcastingStopRequested event when clicking from panel', async () => { xit('should receive the onBroadcastingStopRequested event when clicking from panel', async () => {
@ -550,21 +565,21 @@ describe('Testing videoconference EVENTS', () => {
await utils.clickOn('#broadcasting-activity'); await utils.clickOn('#broadcasting-activity');
const button = await utils.waitForElement('#broadcasting-btn'); const button = await utils.waitForElement('#broadcasting-btn');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
const input = await utils.waitForElement('#broadcast-url-input'); const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys('BroadcastUrl'); await input.sendKeys('BroadcastUrl');
await utils.clickOn('#broadcasting-btn'); await utils.clickOn('#broadcasting-btn');
expect(await utils.isPresent('#broadcasting-tag')).toBeTrue(); expect(await utils.isPresent('#broadcasting-tag')).to.be.true;
await utils.clickOn('#stop-broadcasting-btn'); await utils.clickOn('#stop-broadcasting-btn');
// Checking if onBroadcastingStopRequested has been received // Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested'); await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue(); expect(await utils.isPresent('#onBroadcastingStopRequested')).to.be.true;
expect(await utils.isPresent('#broadcasting-tag')).toBeFalse(); expect(await utils.isPresent('#broadcasting-tag')).to.be.false;
}); });
xit('should receive the onBroadcastingStopRequested event when clicking from toolbar', async () => { xit('should receive the onBroadcastingStopRequested event when clicking from toolbar', async () => {
@ -582,8 +597,8 @@ describe('Testing videoconference EVENTS', () => {
// Checking if onBroadcastingStopRequested has been received // Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested'); await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue(); expect(await utils.isPresent('#onBroadcastingStopRequested')).to.be.true;
expect(await utils.isPresent('#broadcasting-tag')).toBeFalse(); expect(await utils.isPresent('#broadcasting-tag')).to.be.false;
}); });
it('should receive the onRoomCreated event', async () => { it('should receive the onRoomCreated event', async () => {
@ -594,53 +609,36 @@ describe('Testing videoconference EVENTS', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
await utils.waitForElement('#onRoomCreated'); await utils.waitForElement('#onRoomCreated');
expect(await utils.isPresent('#onRoomCreated')).toBeTrue(); expect(await utils.isPresent('#onRoomCreated')).to.be.true;
expect(await utils.isPresent('#onReadyToJoin')).toBeFalse(); expect(await utils.isPresent('#onReadyToJoin')).to.be.false;
}); });
// PARTICIPANT EVENTS // * PUBLISHER EVENTS
it('should receive onParticipantCreated event from LOCAL participant', async () => { it('should receive onParticipantCreated event from LOCAL participant', async () => {
const participantName = 'TEST_USER'; const participantName = 'TEST_USER';
await browser.get(`${url}&participantName=${participantName}&prejoin=false`); await browser.get(`${url}&participantName=${participantName}&prejoin=false`);
await utils.waitForElement(`#${participantName}-onParticipantCreated`); await utils.waitForElement(`#${participantName}-onParticipantCreated`);
expect(await utils.isPresent(`#${participantName}-onParticipantCreated`)).toBeTrue(); expect(await utils.isPresent(`#${participantName}-onParticipantCreated`)).to.be.true;
}); });
it('should receive the onParticipantLeft event', async () => { // * ROOM EVENTS
await browser.get(`${url}&prejoin=false&redirectToHome=false`);
it('should receive roomDisconnected event from LOCAL participant', async () => {
const participantName = 'TEST_USER';
let element;
await browser.get(`${url}&prejoin=false&participantName=${participantName}`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Clicking to leave button // Checking if leave button is not present
const leaveButton = await utils.waitForElement('#leave-btn'); element = await utils.waitForElement('#leave-btn');
expect(await utils.isPresent('#leave-btn')).toBeTrue(); await element.click();
await leaveButton.click();
await utils.waitForElement('#events'); await utils.waitForElement(`#roomDisconnected`);
// Checking if onParticipantLeft has been received expect(await utils.isPresent(`#roomDisconnected`)).to.be.true;
await utils.waitForElement('#onParticipantLeft');
expect(await utils.isPresent('#onParticipantLeft')).toBeTrue();
}); });
// * ROOM EVENTS
//TODO: Implement a mechanism to emulate network disconnection
// it('should receive the onRoomDisconnected event', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// await utils.checkToolbarIsPresent();
// // Emulate network disconnection
// await utils.forceCloseWebsocket();
// // Checking if onRoomDisconnected has been received
// await utils.waitForElement('#onRoomDisconnected');
// expect(await utils.isPresent('#onRoomDisconnected')).toBeTrue();
// });
}); });

View File

@ -1,18 +1,20 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { getBrowserOptionsWithoutDevices, TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { getBrowserOptionsWithoutDevices, WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Media Devices: Virtual Device Replacement and Permissions Handling', () => { describe('Testing replace track with emulated devices', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -22,99 +24,118 @@ describe('Media Devices: Virtual Device Replacement and Permissions Handling', (
}); });
afterEach(async () => { afterEach(async () => {
try { // console.log('data:image/png;base64,' + await browser.takeScreenshot());
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
it('should allow selecting and replacing the video track with a custom virtual device in the prejoin page', async () => { it('should replace the video track in prejoin page', async () => {
const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;'; const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;';
await browser.get(`${url}&fakeDevices=true`); await browser.get(`${url}&fakeDevices=true`);
let videoDevices = await utils.waitForElement('#video-devices-form'); let videoDevices = await utils.waitForElement('#video-devices-form');
await videoDevices.click();
let element = await utils.waitForElement('#option-custom_fake_video_1');
await element.click();
let videoLabel;
await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('custom_fake_video_1');
await videoDevices.click(); await videoDevices.click();
let element = await utils.waitForElement('#option-custom_fake_video_1');
await element.click();
let videoLabel;
await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).to.be.equal('custom_fake_video_1');
await videoDevices.click();
element = await utils.waitForElement('#option-fake_device_0'); element = await utils.waitForElement('#option-fake_device_0');
await element.click(); await element.click();
await browser.sleep(1000); await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script); videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('fake_device_0'); expect(videoLabel).to.be.equal('fake_device_0');
}); });
it('should allow selecting and replacing the video track with a custom virtual device in the videoconference page', async () => { it('should replace the video track in videoconference page', async () => {
const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;'; const script = 'return document.getElementsByTagName("video")[0].srcObject.getVideoTracks()[0].label;';
await browser.get(`${url}&prejoin=false&fakeDevices=true`); await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
await utils.togglePanel('settings'); await utils.togglePanel('settings');
await utils.waitForElement('.settings-container'); await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).toBeTrue(); expect(await utils.isPresent('.settings-container')).to.be.true;
await browser.sleep(500);
await utils.clickOn('#video-opt'); await utils.clickOn('#video-opt');
expect(await utils.isPresent('ov-video-devices-select')).toBeTrue(); expect(await utils.isPresent('ov-video-devices-select')).to.be.true;
let videoDevices = await utils.waitForElement('#video-devices-form'); let videoDevices = await utils.waitForElement('#video-devices-form');
await videoDevices.click(); await videoDevices.click();
let element = await utils.waitForElement('#option-custom_fake_video_1'); let element = await utils.waitForElement('#option-custom_fake_video_1');
await element.click(); await element.click();
let videoLabel; let videoLabel;
await browser.sleep(1000); await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script); videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('custom_fake_video_1'); expect(videoLabel).to.be.equal('custom_fake_video_1');
await videoDevices.click(); await videoDevices.click();
element = await utils.waitForElement('#option-fake_device_0'); element = await utils.waitForElement('#option-fake_device_0');
await element.click(); await element.click();
await browser.sleep(1000); await browser.sleep(1000);
videoLabel = await browser.executeScript<string>(script); videoLabel = await browser.executeScript<string>(script);
expect(videoLabel).toEqual('fake_device_0'); expect(videoLabel).to.be.equal('fake_device_0');
}); });
it('should replace the screen track with a custom virtual device', async () => { // TODO: Uncommented when Livekit allows to replace the screen track
const script = 'return document.getElementsByClassName("OV_video-element screen-type")[0].srcObject.getVideoTracks()[0].label;'; // it('should replace the screen track', async () => {
// const script = 'return document.getElementsByClassName("OV_video-element screen-type")[0].srcObject.getVideoTracks()[0].label;';
await browser.get(`${url}&prejoin=false&fakeDevices=true`); // await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkLayoutPresent(); // await utils.checkLayoutPresent();
await utils.checkToolbarIsPresent(); // await utils.checkToolbarIsPresent();
await utils.clickOn('#screenshare-btn'); // await utils.clickOn('#screenshare-btn');
await browser.sleep(500); // await browser.sleep(500);
let screenLabel = await browser.executeScript<string>(script); // let screenLabel = await browser.executeScript<string>(script);
expect(screenLabel).not.toEqual('custom_fake_screen'); // expect(screenLabel).not.equal('custom_fake_screen');
await utils.clickOn('#screenshare-btn'); // await utils.clickOn('#video-settings-btn-SCREEN');
await browser.sleep(500); // await browser.sleep(500);
await utils.waitForElement('#replace-screen-button'); // await utils.waitForElement('.video-settings-menu');
await utils.clickOn('#replace-screen-button'); // const replaceBtn = await utils.waitForElement('#replace-screen-button');
await browser.sleep(1000); // await replaceBtn.sendKeys(Key.ENTER);
screenLabel = await browser.executeScript<string>(script); // await browser.sleep(1000);
expect(screenLabel).toEqual('custom_fake_screen'); // screenLabel = await browser.executeScript<string>(script);
}); // expect(screenLabel).to.be.equal('custom_fake_screen');
// });
}); });
describe('Media Devices: UI Behavior Without Media Device Permissions', () => { describe('Testing WITHOUT MEDIA DEVICES permissions', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(getBrowserOptionsWithoutDevices()) .setChromeOptions(getBrowserOptionsWithoutDevices())
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -124,57 +145,75 @@ describe('Media Devices: UI Behavior Without Media Device Permissions', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
it('should disable camera and microphone buttons in the prejoin page when permissions are denied', async () => { it('should be able to ACCESS to PREJOIN page', async () => {
await browser.get(`${url}`); await browser.get(`${url}`);
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
let button = await utils.waitForElement('#camera-button'); let button = await utils.waitForElement('#camera-button');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
button = await utils.waitForElement('#microphone-button'); button = await utils.waitForElement('#microphone-button');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
}); });
it('should disable camera and microphone buttons in the room page when permissions are denied', async () => { it('should be able to ACCESS to ROOM page', async () => {
await browser.get(`${url}`); await browser.get(`${url}`);
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button'); await utils.clickOn('#join-button');
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
let button = await utils.waitForElement('#camera-btn'); let button = await utils.waitForElement('#camera-btn');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
button = await utils.waitForElement('#mic-btn'); button = await utils.waitForElement('#mic-btn');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
}); });
it('should disable camera and microphone buttons in the room page without prejoin when permissions are denied', async () => { it('should be able to ACCESS to ROOM page without prejoin', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
let button = await utils.waitForElement('#camera-btn'); let button = await utils.waitForElement('#camera-btn');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
button = await utils.waitForElement('#mic-btn'); button = await utils.waitForElement('#mic-btn');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
}); });
it('should disable camera and microphone device selection buttons in settings when permissions are denied', async () => { it('should the settings buttons be disabled', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Open more options menu
await utils.togglePanel('settings'); await utils.togglePanel('settings');
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('.settings-container'); await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).toBeTrue(); expect(await utils.isPresent('.settings-container')).to.be.true;
await utils.clickOn('#video-opt'); await utils.clickOn('#video-opt');
expect(await utils.isPresent('ov-video-devices-select')).toBeTrue(); expect(await utils.isPresent('ov-video-devices-select')).to.be.true;
let button = await utils.waitForElement('#camera-button'); let button = await utils.waitForElement('#camera-button');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
await utils.clickOn('#audio-opt'); await utils.clickOn('#audio-opt');
expect(await utils.isPresent('ov-audio-devices-select')).toBeTrue(); expect(await utils.isPresent('ov-audio-devices-select')).to.be.true;
button = await utils.waitForElement('#microphone-button'); button = await utils.waitForElement('#microphone-button');
expect(await button.isEnabled()).toBeFalse(); expect(await button.isEnabled()).to.be.false;
}); });
}); });

View File

@ -1,19 +1,21 @@
import { Builder, WebDriver } from 'selenium-webdriver'; import { expect } from 'chai';
import { TestAppConfig } from './selenium.conf'; import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { OpenViduComponentsPO } from './utils.po.test'; import { OPENVIDU_CALL_SERVER } from '../config';
import { getBrowserOptionsWithoutDevices, WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Panels: UI Navigation and Section Switching', () => { describe('Testing panels', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -23,9 +25,6 @@ describe('Panels: UI Navigation and Section Switching', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -37,130 +36,190 @@ describe('Panels: UI Navigation and Section Switching', () => {
// let element; // let element;
// await browser.get(`${url}`); // await browser.get(`${url}`);
// element = await utils.waitForElement('#pre-join-container'); // element = await utils.waitForElement('#pre-join-container');
// expect(await utils.isPresent('#pre-join-container')).toBeTrue(); // expect(await utils.isPresent('#pre-join-container')).to.be.true;
// const backgroundButton = await utils.waitForElement('#background-effects-btn'); // const backgroundButton = await utils.waitForElement('#background-effects-btn');
// expect(await utils.isPresent('#background-effects-btn')).toBeTrue(); // expect(await utils.isPresent('#background-effects-btn')).to.be.true;
// expect(await backgroundButton.isEnabled()).toBeTrue(); // expect(await backgroundButton.isEnabled()).to.be.true;
// await backgroundButton.click(); // await backgroundButton.click();
// await browser.sleep(500); // await browser.sleep(500);
// await utils.waitForElement('#background-effects-container'); // await utils.waitForElement('#background-effects-container');
// expect(await utils.isPresent('#background-effects-container')).toBeTrue(); // expect(await utils.isPresent('#background-effects-container')).to.be.true;
// element = await utils.waitForElement('#camera-button'); // element = await utils.waitForElement('#camera-button');
// expect(await utils.isPresent('#camera-button')).toBeTrue(); // expect(await utils.isPresent('#camera-button')).to.be.true;
// expect(await element.isEnabled()).toBeTrue(); // expect(await element.isEnabled()).to.be.true;
// await element.click(); // await element.click();
// await browser.sleep(500); // await browser.sleep(500);
// element = await utils.waitForElement('#video-poster'); // element = await utils.waitForElement('#video-poster');
// expect(await utils.isPresent('#video-poster')).toBeTrue(); // expect(await utils.isPresent('#video-poster')).to.be.true;
// expect(await backgroundButton.isDisplayed()).toBeTrue(); // expect(await backgroundButton.isDisplayed()).to.be.true;
// expect(await backgroundButton.isEnabled()).toBeFalse(); // expect(await backgroundButton.isEnabled()).to.be.false;
// expect(await utils.isPresent('#background-effects-container')).toBeFalse(); // expect(await utils.isPresent('#background-effects-container')).to.be.false;
// }); // });
it('should open and close the CHAT panel and verify its content', async () => { it('should toggle CHAT panel', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
const chatButton = await utils.waitForElement('#chat-panel-btn'); const chatButton = await utils.waitForElement('#chat-panel-btn');
await chatButton.click(); await chatButton.click();
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.input-container'); await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue(); expect(await utils.isPresent('.input-container')).to.be.true;
await utils.waitForElement('.messages-container'); await utils.waitForElement('.messages-container');
expect(await utils.isPresent('.messages-container')).toBeTrue(); expect(await utils.isPresent('.messages-container')).to.be.true;
await chatButton.click(); await chatButton.click();
expect(await utils.isPresent('.input-container')).toBeFalse();
expect(await utils.isPresent('.messages-container')).toBeFalse(); expect(await utils.isPresent('.input-container')).to.be.false;
expect(await utils.isPresent('.messages-container')).to.be.false;
}); });
it('should open and close the PARTICIPANTS panel and verify its content', async () => { it('should toggle PARTICIPANTS panel', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
const participantBtn = await utils.waitForElement('#participants-panel-btn'); const participantBtn = await utils.waitForElement('#participants-panel-btn');
await participantBtn.click(); await participantBtn.click();
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('.local-participant-container'); await utils.waitForElement('.local-participant-container');
expect(await utils.isPresent('.local-participant-container')).toBeTrue(); expect(await utils.isPresent('.local-participant-container')).to.be.true;
await utils.waitForElement('ov-participant-panel-item'); await utils.waitForElement('ov-participant-panel-item');
expect(await utils.isPresent('ov-participant-panel-item')).toBeTrue(); expect(await utils.isPresent('ov-participant-panel-item')).to.be.true;
await participantBtn.click(); await participantBtn.click();
expect(await utils.isPresent('.local-participant-container')).toBeFalse();
expect(await utils.isPresent('ov-participant-panel-item')).toBeFalse(); expect(await utils.isPresent('.local-participant-container')).to.be.false;
expect(await utils.isPresent('ov-participant-panel-item')).to.be.false;
}); });
it('should open and close the ACTIVITIES panel and verify its content', async () => { it('should toggle ACTIVITIES panel', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Get activities button and click into it
const activitiesBtn = await utils.waitForElement('#activities-panel-btn'); const activitiesBtn = await utils.waitForElement('#activities-panel-btn');
await activitiesBtn.click(); await activitiesBtn.click();
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('#activities-container'); await utils.waitForElement('#activities-container');
expect(await utils.isPresent('#activities-container')).toBeTrue(); expect(await utils.isPresent('#activities-container')).to.be.true;
await utils.waitForElement('#recording-activity'); await utils.waitForElement('#recording-activity');
expect(await utils.isPresent('#recording-activity')).toBeTrue(); expect(await utils.isPresent('#recording-activity')).to.be.true;
await activitiesBtn.click(); await activitiesBtn.click();
expect(await utils.isPresent('#activities-container')).toBeFalse();
expect(await utils.isPresent('#recording-activity')).toBeFalse(); expect(await utils.isPresent('#activities-container')).to.be.false;
expect(await utils.isPresent('#recording-activity')).to.be.false;
}); });
it('should open the SETTINGS panel and verify its content', async () => { it('should toggle SETTINGS panel', async () => {
let element; let element;
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
await utils.togglePanel('settings'); await utils.togglePanel('settings');
element = await utils.waitForElement('.sidenav-menu'); element = await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('#default-settings-panel')).toBeTrue(); expect(await utils.isPresent('#default-settings-panel')).to.be.true;
}); });
it('should switch between PARTICIPANTS and CHAT panels and verify correct content is shown', async () => { it('should switching between PARTICIPANTS and CHAT panels', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Open chat panel
const chatButton = await utils.waitForElement('#chat-panel-btn'); const chatButton = await utils.waitForElement('#chat-panel-btn');
await chatButton.click(); await chatButton.click();
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.sidenav-menu')).toBeTrue(); expect(await utils.isPresent('.sidenav-menu')).to.be.true;
await utils.waitForElement('.input-container'); await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).toBeTrue(); expect(await utils.isPresent('.input-container')).to.be.true;
expect(await utils.isPresent('.messages-container')).toBeTrue();
expect(await utils.isPresent('.messages-container')).to.be.true;
// Open participants panel
const participantBtn = await utils.waitForElement('#participants-panel-btn'); const participantBtn = await utils.waitForElement('#participants-panel-btn');
await participantBtn.click(); await participantBtn.click();
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.local-participant-container')).toBeTrue();
expect(await utils.isPresent('ov-participant-panel-item')).toBeTrue(); expect(await utils.isPresent('.local-participant-container')).to.be.true;
expect(await utils.isPresent('ov-participant-panel-item')).to.be.true;
// Switch to chat panel
await chatButton.click(); await chatButton.click();
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.input-container')).toBeTrue();
expect(await utils.isPresent('.messages-container')).toBeTrue(); expect(await utils.isPresent('.input-container')).to.be.true;
expect(await utils.isPresent('.local-participant-container')).toBeFalse();
expect(await utils.isPresent('ov-participant-panel-item')).toBeFalse(); expect(await utils.isPresent('.messages-container')).to.be.true;
expect(await utils.isPresent('.local-participant-container')).to.be.false;
expect(await utils.isPresent('ov-participant-panel-item')).to.be.false;
// Close chat panel
await chatButton.click(); await chatButton.click();
expect(await utils.getNumberOfElements('.input-container')).toEqual(0); expect(await utils.getNumberOfElements('.input-container')).equals(0);
expect(await utils.isPresent('messages-container')).toBeFalse(); expect(await utils.isPresent('messages-container')).to.be.false;
}); });
it('should switch between sections in the SETTINGS panel and verify correct content is shown', async () => { it('should switching between sections in SETTINGS PANEL', async () => {
let element; let element;
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.togglePanel('settings'); await utils.togglePanel('settings');
await utils.waitForElement('.sidenav-menu'); await utils.waitForElement('.sidenav-menu');
expect(await utils.isPresent('.sidenav-menu')).toBeTrue(); expect(await utils.isPresent('.sidenav-menu')).to.be.true;
await browser.sleep(500);
// Check if general section is shown
element = await utils.waitForElement('#general-opt'); element = await utils.waitForElement('#general-opt');
await element.click(); await element.click();
expect(await utils.isPresent('ov-participant-name-input')).toBeTrue();
expect(await utils.isPresent('ov-participant-name-input')).to.be.true;
// Check if video section is shown
element = await utils.waitForElement('#video-opt'); element = await utils.waitForElement('#video-opt');
await element.click(); await element.click();
expect(await utils.isPresent('ov-video-devices-select')).toBeTrue();
expect(await utils.isPresent('ov-video-devices-select')).to.be.true;
// Check if audio section is shown
element = await utils.waitForElement('#audio-opt'); element = await utils.waitForElement('#audio-opt');
await element.click(); await element.click();
expect(await utils.isPresent('ov-audio-devices-select')).toBeTrue();
expect(await utils.isPresent('ov-audio-devices-select')).to.be.true;
}); });
}); });

View File

@ -1,19 +1,20 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('E2E: Screensharing features', () => { describe('Testing screenshare features', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -23,188 +24,197 @@ describe('E2E: Screensharing features', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
it('should toggle screensharing on and off twice, updating video count', async () => { it('should toggle screensharing twice', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Enable screensharing // Clicking to screensharing button
await utils.waitForElement('#screenshare-btn'); await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).toEqual(2); expect(await utils.getNumberOfElements('video')).equals(2);
expect(await utils.getNumberOfElements('.OV_stream.speaking')).equals(1);
// Disable screensharing // Clicking to screensharing button
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(1);
// Enable again
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('.OV_big'); await utils.waitForElement('#screenshare-menu');
expect(await utils.getNumberOfElements('video')).toEqual(2); await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
// Disable again expect(await utils.getNumberOfElements('video')).equals(1);
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(1); // toggle screenshare again
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).equals(2);
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(1);
}); });
it('should show screenshare and muted camera (camera off, screenshare on)', async () => { it('should show screen and muted camera', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Mute camera
await utils.waitForElement('#camera-btn'); await utils.waitForElement('#camera-btn');
await utils.clickOn('#camera-btn'); await utils.clickOn('#camera-btn');
// Enable screensharing // Clicking to screensharing button
const screenshareButton = await utils.waitForElement('#screenshare-btn'); const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await screenshareButton.isDisplayed()).toBeTrue(); expect(await screenshareButton.isDisplayed()).to.be.true;
await screenshareButton.click(); await screenshareButton.click();
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).toEqual(2); expect(await utils.getNumberOfElements('video')).equals(2);
// Disable screensharing await utils.clickOn('#screenshare-btn');
await utils.disableScreenShare(); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(1); await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(1);
}); });
it('should display screensharing with a single pinned video', async () => { it('should screensharing with PINNED video', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Enable screensharing // Clicking to screensharing button
const screenshareButton = await utils.waitForElement('#screenshare-btn'); const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await screenshareButton.isDisplayed()).toBeTrue(); expect(await screenshareButton.isDisplayed()).to.be.true;
await screenshareButton.click(); await screenshareButton.click();
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1); expect(await utils.getNumberOfElements('.OV_big')).equals(1);
}); });
it('should replace pinned video when a second participant starts screensharing', async () => { it('should screensharing with PINNED video and replace the existing one', async () => {
const roomName = 'screensharingE2E'; const roomName = 'screensharingE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// First participant screenshares // Clicking to screensharing button
await utils.waitForElement('#screenshare-btn'); await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1); expect(await utils.getNumberOfElements('.OV_big')).equals(1);
// Second participant joins and screenshares // Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`; const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript); await browser.executeScript(newTabScript);
const tabs = await browser.getAllWindowHandles(); const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Clicking to screensharing button
await utils.waitForElement('#screenshare-btn'); await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(4); expect(await utils.getNumberOfElements('video')).equals(4);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1); expect(await utils.getNumberOfElements('.OV_big')).equals(1);
// Switch back to first tab and check // Go to first tab
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(4); expect(await utils.getNumberOfElements('video')).equals(4);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1); expect(await utils.getNumberOfElements('.OV_big')).equals(1);
}); });
it('should unpin screensharing and restore previous pinned video when disabled', async () => { it('should disabled a screensharing and pinned the previous one', async () => {
const roomName = 'screensharingtwoE2E'; const roomName = 'screensharingtwoE2E';
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// First participant screenshares // Clicking to screensharing button
await utils.waitForElement('#screenshare-btn'); await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1); expect(await utils.getNumberOfElements('.OV_big')).equals(1);
// Second participant joins and screenshares // Starting new browser for adding the second participant
const tabs = await utils.openTab(fixedUrl); const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Clicking to screensharing button
await utils.waitForElement('#screenshare-btn'); await utils.waitForElement('#screenshare-btn');
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(4); expect(await utils.getNumberOfElements('video')).equals(4);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1); expect(await utils.getNumberOfElements('.OV_big')).equals(1);
// Disable screensharing
// Disable screensharing for second participant
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('video')).toEqual(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Switch back to first tab and check
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
});
it('should correctly share screen with microphone muted and maintain proper track state', async () => {
// Helper for inspecting stream tracks
const getMediaTracks = (className: string) => {
return `
const tracks = document.getElementsByClassName('${className}')[0].srcObject.getTracks();
return tracks.map(track => ({
kind: track.kind,
enabled: track.enabled,
id: track.id,
label: track.label
}));`;
};
// Setup: Navigate to room and skip prejoin
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
// Step 1: First mute the microphone
const micButton = await utils.waitForElement('#mic-btn');
await micButton.click();
// Step 2: Start screen sharing
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await utils.waitForElement('#screenshare-menu');
// Step 3: Verify both streams are present
await utils.waitForElement('.screen-type');
expect(await utils.getNumberOfElements('video')).toEqual(2);
// Step 4: Verify screen share track properties
const screenTracks: any[] = await browser.executeScript(getMediaTracks('screen-type'));
expect(screenTracks.length).toEqual(1);
expect(screenTracks[0].kind).toEqual('video');
expect(screenTracks[0].enabled).toBeTrue();
// Step 5: Verify microphone status indicators for both streams
// await utils.waitForElement('#status-mic');
// const micStatusCount = await utils.getNumberOfElements('#status-mic');
// expect(micStatusCount).toEqual(2);
// Step 6: Stop screen sharing and verify stream count
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.clickOn('#disable-screen-button'); await utils.clickOn('#disable-screen-button');
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(1); expect(await utils.getNumberOfElements('video')).equals(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1);
// Go to first tab
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1);
}); });
// it('should screensharing with audio muted', async () => {
// let isAudioEnabled;
// const getAudioScript = (className: string) => {
// return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`;
// };
// await browser.get(`${url}&prejoin=false`);
// await utils.checkLayoutPresent();
// const micButton = await utils.waitForElement('#mic-btn');
// await micButton.click();
// // Clicking to screensharing button
// const screenshareButton = await utils.waitForElement('#screenshare-btn');
// expect(await utils.isPresent('#screenshare-btn')).to.be.true;
// await screenshareButton.click();
// await utils.waitForElement('.screen-type');
// expect(await utils.getNumberOfElements('video')).equals(2);
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type'));
// expect(isAudioEnabled).to.be.false;
// await utils.waitForElement('#status-mic');
// expect(await utils.getNumberOfElements('#status-mic')).equals(2);
// // Clicking to screensharing button
// await screenshareButton.click();
// expect(await utils.getNumberOfElements('video')).equals(1);
// });
// it('should show and hide CAMERA stream when muting video with screensharing', async () => { // it('should show and hide CAMERA stream when muting video with screensharing', async () => {
// await browser.get(`${url}&prejoin=false`); // await browser.get(`${url}&prejoin=false`);
@ -212,16 +222,16 @@ describe('E2E: Screensharing features', () => {
// // Clicking to screensharing button // // Clicking to screensharing button
// const screenshareButton = await utils.waitForElement('#screenshare-btn'); // const screenshareButton = await utils.waitForElement('#screenshare-btn');
// expect(await screenshareButton.isDisplayed()).toBeTrue(); // expect(await screenshareButton.isDisplayed()).to.be.true;
// await screenshareButton.click(); // await screenshareButton.click();
// await utils.waitForElement('.OV_big'); // await utils.waitForElement('.OV_big');
// expect(await utils.getNumberOfElements('video')).toEqual(2); // expect(await utils.getNumberOfElements('video')).equals(2);
// const muteVideoButton = await utils.waitForElement('#camera-btn'); // const muteVideoButton = await utils.waitForElement('#camera-btn');
// await muteVideoButton.click(); // await muteVideoButton.click();
// expect(await utils.getNumberOfElements('video')).toEqual(1); // expect(await utils.getNumberOfElements('video')).equals(1);
// }); // });
// it('should screenshare has audio active when camera is muted', async () => { // it('should screenshare has audio active when camera is muted', async () => {
@ -234,33 +244,33 @@ describe('E2E: Screensharing features', () => {
// // Clicking to screensharing button // // Clicking to screensharing button
// const screenshareButton = await utils.waitForElement('#screenshare-btn'); // const screenshareButton = await utils.waitForElement('#screenshare-btn');
// expect(await utils.isPresent('#screenshare-btn')).toBeTrue(); // expect(await utils.isPresent('#screenshare-btn')).to.be.true;
// await screenshareButton.click(); // await screenshareButton.click();
// await utils.waitForElement('.OV_big'); // await utils.waitForElement('.OV_big');
// expect(await utils.getNumberOfElements('video')).toEqual(2); // expect(await utils.getNumberOfElements('video')).equals(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); // expect(await utils.getNumberOfElements('#status-mic')).equals(1);
// // Muting camera video // // Muting camera video
// const muteVideoButton = await utils.waitForElement('#camera-btn'); // const muteVideoButton = await utils.waitForElement('#camera-btn');
// await muteVideoButton.click(); // await muteVideoButton.click();
// expect(await utils.getNumberOfElements('video')).toEqual(1); // expect(await utils.getNumberOfElements('video')).equals(1);
// await browser.sleep(500); // await browser.sleep(500);
// expect(await utils.isPresent('#status-mic')).toBeFalse(); // expect(await utils.isPresent('#status-mic')).to.be.false;
// // Checking if audio is muted after join the room // // Checking if audio is muted after join the room
// isAudioEnabled = await browser.executeScript(audioEnableScript); // isAudioEnabled = await browser.executeScript(audioEnableScript);
// expect(isAudioEnabled).toBeTrue(); // expect(isAudioEnabled).to.be.true;
// // Unmuting camera // // Unmuting camera
// await muteVideoButton.click(); // await muteVideoButton.click();
// await browser.sleep(1000); // await browser.sleep(1000);
// await utils.waitForElement('.camera-type'); // await utils.waitForElement('.camera-type');
// expect(await utils.getNumberOfElements('video')).toEqual(2); // expect(await utils.getNumberOfElements('video')).equals(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); // expect(await utils.getNumberOfElements('#status-mic')).equals(1);
// }); // });
// it('should camera come back with audio muted when screensharing', async () => { // it('should camera come back with audio muted when screensharing', async () => {
@ -279,38 +289,38 @@ describe('E2E: Screensharing features', () => {
// await screenshareButton.click(); // await screenshareButton.click();
// await utils.waitForElement('.screen-type'); // await utils.waitForElement('.screen-type');
// expect(await utils.getNumberOfElements('video')).toEqual(2); // expect(await utils.getNumberOfElements('video')).equals(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); // expect(await utils.getNumberOfElements('#status-mic')).equals(1);
// // Mute camera // // Mute camera
// const muteVideoButton = await utils.waitForElement('#camera-btn'); // const muteVideoButton = await utils.waitForElement('#camera-btn');
// await muteVideoButton.click(); // await muteVideoButton.click();
// expect(await utils.getNumberOfElements('video')).toEqual(1); // expect(await utils.getNumberOfElements('video')).equals(1);
// expect(await utils.isPresent('#status-mic')).toBeFalse(); // expect(await utils.isPresent('#status-mic')).to.be.false;
// // Checking if audio is muted after join the room // // Checking if audio is muted after join the room
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type'));
// expect(isAudioEnabled).toBeTrue(); // expect(isAudioEnabled).to.be.true;
// // Mute audio // // Mute audio
// const muteAudioButton = await utils.waitForElement('#mic-btn'); // const muteAudioButton = await utils.waitForElement('#mic-btn');
// await muteAudioButton.click(); // await muteAudioButton.click();
// await utils.waitForElement('#status-mic'); // await utils.waitForElement('#status-mic');
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); // expect(await utils.getNumberOfElements('#status-mic')).equals(1);
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type'));
// expect(isAudioEnabled).toBeFalse(); // expect(isAudioEnabled).to.be.false;
// // Unmute camera // // Unmute camera
// await muteVideoButton.click(); // await muteVideoButton.click();
// await utils.waitForElement('.camera-type'); // await utils.waitForElement('.camera-type');
// expect(await utils.getNumberOfElements('video')).toEqual(2); // expect(await utils.getNumberOfElements('video')).equals(2);
// expect(await utils.getNumberOfElements('#status-mic')).toEqual(2); // expect(await utils.getNumberOfElements('#status-mic')).equals(2);
// isAudioEnabled = await browser.executeScript(getAudioScript('camera-type')); // isAudioEnabled = await browser.executeScript(getAudioScript('camera-type'));
// expect(isAudioEnabled).toBeFalse(); // expect(isAudioEnabled).to.be.false;
// }); // });
}); });

View File

@ -1,18 +1,20 @@
import { expect } from 'chai';
import { Builder, ILocation, IRectangle, ISize, WebDriver } from 'selenium-webdriver'; import { Builder, ILocation, IRectangle, ISize, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Stream rendering and media toggling scenarios', () => { describe('Checking stream elements by disabling/enabling the media', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -22,52 +24,10 @@ describe('Stream rendering and media toggling scenarios', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
it('should not render any video element when joining with video disabled', async () => { it('should show ONE video element when a LOCAL participant joins with VIDEO and AUDIO MUTED', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
});
it('should render a video element but no audio when joining with audio muted', async () => {
await browser.get(`${url}&prejoin=true&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
});
it('should render both video and audio elements when joining with both enabled', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
});
it('should add a screen share video/audio when sharing screen with both camera and mic muted', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=false`); await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=false`);
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
@ -75,24 +35,32 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(1); //screen sharse video
expect(await utils.getNumberOfElements('audio')).toEqual(1); //screen share audio
await utils.disableScreenShare();
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
}); });
it('should add a screen share video/audio when sharing screen with both camera and mic enabled', async () => { it('should show ONE video element when a LOCAL participant joins with AUDIO MUTED', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
});
it('should show a video element when a LOCAL participant joins with VIDEO MUTED', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=true`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
});
it('should show a video element when a LOCAL participant joins', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`); await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`);
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
@ -100,26 +68,104 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
expect(await utils.getNumberOfElements('video')).toEqual(1); });
expect(await utils.getNumberOfElements('audio')).toEqual(1);
it('should show a video element when a LOCAL participant shares its screen with VIDEO and AUDIO MUTED', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(1000); await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share'); await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2); //screen share audio and local audio
await utils.disableScreenShare(); await utils.clickOn('#screenshare-btn');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(1); await utils.waitForElement('#screenshare-menu');
expect(await utils.getNumberOfElements('audio')).toEqual(1); await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
}); });
/* ------------ Checking video/audio elements with two participants ------------ */ it('should show a video element when a LOCAL participant shares its screen with VIDEO MUTED', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=false&audioEnabled=true`);
it('should not render any video/audio elements when two participants join with both video and audio muted', async () => { await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
});
it('should show a video element when a LOCAL participant shares its screen with AUDIO MUTED', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=false`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
});
it('should show a video element when a LOCAL participant shares its screen', async () => {
await browser.get(`${url}&prejoin=true&videoEnabled=true&audioEnabled=true`);
await utils.checkPrejoinIsPresent();
await utils.clickOn('#join-button');
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
});
/* ------------ Checking video elements with two participants ------------ */
it('should show TWO video elements when a REMOTE participant joins with VIDEO and AUDIO MUTED', async () => {
const roomName = `streams-${Date.now()}`; const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
@ -127,27 +173,21 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
const tabs = await utils.openTab(fixedUrl); const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(1000); await browser.sleep(1000);
await utils.waitForElement('.OV_stream.remote'); await utils.waitForElement('.OV_stream.remote');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await browser.sleep(1000); await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
}); });
it('should render two video elements and no audio when two participants join with audio muted', async () => { it('should show TWO video elements when a REMOTE participant joins with AUDIO MUTED', async () => {
const roomName = `streams-${Date.now()}`; const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
@ -155,29 +195,21 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
const tabs = await utils.openTab(fixedUrl); const tabs = await utils.openTab(fixedUrl);
await browser.sleep(1000);
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
await utils.waitForElement('.OV_stream.remote'); await utils.waitForElement('.OV_stream.remote');
await browser.sleep(2000); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await browser.sleep(1000); await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
}); });
it('should not render any video elements but should render two audio elements when two participants join with video disabled', async () => { it('should show TWO video elements when a REMOTE participant joins with VIDEO MUTED', async () => {
const roomName = `streams-${Date.now()}`; const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
@ -185,28 +217,41 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(1);
const tabs = await utils.openTab(fixedUrl); const tabs = await utils.openTab(fixedUrl);
await browser.sleep(1000);
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
await utils.waitForElement('.OV_stream.remote'); await utils.waitForElement('.OV_stream.remote');
await browser.sleep(2000); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await browser.sleep(1000); await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
}); });
it('should add a screen share video/audio when a participant with both video and audio muted shares their screen (two participants)', async () => { it('should show TWO video elements when a REMOTE participant joins', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=true`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).equal(1);
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
await utils.waitForElement('.OV_stream.remote');
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await browser.switchTo().window(tabs[1]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
});
it('should show THREE video elements when a REMOTE participant shares its screen with AUDIO and VIDEO MUTED', async () => {
const roomName = `streams-${Date.now()}`; const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
@ -222,30 +267,96 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(1000); await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share'); await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(1000); await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await utils.disableScreenShare(); await utils.clickOn('#screenshare-btn');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(0); await utils.waitForElement('#screenshare-menu');
expect(await utils.getNumberOfElements('audio')).toEqual(0); await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('audio')).toEqual(0);
}); });
it('should add a screen share video/audio when a remote participant with both video and audio enabled shares their screen', async () => { it('should show THREE video elements when a REMOTE participant shares its screen with VIDEO MUTED', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=true`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]);
await utils.waitForElement('.OV_stream.local');
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
await browser.switchTo().window(tabs[1]);
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
});
it('should show THREE video elements when a REMOTE participant shares its screen with AUDIO MUTED', async () => {
const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=false`;
await browser.get(fixedUrl);
await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local');
const tabs = await utils.openTab(fixedUrl);
await browser.switchTo().window(tabs[1]);
await utils.waitForElement('.OV_stream.local');
await utils.clickOn('#screenshare-btn');
await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
await browser.switchTo().window(tabs[0]);
await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
await browser.switchTo().window(tabs[1]);
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await browser.switchTo().window(tabs[0]);
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
});
it('should show THREE video elements when a REMOTE participant shares its screen', async () => {
const roomName = `streams-${Date.now()}`; const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=true`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=true&audioEnabled=true`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
@ -261,30 +372,26 @@ describe('Stream rendering and media toggling scenarios', () => {
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(1000); await browser.sleep(1000);
await utils.waitForElement('#local-element-screen_share'); await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
expect(await utils.getNumberOfElements('video')).toEqual(3);
expect(await utils.getNumberOfElements('audio')).toEqual(3); // screen share audios and local audio and remote audio
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(1000); await browser.sleep(1000);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
expect(await utils.getNumberOfElements('video')).toEqual(3);
expect(await utils.getNumberOfElements('audio')).toEqual(3); // screen share audios and local audio and remote audio
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await utils.disableScreenShare(); await utils.clickOn('#screenshare-btn');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(2); await utils.waitForElement('#screenshare-menu');
expect(await utils.getNumberOfElements('audio')).toEqual(2); await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(2); expect(await utils.getNumberOfElements('.OV_stream')).equal(2);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2);
}); });
it('should add a screen share video/audio for both participants when both share their screen with video/audio muted', async () => { it('should show FOUR video elements when a two participant shares theirs screen', async () => {
const roomName = `streams-${Date.now()}`; const roomName = `streams-${Date.now()}`;
const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`; const fixedUrl = `${url}&roomName=${roomName}&prejoin=false&videoEnabled=false&audioEnabled=false`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
@ -298,45 +405,39 @@ describe('Stream rendering and media toggling scenarios', () => {
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
await utils.clickOn('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
await browser.sleep(500); await browser.sleep(500);
await utils.waitForElement('#local-element-screen_share'); await utils.waitForElement('#local-element-screen_share');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(4); expect(await utils.getNumberOfElements('.OV_stream')).equal(4);
expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('audio')).toEqual(2); // screen share audios
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(4); expect(await utils.getNumberOfElements('.OV_stream')).equal(4);
expect(await utils.getNumberOfElements('video')).toEqual(2); await utils.clickOn('#camera-btn');
expect(await utils.getNumberOfElements('audio')).toEqual(2); // screen share audios await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.disableScreenShare(); await utils.waitForElement('#screenshare-menu');
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); await utils.clickOn('#disable-screen-button');
expect(await utils.getNumberOfElements('video')).toEqual(1); await browser.sleep(500);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
await browser.switchTo().window(tabs[1]); await browser.switchTo().window(tabs[1]);
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream')).toEqual(3); expect(await utils.getNumberOfElements('.OV_stream')).equal(3);
expect(await utils.getNumberOfElements('video')).toEqual(1);
expect(await utils.getNumberOfElements('audio')).toEqual(1); // screen share audios
}); });
}); });
describe('Stream UI controls and interaction features', () => { describe('Testing stream features', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -346,9 +447,6 @@ describe('Stream UI controls and interaction features', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -359,7 +457,7 @@ describe('Stream UI controls and interaction features', () => {
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local'); await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#pin-btn'); await utils.waitForElement('#pin-btn');
expect(await utils.isPresent('#pin-btn')).toBeTrue(); expect(await utils.isPresent('#pin-btn')).to.be.true;
}); });
it('should show the PIN button over the REMOTE video', async () => { it('should show the PIN button over the REMOTE video', async () => {
@ -380,7 +478,7 @@ describe('Stream UI controls and interaction features', () => {
await utils.waitForElement('.OV_stream.remote'); await utils.waitForElement('.OV_stream.remote');
await utils.hoverOn('.OV_stream.remote'); await utils.hoverOn('.OV_stream.remote');
await utils.waitForElement('#pin-btn'); await utils.waitForElement('#pin-btn');
expect(await utils.isPresent('#pin-btn')).toBeTrue(); expect(await utils.isPresent('#pin-btn')).to.be.true;
}); });
it('should show the SILENCE button ONLY over the REMOTE video', async () => { it('should show the SILENCE button ONLY over the REMOTE video', async () => {
@ -393,7 +491,7 @@ describe('Stream UI controls and interaction features', () => {
await utils.hoverOn('.OV_stream.local'); await utils.hoverOn('.OV_stream.local');
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).toEqual(0); expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).equals(0);
// Starting new browser for adding the second participant // Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`; const newTabScript = `window.open("${fixedUrl}")`;
@ -405,13 +503,13 @@ describe('Stream UI controls and interaction features', () => {
await utils.waitForElement('.OV_stream.remote'); await utils.waitForElement('.OV_stream.remote');
await utils.hoverOn('.OV_stream.remote'); await utils.hoverOn('.OV_stream.remote');
await utils.waitForElement('.OV_stream.remote #silence-btn'); await utils.waitForElement('.OV_stream.remote #silence-btn');
expect(await utils.isPresent('.OV_stream.remote #silence-btn')).toBeTrue(); expect(await utils.isPresent('.OV_stream.remote #silence-btn')).to.be.true;
expect(await utils.getNumberOfElements('.OV_stream.remote #silence-btn')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream.remote #silence-btn')).equals(1);
await utils.hoverOn('.OV_stream.local'); await utils.hoverOn('.OV_stream.local');
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).toEqual(0); expect(await utils.getNumberOfElements('.OV_stream.local #silence-btn')).equals(0);
}); });
it('should show the MINIMIZE button ONLY over the LOCAL video', async () => { it('should show the MINIMIZE button ONLY over the LOCAL video', async () => {
@ -422,7 +520,7 @@ describe('Stream UI controls and interaction features', () => {
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.local'); await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn'); await utils.waitForElement('#minimize-btn');
expect(await utils.isPresent('#minimize-btn')).toBeTrue(); expect(await utils.isPresent('#minimize-btn')).to.be.true;
// Starting new browser for adding the second participant // Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`; const newTabScript = `window.open("${fixedUrl}")`;
@ -433,11 +531,11 @@ describe('Stream UI controls and interaction features', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
await utils.waitForElement('.OV_stream.local'); await utils.waitForElement('.OV_stream.local');
await utils.hoverOn('.OV_stream.remote'); await utils.hoverOn('.OV_stream.remote');
expect(await utils.getNumberOfElements('#minimize-btn')).toEqual(0); expect(await utils.getNumberOfElements('#minimize-btn')).equals(0);
await utils.hoverOn('.OV_stream.local'); await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn'); await utils.waitForElement('#minimize-btn');
expect(await utils.isPresent('#minimize-btn')).toBeTrue(); expect(await utils.isPresent('#minimize-btn')).to.be.true;
}); });
it('should minimize the LOCAL video', async () => { it('should minimize the LOCAL video', async () => {
@ -454,10 +552,10 @@ describe('Stream UI controls and interaction features', () => {
await browser.sleep(900); await browser.sleep(900);
const minimizeStream = await utils.waitForElement('.OV_stream.local'); const minimizeStream = await utils.waitForElement('.OV_stream.local');
const minimizeStreamProps: IRectangle = await minimizeStream.getRect(); const minimizeStreamProps: IRectangle = await minimizeStream.getRect();
expect(streamProps.height).not.toEqual(minimizeStreamProps.height); expect(streamProps.height).not.equals(minimizeStreamProps.height);
expect(streamProps.width).not.toEqual(minimizeStreamProps.width); expect(streamProps.width).not.equals(minimizeStreamProps.width);
expect(minimizeStreamProps.x).toBeLessThan(100); expect(minimizeStreamProps.x).lessThan(100);
expect(minimizeStreamProps.y).toBeLessThan(100); expect(minimizeStreamProps.y).lessThan(100);
}); });
it('should MAXIMIZE the local video', async () => { it('should MAXIMIZE the local video', async () => {
@ -478,8 +576,8 @@ describe('Stream UI controls and interaction features', () => {
let stream = await utils.waitForElement('.OV_stream.local'); let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect(); let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(300 + marginX); expect(streamProps.x).equals(300 + marginX);
expect(streamProps.y).toEqual(300); expect(streamProps.y).equals(300);
await utils.hoverOn('.OV_stream.local'); await utils.hoverOn('.OV_stream.local');
await utils.waitForElement('#minimize-btn'); await utils.waitForElement('#minimize-btn');
@ -488,8 +586,8 @@ describe('Stream UI controls and interaction features', () => {
stream = await utils.waitForElement('.OV_stream.local'); stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect(); streamProps = await stream.getRect();
expect(streamProps.x).toBeLessThan(300 + marginX); expect(streamProps.x).lessThan(300 + marginX);
expect(streamProps.y).toEqual(1); //.OV_publisher element has 1px of padding expect(streamProps.y).equals(1); //.OV_publisher element has 1px of padding
}); });
it('should be able to dragg the minimized LOCAL video', async () => { it('should be able to dragg the minimized LOCAL video', async () => {
@ -510,8 +608,8 @@ describe('Stream UI controls and interaction features', () => {
const stream = await utils.waitForElement('.OV_stream.local'); const stream = await utils.waitForElement('.OV_stream.local');
const streamProps: IRectangle = await stream.getRect(); const streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(300 + marginX); expect(streamProps.x).equals(300 + marginX);
expect(streamProps.y).toEqual(300); expect(streamProps.y).equals(300);
}); });
it('should be the MINIMIZED video ALWAYS VISIBLE when toggling panels', async () => { it('should be the MINIMIZED video ALWAYS VISIBLE when toggling panels', async () => {
@ -533,8 +631,8 @@ describe('Stream UI controls and interaction features', () => {
let stream = await utils.waitForElement('.OV_stream.local'); let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect(); let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(900 + marginX); expect(streamProps.x).equals(900 + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
// Open chat panel // Open chat panel
await utils.clickOn('#chat-panel-btn'); await utils.clickOn('#chat-panel-btn');
@ -543,8 +641,8 @@ describe('Stream UI controls and interaction features', () => {
streamProps = await stream.getRect(); streamProps = await stream.getRect();
let lastX = streamProps.x; let lastX = streamProps.x;
expect(streamProps.x).toBeLessThan(900 + marginX); expect(streamProps.x).lessThan(900 + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
// Close chat panel // Close chat panel
await utils.clickOn('#chat-panel-btn'); await utils.clickOn('#chat-panel-btn');
@ -553,8 +651,8 @@ describe('Stream UI controls and interaction features', () => {
stream = await utils.waitForElement('.OV_stream.local'); stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect(); streamProps = await stream.getRect();
expect(streamProps.x).toBeGreaterThanOrEqual(lastX + marginX); expect(streamProps.x).greaterThan(lastX + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
}); });
it('should be the MINIMIZED video go to the right when panel closes', async () => { it('should be the MINIMIZED video go to the right when panel closes', async () => {
@ -583,8 +681,8 @@ describe('Stream UI controls and interaction features', () => {
let stream = await utils.waitForElement('.OV_stream.local'); let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect(); let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(newX + marginX); expect(streamProps.x).equals(newX + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
// Close chat panel // Close chat panel
// There is a unstable behaviour simulating the drag and drop with selenium (the stream is not moved the first time) // There is a unstable behaviour simulating the drag and drop with selenium (the stream is not moved the first time)
@ -599,8 +697,8 @@ describe('Stream UI controls and interaction features', () => {
streamProps = await stream.getRect(); streamProps = await stream.getRect();
let lastX = streamProps.x; let lastX = streamProps.x;
expect(streamProps.x).toBeGreaterThanOrEqual(newX + marginX); expect(streamProps.x).greaterThanOrEqual(newX + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
// Open chat panel // Open chat panel
await utils.clickOn('#chat-panel-btn'); await utils.clickOn('#chat-panel-btn');
@ -609,8 +707,8 @@ describe('Stream UI controls and interaction features', () => {
stream = await utils.waitForElement('.OV_stream.local'); stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect(); streamProps = await stream.getRect();
expect(streamProps.x).toBeLessThan(lastX + marginX); expect(streamProps.x).lessThan(lastX + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
}); });
it('should be the MINIMIZED video ALWAYS VISIBLE when toggling from small to bigger panel', async () => { it('should be the MINIMIZED video ALWAYS VISIBLE when toggling from small to bigger panel', async () => {
@ -632,8 +730,8 @@ describe('Stream UI controls and interaction features', () => {
let stream = await utils.waitForElement('.OV_stream.local'); let stream = await utils.waitForElement('.OV_stream.local');
let streamProps: IRectangle = await stream.getRect(); let streamProps: IRectangle = await stream.getRect();
expect(streamProps.x).toEqual(900 + marginX); expect(streamProps.x).equals(900 + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
// Open chat panel // Open chat panel
await utils.clickOn('#chat-panel-btn'); await utils.clickOn('#chat-panel-btn');
@ -642,8 +740,8 @@ describe('Stream UI controls and interaction features', () => {
streamProps = await stream.getRect(); streamProps = await stream.getRect();
let lastX = streamProps.x; let lastX = streamProps.x;
expect(streamProps.x).toBeLessThan(900 + marginX); expect(streamProps.x).lessThan(900 + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
// Open settings panel // Open settings panel
await utils.togglePanel('settings'); await utils.togglePanel('settings');
@ -652,8 +750,8 @@ describe('Stream UI controls and interaction features', () => {
stream = await utils.waitForElement('.OV_stream.local'); stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect(); streamProps = await stream.getRect();
expect(streamProps.x).toBeLessThan(lastX + marginX); expect(streamProps.x).lessThan(lastX + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
lastX = streamProps.x; lastX = streamProps.x;
// Open chat panel // Open chat panel
@ -662,8 +760,8 @@ describe('Stream UI controls and interaction features', () => {
stream = await utils.waitForElement('.OV_stream.local'); stream = await utils.waitForElement('.OV_stream.local');
streamProps = await stream.getRect(); streamProps = await stream.getRect();
expect(streamProps.x).toBeGreaterThanOrEqual(lastX + marginX); expect(streamProps.x).greaterThan(lastX + marginX);
expect(streamProps.y).toEqual(0); expect(streamProps.y).equals(0);
}); });
it('should show the audio detection elements when participant is speaking', async () => { it('should show the audio detection elements when participant is speaking', async () => {
@ -676,30 +774,25 @@ describe('Stream UI controls and interaction features', () => {
// Starting new browser for adding the second participant // Starting new browser for adding the second participant
const newTabScript = `window.open("${fixedUrl}")`; const newTabScript = `window.open("${fixedUrl}")`;
await browser.executeScript(newTabScript); await browser.executeScript(newTabScript);
await browser.sleep(1000);
const tabs = await browser.getAllWindowHandles(); const tabs = await browser.getAllWindowHandles();
await browser.switchTo().window(tabs[0]); await browser.switchTo().window(tabs[0]);
await utils.waitForElement('.OV_stream.remote.speaking'); await utils.waitForElement('.OV_stream.remote.speaking');
expect(await utils.getNumberOfElements('.OV_stream.remote.speaking')).toEqual(1); expect(await utils.getNumberOfElements('.OV_stream.remote.speaking')).to.be.equal(1);
expect(await utils.getNumberOfElements('.OV_stream.speaking')).to.be.equal(1);
// Check only one element is marked as speaker due to the local participant is muted
await utils.waitForElement('.OV_stream.speaking');
expect(await utils.getNumberOfElements('.OV_stream.speaking')).toEqual(1);
}); });
}); });
describe('Video playback reliability with different media states', () => { describe('Testing video is playing', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -709,9 +802,6 @@ describe('Video playback reliability with different media states', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -740,7 +830,7 @@ describe('Video playback reliability with different media states', () => {
await browser.sleep(6000); await browser.sleep(6000);
const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT'); const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT');
expect(exceptionQuantity).toEqual(0); expect(exceptionQuantity).equals(0);
}); });
it('should play the participant video with only video', async () => { it('should play the participant video with only video', async () => {
@ -768,6 +858,6 @@ describe('Video playback reliability with different media states', () => {
await browser.sleep(6000); await browser.sleep(6000);
const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT'); const exceptionQuantity = await utils.getNumberOfElements('#NO_STREAM_PLAYING_EVENT');
expect(exceptionQuantity).toEqual(0); expect(exceptionQuantity).equals(0);
}); });
}); });

View File

@ -1,18 +1,20 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf'; import { OPENVIDU_CALL_SERVER } from '../config';
import { OpenViduComponentsPO } from './utils.po.test'; import { WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = TestAppConfig.appUrl; const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Toolbar button functionality for local media control', () => { describe('Testing TOOLBAR features', () => {
let browser: WebDriver; let browser: WebDriver;
let utils: OpenViduComponentsPO; let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(TestAppConfig.browserName) .forBrowser(WebComponentConfig.browserName)
.withCapabilities(TestAppConfig.browserCapabilities) .withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(TestAppConfig.browserOptions) .setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(TestAppConfig.seleniumAddress) .usingServer(WebComponentConfig.seleniumAddress)
.build(); .build();
} }
@ -22,13 +24,10 @@ describe('Toolbar button functionality for local media control', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
it('should toggle mute/unmute on the local microphone and update the icon accordingly', async () => { it('should mute and unmute the local microphone', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
@ -37,15 +36,15 @@ describe('Toolbar button functionality for local media control', () => {
await micButton.click(); await micButton.click();
await utils.waitForElement('#mic-btn #mic_off'); await utils.waitForElement('#mic-btn #mic_off');
expect(await utils.isPresent('#mic-btn #mic_off')).toBeTrue(); expect(await utils.isPresent('#mic-btn #mic_off')).to.be.true;
await micButton.click(); await micButton.click();
await utils.waitForElement('#mic-btn #mic'); await utils.waitForElement('#mic-btn #mic');
expect(await utils.isPresent('#mic-btn #mic')).toBeTrue(); expect(await utils.isPresent('#mic-btn #mic')).to.be.true;
}); });
it('should toggle mute/unmute on the local camera and update the icon accordingly', async () => { it('should mute and unmute the local camera', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
@ -54,11 +53,11 @@ describe('Toolbar button functionality for local media control', () => {
await cameraButton.click(); await cameraButton.click();
await utils.waitForElement('#camera-btn #videocam_off'); await utils.waitForElement('#camera-btn #videocam_off');
expect(await utils.isPresent('#camera-btn #videocam_off')).toBeTrue(); expect(await utils.isPresent('#camera-btn #videocam_off')).to.be.true;
await cameraButton.click(); await cameraButton.click();
await utils.waitForElement('#camera-btn #videocam'); await utils.waitForElement('#camera-btn #videocam');
expect(await utils.isPresent('#camera-btn #videocam')).toBeTrue(); expect(await utils.isPresent('#camera-btn #videocam')).to.be.true;
}); });
}); });

View File

@ -0,0 +1,337 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver';
import { OPENVIDU_CALL_SERVER } from './config';
import { WebComponentConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Testing API Directives', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(WebComponentConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
await browser.quit();
});
it('should change the captions LANG ', async () => {
await browser.get(`${url}&prejoin=false&captionsLang=es-ES`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
// Checking if button panel is present
await utils.waitForElement('#more-options-menu');
expect(await utils.isPresent('#more-options-menu')).to.be.true;
// Checking if captions button is present
await utils.waitForElement('#captions-btn');
expect(await utils.isPresent('#captions-btn')).to.be.true;
await utils.clickOn('#captions-btn');
await utils.waitForElement('.captions-container');
await utils.waitForElement('#caption-settings-btn');
await utils.clickOn('#caption-settings-btn');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).to.be.true;
await utils.waitForElement('ov-captions-settings');
expect(await utils.isPresent('.captions-container')).to.be.true;
const element = await utils.waitForElement('.lang-button');
expect(await element.getText()).equal('Españolexpand_more');
});
it('should override the CAPTIONS LANG OPTIONS', async () => {
await browser.get(`${url}&prejoin=false&captionsLangOptions=true`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
// Checking if button panel is present
await utils.waitForElement('#more-options-menu');
expect(await utils.isPresent('#more-options-menu')).to.be.true;
// Checking if captions button is present
await utils.waitForElement('#captions-btn');
expect(await utils.isPresent('#captions-btn')).to.be.true;
await utils.clickOn('#captions-btn');
await utils.waitForElement('.captions-container');
await utils.waitForElement('#caption-settings-btn');
await utils.clickOn('#caption-settings-btn');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).to.be.true;
await utils.waitForElement('ov-captions-settings');
expect(await utils.isPresent('.captions-container')).to.be.true;
const element = await utils.waitForElement('.lang-button');
expect(await element.getText()).equal('Espexpand_more');
await element.click();
expect(await utils.getNumberOfElements('.mat-menu-item')).equals(2);
});
});
describe('Testing panels', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(WebComponentConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
await browser.quit();
});
it('should toggle BACKGROUND panel on prejoin page when VIDEO is MUTED', async () => {
let element;
await browser.get(`${url}`);
element = await utils.waitForElement('#pre-join-container');
expect(await utils.isPresent('#pre-join-container')).to.be.true;
const backgroundButton = await utils.waitForElement('#background-effects-btn');
expect(await utils.isPresent('#background-effects-btn')).to.be.true;
expect(await backgroundButton.isEnabled()).to.be.true;
await backgroundButton.click();
await browser.sleep(500);
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).to.be.true;
element = await utils.waitForElement('#camera-button');
expect(await utils.isPresent('#camera-button')).to.be.true;
expect(await element.isEnabled()).to.be.true;
await element.click();
await browser.sleep(500);
element = await utils.waitForElement('#video-poster');
expect(await utils.isPresent('#video-poster')).to.be.true;
expect(await backgroundButton.isDisplayed()).to.be.true;
expect(await backgroundButton.isEnabled()).to.be.false;
expect(await utils.isPresent('#background-effects-container')).to.be.false;
});
});
describe('Testing captions features', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(WebComponentConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
await browser.quit();
});
it('should OPEN the CAPTIONS container', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
// Checking if button panel is present
await utils.waitForElement('#more-options-menu');
expect(await utils.isPresent('#more-options-menu')).to.be.true;
// Checking if captions button is present
await utils.waitForElement('#captions-btn');
expect(await utils.isPresent('#captions-btn')).to.be.true;
await utils.clickOn('#captions-btn');
await utils.waitForElement('.captions-container');
});
it('should OPEN the SETTINGS panel from captions button', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
// Checking if button panel is present
await utils.waitForElement('#more-options-menu');
expect(await utils.isPresent('#more-options-menu')).to.be.true;
// Checking if captions button is present
await utils.waitForElement('#captions-btn');
expect(await utils.isPresent('#captions-btn')).to.be.true;
await utils.clickOn('#captions-btn');
await utils.waitForElement('.captions-container');
await utils.waitForElement('#caption-settings-btn');
await utils.clickOn('#caption-settings-btn');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).to.be.true;
await utils.waitForElement('ov-captions-settings');
// Expect caption button is not present
expect(await utils.isPresent('#caption-settings-btn')).to.be.false;
});
it('should TOGGLE the CAPTIONS container from settings panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
// Checking if button panel is present
await utils.waitForElement('#more-options-menu');
expect(await utils.isPresent('#more-options-menu')).to.be.true;
// Checking if captions button is present
await utils.waitForElement('#captions-btn');
expect(await utils.isPresent('#captions-btn')).to.be.true;
await utils.clickOn('#captions-btn');
await utils.waitForElement('.captions-container');
await utils.waitForElement('#caption-settings-btn');
await utils.clickOn('#caption-settings-btn');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).to.be.true;
await utils.waitForElement('ov-captions-settings');
expect(await utils.isPresent('.captions-container')).to.be.true;
await utils.clickOn('#captions-toggle-slide');
expect(await utils.isPresent('.captions-container')).to.be.false;
await browser.sleep(200);
await utils.clickOn('#captions-toggle-slide');
expect(await utils.isPresent('.captions-container')).to.be.true;
});
it('should change the CAPTIONS language from settings panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
// Checking if toolbar is present
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.clickOn('#more-options-btn');
await browser.sleep(500);
// Checking if button panel is present
await utils.waitForElement('#more-options-menu');
expect(await utils.isPresent('#more-options-menu')).to.be.true;
// Checking if captions button is present
await utils.waitForElement('#captions-btn');
expect(await utils.isPresent('#captions-btn')).to.be.true;
await utils.clickOn('#captions-btn');
await utils.waitForElement('.captions-container');
await utils.waitForElement('#caption-settings-btn');
await utils.clickOn('#caption-settings-btn');
await browser.sleep(500);
await utils.waitForElement('.settings-container');
expect(await utils.isPresent('.settings-container')).to.be.true;
await utils.waitForElement('ov-captions-settings');
expect(await utils.isPresent('.captions-container')).to.be.true;
await utils.clickOn('.lang-button');
await browser.sleep(500);
await utils.clickOn('#es-ES');
await utils.clickOn('.panel-close-button');
const button = await utils.waitForElement('#caption-settings-btn');
expect(await button.getText()).equals('settingsEspañol');
});
});

View File

@ -0,0 +1,115 @@
import { expect } from 'chai';
import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { OPENVIDU_CALL_SERVER } from './config';
import { getBrowserOptionsWithoutDevices, WebComponentConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`;
describe('Testing PRO features with OpenVidu CE', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder()
.forBrowser(WebComponentConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress)
.build();
}
beforeEach(async () => {
browser = await createChromeBrowser();
utils = new OpenViduComponentsPO(browser);
});
afterEach(async () => {
await browser.quit();
});
// TODO: Uncomment when background feature is supported
// it('should SHOW the VIRTUAL BACKGROUND PRO feature dialog', async () => {
// await browser.get(`${url}&prejoin=true`);
// await utils.checkPrejoinIsPresent();
// await utils.waitForElement('#background-effects-btn');
// await utils.clickOn('#background-effects-btn');
// await utils.chceckProFeatureAlertIsPresent();
// // Close alert
// await (await utils.waitForElement('html')).sendKeys(Key.ESCAPE);
// // Join to room
// await utils.clickOn('#join-button');
// await utils.checkSessionIsPresent();
// // Checking if toolbar is present
// await utils.checkToolbarIsPresent();
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).to.be.true;
// await utils.waitForElement('#virtual-bg-btn');
// await utils.clickOn('#virtual-bg-btn');
// // Expect it shows the pro feature alert
// await utils.chceckProFeatureAlertIsPresent();
// });
// it('should SHOW the CAPTIONS PRO feature dialog', async () => {
// await browser.get(`${url}&prejoin=false`);
// await utils.checkSessionIsPresent();
// // Checking if toolbar is present
// await utils.checkToolbarIsPresent();
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).to.be.true;
// await utils.waitForElement('#toolbar-settings-btn');
// expect(await utils.isPresent('#toolbar-settings-btn')).to.be.true;
// await utils.clickOn('#toolbar-settings-btn');
// // Expect captions panel shows the pro feature content
// await utils.waitForElement('#settings-container');
// await utils.clickOn('#captions-opt');
// await browser.sleep(1000);
// await utils.waitForElement('.pro-feature');
// // Open more options menu
// await utils.clickOn('#more-options-btn');
// await browser.sleep(500);
// // Checking if button panel is present
// await utils.waitForElement('#more-options-menu');
// expect(await utils.isPresent('#more-options-menu')).to.be.true;
// // Checking if captions button is present
// await utils.waitForElement('#captions-btn');
// expect(await utils.isPresent('#captions-btn')).to.be.true;
// await utils.clickOn('#captions-btn');
// await utils.waitForElement('ov-pro-feature-template');
// expect(await utils.isPresent('.captions-container')).to.be.false;
// });
});

View File

@ -6,7 +6,7 @@ if [[ -z "$BASEHREF_VERSION" ]]; then
fi fi
# Replace version from "stable" to the specified one in all TypeDoc links # Replace version from "stable" to the specified one in all TypeDoc links
grep -rl '/latest/' projects src | xargs sed -i -e 's|/latest/|/'${BASEHREF_VERSION}'/|g' grep -rl '/en/stable/' projects src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g'
# Replace testapp README by openvidu-components-angular README # Replace testapp README by openvidu-components-angular README
mv README.md README-testapp.md mv README.md README-testapp.md
@ -16,12 +16,12 @@ cp ./projects/openvidu-components-angular/README.md .
npm run doc:build npm run doc:build
# Return links to "stable" version # Return links to "stable" version
grep -rl '/'${BASEHREF_VERSION}'/' projects src | xargs sed -i -e 's|/'${BASEHREF_VERSION}'/|/latest/|g' grep -rl '/en/'${BASEHREF_VERSION}'/' projects src | xargs sed -i -e 's|/en/'${BASEHREF_VERSION}'/|/en/stable/|g'
# Undo changes with READMEs # Undo changes with READMEs
rm README.md rm README.md
mv README-testapp.md README.md mv README-testapp.md README.md
# Clean previous docs from openvidu.io repo and copy new ones # Clean previous docs from openvidu.io-docs repo and copy new ones
npm run doc:clean-copy npm run doc:clean-copy

View File

@ -204,6 +204,7 @@ npm install openvidu-components-angular@3.0.0
- `publishScreen` and `unpublishScreen` methods have been renamed to `setScreenShareEnabled` - `publishScreen` and `unpublishScreen` methods have been renamed to `setScreenShareEnabled`
- `isSttReadyObs` observable has been deleted - `isSttReadyObs` observable has been deleted
- `connectRoom` method has been added - `connectRoom` method has been added
- `getRoomMetadata` method has been added to get the metadata of the room
- `getRoomName` method has been added to get the name of the room - `getRoomName` method has been added to get the name of the room
- ##### Panel Service (`PanelService`): - ##### Panel Service (`PanelService`):

View File

@ -0,0 +1,60 @@
const fs = require('fs-extra');
const concat = require('concat');
const VERSION = require('./package.json').version;
const ovWebcomponentRCPath = './dist/openvidu-webcomponent-rc';
const ovWebcomponentProdPath = './dist/openvidu-webcomponent';
module.exports.buildWebcomponent = async () => {
console.log('Building OpenVidu Web Component (' + VERSION + ')');
const tutorialWcPath = '../../openvidu-tutorials/openvidu-webcomponent/web';
const e2eWcPath = './e2e/webcomponent-app';
try {
await buildElement();
await copyFiles(tutorialWcPath);
await copyFiles(e2eWcPath);
await renameWebComponentTestName(e2eWcPath);
console.log(`OpenVidu Web Component (${VERSION}) built`);
} catch (error) {
console.error(error);
}
};
async function buildElement() {
const files = [`${ovWebcomponentRCPath}/runtime.js`, `${ovWebcomponentRCPath}/main.js`, `${ovWebcomponentRCPath}/polyfills.js`];
try {
await fs.ensureDir('./dist/openvidu-webcomponent');
await concat(files, `${ovWebcomponentProdPath}/openvidu-webcomponent-${VERSION}.js`);
await fs.copy(`${ovWebcomponentRCPath}/styles.css`, `${ovWebcomponentProdPath}/openvidu-webcomponent-${VERSION}.css`);
// await fs.copy(
// "./dist/openvidu-webcomponent/assets",
// "./openvidu-webcomponent/assets"
// );
} catch (err) {
console.error('Error executing build function in webcomponent-builds.js');
throw err;
}
}
function renameWebComponentTestName(dir) {
fs.renameSync(`${dir}/openvidu-webcomponent-${VERSION}.js`, `${dir}/openvidu-webcomponent-dev.js`);
fs.renameSync(`${dir}/openvidu-webcomponent-${VERSION}.css`, `${dir}/openvidu-webcomponent-dev.css`);
}
async function copyFiles(destination) {
if (fs.existsSync(destination)) {
try {
console.log(`Copying openvidu-webcomponent files from: ${ovWebcomponentProdPath} to: ${destination}`);
await fs.ensureDir(ovWebcomponentProdPath);
await fs.copy(ovWebcomponentProdPath, destination);
} catch (err) {
console.error('Error executing copy function in webcomponent-builds.js');
throw err;
}
}
}
this.buildWebcomponent();

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +1,65 @@
{ {
"dependencies": { "dependencies": {
"@angular/animations": "19.2.8", "@angular/animations": "18.0.0",
"@angular/cdk": "19.2.11", "@angular/cdk": "18.0.0",
"@angular/common": "19.2.8", "@angular/common": "18.0.0",
"@angular/core": "19.2.8", "@angular/core": "18.0.0",
"@angular/forms": "19.2.8", "@angular/forms": "18.0.0",
"@angular/material": "19.2.11", "@angular/material": "18.0.0",
"@angular/platform-browser": "19.2.8", "@angular/platform-browser": "18.0.0",
"@angular/platform-browser-dynamic": "19.2.8", "@angular/platform-browser-dynamic": "18.0.0",
"@angular/router": "19.2.8", "@angular/router": "18.0.0",
"@livekit/track-processors": "^0.5.6", "@livekit/track-processors": "0.3.2",
"@types/dom-mediacapture-transform": "^0.1.11",
"autolinker": "4.0.0", "autolinker": "4.0.0",
"livekit-client": "2.11.4", "livekit-client": "2.1.0",
"rxjs": "7.8.1", "rxjs": "7.5.7",
"tslib": "2.7.0", "tslib": "2.3.1",
"zone.js": "^0.15.0" "zone.js": "^0.14.6"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "19.2.9", "@angular-devkit/build-angular": "18.0.1",
"@angular/cli": "19.2.9", "@angular/cli": "18.0.1",
"@angular/compiler": "19.2.8", "@angular/compiler": "18.0.0",
"@angular/compiler-cli": "19.2.8", "@angular/compiler-cli": "18.0.0",
"@angular/elements": "18.0.0",
"@compodoc/compodoc": "^1.1.25", "@compodoc/compodoc": "^1.1.25",
"@types/jasmine": "^5.1.4", "@types/chai": "4.3.6",
"@types/node": "20.12.14", "@types/dom-mediacapture-transform": "0.1.9",
"@types/dom-webcodecs": "0.1.11",
"@types/mocha": "9.1.0",
"@types/node": "20.12.7",
"@types/selenium-webdriver": "4.1.16", "@types/selenium-webdriver": "4.1.16",
"@types/ws": "^8.5.12", "@types/ws": "^8.5.4",
"chromedriver": "138.0.0", "chai": "4.3.6",
"chromedriver": "116.0.0",
"codelyzer": "6.0.2",
"concat": "^1.0.3", "concat": "^1.0.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.0.0",
"http-server": "14.1.1", "http-server": "14.1.1",
"husky": "^9.1.6", "husky": "^8.0.3",
"jasmine": "^5.3.1", "jasmine-core": "3.10.1",
"jasmine-core": "5.3.0",
"jasmine-spec-reporter": "7.0.0", "jasmine-spec-reporter": "7.0.0",
"karma": "^6.4.4", "karma": "^6.3.9",
"karma-chrome-launcher": "3.2.0", "karma-chrome-launcher": "3.1.1",
"karma-coverage": "^2.2.1", "karma-coverage": "^2.0.3",
"karma-coverage-istanbul-reporter": "3.0.3", "karma-coverage-istanbul-reporter": "3.0.3",
"karma-jasmine": "5.1.0", "karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "2.1.0", "karma-jasmine-html-reporter": "1.7.0",
"karma-junit-reporter": "2.0.1", "karma-junit-reporter": "2.0.1",
"karma-mocha-reporter": "2.2.5", "karma-mocha-reporter": "2.2.5",
"karma-notify-reporter": "1.3.0", "karma-notify-reporter": "1.3.0",
"lint-staged": "^15.2.10", "lint-staged": "^14.0.1",
"ng-packagr": "19.2.2", "mocha": "9.2.2",
"npm-watch": "^0.13.0", "ng-packagr": "18.0.0",
"prettier": "3.3.3", "npm-watch": "^0.11.0",
"selenium-webdriver": "4.32.0", "prettier": "3.0.3",
"ts-node": "10.9.2", "selenium-webdriver": "4.12.0",
"ts-node": "10.4.0",
"tslint": "6.1.3", "tslint": "6.1.3",
"typescript": "5.8.3", "typescript": "5.4.5",
"webpack-bundle-analyzer": "^4.10.2" "webpack-bundle-analyzer": "^4.5.0"
}, },
"name": "openvidu-components-testapp", "name": "openvidu-components-testapp",
"private": true, "private": true,
@ -69,36 +74,31 @@
} }
}, },
"scripts": { "scripts": {
"start": "ng serve --configuration development --open", "husky": "cd .. && husky install",
"start-prod": "npx http-server ./dist/openvidu-components-testapp/browser --port 4200",
"start:ssl": "ng serve --ssl --configuration development --host 0.0.0.0 --port 5080",
"build": "ng build openvidu-components-testapp --configuration production", "build": "ng build openvidu-components-testapp --configuration production",
"bundle-report": "ng build openvidu-webcomponent --stats-json --configuration production && webpack-bundle-analyzer dist/openvidu-webcomponent/stats.json",
"doc:build": "npx compodoc -c ./projects/openvidu-components-angular/doc/.compodocrc.json", "doc:build": "npx compodoc -c ./projects/openvidu-components-angular/doc/.compodocrc.json",
"doc:generate-directives-tutorials": "node ./projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js", "doc:generate-directives-tutorials": "node ./projects/openvidu-components-angular/doc/scripts/generate-directive-tutorials.js",
"doc:generate-directive-tables": "node ./projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js", "doc:generate-directive-tables": "node ./projects/openvidu-components-angular/doc/scripts/generate-directive-tables.js",
"doc:clean-copy": "rm -rf ../../openvidu.io/docs/docs/reference-docs/openvidu-components-angular && cp -r ./docs/openvidu-components-angular/ ../../openvidu.io/docs/docs/reference-docs/openvidu-components-angular", "doc:clean-copy": "rm -rf ../../openvidu-docs/docs/docs/reference-docs/openvidu-components-angular && cp -r ./docs/openvidu-components-angular/ ../../openvidu-docs/docs/docs/reference-docs/openvidu-components-angular",
"doc:serve": "npx compodoc -c ../openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json --serve --port 7000", "doc:serve": "npx compodoc -c ../openvidu-components-angular/projects/openvidu-components-angular/doc/.compodocrc.json --serve --port 7000",
"doc:serve-watch": "npm-watch doc:serve", "doc:serve-watch": "npm-watch doc:serve",
"lib:serve": "ng build openvidu-components-angular --watch",
"lib:build": "ng build openvidu-components-angular --configuration production && cd ./dist/openvidu-components-angular", "lib:build": "ng build openvidu-components-angular --configuration production && cd ./dist/openvidu-components-angular",
"lib:pack": "cd ./dist/openvidu-components-angular && npm pack", "lib:copy": "cp dist/openvidu-components-angular/openvidu-components-angular-*.tgz ../../openvidu-call-livekit/openvidu-call-front",
"lib:copy": "cp dist/openvidu-components-angular/openvidu-components-angular-*.tgz ../../openvidu-call/frontend", "lib:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/angular.test.js",
"lib:e2e-ci": "cross-env LAUNCH_MODE=CI npm run lib:e2e",
"lib:serve": "ng build openvidu-components-angular --watch",
"lib:test": "ng test openvidu-components-angular --no-watch --code-coverage", "lib:test": "ng test openvidu-components-angular --no-watch --code-coverage",
"e2e:nested-all": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/*.test.js", "lint": "ng lint",
"e2e:nested-events": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/events.test.js", "start": "ng serve --configuration development --open",
"e2e:nested-structural-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/structural-directives.test.js", "start-prod": "npx http-server ./dist/openvidu-components-testapp/ --port 4200",
"e2e:nested-attribute-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/attribute-directives.test.js", "start:ssl": "ng serve --ssl --configuration development --host 0.0.0.0 --port 5080",
"e2e:lib-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/api-directives.test.js", "webcomponent:testing-build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration testing && node ./openvidu-webcomponent-build.js",
"e2e:lib-internal-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/internal-directives.test.js", "webcomponent:build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration production && node ./openvidu-webcomponent-build.js",
"e2e:lib-chat": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/chat.test.js", "webcomponent:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent-e2e/**/*.test.js",
"e2e:lib-events": "tsc --project ./e2e && npx jasmine ./e2e/dist/events.test.js", "webcomponent:e2e-ci": "cross-env LAUNCH_MODE=CI npm run webcomponent:e2e",
"e2e:lib-media-devices": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/media-devices.test.js", "webcomponent:serve-testapp": "npx http-server ./e2e/webcomponent-app/ && echo http://localhost:8080/?OV_URL=https://localhost:4443&OV_SECRET=MY_SECRET&prejoin=false",
"e2e:lib-panels": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/panels.test.js", "simulate:multiparty": "livekit-cli load-test --url ws://localhost:7880 --api-key devkey --api-secret secret --room daily-call --publishers 8 --audio-publishers 8 --identity-prefix Participant --identity publisher"
"e2e:lib-screensharing": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/screensharing.test.js",
"e2e:lib-stream": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/stream.test.js",
"e2e:lib-toolbar": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/toolbar.test.js",
"simulate:multiparty": "livekit-cli load-test --url ws://localhost:7880 --api-key devkey --api-secret secret --room daily-call --publishers 8 --audio-publishers 8 --identity-prefix Participant --identity publisher",
"husky": "cd .. && husky install"
}, },
"version": "3.3.0" "version": "3.0.0-beta2"
} }

View File

@ -8,7 +8,7 @@ Angular Components are the simplest way to create real-time videoconferencing ap
## Getting Started ## Getting Started
To get started with OpenVidu Components Angular, visit our [**Getting Started guide**](https://openvidu.io/latest/docs/ui-components/angular-components/). To get started with OpenVidu Components Angular, visit our [**Getting Started guide**](https://openvidu.io/docs/ui-components/angular-components/).
1. Create an Angular Project (>= 17.0.0) 1. Create an Angular Project (>= 17.0.0)
@ -54,28 +54,24 @@ You can also customize the styles in your `styles.scss` file:
```scss ```scss
:root { :root {
/* Basic colors */ --ov-primary-color: #303030;
--ov-background-color: #303030; // Background color --ov-secondary-color: #3e3f3f;
--ov-surface-color: #ffffff; // Surfaces colors (panels, dialogs) --ov-tertiary-color: #598eff;
--ov-warn-color: #eb5144;
--ov-accent-color: #ffae35;
--ov-light-color: #e6e6e6;
/* Text colors */ --ov-logo-background-color: #3a3d3d;
--ov-text-primary-color: #ffffff; // Text color over primary background
--ov-text-surface-color: #1d1d1d; // Text color over surface background
/* Action colors */ --ov-text-color: #ffffff;
--ov-primary-action-color: #273235; // Primary color for buttons, etc.
--ov-secondary-action-color: #f1f1f1; // Secondary color for buttons, etc.
--ov-accent-action-color: #0089ab; // Color for highlighted elements
/* Status colors */ --ov-panel-text-color: #1d1d1d;
--ov-error-color: #eb5144; // Error color --ov-panel-background: #ffffff;
--ov-warn-color: #ffba53; // Warning color
/* Radius */ --ov-buttons-radius: 50%;
--ov-toolbar-buttons-radius: 50%; // Radius for toolbar buttons --ov-leave-button-radius: 10px;
--ov-leave-button-radius: 10px; // Radius for leave button --ov-video-radius: 5px;
--ov-video-radius: 5px; // Radius for video elements --ov-panel-radius: 5px;
--ov-surface-radius: 5px; // Radius for surface elements
} }
``` ```
@ -92,7 +88,7 @@ You can also customize the styles in your `styles.scss` file:
## API Documentation ## API Documentation
For detailed information on OpenVidu Angular Components, refer to our [**API Reference**](https://openvidu.io/latest/docs/reference-docs/openvidu-components-angular/). For detailed information on OpenVidu Angular Components, refer to our [**API Reference**](https://openvidu.io/docs/reference-docs/openvidu-components-angular).
## Support ## Support

View File

@ -3,334 +3,168 @@ const glob = require('glob');
const startApiLine = '<!-- start-dynamic-api-directives-content -->'; const startApiLine = '<!-- start-dynamic-api-directives-content -->';
const apiDirectivesTable = const apiDirectivesTable =
'| **Parameter** | **Type** | **Reference** | \n' + '| **Parameter** | **Type** | **Reference** | \n' +
'|:--------------------------------: | :-------: | :---------------------------------------------: |'; '|:--------------------------------: | :-------: | :---------------------------------------------: |';
const endApiLine = '<!-- end-dynamic-api-directives-content -->'; const endApiLine = '<!-- end-dynamic-api-directives-content -->';
/**
* Get all directive files from the API directives directory
*/
function getDirectiveFiles() { function getDirectiveFiles() {
const directivesDir = 'projects/openvidu-components-angular/src/lib/directives/api'; // Directory where directive files are located
return listFiles(directivesDir, '.directive.ts'); const directivesDir = 'projects/openvidu-components-angular/src/lib/directives/api';
return listFiles(directivesDir, '.directive.ts');
} }
/**
* Get all component files
*/
function getComponentFiles() { function getComponentFiles() {
const componentsDir = 'projects/openvidu-components-angular/src/lib/components'; // Directory where component files are located
return listFiles(componentsDir, '.component.ts'); const componentsDir = 'projects/openvidu-components-angular/src/lib/components';
return listFiles(componentsDir, '.component.ts');
} }
/**
* Get all admin files
*/
function getAdminFiles() { function getAdminFiles() {
const componentsDir = 'projects/openvidu-components-angular/src/lib/admin'; // Directory where component files are located
return listFiles(componentsDir, '.component.ts'); const componentsDir = 'projects/openvidu-components-angular/src/lib/admin';
return listFiles(componentsDir, '.component.ts');
} }
/**
* List all files with specific extension in directory
*/
function listFiles(directoryPath, fileExtension) { function listFiles(directoryPath, fileExtension) {
const files = glob.sync(`${directoryPath}/**/*${fileExtension}`); const files = glob.sync(`${directoryPath}/**/*${fileExtension}`);
if (files.length === 0) { if (files.length === 0) {
throw new Error(`No ${fileExtension} files found in ${directoryPath}`); throw new Error(`No ${fileExtension} files found in ${directoryPath}`);
} }
return files; return files;
} }
/**
* Extract component selector from component file
*/
function getComponentSelector(componentFile) {
const componentContent = fs.readFileSync(componentFile, 'utf8');
const selectorMatch = componentContent.match(/@Component\({[^]*?selector:\s*['"]([^'"]+)['"][^]*?}\)/s);
if (!selectorMatch) {
throw new Error(`Unable to find selector in component file: ${componentFile}`);
}
return selectorMatch[1];
}
/**
* Check if a directive class has @internal annotation
*/
function isInternalDirective(directiveContent, className) {
const classRegex = new RegExp(`(/\\*\\*[\\s\\S]*?\\*/)?\\s*@Directive\\([\\s\\S]*?\\)\\s*export\\s+class\\s+${escapeRegex(className)}`, 'g');
const match = classRegex.exec(directiveContent);
if (match && match[1]) {
return match[1].includes('@internal');
}
return false;
}
/**
* Extract attribute name from selector for a specific component
*/
function extractAttributeForComponent(selector, componentSelector) {
// Split selector by comma and trim whitespace
const selectorParts = selector.split(',').map(part => part.trim());
// Find the part that matches our component
for (const part of selectorParts) {
if (part.includes(componentSelector)) {
// Extract attribute from this specific part
const attributeMatch = part.match(/\[([^\]]+)\]/);
if (attributeMatch) {
return attributeMatch[1];
}
}
}
// Fallback: if no specific match, return the first attribute found
const fallbackMatch = selector.match(/\[([^\]]+)\]/);
return fallbackMatch ? fallbackMatch[1] : null;
}
/**
* Extract all directive classes from a directive file
*/
function extractDirectiveClasses(directiveContent) {
const classes = [];
// Regex to find all directive class definitions with their preceding @Directive decorators
const directiveClassRegex = /@Directive\(\s*{\s*selector:\s*['"]([^'"]+)['"][^}]*}\s*\)\s*export\s+class\s+(\w+)/gs;
let match;
while ((match = directiveClassRegex.exec(directiveContent)) !== null) {
const selector = match[1];
const className = match[2];
// Skip internal directives
if (isInternalDirective(directiveContent, className)) {
console.log(`Skipping internal directive: ${className}`);
continue;
}
classes.push({
selector,
className
});
}
return classes;
}
/**
* Extract all directives from a directive file that match a component selector
*/
function extractDirectivesForComponent(directiveFile, componentSelector) {
const directiveContent = fs.readFileSync(directiveFile, 'utf8');
const directives = [];
// Get all directive classes in the file (excluding internal ones)
const directiveClasses = extractDirectiveClasses(directiveContent);
// Filter classes that match the component selector
const matchingClasses = directiveClasses.filter(directiveClass =>
directiveClass.selector.includes(componentSelector)
);
// For each matching class, extract input type information
matchingClasses.forEach(directiveClass => {
// Extract the correct attribute name for this component
const attributeName = extractAttributeForComponent(directiveClass.selector, componentSelector);
if (attributeName) {
const inputInfo = extractInputInfo(directiveContent, attributeName, directiveClass.className);
if (inputInfo) {
directives.push({
attribute: attributeName,
type: inputInfo.type,
className: directiveClass.className
});
}
}
});
return directives;
}
/**
* Extract input information (type) for a specific attribute and class
*/
function extractInputInfo(directiveContent, attributeName, className) {
// Create a regex to find the specific class section
const classRegex = new RegExp(`export\\s+class\\s+${escapeRegex(className)}[^}]*?{([^]*?)(?=export\\s+class|$)`, 's');
const classMatch = directiveContent.match(classRegex);
if (!classMatch) {
console.warn(`Could not find class ${className}`);
return null;
}
const classContent = classMatch[1];
// Regex to find the @Input setter for this attribute within the class
const inputRegex = new RegExp(
`@Input\\(\\)\\s+set\\s+${escapeRegex(attributeName)}\\s*\\(\\s*\\w+:\\s*([^)]+)\\s*\\)`,
'g'
);
const inputMatch = inputRegex.exec(classContent);
if (!inputMatch) {
console.warn(`Could not find @Input setter for attribute: ${attributeName} in class: ${className}`);
return null;
}
let type = inputMatch[1].trim();
// Clean up the type (remove extra whitespace, etc.)
type = type.replace(/\s+/g, ' ');
return {
type: type
};
}
/**
* Escape special regex characters
*/
function escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Generate API directives table for components
*/
function generateApiDirectivesTable(componentFiles, directiveFiles) {
componentFiles.forEach((componentFile) => {
try {
console.log(`Processing component: ${componentFile}`);
const componentSelector = getComponentSelector(componentFile);
const readmeFilePath = componentFile.replace('.ts', '.md');
console.log(`Component selector: ${componentSelector}`);
// Initialize table with header
initializeDynamicTableContent(readmeFilePath);
const allDirectives = [];
// Extract directives from all directive files
directiveFiles.forEach((directiveFile) => {
console.log(`Checking directive file: ${directiveFile}`);
const directives = extractDirectivesForComponent(directiveFile, componentSelector);
allDirectives.push(...directives);
});
console.log(`Found ${allDirectives.length} directives for ${componentSelector}`);
// Sort directives alphabetically by attribute name
allDirectives.sort((a, b) => a.attribute.localeCompare(b.attribute));
// Add rows to table
allDirectives.forEach((directive) => {
addRowToTable(readmeFilePath, directive.attribute, directive.type, directive.className);
});
// If no directives found, add "no directives" message
if (allDirectives.length === 0) {
removeApiTableContent(readmeFilePath);
}
} catch (error) {
console.error(`Error processing component ${componentFile}:`, error.message);
}
});
}
/**
* Initialize table with header
*/
function initializeDynamicTableContent(filePath) { function initializeDynamicTableContent(filePath) {
replaceDynamicTableContent(filePath, apiDirectivesTable); replaceDynamicTableContent(filePath, apiDirectivesTable);
} }
/**
* Replace table content with "no directives" message
*/
function removeApiTableContent(filePath) { function removeApiTableContent(filePath) {
const content = '_No API directives available for this component_. \n'; const content = '_No API directives available for this component_. \n';
replaceDynamicTableContent(filePath, content); replaceDynamicTableContent(filePath, content);
} }
/** function apiTableContentIsEmpty(filePath) {
* Add a row to the markdown table try {
*/ const data = fs.readFileSync(filePath, 'utf8');
const startIdx = data.indexOf(startApiLine);
const endIdx = data.indexOf(endApiLine);
if (startIdx !== -1 && endIdx !== -1) {
const capturedContent = data.substring(startIdx + startApiLine.length, endIdx).trim();
return capturedContent === apiDirectivesTable;
}
return false;
} catch (error) {
return false;
}
}
function writeApiDirectivesTable(componentFiles, directiveFiles) {
componentFiles.forEach((componentFile) => {
// const componentName = componentFile.split('/').pop()
const componentFileName = componentFile.split('/').pop().replace('.component.ts', '');
const componentName = componentFileName.replace(/(?:^|-)([a-z])/g, (_, char) => char.toUpperCase());
const readmeFilePath = componentFile.replace('.ts', '.md');
const componentContent = fs.readFileSync(componentFile, 'utf8');
const selectorMatch = componentContent.match(/@Component\({[^]*?selector: ['"]([^'"]+)['"][^]*?}\)/);
const componentSelectorName = selectorMatch[1];
initializeDynamicTableContent(readmeFilePath);
if (!componentSelectorName) {
throw new Error(`Unable to find the component name in the file ${componentFileName}`);
}
// const directiveRegex = new RegExp(`@Directive\\(\\s*{[^}]*selector:\\s*['"]${componentName}\\s*\\[([^'"]+)\\]`, 'g');
const directiveRegex = /^\s*(selector):\s*(['"])(.*?)\2\s*$/gm;
directiveFiles.forEach((directiveFile) => {
const directiveContent = fs.readFileSync(directiveFile, 'utf8');
let directiveNameMatch;
while ((directiveNameMatch = directiveRegex.exec(directiveContent)) !== null) {
if (directiveNameMatch[0].includes('@Directive({\n//')) {
// Skip directives that are commented out
continue;
}
const selectorValue = directiveNameMatch[3].split(',');
const directiveMatch = selectorValue.find((value) => value.includes(componentSelectorName));
if (directiveMatch) {
const directiveName = directiveMatch.match(/\[(.*?)\]/).pop();
const className = directiveName.replace(/(^\w{1})|(\s+\w{1})/g, (letter) => letter.toUpperCase()) + 'Directive';
const inputRegex = new RegExp(
`@Input\\(\\)\\s+set\\s+(${directiveName.replace(/\[/g, '\\[').replace(/\]/g, '\\]')})\\((\\w+):\\s+(\\w+)`
);
const inputMatch = directiveContent.match(inputRegex);
const inputType = inputMatch && inputMatch.pop();
if (inputType && className) {
let finalClassName = componentName === 'Videoconference' ? className : componentName + className;
addRowToTable(readmeFilePath, directiveName, inputType, finalClassName);
}
} else {
console.log(`The selector "${componentSelectorName}" does not match with ${selectorValue}. Skipping...`);
}
}
});
if (apiTableContentIsEmpty(readmeFilePath)) {
removeApiTableContent(readmeFilePath);
}
});
}
// Function to add a row to a Markdown table in a file
function addRowToTable(filePath, parameter, type, reference) { function addRowToTable(filePath, parameter, type, reference) {
try { // Read the current content of the file
const data = fs.readFileSync(filePath, 'utf8'); try {
const markdownRow = `| **${parameter}** | \`${type}\` | [${reference}](../directives/${reference}.html) |`; const data = fs.readFileSync(filePath, 'utf8');
const lines = data.split('\n'); // Define the target line and the Markdown row
const targetIndex = lines.findIndex((line) => line.includes(endApiLine)); const markdownRow = `| **${parameter}** | \`${type}\` | [${reference}](../directives/${reference}.html) |`;
if (targetIndex !== -1) { // Find the line that contains the table
lines.splice(targetIndex, 0, markdownRow); const lines = data.split('\n');
const updatedContent = lines.join('\n'); const targetIndex = lines.findIndex((line) => line.includes(endApiLine));
fs.writeFileSync(filePath, updatedContent, 'utf8');
console.log(`Added directive: ${parameter} -> ${reference}`); if (targetIndex !== -1) {
} else { // Insert the new row above the target line
console.error('End marker not found in file:', filePath); lines.splice(targetIndex, 0, markdownRow);
}
} catch (error) { // Join the lines back together
console.error('Error adding row to table:', error); const updatedContent = lines.join('\n');
}
// Write the updated content to the file
fs.writeFileSync(filePath, updatedContent, 'utf8');
console.log('Row added successfully.');
} else {
console.error('Table not found in the file.');
}
} catch (error) {
console.error('Error writing to file:', error);
}
} }
/**
* Replace content between start and end markers
*/
function replaceDynamicTableContent(filePath, content) { function replaceDynamicTableContent(filePath, content) {
try { // Read the current content of the file
const data = fs.readFileSync(filePath, 'utf8'); try {
const pattern = new RegExp(`${startApiLine}([\\s\\S]*?)${endApiLine}`, 'g'); const data = fs.readFileSync(filePath, 'utf8');
const pattern = new RegExp(`${startApiLine}([\\s\\S]*?)${endApiLine}`, 'g');
const modifiedContent = data.replace(pattern, (match, capturedContent) => { // Replace the content between startLine and endLine with the replacement table
return startApiLine + '\n' + content + '\n' + endApiLine; const modifiedContent = data.replace(pattern, (match, capturedContent) => {
}); return startApiLine + '\n' + content + '\n' + endApiLine;
});
fs.writeFileSync(filePath, modifiedContent, 'utf8'); // Write the modified content back to the file
console.log(`Updated table content in: ${filePath}`); fs.writeFileSync(filePath, modifiedContent, 'utf8');
} catch (error) { } catch (error) {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
console.log(`${filePath} not found! Maybe it is an internal component. Skipping...`); console.log(`${filePath} not found! Maybe it is an internal component. Skipping...`);
} else { } else {
console.error('Error writing to file:', error); console.error('Error writing to file:', error);
} }
} }
} }
// Main execution const directiveFiles = getDirectiveFiles();
if (require.main === module) { const componentFiles = getComponentFiles();
try { const adminFiles = getAdminFiles();
const directiveFiles = getDirectiveFiles(); writeApiDirectivesTable(componentFiles.concat(adminFiles), directiveFiles);
const componentFiles = getComponentFiles();
const adminFiles = getAdminFiles();
console.log('Starting directive table generation...');
generateApiDirectivesTable(componentFiles.concat(adminFiles), directiveFiles);
console.log('Directive table generation completed!');
} catch (error) {
console.error('Script execution failed:', error);
process.exit(1);
}
}
// Export functions for testing
module.exports = {
generateApiDirectivesTable,
getDirectiveFiles,
getComponentFiles,
getAdminFiles
};

View File

@ -5,7 +5,8 @@
"../src/lib/directives/**/*.ts", "../src/lib/directives/**/*.ts",
"../src/lib/services/**/*.ts", "../src/lib/services/**/*.ts",
"../src/lib/models/**/*.ts", "../src/lib/models/**/*.ts",
"../src/lib/pipes/**/*.ts" "../src/lib/pipes/**/*.ts",
// "../../../src/app/openvidu-webcomponent/**/*.ts",
], ],
"exclude": [ "exclude": [
"src/test.ts", "src/test.ts",

View File

@ -38,7 +38,7 @@ module.exports = function (config) {
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['ChromeHeadless'], browsers: ['Chrome'],
singleRun: false, singleRun: false,
restartOnFileChange: true restartOnFileChange: true
}); });

View File

@ -1,418 +0,0 @@
{
"name": "openvidu-components-angular",
"version": "3.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvidu-components-angular",
"version": "3.3.0",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/animations": "^17.0.0 || ^18.0.0",
"@angular/cdk": "^17.0.0 || ^18.0.0",
"@angular/common": "^17.0.0 || ^18.0.0",
"@angular/core": "^17.0.0 || ^18.0.0",
"@angular/forms": "^17.0.0 || ^18.0.0",
"@angular/material": "^17.0.0 || ^18.0.0",
"@livekit/track-processors": "^0.3.2",
"autolinker": "^4.0.0",
"buffer": "^6.0.3",
"livekit-client": "^2.1.0"
}
},
"node_modules/@angular/animations": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.8.tgz",
"integrity": "sha512-dMSn2hg70siv3lhP+vqhMbgc923xw6XBUvnpCPEzhZqFHvPXfh/LubmsD5RtqHmjWebXtgVcgS+zg3Gq3jB2lg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.8"
}
},
"node_modules/@angular/cdk": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.8.tgz",
"integrity": "sha512-J8A2FkwTBzLleAEWz6EgW73dEoeq87GREBPjTv8+2JV09LX+V3hnbgNk6zWq5k4OXtQNg9WrWP9QyRbUyA597g==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"optionalDependencies": {
"parse5": "^7.1.2"
},
"peerDependencies": {
"@angular/common": "^18.0.0 || ^19.0.0",
"@angular/core": "^18.0.0 || ^19.0.0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/common": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.8.tgz",
"integrity": "sha512-TYsKtE5nVaIScWSLGSO34Skc+s3hB/BujSddnfQHoNFvPT/WR0dfmdlpVCTeLj+f50htFoMhW11tW99PbK+whQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/core": "18.2.8",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/core": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.8.tgz",
"integrity": "sha512-NwIuX/Iby1jT6Iv1/s6S3wOFf8xfuQR3MPGvKhGgNtjXLbHG+TXceK9+QPZC0s9/Z8JR/hz+li34B79GrIKgUg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.14.10"
}
},
"node_modules/@angular/forms": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.8.tgz",
"integrity": "sha512-JCLki7KC6D5vF6dE6yGlBmW33khIgpHs8N9SzuiJtkQqNDTIQA8cPsGV6qpLpxflxASynQOX5lDkWYdQyfm77Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/common": "18.2.8",
"@angular/core": "18.2.8",
"@angular/platform-browser": "18.2.8",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/material": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.8.tgz",
"integrity": "sha512-wQGMVsfQ9lQfih2VsWAvV4z3S3uBxrxc61owlE+K0T1BxH9u/jo3A/rnRitIdvR/L4NnYlfhCnmrW9K+Pl+WCg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/animations": "^18.0.0 || ^19.0.0",
"@angular/cdk": "18.2.8",
"@angular/common": "^18.0.0 || ^19.0.0",
"@angular/core": "^18.0.0 || ^19.0.0",
"@angular/forms": "^18.0.0 || ^19.0.0",
"@angular/platform-browser": "^18.0.0 || ^19.0.0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/platform-browser": {
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.8.tgz",
"integrity": "sha512-EPai4ZPqSq3ilLJUC85kPi9wo5j5suQovwtgRyjM/75D9Qy4TV19g8hkVM5Co/zrltO8a2G6vDscCNI5BeGw2A==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
},
"peerDependencies": {
"@angular/animations": "18.2.8",
"@angular/common": "18.2.8",
"@angular/core": "18.2.8"
},
"peerDependenciesMeta": {
"@angular/animations": {
"optional": true
}
}
},
"node_modules/@bufbuild/protobuf": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz",
"integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==",
"license": "(Apache-2.0 AND BSD-3-Clause)",
"peer": true
},
"node_modules/@livekit/protocol": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.24.0.tgz",
"integrity": "sha512-9dCsqnkMn7lvbI4NGh18zhLDsrXyUcpS++TEFgEk5Xv1WM3R2kT3EzqgL1P/mr3jaabM6rJ8wZA/KJLuQNpF5w==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@bufbuild/protobuf": "^1.10.0"
}
},
"node_modules/@livekit/track-processors": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@livekit/track-processors/-/track-processors-0.3.2.tgz",
"integrity": "sha512-4JUCzb7yIKoVsTo8J6FTzLZJHcI6DihfX/pGRDg0SOGaxprcDPrt8jaDBBTsnGBSXHeMxl2ugN+xQjdCWzLKEA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@mediapipe/holistic": "0.5.1675471629",
"@mediapipe/tasks-vision": "0.10.9"
},
"peerDependencies": {
"livekit-client": "^1.12.0 || ^2.1.0"
}
},
"node_modules/@mediapipe/holistic": {
"version": "0.5.1675471629",
"resolved": "https://registry.npmjs.org/@mediapipe/holistic/-/holistic-0.5.1675471629.tgz",
"integrity": "sha512-qY+cxtDeSOvVtevrLgnodiwXYaAtPi7dHZtNv/bUCGEjFicAOYtMmrZSqMmbPkTB2+4jLnPF1vgshkAqQRSYAw==",
"license": "Apache-2.0",
"peer": true
},
"node_modules/@mediapipe/tasks-vision": {
"version": "0.10.9",
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.9.tgz",
"integrity": "sha512-/gFguyJm1ng4Qr7VVH2vKO+zZcQd8wc3YafUfvBuYFX0Y5+CvrV+VNPEVkl5W/gUZF5KNKNZAiaHPULGPCIjyQ==",
"license": "Apache-2.0",
"peer": true
},
"node_modules/autolinker": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-4.0.0.tgz",
"integrity": "sha512-fl5Kh6BmEEZx+IWBfEirnRUU5+cOiV0OK7PEt0RBKvJMJ8GaRseIOeDU3FKf4j3CE5HVefcjHmhYPOcaVt0bZw==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"peer": true
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/livekit-client": {
"version": "2.5.9",
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.5.9.tgz",
"integrity": "sha512-oDpK6SKYB1F+mNO+25DA0bF0cD2XoOJeD8ji4YQpzDBQv2IxeyKrQhoqXAqrYgIKuiMNkImSf+yg2v7EHSl4Og==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@livekit/protocol": "1.24.0",
"events": "^3.3.0",
"loglevel": "^1.8.0",
"sdp-transform": "^2.14.1",
"ts-debounce": "^4.0.0",
"tslib": "2.7.0",
"typed-emitter": "^2.1.0",
"webrtc-adapter": "^9.0.0"
}
},
"node_modules/loglevel": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/parse5": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz",
"integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"entities": "^4.5.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/sdp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz",
"integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==",
"license": "MIT",
"peer": true
},
"node_modules/sdp-transform": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.2.tgz",
"integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==",
"license": "MIT",
"peer": true,
"bin": {
"sdp-verify": "checker.js"
}
},
"node_modules/ts-debounce": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/ts-debounce/-/ts-debounce-4.0.0.tgz",
"integrity": "sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==",
"license": "MIT",
"peer": true
},
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"license": "0BSD"
},
"node_modules/typed-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz",
"integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==",
"license": "MIT",
"peer": true,
"optionalDependencies": {
"rxjs": "*"
}
},
"node_modules/webrtc-adapter": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.1.tgz",
"integrity": "sha512-1AQO+d4ElfVSXyzNVTOewgGT/tAomwwztX/6e3totvyyzXPvXIIuUUjAmyZGbKBKbZOXauuJooZm3g6IuFuiNQ==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"sdp": "^3.2.0"
},
"engines": {
"node": ">=6.0.0",
"npm": ">=3.10.0"
}
},
"node_modules/zone.js": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"license": "MIT",
"peer": true
}
}
}

View File

@ -4,16 +4,15 @@
}, },
"name": "openvidu-components-angular", "name": "openvidu-components-angular",
"peerDependencies": { "peerDependencies": {
"@angular/animations": "^17.0.0 || ^18.0.0 || ^19.0.0", "@angular/animations": "^17.0.0 || ^18.0.0",
"@angular/cdk": "^17.0.0 || ^18.0.0 || ^19.0.0", "@angular/common": "^17.0.0 || ^18.0.0",
"@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0", "@angular/core": "^17.0.0 || ^18.0.0",
"@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0", "@angular/forms": "^17.0.0 || ^18.0.0",
"@angular/forms": "^17.0.0 || ^18.0.0 || ^19.0.0", "@angular/material": "^17.0.0 || ^18.0.0",
"@angular/material": "^17.0.0 || ^18.0.0 || ^19.0.0",
"autolinker": "^4.0.0", "autolinker": "^4.0.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"livekit-client": "^2.1.0", "livekit-client": "2.1.0",
"@livekit/track-processors": "^0.3.2" "@livekit/track-processors": "0.3.2"
}, },
"version": "3.3.0" "version": "3.0.0-beta2"
} }

View File

@ -1,6 +1,6 @@
<div class="dashboard-container"> <div class="dashboard-container">
<mat-toolbar class="header"> <mat-toolbar class="header">
<span>{{ title || ('ADMIN.DASHBOARD_TITLE' | translate) }}</span> <span>{{ 'ADMIN.DASHBOARD_TITLE' | translate }}</span>
<span class="toolbar-spacer"></span> <span class="toolbar-spacer"></span>
<button class="logout-btn" mat-icon-button aria-label="Refresh" (click)="logout()"> <button class="logout-btn" mat-icon-button aria-label="Refresh" (click)="logout()">

View File

@ -4,6 +4,5 @@ With the following directives you can modify the default User Interface with the
<!-- start-dynamic-api-directives-content --> <!-- start-dynamic-api-directives-content -->
| **Parameter** | **Type** | **Reference** | | **Parameter** | **Type** | **Reference** |
|:--------------------------------: | :-------: | :---------------------------------------------: | |:--------------------------------: | :-------: | :---------------------------------------------: |
| **navbarTitle** | `string` | [AdminDashboardTitleDirective](../directives/AdminDashboardTitleDirective.html) | | **recordingsList** | `RecordingInfo` | [AdminDashboardRecordingsListDirective](../directives/AdminDashboardRecordingsListDirective.html) |
| **recordingsList** | `RecordingInfo[]` | [AdminDashboardRecordingsListDirective](../directives/AdminDashboardRecordingsListDirective.html) |
<!-- end-dynamic-api-directives-content --> <!-- end-dynamic-api-directives-content -->

View File

@ -4,16 +4,16 @@
.header { .header {
height: 50px; height: 50px;
background-color: var(--ov-secondary-action-color); background-color: var(--ov-secondary-color);
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.logout-btn { .logout-btn {
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.dashboard-body { .dashboard-body {
background-color: var(--ov-secondary-action-color); background-color: var(--ov-secondary-color);
height: calc(100% - 75px); height: calc(100% - 75px);
} }
.toolbar-spacer { .toolbar-spacer {
@ -41,17 +41,17 @@
#sort-menu-btn { #sort-menu-btn {
margin-left: 5px; margin-left: 5px;
background-color: var(--ov-surface-color); background-color: var(--ov-panel-background);
color: var(--ov-text-primary-color); color: var(--ov-panel-text-color);
} }
.search-bar { .search-bar {
height: 95%; height: 95%;
width: 30%; width: 30%;
display: flex; display: flex;
background-color: var(--ov-surface-color); background-color: var(--ov-panel-background);
padding: 0px 10px; padding: 0px 10px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
} }
#search-input { #search-input {
@ -87,7 +87,7 @@
} }
.recordings-container { .recordings-container {
background-color: var(--ov-secondary-action-color); background-color: var(--ov-secondary-color);
height: calc(100% - 50px); height: calc(100% - 50px);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@ -106,7 +106,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background-color: var(--ov-surface-color); background-color: var(--ov-panel-background);
} }
} }
@ -135,18 +135,18 @@
margin-right: -50%; margin-right: -50%;
top: 50%; top: 50%;
left: 50%; left: 50%;
background-color: var(--ov-secondary-action-color); background-color: var(--ov-logo-background-color);
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
} }
.video-btns button #play { .video-btns button #play {
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.video-btns button #download { .video-btns button #download {
color: var(--ov-accent-action-color); color: var(--ov-tertiary-color);
} }
.video-btns button #delete { .video-btns button #delete {
color: var(--ov-error-color); color: var(--ov-warn-color);
} }
.video-info-container > div { .video-info-container > div {
@ -167,7 +167,7 @@
.video-card-tag { .video-card-tag {
font-size: 13px; font-size: 13px;
color: var(--ov-text-primary-color); color: var(--ov-panel-text-color);
font-weight: bold; font-weight: bold;
} }
@ -204,16 +204,16 @@
transform: translateX(-50%); transform: translateX(-50%);
.load-more-btn { .load-more-btn {
background-color: var(--ov-surface-color); background-color: var(--ov-panel-background);
color: var(--ov-text-primary-color); color: var(--ov-panel-text-color);
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
} }
} }
.footer { .footer {
height: 25px; height: 25px;
background-color: var(--ov-background-color); background-color: var(--ov-primary-color);
color: var(--ov-text-primary-color); color: var(--ov-text-color);
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@ -225,7 +225,7 @@
gap: 2px; gap: 2px;
} }
.footer a { .footer a {
color: var(--ov-accent-action-color); color: var(--ov-tertiary-color);
padding-left: 5px; padding-left: 5px;
} }
@ -234,7 +234,7 @@
width: 100%; width: 100%;
display: table; display: table;
text-align: center; text-align: center;
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
/* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */ /* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */
::ng-deep .mat-form-field-appearance-fill .mat-form-field-flex { ::ng-deep .mat-form-field-appearance-fill .mat-form-field-flex {

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminDashboardComponent } from './admin-dashboard.component';
describe('DashboardComponent', () => {
let component: AdminDashboardComponent;
let fixture: ComponentFixture<AdminDashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminDashboardComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AdminDashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -2,14 +2,13 @@ import { Component, OnInit, Output, EventEmitter, OnDestroy } from '@angular/cor
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { RecordingDeleteRequestedEvent, RecordingInfo, RecordingStatus } from '../../models/recording.model'; import { RecordingDeleteRequestedEvent, RecordingInfo, RecordingStatus } from '../../models/recording.model';
import { ActionService } from '../../services/action/action.service'; import { ActionService } from '../../services/action/action.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service';
import { RecordingService } from '../../services/recording/recording.service'; import { RecordingService } from '../../services/recording/recording.service';
@Component({ @Component({
selector: 'ov-admin-dashboard', selector: 'ov-admin-dashboard',
templateUrl: './admin-dashboard.component.html', templateUrl: './admin-dashboard.component.html',
styleUrls: ['./admin-dashboard.component.scss'], styleUrls: ['./admin-dashboard.component.scss']
standalone: false
}) })
export class AdminDashboardComponent implements OnInit, OnDestroy { export class AdminDashboardComponent implements OnInit, OnDestroy {
/** /**
@ -36,11 +35,6 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
*/ */
@Output() onLogoutRequested: EventEmitter<void> = new EventEmitter<void>(); @Output() onLogoutRequested: EventEmitter<void> = new EventEmitter<void>();
/**
* @internal
*/
title = '';
/** /**
* @internal * @internal
*/ */
@ -61,9 +55,7 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
* @internal * @internal
*/ */
recordingStatusEnum = RecordingStatus; recordingStatusEnum = RecordingStatus;
private recordingsSub: Subscription; private adminSubscription: Subscription;
private titleSub: Subscription;
/** /**
* @internal * @internal
*/ */
@ -84,8 +76,7 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
if (this.recordingsSub) this.recordingsSub.unsubscribe(); if (this.adminSubscription) this.adminSubscription.unsubscribe();
if (this.titleSub) this.titleSub.unsubscribe();
} }
/** /**
@ -254,7 +245,7 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
} }
private subscribeToAdminDirectives() { private subscribeToAdminDirectives() {
this.recordingsSub = this.libService.adminRecordingsList$.subscribe((recordings: RecordingInfo[]) => { this.adminSubscription = this.libService.adminRecordingsList$.subscribe((recordings: RecordingInfo[]) => {
// Remove the recordings that are marked for deletion // Remove the recordings that are marked for deletion
this.filterDeletedRecordings(recordings); this.filterDeletedRecordings(recordings);
@ -264,9 +255,5 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
this.sortRecordings(); this.sortRecordings();
}); });
this.titleSub = this.libService.adminDashboardTitle$.subscribe((value) => {
this.title = value;
});
} }
} }

View File

@ -1,4 +1,4 @@
<mat-toolbar class="header"> {{ title || ('ADMIN.DASHBOARD_TITLE' | translate) }} </mat-toolbar> <mat-toolbar class="header"> {{ 'ADMIN.DASHBOARD_TITLE' | translate }} </mat-toolbar>
<div class="center-container"> <div class="center-container">
<div *ngIf="loading" class="outer"> <div *ngIf="loading" class="outer">

View File

@ -6,5 +6,4 @@ With the following directives you can modify the default User Interface with the
| **Parameter** | **Type** | **Reference** | | **Parameter** | **Type** | **Reference** |
|:--------------------------------: | :-------: | :---------------------------------------------: | |:--------------------------------: | :-------: | :---------------------------------------------: |
| **error** | `any` | [AdminLoginErrorDirective](../directives/AdminLoginErrorDirective.html) | | **error** | `any` | [AdminLoginErrorDirective](../directives/AdminLoginErrorDirective.html) |
| **navbarTitle** | `any` | [AdminLoginTitleDirective](../directives/AdminLoginTitleDirective.html) |
<!-- end-dynamic-api-directives-content --> <!-- end-dynamic-api-directives-content -->

View File

@ -1,7 +1,7 @@
.header { .header {
height: 50px; height: 50px;
background-color: var(--ov-background-color); background-color: var(--ov-primary-color);
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.center-container { .center-container {
@ -9,12 +9,12 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh; height: 100vh;
background-color: var(--ov-secondary-action-color); background-color: var(--ov-secondary-color);
} }
.card-container { .card-container {
text-align: center; text-align: center;
background-color: var(--ov-surface-color); background-color: var(--ov-panel-background);
padding: 10px; padding: 10px;
max-width: 300px; max-width: 300px;
min-width: 300px; min-width: 300px;
@ -23,8 +23,8 @@
} }
.form-btn { .form-btn {
background-color: var(--ov-accent-action-color) !important; background-color: var(--ov-tertiary-color) !important;
color: var(--ov-text-primary-color) !important; color: var(--ov-text-color) !important;
} }
.form-field, .form-field,
@ -33,5 +33,5 @@
} }
::ng-deep .mat-mdc-progress-spinner { ::ng-deep .mat-mdc-progress-spinner {
--mdc-circular-progress-active-indicator-color: var(--ov-accent-action-color); --mdc-circular-progress-active-indicator-color: var(--ov-tertiary-color);
} }

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminLoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: AdminLoginComponent;
let fixture: ComponentFixture<AdminLoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AdminLoginComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AdminLoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -2,13 +2,12 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Validators, FormGroup, FormBuilder } from '@angular/forms'; import { Validators, FormGroup, FormBuilder } from '@angular/forms';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ActionService } from '../../services/action/action.service'; import { ActionService } from '../../services/action/action.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service';
@Component({ @Component({
selector: 'ov-admin-login', selector: 'ov-admin-login',
templateUrl: './admin-login.component.html', templateUrl: './admin-login.component.html',
styleUrls: ['./admin-login.component.scss'], styleUrls: ['./admin-login.component.scss']
standalone: false
}) })
export class AdminLoginComponent implements OnInit { export class AdminLoginComponent implements OnInit {
/** /**
@ -21,11 +20,6 @@ export class AdminLoginComponent implements OnInit {
password: string; password: string;
}>(); }>();
/**
* @internal
*/
title: string;
/** /**
* @internal * @internal
*/ */
@ -42,7 +36,6 @@ export class AdminLoginComponent implements OnInit {
loginForm: FormGroup; loginForm: FormGroup;
private errorSub: Subscription; private errorSub: Subscription;
private titleSub: Subscription;
/** /**
* @internal * @internal
@ -71,7 +64,6 @@ export class AdminLoginComponent implements OnInit {
ngOnDestroy() { ngOnDestroy() {
this.showSpinner = false; this.showSpinner = false;
if (this.errorSub) this.errorSub.unsubscribe(); if (this.errorSub) this.errorSub.unsubscribe();
if (this.titleSub) this.titleSub.unsubscribe();
} }
/** /**
@ -91,9 +83,5 @@ export class AdminLoginComponent implements OnInit {
this.actionService.openDialog(value.error, value.message, true); this.actionService.openDialog(value.error, value.message, true);
} }
}); });
this.titleSub = this.libService.adminLoginTitle$.subscribe((value) => {
this.title = value;
});
} }
} }

View File

@ -23,13 +23,13 @@
} }
.audio-container{ .audio-container{
background-color: var(--ov-accent-action-color); background-color: var(--ov-tertiary-color);
padding: 5px; padding: 5px;
max-width: 15px; max-width: 15px;
max-height: 15px; max-height: 15px;
height: 15px; height: 15px;
width: 15px; width: 15px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-video-radius);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
@ -38,7 +38,7 @@
margin: auto; margin: auto;
height: 80%; height: 80%;
width: 3px; width: 3px;
background: var(--ov-secondary-action-color); background: var(--ov-text-color);
border-radius: 8px; border-radius: 8px;
} }

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AudioWaveComponent } from './audio-wave.component';
describe('AudioWaveComponent', () => {
let component: AudioWaveComponent;
let fixture: ComponentFixture<AudioWaveComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AudioWaveComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AudioWaveComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -10,7 +10,6 @@ import { Component } from '@angular/core';
<div class="stick loud play"></div> <div class="stick loud play"></div>
<div class="stick normal play"></div> <div class="stick normal play"></div>
</div>`, </div>`,
styleUrls: ['./audio-wave.component.scss'], styleUrls: ['./audio-wave.component.scss']
standalone: false
}) })
export class AudioWaveComponent {} export class AudioWaveComponent {}

View File

@ -19,7 +19,7 @@
height: 70px; height: 70px;
width: 70px; width: 70px;
border-radius: var(--ov-video-radius); border-radius: var(--ov-video-radius);
border: 2px solid var(--ov-text-primary-color); border: 2px solid var(--ov-text-color);
color: #000000; color: #000000;
} }

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AvatarProfileComponent } from './avatar-profile.component';
describe('AvatarProfileComponent', () => {
let component: AvatarProfileComponent;
let fixture: ComponentFixture<AvatarProfileComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AvatarProfileComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AvatarProfileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -13,8 +13,7 @@ import { Component, Input } from '@angular/core';
</div> </div>
</div> </div>
`, `,
styleUrls: ['./avatar-profile.component.scss'], styleUrls: ['./avatar-profile.component.scss']
standalone: false
}) })
export class AvatarProfileComponent { export class AvatarProfileComponent {
letter: string; letter: string;

View File

@ -29,7 +29,7 @@
.error-container { .error-container {
display: grid; display: grid;
text-align: center; text-align: center;
color: var(--ov-text-primary-color); color: var(--ov-text-color);
font-size: 18px; font-size: 18px;
} }
@ -43,7 +43,7 @@ mat-spinner {
} }
::ng-deep .mat-mdc-progress-spinner { ::ng-deep .mat-mdc-progress-spinner {
--mdc-circular-progress-active-indicator-color: var(--ov-accent-action-color); --mdc-circular-progress-active-indicator-color: var(--ov-tertiary-color);
} }
/* /*
@ -160,8 +160,8 @@ mat-spinner {
} }
#caption-settings-btn { #caption-settings-btn {
color: var(--ov-text-primary-color); color: var(--ov-text-color);
background-color: var(--ov-secondary-action-color); background-color: var(--ov-secondary-color);
} }
#caption-settings-icon { #caption-settings-icon {
font-size: 15px; font-size: 15px;
@ -190,12 +190,12 @@ mat-spinner {
.caption-text, .caption-text,
#speaker { #speaker {
color: var(--ov-text-primary-color); color: var(--ov-text-color);
font-family: 'Roboto', arial, sans-serif; font-family: 'Roboto', arial, sans-serif;
} }
.caption-text { .caption-text {
background-color: var(--ov-secondary-action-color); background-color: var(--ov-logo-background-color);
padding: 4.5px; padding: 4.5px;
line-height: 1.6; line-height: 1.6;
word-break: break-word; word-break: break-word;

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CaptionsComponent } from './captions.component';
describe('CaptionsComponent', () => {
let component: CaptionsComponent;
let fixture: ComponentFixture<CaptionsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CaptionsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CaptionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -5,39 +5,15 @@ import { MatDialogRef } from '@angular/material/dialog';
* @internal * @internal
*/ */
@Component({ @Component({
selector: 'app-delete-dialog', selector: 'app-delete-dialog',
template: ` template: `
<div mat-dialog-content>{{ 'PANEL.RECORDING.DELETE_QUESTION' | translate }}</div> <div mat-dialog-content>{{'PANEL.RECORDING.DELETE_QUESTION' | translate}}</div>
<div mat-dialog-actions> <div mat-dialog-actions>
<button mat-button [disableRipple]="true" (click)="close()">{{ 'PANEL.RECORDING.CANCEL' | translate }}</button> <button mat-button (click)="close()">{{'PANEL.RECORDING.CANCEL' | translate }}</button>
<button [disableRipple]="true" mat-button cdkFocusInitial (click)="close(true)" id="delete-recording-confirm-btn"> <button mat-button cdkFocusInitial (click)="close(true)" id="delete-recording-confirm-btn">{{'PANEL.RECORDING.DELETE' | translate}}</button>
{{ 'PANEL.RECORDING.DELETE' | translate }}
</button>
</div> </div>
`, `,
styles: [ styles: [``]
`
::ng-deep .mat-mdc-dialog-content {
color: var(--ov-text-surface-color) !important;
}
::ng-deep .mat-mdc-dialog-surface {
background-color: var(--ov-surface-color);
border-radius: var(--ov-surface-radius);
}
#delete-recording-confirm-btn {
background-color: var(--ov-error-color) !important;
color: var(--ov-secondary-action-color);
}
.mat-mdc-button,
.mat-mdc-button:not(:disabled),
::ng-deep .mat-mdc-button .mat-mdc-button-persistent-ripple::before {
color: var(--ov-secondary-action-color);
background-color: var(--ov-primary-action-color) !important;
border-radius: var(--ov-surface-radius);
}
`
],
standalone: false
}) })
export class DeleteDialogComponent { export class DeleteDialogComponent {
constructor(public dialogRef: MatDialogRef<DeleteDialogComponent>) {} constructor(public dialogRef: MatDialogRef<DeleteDialogComponent>) {}

View File

@ -7,40 +7,17 @@ import { DialogData } from '../../models/dialog.model';
*/ */
@Component({ @Component({
selector: 'ov-dialog-template', selector: 'ov-dialog-template',
template: ` template: `
<h1 mat-dialog-title>{{ data.title }}</h1> <h1 mat-dialog-title>{{ data.title }}</h1>
<div mat-dialog-content id="openvidu-dialog">{{ data.description }}</div> <div mat-dialog-content id="openvidu-dialog">{{ data.description }}</div>
<div mat-dialog-actions *ngIf="data.showActionButtons"> <div mat-dialog-actions *ngIf="data.showActionButtons">
<button mat-button [disableRipple]="true" (click)="close()">{{ 'PANEL.CLOSE' | translate }}</button> <button mat-button (click)="close()">{{'PANEL.CLOSE' | translate}}</button>
</div> </div>
`, `
styles: [
`
::ng-deep .mat-mdc-dialog-content {
color: var(--ov-text-surface-color) !important;
}
::ng-deep .mat-mdc-dialog-surface {
background-color: var(--ov-surface-color);
border-radius: var(--ov-surface-radius);
}
.mat-mdc-button,
.mat-mdc-button:not(:disabled),
::ng-deep .mat-mdc-button .mat-mdc-button-persistent-ripple::before {
color: var(--ov-secondary-action-color);
background-color: var(--ov-primary-action-color) !important;
border-radius: var(--ov-surface-radius);
}
`
],
standalone: false
}) })
export class DialogTemplateComponent { export class DialogTemplateComponent {
constructor( constructor(public dialogRef: MatDialogRef<DialogTemplateComponent>, @Inject(MAT_DIALOG_DATA) public data: DialogData) {}
public dialogRef: MatDialogRef<DialogTemplateComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogData
) {}
close() { close() {
this.dialogRef.close(); this.dialogRef.close();

View File

@ -8,8 +8,8 @@ import { DialogData } from '../../models/dialog.model';
*/ */
@Component({ @Component({
selector: 'ov-pro-feature-template', selector: 'ov-pro-feature-template',
template: ` template: `
<h1 mat-dialog-title>{{ data.title }}</h1> <h1 mat-dialog-title>{{ data.title }}</h1>
<div mat-dialog-content>{{ data.description }}</div> <div mat-dialog-content>{{ data.description }}</div>
<div mat-dialog-actions *ngIf="data.showActionButtons"> <div mat-dialog-actions *ngIf="data.showActionButtons">
@ -19,8 +19,7 @@ import { DialogData } from '../../models/dialog.model';
</button> </button>
<button mat-button (click)="close()">{{'PANEL.CLOSE' | translate}}</button> <button mat-button (click)="close()">{{'PANEL.CLOSE' | translate}}</button>
</div> </div>
`, `
standalone: false
}) })
export class ProFeatureDialogTemplateComponent { export class ProFeatureDialogTemplateComponent {
constructor(public dialogRef: MatDialogRef<ProFeatureDialogTemplateComponent>, @Inject(MAT_DIALOG_DATA) public data: DialogData) {} constructor(public dialogRef: MatDialogRef<ProFeatureDialogTemplateComponent>, @Inject(MAT_DIALOG_DATA) public data: DialogData) {}
@ -30,6 +29,6 @@ export class ProFeatureDialogTemplateComponent {
} }
seeMore() { seeMore() {
window.open('https://openvidu.io/pricing/#openvidu-pro', '_blank')?.focus(); window.open('https://docs.openvidu.io/en/stable/openvidu-pro/', '_blank')?.focus();
} }
} }

View File

@ -6,40 +6,23 @@ import { RecordingDialogData } from '../../models/dialog.model';
* @internal * @internal
*/ */
@Component({ @Component({
selector: 'app-recording-dialog', selector: 'app-recording-dialog',
template: ` template: `
<div mat-dialog-content> <div mat-dialog-content>
<video #videoElement controls autoplay [src]="src" (error)="handleError()"></video> <video #videoElement controls autoplay [src]="src" (error)="handleError()"></video>
</div> </div>
<div mat-dialog-actions *ngIf="data.showActionButtons" align="end"> <div mat-dialog-actions *ngIf="data.showActionButtons" align="end">
<button mat-button [disableRipple]="true" (click)="close()">{{ 'PANEL.CLOSE' | translate }}</button> <button mat-button (click)="close()">{{ 'PANEL.CLOSE' | translate }}</button>
</div> </div>
`, `,
styles: [ styles: [
` `
::ng-deep .mat-mdc-dialog-content {
color: var(--ov-text-surface-color) !important;
}
::ng-deep .mat-mdc-dialog-surface {
background-color: var(--ov-surface-color);
border-radius: var(--ov-surface-radius);
}
video { video {
max-height: 64vh; max-height: 64vh;
max-width: 100%; max-width: 100%;
} }
.mat-mdc-button,
.mat-mdc-button:not(:disabled),
::ng-deep .mat-mdc-button .mat-mdc-button-persistent-ripple::before {
color: var(--ov-secondary-action-color);
background-color: var(--ov-primary-action-color) !important;
border-radius: var(--ov-surface-radius);
}
` `
], ]
standalone: false
}) })
export class RecordingDialogComponent { export class RecordingDialogComponent {
@ViewChild('videoElement', { static: true }) videoElement: ElementRef<HTMLVideoElement>; @ViewChild('videoElement', { static: true }) videoElement: ElementRef<HTMLVideoElement>;

View File

@ -19,11 +19,6 @@
<ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: track }"></ng-container> <ng-container *ngTemplateOutlet="streamTemplate; context: { $implicit: track }"></ng-container>
</div> </div>
<!-- Render additional layout elements injected via ovAdditionalLayoutElement -->
@if (layoutAdditionalElementsTemplate) {
<ng-container *ngTemplateOutlet="layoutAdditionalElementsTemplate"></ng-container>
}
<div <div
*ngFor="let track of remoteParticipants | tracks; trackBy: trackParticipantElement" *ngFor="let track of remoteParticipants | tracks; trackBy: trackParticipantElement"
class="remote-participant" class="remote-participant"

View File

@ -21,6 +21,6 @@ It will recognise the following directive in a child element.
With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application. With the following directives you can modify the default User Interface with the aim of fully customizing your videoconference application.
<!-- start-dynamic-api-directives-content --> <!-- start-dynamic-api-directives-content -->
_No API directives available for this component_. _No API directives available for this component_.
<!-- end-dynamic-api-directives-content --> <!-- end-dynamic-api-directives-content -->

View File

@ -31,7 +31,7 @@
.OV_root, .OV_root,
.OV_root * { .OV_root * {
color: var(--ov-secondary-action-color); color: #ffffff;
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;

View File

@ -0,0 +1,37 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RemoteUserService, LocalUserService } from '../../../public-api';
import { ChatService } from '../../services/chat/chat.service';
import { ChatServiceMock } from '../../services/chat/chat.service.mock';
import { LayoutService } from '../../services/layout/layout.service';
import { LocalUserServiceMock } from '../../services/local-user/local-user.service.mock';
import { RemoteUserServiceMock } from '../../services/remote-user/remote-user.service.mock';
import { LayoutComponent } from './layout.component';
describe('LayoutComponent', () => {
let component: LayoutComponent;
let fixture: ComponentFixture<LayoutComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LayoutComponent ],
providers: [
{ provide: RemoteUserService, useClass: RemoteUserServiceMock },
{ provide: LocalUserService, useClass: LocalUserServiceMock },
{ provide: ChatService, useClass: ChatServiceMock },
{ provide: LayoutService, useClass: LayoutService }
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,5 +1,3 @@
import { LayoutAdditionalElementsDirective } from '../../directives/template/internals.directive';
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -13,16 +11,14 @@ import {
ViewChild, ViewChild,
ViewContainerRef ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { combineLatest, map, Subject, takeUntil } from 'rxjs'; import { Subscription } from 'rxjs';
import { StreamDirective } from '../../directives/template/openvidu-components-angular.directive'; import { StreamDirective } from '../../directives/template/openvidu-components-angular.directive';
import { ParticipantTrackPublication, ParticipantModel } from '../../models/participant.model'; import { ParticipantTrackPublication, ParticipantModel } from '../../models/participant.model';
import { LayoutService } from '../../services/layout/layout.service'; import { LayoutService } from '../../services/layout/layout.service';
import { ParticipantService } from '../../services/participant/participant.service'; import { ParticipantService } from '../../services/participant/participant.service';
import { CdkDrag } from '@angular/cdk/drag-drop'; import { CdkDrag } from '@angular/cdk/drag-drop';
import { PanelService } from '../../services/panel/panel.service'; import { PanelService } from '../../services/panel/panel.service';
import { GlobalConfigService } from '../../services/config/global-config.service'; import { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { LayoutTemplateConfiguration, TemplateManagerService } from '../../services/template/template-manager.service';
/** /**
* *
@ -33,8 +29,7 @@ import { LayoutTemplateConfiguration, TemplateManagerService } from '../../servi
selector: 'ov-layout', selector: 'ov-layout',
templateUrl: './layout.component.html', templateUrl: './layout.component.html',
styleUrls: ['./layout.component.scss'], styleUrls: ['./layout.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit { export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
/** /**
@ -42,11 +37,6 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
*/ */
@ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef<any>; @ContentChild('stream', { read: TemplateRef }) streamTemplate: TemplateRef<any>;
/**
* @ignore
*/
@ContentChild('layoutAdditionalElements', { read: TemplateRef }) layoutAdditionalElementsTemplate: TemplateRef<any>;
/** /**
* @ignore * @ignore
*/ */
@ -70,27 +60,9 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
// is inside of the layout component tagged with '*ovLayout' directive // is inside of the layout component tagged with '*ovLayout' directive
if (externalStream) { if (externalStream) {
this.streamTemplate = externalStream.template; this.streamTemplate = externalStream.template;
this.updateTemplatesAndMarkForCheck();
} }
} }
/**
* @ignore
*/
@ContentChild(LayoutAdditionalElementsDirective) set externalAdditionalElements(
externalAdditionalElements: LayoutAdditionalElementsDirective
) {
if (externalAdditionalElements) {
this._externalLayoutAdditionalElements = externalAdditionalElements;
this.updateTemplatesAndMarkForCheck();
}
}
/**
* @ignore
*/
templateConfig: LayoutTemplateConfiguration = {};
localParticipant: ParticipantModel | undefined; localParticipant: ParticipantModel | undefined;
remoteParticipants: ParticipantModel[] = []; remoteParticipants: ParticipantModel[] = [];
/** /**
@ -98,11 +70,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
*/ */
captionsEnabled = true; captionsEnabled = true;
private _externalStream?: StreamDirective; private localParticipantSubs: Subscription;
private _externalLayoutAdditionalElements?: LayoutAdditionalElementsDirective; private remoteParticipantsSubs: Subscription;
private captionsSubs: Subscription;
private destroy$ = new Subject<void>();
private resizeObserver: ResizeObserver; private resizeObserver: ResizeObserver;
private cdkSubscription: Subscription;
private resizeTimeout: NodeJS.Timeout; private resizeTimeout: NodeJS.Timeout;
private videoIsAtRight: boolean = false; private videoIsAtRight: boolean = false;
private lastLayoutWidth: number = 0; private lastLayoutWidth: number = 0;
@ -114,21 +86,16 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
private layoutService: LayoutService, private layoutService: LayoutService,
private panelService: PanelService, private panelService: PanelService,
private participantService: ParticipantService, private participantService: ParticipantService,
private globalService: GlobalConfigService, private libService: OpenViduComponentsConfigService,
private directiveService: OpenViduComponentsConfigService, private cd: ChangeDetectorRef
private cd: ChangeDetectorRef,
private templateManagerService: TemplateManagerService
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.setupTemplates();
this.subscribeToParticipants(); this.subscribeToParticipants();
this.subscribeToCaptions(); this.subscribeToCaptions();
} }
ngAfterViewInit() { ngAfterViewInit() {
console.log('LayoutComponent.ngAfterViewInit');
this.layoutService.initialize(this.layoutContainer.element.nativeElement); this.layoutService.initialize(this.layoutContainer.element.nativeElement);
this.lastLayoutWidth = this.layoutContainer.element.nativeElement.getBoundingClientRect().width; this.lastLayoutWidth = this.layoutContainer.element.nativeElement.getBoundingClientRect().width;
this.listenToResizeLayout(); this.listenToResizeLayout();
@ -136,11 +103,13 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
} }
ngOnDestroy() { ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
this.localParticipant = undefined; this.localParticipant = undefined;
this.remoteParticipants = []; this.remoteParticipants = [];
this.resizeObserver?.disconnect(); this.resizeObserver?.disconnect();
this.localParticipantSubs?.unsubscribe();
this.remoteParticipantsSubs?.unsubscribe();
this.captionsSubs?.unsubscribe();
this.cdkSubscription?.unsubscribe();
this.layoutService.clear(); this.layoutService.clear();
} }
@ -153,36 +122,8 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
return track; return track;
} }
private setupTemplates() {
this.templateConfig = this.templateManagerService.setupLayoutTemplates(
this._externalStream,
this._externalLayoutAdditionalElements
);
// Apply templates to component properties for backward compatibility
this.applyTemplateConfiguration();
}
private applyTemplateConfiguration() {
if (this.templateConfig.layoutStreamTemplate) {
this.streamTemplate = this.templateConfig.layoutStreamTemplate;
}
if (this.templateConfig.layoutAdditionalElementsTemplate) {
this.layoutAdditionalElementsTemplate = this.templateConfig.layoutAdditionalElementsTemplate;
}
}
/**
* @internal
* Updates templates and triggers change detection
*/
private updateTemplatesAndMarkForCheck(): void {
this.setupTemplates();
this.cd.markForCheck();
}
private subscribeToCaptions() { private subscribeToCaptions() {
this.layoutService.captionsTogglingObs.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => { this.captionsSubs = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => {
this.captionsEnabled = value; this.captionsEnabled = value;
this.cd.markForCheck(); this.cd.markForCheck();
this.layoutService.update(); this.layoutService.update();
@ -190,7 +131,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
} }
private subscribeToParticipants() { private subscribeToParticipants() {
this.participantService.localParticipant$.pipe(takeUntil(this.destroy$)).subscribe((p) => { this.localParticipantSubs = this.participantService.localParticipant$.subscribe((p) => {
if (p) { if (p) {
this.localParticipant = p; this.localParticipant = p;
if (!this.localParticipant?.isMinimized) { if (!this.localParticipant?.isMinimized) {
@ -201,18 +142,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
} }
}); });
combineLatest([this.participantService.remoteParticipants$, this.directiveService.layoutRemoteParticipants$]) this.remoteParticipantsSubs = this.participantService.remoteParticipants$.subscribe((participants) => {
.pipe( this.remoteParticipants = participants;
map(([serviceParticipants, directiveParticipants]) => this.layoutService.update();
directiveParticipants !== undefined ? directiveParticipants : serviceParticipants this.cd.markForCheck();
), });
takeUntil(this.destroy$)
)
.subscribe((participants) => {
this.remoteParticipants = participants;
this.layoutService.update();
this.cd.markForCheck();
});
} }
private listenToResizeLayout() { private listenToResizeLayout() {
@ -271,10 +205,9 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
this.videoIsAtRight = false; this.videoIsAtRight = false;
} }
}; };
this.cdkSubscription = this.cdkDrag.released.subscribe(handler);
this.cdkDrag.released.pipe(takeUntil(this.destroy$)).subscribe(handler); if (this.libService.getConfig().production) return;
if (this.globalService.isProduction()) return;
// Just for allow E2E testing with drag and drop // Just for allow E2E testing with drag and drop
document.addEventListener('webcomponentTestingEndedDragAndDropEvent', handler); document.addEventListener('webcomponentTestingEndedDragAndDropEvent', handler);
document.addEventListener('webcomponentTestingEndedDragAndDropRightEvent', (event: any) => { document.addEventListener('webcomponentTestingEndedDragAndDropRightEvent', (event: any) => {

View File

@ -3,7 +3,7 @@ video {
object-fit: cover; object-fit: cover;
width: 100%; width: 100%;
height: 100%; height: 100%;
color: var(--ov-text-primary-color); color: #ffffff;
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { VideoComponent } from './media-element.component';
describe('VideoComponent', () => {
let component: VideoComponent;
let fixture: ComponentFixture<VideoComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ VideoComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(VideoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -18,8 +18,7 @@ import { Track } from 'livekit-client';
transition(':enter', [style({ opacity: 0 }), animate('100ms', style({ opacity: 1 }))]), transition(':enter', [style({ opacity: 0 }), animate('100ms', style({ opacity: 1 }))]),
transition(':leave', [style({ opacity: 1 }), animate('200ms', style({ opacity: 0 }))]) transition(':leave', [style({ opacity: 1 }), animate('200ms', style({ opacity: 0 }))])
]) ])
], ]
standalone: false
}) })
export class MediaElementComponent implements AfterViewInit { export class MediaElementComponent implements AfterViewInit {
_track: Track; _track: Track;

View File

@ -17,8 +17,6 @@
(onRecordingDeleteRequested)="onRecordingDeleteRequested.emit($event)" (onRecordingDeleteRequested)="onRecordingDeleteRequested.emit($event)"
(onRecordingDownloadClicked)="onRecordingDownloadClicked.emit($event)" (onRecordingDownloadClicked)="onRecordingDownloadClicked.emit($event)"
(onRecordingPlayClicked)="onRecordingPlayClicked.emit($event)" (onRecordingPlayClicked)="onRecordingPlayClicked.emit($event)"
(onViewRecordingClicked)="onViewRecordingClicked.emit($event)"
(onViewRecordingsClicked)="onViewRecordingsClicked.emit()"
></ov-recording-activity> ></ov-recording-activity>
<ov-broadcasting-activity <ov-broadcasting-activity
*ngIf="showBroadcastingActivity" *ngIf="showBroadcastingActivity"

View File

@ -4,6 +4,6 @@ With the following directives you can modify the default User Interface with the
<!-- start-dynamic-api-directives-content --> <!-- start-dynamic-api-directives-content -->
| **Parameter** | **Type** | **Reference** | | **Parameter** | **Type** | **Reference** |
|:--------------------------------: | :-------: | :---------------------------------------------: | |:--------------------------------: | :-------: | :---------------------------------------------: |
| **broadcastingActivity** | `boolean` | [ActivitiesPanelBroadcastingActivityDirective](../directives/ActivitiesPanelBroadcastingActivityDirective.html) |
| **recordingActivity** | `boolean` | [ActivitiesPanelRecordingActivityDirective](../directives/ActivitiesPanelRecordingActivityDirective.html) | | **recordingActivity** | `boolean` | [ActivitiesPanelRecordingActivityDirective](../directives/ActivitiesPanelRecordingActivityDirective.html) |
| **broadcastingActivity** | `boolean` | [ActivitiesPanelBroadcastingActivityDirective](../directives/ActivitiesPanelBroadcastingActivityDirective.html) |
<!-- end-dynamic-api-directives-content --> <!-- end-dynamic-api-directives-content -->

View File

@ -1,6 +1,4 @@
$ov-activity-status-color: #afafaf; :host{
:host {
.activities-body-container { .activities-body-container {
display: block !important; display: block !important;
overflow-y: auto; overflow-y: auto;
@ -8,26 +6,16 @@ $ov-activity-status-color: #afafaf;
padding: 10px; padding: 10px;
} }
.activity-status {
color: var(--ov-text-surface-color);
display: inline;
padding: 3px;
font-size: 11px;
border-radius: var(--ov-surface-radius);
background-color: $ov-activity-status-color;
}
.activity-icon { .activity-icon {
display: inherit; display: inherit;
background-color: $ov-activity-status-color;; background-color: var(--ov-light-color);
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
margin: 10px 0px !important; margin: 10px 0px !important;
padding: 10px; padding: 10px;
} }
.activity-title, .activity-title, .activity-subtitle {
.activity-subtitle { color: var(--ov-panel-text-color);
color: var(--ov-text-surface-color);
} }
.activity-subtitle { .activity-subtitle {
@ -58,11 +46,6 @@ $ov-activity-status-color: #afafaf;
::ng-deep .mat-expansion-panel-header { ::ng-deep .mat-expansion-panel-header {
padding: 0px 5px !important; padding: 0px 5px !important;
height: 65px !important; height: 65px !important;
cursor: pointer !important;
}
::ng-deep .mat-expansion-panel-header:hover {
background: none !important;
} }
::ng-deep .mat-mdc-list-base .mat-mdc-list-item .mat-list-item-content, ::ng-deep .mat-mdc-list-base .mat-mdc-list-item .mat-list-item-content,
@ -70,10 +53,6 @@ $ov-activity-status-color: #afafaf;
padding: 0px !important; padding: 0px !important;
} }
::ng-deep mat-list mat-list-item {
cursor: pointer !important;
}
::ng-deep mat-expansion-panel .mat-expansion-panel-body { ::ng-deep mat-expansion-panel .mat-expansion-panel-body {
padding: 0px !important; padding: 0px !important;
min-height: 400px; min-height: 400px;
@ -84,16 +63,14 @@ $ov-activity-status-color: #afafaf;
::ng-deep .mat-expansion-panel { ::ng-deep .mat-expansion-panel {
box-shadow: none !important; box-shadow: none !important;
background-color: var(--ov-surface-color) !important; background-color: var(--ov-panel-background) !important;
} }
::ng-deep .no-body .mat-expansion-panel-content { ::ng-deep .no-body .mat-expansion-panel-content {
display: none !important; display: none !important;
} }
::ng-deep ::ng-deep .mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{
.mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta
.mdc-list-item__end::before {
max-height: 24px; max-height: 24px;
height: 24px; height: 24px;
} }

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivitiesPanelComponent } from './activities-panel.component';
describe('ActivitiesPanelComponent', () => {
let component: ActivitiesPanelComponent;
let fixture: ComponentFixture<ActivitiesPanelComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ActivitiesPanelComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ActivitiesPanelComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subscription } from 'rxjs';
import { PanelStatusInfo, PanelType } from '../../../models/panel.model'; import { PanelStatusInfo, PanelType } from '../../../models/panel.model';
import { OpenViduComponentsConfigService } from '../../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../../services/config/openvidu-components-angular.config.service';
import { PanelService } from '../../../services/panel/panel.service'; import { PanelService } from '../../../services/panel/panel.service';
import { import {
RecordingDeleteRequestedEvent, RecordingDeleteRequestedEvent,
@ -20,8 +20,7 @@ import { BroadcastingStartRequestedEvent, BroadcastingStopRequestedEvent } from
selector: 'ov-activities-panel', selector: 'ov-activities-panel',
templateUrl: './activities-panel.component.html', templateUrl: './activities-panel.component.html',
styleUrls: ['../panel.component.scss', './activities-panel.component.scss'], styleUrls: ['../panel.component.scss', './activities-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
export class ActivitiesPanelComponent implements OnInit { export class ActivitiesPanelComponent implements OnInit {
/** /**
@ -54,21 +53,6 @@ export class ActivitiesPanelComponent implements OnInit {
*/ */
@Output() onRecordingPlayClicked: EventEmitter<RecordingPlayClickedEvent> = new EventEmitter<RecordingPlayClickedEvent>(); @Output() onRecordingPlayClicked: EventEmitter<RecordingPlayClickedEvent> = new EventEmitter<RecordingPlayClickedEvent>();
/**
* @internal
* Provides event notifications that fire when view recordings button has been clicked.
* This event is triggered when the user wants to view all recordings in an external page.
*/
@Output() onViewRecordingsClicked: EventEmitter<void> = new EventEmitter<void>();
/**
* @internal
* Provides event notifications that fire when view recording button has been clicked.
* This event is triggered when the user wants to view a specific recording in an external page.
* It provides the recording ID as event data.
*/
@Output() onViewRecordingClicked: EventEmitter<string> = new EventEmitter<string>();
/** /**
* Provides event notifications that fire when start broadcasting button is clicked. * Provides event notifications that fire when start broadcasting button is clicked.
* It provides the {@link BroadcastingStartRequestedEvent} payload as event data. * It provides the {@link BroadcastingStartRequestedEvent} payload as event data.
@ -95,7 +79,9 @@ export class ActivitiesPanelComponent implements OnInit {
* @internal * @internal
*/ */
showBroadcastingActivity: boolean = true; showBroadcastingActivity: boolean = true;
private destroy$ = new Subject<void>(); private panelSubscription: Subscription;
private recordingActivitySub: Subscription;
private broadcastingActivitySub: Subscription;
/** /**
* @internal * @internal
@ -118,8 +104,9 @@ export class ActivitiesPanelComponent implements OnInit {
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
this.destroy$.next(); if (this.panelSubscription) this.panelSubscription.unsubscribe();
this.destroy$.complete(); if (this.recordingActivitySub) this.recordingActivitySub.unsubscribe();
if (this.broadcastingActivitySub) this.broadcastingActivitySub.unsubscribe();
} }
/** /**
@ -130,7 +117,7 @@ export class ActivitiesPanelComponent implements OnInit {
} }
private subscribeToPanelToggling() { private subscribeToPanelToggling() {
this.panelService.panelStatusObs.pipe(takeUntil(this.destroy$)).subscribe((ev: PanelStatusInfo) => { this.panelSubscription = this.panelService.panelStatusObs.subscribe((ev: PanelStatusInfo) => {
if (ev.panelType === PanelType.ACTIVITIES && !!ev.subOptionType) { if (ev.panelType === PanelType.ACTIVITIES && !!ev.subOptionType) {
this.expandedPanel = ev.subOptionType; this.expandedPanel = ev.subOptionType;
} }
@ -138,12 +125,12 @@ export class ActivitiesPanelComponent implements OnInit {
} }
private subscribeToActivitiesPanelDirective() { private subscribeToActivitiesPanelDirective() {
this.libService.recordingActivity$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => { this.recordingActivitySub = this.libService.recordingActivity$.subscribe((value: boolean) => {
this.showRecordingActivity = value; this.showRecordingActivity = value;
this.cd.markForCheck(); this.cd.markForCheck();
}); });
this.libService.broadcastingActivity$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => { this.broadcastingActivitySub = this.libService.broadcastingActivity$.subscribe((value: boolean) => {
this.showBroadcastingActivity = value; this.showBroadcastingActivity = value;
this.cd.markForCheck(); this.cd.markForCheck();
}); });

View File

@ -1,9 +1,4 @@
<mat-expansion-panel <mat-expansion-panel (opened)="setPanelOpened(true)" (closed)="setPanelOpened(false)" [expanded]="expanded" [ngClass]="{ 'no-body': !isPanelOpened }">
(opened)="setPanelOpened(true)"
(closed)="setPanelOpened(false)"
[expanded]="expanded"
[ngClass]="{ 'no-body': !isPanelOpened }"
>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-list> <mat-list>
<mat-list-item> <mat-list-item>
@ -14,7 +9,7 @@
started: broadcastingStatus === broadcastingStatusEnum.STARTED, started: broadcastingStatus === broadcastingStatusEnum.STARTED,
stopped: broadcastingStatus === broadcastingStatusEnum.STOPPED, stopped: broadcastingStatus === broadcastingStatusEnum.STOPPED,
failed: broadcastingStatus === broadcastingStatusEnum.FAILED, failed: broadcastingStatus === broadcastingStatusEnum.FAILED,
starting: pending:
broadcastingStatus === broadcastingStatusEnum.STARTING || broadcastingStatus === broadcastingStatusEnum.STOPPING broadcastingStatus === broadcastingStatusEnum.STARTING || broadcastingStatus === broadcastingStatusEnum.STOPPING
}" }"
> >
@ -34,12 +29,11 @@
<div class="activity-action-buttons" matListItemMeta> <div class="activity-action-buttons" matListItemMeta>
<div <div
id="broadcasting-status" id="broadcasting-status"
class="activity-status"
[ngClass]="{ [ngClass]="{
started: broadcastingStatus === broadcastingStatusEnum.STARTED, started: broadcastingStatus === broadcastingStatusEnum.STARTED,
stopped: broadcastingStatus === broadcastingStatusEnum.STOPPED, stopped: broadcastingStatus === broadcastingStatusEnum.STOPPED,
failed: broadcastingStatus === broadcastingStatusEnum.FAILED, failed: broadcastingStatus === broadcastingStatusEnum.FAILED,
starting: pending:
broadcastingStatus === broadcastingStatusEnum.STARTING || broadcastingStatus === broadcastingStatusEnum.STARTING ||
broadcastingStatus === broadcastingStatusEnum.STOPPING broadcastingStatus === broadcastingStatusEnum.STOPPING
}" }"

View File

@ -1,26 +1,29 @@
$ov-broadcasting-color: #5903ca; #broadcasting-status {
color: var(--ov-text-color);
$ov-input-color: #cccccc; display: inline;
padding: 3px;
font-size: 11px;
border-radius: var(--ov-panel-radius);
}
.time-container { .time-container {
padding: 2px; padding: 2px;
} }
.error-text { .error-text {
color: var(--ov-error-color); color: var(--ov-warn-color);
font-style: italic; font-style: italic;
font-size: 14px; font-size: 14px;
} }
#broadcasting-icon { #broadcasting-icon {
color: $ov-broadcasting-color !important; color: #5903ca;
} }
.broadcasting-duration { .broadcasting-duration {
background-color: var(--ov-secondary-action-color); background-color: var(--ov-light-color);
padding: 4px 8px; padding: 4px 8px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
font-weight: 500; font-weight: 500;
} }
@ -31,27 +34,27 @@ $ov-input-color: #cccccc;
} }
.started { .started {
background-color: $ov-broadcasting-color !important; background-color: #5903ca !important;
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.activity-icon.started { .activity-icon.started {
background-color: $ov-broadcasting-color !important; background-color: #5903ca !important;
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.failed { .failed {
background-color: var(--ov-error-color) !important; background-color: var(--ov-warn-color) !important;
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} }
.stopped { .stopped {
background-color: var(--ov-secondary-action-color); background-color: var(--ov-light-color);
color: var(--ov-text-surface-color) !important; color: var(--ov-panel-text-color) !important;
} }
.starting { .pending {
background-color: var(--ov-warn-color) !important; background-color: #ffd79b !important;
color: var(--ov-text-primary-color) !important; color: var(--ov-panel-text-color) !important;
} }
.panel-body-container { .panel-body-container {
@ -69,7 +72,7 @@ $ov-input-color: #cccccc;
} }
.broadcasting-error { .broadcasting-error {
color: var(--ov-error-color); color: var(--ov-warn-color);
font-weight: 600; font-weight: 600;
} }
.broadcasting-name { .broadcasting-name {
@ -89,13 +92,13 @@ $ov-input-color: #cccccc;
/* #start-broadcasting-btn { /* #start-broadcasting-btn {
width: 100%; width: 100%;
background-color: var(--ov-accent-action-color); background-color: var(--ov-tertiary-color);
color: var(--ov-text-primary-color); color: var(--ov-text-color);
} */ } */
#stop-broadcasting-btn { #stop-broadcasting-btn {
/* background-color: var(--ov-error-color); */ /* background-color: var(--ov-warn-color); */
color: var(--ov-error-color); color: var(--ov-warn-color);
} }
#reset-broadcasting-status-btn { #reset-broadcasting-status-btn {
@ -119,13 +122,13 @@ mat-expansion-panel {
.input-container { .input-container {
height: 25px; height: 25px;
display: flex; display: flex;
background-color: $ov-input-color; background-color: var(--ov-light-color);
padding: 10px; padding: 10px;
margin: 10px; margin: 10px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
order: 3; order: 3;
justify-content: space-evenly; justify-content: space-evenly;
align-items: center; align-items: center;
} }
.input-container input { .input-container input {
@ -140,6 +143,7 @@ mat-expansion-panel {
white-space: pre-wrap; white-space: pre-wrap;
resize: none; resize: none;
outline: none; outline: none;
-webkit-box-shadow: none; -webkit-box-shadow: none;
-moz-box-shadow: none; -moz-box-shadow: none;
box-shadow: none; box-shadow: none;

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BroadcastingActivityComponent } from './broadcasting-activity.component';
describe('BroadcastingActivityComponent', () => {
let component: BroadcastingActivityComponent;
let fixture: ComponentFixture<BroadcastingActivityComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BroadcastingActivityComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(BroadcastingActivityComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subscription } from 'rxjs';
import { import {
BroadcastingStartRequestedEvent, BroadcastingStartRequestedEvent,
BroadcastingStatus, BroadcastingStatus,
@ -18,8 +18,7 @@ import { OpenViduService } from '../../../../services/openvidu/openvidu.service'
selector: 'ov-broadcasting-activity', selector: 'ov-broadcasting-activity',
templateUrl: './broadcasting-activity.component.html', templateUrl: './broadcasting-activity.component.html',
styleUrls: ['./broadcasting-activity.component.scss', '../activities-panel.component.scss'], styleUrls: ['./broadcasting-activity.component.scss', '../activities-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
// TODO: Allow to add more than one broadcast url // TODO: Allow to add more than one broadcast url
@ -76,7 +75,7 @@ export class BroadcastingActivityComponent implements OnInit {
*/ */
isPanelOpened: boolean = false; isPanelOpened: boolean = false;
private destroy$ = new Subject<void>(); private broadcastingSub: Subscription;
/** /**
* @internal * @internal
@ -99,8 +98,7 @@ export class BroadcastingActivityComponent implements OnInit {
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
this.destroy$.next(); if (this.broadcastingSub) this.broadcastingSub.unsubscribe();
this.destroy$.complete();
} }
/** /**
@ -148,7 +146,7 @@ export class BroadcastingActivityComponent implements OnInit {
} }
private subscribeToBroadcastingStatus() { private subscribeToBroadcastingStatus() {
this.broadcastingService.broadcastingStatusObs.pipe(takeUntil(this.destroy$)).subscribe((event: BroadcastingStatusInfo | undefined) => { this.broadcastingSub = this.broadcastingService.broadcastingStatusObs.subscribe((event: BroadcastingStatusInfo | undefined) => {
if (!!event) { if (!!event) {
const { status, broadcastingId, error } = event; const { status, broadcastingId, error } = event;
this.broadcastingStatus = status; this.broadcastingStatus = status;

View File

@ -1,8 +1,10 @@
:host { :host {
$ov-activity-status-color: #afafaf; #recording-status {
.recording-title, color: var(--ov-text-color);
.recording-subtitle { display: inline;
color: var(--ov-text-surface-color); padding: 3px;
font-size: 11px;
border-radius: var(--ov-panel-radius);
} }
.recording-title { .recording-title {
@ -21,15 +23,14 @@
flex-wrap: wrap; flex-wrap: wrap;
height: auto; height: auto;
align-content: center; align-content: center;
color: var(--ov-text-surface-color) !important; color: var(--ov-panel-text-color) !important;
} }
.recording-duration { .recording-duration {
background-color: $ov-activity-status-color; background-color: var(--ov-light-color);
padding: 4px 8px; padding: 4px 8px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
font-weight: 500; font-weight: 500;
color: var(--ov-text-surface-color);
} }
.recording-duration mat-icon { .recording-duration mat-icon {
@ -40,22 +41,22 @@
.started { .started {
background-color: #3b7430 !important; background-color: #3b7430 !important;
color: #ffffff !important; color: var(--ov-text-color);
} }
.activity-icon.started, .activity-icon.started,
.failed { .failed {
background-color: var(--ov-error-color) !important; background-color: var(--ov-warn-color) !important;
// color: var(--ov-secondary-action-color); color: var(--ov-text-color);
} }
.stopped { .stopped {
// background-color: var(--ov-secondary-action-color); background-color: var(--ov-light-color);
color: var(--ov-text-surface-color) !important; color: var(--ov-panel-text-color) !important;
} }
.starting { .pending {
background-color: var(--ov-warn-color) !important; background-color: #ffd79b !important;
color: #000000 !important; color: var(--ov-panel-text-color) !important;
} }
.panel-body-container { .panel-body-container {
@ -72,458 +73,18 @@
text-align: center; text-align: center;
} }
.recording-placeholder {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
.recording-placeholder-img {
max-width: 100%;
height: auto;
border-radius: 8px;
}
.empty-state {
margin-bottom: 20px;
}
.recording-status-messages {
margin-top: 10px;
}
.recording-status {
display: flex;
align-items: flex-start;
gap: 12px;
border: 1px solid var(--ov-warn-color);
border-radius: 8px;
padding: 12px 16px;
margin: 16px 0;
font-size: 15px;
box-shadow: 0 2px 8px 0 rgba(255, 193, 7, 0.04);
.status-icon {
font-size: 28px;
color: var(--ov-warn-color);
flex-shrink: 0;
margin-top: 2px;
}
.status-content {
display: flex;
flex-direction: column;
gap: 2px;
}
.status-title {
font-weight: 600;
font-size: 15px;
margin-bottom: 2px;
}
.status-message {
font-size: 14px;
opacity: 0.85;
}
}
.recording-status-starting {
background: rgba(255, 193, 7, 0.08);
border-color: var(--ov-warn-color);
}
.recording-status-stopping {
background: rgba(255, 193, 7, 0.13);
border-color: var(--ov-warn-color);
}
.recording-error-container {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 10px;
}
// Modern recording list styles
.recording-list-container {
display: flex;
flex-direction: column;
gap: 10px;
padding-top: 16px;
max-height: 500px;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: var(--ov-accent-action-color);
border-radius: 2px;
opacity: 0.3;
}
&::-webkit-scrollbar-thumb:hover {
opacity: 0.6;
}
}
.recording-card {
background: var(--ov-surface-background-color);
border: 1px solid rgba(0, 102, 204, 0.1);
border-radius: var(--ov-surface-radius);
padding: 8px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
width: 100%;
display: flex;
flex-direction: column;
flex-shrink: 0;
box-sizing: border-box;
&.recording-active {
background: linear-gradient(135deg, transparent 69%, var(--ov-error-color) 250%);
}
}
.recording-header {
display: flex;
align-items: flex-start;
gap: 5px;
width: 100%;
height: 60px;
flex-shrink: 0;
}
.recording-status-indicator {
flex-shrink: 0;
padding-top: 2px;
width: 16px;
height: 16px;
display: flex;
justify-content: center;
align-items: flex-start;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
&.recording-live {
background: #ffffff;
box-shadow: 0 0 0 4px var(--ov-error-color);
animation: pulse-dot 2s infinite;
}
&.recording-stopping {
background: var(--ov-warn-color);
animation: pulse-dot 2s infinite;
}
&.recording-failed {
background: var(--ov-error-color);
}
&.recording-ready {
background: #4caf50;
}
}
.recording-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
justify-content: flex-start;
overflow: hidden;
}
.recording-name {
font-size: 14px;
font-weight: 500;
color: var(--ov-text-surface-color);
margin-bottom: 4px;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 17px;
}
.recording-status-text {
font-size: 12px;
font-weight: 500;
&.recording-live-text {
color: var(--ov-primary-action-color);
text-transform: uppercase;
letter-spacing: 0.5px;
}
}
.recording-metadata {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-top: 4px;
height: auto;
overflow: visible;
}
.metadata-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--ov-text-surface-color);
opacity: 0.7;
white-space: nowrap;
flex-shrink: 0;
.metadata-icon {
font-size: 14px;
width: 14px;
height: 14px;
flex-shrink: 0;
}
}
.recording-actions-menu {
display: flex;
gap: 8px;
flex-shrink: 0;
opacity: 1;
align-items: center;
width: 100%;
justify-content: center;
height: 32px;
margin-top: auto;
}
.action-btn {
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}
&.action-play {
color: var(--ov-accent-action-color);
&:hover {
background: rgba(0, 102, 204, 0.1);
color: var(--ov-accent-action-color);
}
}
&.action-view {
color: var(--ov-accent-action-color);
border-radius: var(--ov-surface-radius);
}
&.action-download {
color: #4caf50;
&:hover {
background: rgba(76, 175, 80, 0.1);
color: #4caf50;
}
}
&.action-delete {
color: var(--ov-error-color);
&:hover {
background: rgba(244, 67, 54, 0.1);
color: var(--ov-error-color);
}
}
}
// Animations
@keyframes pulse-dot {
0%,
100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
}
@keyframes pulse-border {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.recording-actions {
display: flex;
gap: 5px;
}
.action-button {
transition: all 0.2s ease;
&:hover {
transform: scale(1.1);
}
}
// Mobile responsive design for new recording cards
@media (max-width: 768px) {
.recording-list-container {
padding-top: 12px;
gap: 12px;
}
.recording-card {
padding: 8px;
height: 100px;
gap: 8px;
}
.recording-header {
gap: 8px;
height: 50px;
}
.recording-info {
min-width: 0;
}
.recording-metadata {
gap: 8px;
margin-top: 2px;
}
.metadata-item {
font-size: 11px;
gap: 2px;
.metadata-icon {
font-size: 12px;
width: 12px;
height: 12px;
}
}
.recording-actions-menu {
opacity: 1; // Always visible on mobile
gap: 6px;
height: 28px;
}
.action-btn {
width: 28px;
height: 28px;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
}
}
.recording-message {
color: var(--ov-text-surface-color);
}
.recording-error { .recording-error {
color: var(--ov-error-color); color: var(--ov-warn-color);
font-weight: 600; font-weight: 600;
} }
.recording-name {
.recording-error {
display: flex;
align-items: flex-start;
gap: 12px;
background: rgba(244, 67, 54, 0.08);
border: 1px solid var(--ov-error-color);
border-radius: 8px;
padding: 12px 16px;
margin: 16px 0;
color: var(--ov-error-color);
font-size: 15px;
box-shadow: 0 2px 8px 0 rgba(244, 67, 54, 0.04);
.error-icon {
font-size: 28px;
color: var(--ov-error-color);
flex-shrink: 0;
margin-top: 2px;
width: 100%;
height: 100%;
}
.error-content {
display: flex;
flex-direction: column;
gap: 2px;
}
.error-title {
font-weight: 600;
font-size: 15px;
margin-bottom: 2px;
}
.error-message {
font-size: 14px;
opacity: 0.85;
}
}
.disable-recording-btn {
background-color: var(--ov-secondary-action-color) !important;
color: var(--ov-text-surface-color) !important;
cursor: not-allowed !important;
}
// Enhanced empty state
.empty-state {
text-align: center;
padding: 32px 16px;
color: var(--ov-text-surface-color);
}
.empty-state-icon {
margin-bottom: 16px;
mat-icon {
font-size: 48px;
width: 48px;
height: 48px;
color: var(--ov-accent-action-color);
opacity: 0.6;
}
}
.empty-state-title {
font-size: 18px;
font-weight: 500;
margin: 0 0 8px 0;
color: var(--ov-text-surface-color);
}
.empty-state-subtitle {
font-size: 14px; font-size: 14px;
margin: 0; font-weight: bold;
opacity: 0.7; }
line-height: 1.4;
.recording-date {
font-size: 12px !important;
font-style: italic;
} }
.not-allowed-message { .not-allowed-message {
@ -532,44 +93,29 @@
} }
.recording-action-buttons { .recording-action-buttons {
margin: 5px 0px; margin-top: 20px;
margin-bottom: 20px;
} }
#start-recording-btn { #start-recording-btn {
width: 100%; width: 100%;
background-color: var(--ov-primary-action-color); background-color: var(--ov-tertiary-color);
color: var(--ov-secondary-action-color); color: var(--ov-text-color);
border-radius: var(--ov-surface-radius);
}
#view-recordings-btn {
width: 100%;
background-color: var(--ov-accent-action-color);
color: var(--ov-secondary-action-color);
border-radius: var(--ov-surface-radius);
margin-bottom: 10px;
mat-icon {
margin-right: 8px;
}
}
.start-recording-button-container {
width: 100%;
display: inline-block;
} }
#stop-recording-btn { #stop-recording-btn {
width: 100%; width: 100%;
background-color: var(--ov-error-color); background-color: var(--ov-warn-color);
color: var(--ov-secondary-action-color); color: var(--ov-text-color);
border-radius: var(--ov-surface-radius); }
.delete-recording-btn {
color: var(--ov-warn-color);
} }
#reset-recording-status-btn { #reset-recording-status-btn {
width: 100%; width: 100%;
background-color: var(--ov-accent-action-color); background-color: var(--ov-light-color);
border-radius: var(--ov-surface-radius);
} }
.recording-item { .recording-item {
@ -587,7 +133,6 @@
width: 40px !important; width: 40px !important;
height: 40px !important; height: 40px !important;
padding: 5px !important; padding: 5px !important;
color: var(--ov-text-surface-color);
} }
#play-recording-btn > .mat-icon, #play-recording-btn > .mat-icon,
#download-recording-btn > .mat-icon, #download-recording-btn > .mat-icon,
@ -595,13 +140,6 @@
height: 20px !important; height: 20px !important;
} }
#delete-recording-btn {
color: var(--ov-error-color);
}
#download-recording-btn {
color: var(--ov-accent-action-color);
}
mat-expansion-panel { mat-expansion-panel {
margin: 0px 0px 5px 0px; margin: 0px 0px 5px 0px;
} }
@ -618,18 +156,6 @@
height: 0px !important; height: 0px !important;
} }
::ng-deep .mdc-list-item__secondary-text,
::ng-deep .mdc-list-item__primary-text {
color: var(--ov-text-surface-color);
}
// ::ng-deep .mat-mdc-list-item:hover {
// color: #000000 !important;
// }
::ng-deep .mat-mdc-list-item:hover .mat-mdc-list-item-title {
color: var(--ov-text-surface-color) !important;
}
.blink { .blink {
animation: blinker 1.5s linear infinite !important; animation: blinker 1.5s linear infinite !important;
} }

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RecordingActivityComponent } from './recording-activity.component';
describe('RecordingActivityComponent', () => {
let component: RecordingActivityComponent;
let fixture: ComponentFixture<RecordingActivityComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RecordingActivityComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(RecordingActivityComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, OnDestroy, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subscription } from 'rxjs';
import { import {
RecordingDeleteRequestedEvent, RecordingDeleteRequestedEvent,
RecordingDownloadClickedEvent, RecordingDownloadClickedEvent,
@ -16,7 +16,6 @@ import { RecordingService } from '../../../../services/recording/recording.servi
import { OpenViduService } from '../../../../services/openvidu/openvidu.service'; import { OpenViduService } from '../../../../services/openvidu/openvidu.service';
import { ILogger } from '../../../../models/logger.model'; import { ILogger } from '../../../../models/logger.model';
import { LoggerService } from '../../../../services/logger/logger.service'; import { LoggerService } from '../../../../services/logger/logger.service';
import { OpenViduComponentsConfigService } from '../../../../services/config/directive-config.service';
/** /**
* The **RecordingActivityComponent** is the component that allows showing the recording activity. * The **RecordingActivityComponent** is the component that allows showing the recording activity.
@ -25,14 +24,13 @@ import { OpenViduComponentsConfigService } from '../../../../services/config/dir
selector: 'ov-recording-activity', selector: 'ov-recording-activity',
templateUrl: './recording-activity.component.html', templateUrl: './recording-activity.component.html',
styleUrls: ['./recording-activity.component.scss', '../activities-panel.component.scss'], styleUrls: ['./recording-activity.component.scss', '../activities-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
// TODO: Allow to add more than one recording type // TODO: Allow to add more than one recording type
// TODO: Allow to choose where the recording is stored (s3, google cloud, etc) // TODO: Allow to choose where the recording is stored (s3, google cloud, etc)
// TODO: Allow to choose the layout of the recording // TODO: Allow to choose the layout of the recording
export class RecordingActivityComponent implements OnInit, OnDestroy { export class RecordingActivityComponent implements OnInit {
/** /**
* @internal * @internal
*/ */
@ -68,20 +66,6 @@ export class RecordingActivityComponent implements OnInit, OnDestroy {
*/ */
@Output() onRecordingPlayClicked: EventEmitter<RecordingPlayClickedEvent> = new EventEmitter<RecordingPlayClickedEvent>(); @Output() onRecordingPlayClicked: EventEmitter<RecordingPlayClickedEvent> = new EventEmitter<RecordingPlayClickedEvent>();
/**
* @internal
* Provides event notifications that fire when view recordings button has been clicked.
* This event is triggered when the user wants to view all recordings in an external page.
*/
@Output() onViewRecordingsClicked: EventEmitter<void> = new EventEmitter<void>();
/**
* @internal
* This event is fired when the user clicks on the view recording button.
* It provides the recording ID as event data.
*/
@Output() onViewRecordingClicked: EventEmitter<string> = new EventEmitter<string>();
/** /**
* @internal * @internal
*/ */
@ -114,53 +98,12 @@ export class RecordingActivityComponent implements OnInit, OnDestroy {
*/ */
recordingError: any; recordingError: any;
/**
* @internal
*/
hasRoomTracksPublished: boolean = false;
/** /**
* @internal * @internal
*/ */
mouseHovering: boolean = false; mouseHovering: boolean = false;
/**
* @internal
*/
isReadOnlyMode: boolean = false;
/**
* @internal
*/
viewButtonText: string = 'PANEL.RECORDING.VIEW';
/**
* @internal
*/
showStartStopRecordingButton: boolean = true;
/**
* @internal
*/
showViewRecordingsButton: boolean = false;
/**
* @internal
*/
showRecordingList: boolean = true; // Controls visibility of the recording list in the panel
/**
* @internal
*/
showControls: { play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean } = {
play: true,
download: true,
delete: true,
externalView: false
};
private log: ILogger; private log: ILogger;
private destroy$ = new Subject<void>(); private recordingStatusSubscription: Subscription;
/** /**
* @internal * @internal
@ -171,8 +114,7 @@ export class RecordingActivityComponent implements OnInit, OnDestroy {
private actionService: ActionService, private actionService: ActionService,
private openviduService: OpenViduService, private openviduService: OpenViduService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private loggerSrv: LoggerService, private loggerSrv: LoggerService
private libService: OpenViduComponentsConfigService
) { ) {
this.log = this.loggerSrv.get('RecordingActivityComponent'); this.log = this.loggerSrv.get('RecordingActivityComponent');
} }
@ -182,23 +124,13 @@ export class RecordingActivityComponent implements OnInit, OnDestroy {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.subscribeToRecordingStatus(); this.subscribeToRecordingStatus();
this.subscribeToTracksChanges();
this.subscribeToConfigChanges();
} }
/** /**
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
this.destroy$.next(); if (this.recordingStatusSubscription) this.recordingStatusSubscription.unsubscribe();
this.destroy$.complete();
}
/**
* @internal
*/
trackByRecordingId(index: number, recording: RecordingInfo): string | undefined {
return recording.id;
} }
/** /**
@ -293,105 +225,11 @@ export class RecordingActivityComponent implements OnInit, OnDestroy {
this.recordingService.playRecording(recording); this.recordingService.playRecording(recording);
} }
/**
* @internal
*/
viewRecording(recording: RecordingInfo) {
// This method can be overridden or emit a custom event for navigation
// For now, it uses the same behavior as play, but can be customized
if (!recording.filename) {
this.log.e('Error viewing recording. Recording filename is undefined');
return;
}
const payload: RecordingPlayClickedEvent = {
roomName: this.openviduService.getRoomName(),
recordingId: recording.id
};
this.onRecordingPlayClicked.emit(payload);
// You can customize this to navigate to a different page instead
this.recordingService.playRecording(recording);
}
/**
* @internal
*/
viewAllRecordings() {
this.onViewRecordingsClicked.emit();
}
/**
* @internal
* Format duration in seconds to a readable format (e.g., "2m 30s")
*/
formatDuration(seconds: number): string {
if (!seconds || seconds < 0) return '0s';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}h ${minutes}m`;
} else if (minutes > 0) {
return `${minutes}m ${remainingSeconds}s`;
} else {
return `${remainingSeconds}s`;
}
}
/**
* @internal
* Format file size in bytes to a readable format (e.g., "2.5 MB")
*/
formatFileSize(bytes: number): string {
if (!bytes || bytes < 0) return '0 B';
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const size = bytes / Math.pow(1024, i);
return `${size.toFixed(1)} ${sizes[i]}`;
}
private subscribeToConfigChanges() {
this.libService.recordingActivityReadOnly$.pipe(takeUntil(this.destroy$)).subscribe((readOnly: boolean) => {
this.isReadOnlyMode = readOnly;
this.cd.markForCheck();
});
this.libService.recordingActivityShowControls$
.pipe(takeUntil(this.destroy$))
.subscribe((controls: { play?: boolean; download?: boolean; delete?: boolean; externalView?: boolean }) => {
this.showControls = controls;
this.cd.markForCheck();
});
this.libService.recordingActivityStartStopRecordingButton$.pipe(takeUntil(this.destroy$)).subscribe((show: boolean) => {
this.showStartStopRecordingButton = show;
this.cd.markForCheck();
});
this.libService.recordingActivityViewRecordingsButton$.pipe(takeUntil(this.destroy$)).subscribe((show: boolean) => {
this.showViewRecordingsButton = show;
this.cd.markForCheck();
});
this.libService.recordingActivityShowRecordingsList$.pipe(takeUntil(this.destroy$)).subscribe((show: boolean) => {
this.showRecordingList = show;
this.cd.markForCheck();
});
}
private subscribeToRecordingStatus() { private subscribeToRecordingStatus() {
this.recordingService.recordingStatusObs.pipe(takeUntil(this.destroy$)).subscribe((event: RecordingStatusInfo) => { this.recordingStatusSubscription = this.recordingService.recordingStatusObs.subscribe((event: RecordingStatusInfo) => {
const { status, recordingList, error } = event; const { status, recordingList, error } = event;
this.recordingStatus = status; this.recordingStatus = status;
if (this.showRecordingList) { this.recordingList = recordingList;
this.recordingList = recordingList;
} else {
// Avoid showing recordings
this.recordingList = [];
}
this.recordingError = error; this.recordingError = error;
this.recordingAlive = this.recordingStatus === RecordingStatus.STARTED; this.recordingAlive = this.recordingStatus === RecordingStatus.STARTED;
if (this.recordingStatus !== RecordingStatus.FAILED) { if (this.recordingStatus !== RecordingStatus.FAILED) {
@ -400,24 +238,4 @@ export class RecordingActivityComponent implements OnInit, OnDestroy {
this.cd.markForCheck(); this.cd.markForCheck();
}); });
} }
private subscribeToTracksChanges() {
this.hasRoomTracksPublished = this.openviduService.hasRoomTracksPublished();
this.participantService.localParticipant$.pipe(takeUntil(this.destroy$)).subscribe(() => {
const newValue = this.openviduService.hasRoomTracksPublished();
if (this.hasRoomTracksPublished !== newValue) {
this.hasRoomTracksPublished = newValue;
this.cd.markForCheck();
}
});
this.participantService.remoteParticipants$.pipe(takeUntil(this.destroy$)).subscribe(() => {
const newValue = this.openviduService.hasRoomTracksPublished();
if (this.hasRoomTracksPublished !== newValue) {
this.hasRoomTracksPublished = newValue;
this.cd.markForCheck();
}
});
}
} }

View File

@ -8,7 +8,7 @@
<div class="effects-container" fxFlex="100%" fxLayoutAlign="space-evenly none"> <div class="effects-container" fxFlex="100%" fxLayoutAlign="space-evenly none">
<div> <div>
<h4 class="background-title">{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4> <h4>{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4>
<div> <div>
<button <button
*ngFor="let effect of noEffectAndBlurredBackground" *ngFor="let effect of noEffectAndBlurredBackground"
@ -29,13 +29,12 @@
</div> </div>
<hr /> <hr />
<div> <div>
<h4 class="background-title">{{ 'PANEL.BACKGROUND.IMAGES_SECTION' | translate }}</h4> <h4>{{ 'PANEL.BACKGROUND.IMAGES_SECTION' | translate }}</h4>
<div class="grid"> <div class="grid">
<div <div
*ngFor="let effect of backgroundImages" *ngFor="let effect of backgroundImages"
class="effect-button" class="effect-button"
[id]="'effect-' + effect.id"
[class.active-effect-btn]="backgroundSelectedId === effect.id" [class.active-effect-btn]="backgroundSelectedId === effect.id"
(click)="applyBackground(effect)" (click)="applyBackground(effect)"
> >

View File

@ -1,6 +1,3 @@
.background-title {
color: var(--ov-text-surface-color);
}
.effects-container { .effects-container {
display: block !important; display: block !important;
overflow-y: auto; overflow-y: auto;
@ -10,9 +7,8 @@
.effect-button { .effect-button {
margin: 5px; margin: 5px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
background-color: var(--ov-secondary-action-color); background-color: var(--ov-light-color);
color: var(--ov-primary-action-color);
width: 60px; width: 60px;
height: 60px; height: 60px;
line-height: inherit; line-height: inherit;
@ -23,7 +19,7 @@
} }
.active-effect-btn { .active-effect-btn {
border: 2px solid var(--ov-accent-action-color); border: 2px solid var(--ov-tertiary-color);
} }
#hard_blur-btn .mat-icon { #hard_blur-btn .mat-icon {
@ -38,7 +34,7 @@
.grid img { .grid img {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
} }
/* TODO(mdc-migration): The following rule targets internal classes of slider that may no longer apply for the MDC version. */ /* TODO(mdc-migration): The following rule targets internal classes of slider that may no longer apply for the MDC version. */

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BackgroundEffectsPanelComponent } from './background-effects-panel.component';
describe('BackgroundEffectsPanelComponent', () => {
let component: BackgroundEffectsPanelComponent;
let fixture: ComponentFixture<BackgroundEffectsPanelComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BackgroundEffectsPanelComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BackgroundEffectsPanelComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -12,8 +12,7 @@ import { VirtualBackgroundService } from '../../../services/virtual-background/v
selector: 'ov-background-effects-panel', selector: 'ov-background-effects-panel',
templateUrl: './background-effects-panel.component.html', templateUrl: './background-effects-panel.component.html',
styleUrls: ['../panel.component.scss', './background-effects-panel.component.scss'], styleUrls: ['../panel.component.scss', './background-effects-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
export class BackgroundEffectsPanelComponent implements OnInit { export class BackgroundEffectsPanelComponent implements OnInit {
backgroundSelectedId: string; backgroundSelectedId: string;
@ -57,6 +56,10 @@ export class BackgroundEffectsPanelComponent implements OnInit {
} }
async applyBackground(effect: BackgroundEffect) { async applyBackground(effect: BackgroundEffect) {
await this.backgroundService.applyBackground(effect); if (effect.type === EffectType.NONE) {
await this.backgroundService.removeBackground();
} else {
await this.backgroundService.applyBackground(effect);
}
} }
} }

View File

@ -17,7 +17,7 @@
<p *ngIf="data.isLocal">{{ 'PANEL.CHAT.YOU' | translate }}</p> <p *ngIf="data.isLocal">{{ 'PANEL.CHAT.YOU' | translate }}</p>
<p *ngIf="!data.isLocal">{{ data.participantName }}</p> <p *ngIf="!data.isLocal">{{ data.participantName }}</p>
</div> </div>
<div class="chat-message"> <div class="msg-content">
<p [innerHTML]="data.message | linkify"></p> <p [innerHTML]="data.message | linkify"></p>
</div> </div>
</div> </div>

View File

@ -1,7 +1,6 @@
$ov-selection-color: #d4d6d7;
.text-container { .text-container {
color: var(--ov-text-primary-color); background-color: var(--ov-light-color);
color: var(--ov-panel-text-color);
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
flex: inherit; flex: inherit;
@ -9,7 +8,6 @@ $ov-selection-color: #d4d6d7;
.text-info { .text-info {
margin: auto; margin: auto;
color: var(--ov-text-surface-color);
} }
.vertical-align { .vertical-align {
@ -29,15 +27,13 @@ $ov-selection-color: #d4d6d7;
.input-container { .input-container {
height: 65px; height: 65px;
display: flex; display: flex;
background-color: var(--ov-surface-color); background-color: var(--ov-light-color);
border: 1px solid $ov-selection-color;
padding: 10px 5px 10px 10px; padding: 10px 5px 10px 10px;
margin: 10px; margin: 10px;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
order: 3; order: 3;
justify-content: space-evenly; justify-content: space-evenly;
align-items: none; align-items: none;
} }
.input-container textarea { .input-container textarea {
@ -58,7 +54,6 @@ $ov-selection-color: #d4d6d7;
-moz-box-shadow: none; -moz-box-shadow: none;
box-shadow: none; box-shadow: none;
font-family: 'Roboto', 'RobotoDraft', Helvetica, Arial, sans-serif; font-family: 'Roboto', 'RobotoDraft', Helvetica, Arial, sans-serif;
color: var(--ov-text-surface-color);
} }
.message { .message {
@ -78,27 +73,25 @@ $ov-selection-color: #d4d6d7;
.participant-name-container p { .participant-name-container p {
font-size: 13px; font-size: 13px;
font-style: italic; font-weight: bold;
color: var(--ov-text-surface-color); color: var(--ov-panel-text-color);
padding: 5px;
} }
.chat-message { .msg-content {
position: relative; position: relative;
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
padding: 8px; padding: 8px;
color: var(--ov-secondary-action-color); color: #000000;
width: auto; width: auto;
max-width: 95%; max-width: 95%;
font-size: 14px; font-size: 13px;
word-break: break-all; word-break: break-all;
background-color: var(--ov-primary-action-color);
} }
#send-btn { #send-btn {
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
color: var(--ov-secondary-action-color); color: var(--ov-light-color);
background-color: var(--ov-primary-action-color); background-color: var(--ov-tertiary-color);
align-self: center; align-self: center;
height: 75px; height: 75px;
} }
@ -109,7 +102,7 @@ $ov-selection-color: #d4d6d7;
text-align: left; text-align: left;
} }
.message.left .msg-detail .chat-message { .message.left .msg-detail .msg-content {
float: left; float: left;
} }
@ -121,10 +114,10 @@ $ov-selection-color: #d4d6d7;
text-align: right; text-align: right;
} }
.message.right .msg-detail .chat-message { .message.right .msg-detail .msg-content {
float: right; float: right;
} }
::ng-deep a:-webkit-any-link { ::ng-deep a:-webkit-any-link {
color: var(--ov-accent-action-color); color: var(--ov-tertiary-color);
} }

View File

@ -0,0 +1,27 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChatService } from '../../../services/chat/chat.service';
import { ChatServiceMock } from '../../../services/chat/chat.service.mock';
import { ChatPanelComponent } from './chat-panel.component';
describe('ChatPanelComponent', () => {
let component: ChatPanelComponent;
let fixture: ComponentFixture<ChatPanelComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ChatPanelComponent],
providers: [{ provide: ChatService, useClass: ChatServiceMock }]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChatPanelComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,5 +1,5 @@
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subscription } from 'rxjs';
import { ChatMessage } from '../../../models/chat.model'; import { ChatMessage } from '../../../models/chat.model';
import { PanelType } from '../../../models/panel.model'; import { PanelType } from '../../../models/panel.model';
import { ChatService } from '../../../services/chat/chat.service'; import { ChatService } from '../../../services/chat/chat.service';
@ -13,8 +13,7 @@ import { PanelService } from '../../../services/panel/panel.service';
selector: 'ov-chat-panel', selector: 'ov-chat-panel',
templateUrl: './chat-panel.component.html', templateUrl: './chat-panel.component.html',
styleUrls: ['../panel.component.scss', './chat-panel.component.scss'], styleUrls: ['../panel.component.scss', './chat-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
export class ChatPanelComponent implements OnInit, AfterViewInit { export class ChatPanelComponent implements OnInit, AfterViewInit {
/** /**
@ -34,7 +33,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
*/ */
messageList: ChatMessage[] = []; messageList: ChatMessage[] = [];
private destroy$ = new Subject<void>(); private chatMessageSubscription: Subscription;
/** /**
* @ignore * @ignore
@ -66,8 +65,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
* @ignore * @ignore
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.destroy$.next(); if (this.chatMessageSubscription) this.chatMessageSubscription.unsubscribe();
this.destroy$.complete();
} }
/** /**
@ -110,7 +108,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
} }
private subscribeToMessages() { private subscribeToMessages() {
this.chatService.messagesObs.pipe(takeUntil(this.destroy$)).subscribe((messages: ChatMessage[]) => { this.chatMessageSubscription = this.chatService.messagesObs.subscribe((messages: ChatMessage[]) => {
this.messageList = messages; this.messageList = messages;
if (this.panelService.isChatPanelOpened()) { if (this.panelService.isChatPanelOpened()) {
this.scrollToBottom(); this.scrollToBottom();

View File

@ -1,7 +1,7 @@
.panel-container { .panel-container {
margin: 20px; margin: 20px;
background-color: var(--ov-surface-color); background-color: var(--ov-panel-background);
border-radius: var(--ov-surface-radius); border-radius: var(--ov-panel-radius);
max-height: calc(100% - 40px); max-height: calc(100% - 40px);
min-height: calc(100% - 40px); min-height: calc(100% - 40px);
display: flex; display: flex;
@ -16,11 +16,6 @@
align-items: center; align-items: center;
} }
.panel-title,
.panel-close-button {
color: var(--ov-text-surface-color);
}
.panel-title { .panel-title {
margin-left: 5px; margin-left: 5px;
margin-top: auto; margin-top: auto;
@ -29,7 +24,7 @@
.panel-close-button { .panel-close-button {
margin-left: auto; margin-left: auto;
border-radius: 50%; border-radius: var(--ov-buttons-radius);
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
@ -46,6 +41,6 @@
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: var(--ov-secondary-action-color); background: var(--ov-light-color);
border-radius: 4px; border-radius: 4px;
} }

View File

@ -8,7 +8,7 @@ import {
Output, Output,
TemplateRef TemplateRef
} from '@angular/core'; } from '@angular/core';
import { skip, Subject, takeUntil } from 'rxjs'; import { skip, Subscription } from 'rxjs';
import { import {
ActivitiesPanelDirective, ActivitiesPanelDirective,
AdditionalPanelsDirective, AdditionalPanelsDirective,
@ -25,7 +25,6 @@ import {
} from '../../models/panel.model'; } from '../../models/panel.model';
import { PanelService } from '../../services/panel/panel.service'; import { PanelService } from '../../services/panel/panel.service';
import { BackgroundEffect } from '../../models/background-effect.model'; import { BackgroundEffect } from '../../models/background-effect.model';
import { TemplateManagerService, PanelTemplateConfiguration } from '../../services/template/template-manager.service';
/** /**
* *
@ -38,8 +37,7 @@ import { TemplateManagerService, PanelTemplateConfiguration } from '../../servic
selector: 'ov-panel', selector: 'ov-panel',
templateUrl: './panel.component.html', templateUrl: './panel.component.html',
styleUrls: ['./panel.component.scss'], styleUrls: ['./panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
export class PanelComponent implements OnInit { export class PanelComponent implements OnInit {
/** /**
@ -76,20 +74,42 @@ export class PanelComponent implements OnInit {
*/ */
@ContentChild(ParticipantsPanelDirective) @ContentChild(ParticipantsPanelDirective)
set externalParticipantPanel(externalParticipantsPanel: ParticipantsPanelDirective) { set externalParticipantPanel(externalParticipantsPanel: ParticipantsPanelDirective) {
this._externalParticipantPanel = externalParticipantsPanel; // This directive will has value only when PARTICIPANTS PANEL component tagged with '*ovParticipantsPanel'
// is inside of the PANEL component tagged with '*ovPanel'
if (externalParticipantsPanel) { if (externalParticipantsPanel) {
this.updateTemplatesAndMarkForCheck(); this.participantsPanelTemplate = externalParticipantsPanel.template;
} }
} }
// TODO: backgroundEffectsPanel does not provides customization
// @ContentChild(BackgroundEffectsPanelDirective)
// set externalBackgroundEffectsPanel(externalBackgroundEffectsPanel: BackgroundEffectsPanelDirective) {
// This directive will has value only when BACKGROUND EFFECTS PANEL component tagged with '*ovBackgroundEffectsPanel'
// is inside of the PANEL component tagged with '*ovPanel'
// if (externalBackgroundEffectsPanel) {
// this.backgroundEffectsPanelTemplate = externalBackgroundEffectsPanel.template;
// }
// }
// TODO: settingsPanel does not provides customization
// @ContentChild(SettingsPanelDirective)
// set externalSettingsPanel(externalSettingsPanel: SettingsPanelDirective) {
// This directive will has value only when SETTINGS PANEL component tagged with '*ovSettingsPanel'
// is inside of the PANEL component tagged with '*ovPanel'
// if (externalSettingsPanel) {
// this.settingsPanelTemplate = externalSettingsPanel.template;
// }
// }
/** /**
* @ignore * @ignore
*/ */
@ContentChild(ActivitiesPanelDirective) @ContentChild(ActivitiesPanelDirective)
set externalActivitiesPanel(externalActivitiesPanel: ActivitiesPanelDirective) { set externalActivitiesPanel(externalActivitiesPanel: ActivitiesPanelDirective) {
this._externalActivitiesPanel = externalActivitiesPanel; // This directive will has value only when ACTIVITIES PANEL component tagged with '*ovActivitiesPanel'
// is inside of the PANEL component tagged with '*ovPanel'
if (externalActivitiesPanel) { if (externalActivitiesPanel) {
this.updateTemplatesAndMarkForCheck(); this.activitiesPanelTemplate = externalActivitiesPanel.template;
} }
} }
@ -98,9 +118,10 @@ export class PanelComponent implements OnInit {
*/ */
@ContentChild(ChatPanelDirective) @ContentChild(ChatPanelDirective)
set externalChatPanel(externalChatPanel: ChatPanelDirective) { set externalChatPanel(externalChatPanel: ChatPanelDirective) {
this._externalChatPanel = externalChatPanel; // This directive will has value only when CHAT PANEL component tagged with '*ovChatPanel'
// is inside of the PANEL component tagged with '*ovPanel'
if (externalChatPanel) { if (externalChatPanel) {
this.updateTemplatesAndMarkForCheck(); this.chatPanelTemplate = externalChatPanel.template;
} }
} }
@ -109,9 +130,10 @@ export class PanelComponent implements OnInit {
*/ */
@ContentChild(AdditionalPanelsDirective) @ContentChild(AdditionalPanelsDirective)
set externalAdditionalPanels(externalAdditionalPanels: AdditionalPanelsDirective) { set externalAdditionalPanels(externalAdditionalPanels: AdditionalPanelsDirective) {
this._externalAdditionalPanels = externalAdditionalPanels; // This directive will has value only when ADDITIONAL PANELS component tagged with '*ovPanelAdditionalPanels'
// is inside of the PANEL component tagged with '*ovPanel'
if (externalAdditionalPanels) { if (externalAdditionalPanels) {
this.updateTemplatesAndMarkForCheck(); this.additionalPanelsTemplate = externalAdditionalPanels.template;
} }
} }
@ -172,20 +194,7 @@ export class PanelComponent implements OnInit {
* @internal * @internal
*/ */
isExternalPanelOpened: boolean; isExternalPanelOpened: boolean;
private panelSubscription: Subscription;
/**
* @internal
* Template configuration managed by the service
*/
templateConfig: PanelTemplateConfiguration = {};
// Store directive references for template setup
private _externalParticipantPanel?: ParticipantsPanelDirective;
private _externalChatPanel?: ChatPanelDirective;
private _externalActivitiesPanel?: ActivitiesPanelDirective;
private _externalAdditionalPanels?: AdditionalPanelsDirective;
private destroy$ = new Subject<void>();
private panelEmitersHandler: Map< private panelEmitersHandler: Map<
PanelType, PanelType,
@ -197,78 +206,30 @@ export class PanelComponent implements OnInit {
*/ */
constructor( constructor(
private panelService: PanelService, private panelService: PanelService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef
private templateManagerService: TemplateManagerService
) {} ) {}
/** /**
* @ignore * @ignore
*/ */
ngOnInit(): void { ngOnInit(): void {
this.setupTemplates();
this.subscribeToPanelToggling(); this.subscribeToPanelToggling();
this.panelEmitersHandler.set(PanelType.CHAT, this.onChatPanelStatusChanged); this.panelEmitersHandler.set(PanelType.CHAT, this.onChatPanelStatusChanged);
this.panelEmitersHandler.set(PanelType.PARTICIPANTS, this.onParticipantsPanelStatusChanged); this.panelEmitersHandler.set(PanelType.PARTICIPANTS, this.onParticipantsPanelStatusChanged);
this.panelEmitersHandler.set(PanelType.SETTINGS, this.onSettingsPanelStatusChanged); this.panelEmitersHandler.set(PanelType.SETTINGS, this.onSettingsPanelStatusChanged);
this.panelEmitersHandler.set(PanelType.ACTIVITIES, this.onActivitiesPanelStatusChanged); this.panelEmitersHandler.set(PanelType.ACTIVITIES, this.onActivitiesPanelStatusChanged);
} }
/**
* @internal
* Sets up all templates using the template manager service
*/
private setupTemplates(): void {
this.templateConfig = this.templateManagerService.setupPanelTemplates(
this._externalParticipantPanel,
this._externalChatPanel,
this._externalActivitiesPanel,
this._externalAdditionalPanels
);
// Apply templates to component properties for backward compatibility
this.applyTemplateConfiguration();
}
/**
* @internal
* Applies the template configuration to component properties
*/
private applyTemplateConfiguration(): void {
if (this.templateConfig.participantsPanelTemplate) {
this.participantsPanelTemplate = this.templateConfig.participantsPanelTemplate;
}
if (this.templateConfig.chatPanelTemplate) {
this.chatPanelTemplate = this.templateConfig.chatPanelTemplate;
}
if (this.templateConfig.activitiesPanelTemplate) {
this.activitiesPanelTemplate = this.templateConfig.activitiesPanelTemplate;
}
if (this.templateConfig.additionalPanelsTemplate) {
this.additionalPanelsTemplate = this.templateConfig.additionalPanelsTemplate;
}
}
/**
* @internal
* Updates templates and triggers change detection
*/
private updateTemplatesAndMarkForCheck(): void {
this.setupTemplates();
this.cd.markForCheck();
}
/** /**
* @ignore * @ignore
*/ */
ngOnDestroy() { ngOnDestroy() {
this.isChatPanelOpened = false; this.isChatPanelOpened = false;
this.isParticipantsPanelOpened = false; this.isParticipantsPanelOpened = false;
this.destroy$.next(); if (this.panelSubscription) this.panelSubscription.unsubscribe();
this.destroy$.complete();
} }
private subscribeToPanelToggling() { private subscribeToPanelToggling() {
this.panelService.panelStatusObs.pipe(skip(1), takeUntil(this.destroy$)).subscribe((ev: PanelStatusInfo) => { this.panelSubscription = this.panelService.panelStatusObs.pipe(skip(1)).subscribe((ev: PanelStatusInfo) => {
this.isChatPanelOpened = ev.isOpened && ev.panelType === PanelType.CHAT; this.isChatPanelOpened = ev.isOpened && ev.panelType === PanelType.CHAT;
this.isParticipantsPanelOpened = ev.isOpened && ev.panelType === PanelType.PARTICIPANTS; this.isParticipantsPanelOpened = ev.isOpened && ev.panelType === PanelType.PARTICIPANTS;
this.isBackgroundEffectsPanelOpened = ev.isOpened && ev.panelType === PanelType.BACKGROUND_EFFECTS; this.isBackgroundEffectsPanelOpened = ev.isOpened && ev.panelType === PanelType.BACKGROUND_EFFECTS;

View File

@ -1,71 +1,33 @@
<mat-list> <mat-list>
<mat-list-item> <mat-list-item>
<!-- Main participant container with improved structure --> <div matListItemIcon class="participant-avatar" [style.background-color]="_participant.colorProfile">
<div class="participant-container" [attr.data-participant-id]="_participant?.sid"> <mat-icon>person</mat-icon>
<!-- Avatar section with dynamic color --> </div>
<div <h3 matListItemTitle class="participant-name">{{ _participant.name }}
class="participant-avatar" <span *ngIf="_participant.isLocal"> ({{ 'PANEL.PARTICIPANTS.YOU' | translate }})</span>
[style.background-color]="_participant?.colorProfile" </h3>
[attr.aria-label]="'Avatar for ' + participantDisplayName" <p matListItemLine class="participant-subtitle">{{ _participant | tracksPublishedTypes }}</p>
<!-- <p matListItemLine>
<span class="participant-subtitle"></span>
</p> -->
<div class="participant-action-buttons" matListItemMeta>
<button
mat-icon-button
id="mute-btn"
*ngIf="!_participant.isLocal && showMuteButton"
[class.warn-btn]="_participant.isMutedForcibly"
(click)="toggleMuteForcibly()"
[disableRipple]="true"
> >
<mat-icon>person</mat-icon> <mat-icon *ngIf="!_participant.isMutedForcibly">volume_up</mat-icon>
</div> <mat-icon *ngIf="_participant.isMutedForcibly">volume_off</mat-icon>
</button>
<!-- Content section with name and status --> <!-- External item elements -->
<div class="participant-content"> <ng-container *ngIf="participantPanelItemElementsTemplate">
<div class="participant-name"> <ng-container *ngTemplateOutlet="participantPanelItemElementsTemplate"></ng-container>
{{ participantDisplayName }} </ng-container>
<span *ngIf="isLocalParticipant" class="local-indicator">
{{ 'PANEL.PARTICIPANTS.YOU' | translate }}
</span>
<!-- Participant badges -->
<div class="participant-badges">
<ng-container *ngTemplateOutlet="participantBadgeTemplate"></ng-container>
</div>
</div>
<div class="participant-subtitle">
<span class="status-indicator">
{{ _participant | tracksPublishedTypes }}
</span>
<!-- Additional status indicators -->
<span *ngIf="_participant?.isMutedForcibly" class="status-indicator">
<mat-icon>volume_off</mat-icon>
{{ 'PANEL.PARTICIPANTS.MUTED' | translate }}
</span>
</div>
</div>
<!-- Action buttons section -->
<div class="participant-action-buttons">
<!-- Mute/Unmute button for remote participants -->
<button
mat-icon-button
id="mute-btn"
*ngIf="!isLocalParticipant && showMuteButton"
[class.warn-btn]="_participant?.isMutedForcibly"
(click)="toggleMuteForcibly()"
[disabled]="!_participant"
[disableRipple]="true"
[attr.aria-label]="
_participant?.isMutedForcibly
? ('PANEL.PARTICIPANTS.UNMUTE' | translate) + ' ' + participantDisplayName
: ('PANEL.PARTICIPANTS.MUTE' | translate) + ' ' + participantDisplayName
"
[matTooltip]="
_participant?.isMutedForcibly ? ('PANEL.PARTICIPANTS.UNMUTE' | translate) : ('PANEL.PARTICIPANTS.MUTE' | translate)
"
>
<mat-icon *ngIf="!_participant?.isMutedForcibly">volume_up</mat-icon>
<mat-icon *ngIf="_participant?.isMutedForcibly">volume_off</mat-icon>
</button>
<!-- External item elements with improved structure -->
<div class="external-elements" *ngIf="hasExternalElements">
<ng-container *ngTemplateOutlet="participantPanelItemElementsTemplate"></ng-container>
</div>
</div>
</div> </div>
</mat-list-item> </mat-list-item>
</mat-list> </mat-list>

View File

@ -1,443 +1,59 @@
:host { :host {
// Container for the participant item
.participant-container {
position: relative;
display: flex;
align-items: center;
padding: 12px 16px;
border-radius: var(--ov-surface-radius, 8px);
background-color: var(--ov-surface-background, #ffffff);
border-bottom: 1px solid var(--ov-surface-border, #e0e0e0);
transition: all 0.2s ease-in-out;
min-height: 64px;
// &:hover {
// background-color: var(--ov-surface-hover, #f5f5f5);
// transform: translateY(-1px);
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
// }
&:last-child {
border-bottom: none;
}
// Loading state
&.loading {
opacity: 0.7;
pointer-events: none;
&::after {
content: '';
position: absolute;
top: 50%;
right: 16px;
width: 16px;
height: 16px;
border: 2px solid var(--ov-primary-color, #1976d2);
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
}
}
// Focus state for keyboard navigation
&:focus-within {
outline: 2px solid var(--ov-primary-color, #1976d2);
outline-offset: 2px;
}
}
// Avatar styling with improved design
.participant-avatar { .participant-avatar {
display: flex; display: inherit;
align-items: center; border-radius: var(--ov-panel-radius);
justify-content: center; margin: auto !important;
width: 40px; padding: 10px;
height: 40px; color: var(--ov-panel-text-color);
border-radius: var(--ov-surface-radius);
margin-right: 12px;
padding: 0;
color: #ffffff;
font-weight: 500;
flex-shrink: 0;
position: relative;
overflow: hidden;
mat-icon {
font-size: 20px;
width: 20px;
height: 20px;
z-index: 1;
}
} }
// Main content area
.participant-content {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0; // Allows text truncation
margin-right: 8px;
}
// Participant name styling
.participant-name {
font-weight: 600 !important;
font-size: 14px;
line-height: 1.2;
color: var(--ov-text-primary, #212121);
margin: 0 0 4px 0;
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
// Local participant indicator
.local-indicator {
font-size: 10px;
font-weight: 600;
color: var(--ov-primary-color, #1976d2);
background-color: var(--ov-primary-light, #e3f2fd);
padding: 4px 8px;
border-radius: var(--ov-surface-radius);
text-transform: uppercase;
letter-spacing: 0.5px;
flex-shrink: 0;
border: 1px solid var(--ov-primary-color, #1976d2);
}
}
// Subtitle styling
.participant-subtitle { .participant-subtitle {
font-style: normal; font-style: italic;
font-size: 12px !important; font-size: 11px !important;
font-weight: 400;
margin: 0; margin: 0;
color: var(--ov-text-secondary, #757575); }
line-height: 1.3; .participant-name {
display: flex; font-weight: bold !important;
align-items: center; color: var(--ov-panel-text-color);
gap: 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
// Status indicators
.status-indicator {
display: inline-flex;
align-items: center;
gap: 3px;
mat-icon {
font-size: 12px;
width: 12px;
height: 12px;
}
// Different colors for different statuses
&.camera-on {
color: var(--ov-success-color, #4caf50);
}
&.camera-off {
color: var(--ov-warning-color, #ff9800);
}
&.microphone-muted {
color: var(--ov-error-color, #d32f2f);
}
}
} }
// Action buttons container
.participant-action-buttons { .participant-action-buttons {
display: flex; display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
margin-left: auto;
} }
// Mute button styling ::ng-deep .participant-action-buttons > *:not(#mute-btn) {
#mute-btn { display: contents;
width: 32px;
height: 32px;
border-radius: 50%;
color: var(--ov-text-secondary, #757575);
background-color: transparent;
transition: all 0.2s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
position: relative;
&:hover {
background-color: var(--ov-surface-hover, #f5f5f5);
color: var(--ov-text-primary, #212121);
transform: scale(1.1);
}
&:focus {
outline: 2px solid var(--ov-primary-color, #1976d2);
outline-offset: 2px;
}
&:disabled {
opacity: 0.5;
pointer-events: none;
}
&.warn-btn {
color: var(--ov-error-color, #d32f2f);
background-color: var(--ov-error-light, #ffebee);
&:hover {
background-color: var(--ov-error-color, #d32f2f);
color: #ffffff;
}
// Pulsing animation for muted state
animation: pulse 2s infinite;
}
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}
} }
// Participant badges container ::ng-deep .participant-action-buttons > *:not(#mute-btn) > * {
.participant-badges { margin: auto;
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
// Badge styling
::ng-deep .badge {
// Badge variants
&.moderator {
color: var(--ov-warning-color, #f57c00);
}
&.speaker {
color: var(--ov-primary-color, #1976d2);
}
&.host {
color: var(--ov-success-color, #4caf50);
}
}
}
// After local participant content area
.after-local-content {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid var(--ov-surface-border, #e0e0e0);
animation: fadeIn 0.3s ease-in-out;
background-color: var(--ov-surface-alt, #fafafa);
border-radius: var(--ov-surface-radius, 8px);
padding: 12px;
}
// External item elements styling
.external-elements {
display: flex;
align-items: center;
gap: 4px;
// Custom styling for external buttons
::ng-deep button {
transition: all 0.2s ease-in-out;
&:hover {
transform: scale(1.05);
}
}
}
// Material Design overrides for better integration
mat-list {
padding: 0;
} }
::ng-deep .mat-mdc-list-item { ::ng-deep .mat-mdc-list-item {
height: auto !important; height: max-content !important;
padding: 0 !important; padding-bottom: 10px !important;
min-height: auto !important; }
border-radius: var(--ov-surface-radius, 8px);
mat-list {
padding: 3px;
} }
::ng-deep .mdc-list-item__content { ::ng-deep .mdc-list-item__content {
padding: 0 !important; padding-left: 10px !important;
align-self: stretch !important; align-self: center !important;
width: 100%;
} }
::ng-deep .mat-mdc-list-base { ::ng-deep .mat-mdc-list-base {
--mdc-list-list-item-hover-label-text-color: unset; --mdc-list-list-item-hover-label-text-color: unset;
--mdc-list-list-item-hover-leading-icon-color: unset; --mdc-list-list-item-hover-leading-icon-color: unset;
padding: 0;
} }
::ng-deep .mat-mdc-list-item:hover { #mute-btn {
background-color: transparent !important; border-radius: var(--ov-buttons-radius);
} }
// Animations .warn-btn {
@keyframes fadeIn { /* background-color: var(--ov-warn-color) !important; */
from { color: var(--ov-warn-color);
opacity: 0;
transform: translateY(-8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
// Responsive design
@media (max-width: 768px) {
.participant-container {
padding: 10px 12px;
min-height: 56px;
}
.participant-avatar {
width: 36px;
height: 36px;
margin-right: 10px;
mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}
&::after {
width: 10px;
height: 10px;
bottom: 1px;
right: 1px;
}
}
.participant-name {
font-size: 13px;
.local-indicator {
font-size: 9px;
padding: 2px 6px;
}
}
.participant-subtitle {
font-size: 11px !important;
}
#mute-btn {
width: 28px;
height: 28px;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
}
.after-local-content {
margin-top: 10px;
padding-top: 10px;
padding: 10px;
}
}
// High contrast mode support
@media (prefers-contrast: high) {
.participant-container {
border: 2px solid var(--ov-text-primary, #212121);
}
.participant-avatar {
border: 2px solid var(--ov-surface-background, #ffffff);
}
.local-indicator {
border-width: 2px;
}
}
// Reduced motion support
@media (prefers-reduced-motion: reduce) {
.participant-container,
.participant-avatar,
#mute-btn,
.after-local-content,
.external-elements ::ng-deep button {
transition: none;
animation: none;
}
.participant-container:hover {
transform: none;
}
.participant-avatar:hover,
#mute-btn:hover,
.external-elements ::ng-deep button:hover {
transform: none;
}
#mute-btn.warn-btn {
animation: none;
}
}
// Dark theme support
@media (prefers-color-scheme: dark) {
.participant-container {
background-color: var(--ov-surface-background, #424242);
border-bottom-color: var(--ov-surface-border, #616161);
&:hover {
background-color: var(--ov-surface-hover, #484848);
}
}
.participant-name {
color: var(--ov-text-primary, #ffffff);
}
.participant-subtitle {
color: var(--ov-text-secondary, #cccccc);
}
.after-local-content {
background-color: var(--ov-surface-alt, #373737);
}
} }
} }

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ParticipantPanelItemComponent } from './participant-panel-item.component';
describe('ParticipantPanelItemComponent', () => {
let component: ParticipantPanelItemComponent;
let fixture: ComponentFixture<ParticipantPanelItemComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ParticipantPanelItemComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ParticipantPanelItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,23 +1,21 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ParticipantPanelItemElementsDirective } from '../../../../directives/template/openvidu-components-angular.directive'; import { ParticipantPanelItemElementsDirective } from '../../../../directives/template/openvidu-components-angular.directive';
import { ParticipantPanelParticipantBadgeDirective } from '../../../../directives/template/internals.directive';
import { ParticipantModel } from '../../../../models/participant.model'; import { ParticipantModel } from '../../../../models/participant.model';
import { OpenViduComponentsConfigService } from '../../../../services/config/directive-config.service'; import { OpenViduComponentsConfigService } from '../../../../services/config/openvidu-components-angular.config.service';
import { ParticipantService } from '../../../../services/participant/participant.service'; import { ParticipantService } from '../../../../services/participant/participant.service';
import { TemplateManagerService, ParticipantPanelItemTemplateConfiguration } from '../../../../services/template/template-manager.service';
/** /**
*
* The **ParticipantPanelItemComponent** is hosted inside of the {@link ParticipantsPanelComponent}. * The **ParticipantPanelItemComponent** is hosted inside of the {@link ParticipantsPanelComponent}.
* It displays participant information with enhanced UI/UX, including support for custom content * It is in charge of displaying the participants information inside of the ParticipansPanelComponent.
* injection through structural directives.
*/ */
@Component({ @Component({
selector: 'ov-participant-panel-item', selector: 'ov-participant-panel-item',
templateUrl: './participant-panel-item.component.html', templateUrl: './participant-panel-item.component.html',
styleUrls: ['./participant-panel-item.component.scss'], styleUrls: ['./participant-panel-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush
standalone: false
}) })
export class ParticipantPanelItemComponent implements OnInit, OnDestroy { export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
/** /**
@ -36,69 +34,40 @@ export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
*/ */
@ContentChild(ParticipantPanelItemElementsDirective) @ContentChild(ParticipantPanelItemElementsDirective)
set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) { set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) {
this._externalItemElements = externalItemElements; // This directive will has value only when ITEM ELEMENTS component tagget with '*ovParticipantPanelItemElements' directive
// is inside of the P PANEL ITEM component tagged with '*ovParticipantPanelItem' directive
if (externalItemElements) { if (externalItemElements) {
this.updateTemplatesAndMarkForCheck(); this.participantPanelItemElementsTemplate = externalItemElements.template;
} }
} }
/**
* The participant to be displayed
* @ignore
*/
@Input()
set participant(participant: ParticipantModel) {
this._participant = participant;
}
/** /**
* @ignore * @ignore
*/ */
@ContentChild(ParticipantPanelParticipantBadgeDirective)
set externalParticipantBadge(participantBadge: ParticipantPanelParticipantBadgeDirective) {
this._externalParticipantBadge = participantBadge;
if (participantBadge) {
this.updateTemplatesAndMarkForCheck();
}
}
/**
* @internal
* Template configuration managed by the service
*/
templateConfig: ParticipantPanelItemTemplateConfiguration = {};
// Store directive references for template setup
private _externalItemElements?: ParticipantPanelItemElementsDirective;
private _externalParticipantBadge?: ParticipantPanelParticipantBadgeDirective;
/**
* The participant to be displayed
*/
@Input()
set participant(participant: ParticipantModel) {
this._participant = participant;
this.cd.markForCheck();
}
/**
* @internal
* Current participant being displayed
*/
_participant: ParticipantModel; _participant: ParticipantModel;
/**
* Whether to show the mute button for remote participants
*/
@Input()
muteButton: boolean = true;
/** /**
* @ignore * @ignore
*/ */
constructor( constructor(
private libService: OpenViduComponentsConfigService, private libService: OpenViduComponentsConfigService,
private participantService: ParticipantService, private participantService: ParticipantService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef
private templateManagerService: TemplateManagerService
) {} ) {}
/** /**
* @ignore * @ignore
*/ */
ngOnInit(): void { ngOnInit(): void {
this.setupTemplates();
this.subscribeToParticipantPanelItemDirectives(); this.subscribeToParticipantPanelItemDirectives();
} }
@ -110,72 +79,14 @@ export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
} }
/** /**
* Toggles the mute state of a remote participant * @ignore
*/ */
toggleMuteForcibly() { toggleMuteForcibly() {
if (this._participant && !this._participant.isLocal) { if (this._participant) {
this.participantService.setRemoteMutedForcibly(this._participant.sid, !this._participant.isMutedForcibly); this.participantService.setRemoteMutedForcibly(this._participant.sid, !this._participant.isMutedForcibly);
} }
} }
/**
* Gets the template for local participant badge
*/
get participantBadgeTemplate(): TemplateRef<any> | undefined {
return this._externalParticipantBadge?.template;
}
/**
* Checks if the current participant is the local participant
*/
get isLocalParticipant(): boolean {
return this._participant?.isLocal || false;
}
/**
* Gets the participant's display name
*/
get participantDisplayName(): string {
return this._participant?.name || '';
}
/**
* Checks if external elements are available
*/
get hasExternalElements(): boolean {
return !!this.participantPanelItemElementsTemplate;
}
/**
* @internal
* Sets up all templates using the template manager service
*/
private setupTemplates(): void {
this.templateConfig = this.templateManagerService.setupParticipantPanelItemTemplates(this._externalItemElements);
// Apply templates to component properties for backward compatibility
this.applyTemplateConfiguration();
}
/**
* @internal
* Applies the template configuration to component properties
*/
private applyTemplateConfiguration(): void {
if (this.templateConfig.participantPanelItemElementsTemplate) {
this.participantPanelItemElementsTemplate = this.templateConfig.participantPanelItemElementsTemplate;
}
}
/**
* @internal
* Updates templates and triggers change detection
*/
private updateTemplatesAndMarkForCheck(): void {
this.setupTemplates();
this.cd.markForCheck();
}
private subscribeToParticipantPanelItemDirectives() { private subscribeToParticipantPanelItemDirectives() {
this.muteButtonSub = this.libService.participantItemMuteButton$.subscribe((value: boolean) => { this.muteButtonSub = this.libService.participantItemMuteButton$.subscribe((value: boolean) => {
this.showMuteButton = value; this.showMuteButton = value;

Some files were not shown because too many files have changed in this diff Show More