fix: route SMS send rejection through phone restart helper
- Merge latest dev into the PR branch before applying the maintainer fix - Expose the phone resend banned classifier from phone verification helpers - Use the helper from auto-run step 4 and keep restart logs reason-specific
This commit is contained in:
@@ -742,9 +742,11 @@ function getErrorMessage(error) {
|
||||
return error?.message || String(error || '');
|
||||
}
|
||||
|
||||
function isPhoneResendBannedNumberError(error) {
|
||||
return String(error?.message || error || '').startsWith('PHONE_RESEND_BANNED_NUMBER::');
|
||||
}
|
||||
const phoneVerificationHelpers = {
|
||||
isPhoneResendBannedNumberError(error) {
|
||||
return String(error?.message || error || '').startsWith('PHONE_RESEND_BANNED_NUMBER::');
|
||||
},
|
||||
};
|
||||
|
||||
async function getLoginAuthStateFromContent() {
|
||||
return { state: 'password_page', url: 'https://auth.openai.com/log-in' };
|
||||
@@ -771,7 +773,7 @@ return {
|
||||
{
|
||||
step: 1,
|
||||
options: {
|
||||
logLabel: '步骤 4 检测到手机号/密码不匹配后准备回到步骤 1 重新获取手机号重试(第 1 次重开)',
|
||||
logLabel: '步骤 4 检测到当前注册手机号无法接收短信后准备回到步骤 1 重新获取手机号重试(第 1 次重开)',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -28,6 +28,47 @@ test('background free reusable phone setter does not depend on module-scoped pho
|
||||
assert.match(setterBlock, /maxUses:\s*Math\.max\(1,\s*Math\.floor\(Number\(record\.maxUses\)\s*\|\|\s*3\)\)/);
|
||||
});
|
||||
|
||||
test('background free reusable phone setter can recover local HeroSMS activation id by phone number', () => {
|
||||
const source = fs.readFileSync('background.js', 'utf8');
|
||||
const setterStart = source.indexOf('async function setFreeReusablePhoneActivation');
|
||||
const setterEnd = source.indexOf('// ============================================================\n// Tab Registry', setterStart);
|
||||
const setterBlock = source.slice(setterStart, setterEnd);
|
||||
|
||||
assert.match(source, /function findLocalHeroSmsActivationForPhone\(/);
|
||||
assert.match(source, /state\.currentPhoneActivation/);
|
||||
assert.match(source, /state\.reusablePhoneActivation/);
|
||||
assert.match(source, /state\.signupPhoneActivation/);
|
||||
assert.match(source, /state\.signupPhoneCompletedActivation/);
|
||||
assert.match(source, /state\.phonePreferredActivation/);
|
||||
assert.match(source, /state\.phoneReusableActivationPool/);
|
||||
assert.match(setterBlock, /findLocalHeroSmsActivationForPhone\(state,\s*phoneNumber\)/);
|
||||
assert.match(setterBlock, /activationId = String\(\s*record\.activationId[\s\S]*localActivation\?\.activationId/);
|
||||
assert.match(setterBlock, /manualOnly:\s*!activationId/);
|
||||
});
|
||||
|
||||
test('background HeroSMS phone prefix inference covers built-in major countries', () => {
|
||||
const source = fs.readFileSync('background.js', 'utf8');
|
||||
const supportedStart = source.indexOf('const HERO_SMS_SUPPORTED_COUNTRY_IDS = [');
|
||||
const prefixStart = source.indexOf('const HERO_SMS_COUNTRY_BY_PHONE_PREFIX = Object.freeze([');
|
||||
const prefixEnd = source.indexOf(']);', prefixStart);
|
||||
const supportedBlock = source.slice(supportedStart, source.indexOf('];', supportedStart));
|
||||
const prefixBlock = source.slice(prefixStart, prefixEnd);
|
||||
|
||||
assert.match(supportedBlock, /\[6,\s*52,\s*187,\s*16,\s*151,\s*43,\s*73,\s*10/);
|
||||
[
|
||||
['84', 10, 'Vietnam'],
|
||||
['66', 52, 'Thailand'],
|
||||
['62', 6, 'Indonesia'],
|
||||
['44', 16, 'United Kingdom'],
|
||||
['81', 151, 'Japan'],
|
||||
['49', 43, 'Germany'],
|
||||
['33', 73, 'France'],
|
||||
['1', 187, 'USA'],
|
||||
].forEach(([prefix, id, label]) => {
|
||||
assert.match(prefixBlock, new RegExp(`prefix:\\s*'${prefix}'[\\s\\S]*id:\\s*${id}[\\s\\S]*label:\\s*'${label}'`));
|
||||
});
|
||||
});
|
||||
|
||||
test('message router module exposes a factory', () => {
|
||||
const source = fs.readFileSync('background/message-router.js', 'utf8');
|
||||
const globalScope = {};
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
require('../background/cloudmail-provider.js');
|
||||
|
||||
function createProviderApi(options = {}) {
|
||||
const {
|
||||
receiveMailbox = '',
|
||||
messages = [{
|
||||
id: 'mail-1',
|
||||
address: 'user@example.com',
|
||||
receivedDateTime: '2026-05-07T09:20:00.000Z',
|
||||
subject: 'OpenAI verification code',
|
||||
from: { emailAddress: { address: 'noreply@tm.openai.com' } },
|
||||
bodyPreview: 'Your verification code is 123456.',
|
||||
}],
|
||||
} = options;
|
||||
const logs = [];
|
||||
const listCalls = [];
|
||||
const fetchImpl = async (url, request = {}) => {
|
||||
const payload = request.body ? JSON.parse(request.body) : {};
|
||||
if (String(url).includes('/api/public/emailList')) {
|
||||
listCalls.push(payload.toEmail || '');
|
||||
return {
|
||||
ok: true,
|
||||
text: async () => JSON.stringify({ code: 200, data: { records: messages } }),
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
text: async () => JSON.stringify({ code: 200, data: { token: 'token' } }),
|
||||
};
|
||||
};
|
||||
|
||||
const api = globalThis.MultiPageBackgroundCloudMailProvider.createCloudMailProvider({
|
||||
addLog: async (message, level) => logs.push({ message, level }),
|
||||
buildCloudMailHeaders: () => ({}),
|
||||
CLOUD_MAIL_DEFAULT_PAGE_SIZE: 20,
|
||||
CLOUD_MAIL_GENERATOR: 'cloudmail',
|
||||
CLOUD_MAIL_PROVIDER: 'cloudmail',
|
||||
fetchImpl,
|
||||
getCloudMailTokenFromResponse: () => 'token',
|
||||
getState: async () => ({}),
|
||||
joinCloudMailUrl: (baseUrl, path) => `${baseUrl}${path}`,
|
||||
normalizeCloudMailAddress: (value) => String(value || '').trim().toLowerCase(),
|
||||
normalizeCloudMailBaseUrl: (value) => String(value || '').trim(),
|
||||
normalizeCloudMailDomain: (value) => String(value || '').trim(),
|
||||
normalizeCloudMailDomains: (values) => values || [],
|
||||
normalizeCloudMailMailApiMessages: (payload) => payload?.data?.records || [],
|
||||
pickVerificationMessageWithTimeFallback: (currentMessages) => ({
|
||||
match: currentMessages[0]
|
||||
? {
|
||||
code: String(currentMessages[0].bodyPreview).match(/(\d{6})/)[1],
|
||||
receivedAt: Date.parse(currentMessages[0].receivedDateTime),
|
||||
message: currentMessages[0],
|
||||
}
|
||||
: null,
|
||||
usedRelaxedFilters: false,
|
||||
usedTimeFallback: false,
|
||||
}),
|
||||
setEmailState: async () => {},
|
||||
setPersistentSettings: async () => {},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
return {
|
||||
...api,
|
||||
snapshot() {
|
||||
return { logs, listCalls };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('pollCloudMailVerificationCode returns code for generated Cloud Mail address', async () => {
|
||||
const api = createProviderApi();
|
||||
|
||||
const result = await api.pollCloudMailVerificationCode(4, {
|
||||
email: 'user@example.com',
|
||||
cloudMailBaseUrl: 'https://mail.example.com',
|
||||
cloudMailAdminEmail: 'admin@example.com',
|
||||
cloudMailAdminPassword: 'secret',
|
||||
cloudMailToken: 'token',
|
||||
}, {
|
||||
targetEmail: 'user@example.com',
|
||||
maxAttempts: 1,
|
||||
intervalMs: 1,
|
||||
});
|
||||
|
||||
assert.equal(result.code, '123456');
|
||||
assert.deepEqual(api.snapshot().listCalls, ['user@example.com']);
|
||||
});
|
||||
|
||||
test('pollCloudMailVerificationCode prefers configured receive mailbox over registration email', async () => {
|
||||
const api = createProviderApi({
|
||||
receiveMailbox: 'forward-box@example.com',
|
||||
messages: [{
|
||||
id: 'mail-2',
|
||||
address: 'forward-box@example.com',
|
||||
receivedDateTime: '2026-05-07T10:20:00.000Z',
|
||||
subject: 'Login verification code',
|
||||
from: { emailAddress: { address: 'noreply@tm.openai.com' } },
|
||||
bodyPreview: 'Your verification code is 654321.',
|
||||
}],
|
||||
});
|
||||
|
||||
const result = await api.pollCloudMailVerificationCode(8, {
|
||||
email: 'duck-forwarded@duck.com',
|
||||
mailProvider: 'cloudmail',
|
||||
emailGenerator: 'duck',
|
||||
cloudMailBaseUrl: 'https://mail.example.com',
|
||||
cloudMailReceiveMailbox: 'forward-box@example.com',
|
||||
cloudMailAdminEmail: 'admin@example.com',
|
||||
cloudMailAdminPassword: 'secret',
|
||||
cloudMailToken: 'token',
|
||||
}, {
|
||||
targetEmail: 'duck-forwarded@duck.com',
|
||||
maxAttempts: 1,
|
||||
intervalMs: 1,
|
||||
});
|
||||
|
||||
assert.equal(result.code, '654321');
|
||||
assert.deepEqual(api.snapshot().listCalls, ['forward-box@example.com']);
|
||||
});
|
||||
|
||||
test('pollCloudMailVerificationCode ignores stale receive mailbox when generator owns the address', async () => {
|
||||
const api = createProviderApi({
|
||||
receiveMailbox: 'forward-box@example.com',
|
||||
messages: [{
|
||||
id: 'mail-3',
|
||||
address: 'generated@example.com',
|
||||
receivedDateTime: '2026-05-07T11:20:00.000Z',
|
||||
subject: 'Signup verification code',
|
||||
from: { emailAddress: { address: 'noreply@tm.openai.com' } },
|
||||
bodyPreview: 'Your verification code is 246810.',
|
||||
}],
|
||||
});
|
||||
|
||||
const result = await api.pollCloudMailVerificationCode(4, {
|
||||
email: 'generated@example.com',
|
||||
mailProvider: 'cloudmail',
|
||||
emailGenerator: 'cloudmail',
|
||||
cloudMailBaseUrl: 'https://mail.example.com',
|
||||
cloudMailReceiveMailbox: 'forward-box@example.com',
|
||||
cloudMailAdminEmail: 'admin@example.com',
|
||||
cloudMailAdminPassword: 'secret',
|
||||
cloudMailToken: 'token',
|
||||
}, {
|
||||
targetEmail: 'generated@example.com',
|
||||
maxAttempts: 1,
|
||||
intervalMs: 1,
|
||||
});
|
||||
|
||||
assert.equal(result.code, '246810');
|
||||
assert.deepEqual(api.snapshot().listCalls, ['generated@example.com']);
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const {
|
||||
buildCloudMailHeaders,
|
||||
getCloudMailTokenFromResponse,
|
||||
normalizeCloudMailBaseUrl,
|
||||
normalizeCloudMailDomain,
|
||||
normalizeCloudMailDomains,
|
||||
normalizeCloudMailMailApiMessages,
|
||||
} = require('../cloudmail-utils.js');
|
||||
|
||||
test('normalizeCloudMailBaseUrl normalizes host and preserves path', () => {
|
||||
assert.equal(
|
||||
normalizeCloudMailBaseUrl('mail.example.com/api/'),
|
||||
'https://mail.example.com/api'
|
||||
);
|
||||
assert.equal(
|
||||
normalizeCloudMailBaseUrl('http://127.0.0.1:8080'),
|
||||
'http://127.0.0.1:8080'
|
||||
);
|
||||
assert.equal(normalizeCloudMailBaseUrl('::::'), '');
|
||||
});
|
||||
|
||||
test('normalizeCloudMailDomain and domains de-duplicate valid entries', () => {
|
||||
assert.equal(normalizeCloudMailDomain('@Mail.Example.com'), 'mail.example.com');
|
||||
assert.equal(normalizeCloudMailDomain('not-a-domain'), '');
|
||||
assert.deepEqual(
|
||||
normalizeCloudMailDomains(['mail.example.com', 'MAIL.EXAMPLE.COM', 'bad-value']),
|
||||
['mail.example.com']
|
||||
);
|
||||
});
|
||||
|
||||
test('buildCloudMailHeaders includes token and content type when needed', () => {
|
||||
assert.deepEqual(
|
||||
buildCloudMailHeaders({ token: 'token-value' }, { json: true }),
|
||||
{
|
||||
Authorization: 'token-value',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('normalizeCloudMailMailApiMessages extracts rows from nested Cloud Mail payloads', () => {
|
||||
const messages = normalizeCloudMailMailApiMessages({
|
||||
data: {
|
||||
records: [
|
||||
{
|
||||
emailId: 'mail-1',
|
||||
toEmail: 'User@Example.com',
|
||||
sendEmail: 'noreply@tm.openai.com',
|
||||
subject: 'OpenAI verification code',
|
||||
content: '<p>Your code is <strong>654321</strong> & ready.</p>',
|
||||
createTime: '2026-05-07 10:20:00',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(messages.length, 1);
|
||||
assert.equal(messages[0].id, 'mail-1');
|
||||
assert.equal(messages[0].address, 'user@example.com');
|
||||
assert.equal(messages[0].from.emailAddress.address, 'noreply@tm.openai.com');
|
||||
assert.match(messages[0].bodyPreview, /654321/);
|
||||
assert.match(messages[0].bodyPreview, /& ready/);
|
||||
assert.equal(messages[0].receivedDateTime, '2026-05-07T10:20:00.000Z');
|
||||
});
|
||||
|
||||
test('getCloudMailTokenFromResponse supports direct and nested response shapes', () => {
|
||||
assert.equal(getCloudMailTokenFromResponse({ token: 'one' }), 'one');
|
||||
assert.equal(getCloudMailTokenFromResponse({ data: { accessToken: 'two' } }), 'two');
|
||||
});
|
||||
@@ -3569,6 +3569,238 @@ test('phone verification helper gives automatic free reuse priority over paid re
|
||||
assert.equal(currentState.reusablePhoneActivation.activationId, 'paid-reuse');
|
||||
});
|
||||
|
||||
test('phone verification helper hands off manual-only free reuse even when automatic free reuse is enabled', async () => {
|
||||
const requests = [];
|
||||
const messages = [];
|
||||
const stops = [];
|
||||
let currentState = {
|
||||
heroSmsApiKey: 'demo-key',
|
||||
heroSmsReuseEnabled: true,
|
||||
freePhoneReuseEnabled: true,
|
||||
freePhoneReuseAutoEnabled: true,
|
||||
verificationResendCount: 0,
|
||||
currentPhoneActivation: null,
|
||||
reusablePhoneActivation: {
|
||||
activationId: 'paid-reuse',
|
||||
phoneNumber: '66950003333',
|
||||
provider: 'hero-sms',
|
||||
serviceCode: 'dr',
|
||||
countryId: 52,
|
||||
countryLabel: 'Thailand',
|
||||
successfulUses: 0,
|
||||
maxUses: 3,
|
||||
},
|
||||
freeReusablePhoneActivation: {
|
||||
phoneNumber: '84943328460',
|
||||
provider: 'hero-sms',
|
||||
serviceCode: 'dr',
|
||||
countryId: 52,
|
||||
countryLabel: 'Thailand',
|
||||
successfulUses: 1,
|
||||
maxUses: 3,
|
||||
source: 'free-manual-reuse',
|
||||
manualOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
const helpers = api.createPhoneVerificationHelpers({
|
||||
addLog: async () => {},
|
||||
ensureStep8SignupPageReady: async () => {},
|
||||
fetchImpl: async (url) => {
|
||||
const parsedUrl = new URL(url);
|
||||
requests.push(parsedUrl);
|
||||
throw new Error(`HeroSMS should not be called for manual-only free reuse: ${parsedUrl.searchParams.get('action')}`);
|
||||
},
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getState: async () => ({ ...currentState }),
|
||||
requestStop: async (payload) => {
|
||||
stops.push(payload);
|
||||
},
|
||||
sendToContentScriptResilient: async (_source, message) => {
|
||||
messages.push(message);
|
||||
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||
assert.equal(message.payload.phoneNumber, '84943328460');
|
||||
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||
}
|
||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
helpers.completePhoneVerificationFlow(1, {
|
||||
addPhonePage: true,
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
(error) => {
|
||||
assert.match(error.message, /^PHONE_MANUAL_FREE_REUSE::/);
|
||||
assert.equal(error.result?.manualFreePhoneReuse, true);
|
||||
assert.equal(error.result?.phoneNumber, '84943328460');
|
||||
assert.deepStrictEqual(error.result?.fillResult, {
|
||||
phoneVerificationPage: true,
|
||||
url: 'https://auth.openai.com/phone-verification',
|
||||
});
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepStrictEqual(messages.map((message) => message.type), ['SUBMIT_PHONE_NUMBER']);
|
||||
assert.equal(stops.length, 1);
|
||||
assert.match(stops[0].logMessage, /开始手动复用手机 84943328460/);
|
||||
assert.deepStrictEqual(requests, []);
|
||||
assert.equal(currentState.currentPhoneActivation, null);
|
||||
assert.equal(currentState.reusablePhoneActivation.activationId, 'paid-reuse');
|
||||
assert.equal(currentState.freeReusablePhoneActivation.phoneNumber, '84943328460');
|
||||
});
|
||||
|
||||
test('phone verification helper infers manual free reuse country from phone prefix', async () => {
|
||||
const messages = [];
|
||||
const stops = [];
|
||||
let currentState = {
|
||||
heroSmsApiKey: 'demo-key',
|
||||
heroSmsCountryId: 52,
|
||||
heroSmsCountryLabel: 'Thailand',
|
||||
freePhoneReuseEnabled: true,
|
||||
freePhoneReuseAutoEnabled: true,
|
||||
verificationResendCount: 0,
|
||||
currentPhoneActivation: null,
|
||||
freeReusablePhoneActivation: {
|
||||
phoneNumber: '84943328460',
|
||||
provider: 'hero-sms',
|
||||
serviceCode: 'dr',
|
||||
successfulUses: 1,
|
||||
maxUses: 3,
|
||||
source: 'free-manual-reuse',
|
||||
manualOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
const helpers = api.createPhoneVerificationHelpers({
|
||||
addLog: async () => {},
|
||||
ensureStep8SignupPageReady: async () => {},
|
||||
fetchImpl: async (url) => {
|
||||
const parsedUrl = new URL(url);
|
||||
throw new Error(`HeroSMS should not be called for manual-only free reuse: ${parsedUrl.searchParams.get('action')}`);
|
||||
},
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getState: async () => ({ ...currentState }),
|
||||
requestStop: async (payload) => {
|
||||
stops.push(payload);
|
||||
},
|
||||
sendToContentScriptResilient: async (_source, message) => {
|
||||
messages.push(message);
|
||||
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||
}
|
||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
helpers.completePhoneVerificationFlow(1, {
|
||||
addPhonePage: true,
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
(error) => {
|
||||
assert.match(error.message, /^PHONE_MANUAL_FREE_REUSE::/);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(stops.length, 1);
|
||||
assert.deepStrictEqual(messages.map((message) => message.type), ['SUBMIT_PHONE_NUMBER']);
|
||||
assert.equal(messages[0].payload.phoneNumber, '84943328460');
|
||||
assert.equal(messages[0].payload.countryId, 10);
|
||||
assert.equal(messages[0].payload.countryLabel, 'Vietnam');
|
||||
});
|
||||
|
||||
test('phone verification helper infers major HeroSMS countries from phone prefixes', async () => {
|
||||
const cases = [
|
||||
{ phoneNumber: '12025550123', countryId: 187, countryLabel: 'USA' },
|
||||
{ phoneNumber: '447911123456', countryId: 16, countryLabel: 'United Kingdom' },
|
||||
{ phoneNumber: '819012345678', countryId: 151, countryLabel: 'Japan' },
|
||||
{ phoneNumber: '4915112345678', countryId: 43, countryLabel: 'Germany' },
|
||||
{ phoneNumber: '33612345678', countryId: 73, countryLabel: 'France' },
|
||||
{ phoneNumber: '84943328460', countryId: 10, countryLabel: 'Vietnam' },
|
||||
{ phoneNumber: '66950003333', countryId: 52, countryLabel: 'Thailand' },
|
||||
{ phoneNumber: '628111111111', countryId: 6, countryLabel: 'Indonesia' },
|
||||
];
|
||||
|
||||
for (const testCase of cases) {
|
||||
const messages = [];
|
||||
let currentState = {
|
||||
heroSmsApiKey: 'demo-key',
|
||||
heroSmsCountryId: 52,
|
||||
heroSmsCountryLabel: 'Thailand',
|
||||
freePhoneReuseEnabled: true,
|
||||
freePhoneReuseAutoEnabled: true,
|
||||
verificationResendCount: 0,
|
||||
currentPhoneActivation: null,
|
||||
freeReusablePhoneActivation: {
|
||||
phoneNumber: testCase.phoneNumber,
|
||||
provider: 'hero-sms',
|
||||
serviceCode: 'dr',
|
||||
successfulUses: 1,
|
||||
maxUses: 3,
|
||||
source: 'free-manual-reuse',
|
||||
manualOnly: true,
|
||||
},
|
||||
};
|
||||
|
||||
const helpers = api.createPhoneVerificationHelpers({
|
||||
addLog: async () => {},
|
||||
ensureStep8SignupPageReady: async () => {},
|
||||
fetchImpl: async (url) => {
|
||||
const parsedUrl = new URL(url);
|
||||
throw new Error(`HeroSMS should not be called for manual-only free reuse: ${parsedUrl.searchParams.get('action')}`);
|
||||
},
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getState: async () => ({ ...currentState }),
|
||||
requestStop: async () => {},
|
||||
sendToContentScriptResilient: async (_source, message) => {
|
||||
messages.push(message);
|
||||
if (message.type === 'SUBMIT_PHONE_NUMBER') {
|
||||
return { phoneVerificationPage: true, url: 'https://auth.openai.com/phone-verification' };
|
||||
}
|
||||
throw new Error(`Unexpected content-script message: ${message.type}`);
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
helpers.completePhoneVerificationFlow(1, {
|
||||
addPhonePage: true,
|
||||
phoneVerificationPage: false,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
(error) => {
|
||||
assert.match(error.message, /^PHONE_MANUAL_FREE_REUSE::/);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.equal(messages.length, 1, testCase.phoneNumber);
|
||||
assert.equal(messages[0].payload.phoneNumber, testCase.phoneNumber);
|
||||
assert.equal(messages[0].payload.countryId, testCase.countryId, testCase.phoneNumber);
|
||||
assert.equal(messages[0].payload.countryLabel, testCase.countryLabel, testCase.phoneNumber);
|
||||
}
|
||||
});
|
||||
|
||||
test('phone verification helper accepts HeroSMS WAIT_RETRY as free-reuse ready without using its suffix as code', async () => {
|
||||
const requests = [];
|
||||
const events = [];
|
||||
|
||||
Reference in New Issue
Block a user