feat(flow): stabilize step7-9, expand HeroSMS, and update usage tutorial
This commit is contained in:
@@ -215,18 +215,20 @@ test('auto-run stops restarting on generic phone-page failure messages even with
|
||||
assert.ok(!result.events.logs.some(({ message }) => /回到步骤 7 重新开始授权流程/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run restarts from step 7 when phone verification cannot receive SMS after resend', async () => {
|
||||
test('auto-run does not restart step 7 when phone verification exhausted replacement attempts in add-phone flow', async () => {
|
||||
const harness = createHarness({
|
||||
failureStep: 9,
|
||||
failureBudget: 1,
|
||||
failureMessage: 'Phone verification could not receive an SMS after resend. Restart step 7 with a new number. Current number: 66959916439.',
|
||||
failureMessage: 'Step 9: phone verification did not succeed after 3 number replacements. Last reason: sms_timeout_after_resend.',
|
||||
authState: { state: 'add_phone_page', url: 'https://auth.openai.com/add-phone' },
|
||||
});
|
||||
|
||||
const events = await harness.run();
|
||||
const result = await harness.runAndCaptureError();
|
||||
|
||||
assert.equal(events.invalidations.length, 1);
|
||||
assert.deepStrictEqual(events.steps, [7, 8, 9, 7, 8, 9, 10]);
|
||||
assert.ok(result?.error);
|
||||
assert.equal(result.events.invalidations.length, 0);
|
||||
assert.deepStrictEqual(result.events.steps, [7, 8, 9]);
|
||||
assert.ok(!result.events.logs.some(({ message }) => /回到步骤 7 重新开始授权流程/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run stop errors after step 7 are rethrown immediately instead of restarting', async () => {
|
||||
|
||||
@@ -54,6 +54,13 @@ test('background account history settings are normalized independently from hotm
|
||||
extractFunction('normalizeHotmailLocalBaseUrl'),
|
||||
extractFunction('normalizeAccountRunHistoryHelperBaseUrl'),
|
||||
extractFunction('normalizeVerificationResendCount'),
|
||||
extractFunction('normalizePhoneVerificationReplacementLimit'),
|
||||
extractFunction('normalizePhoneCodeWaitSeconds'),
|
||||
extractFunction('normalizePhoneCodeTimeoutWindows'),
|
||||
extractFunction('normalizePhoneCodePollIntervalSeconds'),
|
||||
extractFunction('normalizePhoneCodePollMaxRounds'),
|
||||
extractFunction('normalizeHeroSmsMaxPrice'),
|
||||
extractFunction('normalizeHeroSmsCountryFallback'),
|
||||
extractFunction('normalizePersistentSettingValue'),
|
||||
].join('\n');
|
||||
|
||||
@@ -63,11 +70,28 @@ const DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL = DEFAULT_HOTMAIL_LOCAL_BASE_U
|
||||
const DEFAULT_HOTMAIL_REMOTE_BASE_URL = '';
|
||||
const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts';
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
const PHONE_CODE_WAIT_SECONDS_MIN = 15;
|
||||
const PHONE_CODE_WAIT_SECONDS_MAX = 300;
|
||||
const DEFAULT_PHONE_CODE_WAIT_SECONDS = 60;
|
||||
const PHONE_CODE_TIMEOUT_WINDOWS_MIN = 1;
|
||||
const PHONE_CODE_TIMEOUT_WINDOWS_MAX = 10;
|
||||
const DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS = 2;
|
||||
const PHONE_CODE_POLL_INTERVAL_SECONDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_INTERVAL_SECONDS_MAX = 30;
|
||||
const DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS = 5;
|
||||
const PHONE_CODE_POLL_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_ROUNDS_MAX = 120;
|
||||
const DEFAULT_PHONE_CODE_POLL_ROUNDS = 4;
|
||||
const DEFAULT_SUB2API_PROXY_NAME = '';
|
||||
const HOTMAIL_SERVICE_MODE_REMOTE = 'remote';
|
||||
const HOTMAIL_SERVICE_MODE_LOCAL = 'local';
|
||||
const VERIFICATION_RESEND_COUNT_MIN = 0;
|
||||
const VERIFICATION_RESEND_COUNT_MAX = 20;
|
||||
const HERO_SMS_COUNTRY_ID = 52;
|
||||
const HERO_SMS_COUNTRY_LABEL = 'Thailand';
|
||||
const PERSISTED_SETTING_DEFAULTS = {
|
||||
autoStepDelaySeconds: null,
|
||||
mailProvider: '163',
|
||||
@@ -101,6 +125,18 @@ return {
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneVerificationEnabled', 1), true);
|
||||
assert.equal(api.normalizePersistentSettingValue('verificationResendCount', '7'), 7);
|
||||
assert.equal(api.normalizePersistentSettingValue('verificationResendCount', '-1'), 0);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneVerificationReplacementLimit', '9'), 9);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneVerificationReplacementLimit', '-1'), 1);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneCodeWaitSeconds', '75'), 75);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneCodeTimeoutWindows', '3'), 3);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneCodePollIntervalSeconds', '6'), 6);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneCodePollMaxRounds', '18'), 18);
|
||||
assert.equal(api.normalizePersistentSettingValue('heroSmsMaxPrice', '0.123456'), '0.1235');
|
||||
assert.equal(api.normalizePersistentSettingValue('heroSmsMaxPrice', '0'), '');
|
||||
assert.deepStrictEqual(
|
||||
api.normalizePersistentSettingValue('heroSmsCountryFallback', [{ id: 16, label: 'United Kingdom' }, { id: 52 }]),
|
||||
[{ id: 16, label: 'United Kingdom' }, { id: 52, label: 'Country #52' }]
|
||||
);
|
||||
assert.equal(
|
||||
api.normalizePersistentSettingValue('accountRunHistoryHelperBaseUrl', 'http://127.0.0.1:17373/append-account-log'),
|
||||
'http://127.0.0.1:17373'
|
||||
|
||||
@@ -573,6 +573,8 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
||||
" luckmailPreserveTagName: '保留',",
|
||||
" currentLuckmailPurchase: { token: 'stale' },",
|
||||
" currentLuckmailMailCursor: { messageId: 'stale' },",
|
||||
' currentPhoneActivation: null,',
|
||||
' reusablePhoneActivation: null,',
|
||||
' email: null,',
|
||||
'};',
|
||||
'const CONTRIBUTION_RUNTIME_DEFAULTS = {',
|
||||
@@ -618,6 +620,7 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
||||
" accounts: [{ email: 'saved@example.com' }],",
|
||||
" tabRegistry: { foo: { tabId: 1 } },",
|
||||
" sourceLastUrls: { foo: 'https://example.com' },",
|
||||
" reusablePhoneActivation: { activationId: 'rx-001', phoneNumber: '66951112222', provider: 'hero-sms', serviceCode: 'dr', countryId: 52, successfulUses: 1, maxUses: 3 },",
|
||||
" luckmailApiKey: 'sk-session',",
|
||||
" luckmailBaseUrl: 'https://demo.example.com/',",
|
||||
" luckmailEmailType: 'ms_imap',",
|
||||
@@ -659,6 +662,16 @@ test('resetState preserves LuckMail session config, used map, and preserve tag c
|
||||
assert.equal(snapshot.storedPayload.luckmailPreserveTagName, '保留');
|
||||
assert.equal(snapshot.storedPayload.currentLuckmailPurchase, null);
|
||||
assert.equal(snapshot.storedPayload.currentLuckmailMailCursor, null);
|
||||
assert.deepStrictEqual(snapshot.storedPayload.reusablePhoneActivation, {
|
||||
activationId: 'rx-001',
|
||||
phoneNumber: '66951112222',
|
||||
provider: 'hero-sms',
|
||||
serviceCode: 'dr',
|
||||
countryId: 52,
|
||||
successfulUses: 1,
|
||||
maxUses: 3,
|
||||
});
|
||||
assert.equal(snapshot.storedPayload.currentPhoneActivation, null);
|
||||
});
|
||||
|
||||
test('handleStepData step 10 marks current LuckMail purchase as used and clears runtime state', async () => {
|
||||
|
||||
+1160
-342
File diff suppressed because it is too large
Load Diff
@@ -61,9 +61,6 @@ function createRow(initialDisplay = 'none') {
|
||||
test('sidepanel html places cloudflare temp email controls in a standalone section', () => {
|
||||
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
assert.match(html, /id="cloudflare-temp-email-section"/);
|
||||
assert.match(html, /id="btn-toggle-cloudflare-temp-email-section"/);
|
||||
assert.match(html, /aria-controls="cloudflare-temp-email-section-body"/);
|
||||
assert.match(html, /id="cloudflare-temp-email-section-body" class="section-collapse-body" hidden/);
|
||||
assert.match(html, /id="btn-cloudflare-temp-email-usage-guide"/);
|
||||
assert.match(html, /id="btn-cloudflare-temp-email-github"/);
|
||||
assert.match(html, /id="row-temp-email-random-subdomain-toggle"/);
|
||||
@@ -71,16 +68,6 @@ test('sidepanel html places cloudflare temp email controls in a standalone secti
|
||||
assert.doesNotMatch(html, /id="row-temp-email-random-subdomain-domain"/);
|
||||
});
|
||||
|
||||
test('sidepanel persists cloudflare temp email section collapse state', () => {
|
||||
assert.match(source, /CLOUDFLARE_TEMP_EMAIL_SECTION_EXPANDED_STORAGE_KEY = 'multipage-cloudflare-temp-email-section-expanded'/);
|
||||
assert.match(source, /let cloudflareTempEmailSectionExpanded = false/);
|
||||
assert.match(source, /function updateCloudflareTempEmailSectionExpandedUI\(\)/);
|
||||
assert.match(source, /cloudflareTempEmailSectionBody\.hidden = !expanded/);
|
||||
assert.match(source, /btnToggleCloudflareTempEmailSection\.setAttribute\('aria-expanded', String\(expanded\)\)/);
|
||||
assert.match(source, /btnToggleCloudflareTempEmailSection\?\.addEventListener\('click', \(\) => \{\s*toggleCloudflareTempEmailSectionExpanded\(\)/);
|
||||
assert.match(source, /initCloudflareTempEmailSectionExpandedState\(\)/);
|
||||
});
|
||||
|
||||
test('sidepanel modal message preserves line breaks and supports inline links', () => {
|
||||
const css = fs.readFileSync('sidepanel/sidepanel.css', 'utf8');
|
||||
assert.match(css, /\.modal-message\s*\{[\s\S]*white-space:\s*pre-line;/);
|
||||
|
||||
@@ -222,6 +222,7 @@ const inputAutoDelayMinutes = { value: '30' };
|
||||
const inputAutoStepDelaySeconds = { value: '10' };
|
||||
const inputVerificationResendCount = { value: '6' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
|
||||
function getCloudflareDomainsFromState() { return { domains: ['example.com'], activeDomain: 'example.com' }; }
|
||||
function normalizeCloudflareDomainValue(value) { return String(value || '').trim(); }
|
||||
|
||||
@@ -2,8 +2,6 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
|
||||
|
||||
function createAccountPoolUiStub() {
|
||||
return {
|
||||
createAccountPoolFormController({
|
||||
@@ -62,25 +60,11 @@ test('sidepanel loads hotmail manager before sidepanel bootstrap', () => {
|
||||
|
||||
test('sidepanel html contains collapsible hotmail form controls', () => {
|
||||
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
assert.match(html, /id="btn-toggle-hotmail-section"/);
|
||||
assert.match(html, /aria-controls="hotmail-section-body"/);
|
||||
assert.match(html, /id="hotmail-section-body" class="section-collapse-body" hidden/);
|
||||
assert.match(html, /id="btn-toggle-hotmail-form"/);
|
||||
assert.match(html, /id="hotmail-form-shell"/);
|
||||
assert.match(html, /id="btn-import-hotmail-accounts"[^>]*>批量导入</);
|
||||
});
|
||||
|
||||
test('sidepanel keeps hotmail account pool behind a persisted section collapse', () => {
|
||||
assert.match(sidepanelSource, /HOTMAIL_SECTION_EXPANDED_STORAGE_KEY = 'multipage-hotmail-section-expanded'/);
|
||||
assert.match(sidepanelSource, /let hotmailSectionExpanded = false/);
|
||||
assert.match(sidepanelSource, /function updateHotmailSectionExpandedUI\(\)/);
|
||||
assert.match(sidepanelSource, /hotmailSectionBody\.hidden = !expanded/);
|
||||
assert.match(sidepanelSource, /btnToggleHotmailSection\.setAttribute\('aria-expanded', String\(expanded\)\)/);
|
||||
assert.match(sidepanelSource, /btnToggleHotmailSection\?\.addEventListener\('click', \(\) => \{\s*toggleHotmailSectionExpanded\(\)/);
|
||||
assert.match(sidepanelSource, /btnToggleHotmailForm\?\.addEventListener\('click', \(\) => \{\s*setHotmailSectionExpanded\(true\)/);
|
||||
assert.match(sidepanelSource, /initHotmailSectionExpandedState\(\)/);
|
||||
});
|
||||
|
||||
test('hotmail manager exposes a factory and renders empty state', () => {
|
||||
const source = fs.readFileSync('sidepanel/hotmail-manager.js', 'utf8');
|
||||
const windowObject = {
|
||||
|
||||
@@ -130,6 +130,64 @@ const inputAutoDelayMinutes = { value: '30' };
|
||||
const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputVerificationResendCount = { value: '4' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
const DEFAULT_PHONE_CODE_WAIT_SECONDS = 60;
|
||||
const DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS = 2;
|
||||
const DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS = 5;
|
||||
const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4;
|
||||
const PHONE_CODE_WAIT_SECONDS_MIN = 15;
|
||||
const PHONE_CODE_WAIT_SECONDS_MAX = 300;
|
||||
const PHONE_CODE_TIMEOUT_WINDOWS_MIN = 1;
|
||||
const PHONE_CODE_TIMEOUT_WINDOWS_MAX = 10;
|
||||
const PHONE_CODE_POLL_INTERVAL_SECONDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_INTERVAL_SECONDS_MAX = 30;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120;
|
||||
const DEFAULT_HERO_SMS_REUSE_ENABLED = true;
|
||||
const HERO_SMS_ACQUIRE_PRIORITY_COUNTRY = 'country';
|
||||
const HERO_SMS_ACQUIRE_PRIORITY_PRICE = 'price';
|
||||
const DEFAULT_HERO_SMS_ACQUIRE_PRIORITY = HERO_SMS_ACQUIRE_PRIORITY_COUNTRY;
|
||||
const DEFAULT_HERO_SMS_COUNTRY_ID = 52;
|
||||
const DEFAULT_HERO_SMS_COUNTRY_LABEL = 'Thailand';
|
||||
const inputHeroSmsApiKey = { value: '' };
|
||||
const inputHeroSmsReuseEnabled = { checked: true };
|
||||
const selectHeroSmsAcquirePriority = { value: 'country' };
|
||||
const inputHeroSmsMaxPrice = { value: '' };
|
||||
const inputPhoneReplacementLimit = { value: '3' };
|
||||
const inputPhoneCodeWaitSeconds = { value: '60' };
|
||||
const inputPhoneCodeTimeoutWindows = { value: '2' };
|
||||
const inputPhoneCodePollIntervalSeconds = { value: '5' };
|
||||
const inputPhoneCodePollMaxRounds = { value: '4' };
|
||||
const selectHeroSmsCountry = { value: '52', selectedIndex: 0, options: [{ value: '52', textContent: 'Thailand' }] };
|
||||
function normalizeHeroSmsMaxPriceValue(value = '') { return String(value || '').trim(); }
|
||||
function normalizeHeroSmsReuseEnabledValue(value) { return value === undefined || value === null ? true : Boolean(value); }
|
||||
function normalizeHeroSmsAcquirePriority(value = '') { return String(value || '').trim().toLowerCase() === 'price' ? 'price' : 'country'; }
|
||||
function normalizeHeroSmsCountryId(value) { return Math.max(1, Math.floor(Number(value) || 52)); }
|
||||
function normalizeHeroSmsCountryLabel(value = '') { return String(value || '').trim() || 'Thailand'; }
|
||||
function normalizePhoneVerificationReplacementLimit(value, fallback = 3) {
|
||||
const parsed = Number.parseInt(String(value ?? '').trim(), 10);
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
function normalizePhoneCodeWaitSecondsValue(value, fallback = 60) {
|
||||
const parsed = Number.parseInt(String(value ?? '').trim(), 10);
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
function normalizePhoneCodeTimeoutWindowsValue(value, fallback = 2) {
|
||||
const parsed = Number.parseInt(String(value ?? '').trim(), 10);
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
function normalizePhoneCodePollIntervalSecondsValue(value, fallback = 5) {
|
||||
const parsed = Number.parseInt(String(value ?? '').trim(), 10);
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
function normalizePhoneCodePollMaxRoundsValue(value, fallback = 12) {
|
||||
const parsed = Number.parseInt(String(value ?? '').trim(), 10);
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
function getSelectedHeroSmsCountryOption() { return { id: 52, label: 'Thailand' }; }
|
||||
function syncHeroSmsFallbackSelectionOrderFromSelect() { return [{ id: 52, label: 'Thailand' }]; }
|
||||
function getPayPalAccounts() { return []; }
|
||||
function getCurrentPayPalAccount() { return null; }
|
||||
function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; }
|
||||
function normalizeCloudflareDomainValue(value) { return String(value || '').trim(); }
|
||||
function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; }
|
||||
@@ -322,9 +380,13 @@ const inputPhoneVerificationEnabled = { checked: false };
|
||||
const DEFAULT_PHONE_VERIFICATION_ENABLED = false;
|
||||
const inputHeroSmsApiKey = { value: '' };
|
||||
const inputHeroSmsMaxPrice = { value: '' };
|
||||
const inputPhoneReplacementLimit = { value: '' };
|
||||
const selectHeroSmsCountry = { value: '52', options: [{ value: '52' }] };
|
||||
const inputRunCount = { value: '' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
const PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
function syncLatestState(state) { latestState = { ...latestState, ...state }; }
|
||||
function syncAutoRunState() {}
|
||||
function syncPasswordField() {}
|
||||
@@ -348,7 +410,12 @@ function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) ||
|
||||
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
|
||||
function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); }
|
||||
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
|
||||
function normalizeHeroSmsMaxPriceValue(value) { return String(value ?? '').trim(); }
|
||||
function normalizeHeroSmsMaxPriceValue(value = '') { return String(value || '').trim(); }
|
||||
function normalizePhoneVerificationReplacementLimit(value, fallback = 3) {
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric)) return fallback;
|
||||
return Math.max(PHONE_REPLACEMENT_LIMIT_MIN, Math.min(PHONE_REPLACEMENT_LIMIT_MAX, Math.floor(numeric)));
|
||||
}
|
||||
function normalizeHeroSmsCountryId() { return 52; }
|
||||
function getSelectedHeroSmsCountryOption() { return { label: 'Thailand' }; }
|
||||
function updateHeroSmsPlatformDisplay() {}
|
||||
|
||||
@@ -201,6 +201,7 @@ const inputAutoDelayMinutes = { value: '30' };
|
||||
const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputVerificationResendCount = { value: '4' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
function getCloudflareDomainsFromState() {
|
||||
return { domains: [], activeDomain: '' };
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ const {
|
||||
} = require('../mail-provider-utils');
|
||||
|
||||
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
|
||||
const ipProxyPanelSource = fs.readFileSync('sidepanel/ip-proxy-panel.js', 'utf8');
|
||||
|
||||
function extractFunction(name) {
|
||||
const markers = [`async function ${name}(`, `function ${name}(`];
|
||||
@@ -57,78 +56,125 @@ test('sidepanel html exposes phone verification toggle and dedicated HeroSMS row
|
||||
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
|
||||
assert.match(html, /id="row-phone-verification-enabled"/);
|
||||
assert.match(html, /id="btn-toggle-phone-verification-section"/);
|
||||
assert.match(html, /id="row-phone-verification-fold"/);
|
||||
assert.match(html, /id="input-phone-verification-enabled"/);
|
||||
assert.match(html, /id="ip-proxy-section"/);
|
||||
assert.match(html, /id="row-ip-proxy-enabled"/);
|
||||
assert.match(html, /id="input-ip-proxy-enabled"/);
|
||||
assert.match(html, /id="row-hero-sms-platform"/);
|
||||
assert.match(html, /id="row-hero-sms-country"/);
|
||||
assert.match(html, /id="row-hero-sms-max-price"/);
|
||||
assert.match(html, /id="row-hero-sms-country-fallback"/);
|
||||
assert.match(html, /id="row-hero-sms-acquire-priority"/);
|
||||
assert.match(html, /id="select-hero-sms-acquire-priority"/);
|
||||
assert.match(html, /id="select-hero-sms-country"[^>]*multiple/);
|
||||
assert.doesNotMatch(html, /id="select-hero-sms-country-fallback"/);
|
||||
assert.match(html, /id="row-hero-sms-api-key"/);
|
||||
assert.match(html, /id="row-hero-sms-max-price"/);
|
||||
assert.match(html, /id="row-hero-sms-current-number"/);
|
||||
assert.match(html, /id="row-hero-sms-price-tiers"/);
|
||||
assert.match(html, /id="row-hero-sms-current-code"/);
|
||||
assert.match(html, /id="row-phone-replacement-limit"/);
|
||||
assert.match(html, /id="row-phone-verification-resend-count"/);
|
||||
assert.match(html, /id="row-phone-code-wait-seconds"/);
|
||||
assert.match(html, /id="row-phone-code-timeout-windows"/);
|
||||
assert.match(html, /id="row-phone-code-poll-interval-seconds"/);
|
||||
assert.match(html, /id="row-phone-code-poll-max-rounds"/);
|
||||
assert.doesNotMatch(html, /id="input-account-run-history-text-enabled"/);
|
||||
});
|
||||
|
||||
test('sidepanel renders IP proxy as a standalone card after sms verification without proxy status chrome', () => {
|
||||
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
const phoneToggleIndex = html.indexOf('id="row-phone-verification-enabled"');
|
||||
const ipProxySectionIndex = html.indexOf('id="ip-proxy-section"');
|
||||
const ipProxyToggleIndex = html.indexOf('id="row-ip-proxy-enabled"');
|
||||
const cloudflareSectionIndex = html.indexOf('id="cloudflare-temp-email-section"');
|
||||
|
||||
assert.match(html, /id="ip-proxy-section" class="data-card ip-proxy-card"/);
|
||||
assert.match(html, /id="btn-toggle-ip-proxy-section"/);
|
||||
assert.match(html, /aria-controls="row-ip-proxy-fold"/);
|
||||
assert.match(html, />展开设置<\/button>/);
|
||||
assert.ok(phoneToggleIndex >= 0);
|
||||
assert.ok(ipProxySectionIndex > phoneToggleIndex);
|
||||
assert.ok(ipProxyToggleIndex > phoneToggleIndex);
|
||||
assert.ok(cloudflareSectionIndex > ipProxySectionIndex);
|
||||
assert.doesNotMatch(html, /id="ip-proxy-enabled-status"/);
|
||||
assert.doesNotMatch(html, /id="row-ip-proxy-runtime-status"/);
|
||||
});
|
||||
|
||||
test('IP proxy standalone card supports persisted collapse control', () => {
|
||||
assert.match(ipProxyPanelSource, /IP_PROXY_SECTION_EXPANDED_STORAGE_KEY = 'multipage-ip-proxy-section-expanded'/);
|
||||
assert.match(ipProxyPanelSource, /let ipProxySectionExpanded = false/);
|
||||
assert.match(ipProxyPanelSource, /const showSettings = enabled && ipProxySectionExpanded/);
|
||||
assert.match(ipProxyPanelSource, /rowIpProxyFold\.style\.display = showSettings \? '' : 'none'/);
|
||||
assert.match(ipProxyPanelSource, /btnToggleIpProxySection\.setAttribute\('aria-expanded', String\(showSettings\)\)/);
|
||||
assert.match(sidepanelSource, /btnToggleIpProxySection\?\.addEventListener\('click', \(\) => \{\s*if \(typeof toggleIpProxySectionExpanded === 'function'\)/);
|
||||
assert.match(sidepanelSource, /initIpProxySectionExpandedState\(\)/);
|
||||
});
|
||||
|
||||
test('updatePhoneVerificationSettingsUI toggles HeroSMS rows from the sms switch', () => {
|
||||
const api = new Function(`
|
||||
const phoneVerificationSectionExpanded = true;
|
||||
const inputPhoneVerificationEnabled = { checked: false };
|
||||
const rowPhoneVerificationEnabled = { style: { display: 'none' } };
|
||||
const rowPhoneVerificationFold = { style: { display: 'none' } };
|
||||
const btnTogglePhoneVerificationSection = {
|
||||
disabled: false,
|
||||
textContent: '',
|
||||
title: '',
|
||||
setAttribute: () => {},
|
||||
};
|
||||
const rowHeroSmsPlatform = { style: { display: 'none' } };
|
||||
const rowHeroSmsCountry = { style: { display: 'none' } };
|
||||
const rowHeroSmsMaxPrice = { style: { display: 'none' } };
|
||||
const rowHeroSmsCountryFallback = { style: { display: 'none' } };
|
||||
const rowHeroSmsAcquirePriority = { style: { display: 'none' } };
|
||||
const rowHeroSmsApiKey = { style: { display: 'none' } };
|
||||
const rowHeroSmsMaxPrice = { style: { display: 'none' } };
|
||||
const rowHeroSmsCurrentNumber = { style: { display: 'none' } };
|
||||
const rowHeroSmsPriceTiers = { style: { display: 'none' } };
|
||||
const rowHeroSmsCurrentCode = { style: { display: 'none' } };
|
||||
const rowPhoneVerificationResendCount = { style: { display: 'none' } };
|
||||
const rowPhoneReplacementLimit = { style: { display: 'none' } };
|
||||
const rowPhoneCodeWaitSeconds = { style: { display: 'none' } };
|
||||
const rowPhoneCodeTimeoutWindows = { style: { display: 'none' } };
|
||||
const rowPhoneCodePollIntervalSeconds = { style: { display: 'none' } };
|
||||
const rowPhoneCodePollMaxRounds = { style: { display: 'none' } };
|
||||
|
||||
${extractFunction('updatePhoneVerificationSettingsUI')}
|
||||
|
||||
return {
|
||||
rowPhoneVerificationEnabled,
|
||||
rowPhoneVerificationFold,
|
||||
btnTogglePhoneVerificationSection,
|
||||
inputPhoneVerificationEnabled,
|
||||
rowHeroSmsPlatform,
|
||||
rowHeroSmsCountry,
|
||||
rowHeroSmsMaxPrice,
|
||||
rowHeroSmsCountryFallback,
|
||||
rowHeroSmsAcquirePriority,
|
||||
rowHeroSmsApiKey,
|
||||
rowHeroSmsMaxPrice,
|
||||
rowHeroSmsCurrentNumber,
|
||||
rowHeroSmsPriceTiers,
|
||||
rowHeroSmsCurrentCode,
|
||||
rowPhoneVerificationResendCount,
|
||||
rowPhoneReplacementLimit,
|
||||
rowPhoneCodeWaitSeconds,
|
||||
rowPhoneCodeTimeoutWindows,
|
||||
rowPhoneCodePollIntervalSeconds,
|
||||
rowPhoneCodePollMaxRounds,
|
||||
updatePhoneVerificationSettingsUI,
|
||||
};
|
||||
`)();
|
||||
|
||||
api.updatePhoneVerificationSettingsUI();
|
||||
assert.equal(api.rowPhoneVerificationEnabled.style.display, '');
|
||||
assert.equal(api.rowPhoneVerificationFold.style.display, 'none');
|
||||
assert.equal(api.btnTogglePhoneVerificationSection.disabled, true);
|
||||
assert.equal(api.btnTogglePhoneVerificationSection.textContent, '展开设置');
|
||||
assert.equal(api.rowHeroSmsPlatform.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCountry.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsMaxPrice.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCountryFallback.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsAcquirePriority.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsApiKey.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsMaxPrice.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCurrentNumber.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsPriceTiers.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCurrentCode.style.display, 'none');
|
||||
assert.equal(api.rowPhoneVerificationResendCount.style.display, 'none');
|
||||
assert.equal(api.rowPhoneReplacementLimit.style.display, 'none');
|
||||
assert.equal(api.rowPhoneCodeWaitSeconds.style.display, 'none');
|
||||
assert.equal(api.rowPhoneCodeTimeoutWindows.style.display, 'none');
|
||||
assert.equal(api.rowPhoneCodePollIntervalSeconds.style.display, 'none');
|
||||
assert.equal(api.rowPhoneCodePollMaxRounds.style.display, 'none');
|
||||
|
||||
api.inputPhoneVerificationEnabled.checked = true;
|
||||
api.updatePhoneVerificationSettingsUI();
|
||||
assert.equal(api.rowPhoneVerificationFold.style.display, '');
|
||||
assert.equal(api.btnTogglePhoneVerificationSection.disabled, false);
|
||||
assert.equal(api.btnTogglePhoneVerificationSection.textContent, '收起设置');
|
||||
assert.equal(api.rowHeroSmsPlatform.style.display, '');
|
||||
assert.equal(api.rowHeroSmsCountry.style.display, '');
|
||||
assert.equal(api.rowHeroSmsMaxPrice.style.display, '');
|
||||
assert.equal(api.rowHeroSmsCountryFallback.style.display, '');
|
||||
assert.equal(api.rowHeroSmsAcquirePriority.style.display, '');
|
||||
assert.equal(api.rowHeroSmsApiKey.style.display, '');
|
||||
assert.equal(api.rowHeroSmsMaxPrice.style.display, '');
|
||||
assert.equal(api.rowHeroSmsCurrentNumber.style.display, '');
|
||||
assert.equal(api.rowHeroSmsPriceTiers.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCurrentCode.style.display, '');
|
||||
assert.equal(api.rowPhoneVerificationResendCount.style.display, '');
|
||||
assert.equal(api.rowPhoneReplacementLimit.style.display, '');
|
||||
assert.equal(api.rowPhoneCodeWaitSeconds.style.display, '');
|
||||
assert.equal(api.rowPhoneCodeTimeoutWindows.style.display, '');
|
||||
assert.equal(api.rowPhoneCodePollIntervalSeconds.style.display, '');
|
||||
assert.equal(api.rowPhoneCodePollMaxRounds.style.display, '');
|
||||
});
|
||||
|
||||
test('collectSettingsPayload keeps local helper sync enabled while persisting sms toggle state', () => {
|
||||
@@ -179,9 +225,35 @@ const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputPhoneVerificationEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '4' };
|
||||
const inputHeroSmsApiKey = { value: 'demo-key' };
|
||||
const inputHeroSmsMaxPrice = { value: '0.08' };
|
||||
const inputHeroSmsReuseEnabled = { checked: true };
|
||||
const selectHeroSmsAcquirePriority = { value: 'price' };
|
||||
const inputHeroSmsMaxPrice = { value: '0.12' };
|
||||
const inputPhoneReplacementLimit = { value: '5' };
|
||||
const inputPhoneCodeWaitSeconds = { value: '75' };
|
||||
const inputPhoneCodeTimeoutWindows = { value: '3' };
|
||||
const inputPhoneCodePollIntervalSeconds = { value: '6' };
|
||||
const inputPhoneCodePollMaxRounds = { value: '18' };
|
||||
const inputAccountRunHistoryHelperBaseUrl = { value: 'http://127.0.0.1:17373' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
const DEFAULT_PHONE_CODE_WAIT_SECONDS = 60;
|
||||
const DEFAULT_PHONE_CODE_TIMEOUT_WINDOWS = 2;
|
||||
const DEFAULT_PHONE_CODE_POLL_INTERVAL_SECONDS = 5;
|
||||
const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4;
|
||||
const PHONE_CODE_WAIT_SECONDS_MIN = 15;
|
||||
const PHONE_CODE_WAIT_SECONDS_MAX = 300;
|
||||
const PHONE_CODE_TIMEOUT_WINDOWS_MIN = 1;
|
||||
const PHONE_CODE_TIMEOUT_WINDOWS_MAX = 10;
|
||||
const PHONE_CODE_POLL_INTERVAL_SECONDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_INTERVAL_SECONDS_MAX = 30;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120;
|
||||
const DEFAULT_HERO_SMS_REUSE_ENABLED = true;
|
||||
const HERO_SMS_ACQUIRE_PRIORITY_COUNTRY = 'country';
|
||||
const HERO_SMS_ACQUIRE_PRIORITY_PRICE = 'price';
|
||||
const DEFAULT_HERO_SMS_ACQUIRE_PRIORITY = HERO_SMS_ACQUIRE_PRIORITY_COUNTRY;
|
||||
const PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
const DEFAULT_HERO_SMS_COUNTRY_ID = 52;
|
||||
const DEFAULT_HERO_SMS_COUNTRY_LABEL = 'Thailand';
|
||||
const selectHeroSmsCountry = {
|
||||
@@ -206,10 +278,20 @@ function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) ||
|
||||
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
|
||||
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
|
||||
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
|
||||
${extractFunction('normalizeHeroSmsMaxPriceValue')}
|
||||
${extractFunction('normalizePhoneVerificationReplacementLimit')}
|
||||
${extractFunction('normalizePhoneCodeWaitSecondsValue')}
|
||||
${extractFunction('normalizePhoneCodeTimeoutWindowsValue')}
|
||||
${extractFunction('normalizePhoneCodePollIntervalSecondsValue')}
|
||||
${extractFunction('normalizePhoneCodePollMaxRoundsValue')}
|
||||
${extractFunction('normalizeHeroSmsReuseEnabledValue')}
|
||||
${extractFunction('normalizeHeroSmsAcquirePriority')}
|
||||
${extractFunction('normalizeHeroSmsCountryId')}
|
||||
${extractFunction('normalizeHeroSmsCountryLabel')}
|
||||
${extractFunction('normalizeHeroSmsMaxPriceValue')}
|
||||
${extractFunction('getSelectedHeroSmsCountryOption')}
|
||||
function syncHeroSmsFallbackSelectionOrderFromSelect() {
|
||||
return [{ id: 52, label: 'Thailand' }, { id: 16, label: 'United Kingdom' }];
|
||||
}
|
||||
${extractFunction('collectSettingsPayload')}
|
||||
return { collectSettingsPayload };
|
||||
`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
|
||||
@@ -220,7 +302,15 @@ return { collectSettingsPayload };
|
||||
assert.equal(payload.accountRunHistoryTextEnabled, true);
|
||||
assert.equal(payload.accountRunHistoryHelperBaseUrl, 'http://127.0.0.1:17373');
|
||||
assert.equal(payload.heroSmsApiKey, 'demo-key');
|
||||
assert.equal(payload.heroSmsMaxPrice, '0.08');
|
||||
assert.equal(payload.heroSmsReuseEnabled, true);
|
||||
assert.equal(payload.heroSmsAcquirePriority, 'price');
|
||||
assert.equal(payload.heroSmsMaxPrice, '0.12');
|
||||
assert.equal(payload.phoneVerificationReplacementLimit, 5);
|
||||
assert.equal(payload.phoneCodeWaitSeconds, 75);
|
||||
assert.equal(payload.phoneCodeTimeoutWindows, 3);
|
||||
assert.equal(payload.phoneCodePollIntervalSeconds, 6);
|
||||
assert.equal(payload.phoneCodePollMaxRounds, 18);
|
||||
assert.equal(payload.heroSmsCountryId, 52);
|
||||
assert.equal(payload.heroSmsCountryLabel, 'Thailand');
|
||||
assert.deepStrictEqual(payload.heroSmsCountryFallback, [{ id: 16, label: 'United Kingdom' }]);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
const step9ModuleSource = fs.readFileSync('background/steps/confirm-oauth.js', 'utf8');
|
||||
|
||||
const api = new Function('step9ModuleSource', `
|
||||
const self = {};
|
||||
let webNavListener = null;
|
||||
let webNavCommittedListener = null;
|
||||
let step8TabUpdatedListener = null;
|
||||
let step8PendingReject = null;
|
||||
let cleanupCalls = 0;
|
||||
let timeoutCalls = 0;
|
||||
let recoveryCalls = 0;
|
||||
let completePayload = null;
|
||||
const logs = [];
|
||||
const callbackUrl = 'http://localhost:1455/auth/callback?code=abc&state=xyz';
|
||||
|
||||
const chrome = {
|
||||
webNavigation: {
|
||||
onBeforeNavigate: {
|
||||
addListener(listener) {
|
||||
webNavListener = listener;
|
||||
setTimeout(() => {
|
||||
if (typeof webNavListener === 'function') {
|
||||
webNavListener({ tabId: 123, url: callbackUrl });
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
removeListener() {},
|
||||
},
|
||||
onCommitted: {
|
||||
addListener(listener) {
|
||||
webNavCommittedListener = listener;
|
||||
},
|
||||
removeListener() {},
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
onUpdated: {
|
||||
addListener(listener) {
|
||||
step8TabUpdatedListener = listener;
|
||||
},
|
||||
removeListener() {},
|
||||
},
|
||||
async update() {},
|
||||
},
|
||||
};
|
||||
|
||||
function cleanupStep8NavigationListeners() {
|
||||
cleanupCalls += 1;
|
||||
webNavListener = null;
|
||||
webNavCommittedListener = null;
|
||||
step8TabUpdatedListener = null;
|
||||
}
|
||||
|
||||
async function addLog(message) {
|
||||
logs.push(message);
|
||||
}
|
||||
|
||||
function throwIfStep8SettledOrStopped() {}
|
||||
|
||||
async function getTabId() {
|
||||
return 123;
|
||||
}
|
||||
|
||||
async function isTabAlive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function ensureStep8SignupPageReady() {}
|
||||
|
||||
async function waitForStep8Ready() {
|
||||
return {
|
||||
consentReady: true,
|
||||
url: 'https://auth.openai.com/sign-in-with-chatgpt/codex/consent',
|
||||
};
|
||||
}
|
||||
|
||||
async function triggerStep8ContentStrategy() {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async function waitForStep8ClickEffect() {
|
||||
return {
|
||||
progressed: true,
|
||||
reason: 'url_changed',
|
||||
url: 'https://auth.openai.com/sign-in-with-chatgpt/codex/consent',
|
||||
};
|
||||
}
|
||||
|
||||
async function getOAuthFlowStepTimeoutMs(defaultTimeoutMs, options = {}) {
|
||||
if (options.actionLabel === 'OAuth localhost 回调') {
|
||||
timeoutCalls += 1;
|
||||
if (timeoutCalls === 1) {
|
||||
throw new Error('步骤 9:从拿到 OAuth 登录地址开始,5 分钟内未完成OAuth localhost 回调,结束当前链路,准备从步骤 7 重新开始。');
|
||||
}
|
||||
}
|
||||
return defaultTimeoutMs;
|
||||
}
|
||||
|
||||
async function recoverOAuthLocalhostTimeout(details = {}) {
|
||||
recoveryCalls += 1;
|
||||
return {
|
||||
...(details.state || {}),
|
||||
oauthUrl: 'https://auth.openai.com/recovered-oauth',
|
||||
};
|
||||
}
|
||||
|
||||
function getStep8CallbackUrlFromNavigation(details, signupTabId) {
|
||||
if (
|
||||
Number(signupTabId) === Number(details?.tabId)
|
||||
&& String(details?.url || '').includes('http://localhost:1455/auth/callback')
|
||||
) {
|
||||
return details.url;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getStep8CallbackUrlFromTabUpdate() {
|
||||
return '';
|
||||
}
|
||||
|
||||
function getStep8EffectLabel() {
|
||||
return 'URL 已变化';
|
||||
}
|
||||
|
||||
async function prepareStep8DebuggerClick() {
|
||||
return { rect: { centerX: 10, centerY: 10 } };
|
||||
}
|
||||
|
||||
async function clickWithDebugger() {}
|
||||
async function reloadStep8ConsentPage() {}
|
||||
async function reuseOrCreateTab() { return 123; }
|
||||
async function sleepWithStop() {}
|
||||
|
||||
function setWebNavListener(listener) { webNavListener = listener; }
|
||||
function getWebNavListener() { return webNavListener; }
|
||||
function setWebNavCommittedListener(listener) { webNavCommittedListener = listener; }
|
||||
function getWebNavCommittedListener() { return webNavCommittedListener; }
|
||||
function setStep8TabUpdatedListener(listener) { step8TabUpdatedListener = listener; }
|
||||
function getStep8TabUpdatedListener() { return step8TabUpdatedListener; }
|
||||
function setStep8PendingReject(handler) { step8PendingReject = handler; }
|
||||
|
||||
async function completeStepFromBackground(step, payload) {
|
||||
completePayload = { step, payload };
|
||||
}
|
||||
|
||||
const STEP8_CLICK_RETRY_DELAY_MS = 200;
|
||||
const STEP8_READY_WAIT_TIMEOUT_MS = 30000;
|
||||
const STEP8_MAX_ROUNDS = 2;
|
||||
const STEP8_STRATEGIES = [
|
||||
{ mode: 'content', strategy: 'requestSubmit', label: 'form.requestSubmit' },
|
||||
{ mode: 'debugger', label: 'debugger click' },
|
||||
];
|
||||
|
||||
${step9ModuleSource}
|
||||
|
||||
const executor = self.MultiPageBackgroundStep9.createStep9Executor({
|
||||
addLog,
|
||||
chrome,
|
||||
cleanupStep8NavigationListeners,
|
||||
clickWithDebugger,
|
||||
completeStepFromBackground,
|
||||
ensureStep8SignupPageReady,
|
||||
getOAuthFlowStepTimeoutMs,
|
||||
getStep8CallbackUrlFromNavigation,
|
||||
getStep8CallbackUrlFromTabUpdate,
|
||||
getStep8EffectLabel,
|
||||
getTabId,
|
||||
getWebNavCommittedListener,
|
||||
getWebNavListener,
|
||||
getStep8TabUpdatedListener,
|
||||
isTabAlive,
|
||||
prepareStep8DebuggerClick,
|
||||
recoverOAuthLocalhostTimeout,
|
||||
reloadStep8ConsentPage,
|
||||
reuseOrCreateTab,
|
||||
setStep8PendingReject,
|
||||
setStep8TabUpdatedListener,
|
||||
setWebNavCommittedListener,
|
||||
setWebNavListener,
|
||||
sleepWithStop,
|
||||
STEP8_CLICK_RETRY_DELAY_MS,
|
||||
STEP8_MAX_ROUNDS,
|
||||
STEP8_READY_WAIT_TIMEOUT_MS,
|
||||
STEP8_STRATEGIES,
|
||||
throwIfStep8SettledOrStopped,
|
||||
triggerStep8ContentStrategy,
|
||||
waitForStep8ClickEffect,
|
||||
waitForStep8Ready,
|
||||
});
|
||||
|
||||
return {
|
||||
executeStep9: executor.executeStep9,
|
||||
snapshot() {
|
||||
return {
|
||||
cleanupCalls,
|
||||
timeoutCalls,
|
||||
recoveryCalls,
|
||||
completePayload,
|
||||
hasPendingReject: Boolean(step8PendingReject),
|
||||
logs,
|
||||
};
|
||||
},
|
||||
};
|
||||
`)(step9ModuleSource);
|
||||
|
||||
(async () => {
|
||||
await api.executeStep9({
|
||||
oauthUrl: 'https://auth.openai.com/original-oauth',
|
||||
visibleStep: 9,
|
||||
});
|
||||
|
||||
const snapshot = api.snapshot();
|
||||
assert.strictEqual(snapshot.timeoutCalls, 2, 'step9 should retry timeout budget check after recovery');
|
||||
assert.strictEqual(snapshot.recoveryCalls, 1, 'step9 should call timeout recovery hook exactly once');
|
||||
assert.strictEqual(snapshot.cleanupCalls >= 1, true, 'step9 should cleanup navigation listeners');
|
||||
assert.strictEqual(snapshot.hasPendingReject, false, 'step9 should clear pending reject after completion');
|
||||
assert.deepStrictEqual(snapshot.completePayload, {
|
||||
step: 9,
|
||||
payload: {
|
||||
localhostUrl: 'http://localhost:1455/auth/callback?code=abc&state=xyz',
|
||||
},
|
||||
});
|
||||
|
||||
console.log('step9 timeout recovery tests passed');
|
||||
})().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user