refactor: 2 path variants (Sign in / Join a meeting) with full page load waits
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c892c93215
commit
8ed183f13c
1 changed files with 200 additions and 173 deletions
|
|
@ -49,19 +49,14 @@ export interface AuthTestResults {
|
||||||
|
|
||||||
const _VARIANTS: AuthTestVariant[] = [
|
const _VARIANTS: AuthTestVariant[] = [
|
||||||
{
|
{
|
||||||
id: 'chromiumClean',
|
id: 'signIn',
|
||||||
name: 'Chromium Headful Clean',
|
name: 'Pfad: Sign in',
|
||||||
description: 'Playwright Chromium headful, enhanced stealth + realistic devices, keine fake-flags',
|
description: 'Nach Login auf Teams-Landingpage "Sign in" klicken — was kommt?',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'chromiumNoAutomation',
|
id: 'joinMeeting',
|
||||||
name: 'Chromium No-Automation',
|
name: 'Pfad: Join a meeting',
|
||||||
description: 'Chromium headful, zusaetzlich --disable-extensions, --no-first-run',
|
description: 'Nach Login auf Teams-Landingpage "Join a meeting" klicken — was kommt?',
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rebrowserHeadful',
|
|
||||||
name: 'rebrowser-playwright Headful',
|
|
||||||
description: 'rebrowser-playwright headful, CDP-Leak-Fixes + realistic devices',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -69,7 +64,7 @@ const _VARIANTS: AuthTestVariant[] = [
|
||||||
// CONSTANTS
|
// CONSTANTS
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
const _VARIANT_TIMEOUT_MS = 60000;
|
const _VARIANT_TIMEOUT_MS = 120000;
|
||||||
const _SCREENSHOT_QUALITY = 50;
|
const _SCREENSHOT_QUALITY = 50;
|
||||||
const _USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0';
|
const _USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0';
|
||||||
|
|
||||||
|
|
@ -96,8 +91,8 @@ const _BROWSER_ARGS_NO_AUTOMATION = [
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run auth detection test variants: login at teams.microsoft.com, screenshot where we land.
|
* Run auth tests: 2 variants — "Sign in" and "Join a meeting" on the Teams landing page.
|
||||||
* Does NOT navigate to any meeting URL — only tests if Teams login works.
|
* Both do full Microsoft login first, then click different buttons to see what page loads.
|
||||||
*/
|
*/
|
||||||
export async function runAuthTests(
|
export async function runAuthTests(
|
||||||
meetingUrl: string,
|
meetingUrl: string,
|
||||||
|
|
@ -109,7 +104,6 @@ export async function runAuthTests(
|
||||||
const results: AuthTestResult[] = [];
|
const results: AuthTestResult[] = [];
|
||||||
|
|
||||||
for (const variant of _VARIANTS) {
|
for (const variant of _VARIANTS) {
|
||||||
// All variants require credentials
|
|
||||||
if (!botAccountEmail || !botAccountPassword) {
|
if (!botAccountEmail || !botAccountPassword) {
|
||||||
results.push(_createSkippedResult(variant, 'Keine Credentials angegeben'));
|
results.push(_createSkippedResult(variant, 'Keine Credentials angegeben'));
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -119,7 +113,7 @@ export async function runAuthTests(
|
||||||
try {
|
try {
|
||||||
const result = await _runVariant(variant, meetingUrl, botAccountEmail, botAccountPassword);
|
const result = await _runVariant(variant, meetingUrl, botAccountEmail, botAccountPassword);
|
||||||
results.push(result);
|
results.push(result);
|
||||||
logger.info(`[AuthTest] Variant ${variant.name}: pageType=${result.pageType}, url=${result.finalUrl.substring(0, 80)}`);
|
logger.info(`[AuthTest] Variant ${variant.name}: finalUrl=${result.finalUrl.substring(0, 80)}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`[AuthTest] Variant ${variant.name} failed:`, err);
|
logger.error(`[AuthTest] Variant ${variant.name} failed:`, err);
|
||||||
results.push(_createErrorResult(variant, String(err)));
|
results.push(_createErrorResult(variant, String(err)));
|
||||||
|
|
@ -161,39 +155,30 @@ async function _runVariant(
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Launch browser based on variant
|
// Always use Chromium Headful Clean for both variants
|
||||||
const launchResult = await _launchBrowserForVariant(variant);
|
const launchResult = await _launchChromiumHeadful(_BROWSER_ARGS_CLEAN);
|
||||||
browser = launchResult.browser;
|
browser = launchResult.browser;
|
||||||
context = launchResult.context;
|
context = launchResult.context;
|
||||||
const page = launchResult.page;
|
const page = launchResult.page;
|
||||||
|
|
||||||
// Collect browser console messages
|
// Collect browser console messages (only errors, to reduce noise)
|
||||||
page.on('console', (msg) => {
|
page.on('console', (msg) => {
|
||||||
const type = msg.type();
|
const type = msg.type();
|
||||||
if (type === 'error' || type === 'warning') {
|
if (type === 'error') {
|
||||||
variantLogs.push(`[CONSOLE:${type}] ${msg.text().substring(0, 200)}`);
|
variantLogs.push(`[CONSOLE:${type}] ${msg.text().substring(0, 200)}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collect page errors
|
|
||||||
page.on('pageerror', (err) => {
|
page.on('pageerror', (err) => {
|
||||||
variantLogs.push(`[PAGE_ERROR] ${String(err).substring(0, 200)}`);
|
variantLogs.push(`[PAGE_ERROR] ${String(err).substring(0, 200)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track navigation/redirects
|
|
||||||
page.on('framenavigated', (frame) => {
|
page.on('framenavigated', (frame) => {
|
||||||
if (frame === page.mainFrame()) {
|
if (frame === page.mainFrame()) {
|
||||||
variantLogs.push(`[NAV] ${frame.url().substring(0, 150)}`);
|
variantLogs.push(`[NAV] ${frame.url().substring(0, 150)}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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 authAttempted = false;
|
||||||
let authSuccess: boolean | null = null;
|
let authSuccess: boolean | null = null;
|
||||||
const screenshots: StepScreenshot[] = [];
|
const screenshots: StepScreenshot[] = [];
|
||||||
|
|
@ -206,111 +191,196 @@ async function _runVariant(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Step 1: Navigate to teams.microsoft.com ---
|
// =====================================================================
|
||||||
_log('info', `Step 1: Navigate to teams.microsoft.com`);
|
// STEP 1: Navigate to teams.microsoft.com
|
||||||
|
// =====================================================================
|
||||||
|
_log('info', 'Step 1: Navigate to teams.microsoft.com');
|
||||||
await page.goto('https://teams.microsoft.com', {
|
await page.goto('https://teams.microsoft.com', {
|
||||||
waitUntil: 'domcontentloaded',
|
waitUntil: 'domcontentloaded',
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for Teams to redirect to login.microsoftonline.com (up to 30s)
|
// Wait for login redirect OR the Teams landing page to appear
|
||||||
_log('info', 'Waiting for login redirect to login.microsoftonline.com...');
|
_log('info', 'Waiting for login.microsoftonline.com redirect...');
|
||||||
try {
|
try {
|
||||||
await page.waitForURL('**/login.microsoftonline.com/**', { timeout: 30000 });
|
await page.waitForURL('**/login.microsoftonline.com/**', { timeout: 30000 });
|
||||||
_log('info', `Redirected to login: ${page.url().substring(0, 150)}`);
|
_log('info', `Redirected to login: ${page.url().substring(0, 150)}`);
|
||||||
} catch {
|
} catch {
|
||||||
_log('warn', `No login redirect after 30s, current URL: ${page.url().substring(0, 150)}`);
|
_log('warn', `No login redirect, current URL: ${page.url().substring(0, 150)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the login page to FULLY RENDER (email input visible)
|
||||||
|
_log('info', 'Waiting for login page to fully render...');
|
||||||
|
try {
|
||||||
|
await page.waitForSelector(
|
||||||
|
'input[name="loginfmt"], input[type="email"], button:has-text("Sign in"), button:has-text("Join a meeting")',
|
||||||
|
{ timeout: 15000, state: 'visible' },
|
||||||
|
);
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
} catch {
|
||||||
|
_log('warn', 'Login page elements not found, taking screenshot anyway');
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
}
|
}
|
||||||
await _screenshotStep('1 - Login-Seite');
|
await _screenshotStep('1 - Login-Seite');
|
||||||
|
|
||||||
// --- Step 2: Login ---
|
// =====================================================================
|
||||||
|
// STEP 2: Microsoft Login
|
||||||
|
// =====================================================================
|
||||||
if (botAccountEmail && botAccountPassword) {
|
if (botAccountEmail && botAccountPassword) {
|
||||||
authAttempted = true;
|
authAttempted = true;
|
||||||
_log('info', `Authenticating as ${botAccountEmail} (skipNavigation=true to preserve OAuth context)`);
|
|
||||||
|
|
||||||
const authProcedure = new AuthProcedure(page, logger);
|
// Check if we're on login.microsoftonline.com or on the Teams landing page
|
||||||
authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, true);
|
const currentUrl = page.url();
|
||||||
_log(authSuccess ? 'info' : 'warn', `Auth result: ${authSuccess ? 'success' : 'failed'}`);
|
const onMsLogin = currentUrl.includes('login.microsoftonline.com') || currentUrl.includes('login.live.com');
|
||||||
|
|
||||||
if (authSuccess) {
|
if (onMsLogin) {
|
||||||
// Wait for redirect chain back to Teams
|
_log('info', `On MS login page, authenticating as ${botAccountEmail}`);
|
||||||
_log('info', 'Waiting for redirect back to Teams...');
|
const authProcedure = new AuthProcedure(page, logger);
|
||||||
try {
|
authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, true);
|
||||||
await page.waitForURL('**/teams.microsoft.com/**', { timeout: 30000 });
|
_log(authSuccess ? 'info' : 'warn', `Auth result: ${authSuccess ? 'success' : 'failed'}`);
|
||||||
} catch {
|
} else {
|
||||||
_log('warn', `No Teams redirect after 30s, current URL: ${page.url().substring(0, 150)}`);
|
_log('info', 'Not on MS login page — Teams may already be loaded. Skipping auth step.');
|
||||||
}
|
authSuccess = null;
|
||||||
await page.waitForTimeout(5000);
|
|
||||||
}
|
}
|
||||||
await _screenshotStep('2 - Nach Login');
|
|
||||||
} else {
|
} else {
|
||||||
_log('info', 'No credentials provided — skipping auth');
|
_log('info', 'No credentials provided — skipping auth');
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Step 3: Teams App geladen ---
|
// =====================================================================
|
||||||
_log('info', 'Waiting 10s for Teams app to fully load...');
|
// STEP 3: Wait for Teams landing page to fully render
|
||||||
await page.waitForTimeout(10000);
|
// =====================================================================
|
||||||
await _screenshotStep('3 - Teams App');
|
_log('info', 'Waiting for Teams landing page to fully render...');
|
||||||
|
|
||||||
// --- Step 4: Click "Join a meeting" ---
|
// Wait for redirect back to Teams (if we were on login page)
|
||||||
_log('info', 'Step 4: Looking for "Join a meeting" button...');
|
if (authSuccess) {
|
||||||
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 {
|
try {
|
||||||
const btn = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' });
|
await page.waitForURL('**/teams.microsoft.com/**', { timeout: 30000 });
|
||||||
if (btn) {
|
|
||||||
await btn.click();
|
|
||||||
_log('info', `Clicked "Join a meeting": ${selector}`);
|
|
||||||
joinMeetingClicked = true;
|
|
||||||
await page.waitForTimeout(3000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
// Try next selector
|
_log('warn', `No Teams redirect, current URL: ${page.url().substring(0, 150)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!joinMeetingClicked) {
|
// Wait for either "Sign in" or "Join a meeting" button to be visible
|
||||||
_log('warn', 'Could not find "Join a meeting" button — logging all visible buttons');
|
// This is the Teams landing page ("Everyone together in Teams")
|
||||||
try {
|
_log('info', 'Waiting for "Sign in" or "Join a meeting" button to appear...');
|
||||||
const buttons = await page.evaluate(() => {
|
try {
|
||||||
const btns = document.querySelectorAll('button, a[role="button"], [role="menuitem"]');
|
await page.waitForSelector(
|
||||||
return Array.from(btns).slice(0, 30).map(b => {
|
'button:has-text("Sign in"), button:has-text("Join a meeting"), button:has-text("Anmelden"), button:has-text("An Besprechung teilnehmen")',
|
||||||
const text = (b.textContent || '').trim().substring(0, 80);
|
{ timeout: 30000, state: 'visible' },
|
||||||
const tid = b.getAttribute('data-tid') || '';
|
);
|
||||||
const title = b.getAttribute('title') || '';
|
await page.waitForTimeout(3000);
|
||||||
return `[${b.tagName} tid="${tid}" title="${title}"] ${text}`;
|
_log('info', 'Teams landing page loaded');
|
||||||
});
|
} catch {
|
||||||
|
_log('warn', 'Landing page buttons not found after 30s');
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
}
|
||||||
|
await _screenshotStep('2 - Teams Landingpage');
|
||||||
|
|
||||||
|
// Log all visible buttons for debugging
|
||||||
|
try {
|
||||||
|
const buttons = await page.evaluate(() => {
|
||||||
|
const btns = document.querySelectorAll('button, a[role="button"]');
|
||||||
|
return Array.from(btns).slice(0, 20).map(b => {
|
||||||
|
const text = (b.textContent || '').trim().substring(0, 80);
|
||||||
|
const tid = b.getAttribute('data-tid') || '';
|
||||||
|
return `[${b.tagName} tid="${tid}"] ${text}`;
|
||||||
});
|
});
|
||||||
buttons.forEach(b => _log('info', ` Button: ${b}`));
|
});
|
||||||
} catch (e) {
|
buttons.forEach(b => _log('info', ` Visible button: ${b}`));
|
||||||
_log('warn', `Could not enumerate buttons: ${e}`);
|
} catch {
|
||||||
}
|
// Ignore
|
||||||
}
|
}
|
||||||
await _screenshotStep('4 - Join a meeting');
|
|
||||||
|
|
||||||
// --- Step 5: Enter meeting URL ---
|
// =====================================================================
|
||||||
if (joinMeetingClicked) {
|
// STEP 4: Click variant-specific button
|
||||||
_log('info', `Step 5: Entering meeting URL: ${meetingUrl.substring(0, 80)}`);
|
// =====================================================================
|
||||||
|
if (variant.id === 'signIn') {
|
||||||
|
// --- VARIANT A: Click "Sign in" ---
|
||||||
|
_log('info', 'Step 3: Clicking "Sign in" on Teams landing page...');
|
||||||
|
const signInSelectors = [
|
||||||
|
'button:has-text("Sign in")',
|
||||||
|
'button:has-text("Anmelden")',
|
||||||
|
'a:has-text("Sign in")',
|
||||||
|
'a:has-text("Anmelden")',
|
||||||
|
];
|
||||||
|
|
||||||
|
let clicked = false;
|
||||||
|
for (const selector of signInSelectors) {
|
||||||
|
try {
|
||||||
|
const btn = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' });
|
||||||
|
if (btn) {
|
||||||
|
await btn.click();
|
||||||
|
_log('info', `Clicked: ${selector}`);
|
||||||
|
clicked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clicked) {
|
||||||
|
_log('warn', '"Sign in" button not found on landing page');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the resulting page to FULLY load
|
||||||
|
_log('info', 'Waiting for resulting page to fully load...');
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
try {
|
||||||
|
await page.waitForLoadState('networkidle', { timeout: 20000 });
|
||||||
|
} catch {
|
||||||
|
_log('warn', 'networkidle timeout, continuing');
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
await _screenshotStep('3 - Nach "Sign in"');
|
||||||
|
|
||||||
|
} else if (variant.id === 'joinMeeting') {
|
||||||
|
// --- VARIANT B: Click "Join a meeting" ---
|
||||||
|
_log('info', 'Step 3: Clicking "Join a meeting" on Teams landing page...');
|
||||||
|
const joinSelectors = [
|
||||||
|
'button:has-text("Join a meeting")',
|
||||||
|
'button:has-text("An Besprechung teilnehmen")',
|
||||||
|
'a:has-text("Join a meeting")',
|
||||||
|
'a:has-text("An Besprechung teilnehmen")',
|
||||||
|
];
|
||||||
|
|
||||||
|
let clicked = false;
|
||||||
|
for (const selector of joinSelectors) {
|
||||||
|
try {
|
||||||
|
const btn = await page.waitForSelector(selector, { timeout: 5000, state: 'visible' });
|
||||||
|
if (btn) {
|
||||||
|
await btn.click();
|
||||||
|
_log('info', `Clicked: ${selector}`);
|
||||||
|
clicked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clicked) {
|
||||||
|
_log('warn', '"Join a meeting" button not found on landing page');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the resulting page to FULLY load
|
||||||
|
_log('info', 'Waiting for resulting page to fully load...');
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
try {
|
||||||
|
await page.waitForLoadState('networkidle', { timeout: 20000 });
|
||||||
|
} catch {
|
||||||
|
_log('warn', 'networkidle timeout, continuing');
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
await _screenshotStep('3 - Nach "Join a meeting"');
|
||||||
|
|
||||||
|
// If there's an input for meeting URL, enter it
|
||||||
|
_log('info', 'Looking for meeting URL input...');
|
||||||
const meetingInputSelectors = [
|
const meetingInputSelectors = [
|
||||||
'input[placeholder*="meeting"]',
|
'input[placeholder*="meeting" i]',
|
||||||
'input[placeholder*="Besprechung"]',
|
'input[placeholder*="code" i]',
|
||||||
'input[placeholder*="code"]',
|
'input[placeholder*="link" i]',
|
||||||
'input[placeholder*="Code"]',
|
'input[placeholder*="ID" i]',
|
||||||
'input[placeholder*="link"]',
|
|
||||||
'input[placeholder*="Link"]',
|
|
||||||
'input[data-tid="join-meeting-input"]',
|
|
||||||
'input[type="text"]',
|
'input[type="text"]',
|
||||||
'input[type="url"]',
|
'input[type="url"]',
|
||||||
];
|
];
|
||||||
|
|
@ -323,7 +393,7 @@ async function _runVariant(
|
||||||
await input.fill(meetingUrl);
|
await input.fill(meetingUrl);
|
||||||
_log('info', `Entered meeting URL in: ${selector}`);
|
_log('info', `Entered meeting URL in: ${selector}`);
|
||||||
meetingInputFound = true;
|
meetingInputFound = true;
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(3000);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -331,70 +401,49 @@ async function _runVariant(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!meetingInputFound) {
|
if (meetingInputFound) {
|
||||||
_log('warn', 'Could not find meeting URL input field');
|
await page.waitForTimeout(5000);
|
||||||
|
await _screenshotStep('4 - Meeting URL eingegeben');
|
||||||
|
} else {
|
||||||
|
_log('warn', 'No meeting URL input field found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 ---
|
// =====================================================================
|
||||||
|
// FINAL: Log result
|
||||||
|
// =====================================================================
|
||||||
_log('info', `Final URL: ${page.url().substring(0, 150)}`);
|
_log('info', `Final URL: ${page.url().substring(0, 150)}`);
|
||||||
const detection = await _detectPageType(page);
|
|
||||||
|
|
||||||
// Use last screenshot as the main screenshot for backward compatibility
|
|
||||||
const screenshot = screenshots.length > 0 ? screenshots[screenshots.length - 1].data : undefined;
|
const screenshot = screenshots.length > 0 ? screenshots[screenshots.length - 1].data : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
variantId: variant.id,
|
variantId: variant.id,
|
||||||
variantName: variant.name,
|
variantName: variant.name,
|
||||||
success: true,
|
success: true,
|
||||||
pageType: detection.pageType,
|
pageType: 'unknown',
|
||||||
finalUrl: page.url(),
|
finalUrl: page.url(),
|
||||||
hasSignInLink: detection.hasSignInLink,
|
hasSignInLink: false,
|
||||||
hasNameInput: detection.hasNameInput,
|
hasNameInput: false,
|
||||||
hasJoinButton: detection.hasJoinButton,
|
hasJoinButton: false,
|
||||||
authAttempted,
|
authAttempted,
|
||||||
authSuccess,
|
authSuccess,
|
||||||
screenshot,
|
screenshot,
|
||||||
screenshots,
|
screenshots,
|
||||||
durationMs: Date.now() - startTime,
|
durationMs: Date.now() - startTime,
|
||||||
detectedSignals: detection.signals,
|
detectedSignals: [],
|
||||||
logs: variantLogs,
|
logs: variantLogs,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
_log('error', `Variant failed: ${String(err).substring(0, 300)}`);
|
_log('error', `Variant failed: ${String(err).substring(0, 300)}`);
|
||||||
|
|
||||||
// Try to take an error screenshot
|
|
||||||
let screenshot: string | undefined;
|
let screenshot: string | undefined;
|
||||||
|
const screenshots: StepScreenshot[] = [];
|
||||||
try {
|
try {
|
||||||
if (context) {
|
if (context) {
|
||||||
const pages = context.pages();
|
const pages = context.pages();
|
||||||
if (pages.length > 0) {
|
if (pages.length > 0) {
|
||||||
screenshot = await _takeScreenshot(pages[0]);
|
screenshot = await _takeScreenshot(pages[0]);
|
||||||
|
if (screenshot) screenshots.push({ label: 'Error', data: screenshot });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -413,6 +462,7 @@ async function _runVariant(
|
||||||
authAttempted: false,
|
authAttempted: false,
|
||||||
authSuccess: null,
|
authSuccess: null,
|
||||||
screenshot,
|
screenshot,
|
||||||
|
screenshots,
|
||||||
durationMs: Date.now() - startTime,
|
durationMs: Date.now() - startTime,
|
||||||
error: String(err),
|
error: String(err),
|
||||||
detectedSignals: [],
|
detectedSignals: [],
|
||||||
|
|
@ -438,22 +488,9 @@ interface LaunchResult {
|
||||||
page: Page;
|
page: Page;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _launchBrowserForVariant(variant: AuthTestVariant): Promise<LaunchResult> {
|
async function _launchBrowserForVariant(_variant: AuthTestVariant): Promise<LaunchResult> {
|
||||||
_requireDisplay(); // All variants are headful
|
_requireDisplay();
|
||||||
|
return _launchChromiumHeadful(_BROWSER_ARGS_CLEAN);
|
||||||
switch (variant.id) {
|
|
||||||
case 'chromiumClean':
|
|
||||||
return _launchChromiumHeadful(_BROWSER_ARGS_CLEAN);
|
|
||||||
|
|
||||||
case 'chromiumNoAutomation':
|
|
||||||
return _launchChromiumHeadful(_BROWSER_ARGS_NO_AUTOMATION);
|
|
||||||
|
|
||||||
case 'rebrowserHeadful':
|
|
||||||
return _launchRebrowserHeadful();
|
|
||||||
|
|
||||||
default:
|
|
||||||
return _launchChromiumHeadful(_BROWSER_ARGS_CLEAN);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1100,26 +1137,16 @@ function _createErrorResult(variant: AuthTestVariant, error: string): AuthTestRe
|
||||||
* Generate a recommendation based on test results.
|
* Generate a recommendation based on test results.
|
||||||
*/
|
*/
|
||||||
function _generateRecommendation(results: AuthTestResult[]): string {
|
function _generateRecommendation(results: AuthTestResult[]): string {
|
||||||
const v2Variants = results.filter(r => r.pageType === 'v2');
|
const authSuccess = results.filter(r => r.authAttempted && r.authSuccess === true);
|
||||||
const authSuccess = results.find(r => r.authAttempted && r.authSuccess === true);
|
const failures = results.filter(r => !r.success);
|
||||||
|
|
||||||
if (authSuccess) {
|
if (authSuccess.length > 0) {
|
||||||
return `Variante "${authSuccess.variantName}" hat Auth erfolgreich durchgefuehrt! ` +
|
return `Microsoft-Login erfolgreich. Screenshots pruefen, um den besten Pfad zu waehlen.`;
|
||||||
`Auth-Flow kann mit dieser Konfiguration aktiviert werden.`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2Variants.length > 0) {
|
if (failures.length === results.length) {
|
||||||
const names = v2Variants.map(v => `"${v.variantName}"`).join(', ');
|
return 'Beide Varianten fehlgeschlagen. Logs und Screenshots pruefen.';
|
||||||
return `${v2Variants.length} Variante(n) haben /v2/ erhalten: ${names}. ` +
|
|
||||||
`Auth ist prinzipiell moeglich — Auth-Flow kann re-aktiviert werden.`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const successVariants = results.filter(r => r.success);
|
return 'Test abgeschlossen. Screenshots pruefen und naechste Schritte planen.';
|
||||||
if (successVariants.every(r => r.pageType === 'lightMeetings')) {
|
|
||||||
return 'Alle Varianten wurden auf light-meetings umgeleitet. ' +
|
|
||||||
'Teams blockiert den authentifizierten Join fuer alle getesteten Browser-Konfigurationen. ' +
|
|
||||||
'Weitere Optionen: Graph API / Bot Framework oder andere Stealth-Ansaetze evaluieren.';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Test abgeschlossen. Ergebnisse pruefen und naechste Schritte planen.';
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue