feat: 增强 PayPal 登录流程,支持自动检测邮箱和密码输入,增加超时处理和相应测试用例

This commit is contained in:
QLHazyCoder
2026-04-26 05:47:44 +08:00
parent b86114fa3a
commit 3271dc2f83
3 changed files with 295 additions and 14 deletions
+47 -10
View File
@@ -48,12 +48,17 @@ async function handlePayPalCommand(message) {
async function waitUntil(predicate, options = {}) {
const intervalMs = Math.max(50, Math.floor(Number(options.intervalMs) || 250));
const timeoutMs = Math.max(0, Math.floor(Number(options.timeoutMs) || 0));
const startedAt = Date.now();
while (true) {
throwIfStopped();
const value = await predicate();
if (value) {
return value;
}
if (timeoutMs > 0 && Date.now() - startedAt >= timeoutMs) {
throw new Error(options.timeoutMessage || 'PayPal page timed out waiting for target state.');
}
await sleep(intervalMs);
}
}
@@ -179,6 +184,13 @@ function hasPasskeyPrompt() {
return findPasskeyPromptButtons().length > 0;
}
function getPayPalLoginPhase(emailInput, passwordInput) {
if (emailInput && passwordInput) return 'login_combined';
if (passwordInput) return 'password';
if (emailInput) return 'email';
return '';
}
async function submitPayPalLogin(payload = {}) {
await waitForDocumentComplete();
@@ -192,19 +204,34 @@ async function submitPayPalLogin(payload = {}) {
const emailInput = findEmailInput();
if (!passwordInput && emailInput && email) {
fillInput(emailInput, email);
const nextButton = findLoginNextButton();
if (nextButton && isEnabledControl(nextButton)) {
simulateClick(nextButton);
if (normalizeText(emailInput.value || '') !== email) {
fillInput(emailInput, email);
}
passwordInput = await waitUntil(() => findPasswordInput(), { intervalMs: 250 });
const nextButton = await waitUntil(() => {
const button = findLoginNextButton();
return button && isEnabledControl(button) ? button : null;
}, {
intervalMs: 250,
timeoutMs: 8000,
timeoutMessage: 'PayPal email page did not expose a clickable next/continue button.',
});
simulateClick(nextButton);
return {
submitted: false,
phase: 'email_submitted',
awaiting: 'password_page',
};
} else if (!passwordInput && emailInput && !email) {
throw new Error('PayPal 账号为空,请先在侧边栏配置。');
} else if (emailInput && email && !String(emailInput.value || '').trim()) {
} else if (emailInput && email && normalizeText(emailInput.value || '') !== email) {
fillInput(emailInput, email);
}
passwordInput = passwordInput || await waitUntil(() => findPasswordInput(), { intervalMs: 250 });
passwordInput = passwordInput || await waitUntil(() => findPasswordInput(), {
intervalMs: 250,
timeoutMs: 8000,
timeoutMessage: 'PayPal password page did not expose a password input.',
});
fillInput(passwordInput, password);
await sleep(1000);
@@ -214,10 +241,18 @@ async function submitPayPalLogin(payload = {}) {
/登录|登入|继续/i,
]);
return button && isEnabledControl(button) ? button : null;
}, { intervalMs: 250 });
}, {
intervalMs: 250,
timeoutMs: 8000,
timeoutMessage: 'PayPal password page did not expose a clickable login/continue button.',
});
simulateClick(loginButton);
return { submitted: true };
return {
submitted: true,
phase: 'password_submitted',
awaiting: 'redirect_or_approval',
};
}
async function dismissPayPalPrompts() {
@@ -261,10 +296,12 @@ function inspectPayPalState() {
const emailInput = findEmailInput();
const passwordInput = findPasswordInput();
const approveButton = findApproveButton();
const loginPhase = getPayPalLoginPhase(emailInput, passwordInput);
return {
url: location.href,
readyState: document.readyState,
needsLogin: Boolean(emailInput || passwordInput),
needsLogin: Boolean(loginPhase),
loginPhase,
hasEmailInput: Boolean(emailInput),
hasPasswordInput: Boolean(passwordInput),
approveReady: Boolean(approveButton && isEnabledControl(approveButton)),