ov-components: Add virtual backgrounds tests and enhance utility functions for background effects

master
Carlos Santos 2025-08-22 13:29:27 +02:00
parent 1b9277145c
commit c8f53a48ce
5 changed files with 307 additions and 108 deletions

View File

@ -29,39 +29,34 @@ describe('Panels: UI Navigation and Section Switching', () => {
await browser.quit();
});
/**
* TODO
* It only works with OpenVidu PRO because this is a PRO feature
*/
// 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')).toBeTrue();
fit('should close BACKGROUND panel on prejoin page when VIDEO is MUTED', async () => {
let element;
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
// const backgroundButton = await utils.waitForElement('#background-effects-btn');
// expect(await utils.isPresent('#background-effects-btn')).toBeTrue();
// expect(await backgroundButton.isEnabled()).toBeTrue();
// await backgroundButton.click();
// await browser.sleep(500);
const backgroundButton = await utils.waitForElement('#background-effects-btn');
expect(await utils.isPresent('#background-effects-btn')).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeTrue();
await backgroundButton.click();
await browser.sleep(500);
// await utils.waitForElement('#background-effects-container');
// expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
// element = await utils.waitForElement('#camera-button');
// expect(await utils.isPresent('#camera-button')).toBeTrue();
// expect(await element.isEnabled()).toBeTrue();
// await element.click();
element = await utils.waitForElement('#camera-button');
expect(await utils.isPresent('#camera-button')).toBeTrue();
expect(await element.isEnabled()).toBeTrue();
await element.click();
// await browser.sleep(500);
// element = await utils.waitForElement('#video-poster');
// expect(await utils.isPresent('#video-poster')).toBeTrue();
await browser.sleep(500);
element = await utils.waitForElement('#video-poster');
expect(await utils.isPresent('#video-poster')).toBeTrue();
// expect(await backgroundButton.isDisplayed()).toBeTrue();
// expect(await backgroundButton.isEnabled()).toBeFalse();
expect(await backgroundButton.isDisplayed()).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeFalse();
// expect(await utils.isPresent('#background-effects-container')).toBeFalse();
// });
expect(await utils.isPresent('#background-effects-container')).toBeFalse();
});
it('should open and close the CHAT panel and verify its content', async () => {
await browser.get(`${url}&prejoin=false`);

View File

@ -1,4 +1,8 @@
import { By, until, WebDriver, WebElement } from 'selenium-webdriver';
import * as fs from 'fs';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';
type PNGWithMetadata = PNG & { data: Buffer };
export class OpenViduComponentsPO {
private TIMEOUT = 10 * 1000;
@ -188,6 +192,16 @@ export class OpenViduComponentsPO {
await this.waitForElement('#participants-panel-btn');
await this.clickOn('#participants-panel-btn');
break;
case 'backgrounds':
await this.waitForElement('#more-options-btn');
await this.clickOn('#more-options-btn');
await this.browser.sleep(500);
await this.waitForElement('#virtual-bg-btn');
await this.clickOn('#virtual-bg-btn');
await this.browser.sleep(1000);
break;
case 'settings':
await this.toggleToolbarMoreOptions();
@ -198,4 +212,71 @@ export class OpenViduComponentsPO {
await this.browser.sleep(500);
}
async applyBackground(bgId: string) {
await this.waitForElement('ov-background-effects-panel');
await this.browser.sleep(1000);
await this.waitForElement(`#effect-${bgId}`);
await this.clickOn(`#effect-${bgId}`);
await this.browser.sleep(2000);
}
async applyVirtualBackgroundFromPrejoin(bgId: string): Promise<void> {
await this.waitForElement('#backgrounds-button');
await this.clickOn('#backgrounds-button');
await this.applyBackground(bgId);
await this.clickOn('#backgrounds-button');
}
async saveScreenshot(filename: string, element: WebElement) {
const image = await element.takeScreenshot();
fs.writeFileSync(filename, image, 'base64');
}
async expectVirtualBackgroundApplied(
img1Name: string,
img2Name: string,
{
threshold = 0.4,
minDiffPixels = 500,
debug = false
}: {
threshold?: number;
minDiffPixels?: number;
debug?: boolean;
} = {}
): Promise<void> {
const beforeImg = PNG.sync.read(fs.readFileSync(img1Name));
const afterImg = PNG.sync.read(fs.readFileSync(img2Name));
const { width, height } = beforeImg;
const diff = new PNG({ width, height });
// const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, {
// threshold: 0.4
// // alpha: 0.5,
// // includeAA: false,
// // diffColor: [255, 0, 0]
// });
const numDiffPixels = pixelmatch(beforeImg.data, afterImg.data, diff.data, width, height, {
threshold
// includeAA: true
});
if (numDiffPixels <= minDiffPixels) {
// Sólo guardar los archivos de debug si falla la prueba
if (debug) {
fs.writeFileSync('before.png', PNG.sync.write(beforeImg));
fs.writeFileSync('after.png', PNG.sync.write(afterImg));
fs.writeFileSync('diff.png', PNG.sync.write(diff));
}
}
expect(numDiffPixels).toBeGreaterThan(minDiffPixels, 'The virtual background was not applied correctly');
// fs.writeFileSync('diff.png', PNG.sync.write(diff));
// expect(numDiffPixels).to.be.greaterThan(500, 'The virtual background was not applied correctly');
}
}

View File

@ -0,0 +1,155 @@
import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';
const url = TestAppConfig.appUrl;
describe('Prejoin: Virtual Backgrounds', () => {
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);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should close BACKGROUNDS on prejoin page when VIDEO is disabled', async () => {
let element;
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
const backgroundButton = await utils.waitForElement('#backgrounds-button');
expect(await utils.isPresent('#backgrounds-button')).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeTrue();
await utils.clickOn('#backgrounds-button');
await browser.sleep(500);
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.clickOn('#camera-button');
await browser.sleep(500);
element = await utils.waitForElement('#video-poster');
expect(await utils.isPresent('#video-poster')).toBeTrue();
expect(await backgroundButton.isDisplayed()).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeFalse();
await browser.sleep(1000);
expect(await utils.getNumberOfElements('#background-effects-container')).toBe(0);
});
it('should open and close BACKGROUNDS panel on prejoin page', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
const backgroundButton = await utils.waitForElement('#backgrounds-button');
expect(await utils.isPresent('#backgrounds-button')).toBeTrue();
expect(await backgroundButton.isEnabled()).toBeTrue();
await backgroundButton.click();
await browser.sleep(500);
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.clickOn('#backgrounds-button');
await browser.sleep(1000);
expect(await utils.getNumberOfElements('#background-effects-container')).toBe(0);
});
it('should apply a background effect on prejoin page', async () => {
await browser.get(`${url}`);
await utils.checkPrejoinIsPresent();
let videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('before.png', videoElement);
await utils.applyVirtualBackgroundFromPrejoin('1');
await browser.sleep(1000);
videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('after.png', videoElement);
await utils.expectVirtualBackgroundApplied('before.png', 'after.png');
});
});
describe('Room: Virtual Backgrounds', () => {
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);
});
afterEach(async () => {
try {
await utils.leaveRoom();
} catch (error) {}
await browser.quit();
});
it('should open and close BACKGROUNDS panel in the room', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.checkToolbarIsPresent();
await utils.togglePanel('backgrounds');
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
await utils.togglePanel('backgrounds');
await browser.sleep(1000);
expect(await utils.getNumberOfElements('#background-effects-container')).toBe(0);
});
it('should apply a background effect in the room', async () => {
await browser.get(`${url}&prejoin=false`);
await utils.checkLayoutPresent();
await utils.togglePanel('backgrounds');
await utils.waitForElement('#background-effects-container');
expect(await utils.isPresent('#background-effects-container')).toBeTrue();
let videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('before.png', videoElement);
await utils.applyBackground('1');
await browser.sleep(1000);
videoElement = await utils.waitForElement('.OV_video-element');
await utils.saveScreenshot('after.png', videoElement);
await utils.expectVirtualBackgroundApplied('before.png', 'after.png');
});
});

