import { expect } from 'chai'; import { By, until, WebDriver, WebElement } from 'selenium-webdriver'; import { EMBEDDED_API_URL, APP_URL, STANDALONE_API_URL } from './config'; import ffmpeg from 'fluent-ffmpeg'; export class OpenViduCallPO { private TIMEOUT = 30 * 1000; private POLL_TIMEOUT = 1 * 1000; constructor(private browser: WebDriver) {} async createIframe(url: string): Promise { // access to a page under the same domain await this.browser.get(`${APP_URL}/unauthorized`); await this.browser.executeScript( `const iframe = document.createElement('iframe'); iframe.id = 'test-iframe'; iframe.style.width = '100%'; iframe.style.height = '500px'; iframe.src = '${url}'; iframe.allow = 'camera; microphone; display-capture; fullscreen'; iframe.sandbox = 'allow-same-origin allow-scripts allow-popups allow-forms'; document.body.appendChild(iframe);` ); } async waitForIframe(): Promise { await this.browser.wait( until.elementLocated(By.id('test-iframe')), this.TIMEOUT, 'Time out waiting for iframe', this.POLL_TIMEOUT ); } async switchToIframe(): Promise { const iframe = await this.browser.findElement(By.id('test-iframe')); await this.browser.switchTo().frame(iframe); } async buildIframeAndSwitch(url: string): Promise { await this.createIframe(url); await this.waitForIframe(); await this.switchToIframe(); } async getIframeUrl(): Promise { return this.browser.executeScript('return window.location.href'); } async removeIframe(): Promise { await this.browser.executeScript(`document.getElementById('test-iframe')?.remove();`); } async getEmbeddedUrl(roomName?: string, participantName?: string): Promise { if (!roomName) roomName = 'TestRoom-' + Math.random().toString(36).substring(7); if (!participantName) participantName = 'ParticipantName-' + Math.random().toString(36).substring(7); const response = await fetch(`${EMBEDDED_API_URL}/participant`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ roomName, participantName }) }); const responseJson: any = await response.json(); return responseJson.embeddedURL; } async getInvalidEmbeddedUrl(): Promise { return `${APP_URL}/embedded?token=eyJhbGciOiJIUzI1NiJ9.eyJtZXRhZGF0YSI6IntcImxpdmVraXRVcmxcIjpcIndzOi8vbG9jYWxob3N0Ojc4ODBcIixcInBlcm1pc3Npb25zXCI6e1wiY2FuUmVjb3JkXCI6dHJ1ZSxcImNhbkNoYXRcIjp0cnVlfX0iLCJuYW1lIjoiVGVzdFBhcnRpY2lwYW50IiwidmlkZW8iOnsicm9vbSI6IlRlc3RSb29tIiwicm9vbUNyZWF0ZSI6dHJ1ZSwicm9vbUpvaW4iOnRydWUsInJvb21MaXN0Ijp0cnVlLCJyb29tUmVjb3JkIjp0cnVlLCJyb29tQWRtaW4iOnRydWUsImluZ3Jlc3NBZG1pbiI6ZmFsc2UsImNhblB1Ymxpc2giOnRydWUsImNhblN1YnNjcmliZSI6dHJ1ZSwiY2FuUHVibGlzaERhdGEiOnRydWUsImNhblVwZGF0ZU93bk1ldGFkYXRhIjpmYWxzZSwiaGlkZGVuIjpmYWxzZSwicmVjb3JkZXIiOmZhbHNlLCJhZ2VudCI6ZmFsc2V9LCJpc3MiOiJkZXZrZXkiLCJleHAiOjE3MzkyMDA0OTMsIm5iZiI6MCwic3ViIjoiVGVzdFBhcnRpY2lwYW50In0.7BEE9sdDrkG2VtEkmlJtAfH1YAnOHprBd2HyBLyu8TY`; } async getToken(roomName?: string, participantName?: string): Promise { if (!roomName) roomName = 'TestRoom-' + Math.random().toString(36).substring(7); if (!participantName) participantName = 'ParticipantName-' + Math.random().toString(36).substring(7); const response = await fetch(`${STANDALONE_API_URL}/rooms`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ roomName, participantName }) }); const responseJson: any = await response.json(); return responseJson.token; } async getInvalidToken(): Promise { return `eyJhbGciOiJIUzI1NiJ9.eyJtZXRhZGF0YSI6IntcImxpdmVraXRVcmxcIjpcIndzOi8vbG9jYWxob3N0Ojc4ODBcIixcInBlcm1pc3Npb25zXCI6e1wiY2FuUmVjb3JkXCI6dHJ1ZSxcImNhbkNoYXRcIjp0cnVlfX0iLCJuYW1lIjoiVGVzdFBhcnRpY2lwYW50IiwidmlkZW8iOnsicm9vbSI6IlRlc3RSb29tIiwicm9vbUNyZWF0ZSI6dHJ1ZSwicm9vbUpvaW4iOnRydWUsInJvb21MaXN0Ijp0cnVlLCJyb29tUmVjb3JkIjp0cnVlLCJyb29tQWRtaW4iOnRydWUsImluZ3Jlc3NBZG1pbiI6ZmFsc2UsImNhblB1Ymxpc2giOnRydWUsImNhblN1YnNjcmliZSI6dHJ1ZSwiY2FuUHVibGlzaERhdGEiOnRydWUsImNhblVwZGF0ZU93bk1ldGFkYXRhIjpmYWxzZSwiaGlkZGVuIjpmYWxzZSwicmVjb3JkZXIiOmZhbHNlLCJhZ2VudCI6ZmFsc2V9LCJpc3MiOiJkZXZrZXkiLCJleHAiOjE3MzkyMDA0OTMsIm5iZiI6MCwic3ViIjoiVGVzdFBhcnRpY2lwYW50In0.7BEE9sdDrkG2VtEkmlJtAfH1YAnOHprBd2HyBLyu8TY`; } async waitForElement(selector: string): Promise { return await this.browser.wait( until.elementLocated(By.css(selector)), this.TIMEOUT, `Time out waiting for ${selector}`, this.POLL_TIMEOUT ); } async isPresent(selector: string): Promise { const elements = await this.browser.findElements(By.css(selector)); return elements.length > 0; } async checkPrejoinIsPresent(): Promise { await this.waitForElement('#prejoin-container'); expect(await this.isPresent('#prejoin-container')).to.be.true; } async clickOn(selector: string): Promise { const element = await this.waitForElement(selector); await element.click(); } async checkToolbarIsPresent(): Promise { await this.waitForElement('#toolbar'); await this.waitForElement('#media-buttons-container'); expect(await this.isPresent('#media-buttons-container')).to.be.true; } async checkLayoutIsPresent(): Promise { await this.waitForElement('#layout-container'); expect(await this.isPresent('#layout-container')).to.be.true; await this.waitForElement('#layout'); expect(await this.isPresent('#layout')).to.be.true; } async enableScreenShare(): Promise { await this.waitForElement('#screenshare-btn'); await this.clickOn('#screenshare-btn'); await this.browser.sleep(500); } async disableScreenShare(): Promise { 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 checkLoginFormIsPresent(): Promise { await this.waitForElement('#form-login'); expect(await this.isPresent('#form-room')).to.be.false; await this.waitForElement('#login-username'); await this.waitForElement('#login-password'); } async login(username: string, password: string): Promise { await this.waitForElement('#form-login'); let element = await this.waitForElement('#login-username input'); await element.sendKeys(username); element = await this.waitForElement('#login-password input'); await element.sendKeys(password); await this.clickOn('#join-btn'); } async joinRoom(): Promise { await this.waitForElement('#join-button'); await this.clickOn('#join-button'); } async leaveRoom(): Promise { await this.waitForElement('#leave-btn'); await this.clickOn('#leave-btn'); } async getRoomName(): Promise { const element = await this.waitForElement('#session-name'); return element.getText(); } async openTab(url: string): Promise { const newTabScript = `window.open("${url}")`; await this.browser.executeScript(newTabScript); return this.browser.getAllWindowHandles(); } async sendKeys(selector: string, keys: string): Promise { const element = await this.waitForElement(selector); await element.sendKeys(keys); } async applyVirtualBackground(bgId: string): Promise { await this.waitForElement('#more-options-btn'); await this.clickOn('#more-options-btn'); await this.waitForElement('#virtual-bg-btn'); await this.clickOn('#virtual-bg-btn'); await this.waitForElement('ov-background-effects-panel'); await this.browser.sleep(1000); await this.waitForElement(`#effect-${bgId}`); await this.clickOn(`#effect-${bgId}`); await this.clickOn('.panel-close-button'); await this.browser.sleep(2000); } async getNumberOfElements(selector: string): Promise { return (await this.browser.findElements(By.css(selector))).length; } async startRecordingFromToolbar(): Promise { await this.waitForElement('#more-options-btn'); await this.clickOn('#more-options-btn'); await this.waitForElement('#recording-btn'); await this.clickOn('#recording-btn'); await this.checkRecordingIsStarting(); await this.checkRecordingIsStarted(); } async startRecordingFromPanel(): Promise { await this.waitForElement('ov-activities-panel'); await this.waitForElement('#start-recording-btn'); await this.clickOn('#start-recording-btn'); await this.checkRecordingIsStarting(); await this.checkRecordingIsStarted(); } async stopRecordingFromToolbar(): Promise { await this.waitForElement('#more-options-btn'); await this.clickOn('#more-options-btn'); await this.waitForElement('#recording-btn'); await this.clickOn('#recording-btn'); await this.checkRecordingIsStopped(); } async stopRecordingFromPanel(): Promise { await this.waitForElement('ov-activities-panel'); await this.waitForElement('#stop-recording-btn'); await this.clickOn('#stop-recording-btn'); await this.checkRecordingIsStopped(); } async deleteRecording(): Promise { await this.waitForElement('.recording-item'); await this.waitForElement('#delete-recording-btn'); await this.clickOn('#delete-recording-btn'); await this.waitForElement('app-delete-dialog'); await this.waitForElement('#delete-recording-confirm-btn'); await this.clickOn('#delete-recording-confirm-btn'); } async playRecording() { await this.waitForElement('.recording-item'); await this.waitForElement('#play-recording-btn'); await this.clickOn('#play-recording-btn'); } async checkRecordingFileIsPlayable(filePath: string): Promise { await new Promise((resolve, reject) => { ffmpeg(filePath) .on('error', (err) => { reject(new Error(`❌ Error trying to play the recording ${err.message}`)); }) .on('end', () => { console.log(`The recording file is playable.`); resolve(); }) .ffprobe((err) => { if (err) { reject(new Error(`❌ Error trying to play the recording: ${err.message}`)); } else { console.log(`The recording file is playable.`); resolve(); } }); }); } async checkRecordingIsStopped(): Promise { await this.waitForElement('#recording-status.stopped'); } async checkRecordingIsStarting(): Promise { await this.waitForElement('ov-activities-panel'); await this.waitForElement('#recording-status.starting'); } async checkRecordingIsStopping(): Promise { await this.waitForElement('ov-activities-panel'); await this.waitForElement('#recording-status.stopping'); } async checkRecordingIsStarted(): Promise { await this.waitForElement('ov-activities-panel'); await this.waitForElement('#recording-status.started'); await this.waitForElement('#recording-tag'); } }