feat(phone-sms): support 5sim provider
(cherry picked from commit 7d10cab9765ed662b5088f791c8635fd8cc63e44)
This commit is contained in:
@@ -67,6 +67,12 @@ test('background account history settings are normalized independently from hotm
|
||||
extractFunction('normalizePhoneCodePollMaxRounds'),
|
||||
extractFunction('normalizeHeroSmsMaxPrice'),
|
||||
extractFunction('normalizeHeroSmsCountryFallback'),
|
||||
extractFunction('normalizePhoneSmsProvider'),
|
||||
extractFunction('normalizeFiveSimCountryId'),
|
||||
extractFunction('normalizeFiveSimCountryLabel'),
|
||||
extractFunction('normalizeFiveSimOperator'),
|
||||
extractFunction('normalizeFiveSimMaxPrice'),
|
||||
extractFunction('normalizeFiveSimCountryFallback'),
|
||||
extractFunction('normalizePersistentSettingValue'),
|
||||
].join('\n');
|
||||
|
||||
@@ -98,6 +104,11 @@ 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 PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const FIVE_SIM_OPERATOR = 'any';
|
||||
const PERSISTED_SETTING_DEFAULTS = {
|
||||
autoStepDelaySeconds: null,
|
||||
mailProvider: '163',
|
||||
@@ -142,7 +153,19 @@ return {
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneCodePollMaxRounds', '18'), 18);
|
||||
assert.equal(api.normalizePersistentSettingValue('heroSmsMaxPrice', '0.123456'), '0.1235');
|
||||
assert.equal(api.normalizePersistentSettingValue('heroSmsMaxPrice', '0'), '');
|
||||
assert.equal(api.normalizePersistentSettingValue('heroSmsCountryId', 0), 0);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', '5SIM'), '5sim');
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', 'unknown'), 'hero-sms');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimApiKey', ' demo-five '), ' demo-five ');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ' England! '), 'england');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryId', ''), 'england');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimCountryLabel', ''), '英国 (England)');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimMaxPrice', '9.87654'), '9.8765');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimMaxPrice', '-1'), '');
|
||||
assert.equal(api.normalizePersistentSettingValue('fiveSimOperator', ''), 'any');
|
||||
assert.deepStrictEqual(
|
||||
api.normalizePersistentSettingValue('fiveSimCountryFallback', [{ id: 'usa', label: 'USA' }, 'thailand:Thailand']),
|
||||
[{ id: 'usa', label: 'USA' }, { id: 'thailand', label: 'Thailand' }]
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
api.normalizePersistentSettingValue('heroSmsCountryFallback', [{ id: 16, label: 'United Kingdom' }, { id: 52 }]),
|
||||
[{ id: 16, label: 'United Kingdom' }, { id: 52, label: 'Country #52' }]
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const source = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
|
||||
const api = new Function('self', `${source}; return self.PhoneSmsFiveSimProvider;`)({});
|
||||
|
||||
function createTextResponse(payload, ok = true, status = ok ? 200 : 400) {
|
||||
return {
|
||||
ok,
|
||||
status,
|
||||
text: async () => (typeof payload === 'string' ? payload : JSON.stringify(payload)),
|
||||
};
|
||||
}
|
||||
|
||||
test('5sim provider fetches profile balance with bearer token', async () => {
|
||||
const requests = [];
|
||||
const provider = api.createProvider({
|
||||
fetchImpl: async (url, options = {}) => {
|
||||
requests.push({ url: new URL(url), options });
|
||||
return createTextResponse({ balance: 123.45, frozen_balance: 6.7, rating: 99 });
|
||||
},
|
||||
});
|
||||
|
||||
const balance = await provider.fetchBalance({ fiveSimApiKey: 'demo-key' });
|
||||
|
||||
assert.equal(requests[0].url.pathname, '/v1/user/profile');
|
||||
assert.equal(requests[0].options.headers.Authorization, 'Bearer demo-key');
|
||||
assert.equal(balance.balance, 123.45);
|
||||
assert.equal(balance.frozenBalance, 6.7);
|
||||
});
|
||||
|
||||
test('5sim provider maps countries and prices', async () => {
|
||||
const requests = [];
|
||||
const provider = api.createProvider({
|
||||
fetchImpl: async (url, options = {}) => {
|
||||
const parsed = new URL(url);
|
||||
requests.push({ url: parsed, options });
|
||||
if (parsed.pathname === '/v1/guest/countries') {
|
||||
return createTextResponse({ england: { text_en: 'England', iso: { GB: 1 }, prefix: { 44: 1 } } });
|
||||
}
|
||||
if (parsed.pathname === '/v1/guest/prices') {
|
||||
return createTextResponse({ england: { any: { openai: { cost: 10, count: 2 } } } });
|
||||
}
|
||||
throw new Error(`unexpected ${parsed.pathname}`);
|
||||
},
|
||||
});
|
||||
|
||||
const countries = await provider.fetchCountries({});
|
||||
const prices = await provider.fetchPrices({}, { id: 'england', label: 'England' });
|
||||
const entries = provider.collectPriceEntries(prices, []);
|
||||
|
||||
assert.deepStrictEqual(countries[0], {
|
||||
id: 'england',
|
||||
label: '英国 (England)',
|
||||
searchText: 'england 英国 (England) England GB 44',
|
||||
});
|
||||
assert.equal(requests[1].url.searchParams.get('country'), 'england');
|
||||
assert.equal(requests[1].url.searchParams.get('product'), 'openai');
|
||||
assert.deepStrictEqual(entries, [{ cost: 10, count: 2, inStock: true }]);
|
||||
});
|
||||
|
||||
test('5sim provider buys, checks, finishes, cancels, bans, and reuses activation', async () => {
|
||||
const requests = [];
|
||||
const provider = api.createProvider({
|
||||
fetchImpl: async (url, options = {}) => {
|
||||
const parsed = new URL(url);
|
||||
requests.push({ url: parsed, options });
|
||||
if (parsed.pathname === '/v1/guest/products/england/any') {
|
||||
return createTextResponse({ openai: { Category: 'activation', Qty: 4, Price: 8 } });
|
||||
}
|
||||
if (parsed.pathname === '/v1/guest/prices') {
|
||||
return createTextResponse({ england: { any: { openai: { cost: 9.5, count: 4 } } } });
|
||||
}
|
||||
if (parsed.pathname === '/v1/user/buy/activation/england/any/openai') {
|
||||
return createTextResponse({ id: 1001, phone: '+447911123456', country: 'england', operator: 'any', status: 'PENDING' });
|
||||
}
|
||||
if (parsed.pathname === '/v1/user/check/1001') {
|
||||
return createTextResponse({ id: 1001, phone: '+447911123456', status: 'RECEIVED', sms: [{ text: 'code 112233' }] });
|
||||
}
|
||||
if (parsed.pathname === '/v1/user/finish/1001') return createTextResponse({ status: 'FINISHED' });
|
||||
if (parsed.pathname === '/v1/user/cancel/1001') return createTextResponse({ status: 'CANCELED' });
|
||||
if (parsed.pathname === '/v1/user/ban/1001') return createTextResponse({ status: 'BANNED' });
|
||||
if (parsed.pathname === '/v1/user/reuse/openai/447911123456') {
|
||||
return createTextResponse({ id: 1002, phone: '+447911123456', country: 'england', status: 'PENDING' });
|
||||
}
|
||||
throw new Error(`unexpected ${parsed.pathname}`);
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
const state = { fiveSimApiKey: 'demo-key', fiveSimCountryId: 'england', fiveSimCountryLabel: 'England', fiveSimMaxPrice: '12', fiveSimOperator: 'any' };
|
||||
const activation = await provider.requestActivation(state);
|
||||
const code = await provider.pollActivationCode(state, activation, { timeoutMs: 1000, intervalMs: 1, maxRounds: 1 });
|
||||
await provider.finishActivation(state, activation);
|
||||
await provider.cancelActivation(state, activation);
|
||||
await provider.banActivation(state, activation);
|
||||
const reused = await provider.reuseActivation(state, activation);
|
||||
|
||||
assert.equal(activation.provider, '5sim');
|
||||
assert.equal(activation.activationId, '1001');
|
||||
assert.equal(activation.countryId, 'england');
|
||||
assert.equal(code, '112233');
|
||||
assert.equal(reused.activationId, '1002');
|
||||
const buy = requests.find((entry) => entry.url.pathname.includes('/buy/activation'));
|
||||
assert.equal(buy.url.searchParams.get('maxPrice'), '12');
|
||||
assert.equal(buy.url.searchParams.get('reuse'), '1');
|
||||
assert.deepStrictEqual(
|
||||
requests.map((entry) => entry.url.pathname),
|
||||
[
|
||||
'/v1/guest/products/england/any',
|
||||
'/v1/guest/prices',
|
||||
'/v1/user/buy/activation/england/any/openai',
|
||||
'/v1/user/check/1001',
|
||||
'/v1/user/finish/1001',
|
||||
'/v1/user/cancel/1001',
|
||||
'/v1/user/ban/1001',
|
||||
'/v1/user/reuse/openai/447911123456',
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('5sim provider prefers buy-compatible products price over operator detail price', async () => {
|
||||
const requests = [];
|
||||
const provider = api.createProvider({
|
||||
fetchImpl: async (url, options = {}) => {
|
||||
const parsed = new URL(url);
|
||||
requests.push({ url: parsed, options });
|
||||
if (parsed.pathname === '/v1/guest/products/vietnam/any') {
|
||||
return createTextResponse({ openai: { Category: 'activation', Qty: 4609, Price: 0.08 } });
|
||||
}
|
||||
if (parsed.pathname === '/v1/guest/prices') {
|
||||
return createTextResponse({
|
||||
vietnam: {
|
||||
openai: {
|
||||
virtual21: { cost: 0.0769, count: 0 },
|
||||
virtual47: { cost: 0.1282, count: 4608 },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (parsed.pathname === '/v1/user/buy/activation/vietnam/any/openai') {
|
||||
return createTextResponse({ id: 2001, phone: '+84901234567', country: 'vietnam', operator: 'any', status: 'PENDING' });
|
||||
}
|
||||
throw new Error(`unexpected ${parsed.pathname}`);
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
const activation = await provider.requestActivation({
|
||||
fiveSimApiKey: 'demo-key',
|
||||
fiveSimCountryId: 'vietnam',
|
||||
fiveSimCountryLabel: '越南 (Vietnam)',
|
||||
fiveSimOperator: 'any',
|
||||
});
|
||||
|
||||
assert.equal(activation.activationId, '2001');
|
||||
const buy = requests.find((entry) => entry.url.pathname.includes('/buy/activation'));
|
||||
assert.equal(buy.url.searchParams.get('maxPrice'), '0.08');
|
||||
assert.deepStrictEqual(
|
||||
requests.map((entry) => entry.url.pathname),
|
||||
[
|
||||
'/v1/guest/products/vietnam/any',
|
||||
'/v1/guest/prices',
|
||||
'/v1/user/buy/activation/vietnam/any/openai',
|
||||
]
|
||||
);
|
||||
});
|
||||
@@ -440,11 +440,11 @@ test('phone verification helper retries acquisition rounds when at least one cou
|
||||
assert.equal(sleeps.length, 1);
|
||||
assert.equal(sleeps[0], 2000);
|
||||
assert.equal(
|
||||
logs.filter((entry) => String(entry.message || '').includes('HeroSMS acquiring phone number')).length >= 2,
|
||||
logs.filter((entry) => String(entry.message || '').includes('HeroSMS 正在获取手机号')).length >= 2,
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
logs.some((entry) => String(entry.message || '').includes('HeroSMS has no available numbers (round 1/2); retrying')),
|
||||
logs.some((entry) => String(entry.message || '').includes('HeroSMS 暂无可用号码(第 1/2 轮)')),
|
||||
true
|
||||
);
|
||||
});
|
||||
@@ -1864,7 +1864,7 @@ test('phone verification helper replaces numbers in step 9 and stops after repla
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
/did not succeed after 3 number replacements/i
|
||||
/更换 3 次号码后手机号验证仍未成功/
|
||||
);
|
||||
assert.ok(statusCallsById['123456'] >= 2, 'first number should be polled twice before being replaced');
|
||||
assert.ok(messages.includes('SUBMIT_PHONE_NUMBER'));
|
||||
@@ -1951,7 +1951,7 @@ test('phone verification helper honors timeout-window and poll-round settings be
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
/did not succeed after 1 number replacements/i
|
||||
/更换 1 次号码后手机号验证仍未成功/
|
||||
);
|
||||
|
||||
assert.equal(messages.includes('RESEND_PHONE_VERIFICATION_CODE'), false);
|
||||
@@ -2051,7 +2051,7 @@ test('phone verification helper respects configured number replacement limit', a
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
/did not succeed after 1 number replacements/i
|
||||
/更换 1 次号码后手机号验证仍未成功/
|
||||
);
|
||||
|
||||
const actions = requests.map((requestUrl) => requestUrl.searchParams.get('action'));
|
||||
@@ -4110,3 +4110,205 @@ test('phone verification helper logs no-supply diagnostics with consecutive stre
|
||||
assert.equal(currentState.phoneNoSupplyFailureStreak, 2);
|
||||
assert.equal(requests.some((entry) => entry.searchParams.get('action') === 'getNumber'), true);
|
||||
});
|
||||
|
||||
test('phone verification helper routes 5sim buy, check, and finish by current activation provider', async () => {
|
||||
const requests = [];
|
||||
let currentState = {
|
||||
phoneSmsProvider: '5sim',
|
||||
fiveSimApiKey: 'demo-key',
|
||||
fiveSimCountryId: 'england',
|
||||
fiveSimCountryLabel: 'England',
|
||||
fiveSimMaxPrice: '12',
|
||||
fiveSimOperator: 'any',
|
||||
verificationResendCount: 0,
|
||||
phoneVerificationReplacementLimit: 2,
|
||||
phoneCodeWaitSeconds: 60,
|
||||
phoneCodeTimeoutWindows: 1,
|
||||
phoneCodePollIntervalSeconds: 1,
|
||||
phoneCodePollMaxRounds: 1,
|
||||
currentPhoneActivation: null,
|
||||
reusablePhoneActivation: null,
|
||||
};
|
||||
|
||||
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
|
||||
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
|
||||
const helpers = api.createPhoneVerificationHelpers({
|
||||
addLog: async () => {},
|
||||
ensureStep8SignupPageReady: async () => {},
|
||||
createFiveSimProvider: fiveSimModule.createProvider,
|
||||
fetchImpl: async (url, options = {}) => {
|
||||
const parsedUrl = new URL(url);
|
||||
requests.push({ url: parsedUrl, options });
|
||||
if (parsedUrl.pathname === '/v1/guest/products/england/any') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ openai: { Category: 'activation', Qty: 3, Price: 9.5 } }),
|
||||
};
|
||||
}
|
||||
if (parsedUrl.pathname === '/v1/guest/prices') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ england: { any: { openai: { cost: 9.5, count: 3 } } } }),
|
||||
};
|
||||
}
|
||||
if (parsedUrl.pathname === '/v1/user/buy/activation/england/any/openai') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ id: 5001, phone: '+447911223344', country: 'england', operator: 'any', status: 'PENDING' }),
|
||||
};
|
||||
}
|
||||
if (parsedUrl.pathname === '/v1/user/check/5001') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ id: 5001, phone: '+447911223344', status: 'RECEIVED', sms: [{ text: 'OpenAI code 123456' }] }),
|
||||
};
|
||||
}
|
||||
if (parsedUrl.pathname === '/v1/user/finish/5001') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ status: 'FINISHED' }),
|
||||
};
|
||||
}
|
||||
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
|
||||
},
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getState: async () => ({ ...currentState }),
|
||||
sendToContentScriptResilient: async (_source, message) => {
|
||||
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||
}
|
||||
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
|
||||
return { success: true, consentReady: true, url: 'https://auth.openai.com/authorize' };
|
||||
}
|
||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
const result = await helpers.completePhoneVerificationFlow(1, {
|
||||
addPhonePage: true,
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(result, {
|
||||
success: true,
|
||||
consentReady: true,
|
||||
url: 'https://auth.openai.com/authorize',
|
||||
});
|
||||
assert.equal(currentState.currentPhoneActivation, null);
|
||||
assert.equal(currentState.reusablePhoneActivation.provider, '5sim');
|
||||
assert.equal(currentState.reusablePhoneActivation.activationId, '5001');
|
||||
assert.equal(currentState.reusablePhoneActivation.successfulUses, 1);
|
||||
const buy = requests.find((entry) => entry.url.pathname.includes('/buy/activation'));
|
||||
assert.equal(buy.url.searchParams.get('maxPrice'), '12');
|
||||
assert.equal(buy.url.searchParams.get('reuse'), '1');
|
||||
assert.equal(buy.options.headers.Authorization, 'Bearer demo-key');
|
||||
assert.deepStrictEqual(
|
||||
requests.map((entry) => entry.url.pathname),
|
||||
[
|
||||
'/v1/guest/products/england/any',
|
||||
'/v1/guest/prices',
|
||||
'/v1/user/buy/activation/england/any/openai',
|
||||
'/v1/user/check/5001',
|
||||
'/v1/user/finish/5001',
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test('phone verification helper routes 5sim reusable activation through reuse endpoint', async () => {
|
||||
const requests = [];
|
||||
let currentState = {
|
||||
phoneSmsProvider: '5sim',
|
||||
fiveSimApiKey: 'demo-key',
|
||||
fiveSimCountryId: 'england',
|
||||
fiveSimCountryLabel: 'England',
|
||||
fiveSimOperator: 'any',
|
||||
verificationResendCount: 0,
|
||||
phoneVerificationReplacementLimit: 2,
|
||||
phoneCodeWaitSeconds: 60,
|
||||
phoneCodeTimeoutWindows: 1,
|
||||
phoneCodePollIntervalSeconds: 1,
|
||||
phoneCodePollMaxRounds: 1,
|
||||
currentPhoneActivation: null,
|
||||
reusablePhoneActivation: {
|
||||
activationId: '4001',
|
||||
phoneNumber: '+447911223344',
|
||||
provider: '5sim',
|
||||
serviceCode: 'openai',
|
||||
countryId: 'england',
|
||||
countryLabel: 'England',
|
||||
successfulUses: 1,
|
||||
maxUses: 3,
|
||||
},
|
||||
};
|
||||
|
||||
const fiveSimSource = fs.readFileSync('phone-sms/providers/five-sim.js', 'utf8');
|
||||
const fiveSimModule = new Function('self', `${fiveSimSource}; return self.PhoneSmsFiveSimProvider;`)({});
|
||||
const helpers = api.createPhoneVerificationHelpers({
|
||||
addLog: async () => {},
|
||||
ensureStep8SignupPageReady: async () => {},
|
||||
createFiveSimProvider: fiveSimModule.createProvider,
|
||||
fetchImpl: async (url) => {
|
||||
const parsedUrl = new URL(url);
|
||||
requests.push(parsedUrl);
|
||||
if (parsedUrl.pathname === '/v1/user/reuse/openai/447911223344') {
|
||||
return { ok: true, status: 200, text: async () => JSON.stringify({ id: 4002, phone: '+447911223344', country: 'england', status: 'PENDING' }) };
|
||||
}
|
||||
if (parsedUrl.pathname === '/v1/user/check/4002') {
|
||||
return { ok: true, status: 200, text: async () => JSON.stringify({ id: 4002, phone: '+447911223344', status: 'RECEIVED', sms: [{ code: '654321' }] }) };
|
||||
}
|
||||
if (parsedUrl.pathname === '/v1/user/finish/4002') {
|
||||
return { ok: true, status: 200, text: async () => JSON.stringify({ status: 'FINISHED' }) };
|
||||
}
|
||||
throw new Error(`Unexpected 5sim path: ${parsedUrl.pathname}`);
|
||||
},
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getState: async () => ({ ...currentState }),
|
||||
sendToContentScriptResilient: async (_source, message) => {
|
||||
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||
}
|
||||
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
|
||||
return { success: true, consentReady: true, url: 'https://auth.openai.com/authorize' };
|
||||
}
|
||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
const result = await helpers.completePhoneVerificationFlow(1, {
|
||||
addPhonePage: true,
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(result, {
|
||||
success: true,
|
||||
consentReady: true,
|
||||
url: 'https://auth.openai.com/authorize',
|
||||
});
|
||||
assert.equal(currentState.reusablePhoneActivation.activationId, '4002');
|
||||
assert.equal(currentState.reusablePhoneActivation.successfulUses, 1);
|
||||
assert.deepStrictEqual(
|
||||
requests.map((url) => url.pathname),
|
||||
[
|
||||
'/v1/user/reuse/openai/447911223344',
|
||||
'/v1/user/check/4002',
|
||||
'/v1/user/finish/4002',
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
@@ -200,6 +200,17 @@ const selectEmailGenerator = { value: 'duck' };
|
||||
const checkboxAutoDeleteIcloud = { checked: true };
|
||||
const selectIcloudHostPreference = { value: 'auto' };
|
||||
const inputPhoneVerificationEnabled = { checked: true };
|
||||
const selectPhoneSmsProvider = { value: 'hero-sms' };
|
||||
const inputHeroSmsApiKey = { value: '' };
|
||||
const inputHeroSmsReuseEnabled = { checked: true };
|
||||
const selectHeroSmsAcquirePriority = { value: 'country' };
|
||||
const inputHeroSmsMaxPrice = { value: '' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const inputPhoneReplacementLimit = { value: '3' };
|
||||
const inputPhoneCodeWaitSeconds = { value: '60' };
|
||||
const inputPhoneCodeTimeoutWindows = { value: '2' };
|
||||
const inputPhoneCodePollIntervalSeconds = { value: '5' };
|
||||
const inputPhoneCodePollMaxRounds = { value: '4' };
|
||||
const inputAccountRunHistoryTextEnabled = { checked: true };
|
||||
const inputAccountRunHistoryHelperBaseUrl = { value: 'http://127.0.0.1:17373' };
|
||||
const inputInbucketHost = { value: 'inbucket.local' };
|
||||
@@ -223,7 +234,33 @@ const inputAutoStepDelaySeconds = { value: '10' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '6' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO_SMS;
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
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 PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
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_MAX_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120;
|
||||
const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4;
|
||||
|
||||
function getCloudflareDomainsFromState() { return { domains: ['example.com'], activeDomain: 'example.com' }; }
|
||||
function normalizeCloudflareDomainValue(value) { return String(value || '').trim(); }
|
||||
@@ -242,6 +279,27 @@ 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.isFinite(Number(value)) ? Number(value) : fallback; }
|
||||
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
|
||||
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
|
||||
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { return String(value || '').trim() || fallback; }
|
||||
function normalizeFiveSimOperator(value = '', fallback = DEFAULT_FIVE_SIM_OPERATOR) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimMaxPriceValue(value = '') { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }
|
||||
function normalizeFiveSimCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeFiveSimCountryId(entry?.id ?? entry, ''), label: String(entry?.label || entry?.id || entry || '').trim() })).filter((entry) => entry.id) : []; }
|
||||
function normalizeHeroSmsMaxPriceValue(value = '') { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }
|
||||
function normalizePhoneSmsMaxPriceValue(value = '', provider = getSelectedPhoneSmsProvider()) { return normalizePhoneSmsProvider(provider) === '5sim' ? normalizeFiveSimMaxPriceValue(value) : normalizeHeroSmsMaxPriceValue(value); }
|
||||
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 normalizeHeroSmsCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeHeroSmsCountryId(entry?.id ?? entry), label: String(entry?.label || '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 = 4) { 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' }]; }
|
||||
${bundle}
|
||||
return {
|
||||
collectSettingsPayload,
|
||||
|
||||
@@ -178,6 +178,15 @@ return {
|
||||
assert.equal(api.getRunCountValue(), 3);
|
||||
});
|
||||
|
||||
test('sidepanel queues custom email pool refresh when the pool row is visible', () => {
|
||||
const source = extractFunction('updateMailProviderUI');
|
||||
|
||||
assert.match(
|
||||
source,
|
||||
/if \(useCustomEmailPool\) \{\s*syncRunCountFromCustomEmailPool\(\);\s*if \(typeof queueCustomEmailPoolRefresh === 'function'\) \{\s*queueCustomEmailPoolRefresh\(\);\s*\}\s*\}/
|
||||
);
|
||||
});
|
||||
|
||||
test('sidepanel custom verification dialog exposes add-phone action for step 8', async () => {
|
||||
const bundle = [
|
||||
extractFunction('getCustomVerificationPromptCopy'),
|
||||
|
||||
@@ -108,6 +108,8 @@ const selectIcloudFetchMode = { value: 'reuse_existing' };
|
||||
const selectIcloudTargetMailboxType = { value: 'forward-mailbox' };
|
||||
const selectIcloudForwardMailProvider = { value: 'gmail' };
|
||||
const inputPhoneVerificationEnabled = { checked: false };
|
||||
const selectPhoneSmsProvider = { value: 'hero-sms' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const inputAccountRunHistoryTextEnabled = { checked: false };
|
||||
const inputAccountRunHistoryHelperBaseUrl = { value: '' };
|
||||
const inputInbucketHost = { value: '' };
|
||||
@@ -131,25 +133,33 @@ const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
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 PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO_SMS;
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
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 PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
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_MAX_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120;
|
||||
const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
const inputHeroSmsApiKey = { value: '' };
|
||||
const inputHeroSmsReuseEnabled = { checked: true };
|
||||
const selectHeroSmsAcquirePriority = { value: 'country' };
|
||||
@@ -206,6 +216,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; }
|
||||
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
|
||||
function setPhoneSmsProviderSelectValue(provider) {
|
||||
const normalizedProvider = normalizePhoneSmsProvider(provider);
|
||||
selectPhoneSmsProvider.value = normalizedProvider;
|
||||
return normalizedProvider;
|
||||
}
|
||||
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
|
||||
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { return String(value || '').trim() || fallback; }
|
||||
function normalizeFiveSimOperator(value = '', fallback = DEFAULT_FIVE_SIM_OPERATOR) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimMaxPriceValue(value = '') { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }
|
||||
function normalizeFiveSimCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeFiveSimCountryId(entry?.id ?? entry, ''), label: String(entry?.label || entry?.id || entry || '').trim() })).filter((entry) => entry.id) : []; }
|
||||
function normalizePhoneSmsMaxPriceValue(value = '', provider = getSelectedPhoneSmsProvider()) { return normalizePhoneSmsProvider(provider) === '5sim' ? normalizeFiveSimMaxPriceValue(value) : normalizeHeroSmsMaxPriceValue(value); }
|
||||
function normalizeHeroSmsCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeHeroSmsCountryId(entry?.id ?? entry), label: String(entry?.label || 'Thailand') })) : []; }
|
||||
function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
|
||||
function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
|
||||
${bundle}
|
||||
@@ -379,16 +403,48 @@ const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '' };
|
||||
const inputPhoneVerificationEnabled = { checked: false };
|
||||
const selectPhoneSmsProvider = { value: 'hero-sms' };
|
||||
const DEFAULT_PHONE_VERIFICATION_ENABLED = false;
|
||||
const inputHeroSmsApiKey = { value: '' };
|
||||
const inputHeroSmsReuseEnabled = { checked: true };
|
||||
const selectHeroSmsAcquirePriority = { value: 'country' };
|
||||
const inputHeroSmsMaxPrice = { value: '' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const inputPhoneReplacementLimit = { value: '' };
|
||||
const inputPhoneCodeWaitSeconds = { value: '' };
|
||||
const inputPhoneCodeTimeoutWindows = { value: '' };
|
||||
const inputPhoneCodePollIntervalSeconds = { value: '' };
|
||||
const inputPhoneCodePollMaxRounds = { value: '' };
|
||||
const selectHeroSmsCountry = { value: '52', options: [{ value: '52' }] };
|
||||
const inputRunCount = { value: '' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO_SMS;
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
const PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
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_MAX_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120;
|
||||
const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4;
|
||||
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';
|
||||
function syncLatestState(state) { latestState = { ...latestState, ...state }; }
|
||||
function syncAutoRunState() {}
|
||||
function syncPasswordField() {}
|
||||
@@ -418,8 +474,29 @@ function normalizePhoneVerificationReplacementLimit(value, fallback = 3) {
|
||||
if (!Number.isFinite(numeric)) return fallback;
|
||||
return Math.max(PHONE_REPLACEMENT_LIMIT_MIN, Math.min(PHONE_REPLACEMENT_LIMIT_MAX, Math.floor(numeric)));
|
||||
}
|
||||
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
|
||||
function setPhoneSmsProviderSelectValue(provider) {
|
||||
const normalizedProvider = normalizePhoneSmsProvider(provider);
|
||||
selectPhoneSmsProvider.value = normalizedProvider;
|
||||
return normalizedProvider;
|
||||
}
|
||||
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
|
||||
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { return String(value || '').trim() || fallback; }
|
||||
function normalizeFiveSimOperator(value = '', fallback = DEFAULT_FIVE_SIM_OPERATOR) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimMaxPriceValue(value = '') { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }
|
||||
function normalizeFiveSimCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeFiveSimCountryId(entry?.id ?? entry, ''), label: String(entry?.label || entry?.id || entry || '').trim() })).filter((entry) => entry.id) : []; }
|
||||
function normalizeHeroSmsCountryId() { return 52; }
|
||||
function getSelectedHeroSmsCountryOption() { return { label: 'Thailand' }; }
|
||||
function normalizeHeroSmsCountryLabel(value = '') { return String(value || '').trim() || 'Thailand'; }
|
||||
function normalizeHeroSmsCountryFallbackList(value = []) { return Array.isArray(value) ? value : []; }
|
||||
function normalizeHeroSmsReuseEnabledValue(value) { return value === undefined || value === null ? true : Boolean(value); }
|
||||
function normalizeHeroSmsAcquirePriority(value = '') { return String(value || '').trim().toLowerCase() === 'price' ? 'price' : 'country'; }
|
||||
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 = 4) { const parsed = Number.parseInt(String(value ?? '').trim(), 10); return Number.isFinite(parsed) ? parsed : fallback; }
|
||||
function getSelectedHeroSmsCountryOption() { return { id: 52, label: 'Thailand' }; }
|
||||
function applyHeroSmsFallbackSelection() {}
|
||||
function updateHeroSmsPlatformDisplay() {}
|
||||
function applyAutoRunStatus() {}
|
||||
function markSettingsDirty() {}
|
||||
|
||||
@@ -178,6 +178,8 @@ const selectEmailGenerator = { value: 'duck' };
|
||||
const checkboxAutoDeleteIcloud = { checked: false };
|
||||
const selectIcloudHostPreference = { value: 'auto' };
|
||||
const inputPhoneVerificationEnabled = { checked: false };
|
||||
const selectPhoneSmsProvider = { value: 'hero-sms' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const inputAccountRunHistoryTextEnabled = { checked: false };
|
||||
const inputAccountRunHistoryHelperBaseUrl = { value: '' };
|
||||
const inputMail2925UseAccountPool = { checked: true };
|
||||
@@ -202,6 +204,32 @@ const inputAutoStepDelaySeconds = { value: '' };
|
||||
const inputOAuthFlowTimeoutEnabled = { checked: true };
|
||||
const inputVerificationResendCount = { value: '4' };
|
||||
const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO_SMS;
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
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 PHONE_REPLACEMENT_LIMIT_MIN = 1;
|
||||
const PHONE_REPLACEMENT_LIMIT_MAX = 20;
|
||||
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_MAX_ROUNDS_MIN = 1;
|
||||
const PHONE_CODE_POLL_MAX_ROUNDS_MAX = 120;
|
||||
const DEFAULT_PHONE_CODE_POLL_MAX_ROUNDS = 4;
|
||||
const DEFAULT_PHONE_VERIFICATION_REPLACEMENT_LIMIT = 3;
|
||||
function getCloudflareDomainsFromState() {
|
||||
return { domains: [], activeDomain: '' };
|
||||
@@ -226,6 +254,27 @@ 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; }
|
||||
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
|
||||
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
|
||||
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimCountryLabel(value = '', fallback = DEFAULT_FIVE_SIM_COUNTRY_LABEL) { return String(value || '').trim() || fallback; }
|
||||
function normalizeFiveSimOperator(value = '', fallback = DEFAULT_FIVE_SIM_OPERATOR) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
|
||||
function normalizeFiveSimMaxPriceValue(value = '') { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }
|
||||
function normalizeFiveSimCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeFiveSimCountryId(entry?.id ?? entry, ''), label: String(entry?.label || entry?.id || entry || '').trim() })).filter((entry) => entry.id) : []; }
|
||||
function normalizeHeroSmsMaxPriceValue(value = '') { const numeric = Number(String(value ?? '').trim()); return Number.isFinite(numeric) && numeric > 0 ? String(Math.round(numeric * 10000) / 10000) : ''; }
|
||||
function normalizePhoneSmsMaxPriceValue(value = '', provider = getSelectedPhoneSmsProvider()) { return normalizePhoneSmsProvider(provider) === '5sim' ? normalizeFiveSimMaxPriceValue(value) : normalizeHeroSmsMaxPriceValue(value); }
|
||||
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 normalizeHeroSmsCountryFallbackList(value = []) { return Array.isArray(value) ? value.map((entry) => ({ id: normalizeHeroSmsCountryId(entry?.id ?? entry), label: String(entry?.label || '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 = 4) { 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' }]; }
|
||||
${bundle}
|
||||
return { collectSettingsPayload };
|
||||
`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
|
||||
|
||||
@@ -52,7 +52,7 @@ function extractFunction(name) {
|
||||
return sidepanelSource.slice(start, end);
|
||||
}
|
||||
|
||||
test('sidepanel html exposes phone verification toggle and dedicated HeroSMS rows', () => {
|
||||
test('sidepanel html exposes phone verification toggle and multi-provider SMS rows', () => {
|
||||
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
|
||||
assert.match(html, /id="row-phone-verification-enabled"/);
|
||||
@@ -67,6 +67,12 @@ test('sidepanel html exposes phone verification toggle and dedicated HeroSMS row
|
||||
assert.match(html, /id="row-phone-sms-provider-order-actions"/);
|
||||
assert.match(html, /id="btn-phone-sms-provider-order-reset"/);
|
||||
assert.match(html, /id="row-hero-sms-platform"/);
|
||||
assert.match(html, /id="select-phone-sms-provider"/);
|
||||
assert.match(html, /\.\.\/phone-sms\/providers\/hero-sms\.js/);
|
||||
assert.match(html, /\.\.\/phone-sms\/providers\/five-sim\.js/);
|
||||
assert.match(html, /\.\.\/phone-sms\/providers\/registry\.js/);
|
||||
assert.match(html, /<option value="hero-sms">HeroSMS<\/option>/);
|
||||
assert.match(html, /<option value="5sim">5sim<\/option>/);
|
||||
assert.match(html, /id="row-hero-sms-country"/);
|
||||
assert.match(html, /id="row-hero-sms-country-fallback"/);
|
||||
assert.match(html, /id="row-hero-sms-acquire-priority"/);
|
||||
@@ -75,6 +81,10 @@ test('sidepanel html exposes phone verification toggle and dedicated HeroSMS row
|
||||
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="btn-phone-sms-balance"/);
|
||||
assert.match(html, /id="display-phone-sms-balance"/);
|
||||
assert.match(html, /id="row-five-sim-operator"/);
|
||||
assert.match(html, /id="input-five-sim-operator"/);
|
||||
assert.match(html, /id="row-hero-sms-current-number"/);
|
||||
assert.match(html, /id="row-hero-sms-current-countdown"/);
|
||||
assert.match(html, /id="row-hero-sms-price-tiers"/);
|
||||
@@ -107,7 +117,7 @@ test('sidepanel html exposes phone verification toggle and dedicated HeroSMS row
|
||||
assert.doesNotMatch(html, /id="input-account-run-history-text-enabled"/);
|
||||
});
|
||||
|
||||
test('updatePhoneVerificationSettingsUI toggles HeroSMS rows from the sms switch', () => {
|
||||
test('updatePhoneVerificationSettingsUI toggles SMS rows from the sms switch and provider selection', () => {
|
||||
const api = new Function(`
|
||||
const phoneVerificationSectionExpanded = true;
|
||||
let latestState = {};
|
||||
@@ -159,6 +169,7 @@ const rowHeroSmsCountryFallback = { style: { display: 'none' } };
|
||||
const rowHeroSmsAcquirePriority = { style: { display: 'none' } };
|
||||
const rowHeroSmsApiKey = { style: { display: 'none' } };
|
||||
const rowHeroSmsMaxPrice = { style: { display: 'none' } };
|
||||
const rowFiveSimOperator = { style: { display: 'none' } };
|
||||
const rowHeroSmsCurrentNumber = { style: { display: 'none' } };
|
||||
const rowHeroSmsCurrentCountdown = { style: { display: 'none' } };
|
||||
const rowHeroSmsPriceTiers = { style: { display: 'none' } };
|
||||
@@ -170,15 +181,12 @@ const rowPhoneCodeWaitSeconds = { style: { display: 'none' } };
|
||||
const rowPhoneCodeTimeoutWindows = { style: { display: 'none' } };
|
||||
const rowPhoneCodePollIntervalSeconds = { style: { display: 'none' } };
|
||||
const rowPhoneCodePollMaxRounds = { style: { display: 'none' } };
|
||||
const rowFiveSimApiKey = { style: { display: 'none' } };
|
||||
const rowFiveSimCountry = { style: { display: 'none' } };
|
||||
const rowFiveSimCountryFallback = { style: { display: 'none' } };
|
||||
const rowFiveSimOperator = { style: { display: 'none' } };
|
||||
const rowFiveSimProduct = { style: { display: 'none' } };
|
||||
const rowNexSmsApiKey = { style: { display: 'none' } };
|
||||
const rowNexSmsCountry = { style: { display: 'none' } };
|
||||
const rowNexSmsCountryFallback = { style: { display: 'none' } };
|
||||
const rowNexSmsServiceCode = { style: { display: 'none' } };
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
let selectedPhoneSmsProvider = PHONE_SMS_PROVIDER_HERO_SMS;
|
||||
function getSelectedPhoneSmsProvider() { return selectedPhoneSmsProvider; }
|
||||
function isFiveSimProviderSelected() { return getSelectedPhoneSmsProvider() === PHONE_SMS_PROVIDER_FIVE_SIM; }
|
||||
function updateHeroSmsPlatformDisplay() {}
|
||||
|
||||
${extractFunction('updatePhoneVerificationSettingsUI')}
|
||||
|
||||
@@ -198,6 +206,7 @@ return {
|
||||
rowHeroSmsAcquirePriority,
|
||||
rowHeroSmsApiKey,
|
||||
rowHeroSmsMaxPrice,
|
||||
rowFiveSimOperator,
|
||||
rowHeroSmsCurrentNumber,
|
||||
rowHeroSmsCurrentCountdown,
|
||||
rowHeroSmsPriceTiers,
|
||||
@@ -209,15 +218,7 @@ return {
|
||||
rowPhoneCodeTimeoutWindows,
|
||||
rowPhoneCodePollIntervalSeconds,
|
||||
rowPhoneCodePollMaxRounds,
|
||||
rowFiveSimApiKey,
|
||||
rowFiveSimCountry,
|
||||
rowFiveSimCountryFallback,
|
||||
rowFiveSimOperator,
|
||||
rowFiveSimProduct,
|
||||
rowNexSmsApiKey,
|
||||
rowNexSmsCountry,
|
||||
rowNexSmsCountryFallback,
|
||||
rowNexSmsServiceCode,
|
||||
setSelectedPhoneSmsProvider(value) { selectedPhoneSmsProvider = value; },
|
||||
updatePhoneVerificationSettingsUI,
|
||||
};
|
||||
`)();
|
||||
@@ -230,12 +231,13 @@ return {
|
||||
assert.equal(api.rowPhoneSmsProviderOrderActions.style.display, 'none');
|
||||
assert.equal(api.btnTogglePhoneVerificationSection.disabled, true);
|
||||
assert.equal(api.btnTogglePhoneVerificationSection.textContent, '展开设置');
|
||||
assert.equal(api.rowHeroSmsPlatform.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsPlatform.style.display, '');
|
||||
assert.equal(api.rowHeroSmsCountry.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.rowFiveSimOperator.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCurrentNumber.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCurrentCountdown.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsPriceTiers.style.display, 'none');
|
||||
@@ -271,6 +273,7 @@ return {
|
||||
assert.equal(api.rowHeroSmsAcquirePriority.style.display, '');
|
||||
assert.equal(api.rowHeroSmsApiKey.style.display, '');
|
||||
assert.equal(api.rowHeroSmsMaxPrice.style.display, '');
|
||||
assert.equal(api.rowFiveSimOperator.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsCurrentNumber.style.display, '');
|
||||
assert.equal(api.rowHeroSmsCurrentCountdown.style.display, '');
|
||||
assert.equal(api.rowHeroSmsPriceTiers.style.display, 'none');
|
||||
@@ -282,41 +285,10 @@ return {
|
||||
assert.equal(api.rowPhoneCodeTimeoutWindows.style.display, '');
|
||||
assert.equal(api.rowPhoneCodePollIntervalSeconds.style.display, '');
|
||||
assert.equal(api.rowPhoneCodePollMaxRounds.style.display, '');
|
||||
assert.equal(api.rowFiveSimApiKey.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimCountry.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimCountryFallback.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimOperator.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimProduct.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsApiKey.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsCountry.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsCountryFallback.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsServiceCode.style.display, 'none');
|
||||
|
||||
api.selectPhoneSmsProvider.value = '5sim';
|
||||
api.setLatestState({ phoneSmsProviderOrder: ['5sim'] });
|
||||
api.setSelectedPhoneSmsProvider('5sim');
|
||||
api.updatePhoneVerificationSettingsUI();
|
||||
assert.equal(api.rowHeroSmsCountry.style.display, 'none');
|
||||
assert.equal(api.rowHeroSmsApiKey.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimApiKey.style.display, '');
|
||||
assert.equal(api.rowFiveSimCountry.style.display, '');
|
||||
assert.equal(api.rowFiveSimCountryFallback.style.display, '');
|
||||
assert.equal(api.rowFiveSimOperator.style.display, '');
|
||||
assert.equal(api.rowFiveSimProduct.style.display, '');
|
||||
assert.equal(api.rowNexSmsApiKey.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsCountry.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsCountryFallback.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsServiceCode.style.display, 'none');
|
||||
|
||||
api.selectPhoneSmsProvider.value = 'nexsms';
|
||||
api.setLatestState({ phoneSmsProviderOrder: ['nexsms'] });
|
||||
api.updatePhoneVerificationSettingsUI();
|
||||
assert.equal(api.rowHeroSmsCountry.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimApiKey.style.display, 'none');
|
||||
assert.equal(api.rowFiveSimCountry.style.display, 'none');
|
||||
assert.equal(api.rowNexSmsApiKey.style.display, '');
|
||||
assert.equal(api.rowNexSmsCountry.style.display, '');
|
||||
assert.equal(api.rowNexSmsCountryFallback.style.display, '');
|
||||
assert.equal(api.rowNexSmsServiceCode.style.display, '');
|
||||
});
|
||||
|
||||
test('collectSettingsPayload keeps local helper sync enabled while persisting sms toggle state', () => {
|
||||
@@ -386,6 +358,7 @@ function getSelectedPhonePreferredActivation() {
|
||||
};
|
||||
}
|
||||
const inputHeroSmsMaxPrice = { value: '0.12' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const inputPhoneReplacementLimit = { value: '5' };
|
||||
const inputPhoneCodeWaitSeconds = { value: '75' };
|
||||
const inputPhoneCodeTimeoutWindows = { value: '3' };
|
||||
@@ -414,6 +387,12 @@ 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 PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_PHONE_SMS_PROVIDER = PHONE_SMS_PROVIDER_HERO_SMS;
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
const selectHeroSmsCountry = {
|
||||
value: '52',
|
||||
selectedIndex: 0,
|
||||
@@ -436,6 +415,14 @@ 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('normalizePhoneSmsProvider')}
|
||||
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
|
||||
${extractFunction('normalizeFiveSimCountryId')}
|
||||
${extractFunction('normalizeFiveSimCountryLabel')}
|
||||
${extractFunction('normalizeFiveSimOperator')}
|
||||
${extractFunction('normalizeFiveSimMaxPriceValue')}
|
||||
${extractFunction('normalizeFiveSimCountryFallbackList')}
|
||||
${extractFunction('normalizePhoneSmsMaxPriceValue')}
|
||||
${extractFunction('normalizeHeroSmsMaxPriceValue')}
|
||||
${extractFunction('normalizePhoneVerificationReplacementLimit')}
|
||||
${extractFunction('normalizePhoneCodeWaitSecondsValue')}
|
||||
@@ -485,6 +472,193 @@ return { collectSettingsPayload };
|
||||
assert.equal(payload.heroSmsCountryId, 52);
|
||||
assert.equal(payload.heroSmsCountryLabel, 'Thailand');
|
||||
assert.deepStrictEqual(payload.heroSmsCountryFallback, [{ id: 16, label: 'United Kingdom' }]);
|
||||
assert.equal(payload.fiveSimApiKey, '');
|
||||
assert.equal(payload.fiveSimCountryId, 'england');
|
||||
});
|
||||
|
||||
test('switchPhoneSmsProvider saves API keys independently when the select value has already changed', async () => {
|
||||
const api = new Function(`
|
||||
let latestState = {
|
||||
phoneSmsProvider: 'hero-sms',
|
||||
heroSmsApiKey: 'hero-old',
|
||||
fiveSimApiKey: 'five-old',
|
||||
heroSmsMaxPrice: '0.11',
|
||||
fiveSimMaxPrice: '12',
|
||||
heroSmsCountryId: 52,
|
||||
heroSmsCountryLabel: 'Thailand',
|
||||
heroSmsCountryFallback: [],
|
||||
fiveSimCountryId: 'england',
|
||||
fiveSimCountryLabel: '英国 (England)',
|
||||
fiveSimCountryFallback: [],
|
||||
fiveSimOperator: 'any',
|
||||
};
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
const DEFAULT_HERO_SMS_COUNTRY_ID = 52;
|
||||
const DEFAULT_HERO_SMS_COUNTRY_LABEL = 'Thailand';
|
||||
const selectPhoneSmsProvider = { value: 'hero-sms', dataset: { activeProvider: 'hero-sms' } };
|
||||
const inputHeroSmsApiKey = { value: 'hero-live' };
|
||||
const inputHeroSmsMaxPrice = { value: '0.22' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const displayHeroSmsPriceTiers = { textContent: '' };
|
||||
const displayPhoneSmsBalance = { textContent: '' };
|
||||
const rowHeroSmsPriceTiers = { style: { display: '' } };
|
||||
let heroSmsCountrySelectionOrder = [];
|
||||
let savedPayload = null;
|
||||
|
||||
${extractFunction('normalizePhoneSmsProvider')}
|
||||
${extractFunction('setPhoneSmsProviderSelectValue')}
|
||||
${extractFunction('getLastAppliedPhoneSmsProvider')}
|
||||
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
|
||||
${extractFunction('normalizeFiveSimCountryId')}
|
||||
${extractFunction('normalizeFiveSimCountryLabel')}
|
||||
${extractFunction('normalizeFiveSimOperator')}
|
||||
${extractFunction('normalizeFiveSimMaxPriceValue')}
|
||||
${extractFunction('normalizeHeroSmsMaxPriceValue')}
|
||||
${extractFunction('normalizePhoneSmsMaxPriceValue')}
|
||||
${extractFunction('normalizeHeroSmsCountryId')}
|
||||
${extractFunction('normalizeHeroSmsCountryLabel')}
|
||||
${extractFunction('normalizeHeroSmsCountryFallbackList')}
|
||||
${extractFunction('normalizeFiveSimCountryFallbackList')}
|
||||
function getSelectedHeroSmsCountryOption() {
|
||||
return getSelectedPhoneSmsProvider() === PHONE_SMS_PROVIDER_FIVE_SIM
|
||||
? { id: latestState.fiveSimCountryId || DEFAULT_FIVE_SIM_COUNTRY_ID, label: latestState.fiveSimCountryLabel || DEFAULT_FIVE_SIM_COUNTRY_LABEL }
|
||||
: { id: latestState.heroSmsCountryId || DEFAULT_HERO_SMS_COUNTRY_ID, label: latestState.heroSmsCountryLabel || DEFAULT_HERO_SMS_COUNTRY_LABEL };
|
||||
}
|
||||
function syncHeroSmsFallbackSelectionOrderFromSelect() {
|
||||
return getSelectedPhoneSmsProvider() === PHONE_SMS_PROVIDER_FIVE_SIM
|
||||
? [{ id: 'england', label: '英国 (England)' }]
|
||||
: [{ id: 52, label: 'Thailand' }];
|
||||
}
|
||||
function syncLatestState(patch) { latestState = { ...latestState, ...patch }; }
|
||||
function loadHeroSmsCountries() { return Promise.resolve(); }
|
||||
function applyHeroSmsFallbackSelection() {}
|
||||
function updatePhoneVerificationSettingsUI() {}
|
||||
function markSettingsDirty() {}
|
||||
function saveSettings() { savedPayload = { ...latestState }; return Promise.resolve(); }
|
||||
|
||||
${extractFunction('switchPhoneSmsProvider')}
|
||||
|
||||
return {
|
||||
selectPhoneSmsProvider,
|
||||
inputHeroSmsApiKey,
|
||||
get latestState() { return latestState; },
|
||||
get savedPayload() { return savedPayload; },
|
||||
switchPhoneSmsProvider,
|
||||
};
|
||||
`)();
|
||||
|
||||
// Browser change events update <select>.value before the listener runs.
|
||||
api.selectPhoneSmsProvider.value = '5sim';
|
||||
await api.switchPhoneSmsProvider(api.selectPhoneSmsProvider.value);
|
||||
|
||||
assert.equal(api.latestState.phoneSmsProvider, '5sim');
|
||||
assert.equal(api.latestState.heroSmsApiKey, 'hero-live');
|
||||
assert.equal(api.latestState.fiveSimApiKey, 'five-old');
|
||||
assert.equal(api.inputHeroSmsApiKey.value, 'five-old');
|
||||
assert.equal(api.selectPhoneSmsProvider.dataset.activeProvider, '5sim');
|
||||
|
||||
api.inputHeroSmsApiKey.value = 'five-live';
|
||||
api.selectPhoneSmsProvider.value = 'hero-sms';
|
||||
await api.switchPhoneSmsProvider(api.selectPhoneSmsProvider.value);
|
||||
|
||||
assert.equal(api.latestState.phoneSmsProvider, 'hero-sms');
|
||||
assert.equal(api.latestState.heroSmsApiKey, 'hero-live');
|
||||
assert.equal(api.latestState.fiveSimApiKey, 'five-live');
|
||||
assert.equal(api.inputHeroSmsApiKey.value, 'hero-live');
|
||||
assert.equal(api.selectPhoneSmsProvider.dataset.activeProvider, 'hero-sms');
|
||||
assert.equal(api.savedPayload.heroSmsApiKey, 'hero-live');
|
||||
assert.equal(api.savedPayload.fiveSimApiKey, 'five-live');
|
||||
});
|
||||
|
||||
test('previewHeroSmsPriceTiers prefers 5sim products price for buy-compatible any operator', async () => {
|
||||
const api = new Function(`
|
||||
let latestState = { phoneSmsProvider: '5sim', fiveSimOperator: 'any' };
|
||||
const PHONE_SMS_PROVIDER_HERO_SMS = 'hero-sms';
|
||||
const PHONE_SMS_PROVIDER_FIVE_SIM = '5sim';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_ID = 'england';
|
||||
const DEFAULT_FIVE_SIM_COUNTRY_LABEL = '英国 (England)';
|
||||
const DEFAULT_FIVE_SIM_OPERATOR = 'any';
|
||||
const inputHeroSmsMaxPrice = { value: '' };
|
||||
const inputHeroSmsApiKey = { value: '' };
|
||||
const inputFiveSimOperator = { value: 'any' };
|
||||
const displayHeroSmsPriceTiers = { textContent: '' };
|
||||
const rowHeroSmsPriceTiers = { style: { display: 'none' } };
|
||||
const fetchCalls = [];
|
||||
|
||||
${extractFunction('normalizePhoneSmsProvider')}
|
||||
function getSelectedPhoneSmsProvider() { return '5sim'; }
|
||||
${extractFunction('normalizeFiveSimCountryId')}
|
||||
${extractFunction('normalizeFiveSimCountryLabel')}
|
||||
${extractFunction('normalizeFiveSimOperator')}
|
||||
${extractFunction('normalizePhoneSmsMaxPriceValue')}
|
||||
${extractFunction('normalizeFiveSimMaxPriceValue')}
|
||||
${extractFunction('normalizeHeroSmsMaxPriceValue')}
|
||||
${extractFunction('normalizeHeroSmsPriceForPreview')}
|
||||
${extractFunction('formatHeroSmsPriceForPreview')}
|
||||
${extractFunction('isHeroSmsPreviewEmptyPayload')}
|
||||
${extractFunction('collectHeroSmsPriceEntriesForPreview')}
|
||||
${extractFunction('formatPhoneSmsPriceEntriesSummary')}
|
||||
${extractFunction('describeHeroSmsPreviewPayload')}
|
||||
${extractFunction('summarizeHeroSmsPreviewError')}
|
||||
function normalizeHeroSmsFetchErrorMessage(error) { return error?.message || String(error); }
|
||||
function syncHeroSmsFallbackSelectionOrderFromSelect() {
|
||||
return [{ id: 'vietnam', label: '越南 (Vietnam)' }];
|
||||
}
|
||||
function getSelectedHeroSmsCountryOption() {
|
||||
return { id: 'vietnam', label: '越南 (Vietnam)' };
|
||||
}
|
||||
function normalizePhoneSmsCountryId(value) { return normalizeFiveSimCountryId(value); }
|
||||
function normalizePhoneSmsCountryLabel(value) { return normalizeFiveSimCountryLabel(value); }
|
||||
function getHeroSmsCountryLabelById() { return '越南 (Vietnam)'; }
|
||||
async function fetch(url, options = {}) {
|
||||
const parsed = new URL(url);
|
||||
fetchCalls.push({ url: parsed, options });
|
||||
if (parsed.pathname === '/v1/guest/products/vietnam/any') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({ openai: { Category: 'activation', Qty: 4609, Price: 0.08 } }),
|
||||
};
|
||||
}
|
||||
if (parsed.pathname === '/v1/guest/prices') {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify({
|
||||
vietnam: {
|
||||
openai: {
|
||||
virtual21: { cost: 0.0769, count: 0 },
|
||||
virtual47: { cost: 0.1282, count: 4608 },
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
throw new Error('unexpected ' + parsed.pathname);
|
||||
}
|
||||
|
||||
${extractFunction('previewHeroSmsPriceTiers')}
|
||||
|
||||
return {
|
||||
displayHeroSmsPriceTiers,
|
||||
rowHeroSmsPriceTiers,
|
||||
fetchCalls,
|
||||
previewHeroSmsPriceTiers,
|
||||
};
|
||||
`)();
|
||||
|
||||
await api.previewHeroSmsPriceTiers();
|
||||
|
||||
assert.equal(api.displayHeroSmsPriceTiers.textContent, '越南 (Vietnam): 最低 0.08');
|
||||
assert.equal(api.rowHeroSmsPriceTiers.style.display, '');
|
||||
assert.deepStrictEqual(
|
||||
api.fetchCalls.map((entry) => entry.url.pathname),
|
||||
['/v1/guest/products/vietnam/any', '/v1/guest/prices']
|
||||
);
|
||||
});
|
||||
|
||||
test('hero sms max price input does not auto-save partial typing states', () => {
|
||||
|
||||
Reference in New Issue
Block a user