From c93bce1b8b992d79c4fbdaec2443f9633d54b2d3 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Wed, 18 Feb 2026 21:49:12 +0100 Subject: [PATCH] debug: add forced screenshots at every auth-join step, disable mic toggle Co-authored-by: Cursor --- src/bot/orchestrator.ts | 45 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/bot/orchestrator.ts b/src/bot/orchestrator.ts index 2463c8b..324a7fb 100644 --- a/src/bot/orchestrator.ts +++ b/src/bot/orchestrator.ts @@ -220,13 +220,12 @@ export class BotOrchestrator { this._setState('navigating'); // STEP 1: Navigate to teams.microsoft.com to trigger authentication - this._logger.info('Auth join: navigating to teams.microsoft.com'); + this._logger.info('STEP 1: navigating to teams.microsoft.com'); await this._page!.goto('https://teams.microsoft.com', { waitUntil: 'domcontentloaded', timeout: 30000, }); - // Poll for login page (redirect to login.microsoftonline.com) const emailInput = await this._pollForElement( ['input[name="loginfmt"]', 'input[type="email"]'], 30000, @@ -234,10 +233,11 @@ export class BotOrchestrator { ); if (!emailInput) { this._logger.warn(`No login page found, current URL: ${this._page!.url().substring(0, 150)}`); + await this._takeScreenshot('step1-no-login-page', true); } // STEP 2: Microsoft Authentication - this._logger.info(`Authenticating as ${this._options.botAccountEmail}`); + this._logger.info(`STEP 2: authenticating as ${this._options.botAccountEmail}`); const authProcedure = new AuthProcedure(this._page!, this._logger); const authSuccess = await authProcedure.authenticateWithMicrosoft( this._options.botAccountEmail!, @@ -246,12 +246,14 @@ export class BotOrchestrator { ); if (!authSuccess) { + await this._takeScreenshot('step2-auth-failed', true); throw new Error('Microsoft authentication failed'); } - this._logger.info('Authentication successful'); + this._logger.info('STEP 2: authentication successful'); + await this._takeScreenshot('step2-auth-done', true); - // STEP 3: Wait for Teams to finish loading after auth - this._logger.info('Waiting for Teams to load after auth...'); + // STEP 3: Wait for Teams to load after auth + this._logger.info('STEP 3: waiting for Teams to load after auth...'); try { await this._page!.waitForURL( (url) => url.hostname.includes('teams.microsoft.com') || url.hostname.includes('teams.cloud.microsoft'), @@ -260,16 +262,18 @@ export class BotOrchestrator { } catch { this._logger.warn(`Unexpected URL after auth: ${this._page!.url().substring(0, 150)}`); } + await this._takeScreenshot('step3-teams-loaded', true); // STEP 4: Navigate to the meeting URL - this._logger.info(`Auth join: navigating to meeting URL: ${this._meetingUrl.substring(0, 80)}...`); + this._logger.info(`STEP 4: navigating to meeting URL: ${this._meetingUrl.substring(0, 80)}...`); await this._page!.goto(this._meetingUrl, { waitUntil: 'domcontentloaded', timeout: 30000, }); - this._logger.info(`Auth join: URL after navigation: ${this._page!.url().substring(0, 150)}`); + this._logger.info(`STEP 4: URL after navigation: ${this._page!.url().substring(0, 150)}`); + await this._takeScreenshot('step4-meeting-url-loaded', true); - // STEP 4a: Poll for the first actionable button (interstitial OR pre-join) + // STEP 4a: Poll for first actionable button (interstitial OR pre-join) const interstitialSelectors = [ 'button:has-text("Continue on this browser")', 'button:has-text("In diesem Browser fortfahren")', @@ -299,28 +303,33 @@ export class BotOrchestrator { if (!isPreJoin) { await firstBtn.click(); - this._logger.info(`Clicked interstitial: "${btnText}"`); + this._logger.info(`STEP 4a: clicked interstitial: "${btnText}"`); + await this._takeScreenshot('step4a-after-interstitial', true); + } else { + this._logger.info(`STEP 4a: pre-join button already visible: "${btnText}"`); } } else { - await this._takeScreenshot('auth-no-buttons'); + await this._takeScreenshot('step4a-no-buttons-found', true); } - // STEP 5: Poll for "Join now" on the pre-join screen - await this._ensureMicOn(); + // STEP 5: Poll for "Join now" on the pre-join screen (mic is NOT touched) + await this._takeScreenshot('step5-before-join-now', true); const joinNowBtn = await this._pollForElement(preJoinSelectors, 30000, 'Join now button'); if (!joinNowBtn) { - await this._takeScreenshot('auth-no-join-now'); + await this._takeScreenshot('step5-no-join-now', true); throw new Error('"Join now" button not found on pre-join screen'); } await joinNowBtn.click(); - this._logger.info('Clicked "Join now", waiting for meeting'); + this._logger.info('STEP 5: clicked "Join now", waiting for meeting'); + await this._takeScreenshot('step5-join-now-clicked', true); // STEP 6: Wait for meeting admission (hangup button = in meeting) await this._waitForMeetingAdmission(); this._setState('in_meeting'); - this._logger.info(`Bot joined the meeting (authenticated as ${this._options.botAccountEmail})`); + this._logger.info(`STEP 6: bot joined the meeting (authenticated as ${this._options.botAccountEmail})`); + await this._takeScreenshot('step6-in-meeting', true); this._startKeepAlive(); await this._audioProcedure!.initialize(); @@ -1183,8 +1192,8 @@ export class BotOrchestrator { * Take a screenshot for debugging. * Logs screenshot as base64 for easy viewing from Azure logs. */ - private async _takeScreenshot(name: string): Promise { - if (!config.screenshotOnError || !this._page) { + private async _takeScreenshot(name: string, force: boolean = false): Promise { + if ((!config.screenshotOnError && !force) || !this._page) { return; }