feat: replace failed variants 1-5 with direct /v2/ test, add per-variant logs
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
feb49a4594
commit
2bb0dd20cc
1 changed files with 114 additions and 38 deletions
|
|
@ -27,6 +27,7 @@ export interface AuthTestResult {
|
|||
durationMs: number;
|
||||
error?: string;
|
||||
detectedSignals: string[];
|
||||
logs: string[];
|
||||
}
|
||||
|
||||
export interface AuthTestResults {
|
||||
|
|
@ -42,29 +43,9 @@ export interface AuthTestResults {
|
|||
|
||||
const _VARIANTS: AuthTestVariant[] = [
|
||||
{
|
||||
id: 'baseline',
|
||||
name: 'Baseline (aktuell)',
|
||||
description: 'Vanilla Playwright headless, basic stealth, anon=true',
|
||||
},
|
||||
{
|
||||
id: 'rebrowser',
|
||||
name: 'rebrowser-playwright',
|
||||
description: 'rebrowser-playwright headless, CDP-Leak-Fixes, ohne anon',
|
||||
},
|
||||
{
|
||||
id: 'playwrightExtra',
|
||||
name: 'playwright-extra + stealth',
|
||||
description: 'playwright-extra mit stealth plugin headless, ohne anon',
|
||||
},
|
||||
{
|
||||
id: 'headful',
|
||||
name: 'Headful',
|
||||
description: 'Playwright headful (headless: false), enhanced stealth, ohne anon',
|
||||
},
|
||||
{
|
||||
id: 'headfulAuth',
|
||||
name: 'Headful + Auth',
|
||||
description: 'Playwright headful mit vollstaendigem Auth-Flow (System-Bot Credentials)',
|
||||
id: 'headfulDirect',
|
||||
name: 'Headful + Direct /v2/',
|
||||
description: 'Playwright headful, navigiert direkt zu /v2/ URL (umgeht launcher.html komplett)',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -90,7 +71,7 @@ const _BROWSER_ARGS = [
|
|||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Run all 5 auth detection test variants against a Teams meeting URL.
|
||||
* Run all 6 auth detection test variants against a Teams meeting URL.
|
||||
* Does NOT join the meeting — only checks which page Teams serves.
|
||||
*/
|
||||
export async function runAuthTests(
|
||||
|
|
@ -144,6 +125,15 @@ async function _runVariant(
|
|||
const startTime = Date.now();
|
||||
let browser: Browser | null = null;
|
||||
let context: BrowserContext | null = null;
|
||||
const variantLogs: string[] = [];
|
||||
|
||||
const _log = (level: 'info' | 'warn' | 'error', msg: string) => {
|
||||
const prefix = `[${level.toUpperCase()}]`;
|
||||
variantLogs.push(`${prefix} ${msg}`);
|
||||
if (level === 'error') logger.error(`[AuthTest:${variant.id}] ${msg}`);
|
||||
else if (level === 'warn') logger.warn(`[AuthTest:${variant.id}] ${msg}`);
|
||||
else logger.info(`[AuthTest:${variant.id}] ${msg}`);
|
||||
};
|
||||
|
||||
try {
|
||||
// Launch browser based on variant
|
||||
|
|
@ -152,29 +142,69 @@ async function _runVariant(
|
|||
context = launchResult.context;
|
||||
const page = launchResult.page;
|
||||
|
||||
// Resolve meeting URL (with or without anon=true)
|
||||
// Collect browser console messages
|
||||
page.on('console', (msg) => {
|
||||
const type = msg.type();
|
||||
if (type === 'error' || type === 'warning') {
|
||||
variantLogs.push(`[CONSOLE:${type}] ${msg.text().substring(0, 200)}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Collect page errors
|
||||
page.on('pageerror', (err) => {
|
||||
variantLogs.push(`[PAGE_ERROR] ${String(err).substring(0, 200)}`);
|
||||
});
|
||||
|
||||
// Track navigation/redirects
|
||||
page.on('framenavigated', (frame) => {
|
||||
if (frame === page.mainFrame()) {
|
||||
variantLogs.push(`[NAV] ${frame.url().substring(0, 150)}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (variant.id === 'headfulDirect') {
|
||||
// Variant 6: Navigate directly to /v2/ URL, bypassing launcher.html entirely
|
||||
const directUrl = _buildDirectV2Url(meetingUrl);
|
||||
_log('info', `Direct /v2/ navigation: ${directUrl.substring(0, 120)}`);
|
||||
|
||||
await page.goto(directUrl, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
// Wait and log where Teams actually redirects to
|
||||
await page.waitForTimeout(8000);
|
||||
const currentUrl = page.url();
|
||||
_log('info', `After redirect: ${currentUrl.substring(0, 120)}`);
|
||||
|
||||
if (currentUrl.includes('light-meetings')) {
|
||||
_log('warn', 'Teams redirected /v2/ to light-meetings');
|
||||
} else if (currentUrl.includes('/v2/')) {
|
||||
_log('info', '/v2/ page loaded successfully!');
|
||||
}
|
||||
} else {
|
||||
// Standard flow: resolve URL, navigate, click launcher
|
||||
const useAnon = variant.id === 'baseline';
|
||||
const launchUrl = await _resolveMeetingUrl(meetingUrl, useAnon);
|
||||
logger.info(`[AuthTest:${variant.id}] Navigating to: ${launchUrl.substring(0, 100)}`);
|
||||
_log('info', `Navigating to: ${launchUrl.substring(0, 100)}`);
|
||||
|
||||
// Navigate to meeting
|
||||
await page.goto(launchUrl, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
// Handle launcher dialog ("Continue on this browser")
|
||||
await _handleLauncher(page);
|
||||
|
||||
// Wait for the pre-join page to settle
|
||||
await page.waitForTimeout(5000);
|
||||
}
|
||||
|
||||
// If this is the auth variant, attempt login
|
||||
let authAttempted = false;
|
||||
let authSuccess: boolean | null = null;
|
||||
if (variant.id === 'headfulAuth' && botAccountEmail && botAccountPassword) {
|
||||
authAttempted = true;
|
||||
_log('info', `Attempting auth as ${botAccountEmail}`);
|
||||
authSuccess = await _attemptAuth(page, botAccountEmail, botAccountPassword);
|
||||
_log(authSuccess ? 'info' : 'warn', `Auth result: ${authSuccess ? 'success' : 'failed'}`);
|
||||
}
|
||||
|
||||
// Detect page type and gather signals
|
||||
|
|
@ -197,8 +227,11 @@ async function _runVariant(
|
|||
screenshot,
|
||||
durationMs: Date.now() - startTime,
|
||||
detectedSignals: detection.signals,
|
||||
logs: variantLogs,
|
||||
};
|
||||
} catch (err) {
|
||||
_log('error', `Variant failed: ${String(err).substring(0, 300)}`);
|
||||
|
||||
// Try to take an error screenshot
|
||||
let screenshot: string | undefined;
|
||||
try {
|
||||
|
|
@ -227,9 +260,9 @@ async function _runVariant(
|
|||
durationMs: Date.now() - startTime,
|
||||
error: String(err),
|
||||
detectedSignals: [],
|
||||
logs: variantLogs,
|
||||
};
|
||||
} finally {
|
||||
// Clean up browser
|
||||
try {
|
||||
if (context) await context.close();
|
||||
if (browser) await browser.close();
|
||||
|
|
@ -268,6 +301,10 @@ async function _launchBrowserForVariant(variant: AuthTestVariant): Promise<Launc
|
|||
_requireDisplay();
|
||||
return _launchVanilla(false, 'enhanced');
|
||||
|
||||
case 'headfulDirect':
|
||||
_requireDisplay();
|
||||
return _launchVanilla(false, 'enhanced');
|
||||
|
||||
default:
|
||||
return _launchVanilla(true, 'basic');
|
||||
}
|
||||
|
|
@ -555,6 +592,43 @@ async function _resolveMeetingUrl(meetingUrl: string, withAnon: boolean): Promis
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a direct /v2/ URL that bypasses the launcher.html page entirely.
|
||||
* Constructs the Teams pre-join URL with the /v2/ path, hoping Teams
|
||||
* doesn't redirect to light-meetings when the initial URL is already /v2/.
|
||||
*/
|
||||
function _buildDirectV2Url(meetingUrl: string): string {
|
||||
const trimmed = meetingUrl.trim();
|
||||
|
||||
// Extract the meeting context from the short URL or full URL
|
||||
// Short URL format: https://teams.microsoft.com/meet/XXXXXXX?p=YYYY
|
||||
// Full URL: https://teams.microsoft.com/l/meetup-join/...
|
||||
// /v2/ URL: https://teams.microsoft.com/v2/#/l/meetup-join/...
|
||||
|
||||
try {
|
||||
const parsed = new URL(trimmed);
|
||||
|
||||
// If it's already a full meetup-join URL, inject /v2/
|
||||
if (parsed.pathname.includes('/l/meetup-join/')) {
|
||||
const meetupPath = parsed.pathname.substring(parsed.pathname.indexOf('/l/meetup-join/'));
|
||||
return `https://teams.microsoft.com/v2/#${meetupPath}${parsed.search}`;
|
||||
}
|
||||
|
||||
// For short URLs (/meet/...), we need to build a /v2/ URL
|
||||
// Teams /v2/ format with meeting context
|
||||
if (parsed.pathname.includes('/meet/')) {
|
||||
const meetId = parsed.pathname.split('/meet/')[1];
|
||||
const params = parsed.search;
|
||||
return `https://teams.microsoft.com/v2/#/meet/${meetId}${params}`;
|
||||
}
|
||||
} catch {
|
||||
// URL parsing failed
|
||||
}
|
||||
|
||||
// Fallback: just prepend /v2/# to the path
|
||||
return `https://teams.microsoft.com/v2/#/l/meetup-join/${encodeURIComponent(trimmed)}`;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// LAUNCHER HANDLING
|
||||
// ============================================================================
|
||||
|
|
@ -797,6 +871,7 @@ function _createSkippedResult(variant: AuthTestVariant, reason: string): AuthTes
|
|||
durationMs: 0,
|
||||
error: `Uebersprungen: ${reason}`,
|
||||
detectedSignals: [],
|
||||
logs: [`[INFO] Skipped: ${reason}`],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +890,7 @@ function _createErrorResult(variant: AuthTestVariant, error: string): AuthTestRe
|
|||
durationMs: 0,
|
||||
error,
|
||||
detectedSignals: [],
|
||||
logs: [`[ERROR] ${error}`],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue