diff --git a/src/bot/authTestProcedure.ts b/src/bot/authTestProcedure.ts index 1c58227..a766161 100644 --- a/src/bot/authTestProcedure.ts +++ b/src/bot/authTestProcedure.ts @@ -12,6 +12,11 @@ export interface AuthTestVariant { description: string; } +export interface StepScreenshot { + label: string; + data: string; +} + export interface AuthTestResult { variantId: string; variantName: string; @@ -24,6 +29,7 @@ export interface AuthTestResult { authAttempted: boolean; authSuccess: boolean | null; screenshot?: string; + screenshots?: StepScreenshot[]; durationMs: number; error?: string; detectedSignals: string[]; @@ -181,15 +187,26 @@ async function _runVariant( } }); - // All variants follow the same flow: - // 1. Navigate to teams.microsoft.com (triggers Teams-specific login redirect) - // 2. Login (email → password → stay signed in) - // 3. Wait 20 seconds after login (NO meeting navigation) - // 4. Screenshot where we landed + // Flow with screenshots at every step: + // 1. Navigate to teams.microsoft.com → wait for login redirect → screenshot + // 2. Login (email → password → stay signed in) → screenshot after auth + // 3. Wait for Teams app to load → screenshot + // 4. Click "Join a meeting" → screenshot + // 5. Navigate to meeting URL → screenshot (final) let authAttempted = false; let authSuccess: boolean | null = null; + const screenshots: StepScreenshot[] = []; + async function _screenshotStep(label: string): Promise { + _log('info', `Screenshot: "${label}" — URL: ${page.url().substring(0, 120)}`); + const data = await _takeScreenshot(page); + if (data) { + screenshots.push({ label, data }); + } + } + + // --- Step 1: Navigate to teams.microsoft.com --- _log('info', `Step 1: Navigate to teams.microsoft.com`); await page.goto('https://teams.microsoft.com', { waitUntil: 'domcontentloaded', @@ -204,30 +221,152 @@ async function _runVariant( } catch { _log('warn', `No login redirect after 30s, current URL: ${page.url().substring(0, 150)}`); } + await _screenshotStep('1 - Login-Seite'); - // Step 2: Login + // --- Step 2: Login --- if (botAccountEmail && botAccountPassword) { authAttempted = true; _log('info', `Authenticating as ${botAccountEmail} (skipNavigation=true to preserve OAuth context)`); const authProcedure = new AuthProcedure(page, logger); - // Always skipNavigation=true: we're already on login.microsoftonline.com with the correct - // OAuth parameters (client_id, redirect_uri for Teams). Navigating to plain - // login.microsoftonline.com would lose this context. authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, true); _log(authSuccess ? 'info' : 'warn', `Auth result: ${authSuccess ? 'success' : 'failed'}`); + + if (authSuccess) { + // Wait for redirect chain back to Teams + _log('info', 'Waiting for redirect back to Teams...'); + try { + await page.waitForURL('**/teams.microsoft.com/**', { timeout: 30000 }); + } catch { + _log('warn', `No Teams redirect after 30s, current URL: ${page.url().substring(0, 150)}`); + } + await page.waitForTimeout(5000); + } + await _screenshotStep('2 - Nach Login'); + } else { + _log('info', 'No credentials provided — skipping auth'); } - // Step 3: Wait 20 seconds — NO further navigation - _log('info', 'Waiting 20s after login (no meeting navigation)...'); - await page.waitForTimeout(20000); - _log('info', `Final URL after 20s: ${page.url().substring(0, 150)}`); + // --- Step 3: Teams App geladen --- + _log('info', 'Waiting 10s for Teams app to fully load...'); + await page.waitForTimeout(10000); + await _screenshotStep('3 - Teams App'); - // Detect page type and gather signals + // --- Step 4: Click "Join a meeting" --- + _log('info', 'Step 4: Looking for "Join a meeting" button...'); + const joinMeetingSelectors = [ + 'button:has-text("Join a meeting")', + 'button:has-text("An Besprechung teilnehmen")', + 'a:has-text("Join a meeting")', + 'a:has-text("An Besprechung teilnehmen")', + '[data-tid="join-meeting-button"]', + '[data-tid="join-a-meeting-button"]', + 'button[title="Join a meeting"]', + 'button[aria-label="Join a meeting"]', + ]; + + let joinMeetingClicked = false; + for (const selector of joinMeetingSelectors) { + try { + const btn = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' }); + if (btn) { + await btn.click(); + _log('info', `Clicked "Join a meeting": ${selector}`); + joinMeetingClicked = true; + await page.waitForTimeout(3000); + break; + } + } catch { + // Try next selector + } + } + + if (!joinMeetingClicked) { + _log('warn', 'Could not find "Join a meeting" button — logging all visible buttons'); + try { + const buttons = await page.evaluate(() => { + const btns = document.querySelectorAll('button, a[role="button"], [role="menuitem"]'); + return Array.from(btns).slice(0, 30).map(b => { + const text = (b.textContent || '').trim().substring(0, 80); + const tid = b.getAttribute('data-tid') || ''; + const title = b.getAttribute('title') || ''; + return `[${b.tagName} tid="${tid}" title="${title}"] ${text}`; + }); + }); + buttons.forEach(b => _log('info', ` Button: ${b}`)); + } catch (e) { + _log('warn', `Could not enumerate buttons: ${e}`); + } + } + await _screenshotStep('4 - Join a meeting'); + + // --- Step 5: Enter meeting URL --- + if (joinMeetingClicked) { + _log('info', `Step 5: Entering meeting URL: ${meetingUrl.substring(0, 80)}`); + const meetingInputSelectors = [ + 'input[placeholder*="meeting"]', + 'input[placeholder*="Besprechung"]', + 'input[placeholder*="code"]', + 'input[placeholder*="Code"]', + 'input[placeholder*="link"]', + 'input[placeholder*="Link"]', + 'input[data-tid="join-meeting-input"]', + 'input[type="text"]', + 'input[type="url"]', + ]; + + let meetingInputFound = false; + for (const selector of meetingInputSelectors) { + try { + const input = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' }); + if (input) { + await input.fill(meetingUrl); + _log('info', `Entered meeting URL in: ${selector}`); + meetingInputFound = true; + await page.waitForTimeout(2000); + break; + } + } catch { + // Try next + } + } + + if (!meetingInputFound) { + _log('warn', 'Could not find meeting URL input field'); + } + + // Try clicking "Join" button after entering URL + const joinNowSelectors = [ + 'button:has-text("Join")', + 'button:has-text("Teilnehmen")', + 'button:has-text("Join now")', + 'button:has-text("Jetzt teilnehmen")', + '#prejoin-join-button', + 'button[data-tid="prejoin-join-button"]', + ]; + + for (const selector of joinNowSelectors) { + try { + const btn = await page.$(selector); + if (btn && await btn.isVisible()) { + _log('info', `Found Join button: ${selector} (NOT clicking — test only)`); + break; + } + } catch { + // Try next + } + } + + await page.waitForTimeout(5000); + await _screenshotStep('5 - Meeting URL eingegeben'); + } + + // --- Final: detect page type --- + _log('info', `Final URL: ${page.url().substring(0, 150)}`); const detection = await _detectPageType(page); - // Take screenshot - const screenshot = await _takeScreenshot(page); + // Use last screenshot as the main screenshot for backward compatibility + const screenshot = screenshots.length > 0 ? screenshots[screenshots.length - 1].data : undefined; return { variantId: variant.id, @@ -241,6 +380,7 @@ async function _runVariant( authAttempted, authSuccess, screenshot, + screenshots, durationMs: Date.now() - startTime, detectedSignals: detection.signals, logs: variantLogs,