fix: 兼容 HeroSMS 国家接口字典响应
This commit is contained in:
+30
-2
@@ -5827,6 +5827,34 @@ function normalizeHeroSmsFetchErrorMessage(error) {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeHeroSmsCountryPayloadEntries(source) {
|
||||||
|
if (Array.isArray(source)) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
if (!source || typeof source !== 'object') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return Object.values(source)
|
||||||
|
.filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHeroSmsCountryPayload(payload) {
|
||||||
|
const candidateSources = [
|
||||||
|
payload?.value,
|
||||||
|
payload?.data,
|
||||||
|
payload?.countries,
|
||||||
|
payload?.result,
|
||||||
|
payload,
|
||||||
|
];
|
||||||
|
for (const source of candidateSources) {
|
||||||
|
const entries = normalizeHeroSmsCountryPayloadEntries(source);
|
||||||
|
if (entries.length) {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeHeroSmsPriceForPreview(value) {
|
function normalizeHeroSmsPriceForPreview(value) {
|
||||||
const direct = Number(value);
|
const direct = Number(value);
|
||||||
if (Number.isFinite(direct) && direct >= 0) {
|
if (Number.isFinite(direct) && direct >= 0) {
|
||||||
@@ -7147,7 +7175,7 @@ async function loadHeroSmsCountries(options = {}) {
|
|||||||
});
|
});
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
const countries = Array.isArray(payload?.value) ? payload.value : (Array.isArray(payload) ? payload : []);
|
const countries = parseHeroSmsCountryPayload(payload);
|
||||||
if (!countries.length) {
|
if (!countries.length) {
|
||||||
throw new Error('国家列表为空');
|
throw new Error('国家列表为空');
|
||||||
}
|
}
|
||||||
@@ -16247,7 +16275,7 @@ async function switchPhoneSmsProvider(nextProvider) {
|
|||||||
if (displayPhoneSmsBalance) displayPhoneSmsBalance.textContent = '余额未获取';
|
if (displayPhoneSmsBalance) displayPhoneSmsBalance.textContent = '余额未获取';
|
||||||
if (rowHeroSmsPriceTiers) rowHeroSmsPriceTiers.style.display = 'none';
|
if (rowHeroSmsPriceTiers) rowHeroSmsPriceTiers.style.display = 'none';
|
||||||
|
|
||||||
await loadHeroSmsCountries();
|
await loadHeroSmsCountries({ silent: true });
|
||||||
const restoredPrimary = normalizedNextProvider === PHONE_SMS_PROVIDER_FIVE_SIM
|
const restoredPrimary = normalizedNextProvider === PHONE_SMS_PROVIDER_FIVE_SIM
|
||||||
? {
|
? {
|
||||||
id: normalizeFiveSimCountryId(latestState?.fiveSimCountryId),
|
id: normalizeFiveSimCountryId(latestState?.fiveSimCountryId),
|
||||||
|
|||||||
@@ -160,9 +160,39 @@ test('sidepanel loads SMS country lists silently during startup fallback', () =>
|
|||||||
assert.doesNotMatch(fiveSimLoader, /console\.(?:warn|error)\('加载 5sim 国家列表失败:'/);
|
assert.doesNotMatch(fiveSimLoader, /console\.(?:warn|error)\('加载 5sim 国家列表失败:'/);
|
||||||
assert.match(sidepanelSource, /loadHeroSmsCountries\(\{ silent: true, preferFallbackOnly: true \}\)/);
|
assert.match(sidepanelSource, /loadHeroSmsCountries\(\{ silent: true, preferFallbackOnly: true \}\)/);
|
||||||
assert.match(sidepanelSource, /loadFiveSimCountries\(\{ silent: true, preferFallbackOnly: true \}\)/);
|
assert.match(sidepanelSource, /loadFiveSimCountries\(\{ silent: true, preferFallbackOnly: true \}\)/);
|
||||||
|
assert.match(sidepanelSource, /await loadHeroSmsCountries\(\{ silent: true \}\);/);
|
||||||
assert.doesNotMatch(sidepanelSource, /console\.error\('加载 (?:HeroSMS|5sim|NexSMS) 国家列表失败:'/);
|
assert.doesNotMatch(sidepanelSource, /console\.error\('加载 (?:HeroSMS|5sim|NexSMS) 国家列表失败:'/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('HeroSMS country parser accepts keyed country maps from the live API', () => {
|
||||||
|
const api = new Function(`
|
||||||
|
${extractFunction('normalizeHeroSmsCountryPayloadEntries')}
|
||||||
|
${extractFunction('parseHeroSmsCountryPayload')}
|
||||||
|
return { parseHeroSmsCountryPayload };
|
||||||
|
`)();
|
||||||
|
|
||||||
|
const keyedPayload = {
|
||||||
|
52: { id: 52, eng: 'Thailand', chn: '泰国' },
|
||||||
|
6: { id: 6, eng: 'Indonesia', chn: '印度尼西亚' },
|
||||||
|
};
|
||||||
|
const ids = (payload) => api.parseHeroSmsCountryPayload(payload)
|
||||||
|
.map((entry) => entry.id)
|
||||||
|
.sort((left, right) => left - right);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
ids(keyedPayload),
|
||||||
|
[6, 52]
|
||||||
|
);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
ids({ value: keyedPayload }),
|
||||||
|
[6, 52]
|
||||||
|
);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
ids({ value: Object.values(keyedPayload) }),
|
||||||
|
[6, 52]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('sidepanel source wires free reusable phone save and clear actions to runtime messages', () => {
|
test('sidepanel source wires free reusable phone save and clear actions to runtime messages', () => {
|
||||||
assert.match(sidepanelSource, /const inputFreePhoneReuseEnabled = document\.getElementById\('input-free-phone-reuse-enabled'\);/);
|
assert.match(sidepanelSource, /const inputFreePhoneReuseEnabled = document\.getElementById\('input-free-phone-reuse-enabled'\);/);
|
||||||
assert.match(sidepanelSource, /const inputFreePhoneReuseAutoEnabled = document\.getElementById\('input-free-phone-reuse-auto-enabled'\);/);
|
assert.match(sidepanelSource, /const inputFreePhoneReuseAutoEnabled = document\.getElementById\('input-free-phone-reuse-auto-enabled'\);/);
|
||||||
|
|||||||
Reference in New Issue
Block a user