fix: wait for Teams login redirect + universal auth selectors

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-02-17 10:50:01 +01:00
parent 3724e31a30
commit 08194fe241
2 changed files with 50 additions and 60 deletions

View file

@ -121,22 +121,20 @@ export class AuthProcedure {
/**
* Wait for the email input field.
*
* Hybrid flow: Teams inline modal has input#emailOrPhonerm [data-testid="emailInput"]
* Direct flow: MS login portal has input[name="loginfmt"]
* Uses universal selectors that work on both:
* - MS login portal (login.microsoftonline.com): input[name="loginfmt"]
* - Teams inline modal (light-meetings): input#emailOrPhonerm
*/
private async _waitForEmailInput(isInlineFlow: boolean) {
private async _waitForEmailInput(_isInlineFlow: boolean) {
this._logger.info('Waiting for email input field...');
// Primary selectors — exact IDs/attributes from the real HTML
const selectors = isInlineFlow
? [
// Universal selectors — match both MS login portal and Teams inline modal
const selectors = [
'input[name="loginfmt"]', // MS login portal (primary)
'input[type="email"]', // MS login portal fallback
'input#emailOrPhonerm', // Teams inline modal (exact ID)
'input[data-testid="emailInput"]', // Teams inline modal (data-testid)
'input[placeholder="Enter your email"]', // Teams inline modal (placeholder)
]
: [
'input[name="loginfmt"]', // MS login portal
'input[type="email"]', // MS login portal fallback
];
const combinedSelector = selectors.join(', ');
@ -161,53 +159,37 @@ export class AuthProcedure {
/**
* Click the "Next" button after entering email.
*
* Hybrid flow: button[data-testid="authLoginDialogNextButton"] initially disabled,
* becomes enabled after email is typed. We wait for it to be enabled.
* Direct flow: input[type="submit"] on the MS login portal.
* Tries all known button selectors from both MS login portal and Teams inline modal.
* Works regardless of which page we're on.
*/
private async _clickNextButton(isInlineFlow: boolean): Promise<void> {
if (isInlineFlow) {
// Teams inline modal: wait for the Next button to become enabled
const nextSelector = 'button[data-testid="authLoginDialogNextButton"]';
this._logger.info('Waiting for Teams Next button to become enabled...');
try {
await this._page.waitForSelector(`${nextSelector}:not([disabled])`, {
timeout: 10000,
state: 'visible',
});
await this._page.click(nextSelector);
this._logger.info('Clicked Teams Next button');
// After clicking Next, Teams redirects to login.microsoftonline.com
// Wait for the redirect to begin
await this._page.waitForTimeout(3000);
} catch {
this._logger.warn('Teams Next button not found or not enabled, pressing Enter');
await this._page.keyboard.press('Enter');
await this._page.waitForTimeout(3000);
}
} else {
// MS login portal: click submit button
private async _clickNextButton(_isInlineFlow: boolean): Promise<void> {
// Try all known Next/Submit button selectors (MS login portal + Teams inline modal)
const selectors = [
'input[type="submit"]',
'button[type="submit"]',
'input[type="submit"]', // MS login portal
'button[type="submit"]', // MS login portal alt
'button[data-testid="authLoginDialogNextButton"]', // Teams inline modal
];
for (const selector of selectors) {
try {
const button = await this._page.$(selector);
if (button && await button.isVisible()) {
const button = await this._page.waitForSelector(selector, {
timeout: 5000,
state: 'visible',
});
if (button) {
await button.click();
this._logger.info(`Clicked Next: ${selector}`);
await this._page.waitForTimeout(2000);
await this._page.waitForTimeout(3000);
return;
}
} catch {
// Continue
// Selector not found, try next
}
}
this._logger.warn('No Next button found, pressing Enter');
this._logger.warn('No Next button found via selectors, pressing Enter');
await this._page.keyboard.press('Enter');
await this._page.waitForTimeout(2000);
}
await this._page.waitForTimeout(3000);
}
/**

View file

@ -195,18 +195,26 @@ async function _runVariant(
waitUntil: 'domcontentloaded',
timeout: 30000,
});
await page.waitForTimeout(3000);
_log('info', `After Teams redirect: ${page.url().substring(0, 120)}`);
// Wait for Teams to redirect to login.microsoftonline.com (up to 30s)
_log('info', 'Waiting for login redirect to login.microsoftonline.com...');
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)}`);
}
// Step 2: Login
if (botAccountEmail && botAccountPassword) {
authAttempted = true;
const onLoginPage = page.url().includes('login.microsoftonline.com') || page.url().includes('login.live.com');
_log('info', `On login page: ${onLoginPage}, authenticating as ${botAccountEmail}`);
_log('info', `Authenticating as ${botAccountEmail} (skipNavigation=true to preserve OAuth context)`);
const authProcedure = new AuthProcedure(page, logger);
// skipNavigation=true because we're already on the login page after teams.microsoft.com redirect
authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, !onLoginPage);
// 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'}`);
}