feat: 添加登录超时恢复逻辑,优化登录流程中的错误处理与状态管理
This commit is contained in:
+207
-38
@@ -1621,8 +1621,13 @@ function createStep6RecoverableResult(reason, snapshot, options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
async function createStep6LoginTimeoutRecoverableResult(reason, snapshot, message) {
|
||||
const resolvedSnapshot = normalizeStep6Snapshot(snapshot || inspectLoginAuthState());
|
||||
async function createStep6LoginTimeoutRecoveryTransition(reason, snapshot, message, options = {}) {
|
||||
const {
|
||||
loginVerificationRequestedAt = null,
|
||||
via = 'login_timeout_recovered',
|
||||
} = options;
|
||||
let resolvedSnapshot = normalizeStep6Snapshot(snapshot || inspectLoginAuthState());
|
||||
let recovered = false;
|
||||
if (resolvedSnapshot?.state === 'login_timeout_error_page') {
|
||||
try {
|
||||
const recoveryResult = await recoverCurrentAuthRetryPage({
|
||||
@@ -1631,8 +1636,9 @@ async function createStep6LoginTimeoutRecoverableResult(reason, snapshot, messag
|
||||
step: 7,
|
||||
timeoutMs: 12000,
|
||||
});
|
||||
if (recoveryResult?.recovered) {
|
||||
log('步骤 7:登录超时报错页已点击“重试”,准备重新执行当前步骤。', 'warn');
|
||||
recovered = Boolean(recoveryResult?.recovered);
|
||||
if (recovered) {
|
||||
log('步骤 7:登录超时报错页已点击“重试”,正在按恢复后的页面状态继续当前流程。', 'warn');
|
||||
}
|
||||
} catch (error) {
|
||||
if (/CF_SECURITY_BLOCKED::/i.test(String(error?.message || error || ''))) {
|
||||
@@ -1642,7 +1648,46 @@ async function createStep6LoginTimeoutRecoverableResult(reason, snapshot, messag
|
||||
}
|
||||
}
|
||||
|
||||
return createStep6RecoverableResult(reason, resolvedSnapshot, {
|
||||
resolvedSnapshot = recovered
|
||||
? normalizeStep6Snapshot(await waitForKnownLoginAuthState(4000))
|
||||
: normalizeStep6Snapshot(inspectLoginAuthState());
|
||||
|
||||
if (resolvedSnapshot.state === 'verification_page') {
|
||||
return {
|
||||
action: 'done',
|
||||
result: createStep6SuccessResult(resolvedSnapshot, {
|
||||
via,
|
||||
loginVerificationRequestedAt,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (resolvedSnapshot.state === 'password_page') {
|
||||
log('步骤 7:登录超时报错页恢复后已进入密码页,继续当前登录流程。', 'warn');
|
||||
return { action: 'password', snapshot: resolvedSnapshot };
|
||||
}
|
||||
|
||||
if (resolvedSnapshot.state === 'email_page') {
|
||||
log('步骤 7:登录超时报错页恢复后已回到邮箱输入页,继续当前登录流程。', 'warn');
|
||||
return { action: 'email', snapshot: resolvedSnapshot };
|
||||
}
|
||||
|
||||
return {
|
||||
action: 'recoverable',
|
||||
result: createStep6RecoverableResult(reason, resolvedSnapshot, {
|
||||
message,
|
||||
loginVerificationRequestedAt,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async function createStep6LoginTimeoutRecoverableResult(reason, snapshot, message) {
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(reason, snapshot, message);
|
||||
if (transition?.action === 'done' || transition?.action === 'recoverable') {
|
||||
return transition.result;
|
||||
}
|
||||
|
||||
return createStep6RecoverableResult(reason, transition?.snapshot || normalizeStep6Snapshot(inspectLoginAuthState()), {
|
||||
message,
|
||||
});
|
||||
}
|
||||
@@ -2222,13 +2267,30 @@ async function waitForStep6EmailSubmitTransition(emailSubmittedAt, timeout = 120
|
||||
}
|
||||
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交邮箱后进入登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt: emailSubmittedAt,
|
||||
via: 'email_submit_timeout_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return {
|
||||
action: 'done',
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return { action: 'password', snapshot: transition.snapshot };
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return { action: 'email', snapshot: transition.snapshot };
|
||||
}
|
||||
return {
|
||||
action: 'recoverable',
|
||||
result: await createStep6LoginTimeoutRecoverableResult(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交邮箱后进入登录超时报错页。'
|
||||
),
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2257,13 +2319,30 @@ async function waitForStep6EmailSubmitTransition(emailSubmittedAt, timeout = 120
|
||||
return { action: 'password', snapshot };
|
||||
}
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交邮箱后进入登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt: emailSubmittedAt,
|
||||
via: 'email_submit_timeout_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return {
|
||||
action: 'done',
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return { action: 'password', snapshot: transition.snapshot };
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return { action: 'email', snapshot: transition.snapshot };
|
||||
}
|
||||
return {
|
||||
action: 'recoverable',
|
||||
result: await createStep6LoginTimeoutRecoverableResult(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交邮箱后进入登录超时报错页。'
|
||||
),
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
if (snapshot.state === 'oauth_consent_page') {
|
||||
@@ -2300,13 +2379,30 @@ async function waitForStep6PasswordSubmitTransition(passwordSubmittedAt, timeout
|
||||
}
|
||||
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交密码后进入登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt: passwordSubmittedAt,
|
||||
via: 'password_submit_timeout_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return {
|
||||
action: 'done',
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return { action: 'password', snapshot: transition.snapshot };
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return { action: 'email', snapshot: transition.snapshot };
|
||||
}
|
||||
return {
|
||||
action: 'recoverable',
|
||||
result: await createStep6LoginTimeoutRecoverableResult(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交密码后进入登录超时报错页。'
|
||||
),
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2332,13 +2428,30 @@ async function waitForStep6PasswordSubmitTransition(passwordSubmittedAt, timeout
|
||||
};
|
||||
}
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交密码后进入登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt: passwordSubmittedAt,
|
||||
via: 'password_submit_timeout_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return {
|
||||
action: 'done',
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return { action: 'password', snapshot: transition.snapshot };
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return { action: 'email', snapshot: transition.snapshot };
|
||||
}
|
||||
return {
|
||||
action: 'recoverable',
|
||||
result: await createStep6LoginTimeoutRecoverableResult(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'提交密码后进入登录超时报错页。'
|
||||
),
|
||||
result: transition.result,
|
||||
};
|
||||
}
|
||||
if (snapshot.state === 'oauth_consent_page') {
|
||||
@@ -2375,11 +2488,22 @@ async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeou
|
||||
}
|
||||
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
return await createStep6LoginTimeoutRecoverableResult(
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'切换到一次性验证码登录后进入登录超时报错页。'
|
||||
'切换到一次性验证码登录后进入登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt,
|
||||
via: 'switch_to_one_time_code_timeout_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return transition.result;
|
||||
}
|
||||
if (transition.action === 'password' || transition.action === 'email') {
|
||||
return transition;
|
||||
}
|
||||
return transition.result;
|
||||
}
|
||||
|
||||
if (snapshot.state === 'oauth_consent_page') {
|
||||
@@ -2401,11 +2525,22 @@ async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeou
|
||||
});
|
||||
}
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
return await createStep6LoginTimeoutRecoverableResult(
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'切换到一次性验证码登录后进入登录超时报错页。'
|
||||
'切换到一次性验证码登录后进入登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt,
|
||||
via: 'switch_to_one_time_code_timeout_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return transition.result;
|
||||
}
|
||||
if (transition.action === 'password' || transition.action === 'email') {
|
||||
return transition;
|
||||
}
|
||||
return transition.result;
|
||||
}
|
||||
if (snapshot.state === 'oauth_consent_page') {
|
||||
throw new Error(`切换到一次性验证码登录后页面直接进入 OAuth 授权页,未经过登录验证码页。URL: ${snapshot.url}`);
|
||||
@@ -2419,7 +2554,7 @@ async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeou
|
||||
});
|
||||
}
|
||||
|
||||
async function step6SwitchToOneTimeCodeLogin(snapshot) {
|
||||
async function step6SwitchToOneTimeCodeLogin(payload, snapshot) {
|
||||
const switchTrigger = snapshot?.switchTrigger || findOneTimeCodeLoginTrigger();
|
||||
if (!switchTrigger || !isActionEnabled(switchTrigger)) {
|
||||
return createStep6RecoverableResult('missing_one_time_code_trigger', normalizeStep6Snapshot(inspectLoginAuthState()), {
|
||||
@@ -2441,6 +2576,12 @@ async function step6SwitchToOneTimeCodeLogin(snapshot) {
|
||||
via: result.via || 'switch_to_one_time_code_login',
|
||||
});
|
||||
}
|
||||
if (result?.action === 'password') {
|
||||
return step6LoginFromPasswordPage(payload, result.snapshot);
|
||||
}
|
||||
if (result?.action === 'email') {
|
||||
return step6LoginFromEmailPage(payload, result.snapshot);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2452,7 +2593,7 @@ async function step6LoginFromPasswordPage(payload, snapshot) {
|
||||
if (!hasPassword) {
|
||||
if (currentSnapshot.switchTrigger) {
|
||||
log('步骤 7:当前未提供密码,改走一次性验证码登录。', 'warn');
|
||||
return step6SwitchToOneTimeCodeLogin(currentSnapshot);
|
||||
return step6SwitchToOneTimeCodeLogin(payload, currentSnapshot);
|
||||
}
|
||||
|
||||
return createStep6RecoverableResult('missing_password_and_one_time_code_trigger', currentSnapshot, {
|
||||
@@ -2482,8 +2623,14 @@ async function step6LoginFromPasswordPage(payload, snapshot) {
|
||||
log(`步骤 7:${transition.result.message || '提交密码后仍未进入登录验证码页面,准备重新执行步骤 7。'}`, 'warn');
|
||||
return transition.result;
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return step6LoginFromPasswordPage(payload, transition.snapshot);
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return step6LoginFromEmailPage(payload, transition.snapshot);
|
||||
}
|
||||
if (transition.action === 'switch') {
|
||||
return step6SwitchToOneTimeCodeLogin(transition.snapshot);
|
||||
return step6SwitchToOneTimeCodeLogin(payload, transition.snapshot);
|
||||
}
|
||||
|
||||
return createStep6RecoverableResult('password_submit_unknown', normalizeStep6Snapshot(inspectLoginAuthState()), {
|
||||
@@ -2492,7 +2639,7 @@ async function step6LoginFromPasswordPage(payload, snapshot) {
|
||||
}
|
||||
|
||||
if (currentSnapshot.switchTrigger) {
|
||||
return step6SwitchToOneTimeCodeLogin(currentSnapshot);
|
||||
return step6SwitchToOneTimeCodeLogin(payload, currentSnapshot);
|
||||
}
|
||||
|
||||
return createStep6RecoverableResult('password_page_unactionable', currentSnapshot, {
|
||||
@@ -2532,6 +2679,9 @@ async function step6LoginFromEmailPage(payload, snapshot) {
|
||||
log(`步骤 7:${transition.result.message || '提交邮箱后仍未进入目标页面,准备重新执行步骤 7。'}`, 'warn');
|
||||
return transition.result;
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return step6LoginFromEmailPage(payload, transition.snapshot);
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return step6LoginFromPasswordPage(payload, transition.snapshot);
|
||||
}
|
||||
@@ -2545,11 +2695,10 @@ async function step6_login(payload) {
|
||||
const { email } = payload;
|
||||
if (!email) throw new Error('登录时缺少邮箱地址。');
|
||||
|
||||
log(`步骤 7:正在使用 ${email} 登录...`);
|
||||
|
||||
const snapshot = normalizeStep6Snapshot(await waitForKnownLoginAuthState(15000));
|
||||
|
||||
if (snapshot.state === 'verification_page') {
|
||||
log('步骤 7:认证页已在登录验证码页,开始确认页面是否稳定。');
|
||||
return finalizeStep6VerificationReady({
|
||||
logLabel: '步骤 7 收尾',
|
||||
loginVerificationRequestedAt: null,
|
||||
@@ -2558,19 +2707,39 @@ async function step6_login(payload) {
|
||||
}
|
||||
|
||||
if (snapshot.state === 'login_timeout_error_page') {
|
||||
log('步骤 7:检测到登录超时报错,准备重新执行步骤 7。', 'warn');
|
||||
return await createStep6LoginTimeoutRecoverableResult(
|
||||
log('步骤 7:检测到登录超时报错页,先尝试恢复当前页面。', 'warn');
|
||||
const transition = await createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
snapshot,
|
||||
'当前页面处于登录超时报错页。'
|
||||
'当前页面处于登录超时报错页。',
|
||||
{
|
||||
loginVerificationRequestedAt: null,
|
||||
via: 'login_timeout_initial_recovered',
|
||||
}
|
||||
);
|
||||
if (transition.action === 'done') {
|
||||
return finalizeStep6VerificationReady({
|
||||
logLabel: '步骤 7 收尾',
|
||||
loginVerificationRequestedAt: transition.result.loginVerificationRequestedAt || null,
|
||||
via: transition.result.via || 'login_timeout_initial_recovered',
|
||||
});
|
||||
}
|
||||
if (transition.action === 'email') {
|
||||
return step6LoginFromEmailPage(payload, transition.snapshot);
|
||||
}
|
||||
if (transition.action === 'password') {
|
||||
return step6LoginFromPasswordPage(payload, transition.snapshot);
|
||||
}
|
||||
return transition.result;
|
||||
}
|
||||
|
||||
if (snapshot.state === 'email_page') {
|
||||
log(`步骤 7:正在使用 ${email} 登录...`);
|
||||
return step6LoginFromEmailPage(payload, snapshot);
|
||||
}
|
||||
|
||||
if (snapshot.state === 'password_page') {
|
||||
log('步骤 7:认证页已在密码页,继续当前登录流程。');
|
||||
return step6LoginFromPasswordPage(payload, snapshot);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,8 @@ test('step6LoginFromPasswordPage switches to one-time-code login when password i
|
||||
globalThis.log = (message, level = 'info') => {
|
||||
logs.push({ message, level });
|
||||
};
|
||||
globalThis.step6SwitchToOneTimeCodeLogin = async (value) => {
|
||||
globalThis.step6SwitchToOneTimeCodeLogin = async (payload, value) => {
|
||||
assert.deepStrictEqual(payload, { email: 'user@example.com', password: '' });
|
||||
assert.strictEqual(value, snapshot);
|
||||
return { step6Outcome: 'success', via: 'switch_to_one_time_code_login' };
|
||||
};
|
||||
|
||||
@@ -72,12 +72,18 @@ async function recoverCurrentAuthRetryPage() {
|
||||
return { recovered: true };
|
||||
}
|
||||
|
||||
function throwIfStopped() {}
|
||||
async function sleep() {}
|
||||
|
||||
function log(message, level = 'info') {
|
||||
logs.push({ message, level });
|
||||
}
|
||||
|
||||
${extractFunction('createStep6SuccessResult')}
|
||||
${extractFunction('createStep6RecoverableResult')}
|
||||
${extractFunction('normalizeStep6Snapshot')}
|
||||
${extractFunction('waitForKnownLoginAuthState')}
|
||||
${extractFunction('createStep6LoginTimeoutRecoveryTransition')}
|
||||
${extractFunction('createStep6LoginTimeoutRecoverableResult')}
|
||||
|
||||
return {
|
||||
@@ -104,6 +110,141 @@ return {
|
||||
assert.equal(result.message, '当前页面处于登录超时报错页。');
|
||||
});
|
||||
|
||||
test('step 7 timeout recovery transition continues from password page after retry succeeds', async () => {
|
||||
const api = new Function(`
|
||||
const logs = [];
|
||||
let recoverCalls = 0;
|
||||
let currentState = 'login_timeout_error_page';
|
||||
|
||||
const location = {
|
||||
href: 'https://auth.openai.com/log-in',
|
||||
};
|
||||
|
||||
function inspectLoginAuthState() {
|
||||
return {
|
||||
state: currentState,
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
async function recoverCurrentAuthRetryPage() {
|
||||
recoverCalls += 1;
|
||||
currentState = 'password_page';
|
||||
return { recovered: true };
|
||||
}
|
||||
|
||||
function throwIfStopped() {}
|
||||
async function sleep() {}
|
||||
|
||||
function log(message, level = 'info') {
|
||||
logs.push({ message, level });
|
||||
}
|
||||
|
||||
${extractFunction('createStep6SuccessResult')}
|
||||
${extractFunction('createStep6RecoverableResult')}
|
||||
${extractFunction('normalizeStep6Snapshot')}
|
||||
${extractFunction('waitForKnownLoginAuthState')}
|
||||
${extractFunction('createStep6LoginTimeoutRecoveryTransition')}
|
||||
|
||||
return {
|
||||
async run() {
|
||||
return createStep6LoginTimeoutRecoveryTransition(
|
||||
'login_timeout_error_page',
|
||||
{ state: 'login_timeout_error_page', url: location.href },
|
||||
'当前页面处于登录超时报错页。',
|
||||
{
|
||||
via: 'login_timeout_initial_recovered',
|
||||
}
|
||||
);
|
||||
},
|
||||
snapshot() {
|
||||
return { logs, recoverCalls };
|
||||
},
|
||||
};
|
||||
`)();
|
||||
|
||||
const result = await api.run();
|
||||
const snapshot = api.snapshot();
|
||||
|
||||
assert.equal(snapshot.recoverCalls, 1);
|
||||
assert.equal(result.action, 'password');
|
||||
assert.equal(result.snapshot.state, 'password_page');
|
||||
assert.equal(snapshot.logs.some(({ message }) => /密码页/.test(message)), true);
|
||||
});
|
||||
|
||||
test('step 7 entry resumes password flow after retry page recovery reaches password page', async () => {
|
||||
const api = new Function(`
|
||||
const logs = [];
|
||||
let recoverCalls = 0;
|
||||
let currentState = 'login_timeout_error_page';
|
||||
|
||||
const location = {
|
||||
href: 'https://auth.openai.com/log-in',
|
||||
};
|
||||
|
||||
function inspectLoginAuthState() {
|
||||
return {
|
||||
state: currentState,
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
async function recoverCurrentAuthRetryPage() {
|
||||
recoverCalls += 1;
|
||||
currentState = 'password_page';
|
||||
return { recovered: true };
|
||||
}
|
||||
|
||||
function throwIfStopped() {}
|
||||
async function sleep() {}
|
||||
|
||||
function log(message, level = 'info') {
|
||||
logs.push({ message, level });
|
||||
}
|
||||
|
||||
async function step6LoginFromPasswordPage(payload, snapshot) {
|
||||
return { branch: 'password', payload, snapshot };
|
||||
}
|
||||
|
||||
async function step6LoginFromEmailPage(payload, snapshot) {
|
||||
return { branch: 'email', payload, snapshot };
|
||||
}
|
||||
|
||||
async function finalizeStep6VerificationReady(options) {
|
||||
return { branch: 'verification', options };
|
||||
}
|
||||
|
||||
function throwForStep6FatalState() {}
|
||||
|
||||
${extractFunction('createStep6SuccessResult')}
|
||||
${extractFunction('createStep6RecoverableResult')}
|
||||
${extractFunction('normalizeStep6Snapshot')}
|
||||
${extractFunction('waitForKnownLoginAuthState')}
|
||||
${extractFunction('createStep6LoginTimeoutRecoveryTransition')}
|
||||
${extractFunction('step6_login')}
|
||||
|
||||
return {
|
||||
async run() {
|
||||
return step6_login({
|
||||
email: 'user@example.com',
|
||||
password: 'secret',
|
||||
});
|
||||
},
|
||||
snapshot() {
|
||||
return { logs, recoverCalls };
|
||||
},
|
||||
};
|
||||
`)();
|
||||
|
||||
const result = await api.run();
|
||||
const snapshot = api.snapshot();
|
||||
|
||||
assert.equal(snapshot.recoverCalls, 1);
|
||||
assert.equal(result.branch, 'password');
|
||||
assert.equal(result.snapshot.state, 'password_page');
|
||||
assert.equal(snapshot.logs.some(({ message }) => /密码页/.test(message)), true);
|
||||
});
|
||||
|
||||
test('step 7 finalize converts verification page that falls into retry page into recoverable result', async () => {
|
||||
const api = new Function(`
|
||||
const logs = [];
|
||||
@@ -150,6 +291,8 @@ function getLoginAuthStateLabel(snapshot) {
|
||||
${extractFunction('createStep6SuccessResult')}
|
||||
${extractFunction('createStep6RecoverableResult')}
|
||||
${extractFunction('normalizeStep6Snapshot')}
|
||||
${extractFunction('waitForKnownLoginAuthState')}
|
||||
${extractFunction('createStep6LoginTimeoutRecoveryTransition')}
|
||||
${extractFunction('createStep6LoginTimeoutRecoverableResult')}
|
||||
${extractFunction('finalizeStep6VerificationReady')}
|
||||
|
||||
|
||||
+1
-1
@@ -704,7 +704,7 @@
|
||||
- 新增共享恢复层:`content/auth-page-recovery.js`。
|
||||
- Step 4 在等待注册验证码页时,如果命中认证页 `Try again / 重试` 页,或 `/email-verification` 上的 `405 / Route Error` 重试页,会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复,再继续回到密码页重提和验证码页确认流程。
|
||||
- 但如果 Step 4 的认证重试页正文中出现 `user_already_exists`,则会直接视为“当前用户已存在”:不点击 `重试`,不再回到步骤 1 重开当前轮,而是立即结束当前轮;开启自动重试时直接进入下一轮。
|
||||
- Step 7 在识别到登录超时报错页时,会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复当前页面;若仍未恢复,则按原有可恢复失败逻辑重跑 Step 7。
|
||||
- Step 7 在识别到登录超时报错页时,会先通过共享恢复逻辑最多自动点击 5 次 `重试` 尝试恢复当前页面;恢复成功后会优先按当前页面状态继续当前登录流程,例如直接续跑邮箱页或密码页;只有仍未恢复到可继续状态时,才按原有可恢复失败逻辑重跑 Step 7。
|
||||
- Step 7 在首次识别到登录验证码页后,不会立刻把步骤判定为成功;还会额外做一轮“收尾确认”,确保页面稳定停留在登录验证码页。如果只是短暂进入验证码页、随后又掉进登录重试页,则会先走共享恢复逻辑,再按既有可恢复失败逻辑重跑 Step 7。
|
||||
- Step 7 的这轮收尾确认是主要责任边界;Step 8 默认建立在“登录验证码页已经由 Step 7 稳定确认”的前提上,只在后台入口保留防御性回退判断,不替代 Step 7 收尾。
|
||||
- Step 8 如果发现认证页已经进入登录超时报错/重试页,会直接报错并回到 Step 7 重新开始,而不是在 Step 8 内部点击 `重试`。
|
||||
|
||||
Reference in New Issue
Block a user