fix: restart signup phone mismatch without duplicate logs

This commit is contained in:
yigedludan8
2026-05-06 21:44:17 +08:00
parent 9964b5a317
commit 12f35f1d3b
10 changed files with 642 additions and 11 deletions
+326
View File
@@ -56,6 +56,7 @@ const bundle = [
extractFunction('isAddPhoneAuthUrl'),
extractFunction('isAddPhoneAuthState'),
extractFunction('isMail2925ThreadTerminatedError'),
extractFunction('isSignupPhonePasswordMismatchFailure'),
extractFunction('isSignupUserAlreadyExistsFailure'),
extractFunction('getPostStep6AutoRestartDecision'),
extractFunction('runAutoSequenceFromStep'),
@@ -457,3 +458,328 @@ return {
assert.equal(events.logs.some(({ message }) => /步骤 4 当前状态为 skipped/.test(message)), true);
assert.equal(events.logs.some(({ message }) => /步骤 5 当前状态为 skipped/.test(message)), true);
});
test('auto-run clears fetched signup phone state before restarting when step 4 detects phone/password mismatch', async () => {
const api = new Function(`
const AUTO_STEP_DELAYS = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 };
const LAST_STEP_ID = 10;
const FINAL_OAUTH_CHAIN_START_STEP = 7;
const SIGNUP_METHOD_PHONE = 'phone';
const chrome = {
tabs: {
update: async () => {},
},
runtime: {
sendMessage: async () => {},
},
};
let remainingFailures = 1;
let currentState = {
email: '',
password: 'Secret123!',
signupMethod: 'phone',
accountIdentifierType: 'phone',
accountIdentifier: '+56988841722',
signupPhoneNumber: '+56988841722',
signupPhoneActivation: {
activationId: 'act-1',
phoneNumber: '+56988841722',
},
signupPhoneCompletedActivation: {
activationId: 'act-1',
phoneNumber: '+56988841722',
},
stepStatuses: {
1: 'pending',
2: 'pending',
3: 'pending',
4: 'pending',
5: 'pending',
6: 'pending',
7: 'pending',
8: 'pending',
9: 'pending',
10: 'pending',
},
};
const events = {
steps: [],
invalidations: [],
logs: [],
setStateCalls: [],
};
async function addLog(message, level = 'info') {
events.logs.push({ message, level });
}
async function ensureAutoEmailReady() {
return '';
}
async function broadcastAutoRunStatus() {}
async function ensureResolvedSignupMethodForRun() { return 'phone'; }
async function getState() {
return currentState;
}
async function setState(updates) {
currentState = {
...currentState,
...updates,
stepStatuses: updates.stepStatuses ? { ...updates.stepStatuses } : currentState.stepStatuses,
};
events.setStateCalls.push(updates);
}
function isStopError(error) {
return (error?.message || String(error || '')) === '流程已被用户停止。';
}
function isStepDoneStatus(status) {
return status === 'completed' || status === 'manual_completed' || status === 'skipped';
}
async function executeStepAndWait(step) {
events.steps.push(step);
if (step === 4 && remainingFailures > 0) {
remainingFailures -= 1;
throw new Error('SIGNUP_PHONE_PASSWORD_MISMATCH::步骤 3:检测到注册手机号或密码不正确,需要重新开始当前轮。页面提示:Incorrect phone number or password');
}
}
async function getTabId() {
return 1;
}
async function invalidateDownstreamAfterStepRestart(step, options = {}) {
events.invalidations.push({ step, options });
currentState = {
...currentState,
password: null,
stepStatuses: {
1: currentState.stepStatuses[1] || 'completed',
2: 'pending',
3: 'pending',
4: 'pending',
5: 'pending',
6: 'pending',
7: 'pending',
8: 'pending',
9: 'pending',
10: 'pending',
},
};
}
function getLoginAuthStateLabel(state) {
return state || 'unknown';
}
function getErrorMessage(error) {
return error?.message || String(error || '');
}
async function getLoginAuthStateFromContent() {
return { state: 'password_page', url: 'https://auth.openai.com/log-in' };
}
${bundle}
return {
async run() {
await runAutoSequenceFromStep(1, {
targetRun: 1,
totalRuns: 1,
attemptRuns: 1,
continued: false,
});
return { events, currentState };
},
};
`)();
const { events, currentState } = await api.run();
assert.deepStrictEqual(events.invalidations, [
{
step: 1,
options: {
logLabel: '步骤 4 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 1 次重开)',
},
},
]);
assert.equal(currentState.signupPhoneNumber, '');
assert.equal(currentState.signupPhoneActivation, null);
assert.equal(currentState.signupPhoneCompletedActivation, null);
assert.equal(currentState.accountIdentifierType, null);
assert.equal(currentState.accountIdentifier, '');
assert.equal(currentState.password, 'Secret123!');
assert.equal(events.logs.some(({ message }) => /丢弃当前注册手机号并回到步骤 1 重新开始/.test(message)), true);
assert.equal(events.logs.some(({ message }) => /已清空本轮注册手机号与接码订单/.test(message)), true);
});
test('auto-run restarts from step 1 immediately when step 3 detects phone/password mismatch', async () => {
const api = new Function(`
const AUTO_STEP_DELAYS = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 };
const LAST_STEP_ID = 10;
const FINAL_OAUTH_CHAIN_START_STEP = 7;
const SIGNUP_METHOD_PHONE = 'phone';
const chrome = {
tabs: {
update: async () => {},
},
runtime: {
sendMessage: async () => {},
},
};
let remainingFailures = 1;
let currentState = {
email: '',
password: 'Secret123!',
signupMethod: 'phone',
accountIdentifierType: 'phone',
accountIdentifier: '+56988841722',
signupPhoneNumber: '+56988841722',
signupPhoneActivation: {
activationId: 'act-1',
phoneNumber: '+56988841722',
},
signupPhoneCompletedActivation: {
activationId: 'act-1',
phoneNumber: '+56988841722',
},
stepStatuses: {
1: 'pending',
2: 'pending',
3: 'pending',
4: 'pending',
5: 'pending',
6: 'pending',
7: 'pending',
8: 'pending',
9: 'pending',
10: 'pending',
},
};
const events = {
steps: [],
invalidations: [],
logs: [],
setStateCalls: [],
};
async function addLog(message, level = 'info') {
events.logs.push({ message, level });
}
async function ensureAutoEmailReady() {
return '';
}
async function broadcastAutoRunStatus() {}
async function ensureResolvedSignupMethodForRun() { return 'phone'; }
async function getState() {
return currentState;
}
async function setState(updates) {
currentState = {
...currentState,
...updates,
stepStatuses: updates.stepStatuses ? { ...updates.stepStatuses } : currentState.stepStatuses,
};
events.setStateCalls.push(updates);
}
function isStopError(error) {
return (error?.message || String(error || '')) === '流程已被用户停止。';
}
function isStepDoneStatus(status) {
return status === 'completed' || status === 'manual_completed' || status === 'skipped';
}
async function executeStepAndWait(step) {
events.steps.push(step);
if (step === 3 && remainingFailures > 0) {
remainingFailures -= 1;
throw new Error('SIGNUP_PHONE_PASSWORD_MISMATCH::步骤 3:检测到注册手机号或密码不正确,需要重新开始当前轮。页面提示:Incorrect phone number or password');
}
}
async function getTabId() {
return 1;
}
async function invalidateDownstreamAfterStepRestart(step, options = {}) {
events.invalidations.push({ step, options });
currentState = {
...currentState,
password: null,
stepStatuses: {
1: currentState.stepStatuses[1] || 'completed',
2: 'pending',
3: 'pending',
4: 'pending',
5: 'pending',
6: 'pending',
7: 'pending',
8: 'pending',
9: 'pending',
10: 'pending',
},
};
}
function getLoginAuthStateLabel(state) {
return state || 'unknown';
}
function getErrorMessage(error) {
return error?.message || String(error || '');
}
async function getLoginAuthStateFromContent() {
return { state: 'password_page', url: 'https://auth.openai.com/log-in' };
}
${bundle}
return {
async run() {
await runAutoSequenceFromStep(1, {
targetRun: 1,
totalRuns: 1,
attemptRuns: 1,
continued: false,
});
return { events, currentState };
},
};
`)();
const { events, currentState } = await api.run();
assert.deepStrictEqual(events.steps, [1, 2, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
assert.deepStrictEqual(events.invalidations, [
{
step: 1,
options: {
logLabel: '步骤 3 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 1 次重开)',
},
},
]);
assert.equal(currentState.signupPhoneNumber, '');
assert.equal(currentState.signupPhoneActivation, null);
assert.equal(currentState.signupPhoneCompletedActivation, null);
assert.equal(currentState.accountIdentifierType, null);
assert.equal(currentState.accountIdentifier, '');
assert.equal(currentState.password, 'Secret123!');
assert.equal(events.logs.some(({ message }) => /步骤 3检测到手机号\/密码不匹配/.test(message)), true);
assert.equal(events.logs.some(({ message }) => /步骤 3已清空本轮注册手机号与接码订单/.test(message)), true);
});
@@ -435,6 +435,36 @@ test('message router marks step 3 failed when post-submit finalize fails', async
assert.deepStrictEqual(response, { ok: true, error: '步骤 3 提交后仍停留在密码页。' });
});
test('message router does not duplicate step 3 mismatch failure log after finalize already failed', async () => {
const mismatchError = 'SIGNUP_PHONE_PASSWORD_MISMATCH::步骤 3:检测到注册手机号或密码不正确,需要重新开始当前轮。页面提示:Incorrect phone number or password';
const state = {
stepStatuses: {
3: 'failed',
},
};
const { router, events } = createRouter({
state,
});
const response = await router.handleMessage({
type: 'STEP_ERROR',
step: 3,
source: 'signup-page',
payload: {},
error: mismatchError,
}, {});
assert.deepStrictEqual(events.stepStatuses, []);
assert.equal(events.logs.some(({ message, step }) => /失败SIGNUP_PHONE_PASSWORD_MISMATCH::/.test(message) && step === 3), false);
assert.deepStrictEqual(events.notifyErrors, [
{
step: 3,
error: mismatchError,
},
]);
assert.deepStrictEqual(response, { ok: true });
});
test('message router stops the flow and surfaces cloudflare security block errors', async () => {
const { router, events } = createRouter();
@@ -254,6 +254,64 @@ return {
});
});
test('signup verification state exposes password error text on password page', () => {
const api = new Function(`
const location = {
href: 'https://auth.openai.com/log-in/password',
};
function isStep5Ready() {
return false;
}
function isVerificationPageStillVisible() {
return false;
}
function isSignupPasswordErrorPage() {
return false;
}
function getSignupPasswordTimeoutErrorPageState() {
return null;
}
function isSignupEmailAlreadyExistsPage() {
return false;
}
function getSignupPasswordInput() {
return { value: 'Secret123!' };
}
function getSignupPasswordSubmitButton() {
return { textContent: 'Continue' };
}
function getSignupPasswordFieldErrorText() {
return 'Incorrect phone number or password';
}
${extractFunction('isSignupProfilePageUrl')}
${extractFunction('isLikelyLoggedInChatgptHomeUrl')}
${extractFunction('getStep4PostVerificationState')}
${extractFunction('inspectSignupVerificationState')}
return {
run() {
return inspectSignupVerificationState();
},
};
`)();
assert.deepStrictEqual(api.run(), {
state: 'password',
passwordInput: { value: 'Secret123!' },
submitButton: { textContent: 'Continue' },
passwordErrorText: 'Incorrect phone number or password',
});
});
test('signup verification state keeps verification priority when email-verification page also shows profile fields', () => {
const api = new Function(`
const location = {
+89
View File
@@ -717,3 +717,92 @@ return {
assert.equal(snapshot.sleepCalls >= 3, true);
assert.equal(snapshot.targetChecks >= 1, true);
});
test('prepareSignupVerificationFlow stops immediately when password page shows phone/password mismatch', async () => {
const api = new Function(`
const logs = [];
const clicks = [];
let now = 0;
Date.now = () => now;
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() { return 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 'Incorrect phone number or password'; }
function getVerificationCodeTarget() { return null; }
function is405MethodNotAllowedPage() { return false; }
async function recoverCurrentAuthRetryPage() {}
function createSignupUserAlreadyExistsError() { return new Error('user already exists'); }
function getSignupPasswordInput() { return { value: 'Secret123!' }; }
function getSignupPasswordSubmitButton() { return { textContent: 'Continue' }; }
function isSignupEmailAlreadyExistsPage() { return false; }
function isSignupPasswordErrorPage() { return false; }
function getSignupPasswordTimeoutErrorPageState() { return null; }
function isStep5Ready() { return false; }
function getSignupPasswordFieldErrorText() { return 'Incorrect phone number or password'; }
function simulateClick(target) { clicks.push(target?.textContent || 'clicked'); }
async function humanPause() {}
function fillInput() {}
function logSignupPasswordDiagnostics() {}
function createSignupPhonePasswordMismatchError(detailText = '') {
return new Error('SIGNUP_PHONE_PASSWORD_MISMATCH::' + detailText);
}
const location = {
href: 'https://auth.openai.com/log-in/password',
pathname: '/log-in/password',
};
const document = {
readyState: 'complete',
title: '',
body: {
textContent: 'Incorrect phone number or password',
innerText: 'Incorrect phone number or password',
},
querySelector() {
return null;
},
querySelectorAll() {
return [];
},
};
${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: '步骤 3 收尾',
}, 10000);
return { threw: false, logs, clicks };
} catch (error) {
return { threw: true, error: error.message, logs, clicks };
}
},
};
`)();
const result = await api.run();
assert.equal(result.threw, true);
assert.match(result.error, /SIGNUP_PHONE_PASSWORD_MISMATCH::Incorrect phone number or password/);
assert.equal(result.clicks.length, 0);
assert.equal(result.logs.some(({ message }) => /检测到密码页报错/.test(message)), true);
});