fix: wait for Teams login redirect + universal auth selectors
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
3724e31a30
commit
08194fe241
2 changed files with 50 additions and 60 deletions
|
|
@ -121,23 +121,21 @@ export class AuthProcedure {
|
||||||
/**
|
/**
|
||||||
* Wait for the email input field.
|
* Wait for the email input field.
|
||||||
*
|
*
|
||||||
* Hybrid flow: Teams inline modal has input#emailOrPhonerm [data-testid="emailInput"]
|
* Uses universal selectors that work on both:
|
||||||
* Direct flow: MS login portal has input[name="loginfmt"]
|
* - 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...');
|
this._logger.info('Waiting for email input field...');
|
||||||
|
|
||||||
// Primary selectors — exact IDs/attributes from the real HTML
|
// Universal selectors — match both MS login portal and Teams inline modal
|
||||||
const selectors = isInlineFlow
|
const selectors = [
|
||||||
? [
|
'input[name="loginfmt"]', // MS login portal (primary)
|
||||||
'input#emailOrPhonerm', // Teams inline modal (exact ID)
|
'input[type="email"]', // MS login portal fallback
|
||||||
'input[data-testid="emailInput"]', // Teams inline modal (data-testid)
|
'input#emailOrPhonerm', // Teams inline modal (exact ID)
|
||||||
'input[placeholder="Enter your email"]', // Teams inline modal (placeholder)
|
'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(', ');
|
const combinedSelector = selectors.join(', ');
|
||||||
|
|
||||||
|
|
@ -161,53 +159,37 @@ export class AuthProcedure {
|
||||||
/**
|
/**
|
||||||
* Click the "Next" button after entering email.
|
* Click the "Next" button after entering email.
|
||||||
*
|
*
|
||||||
* Hybrid flow: button[data-testid="authLoginDialogNextButton"] — initially disabled,
|
* Tries all known button selectors from both MS login portal and Teams inline modal.
|
||||||
* becomes enabled after email is typed. We wait for it to be enabled.
|
* Works regardless of which page we're on.
|
||||||
* Direct flow: input[type="submit"] on the MS login portal.
|
|
||||||
*/
|
*/
|
||||||
private async _clickNextButton(isInlineFlow: boolean): Promise<void> {
|
private async _clickNextButton(_isInlineFlow: boolean): Promise<void> {
|
||||||
if (isInlineFlow) {
|
// Try all known Next/Submit button selectors (MS login portal + Teams inline modal)
|
||||||
// Teams inline modal: wait for the Next button to become enabled
|
const selectors = [
|
||||||
const nextSelector = 'button[data-testid="authLoginDialogNextButton"]';
|
'input[type="submit"]', // MS login portal
|
||||||
this._logger.info('Waiting for Teams Next button to become enabled...');
|
'button[type="submit"]', // MS login portal alt
|
||||||
|
'button[data-testid="authLoginDialogNextButton"]', // Teams inline modal
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const selector of selectors) {
|
||||||
try {
|
try {
|
||||||
await this._page.waitForSelector(`${nextSelector}:not([disabled])`, {
|
const button = await this._page.waitForSelector(selector, {
|
||||||
timeout: 10000,
|
timeout: 5000,
|
||||||
state: 'visible',
|
state: 'visible',
|
||||||
});
|
});
|
||||||
await this._page.click(nextSelector);
|
if (button) {
|
||||||
this._logger.info('Clicked Teams Next button');
|
await button.click();
|
||||||
// After clicking Next, Teams redirects to login.microsoftonline.com
|
this._logger.info(`Clicked Next: ${selector}`);
|
||||||
// Wait for the redirect to begin
|
await this._page.waitForTimeout(3000);
|
||||||
await this._page.waitForTimeout(3000);
|
return;
|
||||||
} 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
|
|
||||||
const selectors = [
|
|
||||||
'input[type="submit"]',
|
|
||||||
'button[type="submit"]',
|
|
||||||
];
|
|
||||||
for (const selector of selectors) {
|
|
||||||
try {
|
|
||||||
const button = await this._page.$(selector);
|
|
||||||
if (button && await button.isVisible()) {
|
|
||||||
await button.click();
|
|
||||||
this._logger.info(`Clicked Next: ${selector}`);
|
|
||||||
await this._page.waitForTimeout(2000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Continue
|
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
// Selector not found, try next
|
||||||
}
|
}
|
||||||
this._logger.warn('No Next button found, pressing Enter');
|
|
||||||
await this._page.keyboard.press('Enter');
|
|
||||||
await this._page.waitForTimeout(2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._logger.warn('No Next button found via selectors, pressing Enter');
|
||||||
|
await this._page.keyboard.press('Enter');
|
||||||
|
await this._page.waitForTimeout(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -195,18 +195,26 @@ async function _runVariant(
|
||||||
waitUntil: 'domcontentloaded',
|
waitUntil: 'domcontentloaded',
|
||||||
timeout: 30000,
|
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
|
// Step 2: Login
|
||||||
if (botAccountEmail && botAccountPassword) {
|
if (botAccountEmail && botAccountPassword) {
|
||||||
authAttempted = true;
|
authAttempted = true;
|
||||||
const onLoginPage = page.url().includes('login.microsoftonline.com') || page.url().includes('login.live.com');
|
_log('info', `Authenticating as ${botAccountEmail} (skipNavigation=true to preserve OAuth context)`);
|
||||||
_log('info', `On login page: ${onLoginPage}, authenticating as ${botAccountEmail}`);
|
|
||||||
|
|
||||||
const authProcedure = new AuthProcedure(page, logger);
|
const authProcedure = new AuthProcedure(page, logger);
|
||||||
// skipNavigation=true because we're already on the login page after teams.microsoft.com redirect
|
// Always skipNavigation=true: we're already on login.microsoftonline.com with the correct
|
||||||
authSuccess = await authProcedure.authenticateWithMicrosoft(botAccountEmail, botAccountPassword, !onLoginPage);
|
// 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'}`);
|
_log(authSuccess ? 'info' : 'warn', `Auth result: ${authSuccess ? 'success' : 'failed'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue