fix(tests) Check for continguous thunbnails in the filmstrip

This commit is contained in:
Jaya Allamsetty
2025-10-29 15:06:11 -04:00
committed by Jaya Allamsetty
parent 6f1bdb513a
commit 4432f727a4
5 changed files with 158 additions and 12 deletions

View File

@@ -393,6 +393,7 @@ export interface IConfig {
disabled?: boolean;
initialWidth?: number;
minParticipantCountForTopPanel?: number;
stageFilmstripParticipants?: number;
};
flags?: {
ssrcRewritingEnabled: boolean;

View File

@@ -275,4 +275,111 @@ export default class Filmstrip extends BasePageObject {
reverse: !isDisplayed,
});
}
/**
* Checks for visible gaps in the filmstrip thumbnails.
* This detects if there are any missing thumbnails or excessive spacing between consecutive visible thumbnails.
*
* @returns Returns true if gaps are detected, false otherwise.
*/
async hasGapsInFilmstrip(): Promise<boolean> {
return await this.participant.execute(() => {
// Get all visible thumbnail containers in the filmstrip
const thumbnails = Array.from(
document.querySelectorAll('#remoteVideos span.videocontainer')
).filter((thumb: any) => {
const style = window.getComputedStyle(thumb);
const rect = thumb.getBoundingClientRect();
// Check if element is visible and has dimensions
return style.display !== 'none'
&& style.visibility !== 'hidden'
&& rect.width > 0
&& rect.height > 0;
});
if (thumbnails.length < 2) {
// Can't have gaps with less than 2 thumbnails
return false;
}
// Get positions and calculated margins of all visible thumbnails
const positions = thumbnails.map((thumb: any) => {
const rect = thumb.getBoundingClientRect();
const style = window.getComputedStyle(thumb);
return {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom,
width: rect.width,
height: rect.height,
marginTop: parseFloat(style.marginTop) || 0,
marginBottom: parseFloat(style.marginBottom) || 0,
marginLeft: parseFloat(style.marginLeft) || 0,
marginRight: parseFloat(style.marginRight) || 0
};
});
// Calculate expected spacing between thumbnails based on first two
const firstGap = positions.length >= 2
? Math.abs(positions[1].top - positions[0].top) !== 0
? positions[1].top - positions[0].bottom // vertical
: positions[1].left - positions[0].right // horizontal
: 0;
// Check if filmstrip is vertical or horizontal
const isVertical = Math.abs(positions[1].top - positions[0].top) > Math.abs(positions[1].left - positions[0].left);
if (isVertical) {
// For vertical filmstrip, check vertical spacing consistency
for (let i = 0; i < positions.length - 1; i++) {
const current = positions[i];
const next = positions[i + 1];
const gap = next.top - current.bottom;
// Compare against the first gap with some tolerance
// Flag if gap is more than 2x the expected spacing
if (gap > Math.max(firstGap * 2, current.height * 0.3)) {
return true;
}
}
} else {
// For horizontal filmstrip, check horizontal spacing consistency
for (let i = 0; i < positions.length - 1; i++) {
const current = positions[i];
const next = positions[i + 1];
const gap = next.left - current.right;
// Compare against the first gap with some tolerance
// Flag if gap is more than 2x the expected spacing
if (gap > Math.max(firstGap * 2, current.width * 0.3)) {
return true;
}
}
}
return false;
});
}
/**
* Asserts that there are no gaps in the filmstrip.
* This is useful for detecting layout issues where thumbnails might be missing or mispositioned.
*
* @param reverse - If true, asserts that gaps should exist. Default false.
*/
async assertNoGapsInFilmstrip(reverse = false): Promise<void> {
const hasGaps = await this.hasGapsInFilmstrip();
const expectedResult = reverse ? true : false;
if (hasGaps !== expectedResult) {
throw new Error(
`Expected filmstrip to ${reverse ? 'have' : 'not have'} gaps, but ${
hasGaps ? 'gaps were detected' : 'no gaps were found'
}`
);
}
}
}

View File

@@ -76,6 +76,10 @@ describe('Active speaker', () => {
timeoutMsg: 'P3 tile should remain visible in filmstrip during screenshare'
});
// Check that there are no gaps in the filmstrip during local screenshare
await p2.getFilmstrip().assertNoGapsInFilmstrip();
await p3.getFilmstrip().assertNoGapsInFilmstrip();
await p1.getToolbar().clickAudioMuteButton();
await p1.getToolbar().clickStopDesktopSharingButton();
});
@@ -216,6 +220,10 @@ describe('Active speaker', () => {
timeout: 3_000,
timeoutMsg: 'P3 tile should remain visible in filmstrip during P2 screenshare'
});
// Check that there are no gaps in the filmstrip when remote starts screensharing
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p3.getFilmstrip().assertNoGapsInFilmstrip();
});
it('testFilmstripTilesWithMultipleScreenshares', async () => {
@@ -256,6 +264,9 @@ describe('Active speaker', () => {
// Verify screenshare tiles are present
await checkForScreensharingTile(p1, p3);
await checkForScreensharingTile(p2, p3);
// Check that there are no gaps in the filmstrip with multiple screenshares
await p3.getFilmstrip().assertNoGapsInFilmstrip();
});
});

