Files
FlowPilot/tests/step8-restart-step7-error.test.js
T
QLHazyCoder d054ff65de feat: Enhance login flow with add-email and phone verification support
- Added support for the add-email page in the logging status module.
- Updated step 8 to handle phone-registered accounts during email verification.
- Implemented logic to submit add-email before polling for email verification codes.
- Enhanced tests for step 7 to detect username input as phone number and ignore hidden inputs.
- Improved step 8 to allow add-email handoff only when requested.
- Updated documentation to reflect changes in the login verification flow.
2026-05-05 01:47:58 +08:00

296 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const backgroundSource = fs.readFileSync('background.js', 'utf8');
const step8Source = fs.readFileSync('background/steps/fetch-login-code.js', 'utf8');
const step8GlobalScope = {};
const step8Api = new Function('self', `${step8Source}; return self.MultiPageBackgroundStep8;`)(step8GlobalScope);
function extractFunction(source, name) {
const markers = [`async function ${name}(`, `function ${name}(`];
const start = markers
.map((marker) => source.indexOf(marker))
.find((index) => index >= 0);
if (start < 0) {
throw new Error(`missing function ${name}`);
}
let parenDepth = 0;
let signatureEnded = false;
let braceStart = -1;
for (let i = start; i < source.length; i += 1) {
const ch = source[i];
if (ch === '(') {
parenDepth += 1;
} else if (ch === ')') {
parenDepth -= 1;
if (parenDepth === 0) {
signatureEnded = true;
}
} else if (ch === '{' && signatureEnded) {
braceStart = i;
break;
}
}
if (braceStart < 0) {
throw new Error(`missing body for function ${name}`);
}
let depth = 0;
let end = braceStart;
for (; end < source.length; end += 1) {
const ch = source[end];
if (ch === '{') depth += 1;
if (ch === '}') {
depth -= 1;
if (depth === 0) {
end += 1;
break;
}
}
}
return source.slice(start, end);
}
test('ensureStep8VerificationPageReady throws explicit restart-step7 error on login timeout page', async () => {
const api = new Function(`
function getLoginAuthStateLabel(state) {
return state === 'login_timeout_error_page' ? 'login timeout page' : 'unknown page';
}
async function getLoginAuthStateFromContent() {
return {
state: 'login_timeout_error_page',
url: 'https://auth.openai.com/log-in',
};
}
${extractFunction(backgroundSource, 'ensureStep8VerificationPageReady')}
return {
run() {
return ensureStep8VerificationPageReady({});
},
};
`)();
await assert.rejects(
() => api.run(),
/STEP8_RESTART_STEP7::/
);
});
test('ensureStep8VerificationPageReady throws cloudflare security block error on max_check_attempts page', async () => {
const api = new Function(`
const CLOUDFLARE_SECURITY_BLOCK_ERROR_PREFIX = 'CF_SECURITY_BLOCKED::';
const CLOUDFLARE_SECURITY_BLOCK_USER_MESSAGE = 'cloudflare blocked';
function getLoginAuthStateLabel(state) {
return state === 'login_timeout_error_page' ? 'login timeout page' : 'unknown page';
}
async function getLoginAuthStateFromContent() {
return {
state: 'login_timeout_error_page',
url: 'https://auth.openai.com/log-in',
maxCheckAttemptsBlocked: true,
};
}
${extractFunction(backgroundSource, 'ensureStep8VerificationPageReady')}
return {
run() {
return ensureStep8VerificationPageReady({});
},
};
`)();
await assert.rejects(
() => api.run(),
/CF_SECURITY_BLOCKED::/
);
});
test('ensureStep8VerificationPageReady allows add-email handoff only when requested', async () => {
const api = new Function(`
function getLoginAuthStateLabel(state) {
return state === 'add_email_page' ? '添加邮箱页' : 'unknown page';
}
async function getLoginAuthStateFromContent() {
return {
state: 'add_email_page',
url: 'https://auth.openai.com/add-email',
};
}
${extractFunction(backgroundSource, 'ensureStep8VerificationPageReady')}
return {
run(options) {
return ensureStep8VerificationPageReady(options || {});
},
};
`)();
await assert.rejects(
() => api.run({}),
/当前未进入登录验证码页面/
);
const result = await api.run({ allowAddEmailPage: true });
assert.equal(result.state, 'add_email_page');
});
test('step 8 reruns step 7 when auth page enters login timeout retry state', async () => {
const calls = {
rerunStep7: 0,
ensureReady: 0,
logs: [],
rerunOptions: [],
resolveCalls: 0,
};
const executor = step8Api.createStep8Executor({
addLog: async (message, level) => {
calls.logs.push({ message, level });
},
chrome: {
tabs: {
update: async () => {},
},
},
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
confirmCustomVerificationStepBypass: async () => {},
ensureStep8VerificationPageReady: async () => {
calls.ensureReady += 1;
if (calls.ensureReady === 1) {
throw new Error('STEP8_RESTART_STEP7::step 8 timeout retry page');
}
return { state: 'verification_page' };
},
rerunStep7ForStep8Recovery: async (options) => {
calls.rerunStep7 += 1;
calls.rerunOptions.push(options || null);
},
getOAuthFlowRemainingMs: async () => 8000,
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 8000),
getMailConfig: () => ({
provider: 'qq',
label: 'QQ mail',
source: 'mail-qq',
url: 'https://mail.qq.com',
navigateOnReuse: false,
}),
getState: async () => ({ email: 'user@example.com', password: 'secret', oauthUrl: 'https://oauth.example/latest' }),
getTabId: async () => 1,
HOTMAIL_PROVIDER: 'hotmail-api',
isTabAlive: async () => true,
isVerificationMailPollingError: () => false,
LUCKMAIL_PROVIDER: 'luckmail-api',
resolveVerificationStep: async () => {
calls.resolveCalls += 1;
},
reuseOrCreateTab: async () => {},
setState: async () => {},
setStepStatus: async () => {},
shouldUseCustomRegistrationEmail: () => false,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 3,
throwIfStopped: () => {},
});
await executor.executeStep8({
email: 'user@example.com',
password: 'secret',
oauthUrl: 'https://oauth.example/latest',
});
assert.equal(calls.rerunStep7, 1);
assert.equal(calls.ensureReady, 2);
assert.equal(calls.resolveCalls, 1);
assert.equal(calls.logs.some(({ message }) => /重新开始|重新发起/.test(message)), true);
assert.deepStrictEqual(calls.rerunOptions, [
{
logMessage: '认证页进入重试/超时报错状态,正在回到步骤 7 重新发起登录流程...',
logStep: 8,
logStepKey: 'fetch-login-code',
},
]);
});
test('step 8 escalates to rerun step 7 after too many local retry_without_step7 recoveries', async () => {
const calls = {
rerunStep7: 0,
ensureReady: 0,
logs: [],
};
const executor = step8Api.createStep8Executor({
addLog: async (message, level) => {
calls.logs.push({ message, level });
},
chrome: {
tabs: {
update: async () => {},
},
},
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
completeStepFromBackground: async () => {},
confirmCustomVerificationStepBypass: async () => {},
ensureStep8VerificationPageReady: async () => {
calls.ensureReady += 1;
return { state: 'verification_page' };
},
rerunStep7ForStep8Recovery: async () => {
calls.rerunStep7 += 1;
throw new Error('RERUN_MARKER');
},
getOAuthFlowRemainingMs: async () => 8000,
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 8000),
getMailConfig: () => ({
provider: 'qq',
label: 'QQ mail',
source: 'mail-qq',
url: 'https://mail.qq.com',
navigateOnReuse: false,
}),
getState: async () => ({ email: 'user@example.com', password: 'secret', oauthUrl: 'https://oauth.example/latest' }),
getTabId: async () => 1,
HOTMAIL_PROVIDER: 'hotmail-api',
isTabAlive: async () => true,
isVerificationMailPollingError: () => true,
LUCKMAIL_PROVIDER: 'luckmail-api',
resolveVerificationStep: async () => {
throw new Error('Content script on icloud-mail did not respond in 1s. Try refreshing the tab and retry.');
},
reuseOrCreateTab: async () => {},
setState: async () => {},
shouldUseCustomRegistrationEmail: () => false,
sleepWithStop: async () => {},
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 8,
throwIfStopped: () => {},
});
await assert.rejects(
() => executor.executeStep8({
email: 'user@example.com',
password: 'secret',
oauthUrl: 'https://oauth.example/latest',
}),
/RERUN_MARKER/
);
assert.equal(calls.rerunStep7, 1);
assert.equal(calls.ensureReady >= 4, true);
assert.equal(
calls.logs.some(({ message }) => /连续重试 \d+ 改为回到步骤 7/.test(message)),
true
);
});