ov-components: Added events e2e tests

master
Carlos Santos 2025-05-20 18:54:54 +02:00
parent bd3504b818
commit 02b594e405
7 changed files with 851 additions and 36 deletions

View File

@ -245,10 +245,9 @@ jobs:
if: always() if: always()
uses: ./.github/actions/cleanup uses: ./.github/actions/cleanup
webcomponent_e2e_events: e2e_events:
needs: test_setup needs: test_setup
name: Webcomponent events name: Events E2E
if: false
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repository - name: Checkout Repository
@ -269,26 +268,12 @@ jobs:
uses: ./.github/actions/setup-local-deployment uses: ./.github/actions/setup-local-deployment
- name: Setup OpenVidu Call Backend - name: Setup OpenVidu Call Backend
uses: ./.github/actions/setup-openvidu-call-backend uses: ./.github/actions/setup-openvidu-call-backend
- name: Install dependencies - name: Build and Serve openvidu-components-angular Testapp
run: | uses: ./.github/actions/build-and-serve-components-testapp
cd openvidu-components-angular - name: Run Tests
npm install
- name: Build openvidu-angular
run: npm run lib:build --prefix openvidu-components-angular
- name: Build openvidu-webcomponent
run: npm run webcomponent:testing-build --prefix openvidu-components-angular
- name: Serve Webcomponent Testapp
run: npm run webcomponent:serve-testapp --prefix openvidu-components-angular &
- name: Wait for openvidu-components-angular Testapp
run: |
until curl -s -f -o /dev/null http://localhost:8080; do
echo "Waiting for openvidu-components-angular Testapp to be ready..."
sleep 5
done
- name: Run Webcomponent E2E
env: env:
LAUNCH_MODE: CI LAUNCH_MODE: CI
run: npm run e2e:webcomponent-events --prefix openvidu-components-angular run: npm run e2e:lib-events --prefix openvidu-components-angular
- name: Cleanup - name: Cleanup
if: always() if: always()
uses: ./.github/actions/cleanup uses: ./.github/actions/cleanup

View File

