修复密码页收尾重复点击
This commit is contained in:
+46
-3
@@ -4714,6 +4714,49 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
let recoveryRound = 0;
|
||||
const maxRecoveryRounds = 3;
|
||||
let passwordPageDiagnosticsLogged = false;
|
||||
const isPasswordSubmitButtonReadyForRetry = (button) => {
|
||||
if (!button || !isActionEnabled(button)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ariaBusy = String(button.getAttribute?.('aria-busy') || '').trim().toLowerCase();
|
||||
if (ariaBusy === 'true') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pendingAttr = [
|
||||
button.getAttribute?.('data-loading'),
|
||||
button.getAttribute?.('data-pending'),
|
||||
button.getAttribute?.('data-submitting'),
|
||||
button.getAttribute?.('data-state'),
|
||||
]
|
||||
.map((value) => String(value || '').trim().toLowerCase())
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
if (/\b(?:true|loading|pending|submitting|busy)\b/.test(pendingAttr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let style = null;
|
||||
try {
|
||||
style = typeof window !== 'undefined' && window.getComputedStyle
|
||||
? window.getComputedStyle(button)
|
||||
: null;
|
||||
} catch {
|
||||
style = null;
|
||||
}
|
||||
|
||||
if (style?.pointerEvents === 'none') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const opacity = Number.parseFloat(style?.opacity || '');
|
||||
if (Number.isFinite(opacity) && opacity < 0.8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
while (Date.now() - start < timeout && recoveryRound < maxRecoveryRounds) {
|
||||
throwIfStopped();
|
||||
@@ -4749,12 +4792,11 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
throw new Error('当前邮箱已存在,需要重新开始新一轮。');
|
||||
}
|
||||
|
||||
recoveryRound += 1;
|
||||
|
||||
if (snapshot.state === 'error') {
|
||||
if (snapshot.userAlreadyExistsBlocked) {
|
||||
throw createSignupUserAlreadyExistsError();
|
||||
}
|
||||
recoveryRound += 1;
|
||||
await recoverCurrentAuthRetryPage({
|
||||
flow: 'signup',
|
||||
logLabel: `${prepareLogLabel}:检测到注册认证重试页,正在点击“重试”恢复(第 ${recoveryRound}/${maxRecoveryRounds} 次)`,
|
||||
@@ -4785,7 +4827,8 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
});
|
||||
}
|
||||
|
||||
if (snapshot.submitButton && isActionEnabled(snapshot.submitButton)) {
|
||||
if (snapshot.submitButton && isPasswordSubmitButtonReadyForRetry(snapshot.submitButton)) {
|
||||
recoveryRound += 1;
|
||||
log(`${prepareLogLabel}:页面仍停留在密码页,正在重新点击“继续”(第 ${recoveryRound}/${maxRecoveryRounds} 次)...`, 'warn');
|
||||
await humanPause(350, 900);
|
||||
await performOperationWithDelay({ stepKey: 'fill-password', kind: 'submit', label: 'retry-submit-signup-password' }, async () => {
|
||||
|
||||
@@ -1124,6 +1124,116 @@ return {
|
||||
assert.equal(result.logs.some(({ message }) => /检测到密码页报错/.test(message)), true);
|
||||
});
|
||||
|
||||
test('prepareSignupVerificationFlow waits instead of retrying while matched password submit is pending', async () => {
|
||||
const api = new Function(`
|
||||
const logs = [];
|
||||
const clicks = [];
|
||||
let now = 0;
|
||||
|
||||
Date.now = () => now;
|
||||
|
||||
const passwordInput = { value: 'Secret123!' };
|
||||
const submitButton = {
|
||||
textContent: 'Continue',
|
||||
disabled: false,
|
||||
getAttribute(name) {
|
||||
if (name === 'aria-disabled') return 'false';
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
const window = {
|
||||
getComputedStyle() {
|
||||
return {
|
||||
opacity: '0.5',
|
||||
pointerEvents: 'auto',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const location = {
|
||||
href: 'https://auth.openai.com/create-account/password',
|
||||
pathname: '/create-account/password',
|
||||
};
|
||||
const document = {
|
||||
readyState: 'interactive',
|
||||
title: '',
|
||||
body: {
|
||||
textContent: 'Create password Continue',
|
||||
innerText: 'Create password Continue',
|
||||
},
|
||||
querySelector() {
|
||||
return null;
|
||||
},
|
||||
querySelectorAll() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
||||
function throwIfStopped() {}
|
||||
function log(message, level = 'info') { logs.push({ message, level }); }
|
||||
async function sleep(ms = 0) { now += ms || 200; }
|
||||
function isVisibleElement() { return true; }
|
||||
function isActionEnabled(el) { return Boolean(el) && !el.disabled && el.getAttribute?.('aria-disabled') !== 'true'; }
|
||||
function getActionText(el) { return el?.textContent || ''; }
|
||||
function getCurrentAuthRetryPageState() { return null; }
|
||||
function isPhoneVerificationPageReady() { return false; }
|
||||
function findResendVerificationCodeTrigger() { return null; }
|
||||
function isEmailVerificationPage() { return false; }
|
||||
function getPageTextSnapshot() { return document.body.textContent; }
|
||||
function getVerificationCodeTarget() { return null; }
|
||||
function is405MethodNotAllowedPage() { return false; }
|
||||
async function recoverCurrentAuthRetryPage() {}
|
||||
function createSignupUserAlreadyExistsError() { return new Error('user already exists'); }
|
||||
function getSignupPasswordInput() { return passwordInput; }
|
||||
function getSignupPasswordSubmitButton() { return submitButton; }
|
||||
function isSignupEmailAlreadyExistsPage() { return false; }
|
||||
function isSignupPasswordErrorPage() { return false; }
|
||||
function getSignupPasswordTimeoutErrorPageState() { return null; }
|
||||
function isStep5Ready() { return false; }
|
||||
function getSignupPasswordFieldErrorText() { return ''; }
|
||||
function simulateClick(target) { clicks.push(target?.textContent || 'clicked'); }
|
||||
async function humanPause() {}
|
||||
function fillInput(input, value) { input.value = value; }
|
||||
function logSignupPasswordDiagnostics(message) { logs.push({ message, level: 'warn' }); }
|
||||
function createSignupPhonePasswordMismatchError(detailText = '') {
|
||||
return new Error('SIGNUP_PHONE_PASSWORD_MISMATCH::' + detailText);
|
||||
}
|
||||
|
||||
const VERIFICATION_PAGE_PATTERN = /verification code/i;
|
||||
|
||||
${extractFunction('isSignupVerificationPageInteractiveReady')}
|
||||
${extractFunction('isVerificationPageStillVisible')}
|
||||
${extractFunction('isSignupProfilePageUrl')}
|
||||
${extractFunction('isLikelyLoggedInChatgptHomeUrl')}
|
||||
${extractFunction('getStep4PostVerificationState')}
|
||||
${extractFunction('inspectSignupVerificationState')}
|
||||
${extractFunction('waitForSignupVerificationTransition')}
|
||||
${extractFunction('prepareSignupVerificationFlow')}
|
||||
|
||||
return {
|
||||
async run() {
|
||||
try {
|
||||
await prepareSignupVerificationFlow({
|
||||
password: 'Secret123!',
|
||||
prepareLogLabel: 'step 3 finalize',
|
||||
}, 11000);
|
||||
return { threw: false, logs, clicks, now };
|
||||
} catch (error) {
|
||||
return { threw: true, error: error.message, logs, clicks, now };
|
||||
}
|
||||
},
|
||||
};
|
||||
`)();
|
||||
|
||||
const result = await api.run();
|
||||
|
||||
assert.equal(result.threw, true);
|
||||
assert.equal(result.clicks.length, 0);
|
||||
assert.equal(result.error.includes('0/3'), true);
|
||||
assert.equal(result.now >= 11000, true);
|
||||
});
|
||||
|
||||
test('fillSignupEmailAndContinue reports before deferred submit while submit still waits for operation delay', async () => {
|
||||
const api = new Function(`
|
||||
const events = [];
|
||||
|
||||
Reference in New Issue
Block a user