diff --git a/background/phone-verification-flow.js b/background/phone-verification-flow.js index d5c452c..c7f0277 100644 --- a/background/phone-verification-flow.js +++ b/background/phone-verification-flow.js @@ -565,6 +565,17 @@ return Math.max(PHONE_CODE_POLL_ROUNDS_MIN, Math.min(PHONE_CODE_POLL_ROUNDS_MAX, parsed)); } + function resolvePhoneCodePollMaxRoundsForWindow(waitSeconds, pollIntervalSeconds, configuredMaxRounds) { + const normalizedWaitSeconds = normalizePhoneCodeWaitSeconds(waitSeconds); + const normalizedPollIntervalSeconds = normalizePhoneCodePollIntervalSeconds(pollIntervalSeconds); + const normalizedConfiguredRounds = normalizePhoneCodePollMaxRounds(configuredMaxRounds); + const roundsNeededForWaitWindow = Math.max( + PHONE_CODE_POLL_ROUNDS_MIN, + Math.ceil(normalizedWaitSeconds / normalizedPollIntervalSeconds) + ); + return Math.max(normalizedConfiguredRounds, roundsNeededForWaitWindow); + } + function normalizeHeroSmsReuseEnabled(value) { if (value === undefined || value === null) { return Boolean(DEFAULT_HERO_SMS_REUSE_ENABLED); @@ -5461,15 +5472,17 @@ const waitSeconds = normalizePhoneCodeWaitSeconds(state?.phoneCodeWaitSeconds); const timeoutWindows = normalizePhoneCodeTimeoutWindows(state?.phoneCodeTimeoutWindows); const pollIntervalSeconds = normalizePhoneCodePollIntervalSeconds(state?.phoneCodePollIntervalSeconds); - const pollMaxRounds = normalizePhoneCodePollMaxRounds(state?.phoneCodePollMaxRounds); - let lastLoggedStatus = ''; - let lastLoggedPollCount = 0; + const pollMaxRounds = resolvePhoneCodePollMaxRoundsForWindow( + waitSeconds, + pollIntervalSeconds, + state?.phoneCodePollMaxRounds + ); let resendTriggeredForCurrentNumber = false; for (let windowIndex = 1; windowIndex <= timeoutWindows; windowIndex += 1) { await setPhoneRuntimeCountdown(normalizedActivation, waitSeconds, windowIndex, timeoutWindows); await addLog( - `步骤 9:等待号码 ${normalizedActivation.phoneNumber} 接收短信,最长 ${waitSeconds} 秒(第 ${windowIndex}/${timeoutWindows} 轮)。`, + `步骤 9:等待号码 ${normalizedActivation.phoneNumber} 接收短信(等待窗口 ${windowIndex}/${timeoutWindows},最长 ${waitSeconds} 秒,每 ${pollIntervalSeconds} 秒轮询一次,最多 ${pollMaxRounds} 次轮询)。`, 'info' ); try { @@ -5502,16 +5515,6 @@ ); } } - const shouldLog = ( - pollCount === 1 - || statusText !== lastLoggedStatus - || pollCount - lastLoggedPollCount >= 3 - ); - if (!shouldLog) { - return; - } - lastLoggedStatus = statusText; - lastLoggedPollCount = pollCount; await addLog( `步骤 9:${getPhoneSmsProviderLabel(normalizedActivation.provider)} 号码 ${normalizedActivation.phoneNumber} 状态:${statusText}(已等待 ${Math.ceil(elapsedMs / 1000)} 秒,第 ${pollCount}/${pollMaxRounds} 次轮询)。`, 'info' @@ -5737,9 +5740,11 @@ const waitSeconds = normalizePhoneCodeWaitSeconds(state?.phoneCodeWaitSeconds); const timeoutWindows = normalizePhoneCodeTimeoutWindows(state?.phoneCodeTimeoutWindows); const pollIntervalSeconds = normalizePhoneCodePollIntervalSeconds(state?.phoneCodePollIntervalSeconds); - const pollMaxRounds = normalizePhoneCodePollMaxRounds(state?.phoneCodePollMaxRounds); - let lastLoggedStatus = ''; - let lastLoggedPollCount = 0; + const pollMaxRounds = resolvePhoneCodePollMaxRoundsForWindow( + waitSeconds, + pollIntervalSeconds, + state?.phoneCodePollMaxRounds + ); for (let windowIndex = 1; windowIndex <= timeoutWindows; windowIndex += 1) { await setPhoneRuntimeState({ @@ -5752,7 +5757,7 @@ [PHONE_RUNTIME_COUNTDOWN_WINDOW_TOTAL_KEY]: timeoutWindows, }); await addLog( - `步骤 ${visibleStep}:正在等待 ${normalizedActivation.phoneNumber} 的短信验证码(${windowIndex}/${timeoutWindows},最长 ${waitSeconds} 秒)。`, + `步骤 ${visibleStep}:正在等待 ${normalizedActivation.phoneNumber} 的短信验证码(等待窗口 ${windowIndex}/${timeoutWindows},最长 ${waitSeconds} 秒,每 ${pollIntervalSeconds} 秒轮询一次,最多 ${pollMaxRounds} 次轮询)。`, 'info', { step: visibleStep, stepKey } ); @@ -5765,18 +5770,8 @@ intervalMs: pollIntervalSeconds * 1000, maxRounds: pollMaxRounds, onStatus: async ({ elapsedMs, pollCount, statusText }) => { - const shouldLog = ( - pollCount === 1 - || statusText !== lastLoggedStatus - || pollCount - lastLoggedPollCount >= 3 - ); - if (!shouldLog) { - return; - } - lastLoggedStatus = statusText; - lastLoggedPollCount = pollCount; await addLog( - `步骤 ${visibleStep}:${providerLabel} 状态 ${normalizedActivation.phoneNumber}: ${statusText}(已等待 ${Math.ceil(elapsedMs / 1000)} 秒,第 ${pollCount}/${pollMaxRounds} 轮)。`, + `步骤 ${visibleStep}:${providerLabel} 状态 ${normalizedActivation.phoneNumber}: ${statusText}(已等待 ${Math.ceil(elapsedMs / 1000)} 秒,第 ${pollCount}/${pollMaxRounds} 次轮询)。`, 'info', { step: visibleStep, stepKey } ); diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 80c1406..ede2cdc 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -1549,7 +1549,7 @@ diff --git a/tests/phone-verification-flow.test.js b/tests/phone-verification-flow.test.js index 9e4e35a..ce31cc0 100644 --- a/tests/phone-verification-flow.test.js +++ b/tests/phone-verification-flow.test.js @@ -715,7 +715,7 @@ test('signup phone helper does not let a hung page-state probe stall HeroSMS pol heroSmsReuseEnabled: false, phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 1, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, signupPhoneNumber: '66959916439', signupPhoneVerificationPurpose: 'signup', @@ -795,7 +795,7 @@ test('signup phone helper does not let a hung page-state probe stall HeroSMS pol caughtError = error; } - assert.equal(smsPollCount, 1, 'HeroSMS polling should continue after the first waiting status'); + assert.equal(smsPollCount, 1, 'HeroSMS polling should time out cleanly even when the page-state probe hangs'); assert.equal(pageReadyCalls, 2, 'should attempt the page-state probe during SMS polling'); assert.deepStrictEqual(contentMessages.map((message) => message.type), ['STEP8_GET_STATE']); assert.deepStrictEqual(statusActions, ['8']); @@ -3712,9 +3712,10 @@ test('phone verification helper replaces numbers in step 9 and stops after repla } }); -test('phone verification helper honors timeout-window and poll-round settings before replacing numbers', async () => { +test('phone verification helper supplements poll rounds to cover the full wait window before replacing numbers', async () => { const requests = []; const messages = []; + const logs = []; let currentState = { heroSmsApiKey: 'demo-key', verificationResendCount: 0, @@ -3728,7 +3729,9 @@ test('phone verification helper honors timeout-window and poll-round settings be }; const helpers = api.createPhoneVerificationHelpers({ - addLog: async () => {}, + addLog: async (message) => { + logs.push(String(message || '')); + }, ensureStep8SignupPageReady: async () => {}, fetchImpl: async (url) => { const parsedUrl = new URL(url); @@ -3787,8 +3790,16 @@ test('phone verification helper honors timeout-window and poll-round settings be assert.equal(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), false); assert.ok( - requests.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getStatus').length >= 2, - 'each replacement attempt should still poll HeroSMS at least once' + logs.some((message) => message.includes('等待窗口 1/1') && message.includes('最多 60 次轮询')), + 'wait log should show the effective poll count for the full window' + ); + assert.ok( + logs.some((message) => message.includes('第 2/60 次轮询')), + 'status logs should include consecutive poll counts instead of skipping from 1 to the last poll' + ); + assert.ok( + requests.filter((requestUrl) => requestUrl.searchParams.get('action') === 'getStatus').length >= 60, + 'each replacement attempt should poll long enough to cover the configured wait window' ); }); @@ -6203,9 +6214,9 @@ test('phone verification helper replaces number immediately when phone-verificat heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, phoneVerificationReplacementLimit: 2, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, currentPhoneActivation: null, reusablePhoneActivation: null, @@ -6309,9 +6320,9 @@ test('phone verification helper directly navigates back to add-phone when replac heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, phoneVerificationReplacementLimit: 2, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, currentPhoneActivation: null, reusablePhoneActivation: null, @@ -6474,9 +6485,9 @@ test('phone verification helper stops when add-phone recovery cannot be verified heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, phoneVerificationReplacementLimit: 2, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, currentPhoneActivation: null, reusablePhoneActivation: null, @@ -6597,9 +6608,9 @@ test('signup phone verification cancels activation when resend lands on contact- heroSmsCountryId: 52, heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, signupPhoneActivation: { activationId: '920001', @@ -6662,9 +6673,9 @@ test('signup phone verification cancels activation when resend lands on contact- heroSmsCountryId: 52, heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, signupPhoneActivation: { activationId: '930001', @@ -6746,9 +6757,9 @@ test('signup phone verification does not treat contact-verification URL-only sna heroSmsCountryId: 52, heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, signupPhoneActivation: { activationId: '930002', @@ -6828,9 +6839,9 @@ test('signup phone verification fails when contact-verification 500 appears afte heroSmsCountryId: 52, heroSmsCountryLabel: 'Thailand', verificationResendCount: 0, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, signupPhoneActivation: { activationId: '930003', @@ -6922,9 +6933,9 @@ test('phone verification helper skips page resend for 5sim timeouts and rotates fiveSimProduct: 'openai', verificationResendCount: 0, phoneVerificationReplacementLimit: 2, - phoneCodeWaitSeconds: 60, + phoneCodeWaitSeconds: 15, phoneCodeTimeoutWindows: 2, - phoneCodePollIntervalSeconds: 1, + phoneCodePollIntervalSeconds: 15, phoneCodePollMaxRounds: 1, currentPhoneActivation: null, reusablePhoneActivation: null,