From 5a99839ed71bf0a24fb98792be9c484b2a3cff5d Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Fri, 14 Nov 2025 13:30:47 +0100 Subject: [PATCH] ov-components: Enhance screensharing tests to address pinning bugs and add utility methods for stream management --- .../e2e/screensharing.test.ts | 201 +++++++++++------- .../e2e/utils.po.test.ts | 45 ++++ 2 files changed, 165 insertions(+), 81 deletions(-) diff --git a/openvidu-components-angular/e2e/screensharing.test.ts b/openvidu-components-angular/e2e/screensharing.test.ts index 3abefa579..806208e43 100644 --- a/openvidu-components-angular/e2e/screensharing.test.ts +++ b/openvidu-components-angular/e2e/screensharing.test.ts @@ -205,112 +205,151 @@ describe('E2E: Screensharing features', () => { await browser.sleep(500); expect(await utils.getNumberOfElements('video')).toEqual(1); }); - // it('should show and hide CAMERA stream when muting video with screensharing', async () => { - // await browser.get(`${url}&prejoin=false`); - // await utils.checkLayoutPresent(); + // ==================== PIN/UNPIN TESTS ==================== + // These tests demonstrate bugs in the pin system: + // 1. Multiple screens can be auto-pinned simultaneously + // 2. Manual unpins can be overridden by auto-pin logic when participants join - // // Clicking to screensharing button - // const screenshareButton = await utils.waitForElement('#screenshare-btn'); - // expect(await screenshareButton.isDisplayed()).toBeTrue(); - // await screenshareButton.click(); + it('should NOT have multiple screens pinned when both participants share screen', async () => { + const roomName = 'pinBugCase1'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; - // await utils.waitForElement('.OV_big'); - // expect(await utils.getNumberOfElements('video')).toEqual(2); + // Participant A joins and shares screen + await browser.get(fixedUrl); + await utils.checkLayoutPresent(); + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); - // const muteVideoButton = await utils.waitForElement('#camera-btn'); - // await muteVideoButton.click(); + // Verify A's screen is pinned + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfPinnedStreams()).toEqual(1); + const pinnedCountA1 = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab A] After A shares: ${pinnedCountA1} pinned stream(s)`); - // expect(await utils.getNumberOfElements('video')).toEqual(1); - // }); + // Participant B joins + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + await utils.checkLayoutPresent(); + await browser.sleep(1000); - // it('should screenshare has audio active when camera is muted', async () => { - // let isAudioEnabled; - // const audioEnableScript = 'return document.getElementsByTagName("video")[0].srcObject.getAudioTracks()[0].enabled;'; + // B should see A's screen pinned + expect(await utils.getNumberOfElements('video')).toEqual(3); // 2 cameras + 1 screen + expect(await utils.getNumberOfPinnedStreams()).toEqual(1); + const pinnedCountB1 = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab B] After B joins: ${pinnedCountB1} pinned stream(s)`); - // await browser.get(`${url}&prejoin=false`); + // B shares screen + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); - // await utils.checkLayoutPresent(); + // B should see only their own screen pinned (auto-pin + unpin previous) + expect(await utils.getNumberOfElements('video')).toEqual(4); // 2 cameras + 2 screens + await utils.waitForElement('.OV_big'); + const pinnedCountB2 = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab B] After B shares: ${pinnedCountB2} pinned stream(s)`); + expect(pinnedCountB2).toEqual(1); // Should be 1, but implementation might show different - // // Clicking to screensharing button - // const screenshareButton = await utils.waitForElement('#screenshare-btn'); - // expect(await utils.isPresent('#screenshare-btn')).toBeTrue(); - // await screenshareButton.click(); + // Switch to Tab A and check + await browser.switchTo().window(tabs[0]); + await browser.sleep(1000); + expect(await utils.getNumberOfElements('video')).toEqual(4); // 2 cameras + 2 screens - // await utils.waitForElement('.OV_big'); - // expect(await utils.getNumberOfElements('video')).toEqual(2); - // expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); + // BUG: In A's view, BOTH screens are pinned + const pinnedCountA2 = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab A] After B shares: ${pinnedCountA2} pinned stream(s)`); - // // Muting camera video - // const muteVideoButton = await utils.waitForElement('#camera-btn'); - // await muteVideoButton.click(); + // EXPECTED: Only B's screen should be pinned (the most recent one) + // ACTUAL: Both A's and B's screens are pinned + expect(pinnedCountA2).toEqual(1, 'BUG DETECTED: Multiple screens are pinned. Expected only the most recent screen to be pinned.'); + }); - // expect(await utils.getNumberOfElements('video')).toEqual(1); + it('should NOT re-pin manually unpinned screen when new participant joins', async () => { + const roomName = 'pinBugCase2'; + const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`; - // await browser.sleep(500); - // expect(await utils.isPresent('#status-mic')).toBeFalse(); + // Participant A joins and shares screen + await browser.get(fixedUrl); + await utils.checkLayoutPresent(); + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); - // // Checking if audio is muted after join the room - // isAudioEnabled = await browser.executeScript(audioEnableScript); - // expect(isAudioEnabled).toBeTrue(); + // Verify A's screen is auto-pinned + await utils.waitForElement('.OV_big'); + expect(await utils.getNumberOfPinnedStreams()).toEqual(1); - // // Unmuting camera - // await muteVideoButton.click(); - // await browser.sleep(1000); + // Participant B joins and shares screen + const tabs = await utils.openTab(fixedUrl); + await browser.switchTo().window(tabs[1]); + await utils.checkLayoutPresent(); + await browser.sleep(1000); + await utils.waitForElement('#screenshare-btn'); + await utils.clickOn('#screenshare-btn'); + await browser.sleep(500); - // await utils.waitForElement('.camera-type'); - // expect(await utils.getNumberOfElements('video')).toEqual(2); - // expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); - // }); + // B should see their own screen pinned + expect(await utils.getNumberOfElements('video')).toEqual(4); // 2 cameras + 2 screens + await utils.waitForElement('.OV_big'); + let pinnedCountB = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab B] After B shares: ${pinnedCountB} pinned stream(s)`); - // it('should camera come back with audio muted when screensharing', async () => { - // let element, isAudioEnabled; + // B manually unpins their own screen + const screenStreams = await utils.getScreenShareStreams(); + if (screenStreams.length > 0) { + // Find B's own screen (it should be the pinned one) + await utils.toggleStreamPin('.OV_big'); + await browser.sleep(500); + } - // const getAudioScript = (className: string) => { - // return `return document.getElementsByClassName('${className}')[0].srcObject.getAudioTracks()[0].enabled;`; - // }; + // Verify B's screen is now unpinned + pinnedCountB = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab B] After manually unpinning B's screen: ${pinnedCountB} pinned stream(s)`); + expect(pinnedCountB).toEqual(0, 'B should have no pinned streams after manual unpin'); - // await browser.get(`${url}&prejoin=false`); + // B manually pins A's screen + const screenElements = await utils.getScreenShareStreams(); + if (screenElements.length >= 2) { + // Pin the first screen that is not already pinned (should be A's screen) + await utils.toggleStreamPin('.OV_stream.remote .screen-type'); + await utils.toggleStreamPin('#pin-btn'); + await browser.sleep(500); + } - // await utils.checkLayoutPresent(); + // Verify A's screen is now pinned in B's view + pinnedCountB = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab B] After manually pinning A's screen: ${pinnedCountB} pinned stream(s)`); + expect(pinnedCountB).toEqual(1, "Only A's screen should be pinned"); - // // Clicking to screensharing button - // const screenshareButton = await utils.waitForElement('#screenshare-btn'); - // await screenshareButton.click(); + // Participant C joins the room + const tab3 = await utils.openTab(fixedUrl); + await browser.switchTo().window(tab3[2]); + await utils.checkLayoutPresent(); + await browser.sleep(1500); - // await utils.waitForElement('.screen-type'); - // expect(await utils.getNumberOfElements('video')).toEqual(2); - // expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); + // Switch back to B's tab + await browser.switchTo().window(tabs[1]); + await browser.sleep(1000); - // // Mute camera - // const muteVideoButton = await utils.waitForElement('#camera-btn'); - // await muteVideoButton.click(); + // B's screen should still be unpinned, but might get re-pinned automatically + pinnedCountB = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab B] After C joins: ${pinnedCountB} pinned stream(s)`); - // expect(await utils.getNumberOfElements('video')).toEqual(1); - // expect(await utils.isPresent('#status-mic')).toBeFalse(); + // EXPECTED: No screens should be pinned (B manually unpinned everything) + // ACTUAL: B's screen gets re-pinned automatically + expect(pinnedCountB).toEqual(1, 'BUG DETECTED: Only one screen should be pinned after C joins.'); - // // Checking if audio is muted after join the room - // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); - // expect(isAudioEnabled).toBeTrue(); + // Switch back to A's tab to verify + await browser.switchTo().window(tabs[0]); + await browser.sleep(500); - // // Mute audio - // const muteAudioButton = await utils.waitForElement('#mic-btn'); - // await muteAudioButton.click(); + const pinnedCountA2 = await utils.getNumberOfPinnedStreams(); + console.log(`[Tab A] After C joins: ${pinnedCountA2} pinned stream(s)`); - // await utils.waitForElement('#status-mic'); - // expect(await utils.getNumberOfElements('#status-mic')).toEqual(1); - - // isAudioEnabled = await browser.executeScript(getAudioScript('screen-type')); - // expect(isAudioEnabled).toBeFalse(); - - // // Unmute camera - // await muteVideoButton.click(); - - // await utils.waitForElement('.camera-type'); - // expect(await utils.getNumberOfElements('video')).toEqual(2); - // expect(await utils.getNumberOfElements('#status-mic')).toEqual(2); - - // isAudioEnabled = await browser.executeScript(getAudioScript('camera-type')); - // expect(isAudioEnabled).toBeFalse(); - // }); + // EXPECTED: Only A's screen should be pinned + // ACTUAL: A's screen remains pinned + expect(pinnedCountA2).toEqual(1, "BUG DETECTED: A's screen should remain pinned after C joins."); + }); }); diff --git a/openvidu-components-angular/e2e/utils.po.test.ts b/openvidu-components-angular/e2e/utils.po.test.ts index fcc93935b..28f6661bb 100644 --- a/openvidu-components-angular/e2e/utils.po.test.ts +++ b/openvidu-components-angular/e2e/utils.po.test.ts @@ -279,4 +279,49 @@ export class OpenViduComponentsPO { // fs.writeFileSync('diff.png', PNG.sync.write(diff)); // expect(numDiffPixels).to.be.greaterThan(500, 'The virtual background was not applied correctly'); } + + /** + * Pins or unpins a stream by clicking on it + * @param streamSelector CSS selector for the stream element (e.g., '.screen-type', '.camera-type') + */ + async toggleStreamPin(streamSelector: string): Promise { + const stream = await this.waitForElement(streamSelector); + await stream.click(); + await this.browser.sleep(300); + } + + /** + * Gets the number of pinned streams (elements with class .OV_big) + */ + async getNumberOfPinnedStreams(): Promise { + return await this.getNumberOfElements('.OV_big'); + } + + /** + * Checks if a specific stream is pinned + * @param streamSelector CSS selector for the stream element + */ + async isStreamPinned(streamSelector: string): Promise { + try { + const stream = await this.waitForElement(streamSelector); + const classes = await stream.getAttribute('class'); + return classes.includes('OV_big'); + } catch (error) { + return false; + } + } + + /** + * Gets all screen share streams + */ + async getScreenShareStreams(): Promise { + return await this.browser.findElements(By.css('.screen-type')); + } + + /** + * Gets all camera streams + */ + async getCameraStreams(): Promise { + return await this.browser.findElements(By.css('.camera-type')); + } }