View File

@ -33,6 +33,7 @@
"@compodoc/compodoc": "^1.1.25",
"@types/jasmine": "^5.1.4",
"@types/node": "20.12.14",
"@types/pngjs": "^6.0.5",
"@types/selenium-webdriver": "4.1.16",
"@types/ws": "^8.5.12",
"chromedriver": "138.0.0",
@ -57,6 +58,8 @@
"lint-staged": "^15.2.10",
"ng-packagr": "19.2.2",
"npm-watch": "^0.13.0",
"pixelmatch": "^7.1.0",
"pngjs": "^7.0.0",
"prettier": "3.3.3",
"selenium-webdriver": "4.32.0",
"ts-node": "10.9.2",
@ -2937,33 +2940,6 @@
}
}
},
"node_modules/@compodoc/compodoc/node_modules/@angular-devkit/schematics/node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/@compodoc/compodoc/node_modules/@babel/core": {
"version": "7.25.8",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz",
@ -3136,21 +3112,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@compodoc/compodoc/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"optional": true,
"peer": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@compodoc/compodoc/node_modules/magic-string": {
"version": "0.30.11",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
@ -3161,36 +3122,6 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/@compodoc/compodoc/node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/@compodoc/compodoc/node_modules/readdirp/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/@compodoc/live-server": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@compodoc/live-server/-/live-server-1.2.3.tgz",
@ -6659,6 +6590,16 @@
"@types/node": "*"
}
},
"node_modules/@types/pngjs": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz",
"integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
@ -15821,6 +15762,19 @@
"@napi-rs/nice": "^1.0.1"
}
},
"node_modules/pixelmatch": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz",
"integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==",
"dev": true,
"license": "ISC",
"dependencies": {
"pngjs": "^7.0.0"
},
"bin": {
"pixelmatch": "bin/pixelmatch"
}
},
"node_modules/pkg-dir": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz",
@ -15925,6 +15879,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pngjs": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
"integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.19.0"
}
},
"node_modules/portfinder": {
"version": "1.0.37",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz",

View File

@ -25,6 +25,7 @@
"@compodoc/compodoc": "^1.1.25",
"@types/jasmine": "^5.1.4",
"@types/node": "20.12.14",
"@types/pngjs": "^6.0.5",
"@types/selenium-webdriver": "4.1.16",
"@types/ws": "^8.5.12",
"chromedriver": "138.0.0",
@ -49,6 +50,8 @@
"lint-staged": "^15.2.10",
"ng-packagr": "19.2.2",
"npm-watch": "^0.13.0",
"pixelmatch": "^7.1.0",
"pngjs": "^7.0.0",
"prettier": "3.3.3",
"selenium-webdriver": "4.32.0",
"ts-node": "10.9.2",
@ -97,6 +100,7 @@
"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",
"e2e:lib-virtual-backgrounds": "tsc --project ./e2e && npx jasmine --fail-fast ./e2e/dist/virtual-backgrounds.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"
},