View File

@@ -83,7 +83,9 @@ describe('Desktop sharing', () => {
timeoutMsg: 'P3 camera tile should be visible on P1 during P2 screenshare'
});
expect(await p3.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p3.getFilmstrip().assertNoGapsInFilmstrip();
await p3.waitForParticipantOnLargeVideo(`${p2EndpointId}-v1`);
});
/**
@@ -103,7 +105,7 @@ describe('Desktop sharing', () => {
await checkForScreensharingTile(p2, p2);
// The video should be playing.
expect(await p1.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
await p1.waitForParticipantOnLargeVideo(`${await p2.getEndpointId()}-v1`);
// Start desktop share on p1.
await p1.getToolbar().clickDesktopSharingButton();
@@ -123,9 +125,6 @@ describe('Desktop sharing', () => {
await checkForScreensharingTile(p1, p3);
await checkForScreensharingTile(p2, p3);
// The large video should be playing on p3.
expect(await p3.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
});
/**
@@ -166,8 +165,22 @@ describe('Desktop sharing', () => {
await checkForScreensharingTile(p1, p3);
await checkForScreensharingTile(p2, p3);
// The large video should be playing on p3.
expect(await p3.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
// Add another particpant to verify multiple screenshares are visible without gaps in filmstrip.
await ensureFourParticipants({
configOverwrite: {
filmstrip: {
stageFilmstripParticipants: 2
},
startWithAudioMuted: true
}
});
const { p4 } = ctx;
await checkForScreensharingTile(p1, p4);
await checkForScreensharingTile(p2, p4);
await p3.getFilmstrip().assertNoGapsInFilmstrip();
await p4.getFilmstrip().assertNoGapsInFilmstrip();
});
/**
@@ -205,6 +218,10 @@ describe('Desktop sharing', () => {
}
});
const { p2, p3 } = ctx;
const p1EndpointId = await p1.getEndpointId();
const p1ScreenShareTileId = `${p1EndpointId}-v1`;
const p2EndpointId = await p2.getEndpointId();
const p2ScreenShareTileId = `${p2EndpointId}-v1`;
// p1 starts share again when call switches to jvb.
await p1.getToolbar().clickDesktopSharingButton();
@@ -213,8 +230,8 @@ describe('Desktop sharing', () => {
await checkForScreensharingTile(p1, p2);
await checkForScreensharingTile(p1, p3);
expect(await p2.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
expect(await p3.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
await p2.waitForParticipantOnLargeVideo(p1ScreenShareTileId);
await p3.waitForParticipantOnLargeVideo(p1ScreenShareTileId);
// p3 leaves the call.
await p3.hangup();
@@ -225,14 +242,14 @@ describe('Desktop sharing', () => {
// Make sure p2 see's p1's share after the call switches back to p2p.
await checkForScreensharingTile(p1, p2);
expect(await p2.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
await p2.waitForParticipantOnLargeVideo(p1ScreenShareTileId);
// p2 starts share when in p2p.
await p2.getToolbar().clickDesktopSharingButton();
// Makes sure p2's share is visible on p1.
await checkForScreensharingTile(p2, p1);
expect(await p1.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
await p1.waitForParticipantOnLargeVideo(p2ScreenShareTileId);
});
/**
@@ -357,6 +374,11 @@ describe('Desktop sharing', () => {
// And the video should be playing
expect(await p4.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
// Check that there are no gaps in the filmstrip when participant joins during screensharing
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p2.getFilmstrip().assertNoGapsInFilmstrip();
await p4.getFilmstrip().assertNoGapsInFilmstrip();
const p1EndpointId = await p1.getEndpointId();
const p2EndpointId = await p2.getEndpointId();

View File

@@ -323,7 +323,7 @@ describe('Lobby', () => {
await ensureTwoParticipants();
await enableLobby();
const { p1 } = ctx;
const { p1, p2 } = ctx;
const knockingParticipant = await enterLobby(p1, true, true);
@@ -368,6 +368,11 @@ describe('Lobby', () => {
await p3.waitForRemoteStreams(2);
expect(await p3.getFilmstrip().countVisibleThumbnails()).toBe(3);
// Check that there are no gaps in the filmstrip after joining from lobby
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p2.getFilmstrip().assertNoGapsInFilmstrip();
await p3.getFilmstrip().assertNoGapsInFilmstrip();
});
});