diff --git a/tests/behavioral/helpers.ts b/tests/behavioral/helpers.ts index f97aa70..30019df 100644 --- a/tests/behavioral/helpers.ts +++ b/tests/behavioral/helpers.ts @@ -36,7 +36,7 @@ export async function clickToggleOnWithRetry( * a sidebar and stays open between tests, so this is idempotent. */ export async function openPickRandomUserPanel(modPage: Page): Promise { - await modPage.page.waitForSelector(e.whiteboard, { timeout: ELEMENT_WAIT_LONGER_TIME }); + await modPage.waitUntilInMeeting(); if (await modPage.page.locator(e.pickRandomUserPanel).isVisible()) return; diff --git a/tests/behavioral/multi-user.spec.ts b/tests/behavioral/multi-user.spec.ts index e09be96..31b0c3f 100644 --- a/tests/behavioral/multi-user.spec.ts +++ b/tests/behavioral/multi-user.spec.ts @@ -29,11 +29,11 @@ const setPluginUrl = (url: string) => { pluginUrl = url; }; const getPluginUrl = () => pluginUrl; /** - * Wait for the attendee's page to finish loading the whiteboard. + * Wait for the attendee's client to finish loading. * This ensures the attendee is fully in the meeting before the presenter picks. */ async function waitForAttendeeMeeting(attendeePage: Page): Promise { - await attendeePage.page.waitForSelector(e.whiteboard, { timeout: ELEMENT_WAIT_LONGER_TIME }); + await attendeePage.waitUntilInMeeting(); } /** @@ -99,6 +99,7 @@ test.describe('Pick Random User Plugin - Behavioural (multi-user)', () => { if (attendeePage.settings?.autoJoinAudioModal) { await attendeePage.closeAudioModal(); } + await attendeePage.waitUntilInMeeting(); await attendeeRawPage.addStyleTag({ content: "body { font-family: 'Liberation Sans', Arial, sans-serif; }", }); @@ -338,6 +339,7 @@ test.describe('Pick Random User Plugin - Behavioural (countdown and close-preven if (attendeePage.settings?.autoJoinAudioModal) { await attendeePage.closeAudioModal(); } + await attendeePage.waitUntilInMeeting(); await attendeeRawPage.addStyleTag({ content: "body { font-family: 'Liberation Sans', Arial, sans-serif; }", }); diff --git a/tests/core/sessionPage.ts b/tests/core/sessionPage.ts index 555e127..8e560c9 100644 --- a/tests/core/sessionPage.ts +++ b/tests/core/sessionPage.ts @@ -83,6 +83,7 @@ export class SessionPage { this.settings = await generateSettingsData(this.page); const autoJoinAudioModal = this.settings?.autoJoinAudioModal; if (shouldCloseAudioModal && autoJoinAudioModal) await this.closeAudioModal(); + await this.waitUntilInMeeting(); } // overwrite for font used in CI await this.page.addStyleTag({ @@ -132,4 +133,22 @@ export class SessionPage { await this.hasElement(e.audioModal, 'should display the audio modal', ELEMENT_WAIT_EXTRA_LONG_TIME); await this.page.click(e.closeModal); } + + /** + * Wait until the in-session client is ready to be driven. + * + * Deliberately keyed on the apps gallery button rather than on the whiteboard: this + * plugin is reached through the apps gallery, and the button renders for moderators and + * viewers alike regardless of what the media area is showing. The whiteboard is a poor + * readiness signal here — a server that sets layout.hidePresentationOnJoin + * joins users with the presentation minimized, + * and the canvas is then not in the DOM at all, so waiting for it can only time out. + */ + async waitUntilInMeeting(timeout = ELEMENT_WAIT_EXTRA_LONG_TIME) { + await this.hasElement( + e.appsGallerySidebarButton, + 'should display the apps gallery button once the client is in the meeting', + timeout, + ); + } } diff --git a/tests/structural/test.spec.ts b/tests/structural/test.spec.ts index d0424ad..7b2bf2c 100644 --- a/tests/structural/test.spec.ts +++ b/tests/structural/test.spec.ts @@ -76,7 +76,7 @@ test.describe('Pick Random User Plugin - Structural', () => { } test('should list "Pick random user" in the apps gallery for a presenter', async (): Promise => { - await modPage.page.waitForSelector(e.whiteboard, { timeout: ELEMENT_WAIT_LONGER_TIME }); + await modPage.waitUntilInMeeting(); await modPage.page.click(e.appsGallerySidebarButton); await modPage.hasElement( e.pickRandomUserAppsGalleryItem,