@ -0,0 +1,645 @@
import { Builder, Key, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Testing videoconference EVENTS', () => {
let browser: WebDriver;
let utils: OpenViduComponentsPO;
const isHeadless: boolean = (TestAppConfig.browserOptions as any).options_.args.includes('--headless');
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);
});
afterEach(async () => {
try {
// leaving room if connected
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should receive the onReadyToJoin event', async () => {
await browser.get(`${url}`);
await utils.waitForElement('#prejoin-container');
expect(await utils.isPresent('#prejoin-container')).toBeTrue();
// Clicking to join button
await utils.waitForElement('#join-button');
await utils.clickOn('#join-button');
// Checking if onReadyToJoin has been received
await utils.waitForElement('#onReadyToJoin');
expect(await utils.isPresent('#onReadyToJoin')).toBeTrue();
});
it('should receive the onTokenRequested event', async () => {
await browser.get(`${url}`);
await utils.waitForElement('#prejoin-container');
expect(await utils.isPresent('#prejoin-container')).toBeTrue();
// Clicking to join button
await utils.waitForElement('#join-button');
await utils.clickOn('#join-button');
// Checking if onTokenRequested has been received
await utils.waitForElement('#onTokenRequested');
expect(await utils.isPresent('#onTokenRequested')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event when clicking on the prejoin', async () => {
await browser.get(url);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#camera-button');
await utils.clickOn('#camera-button');
// Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event when clicking on the toolbar', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
await utils.waitForElement('#camera-btn');
await utils.clickOn('#camera-btn');
// Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue();
await utils.clickOn('#camera-btn');
await utils.waitForElement('#onVideoEnabledChanged-true');
expect(await utils.isPresent('#onVideoEnabledChanged-true')).toBeTrue();
});
it('should receive the onVideoEnabledChanged event when clicking on the settings panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.clickOn('#video-opt');
await utils.waitForElement('ov-video-devices-select');
await utils.clickOn('ov-video-devices-select #camera-button');
// Checking if onVideoEnabledChanged has been received
await utils.waitForElement('#onVideoEnabledChanged-false');
expect(await utils.isPresent('#onVideoEnabledChanged-false')).toBeTrue();
await utils.clickOn('ov-video-devices-select #camera-button');
await utils.waitForElement('#onVideoEnabledChanged-true');
expect(await utils.isPresent('#onVideoEnabledChanged-true')).toBeTrue();
});
it('should receive the onVideoDeviceChanged event on prejoin', async () => {
await browser.get(`${url}&fakeDevices=true`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#video-devices-form');
await utils.clickOn('#video-devices-form');
await utils.waitForElement('#option-custom_fake_video_1');
await utils.clickOn('#option-custom_fake_video_1');
await utils.waitForElement('#onVideoDeviceChanged');
expect(await utils.isPresent('#onVideoDeviceChanged')).toBeTrue();
});
it('should receive the onVideoDeviceChanged event on settings panel', async () => {
await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.clickOn('#video-opt');
await utils.waitForElement('ov-video-devices-select');
await utils.waitForElement('#video-devices-form');
await utils.clickOn('#video-devices-form');
await utils.waitForElement('#option-custom_fake_video_1');
await utils.clickOn('#option-custom_fake_video_1');
await utils.waitForElement('#onVideoDeviceChanged');
expect(await utils.isPresent('#onVideoDeviceChanged')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event when clicking on the prejoin', async () => {
await browser.get(url);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#microphone-button');
await utils.clickOn('#microphone-button');
// Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event when clicking on the toolbar', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
await utils.waitForElement('#mic-btn');
await utils.clickOn('#mic-btn');
// Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue();
await utils.clickOn('#mic-btn');
await utils.waitForElement('#onAudioEnabledChanged-true');
expect(await utils.isPresent('#onAudioEnabledChanged-true')).toBeTrue();
});
it('should receive the onAudioEnabledChanged event when clicking on the settings panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.clickOn('#audio-opt');
await utils.waitForElement('ov-audio-devices-select');
await utils.clickOn('ov-audio-devices-select #microphone-button');
// Checking if onAudioEnabledChanged has been received
await utils.waitForElement('#onAudioEnabledChanged-false');
expect(await utils.isPresent('#onAudioEnabledChanged-false')).toBeTrue();
await utils.clickOn('ov-audio-devices-select #microphone-button');
await utils.waitForElement('#onAudioEnabledChanged-true');
expect(await utils.isPresent('#onAudioEnabledChanged-true')).toBeTrue();
});
it('should receive the onAudioDeviceChanged event on prejoin', async () => {
await browser.get(`${url}&fakeDevices=true`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#audio-devices-form');
await utils.clickOn('#audio-devices-form');
await utils.waitForElement('#option-custom_fake_audio_1');
await utils.clickOn('#option-custom_fake_audio_1');
await utils.waitForElement('#onAudioDeviceChanged');
expect(await utils.isPresent('#onAudioDeviceChanged')).toBeTrue();
});
it('should receive the onAudioDeviceChanged event on settings panel', async () => {
await browser.get(`${url}&prejoin=false&fakeDevices=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.clickOn('#audio-opt');
await utils.waitForElement('ov-audio-devices-select');
await utils.waitForElement('#audio-devices-form');
await utils.clickOn('#audio-devices-form');
await utils.waitForElement('#option-custom_fake_audio_1');
await utils.clickOn('#option-custom_fake_audio_1');
await utils.waitForElement('#onAudioDeviceChanged');
expect(await utils.isPresent('#onAudioDeviceChanged')).toBeTrue();
});
it('should receive the onLangChanged event on prejoin', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
await utils.waitForElement('#lang-btn-compact');
await utils.clickOn('#lang-btn-compact');
await browser.sleep(500);
await utils.clickOn('#lang-opt-es');
await browser.sleep(500);
await utils.waitForElement('#onLangChanged-es');
expect(await utils.isPresent('#onLangChanged-es')).toBeTrue();
});
it('should receive the onLangChanged event on settings panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
await browser.sleep(500);
await utils.waitForElement('#settings-container');
await utils.waitForElement('.lang-button');
await utils.clickOn('.lang-button');
await browser.sleep(500);
await utils.clickOn('#lang-opt-es');
await browser.sleep(500);
await utils.waitForElement('#onLangChanged-es');
expect(await utils.isPresent('#onLangChanged-es')).toBeTrue();
});
it('should receive the onScreenShareEnabledChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
const screenshareButton = await utils.waitForElement('#screenshare-btn');
expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
await screenshareButton.click();
// Checking if onScreenShareEnabledChanged has been received
await utils.waitForElement('#onScreenShareEnabledChanged');
expect(await utils.isPresent('#onScreenShareEnabledChanged')).toBeTrue();
});
// With headless mode, the Fullscreen API doesn't work
it('should receive the onFullscreenEnabledChanged event', async () => {
let element;
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.toggleFullscreenFromToolbar();
await browser.sleep(500);
// Checking if onFullscreenEnabledChanged has been received
await utils.waitForElement('#onFullscreenEnabledChanged-true');
expect(await utils.isPresent('#onFullscreenEnabledChanged-true')).toBeTrue();
await (await utils.waitForElement('html')).sendKeys(Key.F11);
await browser.sleep(500);
await utils.waitForElement('#onFullscreenEnabledChanged-false');
expect(await utils.isPresent('#onFullscreenEnabledChanged-false')).toBeTrue();
});
it('should receive the onChatPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('chat');
// Checking if onChatPanelStatusChanged has been received
await utils.waitForElement('#onChatPanelStatusChanged-true');
expect(await utils.isPresent('#onChatPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('chat');
// Checking if onChatPanelStatusChanged has been received
await utils.waitForElement('#onChatPanelStatusChanged-false');
expect(await utils.isPresent('#onChatPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onParticipantsPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('participants');
// Checking if onParticipantsPanelStatusChanged has been received
await utils.waitForElement('#onParticipantsPanelStatusChanged-true');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('participants');
// Checking if onParticipantsPanelStatusChanged has been received
await utils.waitForElement('#onParticipantsPanelStatusChanged-false');
expect(await utils.isPresent('#onParticipantsPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onActivitiesPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
// Checking if onActivitiesPanelStatusChanged has been received
await utils.waitForElement('#onActivitiesPanelStatusChanged-true');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('activities');
// Checking if onActivitiesPanelStatusChanged has been received
await utils.waitForElement('#onActivitiesPanelStatusChanged-false');
expect(await utils.isPresent('#onActivitiesPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onSettingsPanelStatusChanged event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('settings');
// Checking if onSettingsPanelStatusChanged has been received
await utils.waitForElement('#onSettingsPanelStatusChanged-true');
expect(await utils.isPresent('#onSettingsPanelStatusChanged-true')).toBeTrue();
await utils.togglePanel('settings');
// Checking if onSettingsPanelStatusChanged has been received
await utils.waitForElement('#onSettingsPanelStatusChanged-false');
expect(await utils.isPresent('#onSettingsPanelStatusChanged-false')).toBeTrue();
});
it('should receive the onRecordingStartRequested event when clicking toolbar button', async () => {
const roomName = 'recordingToolbarEvent';
await browser.get(`${url}&prejoin=false&roomName=${roomName}`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.toggleRecordingFromToolbar();
// Checking if onRecordingStartRequested has been received
await utils.waitForElement(`#onRecordingStartRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).toBeTrue();
});
xit('should receive the onRecordingStopRequested event when clicking toolbar button', async () => {});
xit('should receive the onBroadcastingStopRequested event when clicking toolbar button', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.toggleToolbarMoreOptions();
await utils.waitForElement('#broadcasting-btn');
await utils.clickOn('#broadcasting-btn');
await browser.sleep(500);
await utils.waitForElement('.sidenav-menu');
await utils.waitForElement('#activities-container');
await utils.waitForElement('#broadcasting-url-input');
const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys('BroadcastUrl');
await utils.clickOn('#broadcasting-btn');
// Open more options menu
await utils.toggleToolbarMoreOptions();
await utils.waitForElement('#broadcasting-btn');
await utils.clickOn('#broadcasting-btn');
// Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue();
});
it('should receive the onRecordingStartRequested when clicking from activities panel', async () => {
const roomName = 'recordingActivitiesEvent';
await browser.get(`${url}&prejoin=false&roomName=${roomName}`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
await browser.sleep(1000);
// Open recording
await utils.waitForElement('ov-recording-activity');
await utils.clickOn('ov-recording-activity');
await browser.sleep(1000);
// Clicking to recording button
await utils.waitForElement('#start-recording-btn');
await utils.clickOn('#start-recording-btn');
// Checking if onRecordingStartRequested has been received
await utils.waitForElement(`#onRecordingStartRequested-${roomName}`);
expect(await utils.isPresent(`#onRecordingStartRequested-${roomName}`)).toBeTrue();
});
xit('should receive the onRecordingStopRequested when clicking from activities panel', async () => {});
xit('should receive the onRecordingDeleteRequested event', async () => {
let element;
const roomName = 'deleteRecordingEvent';
await browser.get(`${url}&prejoin=false&roomName=${roomName}&fakeRecordings=true`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to activities button
const activitiesButton = await utils.waitForElement('#activities-panel-btn');
expect(await utils.isPresent('#activities-panel-btn')).toBeTrue();
await activitiesButton.click();
await browser.sleep(1500);
// Open recording
element = await utils.waitForElement('ov-recording-activity');
await element.click();
await browser.sleep(1500);
// Delete event
element = await utils.waitForElement('#delete-recording-btn');
expect(await utils.isPresent('#delete-recording-btn')).toBeTrue();
await element.click();
element = await utils.waitForElement('#delete-recording-confirm-btn');
expect(await utils.isPresent('#delete-recording-confirm-btn')).toBeTrue();
await element.click();
await utils.waitForElement(`#onRecordingDeleteRequested-${roomName}-fakeRecording`);
expect(await utils.isPresent(`#onRecordingDeleteRequested-${roomName}-fakeRecording`)).toBeTrue();
});
it('should receive the onBroadcastingStartRequested event when clicking from panel', async () => {
const roomName = 'broadcastingStartEvent';
const broadcastUrl = 'BroadcastUrl';
await browser.get(`${url}&prejoin=false&roomName=${roomName}`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('activities');
await browser.sleep(1000);
await utils.waitForElement('#broadcasting-activity');
await utils.clickOn('#broadcasting-activity');
await browser.sleep(1000);
const button = await utils.waitForElement('#broadcasting-btn');
expect(await button.isEnabled()).toBeFalse();
const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys(broadcastUrl);
await utils.clickOn('#broadcasting-btn');
// Checking if onBroadcastingStartRequested has been received
await utils.waitForElement(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`);
expect(await utils.isPresent(`#onBroadcastingStartRequested-${roomName}-${broadcastUrl}`)).toBeTrue();
});
xit('should receive the onBroadcastingStopRequested event when clicking from panel', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Open activities panel
await utils.togglePanel('activities');
await utils.waitForElement('#broadcasting-activity');
await utils.clickOn('#broadcasting-activity');
const button = await utils.waitForElement('#broadcasting-btn');
expect(await button.isEnabled()).toBeFalse();
const input = await utils.waitForElement('#broadcast-url-input');
await input.sendKeys('BroadcastUrl');
await utils.clickOn('#broadcasting-btn');
expect(await utils.isPresent('#broadcasting-tag')).toBeTrue();
await utils.clickOn('#stop-broadcasting-btn');
// Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue();
expect(await utils.isPresent('#broadcasting-tag')).toBeFalse();
});
xit('should receive the onBroadcastingStopRequested event when clicking from toolbar', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Open more options menu
await utils.toggleToolbarMoreOptions();
await utils.waitForElement('#broadcasting-btn');
await utils.clickOn('#broadcasting-btn');
await browser.sleep(500);
// Checking if onBroadcastingStopRequested has been received
await utils.waitForElement('#onBroadcastingStopRequested');
expect(await utils.isPresent('#onBroadcastingStopRequested')).toBeTrue();
expect(await utils.isPresent('#broadcasting-tag')).toBeFalse();
});
it('should receive the onRoomCreated event', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
await utils.waitForElement('#onRoomCreated');
expect(await utils.isPresent('#onRoomCreated')).toBeTrue();
expect(await utils.isPresent('#onReadyToJoin')).toBeFalse();
});
// * PUBLISHER EVENTS
it('should receive onParticipantCreated event from LOCAL participant', async () => {
const participantName = 'TEST_USER';
await browser.get(`${url}&participantName=${participantName}&prejoin=false`);
await utils.waitForElement(`#${participantName}-onParticipantCreated`);
expect(await utils.isPresent(`#${participantName}-onParticipantCreated`)).toBeTrue();
});
it('should receive the onParticipantLeft event', async () => {
await browser.get(`${url}&prejoin=false&redirect=false`);
await utils.checkSessionIsPresent();
await utils.checkToolbarIsPresent();
// Clicking to leave button
const leaveButton = await utils.waitForElement('#leave-btn');
expect(await utils.isPresent('#leave-btn')).toBeTrue();
await leaveButton.click();
// 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

@ -92,11 +92,10 @@
"e2e:nested-attribute-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/attribute-directives.test.js", "e2e:nested-attribute-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/nested-components/attribute-directives.test.js",
"e2e:lib-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/api-directives.test.js", "e2e:lib-directives": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/api-directives.test.js",
"e2e:lib-chat": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/chat.test.js", "e2e:lib-chat": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/chat.test.js",
"e2e:lib-events": "tsc --project ./e2e && npx jasmine ./e2e/dist/events.test.js",
"e2e:webcomponent-all": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/**/*.test.js", "e2e:webcomponent-all": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/**/*.test.js",
"e2e:webcomponent-captions": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/captions.test.js", "e2e:webcomponent-captions": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/captions.test.js",
"e2e:webcomponent-chat": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/chat.test.js",
"e2e:webcomponent-events": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/events.test.js",
"e2e:webcomponent-media-devices": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/media-devices.test.js", "e2e:webcomponent-media-devices": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/media-devices.test.js",
"e2e:webcomponent-panels": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/panels.test.js", "e2e:webcomponent-panels": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/panels.test.js",
"e2e:webcomponent-screensharing": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/screensharing.test.js", "e2e:webcomponent-screensharing": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/webcomponent-e2e/screensharing.test.js",

View File

@ -30,9 +30,11 @@
[activitiesPanelRecordingActivity]="activitiesPanelRecordingActivity" [activitiesPanelRecordingActivity]="activitiesPanelRecordingActivity"
[activitiesPanelBroadcastingActivity]="activitiesPanelBroadcastingActivity" [activitiesPanelBroadcastingActivity]="activitiesPanelBroadcastingActivity"
[toolbarSettingsButton]="toolbarSettingsButton" [toolbarSettingsButton]="toolbarSettingsButton"
(onTokenRequested)="onTokenRequested($event)" (onTokenRequested)="onTokenRequested($event)"
(onReadyToJoin)="onReadyToJoin()" (onReadyToJoin)="onReadyToJoin()"
(onRoomCreated)="onRoomCreated($event)" (onRoomCreated)="onRoomCreated($event)"
(onParticipantCreated)="onParticipantCreated($event)"
(onParticipantLeft)="onParticipantLeft($event)" (onParticipantLeft)="onParticipantLeft($event)"
(onRoomDisconnected)="onRoomDisconnected()" (onRoomDisconnected)="onRoomDisconnected()"
(onVideoEnabledChanged)="onVideoEnabledChanged($event)" (onVideoEnabledChanged)="onVideoEnabledChanged($event)"
@ -49,7 +51,12 @@
(onRecordingDeleteRequested)="onRecordingDeleteRequested($event)" (onRecordingDeleteRequested)="onRecordingDeleteRequested($event)"
(onBroadcastingStartRequested)="onBroadcastingStartRequested($event)" (onBroadcastingStartRequested)="onBroadcastingStartRequested($event)"
(onBroadcastingStopRequested)="onBroadcastingStopRequested($event)" (onBroadcastingStopRequested)="onBroadcastingStopRequested($event)"
(onSettingsPanelStatusChanged)="onSettingsPanelStatusChanged($event)"
(onActivitiesPanelStatusChanged)="onActivitiesPanelStatusChanged($event)"
> >
</ov-videoconference> </ov-videoconference>
} }
<div id="events"></div>

View File

@ -12,7 +12,8 @@ import { RestService } from '../services/rest.service';
import { CustomDevice } from 'dist/openvidu-components-angular/lib/models/device.model'; import { CustomDevice } from 'dist/openvidu-components-angular/lib/models/device.model';
import { LangOption } from 'dist/openvidu-components-angular/lib/models/lang.model'; import { LangOption } from 'dist/openvidu-components-angular/lib/models/lang.model';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { ParticipantLeftEvent } from '../../../projects/openvidu-components-angular/src/lib/models/participant.model'; import { ParticipantLeftEvent, ParticipantModel } from '../../../projects/openvidu-components-angular/src/lib/models/participant.model';
import { monkeyPatchMediaDevices } from '../utils/media-devices';
@Component({ @Component({
selector: 'app-call', selector: 'app-call',
@ -58,6 +59,8 @@ export class CallComponent implements OnInit {
activitiesPanelRecordingActivity: boolean = true; activitiesPanelRecordingActivity: boolean = true;
activitiesPanelBroadcastingActivity: boolean = true; activitiesPanelBroadcastingActivity: boolean = true;
toolbarSettingsButton: boolean = true; toolbarSettingsButton: boolean = true;
fakeDevices: boolean = false;
private redirectOnLeaves: boolean = true;
private staticVideos = [ private staticVideos = [
'https://videos.pexels.com/video-files/4089575/4089575-hd_1280_720_50fps.mp4', 'https://videos.pexels.com/video-files/4089575/4089575-hd_1280_720_50fps.mp4',
@ -110,8 +113,7 @@ export class CallComponent implements OnInit {
if (params['cameraBtn'] !== undefined) this.toolbarCameraButton = params['cameraBtn'] === 'true'; if (params['cameraBtn'] !== undefined) this.toolbarCameraButton = params['cameraBtn'] === 'true';
if (params['toolbarMicrophoneButton'] !== undefined) if (params['toolbarMicrophoneButton'] !== undefined)
this.toolbarMicrophoneButton = params['toolbarMicrophoneButton'] === 'true'; this.toolbarMicrophoneButton = params['toolbarMicrophoneButton'] === 'true';
if (params['screenshareBtn'] !== undefined) if (params['screenshareBtn'] !== undefined) this.toolbarScreenshareButton = params['screenshareBtn'] === 'true';
this.toolbarScreenshareButton = params['screenshareBtn'] === 'true';
if (params['fullscreenBtn'] !== undefined) this.toolbarFullscreenButton = params['fullscreenBtn'] === 'true'; if (params['fullscreenBtn'] !== undefined) this.toolbarFullscreenButton = params['fullscreenBtn'] === 'true';
if (params['toolbarRecordingButton'] !== undefined) this.toolbarRecordingButton = params['toolbarRecordingButton'] === 'true'; if (params['toolbarRecordingButton'] !== undefined) this.toolbarRecordingButton = params['toolbarRecordingButton'] === 'true';
if (params['toolbarBroadcastingButton'] !== undefined) if (params['toolbarBroadcastingButton'] !== undefined)
@ -130,8 +132,7 @@ export class CallComponent implements OnInit {
if (params['displayAudioDetection'] !== undefined) if (params['displayAudioDetection'] !== undefined)
this.streamDisplayAudioDetection = params['displayAudioDetection'] === 'true'; this.streamDisplayAudioDetection = params['displayAudioDetection'] === 'true';
if (params['streamVideoControls'] !== undefined) this.streamVideoControls = params['streamVideoControls'] === 'true'; if (params['streamVideoControls'] !== undefined) this.streamVideoControls = params['streamVideoControls'] === 'true';
if (params['participantMuteBtn'] !== undefined) if (params['participantMuteBtn'] !== undefined) this.participantPanelItemMuteButton = params['participantMuteBtn'] === 'true';
this.participantPanelItemMuteButton = params['participantMuteBtn'] === 'true';
if (params['activitiesPanelRecordingActivity'] !== undefined) if (params['activitiesPanelRecordingActivity'] !== undefined)
this.activitiesPanelRecordingActivity = params['activitiesPanelRecordingActivity'] === 'true'; this.activitiesPanelRecordingActivity = params['activitiesPanelRecordingActivity'] === 'true';
if (params['activitiesPanelBroadcastingActivity'] !== undefined) if (params['activitiesPanelBroadcastingActivity'] !== undefined)
@ -139,31 +140,48 @@ export class CallComponent implements OnInit {
if (params['toolbarSettingsBtn'] !== undefined) this.toolbarSettingsButton = params['toolbarSettingsBtn'] === 'true'; if (params['toolbarSettingsBtn'] !== undefined) this.toolbarSettingsButton = params['toolbarSettingsBtn'] === 'true';
if (params['staticVideos'] !== undefined) this.areStaticVideosEnabled = params['staticVideos'] === 'true'; if (params['staticVideos'] !== undefined) this.areStaticVideosEnabled = params['staticVideos'] === 'true';
if (params['fakeDevices'] !== undefined) this.fakeDevices = params['fakeDevices'] === 'true';
if (params['redirect'] === undefined) {
this.redirectOnLeaves = true;
} else {
this.redirectOnLeaves = params['redirect'] === 'true';
}
this.configReady = true; this.configReady = true;
if (this.areStaticVideosEnabled) {
setTimeout(() => {
const videoElements = document.querySelectorAll('video');
this.replaceWithStaticVideos(videoElements);
}, 3000);
}
if (this.fakeDevices) {
console.warn('Using fake devices');
monkeyPatchMediaDevices();
}
}); });
if (this.areStaticVideosEnabled) {
setTimeout(() => {
const videoElements = document.querySelectorAll('video');
this.replaceWithStaticVideos(videoElements);
}, 3000);
}
} }
async onTokenRequested(participantName: string) { async onTokenRequested(participantName: string) {
console.warn('VC TOKEN REQUESTED', participantName); console.warn('VC TOKEN REQUESTED', participantName);
this.appendElement('onTokenRequested');
await this.requestForTokens(participantName); await this.requestForTokens(participantName);
} }
async onReadyToJoin() { async onReadyToJoin() {
this.appendElement('onReadyToJoin');
console.warn('VC IS READY TO JOIN'); console.warn('VC IS READY TO JOIN');
} }
async onParticipantLeft(event: ParticipantLeftEvent) { async onParticipantLeft(event: ParticipantLeftEvent) {
this.appendElement('onParticipantLeft');
console.warn('VC PARTICIPANT LEFT', event); console.warn('VC PARTICIPANT LEFT', event);
await this.router.navigate(['/']); if (this.redirectOnLeaves) await this.router.navigate(['/']);
} }
onRoomCreated(room: Room) { onRoomCreated(room: Room) {
this.appendElement('onRoomCreated');
console.warn('VC ROOM CREATED', room.name); console.warn('VC ROOM CREATED', room.name);
room.on(RoomEvent.Connected, () => { room.on(RoomEvent.Connected, () => {
if (this.areStaticVideosEnabled) { if (this.areStaticVideosEnabled) {
@ -189,57 +207,86 @@ export class CallComponent implements OnInit {
}); });
} }
onParticipantCreated(event: ParticipantModel) {
this.appendElement(event.name + '-onParticipantCreated');
console.warn('VC PARTICIPANT CREATED', event);
}
onVideoEnabledChanged(value: boolean) { onVideoEnabledChanged(value: boolean) {
this.appendElement('onVideoEnabledChanged-' + value);
console.warn('VC video enabled: ', value); console.warn('VC video enabled: ', value);
} }
onVideoDeviceChanged(device: CustomDevice) { onVideoDeviceChanged(device: CustomDevice) {
this.appendElement('onVideoDeviceChanged');
console.warn('VC video device changed: ', device); console.warn('VC video device changed: ', device);
} }
onAudioEnabledChanged(value: boolean) { onAudioEnabledChanged(value: boolean) {
this.appendElement('onAudioEnabledChanged-' + value);
console.warn('VC audio enabled: ', value); console.warn('VC audio enabled: ', value);
} }
onAudioDeviceChanged(device: CustomDevice) { onAudioDeviceChanged(device: CustomDevice) {
this.appendElement('onAudioDeviceChanged');
console.warn('VC audio device changed: ', device); console.warn('VC audio device changed: ', device);
} }
onScreenShareEnabledChanged(enabled: boolean) { onScreenShareEnabledChanged(enabled: boolean) {
this.appendElement('onScreenShareEnabledChanged');
console.warn('VC screenshare enabled: ', enabled); console.warn('VC screenshare enabled: ', enabled);
} }
onFullscreenEnabledChanged(enabled: boolean) { onFullscreenEnabledChanged(enabled: boolean) {
this.appendElement('onFullscreenEnabledChanged-' + enabled);
console.warn('VC fullscreen enabled: ', enabled); console.warn('VC fullscreen enabled: ', enabled);
} }
onParticipantsPanelStatusChanged(event) { onParticipantsPanelStatusChanged(event) {
this.appendElement('onParticipantsPanelStatusChanged-' + event.isOpened);
console.warn('VC participants panel status changed: ', event); console.warn('VC participants panel status changed: ', event);
} }
onChatPanelStatusChanged(event) { onChatPanelStatusChanged(event) {
this.appendElement('onChatPanelStatusChanged-' + event.isOpened);
console.warn('VC chat status changed: ', event); console.warn('VC chat status changed: ', event);
} }
async onRoomDisconnected() { async onRoomDisconnected() {
this.appendElement('onRoomDisconnected');
this.isSessionAlive = false; this.isSessionAlive = false;
console.log('VC LEAVE BUTTON CLICKED'); console.log('VC LEAVE BUTTON CLICKED');
await this.router.navigate(['/']); await this.router.navigate(['/']);
} }
onFullscreenButtonClicked() { onFullscreenButtonClicked() {
this.appendElement('onFullscreenButtonClicked');
console.warn('TOOLBAR fullscreen CLICKED'); console.warn('TOOLBAR fullscreen CLICKED');
} }
onParticipantsPanelButtonClicked() { onParticipantsPanelButtonClicked() {
this.appendElement('onParticipantsPanelButtonClicked');
console.warn('TOOLBAR participants CLICKED'); console.warn('TOOLBAR participants CLICKED');
} }
onChatPanelButtonClicked() { onChatPanelButtonClicked() {
this.appendElement('onChatPanelButtonClicked');
console.warn('TOOLBAR chat CLICKED'); console.warn('TOOLBAR chat CLICKED');
} }
onLeaveButtonClicked() { onLeaveButtonClicked() {
this.appendElement('onLeaveButtonClicked');
this.isSessionAlive = false; this.isSessionAlive = false;
console.log('TOOLBAR LEAVE CLICKED'); console.log('TOOLBAR LEAVE CLICKED');
} }
onLangChanged(event: LangOption) { onLangChanged(event: LangOption) {
this.appendElement('onLangChanged-' + event.lang);
console.warn('LANG CHANGED', event); console.warn('LANG CHANGED', event);
} }
onSettingsPanelStatusChanged(event) {
this.appendElement('onSettingsPanelStatusChanged-' + event.isOpened);
console.warn('VC settings panel status changed: ', event);
}
onActivitiesPanelStatusChanged(event) {
this.appendElement('onActivitiesPanelStatusChanged-' + event.isOpened);
console.warn('VC activities panel status changed: ', event);
}
async onBroadcastingStartRequested(event: BroadcastingStartRequestedEvent) { async onBroadcastingStartRequested(event: BroadcastingStartRequestedEvent) {
this.appendElement(`onBroadcastingStartRequested-${event.roomName}-${event.broadcastUrl}`);
console.log('START STREAMING', event); console.log('START STREAMING', event);
try { try {
const resp = await this.restService.startBroadcasting(event.broadcastUrl); const resp = await this.restService.startBroadcasting(event.broadcastUrl);
@ -248,8 +295,8 @@ export class CallComponent implements OnInit {
console.error(error); console.error(error);
} }
} }
async onBroadcastingStopRequested(event: BroadcastingStopRequestedEvent) { async onBroadcastingStopRequested(event: BroadcastingStopRequestedEvent) {
this.appendElement('onBroadcastingStopRequested');
console.log('STOP STREAMING', event); console.log('STOP STREAMING', event);
try { try {
const resp = await this.restService.stopBroadcasting(); const resp = await this.restService.stopBroadcasting();
@ -260,6 +307,7 @@ export class CallComponent implements OnInit {
} }
async onRecordingStartRequested(event: RecordingStartRequestedEvent) { async onRecordingStartRequested(event: RecordingStartRequestedEvent) {
this.appendElement('onRecordingStartRequested-' + event.roomName);
console.warn('START RECORDING CLICKED', event); console.warn('START RECORDING CLICKED', event);
try { try {
await this.restService.startRecording(this.roomName); await this.restService.startRecording(this.roomName);
@ -268,6 +316,7 @@ export class CallComponent implements OnInit {
} }
} }
async onRecordingStopRequested(event: RecordingStopRequestedEvent) { async onRecordingStopRequested(event: RecordingStopRequestedEvent) {
this.appendElement('onRecordingStopRequested');
console.warn('STOP RECORDING CLICKED', event); console.warn('STOP RECORDING CLICKED', event);
try { try {
await this.restService.stopRecording(event); await this.restService.stopRecording(event);
@ -277,6 +326,7 @@ export class CallComponent implements OnInit {
} }
async onRecordingDeleteRequested(event: RecordingDeleteRequestedEvent) { async onRecordingDeleteRequested(event: RecordingDeleteRequestedEvent) {
this.appendElement('onRecordingDeleteRequested');
console.warn('DELETE RECORDING requested', event); console.warn('DELETE RECORDING requested', event);
try { try {
@ -311,4 +361,13 @@ export class CallComponent implements OnInit {
}); });
} }
} }
private appendElement(id: string) {
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);
}
} }

View File

@ -0,0 +1,30 @@
class FilterStream {
constructor(stream, label) {
const videoTrack = stream.getVideoTracks()[0];
const { width, height } = videoTrack.getSettings();
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const video = document.createElement('video');
video.srcObject = new MediaStream([videoTrack]);
video.play();
video.addEventListener('play', () => {
const loop = () => {
if (!video.paused && !video.ended) {
ctx.filter = 'grayscale(100%)';
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, video.videoWidth, video.videoHeight);
setTimeout(loop, 33);
}
};
loop();
});
this.outputStream = canvas.captureStream();
Object.defineProperty(this.outputStream.getVideoTracks()[0], 'label', {
writable: true,
value: label
});
}
}
export { FilterStream };

View File

@ -0,0 +1,90 @@
// Ideally we'd use an editor or import shaders directly from the API.
import { FilterStream } from './filter-stream.js';
export const monkeyPatchMediaDevices = () => {
const enumerateDevicesFn = MediaDevices.prototype.enumerateDevices;
const getUserMediaFn = MediaDevices.prototype.getUserMedia;
const getDisplayMediaFn = MediaDevices.prototype.getDisplayMedia;
const fakeVideoDevice = {
deviceId: 'virtual_video',
groupId: '',
kind: 'videoinput',
label: 'custom_fake_video_1'
};
const fakeAudioDevice = {
deviceId: 'virtual_audio',
groupId: '',
kind: 'audioinput',
label: 'custom_fake_audio_1'
};
const enumerateDevicesMonkeyPatch = async function () {
const res = await enumerateDevicesFn.call(navigator.mediaDevices);
res.push(fakeVideoDevice);
res.push(fakeAudioDevice);
return res;
};
const getUserMediaMonkeyPatch = async function () {
const args = arguments[0];
if (args.audio && (args.audio.deviceId === 'virtual_audio' || args.audio.deviceId?.exact === 'virtual_audio')) {
const constraints = {
audio: {
facingMode: args.facingMode,
advanced: args.audio.advanced,
deviceId: fakeAudioDevice.deviceId
},
video: false
};
const res = await getUserMediaFn.call(navigator.mediaDevices, constraints);
return res;
} else if (args.video && (args.video.deviceId === 'virtual_video' || args.video.deviceId?.exact === 'virtual_video')) {
const { deviceId, advanced, width, height } = args.video;
const constraints = {
video: {
facingMode: args.facingMode,
advanced,
width,
height
},
audio: false
};
const res = await getUserMediaFn.call(navigator.mediaDevices, constraints);
if (res) {
const filter = new FilterStream(res, fakeVideoDevice.label);
return filter.outputStream;
}
return res;
}
return getUserMediaFn.call(navigator.mediaDevices, ...arguments);
};
const getDisplayMediaMonkeyPatch = async function () {
const { video, audio } = arguments[0];
const screenVideoElement = document.getElementsByClassName('OV_video-element screen-type')[0];
const currentTrackLabel = screenVideoElement?.srcObject?.getVideoTracks()[0]?.label;
const res = await getDisplayMediaFn.call(navigator.mediaDevices, { video, audio });
if (res && currentTrackLabel && currentTrackLabel !== 'custom_fake_screen') {
const filter = new FilterStream(res, 'custom_fake_screen');
return filter.outputStream;
}
return res;
};
MediaDevices.prototype.enumerateDevices = enumerateDevicesMonkeyPatch;
navigator.mediaDevices.enumerateDevices = enumerateDevicesMonkeyPatch;
MediaDevices.prototype.getUserMedia = getUserMediaMonkeyPatch;
navigator.mediaDevices.getUserMedia = getUserMediaMonkeyPatch;
MediaDevices.prototype.getDisplayMedia = getDisplayMediaMonkeyPatch;
navigator.mediaDevices.getDisplayMedia = getDisplayMediaMonkeyPatch;
}