Compare commits

...

424 Commits

Author SHA1 Message Date
Carlos Santos efada4c166 ov-components: (test) Add check for speaker element when local participant is muted 2025-08-14 12:54:30 +02:00
Carlos Santos 3fc0193260 Revert "ov-components: (test) Enhance element counting method to handle timeouts gracefully"
This reverts commit 61a3589dd7.
2025-08-14 12:53:31 +02:00
Carlos Santos 61a3589dd7 ov-components: (test) Enhance element counting method to handle timeouts gracefully 2025-08-14 12:43:10 +02:00
Carlos Santos 8d1c2468f5 ov-components: Refactor VideoconferenceComponent to use private properties for external structural directives, enhancing encapsulation and template setup 2025-08-14 12:16:54 +02:00
Carlos Santos 020413257f ov-components: Implement toolbar room name directive and update toolbar to display room name dynamically 2025-08-13 12:49:03 +02:00
Carlos Santos 6c78abdcc0 ov-components: Remove unnecessary mic button interaction in stream UI tests 2025-08-11 14:17:36 +02:00
Carlos Santos e31a78d153 ov-components: refactor(storage): Enhance tab management and cleanup mechanisms in StorageService 2025-08-11 13:57:23 +02:00
Carlos Santos b1d0269211 ov-components: Add participant badges directive for enhanced participant panel functionality 2025-08-05 17:39:29 +02:00
Carlos Santos 00fcb0b115 ov-components: Revamp participant panel item for improved UI/UX and accessibility; add mute/unmute functionality and translations 2025-08-05 16:35:20 +02:00
Carlos Santos 4bf351b2df ov-components: Add layout additional elements directive for customizable UI extensions 2025-07-31 13:50:47 +02:00
Carlos Santos e9ecceeb77 ov-components: Add participant panel directive for enhanced user experience 2025-07-30 19:37:40 +02:00
Carlos Santos 413dec3e0f ov-components: Close connection dialog on room disconnection event 2025-07-30 12:38:09 +02:00
Carlos Santos 414c26c31b ov-components: Enhance recording status messages with improved styling and structure for starting and stopping states 2025-07-30 12:26:19 +02:00
Carlos Santos fce026766b ov-components: Enhance recording activity component: update view recordings button visibility based on recording status and improve toolbar button text for active recording state 2025-07-29 19:10:35 +02:00
Carlos Santos fe3f90d266 ov-components: Implement error handling for recording start failures and enhance UI interactions 2025-07-29 18:19:16 +02:00
Carlos Santos 5f6b404576 ov-components: Enhance error handling UI in recording activity component with modern styling 2025-07-29 17:50:15 +02:00
Carlos Santos 9b8348bc04 ov-components: Enhance recording activity component with improved status handling and styling 2025-07-29 17:35:28 +02:00
Carlos Santos 1403d062e9 ov-components: Fixed wrong initialize value in show recordings directive 2025-07-29 15:50:31 +02:00
Carlos Santos 7bf0e0036c ov-components: optimize participant name subscription with filter and tap operators 2025-07-29 15:46:05 +02:00
Carlos Santos 76c957903f ov-components: Refactors config service to use RxJS Subjects
Updates the configuration service to use RxJS BehaviorSubjects and Observables for managing configuration values.

This change improves the reactivity and maintainability of the configuration system by providing a consistent and type-safe way to manage application settings.

Specifically, it introduces a helper method to create configuration items with BehaviorSubject and Observable, and uses distinctUntilChanged and shareReplay operators to optimize the observable streams.

ov-components: Refactor configuration management in OpenVidu components

- Updated directive methods to use centralized configuration updates for general, stream, and toolbar settings.
- Replaced individual setter methods with batch update methods for improved performance and maintainability.
- Introduced specific comparison methods for configuration objects to optimize change detection.
- Enhanced the structure of configuration interfaces for better clarity and organization.
- Removed redundant code and streamlined the configuration service for better readability.

ov-components: Enhance participant name handling in PreJoin and Videoconference components
2025-07-29 14:05:14 +02:00
Carlos Santos 68ea8001f1 ci: Update Selenium Chrome version to 138.0 and add internal directives tests workflow 2025-07-29 11:42:56 +02:00
Carlos Santos 5a249fc3e1 ov-components: Add internal directives tests and update related components for recording functionality 2025-07-29 11:38:46 +02:00
Carlos Santos 9bac0f6490 ov-components: Add directive to control visibility of recording list and update related services and components 2025-07-29 10:47:22 +02:00
Carlos Santos fa664c97f1 ov-components: Add view recordings button functionality and related directives 2025-07-29 10:26:25 +02:00
Carlos Santos 8e218ade3c ov-components: Add start/stop recording button directive and update related components 2025-07-28 18:51:20 +02:00
Carlos Santos 22af5c7df6 ov-components: Implement centralized template management for videoconference components 2025-07-22 19:24:07 +02:00
Carlos Santos 04b8b741e2 ov-components: Refactor videoconference component to use centralized state management 2025-07-22 18:17:57 +02:00
Carlos Santos 8af9f75a10 ov-components: Prevent prejoin from showing again after user initiates join process 2025-07-22 17:45:03 +02:00
Carlos Santos fabeaf1471 ov-components: Add ID to external view recording buttons for better accessibility 2025-07-22 17:05:33 +02:00
Carlos Santos 1ffd7ea9d6 ov-components: Fixed bug showing prejoin
- Rename joinSession method to join and update related calls for consistency
refactor
- Change isPrejoin method to showPrejoin in directive config service
2025-07-22 16:23:33 +02:00
cruizba 3af490522e openvidu-deployment: Change DefaultApp string literals to Meet in templates. 2025-07-22 14:08:25 +02:00
cruizba 0280b64084 openvidu-deployment: Azure HA - Replace Default App (OpenVidu Call) with OpenVidu Meet 2025-07-22 14:00:56 +02:00
cruizba 83aad06574 openvidu-deployment: Azure HA - Fix port priority rules. 2025-07-22 13:52:54 +02:00
cruizba 892c6efed2 openvidu-deployment: Azure Elastic - Fix port priority rules. 2025-07-21 21:21:04 +02:00
cruizba 5b888aafc0 openvidu-deployment: Build azure bicep of Elastic. 2025-07-21 21:02:53 +02:00
cruizba d918e8059a openvidu-deployment: Azure Elastic - Replace Default App (OpenVidu Call) with OpenVidu Meet 2025-07-21 21:00:23 +02:00
Carlos Santos 82ddca6b50 ov-components: Add IDs to action buttons in recording activity component for improved accessibility 2025-07-21 17:36:11 +02:00
Carlos Santos 6fb7d9583c ov-components: Enhance recording activity UI with additional status indicators and style adjustments 2025-07-21 17:03:22 +02:00
Carlos Santos 181c5f0789 ov-components: Improves recording activity UI
Refactors the recording activity component's template and styles
to use cards for displaying recording information.

Enhances the display of recording metadata, including duration,
size, and date, with appropriate icons.

Adds visual cues for active recordings and improves overall
responsiveness of the recording list.
2025-07-21 14:12:28 +02:00
Carlos Santos e486665efd ov-components: Updated recording activity component 2025-07-21 14:11:40 +02:00
Carlos Santos b659400c88 ov-components: implement read-only mode and customizable controls for recording activity 2025-07-21 14:11:40 +02:00
cruizba 98c7e3f751 openvidu-deployment: Build azure bicep of Single Node - PRO 2025-07-20 22:56:50 +02:00
cruizba 5e1df8b511 openvidu-deployment: Fix wrong environment variable in Azure Single Node - PRO 2025-07-20 22:42:23 +02:00
cruizba 16ec1f3920 openvidu-deployment: Replace Default App (OpenVidu Call) with OpenVidu Meet in Single Node Pro 2025-07-20 22:23:19 +02:00
cruizba b01e8f4d23 openvidu-deployment: update enabled modules to include openviduMeet in deployment script 2025-07-20 20:59:56 +02:00
cruizba 2413a0bb6d openvidu-deployment: Generate json from bicep Single Node - Community 2025-07-20 20:12:10 +02:00
cruizba bf6091e997 openvidu-deployment: Replace Default App (OpenVidu Call) with OpenVidu Meet in Single Node - Community 2025-07-20 20:10:25 +02:00
cruizba 9e0034dfac openvidu-deployment: Replace Default App (OpenVidu Call) with OpenVidu Meet 2025-07-18 21:53:32 +02:00
Carlos Santos 637142cec6 ov-components: add room initialization checks and error handling in SessionComponent 2025-07-17 17:03:18 +02:00
Carlos Santos c304c9c761 ov-components: add PreJoin directive to support custom pre-join templates in VideoconferenceComponent 2025-07-17 16:53:17 +02:00
Carlos Santos 4dd007395f ov-components: Refactor components to use takeUntil for unsubscribing from observables
- Replaced manual subscription management with takeUntil pattern
- Introduced a destroy$ Subject in each component to handle unsubscriptions on component destruction.
- Improved memory management and code readability by eliminating multiple subscription variables.
2025-07-17 15:44:37 +02:00
Carlos Santos 7573656060 ov-components: refactor VideoconferenceComponent to improve template setup and icon management 2025-07-17 14:00:51 +02:00
Carlos Santos 8407363aaf ov-components: add track subscription and manage room tracks published state 2025-07-17 13:29:17 +02:00
Carlos Santos 55fd64c254 ov-components: enhance recording functionality with track checks and UI updates 2025-07-17 13:29:17 +02:00
cruizba d151834048 openvidu-deployment: Replace OPENVIDU_CALL_SERVER_IMAGE with OPENVIDU_MEET_SERVER_IMAGE in deployment scripts 2025-07-16 12:36:15 +02:00
pabloFuente ce47224400 openvidu-testapp: make update interval for dialog optional 2025-07-14 22:18:12 +02:00
cruizba 61fbf9850b Add TCP port rules for WebRTC traffic on port 7881 and 50000-60000 across multiple deployment configurations 2025-07-11 21:33:05 +02:00
Piwccle ba1df4660c openvidu-testapp: RTCIceCandidate stats for publisher and subscriber peer connections fixed in firefox 2025-07-10 16:12:08 +02:00
pabloFuente d44e24592d openvidu-testapp: RTCIceCandidate stats for publisher and subscriber peer connections 2025-07-09 18:24:00 +02:00
Carlos Santos 91aa127dad ov-components: replace and improve recordingElapsedTime logic 2025-07-04 17:51:30 +02:00
Carlos Santos 1be876678c ov-components: add subscription for virtual background effects management 2025-07-04 15:55:22 +02:00
Carlos Santos 388981be31 ov-components: reorder imports and add ShowDisconnectionDialogDirective to ApiDirectiveModule 2025-07-04 12:51:57 +02:00
Carlos Santos 6497751375 ov-component: add showDisconnectionDialog directive and update service for disconnection dialog management 2025-07-04 12:45:34 +02:00
pabloFuente 5f0639c157 openvidu-test-e2e: adapted egress test to tolerate mediasoup limitation 2025-07-03 14:23:10 +02:00
pabloFuente e435a1a937 openvidu-test-e2e: fix signalTest 2025-07-02 21:53:45 +02:00
pabloFuente 932eda8115 openvidu-test-e2e: include event RoomEvent.ParticipantActive 2025-07-02 18:35:44 +02:00
pabloFuente 5d91f4d343 openvidu-testapp: add RoomEvent.ParticipantActive 2025-07-02 18:14:45 +02:00
pabloFuente 703698b25f openvidu-components-angular: fix comment links 2025-07-02 18:14:07 +02:00
pabloFuente b884e924b6 openvidu-test-e2e: added Egress tests 2025-06-30 14:34:37 +02:00
pabloFuente ee8847c5cb openvidu-testapp: add checkbox to force relay from browser 2025-06-30 12:11:09 +02:00
GitHub Actions 99c787c4f5 Revert "Bump version to 3.3.0"
This reverts commit 2083c078fd.
2025-06-26 20:02:58 +00:00
GitHub Actions 2083c078fd Bump version to 3.3.0 2025-06-26 20:02:55 +00:00
github-actions bc42a72836 openvidu-components-angular: Bumped version to 3.3.0 2025-06-26 18:43:30 +00:00
cruizba c3b7c6f4bb openvidu-deployment: Shutdown gracefully agents in aws and azure. 2025-06-25 19:03:10 +02:00
pabloFuente b913dbb3b8 openvidu-testapp: updated dependencies 2025-06-25 13:26:42 +02:00
pabloFuente f16eefa9df openvidu-testapp: differ between final and non-final transcription events 2025-06-25 13:26:28 +02:00
pabloFuente 96132553ae openvidu-testapp: add lk.transcription handler 2025-06-24 17:26:38 +02:00
Carlos Santos 709779b7fd ov-components: Remove debugger statement from setRecordingStarted method in RecordingService 2025-06-24 16:18:18 +02:00
cruizba 11ac3d32eb openvidu-deployment: Fix wrong image 2025-06-24 11:34:54 +02:00
cruizba c8bbcfed56 openvidu-deployment: Add OPENVIDU_AGENT_SPEECH_PROCESSING_IMAGE environment variable to installation scripts 2025-06-24 11:33:21 +02:00
Piwccle 76a6f6c301 openvidu-deployment: azure - Add support for additional install flags in all the deployments 2025-06-18 16:26:50 +02:00
Carlos Santos 2de2920acf ov-components: Add check for existing Material Icons link in VideoconferenceComponent 2025-06-18 10:12:39 +02:00
cruizba fd8be9f23f openvidu-deployment: - AWS HA - Add experimental TURN TLS 2025-06-14 02:02:29 +02:00
cruizba e66e5a23e1 openvidu-deployment: - HA - Open port 5349 in media nodes for master nodes if Turn Domain is not configured 2025-06-13 22:02:13 +02:00
cruizba 335fd8e3c3 Remove unnecessary condition for SecurityGroupIngress in CloudFormation templates 2025-06-13 21:37:39 +02:00
cruizba 237ebe1d59 openvidu-deployment: Add "Additional Installer Flag" 2025-06-13 19:20:25 +02:00
cruizba 5e5e404d7d openvidu-deployment: Remove non official Cloudformation 2025-06-13 19:20:25 +02:00
Carlos Santos 16e869c5da ov-components: update API directive documentation 2025-06-11 12:50:08 +02:00
Carlos Santos c628b2ab68 ov-components: enhance directive table generation logic and improve error handling 2025-06-11 12:49:41 +02:00
Carlos Santos b7d9f822de ov-components: apply background from storage based on background effects button status 2025-06-10 17:28:00 +02:00
Piwccle fe606cbf15 openvidu-deployment: azure - added cluster data container for caddy and observability info 2025-06-10 16:30:41 +02:00
Piwccle 7c71f41d95 openvidu-deployment: azure - removed one '}' that was making TURN HA fail 2025-06-10 12:48:12 +02:00
Piwccle 9728d966ad openvidu-deployment: azure - bugfix: added dependsOn to remove possible race condition 2025-06-10 12:17:10 +02:00
Piwccle ce610cab03 openvidu-deployment: azure - removed parameter from HA CUID 2025-06-10 11:46:42 +02:00
Piwccle d8f14c6905 openvidu-deployment: azure - changes to let TURN work in HA deployment 2025-06-10 11:34:57 +02:00
Piwccle bdf4f07a28 openvidu-deployment: azure - removed one line of the install script that was there to try the new delete_media_node 2025-06-10 11:08:55 +02:00
Piwccle 206a51baf7 openvidu-deployment: azure - added scripts to delete media node if fails 2025-06-10 10:56:50 +02:00
Carlos Santos 94b51b9971 ov-components: Update language files to replace "session" with "room" for consistency across translations 2025-06-09 11:21:35 +02:00
github-actions 8984cfdcad openvidu-components-angular: Bumped version to 3.2.1 2025-06-05 11:01:42 +00:00
Carlos Santos 022d18578e ov-components: Remove unnecessary mat-line directive from settings panel options 2025-06-05 12:49:53 +02:00
GitHub Actions 9beb8b435a Revert "Bump version to 3.2.0"
This reverts commit 26b790bfd8.
2025-06-04 15:01:06 +00:00
GitHub Actions 26b790bfd8 Bump version to 3.2.0 2025-06-04 15:01:02 +00:00
github-actions 308315dee4 openvidu-components-angular: Bumped version to 3.2.0 2025-06-04 14:50:58 +00:00
cruizba 5fac396487 Update submodule openvidu-livekit for 3.2.0 2025-06-04 16:34:41 +02:00
Piwccle 99e36178a5 openvidu-deployment: builded .bicep 2025-06-04 13:29:32 +02:00
Piwccle 8999076291 openvidu-deployment: azure - HA fixed turn 2025-06-04 13:29:32 +02:00
cruizba 220c6d7d4a openvidu-deployment: Bump Redis server image version to 7.4.4. 2025-06-02 20:41:24 +02:00
cruizba f989dc26d2 openvidu-deployment: Force minimum docker and docker-compose version in update script. 2025-06-02 17:54:33 +02:00
cruizba 3107d504ce openvidu-deployment: Force minimum docker and docker-compose version. 2025-06-02 17:47:46 +02:00
cruizba b8c0bb3283 openvidu-deployment: azure - update signal for stopping media node services to SIGQUIT 2025-06-02 17:00:49 +02:00
Piwccle 4aa8739ee8 openvidu-deployment: compiled all .bicep and fixed roles 2025-05-29 17:43:37 +02:00
Piwccle bcfcfdd704 openvidu-deployment: added roles in HA to configure blob storage 2025-05-29 17:40:53 +02:00
Piwccle a59872c9e9 openvidu-deployment: fixed common arguments for openvidu single node pro 2025-05-29 17:33:37 +02:00
Piwccle cbf4bafa36 openvidu-deployment: added more validation in CUID 2025-05-29 17:21:47 +02:00
Piwccle 95584d7437 openvidu-deployment: CUID for azure deployments modified to work better 2025-05-29 17:17:59 +02:00
cruizba 20e348ed60 Revert redis to 7.4.3-alpine 2025-05-29 17:11:18 +02:00
Piwccle fbcc460f10 openvidu-deployment: fixed ha config_blobstorage in all master nodes 2025-05-29 16:51:59 +02:00
Piwccle e7e1a39e05 openvidu-deployment: added Openvidu Single Node PRO and added v2compatibility modules in HA and Elastic deployments 2025-05-29 15:58:41 +02:00
pabloFuente 18fd65350c openvidu-integration-tests: update start-openvidu-local-deployment action 2025-05-29 14:43:41 +02:00
cruizba 1551bf8244 openvidu-deployment: Bump docker image versions for 3.2.0 2025-05-28 23:39:14 +02:00
pabloFuente 882f9405db openvidu-test-e2e: put RTSP container in network mode host 2025-05-28 12:51:37 +02:00
Piwccle af3608c81c openvidu-deployment: azure - added optional automationAccountName 2025-05-27 11:18:05 +02:00
Carlos Santos 395d0c3ade ov-components: fix name getter in ParticipantModel to return participant's name 2025-05-26 19:09:19 +02:00
Carlos Santos f0bcb3ac03 ov-components:remove unnecessary screenshot logging in API directive tests 2025-05-26 18:38:25 +02:00
Carlos Santos 3579b9dc6a ov-components: update URL handling in API directive tests and improve error logging in leaveRoom method 2025-05-26 18:31:51 +02:00
Carlos Santos dbb45eaffd ov-components: improve leaveRoom method by adding fallback navigation on error 2025-05-26 18:24:14 +02:00
Carlos Santos fc7d7fef5a ov-components: replace fit with it for consistency in API directive tests 2025-05-26 17:52:55 +02:00
Carlos Santos 3f234276ee ov-components: enhance leaveRoom method with error handling and verification steps 2025-05-26 17:46:54 +02:00
GitHub Actions b10bc5f8c6 Revert "Bump version to 3.2.0-dev1"
This reverts commit a9e727fd51.
2025-05-26 14:34:37 +00:00
GitHub Actions a9e727fd51 Bump version to 3.2.0-dev1 2025-05-26 14:34:34 +00:00
cruizba c9b350eddc openvidu-deployment: Add single node PRO. Update,sh changes 2025-05-26 13:39:18 +02:00
Carlos Santos 21c297c73b ov-components: ensure room is left after each test in attribute and event directives 2025-05-26 10:33:17 +02:00
Carlos Santos 7df7b31d97 ov-components: add screenshot logging and delay in API directives tests 2025-05-23 18:13:35 +02:00
Carlos Santos d8d4870eb7 ov-components: add screenshot logging for video presence check 2025-05-23 17:18:02 +02:00
Carlos Santos aa15e8b713 ov-components: add delay before checking session presence in audio disabled test 2025-05-23 15:48:15 +02:00
Carlos Santos 621df40a7f ci: update cleanup action references in workflows to use official OpenVidu action 2025-05-23 15:37:13 +02:00
Carlos Santos 147f7078a0 ci: update action for building and serving openvidu-components-angular Testapp to use the latest version 2025-05-23 15:19:48 +02:00
Carlos Santos 55a67f191a ci: replace custom action for building and serving openvidu-components-angular Testapp with official OpenVidu action 2025-05-23 15:17:50 +02:00
Piwccle 31587d7181 openvidu-deployment: azure - added 2 'none' values missing in HA .bicep 2025-05-23 14:32:27 +02:00
Carlos Santos d0f714a5e2 ci: replace setup action for OpenVidu Call Backend with official action 2025-05-23 13:58:20 +02:00
Carlos Santos e4ed447009 ci: remove setup action for OpenVidu local deployment 2025-05-23 13:58:20 +02:00
Piwccle 57ea002953 openvidu-deployment: azure - compiled all new .bicep 2025-05-23 13:53:24 +02:00
Carlos Santos 1b5a230132 ci: uses OpenVidu local deployment action in integration tests 2025-05-23 13:29:06 +02:00
Carlos Santos 7348402364 ci: update openvidu-local-deployment action to use the official OpenVidu action 2025-05-23 13:27:22 +02:00
Piwccle 915a4598b1 openvidu-deployment: azure - CUID for elastic and ha deployments and adjustments to those .bicep to support the new CUID 2025-05-23 13:19:59 +02:00
Carlos Santos 0b017b2abb ci: update workflow to use OpenVidu action for local deployment setup 2025-05-23 12:55:36 +02:00
Carlos Santos c12dbf247c ov-components: refactor participant left and room disconnected handling to use a common disconnect method 2025-05-23 12:02:29 +02:00
pabloFuente f087c172fc openvidu-testapp: remove bold font weight from buttons 2025-05-23 11:49:25 +02:00
pabloFuente fe3e99e4e1 Find out RTSP container IP instead of using host.docker.internal 2025-05-23 11:49:25 +02:00
Carlos Santos 442b99771a ov-components: enhance error handling and add screenshot capture during room leave process 2025-05-23 11:41:56 +02:00
Carlos Santos 5f9fb06c2a ov-components: add error logging and screenshot capture on room leave failure 2025-05-23 11:35:21 +02:00
Carlos Santos dfae82d4cf ov-components: add event handling for participant left and update config state 2025-05-23 11:24:45 +02:00
Carlos Santos 549a822491 ov-components: rename redirectOnLeaves to redirectToHomeOnLeaves for clarity and update related logic 2025-05-23 11:09:07 +02:00
Carlos Santos b6f242107e ov-components: adjust max-width and padding for prejoin card styling 2025-05-22 13:29:16 +02:00
Carlos Santos 9023f03cee ov-components: center progress spinner in loading container 2025-05-22 13:26:11 +02:00
Carlos Santos 500b19d1b9 Revert "ov-components: change runner for API Directives and Events E2E tests to ov-actions-runner"
This reverts commit c275381cfd.
2025-05-22 13:06:59 +02:00
Carlos Santos 421c5ea29c ov-components: add participant left event handling and navigation in testapp 2025-05-22 12:47:13 +02:00
Carlos Santos cea8ce0def ov-components: update test descriptions for structural directives to improve clarity and consistency 2025-05-22 12:46:45 +02:00
Carlos Santos daf1349e75 ov-components: fixed panel toggle logic for external panels. Fixes #854
refactor togglePanel method for improved state management and clarity
2025-05-22 12:44:06 +02:00
Carlos Santos ffbc1f5e5d ov-components: update toolbar additional panel button count in tests and template 2025-05-22 12:42:26 +02:00
pabloFuente a02e23763a openvidu-testapp: updated all dependencies 2025-05-22 12:11:38 +02:00
Carlos Santos c275381cfd ov-components: change runner for API Directives and Events E2E tests to ov-actions-runner 2025-05-22 10:54:56 +02:00
Carlos Santos 34eb27dfe3 ov-components: update onRoomDisconnected event to reference onParticipantLeft for deprecation notice 2025-05-21 16:02:52 +02:00
Carlos Santos d3702ab6ac ov-components: update audio path for CI mode in Selenium configuration 2025-05-21 14:23:05 +02:00
Carlos Santos dafba8cdee ov-components: update audio capture path to use dynamic variable based on launch mode 2025-05-21 13:46:12 +02:00
Carlos Santos 995ce8c00b ov-components: add autoplay policy and file access arguments for CI chrome configuration 2025-05-21 13:45:04 +02:00
Carlos Santos 1e15fea5e4 ov-components: enable Toolbar E2E tests by removing conditional check 2025-05-21 13:33:12 +02:00
Carlos Santos 3139437bfe ov-components: remove OpenviduWebComponent and related files 2025-05-21 13:31:26 +02:00
Carlos Santos 59112b79fa ov-components: add initial E2E tests for captions functionality 2025-05-21 13:31:18 +02:00
Carlos Santos fd486270b9 ov-components: update audio capture file in E2E tests and remove unnecessary entry from .gitignore 2025-05-21 13:24:08 +02:00
Carlos Santos 042f9b5a89 ov-components: add E2E tests for toolbar 2025-05-21 13:22:29 +02:00
Carlos Santos 28e96647fa ci: Added stream tests 2025-05-21 13:15:30 +02:00
Carlos Santos 35eacb45a1 ov-components: update E2E tests for stream functionality and adjust audio capture file 2025-05-21 13:13:56 +02:00
Carlos Santos c42108bd22 ov-components: enable Panels E2E test by removing conditional check 2025-05-21 12:45:48 +02:00
Carlos Santos 63588148a7 ci: update screen sharing test command 2025-05-21 12:43:13 +02:00
Carlos Santos 52d253a1db ov-components: add E2E tests for screensharing features 2025-05-21 12:41:34 +02:00
Carlos Santos bf7d83134a ov-components: add panels E2E test suite 2025-05-21 12:11:01 +02:00
Carlos Santos 77bba7e587 ov-components: uncomment screen track replacement test 2025-05-21 12:06:36 +02:00
Carlos Santos fb999df526 ov-components: comment out headless mode in chrome arguments for local testing 2025-05-21 11:55:44 +02:00
Carlos Santos 314a3d1898 ov-components: change fit to it for video disabled prejoin page test 2025-05-21 11:55:31 +02:00
Carlos Santos 35f1085870 ov-components: remove obsolete events E2E tests 2025-05-21 11:55:10 +02:00
Carlos Santos cfdaabfc0b ov-components: remove unit tests and update media devices E2E tests 2025-05-21 11:54:36 +02:00
Carlos Santos 146aeed893 ov-components: assign participant name to observable on ready to join 2025-05-21 11:07:57 +02:00
Carlos Santos 02b594e405 ov-components: Added events e2e tests 2025-05-20 18:55:03 +02:00
Piwccle bd3504b818 openvidu-deployment: azure - fixed one thing left to be able to try the new .bicep 2025-05-20 18:49:39 +02:00
Piwccle 743b05911c openvidu-deployment: azure - fixing .bicep file to work with the new CUID 2025-05-20 18:46:31 +02:00
Piwccle 1b2af77196 openvidu-deployment: azure - CUID for Single Node Deployment ready 2025-05-20 18:29:22 +02:00
Carlos Santos 1f13b11f88 ov-components: enable Chat E2E tests in workflow 2025-05-20 17:38:44 +02:00
Carlos Santos 1cba6761bb ov-components: update E2E test names and add chat feature tests 2025-05-20 17:38:06 +02:00
Carlos Santos 920bba1bf3 ov-components: update test to focus on VIDEO DISABLED scenario in prejoin page 2025-05-20 17:33:09 +02:00
Piwccle 1d3cba9517 openvidu-deployment: azure - fixing SSH key parameter in Single Node CUID 2025-05-20 17:15:47 +02:00
Carlos Santos 331a102a19 ov-components: add end-to-end tests for structural directives in OpenVidu Components 2025-05-20 16:56:19 +02:00
Carlos Santos 6fe461dc0c ov-components: refactor workflow jobs for clarity and organization 2025-05-20 16:51:23 +02:00
Carlos Santos baf3da51c2 ov-components: add end-to-end tests for attribute and structural directives 2025-05-20 16:51:23 +02:00
Carlos Santos 48084544ba ov-components: remove debug console logs for query parameters and participant name in testapp 2025-05-20 16:51:23 +02:00
Piwccle 6a02f96af2 openvidu-deployment: azure - added UI definition to Single Node 2025-05-20 13:58:37 +02:00
Carlos Santos 3c72bc6c4d ov-components: update disconnect logic to handle client-initiated events in OpenViduService 2025-05-20 13:17:00 +02:00
Carlos Santos 3027ab6c5b ov-components: update parameter name for audio detection display in testapp 2025-05-20 12:51:19 +02:00
Carlos Santos ffc6bb4c41 ov-components: streamline E2E test setup by removing redundant steps and renaming jobs 2025-05-20 12:44:02 +02:00
Carlos Santos 5df42d5344 ov-components: refactor participant name retrieval logic in VideoconferenceComponent 2025-05-20 12:40:22 +02:00
Carlos Santos 4ad8c52c38 ov-components: enhance testapp with dynamic configuration options 2025-05-20 12:40:04 +02:00
Carlos Santos 741954ac2b OV-COMPONENTS: upgrade chromedriver to 136.0.2 and selenium-webdriver to 4.32.0 2025-05-20 12:39:19 +02:00
Carlos Santos 75d6576603 ov-components: add end-to-end tests for API directives and implement leaveRoom utility function 2025-05-20 12:38:20 +02:00
Carlos Santos 8a9e9e0d27 ov-components: fixed race condition with onTokenRequested event and participantName 2025-05-19 19:10:40 +02:00
Carlos Santos 1065f32a3a ov-components: add action for building and serving openvidu-components-angular Testapp 2025-05-19 18:08:49 +02:00
Carlos Santos 223c7473e5 ov-components: add setup action for OpenVidu Call Backend in workflows 2025-05-19 18:03:26 +02:00
Carlos Santos f951eddfe5 ov-components: add cleanup action to workflows for better resource management 2025-05-19 17:55:09 +02:00
Carlos Santos 8dfb513bb0 ov-components: add setup action for local deployment and streamline workflow steps 2025-05-19 17:52:29 +02:00
Carlos Santos ab7a453507 ov-components: remove unused active button styles and clean up SCSS variables 2025-05-19 17:08:50 +02:00
Carlos Santos 5b112286d9 ov-components: add material symbols support and improve toolbar symbols handling 2025-05-19 16:59:24 +02:00
Carlos Santos c4c5fb9866 ov-components: improve logging for participant disconnection event 2025-05-14 17:50:49 +02:00
Carlos Santos bd711b57f3 ov-components: simplify disconnect logic by removing callback. The participantLeft event will be fired by LK event handler 2025-05-14 13:48:53 +02:00
Piwccle 94f2e3e573 openvidu-deployment: azure - changed one description of a param to clarify the use of the param 2025-05-13 14:36:12 +02:00
Piwccle b7a94f4b03 openvidu-deployment: azure - compiled .bicep into .json 2025-05-13 11:59:15 +02:00
Piwccle 21b94bc4fd Merge branch 'master' of https://github.com/OpenVidu/openvidu 2025-05-13 11:55:40 +02:00
Carlos Santos 745e04baa6 ov-components: enhance recording URL handling for backward compatibility 2025-05-13 11:35:04 +02:00
Carlos Santos 4b611893e9 ov-components: enhance recording URL handling for backward compatibility 2025-05-13 11:14:40 +02:00
Piwccle b8fc003a4c openvidu-deployment: azure - added existing storage account support in CE, Elastic and HA deployments 2025-05-13 11:01:22 +02:00
Carlos Santos ed579b4e72 ov-components: update recording stream URL to use '/media' segment instead of '/stream' 2025-05-12 16:00:42 +02:00
Carlos Santos d8ce736cc0 ov-components: improve disconnect handling and manage client-initiated disconnect events 2025-05-12 13:39:45 +02:00
Carlos Santos cb8f11449f ov-components: add UNKNOWN_DISCONNECT message for various languages and handle participant disconnection reason 2025-05-06 15:43:56 +02:00
Piwccle efb691f481 openvidu-deployment: added config script of azure blob storage in elastic deployment and added a rol assignment in CE needed 2025-05-06 09:03:55 +02:00
Carlos Santos a8c2459d5f ov-components: implement OnPush change detection strategy and optimize change detection calls in prejoin component 2025-05-05 14:35:51 +02:00
Carlos Santos 0074b28d3a ov-components: Fix available langs for allowing custom ones 2025-05-05 13:52:10 +02:00
Carlos Santos 760cf604eb ov-components: update package dependencies and TypeScript configurations
- Added @types/dom-mediacapture-transform to package.json for type definitions.
- Changed hoveringTimeout type in StreamComponent to ReturnType<typeof setTimeout> for better type safety.
- Updated TypeScript lib version from ES2020 to ES2021 in tsconfig files for improved features.
- Included dom-mediacapture-transform in types for TypeScript configurations across various tsconfig files.
- Removed empty types array in tsconfig.app.json to ensure proper type checking.
- Adjusted skipLibCheck settings in tsconfig files to improve compatibility with Livekit track processors.
2025-05-05 12:15:16 +02:00
Carlos Santos 8ce155df6a ov-components: enhance recording status management and elapsed time calculation 2025-04-30 19:18:29 +02:00
Carlos Santos cff617b0c3 ov-components: disable webcomponent E2E tests 2025-04-30 18:12:36 +02:00
Carlos Santos 127fbbd4e1 ov-components: update build process and module definition for improved structure 2025-04-30 17:58:11 +02:00
Carlos Santos 3adfa91c54 fix(tsconfig): add skipLibCheck comment for Livekit track processors 2025-04-30 15:22:18 +02:00
Carlos Santos 7f00318cbb ov-components: update dependencies and TypeScript configuration
- Updated @livekit/track-processors from 0.3.2 to ^0.5.6
- Updated livekit-client from 2.5.2 to 2.11.4
- Changed TypeScript lib option from "dom" to "DOM" in tsconfig.lib.json
- Added skipLibCheck option in tsconfig.lib.json and tsconfig.base.json
- Updated TypeScript lib option from "dom" to "dom" in tsconfig.base.json
2025-04-30 14:57:47 +02:00
Carlos Santos 5433f516a9 ov-components: remove redundant background processor removal logic in virtual background service 2025-04-30 14:54:26 +02:00
Carlos Santos 81fcae2d4e ov-components: enhance virtual background service updating the switching logic for improving performance 2025-04-30 14:38:43 +02:00
Carlos Santos 0c1e1a7134 ov-components: Updated to Angular 19 2025-04-30 14:37:10 +02:00
Carlos Santos 518e1511d2 ov-components: add participant left handling and update room disconnection logic 2025-04-30 12:13:03 +02:00
Carlos Santos b92630ecd8 ov-components: enhance participant disconnection handling with reasons and refactor disconnect logic 2025-04-30 12:13:03 +02:00
Carlos Santos 11137a2a8f ov-components: add disconnect and error messages for various scenarios in multiple language files 2025-04-30 12:13:03 +02:00
Piwccle 9310ff3ea1 openvidu-deployment: azure - added blob storage resources and modified CE to support config blobStorage 2025-04-23 13:26:33 +02:00
Piwccle 37428c91b6 openvidu-deployment: azure - changed links that references de runbook 2025-04-22 12:06:04 +02:00
cruizba 9e04d59b61 Add openvidu-deployment scripts 2025-04-22 11:46:24 +02:00
Micael Gallego c1f2971881
Fix NewGenVidu project logo in README.md 2025-03-24 19:49:32 +01:00
Carlos Santos 8c28228357 ov-components: update default recording stream URL in directives and config service 2025-03-23 14:25:35 +01:00
Carlos Santos 90fd0ef44e ov-components: add recordingStreamBaseUrl directive and integrate with config service for dynamic stream URL construction 2025-03-14 19:08:34 +01:00
Carlos Santos 6137bdbbbc ov-components: add null check for participant name updates in pre-join component and participant name directive 2025-03-12 19:03:14 +01:00
Carlos Santos 2acf636842 ov-components: reduce max-width of pre-join component to improve layout 2025-03-12 18:41:42 +01:00
Carlos Santos 0ad51d6c58 ov-components: remove max-height constraint from pre-join component styles 2025-03-12 18:28:53 +01:00
Carlos Santos 6f97b9d8c2 ov-components: move loading state update to ngAfterContentChecked lifecycle hook 2025-03-12 18:28:47 +01:00
Carlos Santos a64cf1d577 ov-components: enhance pre-join component layout and add participant name visibility check 2025-03-12 18:05:37 +01:00
Carlos Santos e373d23cc9 ov-components: add participant name visibility control to pre-join component 2025-03-12 17:19:55 +01:00
Carlos Santos 72888e4ea9 ov-components: add camera and microphone button visibility controls in the E2E tests 2025-03-10 10:27:44 +01:00
Carlos Santos 2bf212ccfe ov-components: add camera and microphone button visibility controls in toolbar and settings panel 2025-03-10 10:11:38 +01:00
Carlos Santos 5fba250b1d ov-components: remove ServiceConfigService and update components to use LayoutService directly 2025-03-08 17:42:43 +01:00
Carlos Santos 7315360fbc ov-components: add onParticipantLeft event and disconnect callback to notify when local participant leaves 2025-03-07 19:25:27 +01:00
Carlos Santos d965e72822 ov-components: add OpenViduComponentsUiModule with component and pipe declarations 2025-03-07 18:52:12 +01:00
Carlos Santos 288a585809 ov-components: rename participantId to participantName in ParticipantLeftEvent and update disconnect method 2025-03-07 13:52:43 +01:00
Carlos Santos 272eb9002c ov-components: rename participant created event to participant connected and update documentation 2025-03-07 13:42:50 +01:00
Carlos Santos dee470609c ov-components: clarify event documentation for local participant actions 2025-03-07 13:36:10 +01:00
Carlos Santos ad8f368a91 ov-components: enhance documentation for room and participant events 2025-03-07 13:30:26 +01:00
Carlos Santos 5d855a1338 ov-components: log room name when a room is created 2025-03-07 13:04:22 +01:00
Carlos Santos 3defad20cc ov-components: emit room created event after participant connection 2025-03-07 13:04:06 +01:00
Carlos Santos 9b4f330c4a ov-components: clarify method documentation for local participant connection 2025-03-06 13:45:02 +01:00
pabloFuente 7d10ec6097 openvidu-testapp: fix webpack builder with custom configuration 2025-02-19 12:38:57 +01:00
pabloFuente 8d443ac506 openvidu-testapp: update rest of dependencies 2025-02-19 12:13:37 +01:00
pabloFuente 1521a766e5 openvidu-testapp: update minor upgrade change 2025-02-19 12:09:43 +01:00
pabloFuente a1edded5df openvidu-testapp: update to Angular Material 19 2025-02-19 12:04:21 +01:00
pabloFuente fa507d492f openvidu-testapp: update to Angular 19 2025-02-19 12:03:43 +01:00
pabloFuente 0ae67e8a87 openvidu-testapp: update Angular Material 18 2025-02-19 12:01:54 +01:00
pabloFuente 051b14c5b2 openvidu-testapp: update to Angular 18 2025-02-19 11:48:56 +01:00
pabloFuente f610f37ac3 openvidu-testapp: update pollyfils.ts 2025-02-19 11:47:11 +01:00
pabloFuente 501881c602 openvidu-testapp: update to Angular Material 17 2025-02-19 11:42:14 +01:00
pabloFuente ea8f0e2101 Upgrade to Angular 17 2025-02-19 11:41:31 +01:00
github-actions d3520e9098 openvidu-components-angular: Bumped version to 3.1.0 2025-02-17 14:42:29 +00:00
cruizba b29b9102a9 update openvidu-livekit subproject to latest commit 2025-02-17 15:23:37 +01:00
Carlos Santos 64fbaa2a42 ov-components: Fixed nested event test 2025-02-13 12:43:22 +01:00
Carlos Santos 4c4380a87f ov-components e2e: Added participantLeft listener 2025-02-13 12:33:04 +01:00
Carlos Santos d8452c42ad ov-components: Fired room and participantLeft events 2025-02-13 12:31:50 +01:00
cruizba 09ba39642e openvidu-test-e2e: Disable flaky audio ingress tests for RTSP 2025-02-11 13:35:20 +01:00
pabloFuente 11c6f2f965 openvidu-testapp: remove unused imports 2025-02-11 12:47:02 +01:00
cruizba 9a01c0e179 openvidu-test-e2e: Fix file URL selection logic for lossless video encoding 2025-02-11 12:33:44 +01:00
cruizba 06fc88f2b0 openvidu-test-e2e: Add lossless flag to getFileUrl method 2025-02-11 12:30:56 +01:00
cruizba f19a9129ba openvidu-test-e2e: Improve CPU performance by using losless codec ffv1 and flac. Fix some tests also because of mp3 used as source instead of flac. 2025-02-11 02:28:22 +01:00
Carlos Santos f785dfd7ad ov-components: subscribe to vc directives in constructor 2025-02-07 17:21:09 +01:00
pabloFuente 8185324a0a openvidu-test-e2e: disable audio only opus RTSP test 2025-02-07 11:14:06 +01:00
pabloFuente 88214676a8 openvidu-test-e2e: add -crf flags to video encodings with ffmpeg 2025-02-06 23:16:36 +01:00
pabloFuente 9f9eda5ce4 openvidu-test-e2e: improve waiters resilience 2025-02-06 20:49:42 +01:00
pabloFuente 75bb35d00a openvidu-test-e2e: fix opus codec with "-ac 2" flag 2025-02-06 20:43:47 +01:00
pabloFuente d4006421e9 openvidu-test-e2e: add AAC audio codec RTSP tests 2025-02-06 20:27:32 +01:00
pabloFuente 0fe47c4b13 openvidu-test-e2e: test RTSP with all codecs 2025-02-06 20:07:17 +01:00
pabloFuente 5c97301c6d openvidu-testapp: allow configuring URI for Ingress URL types 2025-02-06 18:59:29 +01:00
pabloFuente de5f67dd3c openvidu-test-e2e: parameterized rtsp/srt port 2025-02-03 19:17:50 +01:00
pabloFuente da108a4031 openvidu-test-e2e: update rtstp server container to network host 2025-02-03 19:10:44 +01:00
pabloFuente 5fb3be503c openvidu-test-e2e: add RTSP and SRT tests 2025-02-03 15:36:37 +01:00
pabloFuente 6ea42e82c0 openvidu-testapp: add ingress URL type selector 2025-02-03 15:35:52 +01:00
Carlos Santos 6e1d9c7ee7 ov-components: Updated logo directive 2025-01-28 21:37:53 +01:00
Carlos Santos 4cdb761737 ov-components: Allowed Angular 19 2025-01-27 17:09:13 +01:00
Carlos Santos 51e3abd483 ov-components: Updated npm script 2025-01-27 17:04:11 +01:00
pabloFuente 77f23512a6 openvidu-test-e2e: add comment 2025-01-20 14:48:09 +01:00
pabloFuente f2cb2fb252 openvidu-test-e2e: add tags @OnlyPion and @OnlyMediasoup. Fix customIngress test 2025-01-20 14:46:16 +01:00
pabloFuente d3b8214dcb openvidu-test-e2e: add signal test 2025-01-17 11:45:24 +01:00
pabloFuente 98a4b1b2cb openvidu-test-e2e: fix array outbound exception in customIngressTest 2025-01-15 18:31:39 +01:00
github-actions 9114f4fcc6 openvidu-components-angular: Bumped version to 3.0.1-beta1 2025-01-09 12:26:03 +00:00
Carlos Santos 2ea61d05a1 ov-components: Fixed message badge color 2025-01-07 09:17:20 +01:00
pabloFuente c82d7315ef Update all Java project dependencies 2025-01-03 13:52:33 +01:00
pabloFuente 6f3475fd94 openvidu-test-e2e: fix version.surefire.plugin pom.xml version 2025-01-03 13:17:43 +01:00
pabloFuente 7de722714e Update pom.xml to remove openvidu-parent dependency 2025-01-03 13:15:11 +01:00
pabloFuente d2a59f1193 openvidu-test-e2e: disable required tests 2025-01-03 12:38:02 +01:00
pabloFuente 36df6348d6 Update pom.xml to update openvidu-java-client versions 2025-01-02 20:43:56 +01:00
pabloFuente fa0392a0a3 openvidu-test-e2e: fix testcontainer usage 2025-01-02 18:10:45 +01:00
juancarmore b829142caf openvidu-components-angular: Update links in documentation to use the latest version 2024-12-18 15:32:17 +01:00
juancarmore dead4853aa integration-tests: Refactor utility code. Exec MongoDB disconnect method after running all tests 2024-12-12 15:42:09 +01:00
cruizba d320cb2bdd ci: Remove OpenVidu V2 Test from master 2024-12-10 12:54:51 +01:00
juancarmore 07ba255beb Update integration tests workflow to use "development" branch when checking out openvidu-local-deployment and install LiveKit CLI 2024-12-09 19:04:05 +01:00
cruizba 5e4cbadff1 ci: Rename v2 test job 2024-12-09 14:06:20 +01:00
cruizba ee876987c1 Update workflow name for OpenVidu CE V2 tests 2024-12-09 13:58:53 +01:00
cruizba 0fd0b6c11c Fixing v2 e2e tests 2024-12-09 13:58:17 +01:00
pabloFuente 01d01f3d00 openvidu-test-e2e: add simple Firefox subscriber tests 2024-12-09 12:32:36 +01:00
juancarmore 363d352c79 Increase timeout for active entities fixer integration test 2024-12-06 14:08:07 +01:00
juancarmore 40f6bd790d Add GitHub Actions workflow for OpenVidu integration tests 2024-12-06 13:54:45 +01:00
juancarmore a01b1e2a2f integration-tests: Add test for openvidu active entities fixer 2024-12-05 21:04:41 +01:00
juancarmore c2d7f01310 Add integration tests setup with Jest 2024-12-05 21:02:04 +01:00
pabloFuente 5e0af77913 openvidu-test-e2e: fix race condition on client event management 2024-12-05 19:21:21 +01:00
pabloFuente 40ffd97aca openvidu-testpp: fixed versions of livekit dependencies to avoid typescript error TS1046 2024-12-05 12:13:10 +01:00
pabloFuente 09132835fe openvidu-testapp: update package-lock.json 2024-12-05 11:36:23 +01:00
pabloFuente 8c18243c88 openvidu-testapp: print string reasons 2024-12-05 11:35:16 +01:00
pabloFuente 01530fef32 openvidu-test-browsers: fix h264 codec in Selenium-managed Firefox 2024-12-04 17:21:11 +01:00
pabloFuente d1cca5efac openvidu-test-e2e: added force codec and ingress tests 2024-12-02 17:26:21 +01:00
github-actions d7f1d859a0 openvidu-components-angular: Bumped version to 3.0.0 2024-11-29 11:27:08 +00:00
cruizba 235d586d0f Update submodule openvidu for 3.0.0 2024-11-29 11:53:28 +01:00
pabloFuente 57be57d892 openvidu-testapp: fix codec selector 2024-11-27 16:47:50 +01:00
pabloFuente a4b010faba Add enableTranscoding ingress option for WHIP 2024-11-21 16:11:33 +01:00
pabloFuente 18343fa47d openvidu-testapp: add CreateIngressOptions to dialog 2024-11-21 14:54:49 +01:00
pabloFuente 738431b6bf openvidu-testapp: fix error messages 2024-11-18 17:08:47 +01:00
pabloFuente d156db6051 openvidu-testapp: refactor room service to use Node SDKs 2024-11-18 13:59:56 +01:00
Carlos Santos e1634efe39 ov-components: Refactored lang selector template 2024-11-06 12:34:04 +01:00
Carlos Santos ee79e39d4b ov-components: Updated css 2024-11-06 12:33:26 +01:00
Carlos Santos 47ccaf58c7 ov-components: Fixed recording activity panel styles 2024-11-06 11:43:34 +01:00
pabloFuente 0a3129e818 openvidu-testapp: add auto refresh of video track information 2024-11-06 11:32:19 +01:00
pabloFuente baa753ac21 openvidu-test-e2e: add simulcastDisabledDynacastEnabled test 2024-11-06 11:31:46 +01:00
Carlos Santos 6a7ab9b026 ov-components: Updated audio-wave css 2024-11-06 10:49:29 +01:00
Carlos Santos b59c56c3e8 ov-components: Updated css variable names 2024-11-06 10:06:51 +01:00
Carlos Santos 02e3fb0d4e ov-components: Updated css styles for improving custom color support 2024-11-05 17:48:10 +01:00
Carlos Santos ada5984a32 ov-components: Updated css color variables 2024-11-05 12:38:52 +01:00
pabloFuente 1d91d6fa9d openvidu-testapp: make deviceId a proper selector in VideoCaptureOptions 2024-10-28 19:12:16 +01:00
pabloFuente ed2fa45d59 openvidu-testapp: show codec in video track info window 2024-10-28 14:30:22 +01:00
pabloFuente 57d92873da openvidu-testapp: toggle video zoom 2024-10-24 11:37:04 +02:00
Carlos Santos 897404b54b ov-components: Removed unnecessary code
Removed getTokenMetadata method
2024-10-23 16:57:48 +02:00
pabloFuente c53b1b5b81 openvidu-test-e2e: improve test adaptiveStreamDisabledDynacastDisabledTest 2024-10-22 15:25:41 +02:00
pabloFuente 083adb1775 openvidu-testapp: update livekit dependencies 2024-10-22 12:13:11 +02:00
pabloFuente b74f340042 openvidu-test-browsers: move srcObject wait to JS script 2024-10-21 22:03:36 +02:00
pabloFuente 23ac1c3b00 Update to Java 17 2024-10-21 22:03:04 +02:00
pabloFuente cbc8a6b859 openvidu-tesr-browsers: wait for srcObject to be defined when checking media elements 2024-10-21 20:33:43 +02:00
pabloFuente 9f4bcc827e openvidu-test-e2e: reduce tests flakyness with active waits 2024-10-21 13:33:34 +02:00
pabloFuente 333436a90e Fix path in npm script 2024-10-15 15:56:47 +02:00
github-actions 2132d5b984 openvidu-components-angular: Bumped version to 3.0.0-beta3 2024-10-15 13:21:16 +00:00
cruizba 7ff73ac225 Update submodule openvidu for 3.0.0-beta3 2024-10-15 15:03:54 +02:00
Carlos Santos ee57064095 ci: Renamed openvidu components test file 2024-10-11 10:21:00 +02:00
Carlos Santos 28f2405cc6 ov-components: Enhanced tests timeout 2024-10-10 21:14:08 +02:00
Carlos Santos 7f2660abd9 ov-components: Removed unnecessary files 2024-10-10 21:07:23 +02:00
Carlos Santos 5a0aea9ecb ov-components: Used jasmine instead of mocha 2024-10-10 21:01:01 +02:00
Carlos Santos 5a40ce9056 ov-components: Set headless chrome for unit tests 2024-10-10 20:39:16 +02:00
Carlos Santos f309527c26 ci: Run unit tests 2024-10-10 20:32:10 +02:00
Carlos Santos 52422fb58a ov-components: Added unit tests 2024-10-10 20:30:02 +02:00
Carlos Santos 9a7a2e3adb ov-components: Fixed blocking dialog when connection issues 2024-10-10 20:28:48 +02:00
pabloFuente c273ae0109 openvidu-test-e2e: update manyToMany test 2024-10-10 13:18:58 +02:00
pabloFuente f19432af8e openvidu-test-e2e: improve test's flakiness with more checks 2024-10-09 10:47:00 +02:00
pabloFuente 9238becfe3 openvidu-testapp: update livekit dependencies 2024-10-09 10:24:23 +02:00
pabloFuente 8e12739c97 openvidu-test-e2e: fix uri resolution from protocol 2024-10-07 16:43:16 +02:00
Carlos Santos d19f0f7bb5 ov-components: Allowed custom title on admin components 2024-10-07 12:01:08 +02:00
pabloFuente 6bb5937b23 openvidu-test-browsers: remove geckodriver location 2024-10-04 13:28:18 +02:00
pabloFuente 3d60920447 openvidu-test-e2e: add enabled/disabled, Server API Mute and simulcast disabled tests 2024-10-04 13:27:20 +02:00
pabloFuente 0ce0e2acde openvidu-testapp: refactor TrackComponent 2024-10-04 13:25:44 +02:00
pabloFuente 4354561d5a openvidu-test-e2e: add new tests (speaker detection, simulcast, dynacast, adaptive stream) 2024-10-02 01:17:02 +02:00
pabloFuente 88baf99368 openvidu-testapp: new features to test simulcast, dynacast and adaptive stream 2024-10-02 01:14:42 +02:00
pabloFuente fdc3b82122 openvidu-test-e2e: add speaker detection and adaptive stream tests 2024-10-01 14:16:01 +02:00
pabloFuente e307af24e3 openvidu-test-e2e: improved e2e tests 2024-09-30 15:19:33 +02:00
pabloFuente 71598c2d7d openvidu-test-e2e: enable tests (one-to-one audio and video only, massive room) 2024-09-28 12:13:51 +02:00
Carlos Santos 28a0574d99 ov-components: Refactored translate service 2024-09-26 10:26:59 +02:00
Carlos Santos 7336f4f609 ci: Fixed start backend command 2024-09-24 14:47:25 +02:00
Carlos Santos 3d164c6422 ci: Fixed openvidu call backend path 2024-09-24 13:26:19 +02:00
Carlos Santos 72c5aa97ad ov-components: Refactored storage service 2024-09-24 12:51:43 +02:00
Carlos Santos c3b2b4663f ov-components: Exported storage service 2024-09-24 11:30:51 +02:00
Carlos Santos f7d6e81261 ov-components: Fixed selenium-webdriver types 2024-09-23 10:32:45 +02:00
Carlos Santos 8d99c376f7 ov-components: Added node types into tsconfig 2024-09-23 10:21:19 +02:00
Carlos Santos d37f094773 ov-components: Updated dependencies 2024-09-23 10:10:57 +02:00
pabloFuente 5bacf37523 openvidu-testapp: better end scenario logic 2024-09-19 13:37:46 +02:00
pabloFuente 888f0bdb7e openvidu-testapp: complete participant options. Add VideoResolution 2024-09-17 14:46:21 +02:00
pabloFuente 7e7b6d2915 openvidu-testapp: test scenarios. Full LiveKit options dialog 2024-09-17 01:32:41 +02:00
Carlos Santos bd6ba55504 ov-components: Refactored and improved config 2024-09-04 12:19:13 +02:00
Carlos Santos 724fbd9186 ov-components: Refactored config service 2024-08-26 13:20:14 +02:00
Carlos Santos 95b8d055ed ov-components: Exported toolbar model 2024-08-22 11:35:38 +02:00
Carlos Santos 0a6dd252af ov-components: Exported config service to make it public 2024-08-22 11:20:01 +02:00
Carlos Santos a82b2189bc ov-componentes: Added ovToolbarAdditionalButtonsPosition directive 2024-08-22 11:17:02 +02:00
Carlos Santos 9f0246db2f ov-components: Added cdk as peerDependencies 2024-08-22 11:13:37 +02:00
Carlos Santos 64f62dd716 ci: Fixed server start commands 2024-08-12 13:47:48 +02:00
Carlos Santos 33d71f24c3 ov-components: fixed screen sharing e2e 2024-08-02 11:44:57 +02:00
Carlos Santos 7c1066d126 ov-components: Specify screen sharing options 2024-08-02 11:12:52 +02:00
Carlos Santos 5bf5c68da4 ov-components: Updated css classes 2024-08-01 13:34:13 +02:00
Carlos Santos 33d11926a7 ci: Updated e2e test 2024-07-30 18:13:06 +02:00
Carlos Santos 6604d6aa1f ci: Updated build webcomponent step 2024-07-30 17:08:07 +02:00
Carlos Santos 1b93b1a551 ov-components: included fullscreen e2e event test 2024-07-30 16:44:07 +02:00
Carlos Santos 822ef1ad09 ov-components: Refactored screensharing e2e tests 2024-07-30 16:42:51 +02:00
Carlos Santos 2d6ab64d00 ov-components: Fixed chat e2e tests 2024-07-30 16:42:21 +02:00
Carlos Santos 4c9e949133 ci: Added webcomponent e2e tests 2024-07-30 16:34:07 +02:00
Carlos Santos 31dd06c7e8 ci: Updated e2e tests 2024-07-30 16:07:02 +02:00
Carlos Santos 17a6edbe91 ci: Added webcomponent directives e2e tests 2024-07-30 16:01:54 +02:00
Carlos Santos 9fd1c5b746 ov-components: Fixed webcomponent directives e2e tests 2024-07-30 15:48:13 +02:00
Carlos Santos 5072804e67 ov-components: Refactored and fixed device service 2024-07-30 15:45:22 +02:00
Carlos Santos 7b560b447c ov-components: Fixed fullscreen e2e test 2024-07-29 17:52:56 +02:00
Carlos Santos d52dc9b6cf ov-components: Remove old and unnecessary test and refactored code 2024-07-29 13:20:07 +02:00
Carlos Santos b2e50aa53b ci: Run E2E tests in parallel for improved performance 2024-07-29 13:08:37 +02:00
Carlos Santos 5fa86f4f4c ov-components: split ee2 tests into files 2024-07-29 12:59:25 +02:00
Carlos Santos c61bed5a75 ov-components: Fixed e2e tests events 2024-07-29 12:38:35 +02:00
Carlos Santos 27644e8fa5 ci: Commented webcomponent tests 2024-07-26 13:26:53 +02:00
Carlos Santos 9f7efd8ad5 ov-components: Added elements ids for testing 2024-07-26 13:24:02 +02:00
Carlos Santos a7e5f27eb0 ov-components: Updated generate-docs script 2024-07-26 10:30:24 +02:00
Carlos Santos 52564b5f5d ov-components: Avoid null pointer 2024-07-26 10:29:55 +02:00
Carlos Santos 3f1334b65d ci: Fixed ov-components test workflow running openvidu-call-backend 2024-07-24 18:32:12 +02:00
Carlos Santos 110cbc130f ci: Fixed start test app in e2e workflow 2024-07-24 18:07:32 +02:00
Carlos Santos 5097dc925e ov-components: fixed bundle path 2024-07-24 18:07:13 +02:00
Carlos Santos 551bc76ba1 ci: Wait for openvidu-local-deployment and testapp 2024-07-24 17:29:02 +02:00
Carlos Santos 25ef634757 ci: Updated openvidu-components e2e tests 2024-07-24 17:21:19 +02:00
Carlos Santos aa437be8cf ov-components: Updated dependencies 2024-07-24 17:02:15 +02:00
Carlos Santos d02b12141b ov-components: Refactored and cleaned code 2024-07-24 16:59:40 +02:00
Carlos Santos f28652b425 ov-components: Added missing services to providers section module
Fixed virtual background feature
2024-07-24 16:58:28 +02:00
Carlos Santos fc17b3bf9e ov-components: Fixed base href when playing and downloading recordings
Added base href to video src and downloading link for fixing the recordings features
2024-07-17 15:54:07 +02:00
359 changed files with 64185 additions and 26991 deletions

View File

@ -1,113 +0,0 @@
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

@ -1,158 +0,0 @@
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

@ -0,0 +1,445 @@
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

@ -0,0 +1,56 @@
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://openvidu.io/img/logos/support.jpg"> <img height="75px" src="https://docs.openvidu.io/en/stable/img/logos/support.jpg">
## Sponsors ## Sponsors

View File

@ -10,9 +10,4 @@
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,7 +18,8 @@
"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"],
@ -148,81 +149,6 @@
} }
} }
} }
},
"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,30 +1,35 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { OPENVIDU_CALL_SERVER } from '../config'; import { TestAppConfig } from './selenium.conf';
import { WebComponentConfig } from '../selenium.conf'; import { OpenViduComponentsPO } from './utils.po.test';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; let url = '';
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(WebComponentConfig.browserName) .forBrowser(TestAppConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities) .withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions) .setChromeOptions(TestAppConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress) .usingServer(TestAppConfig.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();
}); });
@ -34,7 +39,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')).to.be.false; expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
const joinButton = await utils.waitForElement('#join-button'); const joinButton = await utils.waitForElement('#join-button');
await joinButton.click(); await joinButton.click();
@ -52,31 +57,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')).to.be.false; expect(await utils.isPresent('#screenshare-btn')).toBeFalse();
// Checking if more options button is not present // Checking if more options button is not present
expect(await utils.isPresent('#more-options-btn')).to.be.false; expect(await utils.isPresent('#more-options-btn')).toBeFalse();
// Checking if participants panel button is not present // Checking if participants panel button is not present
expect(await utils.isPresent('#participants-panel-btn')).to.be.false; expect(await utils.isPresent('#participants-panel-btn')).toBeFalse();
// Checking if activities panel button is not present // Checking if activities panel button is not present
expect(await utils.isPresent('#activities-panel-btn')).to.be.false; expect(await utils.isPresent('#activities-panel-btn')).toBeFalse();
// Checking if logo is not displayed // Checking if logo is not displayed
expect(await utils.isPresent('#branding-logo')).to.be.false; expect(await utils.isPresent('#branding-logo')).toBeFalse();
// Checking if session name is not displayed // Checking if session name is not displayed
expect(await utils.isPresent('#session-name')).to.be.false; expect(await utils.isPresent('#session-name')).toBeFalse();
// Checking if nickname is not displayed // Checking if nickname is not displayed
expect(await utils.getNumberOfElements('#participant-name-container')).equals(0); expect(await utils.getNumberOfElements('#participant-name-container')).toEqual(0);
// Checking if audio detection is not displayed // Checking if audio detection is not displayed
expect(await utils.isPresent('#audio-wave-container')).to.be.false; expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
// Checking if settings button is not displayed // Checking if settings button is not displayed
expect(await utils.isPresent('#settings-container')).to.be.false; expect(await utils.isPresent('#settings-container')).toBeFalse();
}); });
it('should change the UI LANG in prejoin page', async () => { it('should change the UI LANG in prejoin page', async () => {
@ -87,7 +92,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()).equal('Unirme ahora'); expect(await element.getText()).toEqual('Unirme ahora');
}); });
it('should change the UI LANG in room page', async () => { it('should change the UI LANG in room page', async () => {
@ -99,12 +104,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')).to.be.true; expect(await utils.isPresent('#default-settings-panel')).toBeTrue();
const panelTitle = await utils.waitForElement('.panel-title'); const panelTitle = await utils.waitForElement('.panel-title');
expect(await panelTitle.getText()).equal('Configuración'); expect(await panelTitle.getText()).toEqual('Configuración');
const element = await utils.waitForElement('#lang-selected-name'); const element = await utils.waitForElement('#lang-selected-name');
expect(await element.getAttribute('innerText')).equal('Español'); expect(await element.getAttribute('innerText')).toEqual('Español');
}); });
it('should override the LANG OPTIONS', async () => { it('should override the LANG OPTIONS', async () => {
@ -114,7 +119,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')).equals(2); expect(await utils.getNumberOfElements('.lang-menu-opt')).toEqual(2);
await utils.clickOn('.lang-menu-opt'); await utils.clickOn('.lang-menu-opt');
await browser.sleep(500); await browser.sleep(500);
@ -136,7 +141,7 @@ describe('Testing API Directives', () => {
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('.lang-menu-opt')).equals(2); expect(await utils.getNumberOfElements('.lang-menu-opt')).toEqual(2);
}); });
it('should show the PREJOIN page', async () => { it('should show the PREJOIN page', async () => {
@ -165,11 +170,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')).to.be.true; expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
}); });
it('should show the token error WITH prejoin page', async () => { it('should show the token error WITH prejoin page', async () => {
const fixedUrl = `${url}&roomName=TEST_TOKEN&participantName=PNAME`; const fixedUrl = `${TestAppConfig.appUrl}&roomName=TEST_TOKEN&participantName=PNAME`;
await browser.get(`${fixedUrl}`); await browser.get(`${fixedUrl}`);
// Checking if prejoin page exist // Checking if prejoin page exist
@ -195,11 +200,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')).to.be.true; expect(await utils.isPresent('#token-error')).toBeTrue();
}); });
it('should show the token error WITHOUT prejoin page', async () => { it('should show the token error WITHOUT prejoin page', async () => {
const fixedUrl = `${url}&roomName=TOKEN_ERROR&prejoin=false&participantName=PNAME`; const fixedUrl = `${TestAppConfig.appUrl}&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
@ -215,7 +220,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')).to.be.true; expect(await utils.isPresent('#openvidu-dialog')).toBeTrue();
}); });
it('should run the app with VIDEO DISABLED in prejoin page', async () => { it('should run the app with VIDEO DISABLED in prejoin page', async () => {
@ -224,21 +229,20 @@ describe('Testing API Directives', () => {
await utils.checkPrejoinIsPresent(); await utils.checkPrejoinIsPresent();
// Checking if video is displayed // Checking if video is displayed
expect(await utils.getNumberOfElements('video')).equals(1); await utils.waitForElement('#video-poster');
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();
expect(await utils.getNumberOfElements('video')).equals(1);
await utils.waitForElement('#videocam_off'); await utils.waitForElement('#videocam_off');
expect(await utils.isPresent('#videocam_off')).to.be.true; expect(await utils.isPresent('#videocam_off')).toBeTrue();
await utils.waitForElement('#video-poster');
expect(await utils.getNumberOfElements('video')).toEqual(0);
}); });
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 () => {
@ -249,58 +253,74 @@ describe('Testing API Directives', () => {
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Checking if video is displayed // Checking if video is displayed
expect(await utils.getNumberOfElements('video')).equals(1); await utils.waitForElement('#video-poster');
expect(await utils.getNumberOfElements('#video-poster')).equals(1); expect(await utils.getNumberOfElements('video')).toEqual(0);
expect(await utils.getNumberOfElements('#video-poster')).toEqual(1);
await utils.waitForElement('#videocam_off'); await utils.waitForElement('#videocam_off');
expect(await utils.isPresent('#videocam_off')).to.be.true; expect(await utils.isPresent('#videocam_off')).toBeTrue();
}); });
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);
// Checking if audio track is disabled/muted expect(await utils.getNumberOfElements('audio')).toEqual(0);
// 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')).to.be.true; expect(await utils.isPresent('#mic_off')).toBeTrue();
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')).to.be.true; expect(await utils.isPresent('#mic_off')).toBeTrue();
}); });
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);
// Checking if audio track is disabled/muted expect(await utils.getNumberOfElements('audio')).toEqual(0);
// 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')).to.be.true; expect(await utils.isPresent('#mic_off')).toBeTrue();
});
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 () => {
@ -312,7 +332,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')).to.be.false; expect(await utils.isPresent('#screenshare-btn')).toBeFalse();
}); });
it('should HIDE the FULLSCREEN button', async () => { it('should HIDE the FULLSCREEN button', async () => {
@ -324,10 +344,10 @@ describe('Testing API Directives', () => {
await utils.checkToolbarIsPresent(); await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions(); await utils.toggleToolbarMoreOptions();
expect(await utils.getNumberOfElements('#fullscreen-btn')).equals(0); expect(await utils.getNumberOfElements('#fullscreen-btn')).toEqual(0);
}); });
it('should HIDE the CAPTIONS button', async () => { xit('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();
@ -338,16 +358,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')).to.be.false; expect(await utils.isPresent('#captions-btn')).toBeFalse();
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')).to.be.true; expect(await utils.isPresent('.settings-container')).toBeTrue();
expect(await utils.isPresent('#captions-opt')).to.be.false; expect(await utils.isPresent('#captions-opt')).toBeFalse();
}); });
it('should HIDE the TOOLBAR RECORDING button', async () => { it('should HIDE the TOOLBAR RECORDING button', async () => {
@ -361,7 +381,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')).to.be.false; expect(await utils.isPresent('#recording-btn')).toBeFalse();
}); });
it('should HIDE the TOOLBAR BROADCASTING button', async () => { it('should HIDE the TOOLBAR BROADCASTING button', async () => {
@ -375,7 +395,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')).to.be.false; expect(await utils.isPresent('#broadcasting-btn')).toBeFalse();
}); });
it('should HIDE the TOOLBAR SETTINGS button', async () => { it('should HIDE the TOOLBAR SETTINGS button', async () => {
@ -389,7 +409,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')).to.be.false; expect(await utils.isPresent('#toolbar-settings-btn')).toBeFalse();
}); });
it('should HIDE the LEAVE button', async () => { it('should HIDE the LEAVE button', async () => {
@ -401,7 +421,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')).equals(0); expect(await utils.getNumberOfElements('#leave-btn')).toEqual(0);
}); });
it('should HIDE the ACTIVITIES PANEL button', async () => { it('should HIDE the ACTIVITIES PANEL button', async () => {
@ -413,7 +433,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')).to.be.false; expect(await utils.isPresent('#activities-panel-btn')).toBeFalse();
}); });
it('should HIDE the CHAT PANEL button', async () => { it('should HIDE the CHAT PANEL button', async () => {
@ -425,7 +445,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')).to.be.false; expect(await utils.isPresent('#chat-panel-btn')).toBeFalse();
}); });
it('should HIDE the PARTICIPANTS PANEL button', async () => { it('should HIDE the PARTICIPANTS PANEL button', async () => {
@ -437,7 +457,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')).to.be.false; expect(await utils.isPresent('#participants-panel-btn')).toBeFalse();
}); });
it('should HIDE the LOGO', async () => { it('should HIDE the LOGO', async () => {
@ -450,13 +470,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')).to.be.true; expect(await utils.isPresent('#info-container')).toBeTrue();
// Checking if logo is not displayed // Checking if logo is not displayed
expect(await utils.isPresent('#branding-logo')).to.be.false; expect(await utils.isPresent('#branding-logo')).toBeFalse();
}); });
it('should HIDE the SESSION NAME', async () => { it('should HIDE the ROOM NAME', async () => {
await browser.get(`${url}&prejoin=false&displayRoomName=false`); await browser.get(`${url}&prejoin=false&displayRoomName=false`);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
@ -466,10 +486,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')).to.be.true; expect(await utils.isPresent('#info-container')).toBeTrue();
// Checking if session name is not displayed // Checking if session name is not displayed
expect(await utils.isPresent('#session-name')).to.be.false; expect(await utils.isPresent('#session-name')).toBeFalse();
}); });
it('should HIDE the PARTICIPANT NAME', async () => { it('should HIDE the PARTICIPANT NAME', async () => {
@ -484,7 +504,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')).to.be.false; expect(await utils.isPresent('#participant-name-container')).toBeFalse();
}); });
it('should HIDE the AUDIO DETECTION element', async () => { it('should HIDE the AUDIO DETECTION element', async () => {
@ -499,7 +519,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')).to.be.false; expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
}); });
it('should HIDE the STREAM VIDEO CONTROLS button', async () => { it('should HIDE the STREAM VIDEO CONTROLS button', async () => {
@ -514,12 +534,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')).to.be.false; expect(await utils.isPresent('.stream-video-controls')).toBeFalse();
}); });
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 = `${url}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`; const fixedUrl = `${TestAppConfig.appUrl}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`;
await browser.get(fixedUrl); await browser.get(fixedUrl);
await utils.checkSessionIsPresent(); await utils.checkSessionIsPresent();
@ -532,14 +552,15 @@ 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')).to.be.true; expect(await utils.isPresent('#participants-container')).toBeTrue();
// Checking remote participants item // Checking remote participants item
expect(await utils.isPresent('#remote-participant-item')).to.be.false; expect(await utils.isPresent('#remote-participant-item')).toBeFalse();
// Starting new browser for adding a new participant // Starting new browser for adding a new participant
const newTabScript = `window.open("${fixedUrl}")`; const newTabScript = `window.open("${fixedUrl}&participantName=SecondParticipant")`;
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();
@ -547,9 +568,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')).to.be.true; expect(await utils.isPresent('#remote-participant-item')).toBeTrue();
expect(await utils.isPresent('#mute-btn')).to.be.false; expect(await utils.isPresent('#mute-btn')).toBeFalse();
}); });
it('should HIDE the RECORDING ACTIVITY in activities panel', async () => { it('should HIDE the RECORDING ACTIVITY in activities panel', async () => {
@ -567,86 +588,13 @@ 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')).to.be.true; expect(await utils.isPresent('#default-activities-panel')).toBeTrue();
// 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')).to.be.false; expect(await utils.isPresent('ov-recording-activity')).toBeFalse();
});
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 () => {
@ -662,12 +610,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')).to.be.true; expect(await utils.isPresent('#default-activities-panel')).toBeTrue();
// 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')).to.be.false; expect(await utils.isPresent('ov-broadcasting-activity')).toBeFalse();
}); });
}); });

Binary file not shown.

View File

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

View File

@ -1,10 +1,8 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { OPENVIDU_CALL_SERVER } from '../config'; import { TestAppConfig } from './selenium.conf';
import { WebComponentConfig } from '../selenium.conf'; import { OpenViduComponentsPO } from './utils.po.test';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; const url = TestAppConfig.appUrl;
describe('Testing CHAT features', () => { describe('Testing CHAT features', () => {
let browser: WebDriver; let browser: WebDriver;
@ -12,10 +10,10 @@ describe('Testing CHAT features', () => {
async function createChromeBrowser(): Promise<WebDriver> { async function createChromeBrowser(): Promise<WebDriver> {
return await new Builder() return await new Builder()
.forBrowser(WebComponentConfig.browserName) .forBrowser(TestAppConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities) .withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions) .setChromeOptions(TestAppConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress) .usingServer(TestAppConfig.seleniumAddress)
.build(); .build();
} }
@ -25,6 +23,10 @@ describe('Testing CHAT features', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
// leaving room if connected
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -38,7 +40,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')).to.be.true; expect(await utils.isPresent('.input-container')).toBeTrue();
const input = await utils.waitForElement('#chat-input'); const input = await utils.waitForElement('#chat-input');
await input.sendKeys('Test message'); await input.sendKeys('Test message');
@ -47,13 +49,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')).to.be.true; expect(await utils.isPresent('.message')).toBeTrue();
expect(await utils.getNumberOfElements('.message')).equals(1); expect(await utils.getNumberOfElements('.message')).toEqual(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')).equals(2); expect(await utils.getNumberOfElements('.message')).toEqual(2);
}); });
it('should receive a message', async () => { it('should receive a message', async () => {
@ -61,6 +63,7 @@ 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
@ -76,7 +79,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')).to.be.true; expect(await utils.isPresent('.input-container')).toBeTrue();
const input = await utils.waitForElement('#chat-input'); const input = await utils.waitForElement('#chat-input');
await input.sendKeys('test message'); await input.sendKeys('test message');
@ -90,8 +93,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')).equals(1); expect(await utils.getNumberOfElements('.message')).toEqual(1);
expect(await participantName.getText()).equals(pName); expect(await participantName.getText()).toEqual(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 () => {
@ -104,14 +107,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')).to.be.true; expect(await utils.isPresent('.input-container')).toBeTrue();
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('.msg-content a'); await utils.waitForElement('.chat-message a');
expect(await utils.isPresent('.msg-content a')).to.be.true; expect(await utils.isPresent('.chat-message a')).toBeTrue();
}); });
}); });

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

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

View File

@ -0,0 +1,380 @@
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

@ -0,0 +1,186 @@
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

@ -0,0 +1,806 @@
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

@ -1,21 +1,19 @@
import { expect } from 'chai'; import { Builder, 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 { getBrowserOptionsWithoutDevices, WebComponentConfig } from '../selenium.conf';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; const url = TestAppConfig.appUrl;
describe('Testing panels', () => { describe('Panels: UI Navigation and Section Switching', () => {
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(WebComponentConfig.browserName) .forBrowser(TestAppConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities) .withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions) .setChromeOptions(TestAppConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress) .usingServer(TestAppConfig.seleniumAddress)
.build(); .build();
} }
@ -25,6 +23,9 @@ describe('Testing panels', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
@ -36,190 +37,130 @@ describe('Testing panels', () => {
// 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')).to.be.true; // expect(await utils.isPresent('#pre-join-container')).toBeTrue();
// const backgroundButton = await utils.waitForElement('#background-effects-btn'); // const backgroundButton = await utils.waitForElement('#background-effects-btn');
// expect(await utils.isPresent('#background-effects-btn')).to.be.true; // expect(await utils.isPresent('#background-effects-btn')).toBeTrue();
// expect(await backgroundButton.isEnabled()).to.be.true; // expect(await backgroundButton.isEnabled()).toBeTrue();
// 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')).to.be.true; // expect(await utils.isPresent('#background-effects-container')).toBeTrue();
// element = await utils.waitForElement('#camera-button'); // element = await utils.waitForElement('#camera-button');
// expect(await utils.isPresent('#camera-button')).to.be.true; // expect(await utils.isPresent('#camera-button')).toBeTrue();
// expect(await element.isEnabled()).to.be.true; // expect(await element.isEnabled()).toBeTrue();
// 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')).to.be.true; // expect(await utils.isPresent('#video-poster')).toBeTrue();
// expect(await backgroundButton.isDisplayed()).to.be.true; // expect(await backgroundButton.isDisplayed()).toBeTrue();
// expect(await backgroundButton.isEnabled()).to.be.false; // expect(await backgroundButton.isEnabled()).toBeFalse();
// expect(await utils.isPresent('#background-effects-container')).to.be.false; // expect(await utils.isPresent('#background-effects-container')).toBeFalse();
// }); // });
it('should toggle CHAT panel', async () => { it('should open and close the CHAT panel and verify its content', 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')).to.be.true; expect(await utils.isPresent('.input-container')).toBeTrue();
await utils.waitForElement('.messages-container'); await utils.waitForElement('.messages-container');
expect(await utils.isPresent('.messages-container')).to.be.true; expect(await utils.isPresent('.messages-container')).toBeTrue();
await chatButton.click(); await chatButton.click();
expect(await utils.isPresent('.input-container')).toBeFalse();
expect(await utils.isPresent('.input-container')).to.be.false; expect(await utils.isPresent('.messages-container')).toBeFalse();
expect(await utils.isPresent('.messages-container')).to.be.false;
}); });
it('should toggle PARTICIPANTS panel', async () => { it('should open and close the PARTICIPANTS panel and verify its content', 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')).to.be.true; expect(await utils.isPresent('.local-participant-container')).toBeTrue();
await utils.waitForElement('ov-participant-panel-item'); await utils.waitForElement('ov-participant-panel-item');
expect(await utils.isPresent('ov-participant-panel-item')).to.be.true; expect(await utils.isPresent('ov-participant-panel-item')).toBeTrue();
await participantBtn.click(); await participantBtn.click();
expect(await utils.isPresent('.local-participant-container')).toBeFalse();
expect(await utils.isPresent('.local-participant-container')).to.be.false; expect(await utils.isPresent('ov-participant-panel-item')).toBeFalse();
expect(await utils.isPresent('ov-participant-panel-item')).to.be.false;
}); });
it('should toggle ACTIVITIES panel', async () => { it('should open and close the ACTIVITIES panel and verify its content', 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')).to.be.true; expect(await utils.isPresent('#activities-container')).toBeTrue();
await utils.waitForElement('#recording-activity'); await utils.waitForElement('#recording-activity');
expect(await utils.isPresent('#recording-activity')).to.be.true; expect(await utils.isPresent('#recording-activity')).toBeTrue();
await activitiesBtn.click(); await activitiesBtn.click();
expect(await utils.isPresent('#activities-container')).toBeFalse();
expect(await utils.isPresent('#activities-container')).to.be.false; expect(await utils.isPresent('#recording-activity')).toBeFalse();
expect(await utils.isPresent('#recording-activity')).to.be.false;
}); });
it('should toggle SETTINGS panel', async () => { it('should open the SETTINGS panel and verify its content', 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')).to.be.true; expect(await utils.isPresent('#default-settings-panel')).toBeTrue();
}); });
it('should switching between PARTICIPANTS and CHAT panels', async () => { it('should switch between PARTICIPANTS and CHAT panels and verify correct content is shown', 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')).to.be.true; expect(await utils.isPresent('.sidenav-menu')).toBeTrue();
await utils.waitForElement('.input-container'); await utils.waitForElement('.input-container');
expect(await utils.isPresent('.input-container')).to.be.true; expect(await utils.isPresent('.input-container')).toBeTrue();
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('.local-participant-container')).to.be.true; expect(await utils.isPresent('ov-participant-panel-item')).toBeTrue();
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('.input-container')).to.be.true; expect(await utils.isPresent('.messages-container')).toBeTrue();
expect(await utils.isPresent('.local-participant-container')).toBeFalse();
expect(await utils.isPresent('.messages-container')).to.be.true; 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;
// Close chat panel
await chatButton.click(); await chatButton.click();
expect(await utils.getNumberOfElements('.input-container')).equals(0); expect(await utils.getNumberOfElements('.input-container')).toEqual(0);
expect(await utils.isPresent('messages-container')).to.be.false; expect(await utils.isPresent('messages-container')).toBeFalse();
}); });
it('should switching between sections in SETTINGS PANEL', async () => { it('should switch between sections in the SETTINGS panel and verify correct content is shown', 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')).to.be.true; expect(await utils.isPresent('.sidenav-menu')).toBeTrue();
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,20 +1,19 @@
import { expect } from 'chai';
import { Builder, WebDriver } from 'selenium-webdriver'; import { Builder, WebDriver } from 'selenium-webdriver';
import { OPENVIDU_CALL_SERVER } from '../config'; import { TestAppConfig } from './selenium.conf';
import { WebComponentConfig } from '../selenium.conf'; import { OpenViduComponentsPO } from './utils.po.test';
import { OpenViduComponentsPO } from '../utils.po.test';
const url = `${WebComponentConfig.appUrl}?OV_URL=${OPENVIDU_CALL_SERVER}`; const url = TestAppConfig.appUrl;
describe('Testing screenshare features', () => { describe('E2E: Screensharing 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(WebComponentConfig.browserName) .forBrowser(TestAppConfig.browserName)
.withCapabilities(WebComponentConfig.browserCapabilities) .withCapabilities(TestAppConfig.browserCapabilities)
.setChromeOptions(WebComponentConfig.browserOptions) .setChromeOptions(TestAppConfig.browserOptions)
.usingServer(WebComponentConfig.seleniumAddress) .usingServer(TestAppConfig.seleniumAddress)
.build(); .build();
} }
@ -24,197 +23,188 @@ describe('Testing screenshare features', () => {
}); });
afterEach(async () => { afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit(); await browser.quit();
}); });
it('should toggle screensharing twice', async () => { it('should toggle screensharing on and off twice, updating video count', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Clicking to screensharing button // Enable screensharing
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')).equals(2); expect(await utils.getNumberOfElements('video')).toEqual(2);
expect(await utils.getNumberOfElements('.OV_stream.speaking')).equals(1);
// Clicking to screensharing button // Disable screensharing
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('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(1);
// toggle screenshare again
await utils.clickOn('#screenshare-btn');
await browser.sleep(500);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('video')).equals(2); expect(await utils.getNumberOfElements('video')).toEqual(2);
await utils.clickOn('#screenshare-btn'); // Disable again
await browser.sleep(500); await utils.disableScreenShare();
await utils.waitForElement('#screenshare-menu'); expect(await utils.getNumberOfElements('video')).toEqual(1);
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(1);
}); });
it('should show screen and muted camera', async () => { it('should show screenshare and muted camera (camera off, screenshare on)', 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');
// Clicking to screensharing button // Enable screensharing
const screenshareButton = await utils.waitForElement('#screenshare-btn'); const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await screenshareButton.isDisplayed()).to.be.true; expect(await screenshareButton.isDisplayed()).toBeTrue();
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')).equals(2); expect(await utils.getNumberOfElements('video')).toEqual(2);
await utils.clickOn('#screenshare-btn'); // Disable screensharing
await browser.sleep(500); await utils.disableScreenShare();
await utils.waitForElement('#screenshare-menu'); expect(await utils.getNumberOfElements('video')).toEqual(1);
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(1);
}); });
it('should screensharing with PINNED video', async () => { it('should display screensharing with a single pinned video', async () => {
await browser.get(`${url}&prejoin=false`); await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent(); await utils.checkLayoutPresent();
// Clicking to screensharing button // Enable screensharing
const screenshareButton = await utils.waitForElement('#screenshare-btn'); const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await screenshareButton.isDisplayed()).to.be.true; expect(await screenshareButton.isDisplayed()).toBeTrue();
await screenshareButton.click(); await screenshareButton.click();
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
}); });
it('should screensharing with PINNED video and replace the existing one', async () => { it('should replace pinned video when a second participant starts screensharing', 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();
// Clicking to screensharing button // First participant screenshares
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')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Starting new browser for adding the second participant // Second participant joins and screenshares
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')).equals(4); expect(await utils.getNumberOfElements('video')).toEqual(4);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Go to first tab // Switch back to first tab and check
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')).equals(4); expect(await utils.getNumberOfElements('video')).toEqual(4);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
}); });
it('should disabled a screensharing and pinned the previous one', async () => { it('should unpin screensharing and restore previous pinned video when disabled', 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();
// Clicking to screensharing button // First participant screenshares
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')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Starting new browser for adding the second participant // Second participant joins and screenshares
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')).equals(4); expect(await utils.getNumberOfElements('video')).toEqual(4);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
// Disable screensharing
await utils.clickOn('#screenshare-btn');
await utils.waitForElement('#screenshare-menu');
await utils.clickOn('#disable-screen-button');
await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(3);
await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1);
// Go to first tab // 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.switchTo().window(tabs[0]);
await browser.sleep(500); await browser.sleep(500);
expect(await utils.getNumberOfElements('video')).equals(3); expect(await utils.getNumberOfElements('video')).toEqual(3);
await utils.waitForElement('.OV_big'); await utils.waitForElement('.OV_big');
expect(await utils.getNumberOfElements('.OV_big')).equals(1); expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
}); });
// it('should screensharing with audio muted', async () => { it('should correctly share screen with microphone muted and maintain proper track state', async () => {
// let isAudioEnabled; // Helper for inspecting stream tracks
// const getAudioScript = (className: string) => { const getMediaTracks = (className: string) => {
// return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`; return `
// }; const tracks = document.getElementsByClassName('${className}')[0].srcObject.getTracks();
// await browser.get(`${url}&prejoin=false`); return tracks.map(track => ({
kind: track.kind,
enabled: track.enabled,
id: track.id,
label: track.label
}));`;
};
// await utils.checkLayoutPresent(); // Setup: Navigate to room and skip prejoin
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
// const micButton = await utils.waitForElement('#mic-btn'); // Step 1: First mute the microphone
// await micButton.click(); const micButton = await utils.waitForElement('#mic-btn');
await micButton.click();
// // Clicking to screensharing button // Step 2: Start screen sharing
// const screenshareButton = await utils.waitForElement('#screenshare-btn'); await utils.clickOn('#screenshare-btn');
// expect(await utils.isPresent('#screenshare-btn')).to.be.true;
// await screenshareButton.click();
// await utils.waitForElement('.screen-type'); // Step 3: Verify both streams are present
// expect(await utils.getNumberOfElements('video')).equals(2); await utils.waitForElement('.screen-type');
expect(await utils.getNumberOfElements('video')).toEqual(2);
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); // Step 4: Verify screen share track properties
// expect(isAudioEnabled).to.be.false; 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'); // await utils.waitForElement('#status-mic');
// expect(await utils.getNumberOfElements('#status-mic')).equals(2); // const micStatusCount = await utils.getNumberOfElements('#status-mic');
// expect(micStatusCount).toEqual(2);
// // Clicking to screensharing button
// await screenshareButton.click();
// expect(await utils.getNumberOfElements('video')).equals(1);
// });
// 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 browser.sleep(500);
expect(await utils.getNumberOfElements('video')).toEqual(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`);
@ -222,16 +212,16 @@ describe('Testing screenshare 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()).to.be.true; // expect(await screenshareButton.isDisplayed()).toBeTrue();
// await screenshareButton.click(); // await screenshareButton.click();
// await utils.waitForElement('.OV_big'); // await utils.waitForElement('.OV_big');
// expect(await utils.getNumberOfElements('video')).equals(2); // expect(await utils.getNumberOfElements('video')).toEqual(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')).equals(1); // expect(await utils.getNumberOfElements('video')).toEqual(1);
// }); // });
// it('should screenshare has audio active when camera is muted', async () => { // it('should screenshare has audio active when camera is muted', async () => {
@ -244,33 +234,33 @@ describe('Testing screenshare 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')).to.be.true; // expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
// await screenshareButton.click(); // await screenshareButton.click();
// await utils.waitForElement('.OV_big'); // await utils.waitForElement('.OV_big');
// expect(await utils.getNumberOfElements('video')).equals(2); // expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).equals(1); // expect(await utils.getNumberOfElements('#status-mic')).toEqual(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')).equals(1); // expect(await utils.getNumberOfElements('video')).toEqual(1);
// await browser.sleep(500); // await browser.sleep(500);
// expect(await utils.isPresent('#status-mic')).to.be.false; // expect(await utils.isPresent('#status-mic')).toBeFalse();
// // 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).to.be.true; // expect(isAudioEnabled).toBeTrue();
// // 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')).equals(2); // expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).equals(1); // expect(await utils.getNumberOfElements('#status-mic')).toEqual(1);
// }); // });
// it('should camera come back with audio muted when screensharing', async () => { // it('should camera come back with audio muted when screensharing', async () => {
@ -289,38 +279,38 @@ describe('Testing screenshare features', () => {
// await screenshareButton.click(); // await screenshareButton.click();
// await utils.waitForElement('.screen-type'); // await utils.waitForElement('.screen-type');
// expect(await utils.getNumberOfElements('video')).equals(2); // expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).equals(1); // expect(await utils.getNumberOfElements('#status-mic')).toEqual(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')).equals(1); // expect(await utils.getNumberOfElements('video')).toEqual(1);
// expect(await utils.isPresent('#status-mic')).to.be.false; // expect(await utils.isPresent('#status-mic')).toBeFalse();
// // 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).to.be.true; // expect(isAudioEnabled).toBeTrue();
// // 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')).equals(1); // expect(await utils.getNumberOfElements('#status-mic')).toEqual(1);
// isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type'));
// expect(isAudioEnabled).to.be.false; // expect(isAudioEnabled).toBeFalse();
// // 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')).equals(2); // expect(await utils.getNumberOfElements('video')).toEqual(2);
// expect(await utils.getNumberOfElements('#status-mic')).equals(2); // expect(await utils.getNumberOfElements('#status-mic')).toEqual(2);
// isAudioEnabled = await browser.executeScript(getAudioScript('camera-type')); // isAudioEnabled = await browser.executeScript(getAudioScript('camera-type'));
// expect(isAudioEnabled).to.be.false; // expect(isAudioEnabled).toBeFalse();
// }); // });
}); });

View File

@ -10,12 +10,14 @@ 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=e2e/assets/audio.wav' `--use-file-for-fake-audio-capture=${audioPath}`
]; ];
const chromeArgumentsCI = [ const chromeArgumentsCI = [
'--window-size=1300,1000', '--window-size=1300,1000',
@ -29,7 +31,10 @@ 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 = [
@ -46,17 +51,17 @@ const chromeArgumentsWithoutMediaDevicesCI = [
'--deny-permission-prompts' '--deny-permission-prompts'
]; ];
export const WebComponentConfig: BrowserConfig = { export const TestAppConfig: BrowserConfig = {
appUrl: 'http://localhost:8080/', appUrl: 'http://localhost:4200/#/call?staticVideos=false',
seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:3000/webdriver' : '', seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:4444/wd/hub' : '',
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 AngularConfig: BrowserConfig = { export const NestedConfig: BrowserConfig = {
appUrl: 'http://localhost:4200/#/testing', appUrl: 'http://localhost:4200/#/testing',
seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:3000/webdriver' : '', seleniumAddress: LAUNCH_MODE === 'CI' ? 'http://localhost:4444/wd/hub' : '',
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

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import { expect } from 'chai'; import { By, until, WebDriver, WebElement } from 'selenium-webdriver';
import { By, Origin, until, WebDriver, WebElement } from 'selenium-webdriver';
export class OpenViduComponentsPO { export class OpenViduComponentsPO {
private TIMEOUT = 10 * 1000; private TIMEOUT = 10 * 1000;
@ -31,44 +30,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')).to.be.true; expect(await this.isPresent('#prejoin-container')).toBe(true);
} }
async checkSessionIsPresent() { async checkSessionIsPresent() {
await this.waitForElement('#call-container'); await this.waitForElement('#call-container');
expect(await this.isPresent('#call-container')).to.be.true; expect(await this.isPresent('#call-container')).toBe(true);
await this.waitForElement('#session-container'); await this.waitForElement('#session-container');
expect(await this.isPresent('#session-container')).to.be.true; expect(await this.isPresent('#session-container')).toBe(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')).to.be.true; expect(await this.isPresent('#layout-container')).toBe(true);
await this.waitForElement('#layout'); await this.waitForElement('#layout');
expect(await this.isPresent('#layout')).to.be.true; expect(await this.isPresent('#layout')).toBe(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')).to.be.true; expect(await this.isPresent('.OV_stream')).toBe(true);
} }
async checkVideoElementIsPresent(): Promise<void> { async checkVideoElementIsPresent(): Promise<void> {
await this.waitForElement('video'); await this.waitForElement('video');
expect(await this.isPresent('video')).to.be.true; expect(await this.isPresent('video')).toBe(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')).to.be.true; expect(await this.isPresent('#media-buttons-container')).toBe(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')).to.be.true; expect(await this.isPresent('ov-pro-feature-template')).toBe(true);
} }
async clickOn(selector: string): Promise<void> { async clickOn(selector: string): Promise<void> {
@ -104,18 +103,27 @@ 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')).to.be.true; expect(await this.isPresent('#more-options-btn')).toBe(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')).to.be.true; expect(await this.isPresent('#recording-btn')).toBe(true);
await this.clickOn('#recording-btn'); await this.clickOn('#recording-btn');
} }
@ -123,16 +131,52 @@ export class OpenViduComponentsPO {
// Open more options menu // Open more options menu
await this.toggleToolbarMoreOptions(); await this.toggleToolbarMoreOptions();
const fullscreenButton = await this.waitForElement('#fullscreen-btn'); await this.waitForElement('#fullscreen-btn');
expect(await this.isPresent('#fullscreen-btn')).to.be.true; expect(await this.isPresent('#fullscreen-btn')).toBe(true);
await fullscreenButton.click(); await this.clickOn('#fullscreen-btn');
}
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')).to.be.true; expect(await this.isPresent('#activities-panel-btn')).toBe(true);
await this.clickOn('#activities-panel-btn'); await this.clickOn('#activities-panel-btn');
break; break;
@ -151,5 +195,7 @@ export class OpenViduComponentsPO {
await this.clickOn('#toolbar-settings-btn'); await this.clickOn('#toolbar-settings-btn');
break; break;
} }
await this.browser.sleep(500);
} }
} }

View File

@ -1,292 +0,0 @@
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

@ -1,40 +0,0 @@
<!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,337 +0,0 @@
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

@ -1,115 +0,0 @@
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 '/en/stable/' projects src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g' grep -rl '/latest/' projects src | xargs sed -i -e 's|/latest/|/'${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 '/en/'${BASEHREF_VERSION}'/' projects src | xargs sed -i -e 's|/en/'${BASEHREF_VERSION}'/|/en/stable/|g' grep -rl '/'${BASEHREF_VERSION}'/' projects src | xargs sed -i -e 's|/'${BASEHREF_VERSION}'/|/latest/|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-docs repo and copy new ones # Clean previous docs from openvidu.io repo and copy new ones
npm run doc:clean-copy npm run doc:clean-copy

View File

@ -204,7 +204,6 @@ 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

@ -1,60 +0,0 @@
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,65 +1,60 @@
{ {
"dependencies": { "dependencies": {
"@angular/animations": "18.0.0", "@angular/animations": "19.2.8",
"@angular/cdk": "18.0.0", "@angular/cdk": "19.2.11",
"@angular/common": "18.0.0", "@angular/common": "19.2.8",
"@angular/core": "18.0.0", "@angular/core": "19.2.8",
"@angular/forms": "18.0.0", "@angular/forms": "19.2.8",
"@angular/material": "18.0.0", "@angular/material": "19.2.11",
"@angular/platform-browser": "18.0.0", "@angular/platform-browser": "19.2.8",
"@angular/platform-browser-dynamic": "18.0.0", "@angular/platform-browser-dynamic": "19.2.8",
"@angular/router": "18.0.0", "@angular/router": "19.2.8",
"@livekit/track-processors": "0.3.2", "@livekit/track-processors": "^0.5.6",
"@types/dom-mediacapture-transform": "^0.1.11",
"autolinker": "4.0.0", "autolinker": "4.0.0",
"livekit-client": "2.1.0", "livekit-client": "2.11.4",
"rxjs": "7.5.7", "rxjs": "7.8.1",
"tslib": "2.3.1", "tslib": "2.7.0",
"zone.js": "^0.14.6" "zone.js": "^0.15.0"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "18.0.1", "@angular-devkit/build-angular": "19.2.9",
"@angular/cli": "18.0.1", "@angular/cli": "19.2.9",
"@angular/compiler": "18.0.0", "@angular/compiler": "19.2.8",
"@angular/compiler-cli": "18.0.0", "@angular/compiler-cli": "19.2.8",
"@angular/elements": "18.0.0",
"@compodoc/compodoc": "^1.1.25", "@compodoc/compodoc": "^1.1.25",
"@types/chai": "4.3.6", "@types/jasmine": "^5.1.4",
"@types/dom-mediacapture-transform": "0.1.9", "@types/node": "20.12.14",
"@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.4", "@types/ws": "^8.5.12",
"chai": "4.3.6", "chromedriver": "138.0.0",
"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.0.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.2.1",
"http-server": "14.1.1", "http-server": "14.1.1",
"husky": "^8.0.3", "husky": "^9.1.6",
"jasmine-core": "3.10.1", "jasmine": "^5.3.1",
"jasmine-core": "5.3.0",
"jasmine-spec-reporter": "7.0.0", "jasmine-spec-reporter": "7.0.0",
"karma": "^6.3.9", "karma": "^6.4.4",
"karma-chrome-launcher": "3.1.1", "karma-chrome-launcher": "3.2.0",
"karma-coverage": "^2.0.3", "karma-coverage": "^2.2.1",
"karma-coverage-istanbul-reporter": "3.0.3", "karma-coverage-istanbul-reporter": "3.0.3",
"karma-jasmine": "4.0.1", "karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "1.7.0", "karma-jasmine-html-reporter": "2.1.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": "^14.0.1", "lint-staged": "^15.2.10",
"mocha": "9.2.2", "ng-packagr": "19.2.2",
"ng-packagr": "18.0.0", "npm-watch": "^0.13.0",
"npm-watch": "^0.11.0", "prettier": "3.3.3",
"prettier": "3.0.3", "selenium-webdriver": "4.32.0",
"selenium-webdriver": "4.12.0", "ts-node": "10.9.2",
"ts-node": "10.4.0",
"tslint": "6.1.3", "tslint": "6.1.3",
"typescript": "5.4.5", "typescript": "5.8.3",
"webpack-bundle-analyzer": "^4.5.0" "webpack-bundle-analyzer": "^4.10.2"
}, },
"name": "openvidu-components-testapp", "name": "openvidu-components-testapp",
"private": true, "private": true,
@ -74,31 +69,36 @@
} }
}, },
"scripts": { "scripts": {
"husky": "cd .. && husky install", "start": "ng serve --configuration development --open",
"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-docs/docs/docs/reference-docs/openvidu-components-angular && cp -r ./docs/openvidu-components-angular/ ../../openvidu-docs/docs/docs/reference-docs/openvidu-components-angular", "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: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:build": "ng build openvidu-components-angular --configuration production && cd ./dist/openvidu-components-angular",
"lib:copy": "cp dist/openvidu-components-angular/openvidu-components-angular-*.tgz ../../openvidu-call-livekit/openvidu-call-front",
"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:serve": "ng build openvidu-components-angular --watch",
"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/frontend",
"lib:test": "ng test openvidu-components-angular --no-watch --code-coverage", "lib:test": "ng test openvidu-components-angular --no-watch --code-coverage",
"lint": "ng lint", "e2e:nested-all": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/*.test.js",
"start": "ng serve --configuration development --open", "e2e:nested-events": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/events.test.js",
"start-prod": "npx http-server ./dist/openvidu-components-testapp/ --port 4200", "e2e:nested-structural-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/structural-directives.test.js",
"start:ssl": "ng serve --ssl --configuration development --host 0.0.0.0 --port 5080", "e2e:nested-attribute-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/attribute-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-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/api-directives.test.js",
"webcomponent:build": "./node_modules/@angular/cli/bin/ng.js build openvidu-webcomponent --configuration production && node ./openvidu-webcomponent-build.js", "e2e:lib-internal-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/internal-directives.test.js",
"webcomponent:e2e": "tsc --project ./e2e && npx mocha --recursive --timeout 30000 ./e2e/dist/webcomponent-e2e/**/*.test.js", "e2e:lib-chat": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/chat.test.js",
"webcomponent:e2e-ci": "cross-env LAUNCH_MODE=CI npm run webcomponent:e2e", "e2e:lib-events": "tsc --project ./e2e && npx jasmine ./e2e/dist/events.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-media-devices": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/media-devices.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-panels": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/panels.test.js",
"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.0.0-beta2" "version": "3.3.0"
} }

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

View File

@ -7,24 +7,33 @@ const apiDirectivesTable =
'|:--------------------------------: | :-------: | :---------------------------------------------: |'; '|:--------------------------------: | :-------: | :---------------------------------------------: |';
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() {
// Directory where directive files are located
const directivesDir = 'projects/openvidu-components-angular/src/lib/directives/api'; const directivesDir = 'projects/openvidu-components-angular/src/lib/directives/api';
return listFiles(directivesDir, '.directive.ts'); return listFiles(directivesDir, '.directive.ts');
} }
/**
* Get all component files
*/
function getComponentFiles() { function getComponentFiles() {
// Directory where component files are located
const componentsDir = 'projects/openvidu-components-angular/src/lib/components'; const componentsDir = 'projects/openvidu-components-angular/src/lib/components';
return listFiles(componentsDir, '.component.ts'); return listFiles(componentsDir, '.component.ts');
} }
/**
* Get all admin files
*/
function getAdminFiles() { function getAdminFiles() {
// Directory where component files are located
const componentsDir = 'projects/openvidu-components-angular/src/lib/admin'; const componentsDir = 'projects/openvidu-components-angular/src/lib/admin';
return listFiles(componentsDir, '.component.ts'); 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) {
@ -33,128 +42,265 @@ function listFiles(directoryPath, fileExtension) {
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) { /**
try { * Add a row to the markdown table
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) {
// Read the current content of the file
try { try {
const data = fs.readFileSync(filePath, 'utf8'); const data = fs.readFileSync(filePath, 'utf8');
// Define the target line and the Markdown row
const markdownRow = `| **${parameter}** | \`${type}\` | [${reference}](../directives/${reference}.html) |`; const markdownRow = `| **${parameter}** | \`${type}\` | [${reference}](../directives/${reference}.html) |`;
// Find the line that contains the table
const lines = data.split('\n'); const lines = data.split('\n');
const targetIndex = lines.findIndex((line) => line.includes(endApiLine)); const targetIndex = lines.findIndex((line) => line.includes(endApiLine));
if (targetIndex !== -1) { if (targetIndex !== -1) {
// Insert the new row above the target line
lines.splice(targetIndex, 0, markdownRow); lines.splice(targetIndex, 0, markdownRow);
// Join the lines back together
const updatedContent = lines.join('\n'); const updatedContent = lines.join('\n');
// Write the updated content to the file
fs.writeFileSync(filePath, updatedContent, 'utf8'); fs.writeFileSync(filePath, updatedContent, 'utf8');
console.log('Row added successfully.'); console.log(`Added directive: ${parameter} -> ${reference}`);
} else { } else {
console.error('Table not found in the file.'); console.error('End marker not found in file:', filePath);
} }
} catch (error) { } catch (error) {
console.error('Error writing to file:', error); console.error('Error adding row to table:', error);
} }
} }
/**
* Replace content between start and end markers
*/
function replaceDynamicTableContent(filePath, content) { function replaceDynamicTableContent(filePath, content) {
// Read the current content of the file
try { try {
const data = fs.readFileSync(filePath, 'utf8'); const data = fs.readFileSync(filePath, 'utf8');
const pattern = new RegExp(`${startApiLine}([\\s\\S]*?)${endApiLine}`, 'g'); const pattern = new RegExp(`${startApiLine}([\\s\\S]*?)${endApiLine}`, 'g');
// Replace the content between startLine and endLine with the replacement table
const modifiedContent = data.replace(pattern, (match, capturedContent) => { const modifiedContent = data.replace(pattern, (match, capturedContent) => {
return startApiLine + '\n' + content + '\n' + endApiLine; return startApiLine + '\n' + content + '\n' + endApiLine;
}); });
// Write the modified content back to the file
fs.writeFileSync(filePath, modifiedContent, 'utf8'); fs.writeFileSync(filePath, modifiedContent, 'utf8');
console.log(`Updated table content in: ${filePath}`);
} 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...`);
@ -164,7 +310,27 @@ function replaceDynamicTableContent(filePath, content) {
} }
} }
const directiveFiles = getDirectiveFiles(); // Main execution
const componentFiles = getComponentFiles(); if (require.main === module) {
const adminFiles = getAdminFiles(); try {
writeApiDirectivesTable(componentFiles.concat(adminFiles), directiveFiles); const directiveFiles = getDirectiveFiles();
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,8 +5,7 @@
"../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: ['Chrome'], browsers: ['ChromeHeadless'],
singleRun: false, singleRun: false,
restartOnFileChange: true restartOnFileChange: true
}); });

View File

@ -0,0 +1,418 @@
{
"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,15 +4,16 @@
}, },
"name": "openvidu-components-angular", "name": "openvidu-components-angular",
"peerDependencies": { "peerDependencies": {
"@angular/animations": "^17.0.0 || ^18.0.0", "@angular/animations": "^17.0.0 || ^18.0.0 || ^19.0.0",
"@angular/common": "^17.0.0 || ^18.0.0", "@angular/cdk": "^17.0.0 || ^18.0.0 || ^19.0.0",
"@angular/core": "^17.0.0 || ^18.0.0", "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0",
"@angular/forms": "^17.0.0 || ^18.0.0", "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0",
"@angular/material": "^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 || ^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.0.0-beta2" "version": "3.3.0"
} }

View File

@ -1,6 +1,6 @@
<div class="dashboard-container"> <div class="dashboard-container">
<mat-toolbar class="header"> <mat-toolbar class="header">
<span>{{ 'ADMIN.DASHBOARD_TITLE' | translate }}</span> <span>{{ title || ('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,5 +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** |
|:--------------------------------: | :-------: | :---------------------------------------------: | |:--------------------------------: | :-------: | :---------------------------------------------: |
| **recordingsList** | `RecordingInfo` | [AdminDashboardRecordingsListDirective](../directives/AdminDashboardRecordingsListDirective.html) | | **navbarTitle** | `string` | [AdminDashboardTitleDirective](../directives/AdminDashboardTitleDirective.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-color); background-color: var(--ov-secondary-action-color);
color: var(--ov-text-color); color: var(--ov-text-primary-color);
} }
.logout-btn { .logout-btn {
color: var(--ov-text-color); color: var(--ov-text-primary-color);
} }
.dashboard-body { .dashboard-body {
background-color: var(--ov-secondary-color); background-color: var(--ov-secondary-action-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-panel-background); background-color: var(--ov-surface-color);
color: var(--ov-panel-text-color); color: var(--ov-text-primary-color);
} }
.search-bar { .search-bar {
height: 95%; height: 95%;
width: 30%; width: 30%;
display: flex; display: flex;
background-color: var(--ov-panel-background); background-color: var(--ov-surface-color);
padding: 0px 10px; padding: 0px 10px;
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-radius);
} }
#search-input { #search-input {
@ -87,7 +87,7 @@
} }
.recordings-container { .recordings-container {
background-color: var(--ov-secondary-color); background-color: var(--ov-secondary-action-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-panel-background); background-color: var(--ov-surface-color);
} }
} }
@ -135,18 +135,18 @@
margin-right: -50%; margin-right: -50%;
top: 50%; top: 50%;
left: 50%; left: 50%;
background-color: var(--ov-logo-background-color); background-color: var(--ov-secondary-action-color);
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-radius);
} }
.video-btns button #play { .video-btns button #play {
color: var(--ov-text-color); color: var(--ov-text-primary-color);
} }
.video-btns button #download { .video-btns button #download {
color: var(--ov-tertiary-color); color: var(--ov-accent-action-color);
} }
.video-btns button #delete { .video-btns button #delete {
color: var(--ov-warn-color); color: var(--ov-error-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-panel-text-color); color: var(--ov-text-primary-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-panel-background); background-color: var(--ov-surface-color);
color: var(--ov-panel-text-color); color: var(--ov-text-primary-color);
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-radius);
} }
} }
.footer { .footer {
height: 25px; height: 25px;
background-color: var(--ov-primary-color); background-color: var(--ov-background-color);
color: var(--ov-text-color); color: var(--ov-text-primary-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-tertiary-color); color: var(--ov-accent-action-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-color); color: var(--ov-text-primary-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

@ -1,25 +0,0 @@
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,13 +2,14 @@ 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/openvidu-components-angular.config.service'; import { OpenViduComponentsConfigService } from '../../services/config/directive-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 {
/** /**
@ -35,6 +36,11 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
*/ */
@Output() onLogoutRequested: EventEmitter<void> = new EventEmitter<void>(); @Output() onLogoutRequested: EventEmitter<void> = new EventEmitter<void>();
/**
* @internal
*/
title = '';
/** /**
* @internal * @internal
*/ */
@ -55,7 +61,9 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
* @internal * @internal
*/ */
recordingStatusEnum = RecordingStatus; recordingStatusEnum = RecordingStatus;
private adminSubscription: Subscription; private recordingsSub: Subscription;
private titleSub: Subscription;
/** /**
* @internal * @internal
*/ */
@ -76,7 +84,8 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
if (this.adminSubscription) this.adminSubscription.unsubscribe(); if (this.recordingsSub) this.recordingsSub.unsubscribe();
if (this.titleSub) this.titleSub.unsubscribe();
} }
/** /**
@ -245,7 +254,7 @@ export class AdminDashboardComponent implements OnInit, OnDestroy {
} }
private subscribeToAdminDirectives() { private subscribeToAdminDirectives() {
this.adminSubscription = this.libService.adminRecordingsList$.subscribe((recordings: RecordingInfo[]) => { this.recordingsSub = 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);
@ -255,5 +264,9 @@ 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"> {{ 'ADMIN.DASHBOARD_TITLE' | translate }} </mat-toolbar> <mat-toolbar class="header"> {{ title || ('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,4 +6,5 @@ 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-primary-color); background-color: var(--ov-background-color);
color: var(--ov-text-color); color: var(--ov-text-primary-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-color); background-color: var(--ov-secondary-action-color);
} }
.card-container { .card-container {
text-align: center; text-align: center;
background-color: var(--ov-panel-background); background-color: var(--ov-surface-color);
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-tertiary-color) !important; background-color: var(--ov-accent-action-color) !important;
color: var(--ov-text-color) !important; color: var(--ov-text-primary-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-tertiary-color); --mdc-circular-progress-active-indicator-color: var(--ov-accent-action-color);
} }

View File

@ -1,25 +0,0 @@
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,12 +2,13 @@ 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/openvidu-components-angular.config.service'; import { OpenViduComponentsConfigService } from '../../services/config/directive-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 {
/** /**
@ -20,6 +21,11 @@ export class AdminLoginComponent implements OnInit {
password: string; password: string;
}>(); }>();
/**
* @internal
*/
title: string;
/** /**
* @internal * @internal
*/ */
@ -36,6 +42,7 @@ export class AdminLoginComponent implements OnInit {
loginForm: FormGroup; loginForm: FormGroup;
private errorSub: Subscription; private errorSub: Subscription;
private titleSub: Subscription;
/** /**
* @internal * @internal
@ -64,6 +71,7 @@ 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();
} }
/** /**
@ -83,5 +91,9 @@ 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-tertiary-color); background-color: var(--ov-accent-action-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-video-radius); border-radius: var(--ov-surface-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-text-color); background: var(--ov-secondary-action-color);
border-radius: 8px; border-radius: 8px;
} }

View File

@ -1,25 +0,0 @@
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,6 +10,7 @@ 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-color); border: 2px solid var(--ov-text-primary-color);
color: #000000; color: #000000;
} }

View File

@ -1,25 +0,0 @@
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,7 +13,8 @@ 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-color); color: var(--ov-text-primary-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-tertiary-color); --mdc-circular-progress-active-indicator-color: var(--ov-accent-action-color);
} }
/* /*
@ -160,8 +160,8 @@ mat-spinner {
} }
#caption-settings-btn { #caption-settings-btn {
color: var(--ov-text-color); color: var(--ov-text-primary-color);
background-color: var(--ov-secondary-color); background-color: var(--ov-secondary-action-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-color); color: var(--ov-text-primary-color);
font-family: 'Roboto', arial, sans-serif; font-family: 'Roboto', arial, sans-serif;
} }
.caption-text { .caption-text {
background-color: var(--ov-logo-background-color); background-color: var(--ov-secondary-action-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

@ -1,25 +0,0 @@
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

@ -7,13 +7,37 @@ import { MatDialogRef } from '@angular/material/dialog';
@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 (click)="close()">{{'PANEL.RECORDING.CANCEL' | translate }}</button> <button mat-button [disableRipple]="true" (click)="close()">{{ 'PANEL.RECORDING.CANCEL' | translate }}</button>
<button mat-button cdkFocusInitial (click)="close(true)" id="delete-recording-confirm-btn">{{'PANEL.RECORDING.DELETE' | translate}}</button> <button [disableRipple]="true" mat-button cdkFocusInitial (click)="close(true)" id="delete-recording-confirm-btn">
{{ '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

@ -12,12 +12,35 @@ import { DialogData } from '../../models/dialog.model';
<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 (click)="close()">{{'PANEL.CLOSE' | translate}}</button> <button mat-button [disableRipple]="true" (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(public dialogRef: MatDialogRef<DialogTemplateComponent>, @Inject(MAT_DIALOG_DATA) public data: DialogData) {} constructor(
public dialogRef: MatDialogRef<DialogTemplateComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogData
) {}
close() { close() {
this.dialogRef.close(); this.dialogRef.close();

View File

@ -19,7 +19,8 @@ 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) {}
@ -29,6 +30,6 @@ export class ProFeatureDialogTemplateComponent {
} }
seeMore() { seeMore() {
window.open('https://docs.openvidu.io/en/stable/openvidu-pro/', '_blank')?.focus(); window.open('https://openvidu.io/pricing/#openvidu-pro', '_blank')?.focus();
} }
} }

View File

@ -12,17 +12,34 @@ import { RecordingDialogData } from '../../models/dialog.model';
<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 (click)="close()">{{ 'PANEL.CLOSE' | translate }}</button> <button mat-button [disableRipple]="true" (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,6 +19,11 @@
<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

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

View File

@ -1,37 +0,0 @@
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,3 +1,5 @@
import { LayoutAdditionalElementsDirective } from '../../directives/template/internals.directive';
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy, ChangeDetectionStrategy,
@ -11,14 +13,16 @@ import {
ViewChild, ViewChild,
ViewContainerRef ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { Subscription } from 'rxjs'; import { combineLatest, map, Subject, takeUntil } 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 { OpenViduComponentsConfigService } from '../../services/config/openvidu-components-angular.config.service'; import { GlobalConfigService } from '../../services/config/global-config.service';
import { OpenViduComponentsConfigService } from '../../services/config/directive-config.service';
import { LayoutTemplateConfiguration, TemplateManagerService } from '../../services/template/template-manager.service';
/** /**
* *
@ -29,7 +33,8 @@ import { OpenViduComponentsConfigService } from '../../services/config/openvidu-
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 {
/** /**
@ -37,6 +42,11 @@ 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
*/ */
@ -60,9 +70,27 @@ 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[] = [];
/** /**
@ -70,11 +98,11 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
*/ */
captionsEnabled = true; captionsEnabled = true;
private localParticipantSubs: Subscription; private _externalStream?: StreamDirective;
private remoteParticipantsSubs: Subscription; private _externalLayoutAdditionalElements?: LayoutAdditionalElementsDirective;
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;
@ -86,16 +114,21 @@ 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 libService: OpenViduComponentsConfigService, private globalService: GlobalConfigService,
private cd: ChangeDetectorRef private directiveService: OpenViduComponentsConfigService,
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();
@ -103,13 +136,11 @@ 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();
} }
@ -122,8 +153,36 @@ 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.captionsSubs = this.layoutService.captionsTogglingObs.subscribe((value: boolean) => { this.layoutService.captionsTogglingObs.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
this.captionsEnabled = value; this.captionsEnabled = value;
this.cd.markForCheck(); this.cd.markForCheck();
this.layoutService.update(); this.layoutService.update();
@ -131,7 +190,7 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
} }
private subscribeToParticipants() { private subscribeToParticipants() {
this.localParticipantSubs = this.participantService.localParticipant$.subscribe((p) => { this.participantService.localParticipant$.pipe(takeUntil(this.destroy$)).subscribe((p) => {
if (p) { if (p) {
this.localParticipant = p; this.localParticipant = p;
if (!this.localParticipant?.isMinimized) { if (!this.localParticipant?.isMinimized) {
@ -142,7 +201,14 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
} }
}); });
this.remoteParticipantsSubs = this.participantService.remoteParticipants$.subscribe((participants) => { combineLatest([this.participantService.remoteParticipants$, this.directiveService.layoutRemoteParticipants$])
.pipe(
map(([serviceParticipants, directiveParticipants]) =>
directiveParticipants !== undefined ? directiveParticipants : serviceParticipants
),
takeUntil(this.destroy$)
)
.subscribe((participants) => {
this.remoteParticipants = participants; this.remoteParticipants = participants;
this.layoutService.update(); this.layoutService.update();
this.cd.markForCheck(); this.cd.markForCheck();
@ -205,9 +271,10 @@ export class LayoutComponent implements OnInit, OnDestroy, AfterViewInit {
this.videoIsAtRight = false; this.videoIsAtRight = false;
} }
}; };
this.cdkSubscription = this.cdkDrag.released.subscribe(handler);
if (this.libService.getConfig().production) return; this.cdkDrag.released.pipe(takeUntil(this.destroy$)).subscribe(handler);
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: #ffffff; color: var(--ov-text-primary-color);
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;

View File

@ -1,25 +0,0 @@
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,7 +18,8 @@ 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,6 +17,8 @@
(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** |
|:--------------------------------: | :-------: | :---------------------------------------------: | |:--------------------------------: | :-------: | :---------------------------------------------: |
| **recordingActivity** | `boolean` | [ActivitiesPanelRecordingActivityDirective](../directives/ActivitiesPanelRecordingActivityDirective.html) |
| **broadcastingActivity** | `boolean` | [ActivitiesPanelBroadcastingActivityDirective](../directives/ActivitiesPanelBroadcastingActivityDirective.html) | | **broadcastingActivity** | `boolean` | [ActivitiesPanelBroadcastingActivityDirective](../directives/ActivitiesPanelBroadcastingActivityDirective.html) |
| **recordingActivity** | `boolean` | [ActivitiesPanelRecordingActivityDirective](../directives/ActivitiesPanelRecordingActivityDirective.html) |
<!-- end-dynamic-api-directives-content --> <!-- end-dynamic-api-directives-content -->

View File

@ -1,4 +1,6 @@
:host{ $ov-activity-status-color: #afafaf;
:host {
.activities-body-container { .activities-body-container {
display: block !important; display: block !important;
overflow-y: auto; overflow-y: auto;
@ -6,16 +8,26 @@
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: var(--ov-light-color); background-color: $ov-activity-status-color;;
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-radius);
margin: 10px 0px !important; margin: 10px 0px !important;
padding: 10px; padding: 10px;
} }
.activity-title, .activity-subtitle { .activity-title,
color: var(--ov-panel-text-color); .activity-subtitle {
color: var(--ov-text-surface-color);
} }
.activity-subtitle { .activity-subtitle {
@ -46,6 +58,11 @@
::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,
@ -53,6 +70,10 @@
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;
@ -63,14 +84,16 @@
::ng-deep .mat-expansion-panel { ::ng-deep .mat-expansion-panel {
box-shadow: none !important; box-shadow: none !important;
background-color: var(--ov-panel-background) !important; background-color: var(--ov-surface-color) !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 .mdc-list-item--with-leading-icon.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{ ::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 {
max-height: 24px; max-height: 24px;
height: 24px; height: 24px;
} }

View File

@ -1,25 +0,0 @@
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 { Subscription } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { PanelStatusInfo, PanelType } from '../../../models/panel.model'; import { PanelStatusInfo, PanelType } from '../../../models/panel.model';
import { OpenViduComponentsConfigService } from '../../../services/config/openvidu-components-angular.config.service'; import { OpenViduComponentsConfigService } from '../../../services/config/directive-config.service';
import { PanelService } from '../../../services/panel/panel.service'; import { PanelService } from '../../../services/panel/panel.service';
import { import {
RecordingDeleteRequestedEvent, RecordingDeleteRequestedEvent,
@ -20,7 +20,8 @@ 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 {
/** /**
@ -53,6 +54,21 @@ 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.
@ -79,9 +95,7 @@ export class ActivitiesPanelComponent implements OnInit {
* @internal * @internal
*/ */
showBroadcastingActivity: boolean = true; showBroadcastingActivity: boolean = true;
private panelSubscription: Subscription; private destroy$ = new Subject<void>();
private recordingActivitySub: Subscription;
private broadcastingActivitySub: Subscription;
/** /**
* @internal * @internal
@ -104,9 +118,8 @@ export class ActivitiesPanelComponent implements OnInit {
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
if (this.panelSubscription) this.panelSubscription.unsubscribe(); this.destroy$.next();
if (this.recordingActivitySub) this.recordingActivitySub.unsubscribe(); this.destroy$.complete();
if (this.broadcastingActivitySub) this.broadcastingActivitySub.unsubscribe();
} }
/** /**
@ -117,7 +130,7 @@ export class ActivitiesPanelComponent implements OnInit {
} }
private subscribeToPanelToggling() { private subscribeToPanelToggling() {
this.panelSubscription = this.panelService.panelStatusObs.subscribe((ev: PanelStatusInfo) => { this.panelService.panelStatusObs.pipe(takeUntil(this.destroy$)).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;
} }
@ -125,12 +138,12 @@ export class ActivitiesPanelComponent implements OnInit {
} }
private subscribeToActivitiesPanelDirective() { private subscribeToActivitiesPanelDirective() {
this.recordingActivitySub = this.libService.recordingActivity$.subscribe((value: boolean) => { this.libService.recordingActivity$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
this.showRecordingActivity = value; this.showRecordingActivity = value;
this.cd.markForCheck(); this.cd.markForCheck();
}); });
this.broadcastingActivitySub = this.libService.broadcastingActivity$.subscribe((value: boolean) => { this.libService.broadcastingActivity$.pipe(takeUntil(this.destroy$)).subscribe((value: boolean) => {
this.showBroadcastingActivity = value; this.showBroadcastingActivity = value;
this.cd.markForCheck(); this.cd.markForCheck();
}); });

View File

@ -1,4 +1,9 @@
<mat-expansion-panel (opened)="setPanelOpened(true)" (closed)="setPanelOpened(false)" [expanded]="expanded" [ngClass]="{ 'no-body': !isPanelOpened }"> <mat-expansion-panel
(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>
@ -9,7 +14,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,
pending: starting:
broadcastingStatus === broadcastingStatusEnum.STARTING || broadcastingStatus === broadcastingStatusEnum.STOPPING broadcastingStatus === broadcastingStatusEnum.STARTING || broadcastingStatus === broadcastingStatusEnum.STOPPING
}" }"
> >
@ -29,11 +34,12 @@
<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,
pending: starting:
broadcastingStatus === broadcastingStatusEnum.STARTING || broadcastingStatus === broadcastingStatusEnum.STARTING ||
broadcastingStatus === broadcastingStatusEnum.STOPPING broadcastingStatus === broadcastingStatusEnum.STOPPING
}" }"

View File

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

@ -1,23 +0,0 @@
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 { Subscription } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { import {
BroadcastingStartRequestedEvent, BroadcastingStartRequestedEvent,
BroadcastingStatus, BroadcastingStatus,
@ -18,7 +18,8 @@ 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
@ -75,7 +76,7 @@ export class BroadcastingActivityComponent implements OnInit {
*/ */
isPanelOpened: boolean = false; isPanelOpened: boolean = false;
private broadcastingSub: Subscription; private destroy$ = new Subject<void>();
/** /**
* @internal * @internal
@ -98,7 +99,8 @@ export class BroadcastingActivityComponent implements OnInit {
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
if (this.broadcastingSub) this.broadcastingSub.unsubscribe(); this.destroy$.next();
this.destroy$.complete();
} }
/** /**
@ -146,7 +148,7 @@ export class BroadcastingActivityComponent implements OnInit {
} }
private subscribeToBroadcastingStatus() { private subscribeToBroadcastingStatus() {
this.broadcastingSub = this.broadcastingService.broadcastingStatusObs.subscribe((event: BroadcastingStatusInfo | undefined) => { this.broadcastingService.broadcastingStatusObs.pipe(takeUntil(this.destroy$)).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,10 +1,8 @@
:host { :host {
#recording-status { $ov-activity-status-color: #afafaf;
color: var(--ov-text-color); .recording-title,
display: inline; .recording-subtitle {
padding: 3px; color: var(--ov-text-surface-color);
font-size: 11px;
border-radius: var(--ov-panel-radius);
} }
.recording-title { .recording-title {
@ -23,14 +21,15 @@
flex-wrap: wrap; flex-wrap: wrap;
height: auto; height: auto;
align-content: center; align-content: center;
color: var(--ov-panel-text-color) !important; color: var(--ov-text-surface-color) !important;
} }
.recording-duration { .recording-duration {
background-color: var(--ov-light-color); background-color: $ov-activity-status-color;
padding: 4px 8px; padding: 4px 8px;
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-radius);
font-weight: 500; font-weight: 500;
color: var(--ov-text-surface-color);
} }
.recording-duration mat-icon { .recording-duration mat-icon {
@ -41,22 +40,22 @@
.started { .started {
background-color: #3b7430 !important; background-color: #3b7430 !important;
color: var(--ov-text-color); color: #ffffff !important;
} }
.activity-icon.started, .activity-icon.started,
.failed { .failed {
background-color: var(--ov-warn-color) !important; background-color: var(--ov-error-color) !important;
color: var(--ov-text-color); // color: var(--ov-secondary-action-color);
} }
.stopped { .stopped {
background-color: var(--ov-light-color); // background-color: var(--ov-secondary-action-color);
color: var(--ov-panel-text-color) !important; color: var(--ov-text-surface-color) !important;
} }
.pending { .starting {
background-color: #ffd79b !important; background-color: var(--ov-warn-color) !important;
color: var(--ov-panel-text-color) !important; color: #000000 !important;
} }
.panel-body-container { .panel-body-container {
@ -73,18 +72,458 @@
text-align: center; text-align: center;
} }
.recording-error { .recording-placeholder {
color: var(--ov-warn-color); display: flex;
font-weight: 600; justify-content: center;
} margin-bottom: 20px;
.recording-name {
font-size: 14px;
font-weight: bold;
} }
.recording-date { .recording-placeholder-img {
font-size: 12px !important; max-width: 100%;
font-style: italic; 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 {
color: var(--ov-error-color);
font-weight: 600;
}
.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;
margin: 0;
opacity: 0.7;
line-height: 1.4;
} }
.not-allowed-message { .not-allowed-message {
@ -93,29 +532,44 @@
} }
.recording-action-buttons { .recording-action-buttons {
margin-top: 20px; margin: 5px 0px;
margin-bottom: 20px;
} }
#start-recording-btn { #start-recording-btn {
width: 100%; width: 100%;
background-color: var(--ov-tertiary-color); background-color: var(--ov-primary-action-color);
color: var(--ov-text-color); color: var(--ov-secondary-action-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-warn-color); background-color: var(--ov-error-color);
color: var(--ov-text-color); color: var(--ov-secondary-action-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-light-color); background-color: var(--ov-accent-action-color);
border-radius: var(--ov-surface-radius);
} }
.recording-item { .recording-item {
@ -133,6 +587,7 @@
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,
@ -140,6 +595,13 @@
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;
} }
@ -156,6 +618,18 @@
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

@ -1,25 +0,0 @@
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, Output } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, OnDestroy, Output } from '@angular/core';
import { Subscription } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { import {
RecordingDeleteRequestedEvent, RecordingDeleteRequestedEvent,
RecordingDownloadClickedEvent, RecordingDownloadClickedEvent,
@ -16,6 +16,7 @@ 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.
@ -24,13 +25,14 @@ import { LoggerService } from '../../../../services/logger/logger.service';
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 { export class RecordingActivityComponent implements OnInit, OnDestroy {
/** /**
* @internal * @internal
*/ */
@ -66,6 +68,20 @@ export class RecordingActivityComponent 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
* 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
*/ */
@ -98,12 +114,53 @@ export class RecordingActivityComponent implements OnInit {
*/ */
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 recordingStatusSubscription: Subscription; private destroy$ = new Subject<void>();
/** /**
* @internal * @internal
@ -114,7 +171,8 @@ export class RecordingActivityComponent implements OnInit {
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');
} }
@ -124,13 +182,23 @@ export class RecordingActivityComponent implements OnInit {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.subscribeToRecordingStatus(); this.subscribeToRecordingStatus();
this.subscribeToTracksChanges();
this.subscribeToConfigChanges();
} }
/** /**
* @internal * @internal
*/ */
ngOnDestroy() { ngOnDestroy() {
if (this.recordingStatusSubscription) this.recordingStatusSubscription.unsubscribe(); this.destroy$.next();
this.destroy$.complete();
}
/**
* @internal
*/
trackByRecordingId(index: number, recording: RecordingInfo): string | undefined {
return recording.id;
} }
/** /**
@ -225,11 +293,104 @@ export class RecordingActivityComponent implements OnInit {
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.recordingStatusSubscription = this.recordingService.recordingStatusObs.subscribe((event: RecordingStatusInfo) => { this.recordingService.recordingStatusObs.pipe(takeUntil(this.destroy$)).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 {
this.recordingList = recordingList.filter((rec) => rec.status === RecordingStatus.STARTED);
}
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) {
@ -238,4 +399,24 @@ export class RecordingActivityComponent implements OnInit {
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>{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4> <h4 class="background-title">{{ 'PANEL.BACKGROUND.BLURRED_SECTION' | translate }}</h4>
<div> <div>
<button <button
*ngFor="let effect of noEffectAndBlurredBackground" *ngFor="let effect of noEffectAndBlurredBackground"
@ -29,12 +29,13 @@
</div> </div>
<hr /> <hr />
<div> <div>
<h4>{{ 'PANEL.BACKGROUND.IMAGES_SECTION' | translate }}</h4> <h4 class="background-title">{{ '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,3 +1,6 @@
.background-title {
color: var(--ov-text-surface-color);
}
.effects-container { .effects-container {
display: block !important; display: block !important;
overflow-y: auto; overflow-y: auto;
@ -7,8 +10,9 @@
.effect-button { .effect-button {
margin: 5px; margin: 5px;
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-radius);
background-color: var(--ov-light-color); background-color: var(--ov-secondary-action-color);
color: var(--ov-primary-action-color);
width: 60px; width: 60px;
height: 60px; height: 60px;
line-height: inherit; line-height: inherit;
@ -19,7 +23,7 @@
} }
.active-effect-btn { .active-effect-btn {
border: 2px solid var(--ov-tertiary-color); border: 2px solid var(--ov-accent-action-color);
} }
#hard_blur-btn .mat-icon { #hard_blur-btn .mat-icon {
@ -34,7 +38,7 @@
.grid img { .grid img {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-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

@ -1,25 +0,0 @@
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,7 +12,8 @@ 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;
@ -56,10 +57,6 @@ export class BackgroundEffectsPanelComponent implements OnInit {
} }
async applyBackground(effect: BackgroundEffect) { async applyBackground(effect: BackgroundEffect) {
if (effect.type === EffectType.NONE) {
await this.backgroundService.removeBackground();
} else {
await this.backgroundService.applyBackground(effect); 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="msg-content"> <div class="chat-message">
<p [innerHTML]="data.message | linkify"></p> <p [innerHTML]="data.message | linkify"></p>
</div> </div>
</div> </div>

View File

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

View File

@ -1,27 +0,0 @@
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 { Subscription } from 'rxjs'; import { Subject, takeUntil } 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,7 +13,8 @@ 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 {
/** /**
@ -33,7 +34,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
*/ */
messageList: ChatMessage[] = []; messageList: ChatMessage[] = [];
private chatMessageSubscription: Subscription; private destroy$ = new Subject<void>();
/** /**
* @ignore * @ignore
@ -65,7 +66,8 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
* @ignore * @ignore
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.chatMessageSubscription) this.chatMessageSubscription.unsubscribe(); this.destroy$.next();
this.destroy$.complete();
} }
/** /**
@ -108,7 +110,7 @@ export class ChatPanelComponent implements OnInit, AfterViewInit {
} }
private subscribeToMessages() { private subscribeToMessages() {
this.chatMessageSubscription = this.chatService.messagesObs.subscribe((messages: ChatMessage[]) => { this.chatService.messagesObs.pipe(takeUntil(this.destroy$)).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-panel-background); background-color: var(--ov-surface-color);
border-radius: var(--ov-panel-radius); border-radius: var(--ov-surface-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,6 +16,11 @@
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;
@ -24,7 +29,7 @@
.panel-close-button { .panel-close-button {
margin-left: auto; margin-left: auto;
border-radius: var(--ov-buttons-radius); border-radius: 50%;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
@ -41,6 +46,6 @@
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: var(--ov-light-color); background: var(--ov-secondary-action-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, Subscription } from 'rxjs'; import { skip, Subject, takeUntil } from 'rxjs';
import { import {
ActivitiesPanelDirective, ActivitiesPanelDirective,
AdditionalPanelsDirective, AdditionalPanelsDirective,
@ -25,6 +25,7 @@ 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';
/** /**
* *
@ -37,7 +38,8 @@ import { BackgroundEffect } from '../../models/background-effect.model';
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 {
/** /**
@ -74,42 +76,20 @@ export class PanelComponent implements OnInit {
*/ */
@ContentChild(ParticipantsPanelDirective) @ContentChild(ParticipantsPanelDirective)
set externalParticipantPanel(externalParticipantsPanel: ParticipantsPanelDirective) { set externalParticipantPanel(externalParticipantsPanel: ParticipantsPanelDirective) {
// This directive will has value only when PARTICIPANTS PANEL component tagged with '*ovParticipantsPanel' this._externalParticipantPanel = externalParticipantsPanel;
// is inside of the PANEL component tagged with '*ovPanel'
if (externalParticipantsPanel) { if (externalParticipantsPanel) {
this.participantsPanelTemplate = externalParticipantsPanel.template; this.updateTemplatesAndMarkForCheck();
} }
} }
// 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 directive will has value only when ACTIVITIES PANEL component tagged with '*ovActivitiesPanel' this._externalActivitiesPanel = externalActivitiesPanel;
// is inside of the PANEL component tagged with '*ovPanel'
if (externalActivitiesPanel) { if (externalActivitiesPanel) {
this.activitiesPanelTemplate = externalActivitiesPanel.template; this.updateTemplatesAndMarkForCheck();
} }
} }
@ -118,10 +98,9 @@ export class PanelComponent implements OnInit {
*/ */
@ContentChild(ChatPanelDirective) @ContentChild(ChatPanelDirective)
set externalChatPanel(externalChatPanel: ChatPanelDirective) { set externalChatPanel(externalChatPanel: ChatPanelDirective) {
// This directive will has value only when CHAT PANEL component tagged with '*ovChatPanel' this._externalChatPanel = externalChatPanel;
// is inside of the PANEL component tagged with '*ovPanel'
if (externalChatPanel) { if (externalChatPanel) {
this.chatPanelTemplate = externalChatPanel.template; this.updateTemplatesAndMarkForCheck();
} }
} }
@ -130,10 +109,9 @@ export class PanelComponent implements OnInit {
*/ */
@ContentChild(AdditionalPanelsDirective) @ContentChild(AdditionalPanelsDirective)
set externalAdditionalPanels(externalAdditionalPanels: AdditionalPanelsDirective) { set externalAdditionalPanels(externalAdditionalPanels: AdditionalPanelsDirective) {
// This directive will has value only when ADDITIONAL PANELS component tagged with '*ovPanelAdditionalPanels' this._externalAdditionalPanels = externalAdditionalPanels;
// is inside of the PANEL component tagged with '*ovPanel'
if (externalAdditionalPanels) { if (externalAdditionalPanels) {
this.additionalPanelsTemplate = externalAdditionalPanels.template; this.updateTemplatesAndMarkForCheck();
} }
} }
@ -194,7 +172,20 @@ 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,
@ -206,30 +197,78 @@ 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;
if (this.panelSubscription) this.panelSubscription.unsubscribe(); this.destroy$.next();
this.destroy$.complete();
} }
private subscribeToPanelToggling() { private subscribeToPanelToggling() {
this.panelSubscription = this.panelService.panelStatusObs.pipe(skip(1)).subscribe((ev: PanelStatusInfo) => { this.panelService.panelStatusObs.pipe(skip(1), takeUntil(this.destroy$)).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,33 +1,71 @@
<mat-list> <mat-list>
<mat-list-item> <mat-list-item>
<div matListItemIcon class="participant-avatar" [style.background-color]="_participant.colorProfile"> <!-- Main participant container with improved structure -->
<div class="participant-container" [attr.data-participant-id]="_participant?.sid">
<!-- Avatar section with dynamic color -->
<div
class="participant-avatar"
[style.background-color]="_participant?.colorProfile"
[attr.aria-label]="'Avatar for ' + participantDisplayName"
>
<mat-icon>person</mat-icon> <mat-icon>person</mat-icon>
</div> </div>
<h3 matListItemTitle class="participant-name">{{ _participant.name }}
<span *ngIf="_participant.isLocal"> ({{ 'PANEL.PARTICIPANTS.YOU' | translate }})</span>
</h3>
<p matListItemLine class="participant-subtitle">{{ _participant | tracksPublishedTypes }}</p>
<!-- <p matListItemLine>
<span class="participant-subtitle"></span>
</p> -->
<div class="participant-action-buttons" matListItemMeta> <!-- Content section with name and status -->
<div class="participant-content">
<div class="participant-name">
{{ participantDisplayName }}
<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 <button
mat-icon-button mat-icon-button
id="mute-btn" id="mute-btn"
*ngIf="!_participant.isLocal && showMuteButton" *ngIf="!isLocalParticipant && showMuteButton"
[class.warn-btn]="_participant.isMutedForcibly" [class.warn-btn]="_participant?.isMutedForcibly"
(click)="toggleMuteForcibly()" (click)="toggleMuteForcibly()"
[disabled]="!_participant"
[disableRipple]="true" [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_up</mat-icon>
<mat-icon *ngIf="_participant.isMutedForcibly">volume_off</mat-icon> <mat-icon *ngIf="_participant?.isMutedForcibly">volume_off</mat-icon>
</button> </button>
<!-- External item elements --> <!-- External item elements with improved structure -->
<ng-container *ngIf="participantPanelItemElementsTemplate"> <div class="external-elements" *ngIf="hasExternalElements">
<ng-container *ngTemplateOutlet="participantPanelItemElementsTemplate"></ng-container> <ng-container *ngTemplateOutlet="participantPanelItemElementsTemplate"></ng-container>
</ng-container> </div>
</div>
</div> </div>
</mat-list-item> </mat-list-item>
</mat-list> </mat-list>

View File

@ -1,59 +1,443 @@
: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: inherit; display: flex;
border-radius: var(--ov-panel-radius); align-items: center;
margin: auto !important; justify-content: center;
padding: 10px; width: 40px;
color: var(--ov-panel-text-color); height: 40px;
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;
}
} }
.participant-subtitle { // Main content area
font-style: italic; .participant-content {
font-size: 11px !important; flex: 1;
margin: 0; display: flex;
flex-direction: column;
min-width: 0; // Allows text truncation
margin-right: 8px;
} }
// Participant name styling
.participant-name { .participant-name {
font-weight: bold !important; font-weight: 600 !important;
color: var(--ov-panel-text-color); 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 {
font-style: normal;
font-size: 12px !important;
font-weight: 400;
margin: 0;
color: var(--ov-text-secondary, #757575);
line-height: 1.3;
display: flex;
align-items: center;
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;
} }
::ng-deep .participant-action-buttons > *:not(#mute-btn) { // Mute button styling
display: contents; #mute-btn {
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);
} }
::ng-deep .participant-action-buttons > *:not(#mute-btn) > * { &:focus {
margin: auto; 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
.participant-badges {
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: max-content !important; height: auto !important;
padding-bottom: 10px !important; padding: 0 !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-left: 10px !important; padding: 0 !important;
align-self: center !important; align-self: stretch !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 {
background-color: transparent !important;
}
// Animations
@keyframes fadeIn {
from {
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 { #mute-btn {
border-radius: var(--ov-buttons-radius); width: 28px;
height: 28px;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
} }
.warn-btn { .after-local-content {
/* background-color: var(--ov-warn-color) !important; */ margin-top: 10px;
color: var(--ov-warn-color); 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

@ -1,25 +0,0 @@
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,21 +1,23 @@
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/openvidu-components-angular.config.service'; import { OpenViduComponentsConfigService } from '../../../../services/config/directive-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 is in charge of displaying the participants information inside of the ParticipansPanelComponent. * It displays participant information with enhanced UI/UX, including support for custom content
* 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 {
/** /**
@ -34,40 +36,69 @@ export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
*/ */
@ContentChild(ParticipantPanelItemElementsDirective) @ContentChild(ParticipantPanelItemElementsDirective)
set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) { set externalItemElements(externalItemElements: ParticipantPanelItemElementsDirective) {
// This directive will has value only when ITEM ELEMENTS component tagget with '*ovParticipantPanelItemElements' directive this._externalItemElements = externalItemElements;
// is inside of the P PANEL ITEM component tagged with '*ovParticipantPanelItem' directive
if (externalItemElements) { if (externalItemElements) {
this.participantPanelItemElementsTemplate = externalItemElements.template; this.updateTemplatesAndMarkForCheck();
} }
} }
/** /**
* The participant to be displayed
* @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() @Input()
set participant(participant: ParticipantModel) { set participant(participant: ParticipantModel) {
this._participant = participant; this._participant = participant;
this.cd.markForCheck();
} }
/** /**
* @ignore * @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();
} }
@ -79,14 +110,72 @@ export class ParticipantPanelItemComponent implements OnInit, OnDestroy {
} }
/** /**
* @ignore * Toggles the mute state of a remote participant
*/ */
toggleMuteForcibly() { toggleMuteForcibly() {
if (this._participant) { if (this._participant && !this._participant.isLocal) {
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;

View File

@ -7,14 +7,13 @@
</div> </div>
<div class="scrollable"> <div class="scrollable">
<div class="local-participant-container" *ngIf="localParticipant"> <div class="local-participant-container" *ngIf="localParticipant">
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: localParticipant }"></ng-container> <ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: localParticipant }"></ng-container>
<mat-divider *ngIf="true"></mat-divider> <mat-divider *ngIf="true"></mat-divider>
</div> </div>
<ng-container *ngTemplateOutlet="participantPanelAfterLocalParticipantTemplate"></ng-container>
<div class="remote-participants-container" id="remote-participants-container" *ngIf="remoteParticipants.length > 0"> <div class="remote-participants-container" id="remote-participants-container" *ngIf="remoteParticipants.length > 0">
<div *ngFor="let participant of this.remoteParticipants" id="remote-participant-item"> <div *ngFor="let participant of this.remoteParticipants" id="remote-participant-item">
<ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: participant }"></ng-container> <ng-container *ngTemplateOutlet="participantPanelItemTemplate; context: { $implicit: participant }"></ng-container>
</div> </div>

View File

@ -8,16 +8,3 @@
max-height: calc(100% - 60px); max-height: calc(100% - 60px);
overflow: auto; overflow: auto;
} }
.message-container {
padding: 5px;
background-color: var(--ov-light-color);
color: var(--ov-panel-text-color);
text-align: center;
margin: 5px 5px;
font-size: 12px;
}
.message-container p {
margin: 0;
}

View File

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

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