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[] = [
|
||||
{
|
||||
id: 'chromiumClean',
|
||||
name: 'Chromium Headful Clean',
|
||||
description: 'Playwright Chromium headful, enhanced stealth + realistic devices, keine fake-flags',
|
||||
id: 'signIn',
|
||||
name: 'Pfad: Sign in',
|
||||
description: 'Nach Login auf Teams-Landingpage "Sign in" klicken — was kommt?',
|
||||
},
|
||||
{
|
||||
id: 'chromiumNoAutomation',
|
||||
name: 'Chromium No-Automation',
|
||||
description: 'Chromium headful, zusaetzlich --disable-extensions, --no-first-run',
|
||||
},
|
||||
{
|
||||
id: 'rebrowserHeadful',
|
||||
name: 'rebrowser-playwright Headful',
|
||||
description: 'rebrowser-playwright headful, CDP-Leak-Fixes + realistic devices',
|
||||
id: 'joinMeeting',
|
||||
name: 'Pfad: Join a meeting',
|
||||
description: 'Nach Login auf Teams-Landingpage "Join a meeting" klicken — was kommt?',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -69,7 +64,7 @@ const _VARIANTS: AuthTestVariant[] = [
|
|||
// CONSTANTS
|
||||
// ============================================================================
|
||||
|
||||
const _VARIANT_TIMEOUT_MS = 60000;
|
||||
const _VARIANT_TIMEOUT_MS = 120000;
|
||||
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';
|
||||
|
||||
|
|
@ -96,8 +91,8 @@ const _BROWSER_ARGS_NO_AUTOMATION = [
|
|||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Run auth detection test variants: login at teams.microsoft.com, screenshot where we land.
|
||||
* Does NOT navigate to any meeting URL — only tests if Teams login works.
|
||||
* Run auth tests: 2 variants — "Sign in" and "Join a meeting" on the Teams landing page.
|
||||
* Both do full Microsoft login first, then click different buttons to see what page loads.
|
||||
*/
|
||||
export async function runAuthTests(
|
||||
meetingUrl: string,
|
||||
|
|
@ -109,7 +104,6 @@ export async function runAuthTests(
|
|||
const results: AuthTestResult[] = [];
|
||||
|
||||
for (const variant of _VARIANTS) {
|
||||
// All variants require credentials
|
||||
if (!botAccountEmail || !botAccountPassword) {
|
||||
results.push(_createSkippedResult(variant, 'Keine Credentials angegeben'));
|
||||
continue;
|
||||
|
|
@ -119,7 +113,7 @@ export async function runAuthTests(
|
|||
try {
|
||||
const result = await _runVariant(variant, meetingUrl, botAccountEmail, botAccountPassword);
|
||||
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) {
|
||||
logger.error(`[AuthTest] Variant ${variant.name} failed:`, err);
|
||||
results.push(_createErrorResult(variant, String(err)));
|
||||
|
|
@ -161,39 +155,30 @@ async function _runVariant(
|
|||
};
|
||||
|
||||
try {
|
||||
// Launch browser based on variant
|
||||
const launchResult = await _launchBrowserForVariant(variant);
|
||||
// Always use Chromium Headful Clean for both variants
|
||||
const launchResult = await _launchChromiumHeadful(_BROWSER_ARGS_CLEAN);
|
||||
browser = launchResult.browser;
|
||||
context = launchResult.context;
|
||||
const page = launchResult.page;
|
||||
|
||||
// Collect browser console messages
|
||||
// Collect browser console messages (only errors, to reduce noise)
|
||||
page.on('console', (msg) => {
|
||||
const type = msg.type();
|
||||
if (type === 'error' || type === 'warning') {
|
||||
if (type === 'error') {
|
||||
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)}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 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[] = [];
|
||||
|
|
@ -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', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
// Wait for Teams to redirect to login.microsoftonline.com (up to 30s)
|
||||
_log('info', 'Waiting for login redirect to login.microsoftonline.com...');
|
||||
// Wait for login redirect OR the Teams landing page to appear
|
||||
_log('info', 'Waiting for login.microsoftonline.com redirect...');
|
||||
try {
|
||||
await page.waitForURL('**/login.microsoftonline.com/**', { timeout: 30000 });
|
||||
_log('info', `Redirected to login: ${page.url().substring(0, 150)}`);
|
||||
} 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');
|
||||
|
||||
// --- Step 2: Login ---
|
||||
// =====================================================================
|
||||
// STEP 2: Microsoft Login
|
||||
// =====================================================================
|
||||
if (botAccountEmail && botAccountPassword) {
|
||||
authAttempted = true;
|
||||
_log('info', `Authenticating as ${botAccountEmail} (skipNavigation=true to preserve OAuth context)`);
|
||||
|
||||
// Check if we're on login.microsoftonline.com or on the Teams landing page
|
||||
const currentUrl = page.url();
|
||||
const onMsLogin = currentUrl.includes('login.microsoftonline.com') || currentUrl.includes('login.live.com');
|
||||
|
||||
if (onMsLogin) {
|
||||
_log('info', `On MS login page, authenticating as ${botAccountEmail}`);
|
||||
const authProcedure = new AuthProcedure(page, logger);
|
||||
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)}`);
|
||||
} else {
|
||||
_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 {
|
||||
_log('info', 'No credentials provided — skipping auth');
|
||||
}
|
||||
|
||||
// --- Step 3: Teams App geladen ---
|
||||
_log('info', 'Waiting 10s for Teams app to fully load...');
|
||||
await page.waitForTimeout(10000);
|
||||
await _screenshotStep('3 - Teams App');
|
||||
// =====================================================================
|
||||
// STEP 3: Wait for Teams landing page to fully render
|
||||
// =====================================================================
|
||||
_log('info', 'Waiting for Teams landing page to fully render...');
|
||||
|
||||
// --- 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"]',
|
||||
// Wait for redirect back to Teams (if we were on login page)
|
||||
if (authSuccess) {
|
||||
try {
|
||||
await page.waitForURL('**/teams.microsoft.com/**', { timeout: 30000 });
|
||||
} catch {
|
||||
_log('warn', `No Teams redirect, current URL: ${page.url().substring(0, 150)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for either "Sign in" or "Join a meeting" button to be visible
|
||||
// This is the Teams landing page ("Everyone together in Teams")
|
||||
_log('info', 'Waiting for "Sign in" or "Join a meeting" button to appear...');
|
||||
try {
|
||||
await page.waitForSelector(
|
||||
'button:has-text("Sign in"), button:has-text("Join a meeting"), button:has-text("Anmelden"), button:has-text("An Besprechung teilnehmen")',
|
||||
{ timeout: 30000, state: 'visible' },
|
||||
);
|
||||
await page.waitForTimeout(3000);
|
||||
_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', ` Visible button: ${b}`));
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// STEP 4: Click variant-specific button
|
||||
// =====================================================================
|
||||
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 joinMeetingClicked = false;
|
||||
for (const selector of joinMeetingSelectors) {
|
||||
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 "Join a meeting": ${selector}`);
|
||||
joinMeetingClicked = true;
|
||||
await page.waitForTimeout(3000);
|
||||
_log('info', `Clicked: ${selector}`);
|
||||
clicked = true;
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// Try next selector
|
||||
// Try next
|
||||
}
|
||||
}
|
||||
|
||||
if (!joinMeetingClicked) {
|
||||
_log('warn', 'Could not find "Join a meeting" button — logging all visible buttons');
|
||||
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 {
|
||||
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 page.waitForLoadState('networkidle', { timeout: 20000 });
|
||||
} catch {
|
||||
_log('warn', 'networkidle timeout, continuing');
|
||||
}
|
||||
}
|
||||
await _screenshotStep('4 - Join a meeting');
|
||||
await page.waitForTimeout(5000);
|
||||
await _screenshotStep('3 - Nach "Sign in"');
|
||||
|
||||
// --- Step 5: Enter meeting URL ---
|
||||
if (joinMeetingClicked) {
|
||||
_log('info', `Step 5: Entering meeting URL: ${meetingUrl.substring(0, 80)}`);
|
||||
} 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 = [
|
||||
'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[placeholder*="meeting" i]',
|
||||
'input[placeholder*="code" i]',
|
||||
'input[placeholder*="link" i]',
|
||||
'input[placeholder*="ID" i]',
|
||||
'input[type="text"]',
|
||||
'input[type="url"]',
|
||||
];
|
||||
|
|
@ -323,33 +393,7 @@ async function _runVariant(
|
|||
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)`);
|
||||
await page.waitForTimeout(3000);
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -357,44 +401,49 @@ async function _runVariant(
|
|||
}
|
||||
}
|
||||
|
||||
if (meetingInputFound) {
|
||||
await page.waitForTimeout(5000);
|
||||
await _screenshotStep('5 - Meeting URL eingegeben');
|
||||
await _screenshotStep('4 - Meeting URL eingegeben');
|
||||
} else {
|
||||
_log('warn', 'No meeting URL input field found');
|
||||
}
|
||||
}
|
||||
|
||||
// --- Final: detect page type ---
|
||||
// =====================================================================
|
||||
// FINAL: Log result
|
||||
// =====================================================================
|
||||
_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;
|
||||
|
||||
return {
|
||||
variantId: variant.id,
|
||||
variantName: variant.name,
|
||||
success: true,
|
||||
pageType: detection.pageType,
|
||||
pageType: 'unknown',
|
||||
finalUrl: page.url(),
|
||||
hasSignInLink: detection.hasSignInLink,
|
||||
hasNameInput: detection.hasNameInput,
|
||||
hasJoinButton: detection.hasJoinButton,
|
||||
hasSignInLink: false,
|
||||
hasNameInput: false,
|
||||
hasJoinButton: false,
|
||||
authAttempted,
|
||||
authSuccess,
|
||||
screenshot,
|
||||
screenshots,
|
||||
durationMs: Date.now() - startTime,
|
||||
detectedSignals: detection.signals,
|
||||
detectedSignals: [],
|
||||
logs: variantLogs,
|
||||
};
|
||||
} catch (err) {
|
||||
_log('error', `Variant failed: ${String(err).substring(0, 300)}`);
|
||||
|
||||
// Try to take an error screenshot
|
||||
let screenshot: string | undefined;
|
||||
const screenshots: StepScreenshot[] = [];
|
||||
try {
|
||||
if (context) {
|
||||
const pages = context.pages();
|
||||
if (pages.length > 0) {
|
||||
screenshot = await _takeScreenshot(pages[0]);
|
||||
if (screenshot) screenshots.push({ label: 'Error', data: screenshot });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -413,6 +462,7 @@ async function _runVariant(
|
|||
authAttempted: false,
|
||||
authSuccess: null,
|
||||
screenshot,
|
||||
screenshots,
|
||||
durationMs: Date.now() - startTime,
|
||||
error: String(err),
|
||||
detectedSignals: [],
|
||||
|
|
@ -438,22 +488,9 @@ interface LaunchResult {
|
|||
page: Page;
|
||||
}
|
||||
|
||||
async function _launchBrowserForVariant(variant: AuthTestVariant): Promise<LaunchResult> {
|
||||
_requireDisplay(); // All variants are headful
|
||||
|
||||
switch (variant.id) {
|
||||
case 'chromiumClean':
|
||||
async function _launchBrowserForVariant(_variant: AuthTestVariant): Promise<LaunchResult> {
|
||||
_requireDisplay();
|
||||
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.
|
||||
*/
|
||||
function _generateRecommendation(results: AuthTestResult[]): string {
|
||||
const v2Variants = results.filter(r => r.pageType === 'v2');
|
||||
const authSuccess = results.find(r => r.authAttempted && r.authSuccess === true);
|
||||
const authSuccess = results.filter(r => r.authAttempted && r.authSuccess === true);
|
||||
const failures = results.filter(r => !r.success);
|
||||
|
||||
if (authSuccess) {
|
||||
return `Variante "${authSuccess.variantName}" hat Auth erfolgreich durchgefuehrt! ` +
|
||||
`Auth-Flow kann mit dieser Konfiguration aktiviert werden.`;
|
||||
if (authSuccess.length > 0) {
|
||||
return `Microsoft-Login erfolgreich. Screenshots pruefen, um den besten Pfad zu waehlen.`;
|
||||
}
|
||||
|
||||
if (v2Variants.length > 0) {
|
||||
const names = v2Variants.map(v => `"${v.variantName}"`).join(', ');
|
||||
return `${v2Variants.length} Variante(n) haben /v2/ erhalten: ${names}. ` +
|
||||
`Auth ist prinzipiell moeglich — Auth-Flow kann re-aktiviert werden.`;
|
||||
if (failures.length === results.length) {
|
||||
return 'Beide Varianten fehlgeschlagen. Logs und Screenshots pruefen.';
|
||||
}
|
||||
|
||||
const successVariants = results.filter(r => r.success);
|
||||
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.';
|
||||
return 'Test abgeschlossen. Screenshots pruefen und naechste Schritte planen.';
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue