feat: 添加邮箱验证页面处理逻辑,优化步骤 2 跳过密码步骤的逻辑,更新相关测试用例

This commit is contained in:
QLHazyCoder
2026-04-17 03:54:30 +08:00
parent 5a0003af3c
commit 1a5f569d16
8 changed files with 388 additions and 15 deletions
@@ -0,0 +1,127 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('background/message-router.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundMessageRouter;`)(globalScope);
function createRouter(overrides = {}) {
const events = {
logs: [],
stepStatuses: [],
emailStates: [],
};
const router = api.createMessageRouter({
addLog: async (message, level) => {
events.logs.push({ message, level });
},
appendAccountRunRecord: async () => null,
batchUpdateLuckmailPurchases: async () => {},
buildLocalhostCleanupPrefix: () => '',
buildLuckmailSessionSettingsPayload: () => ({}),
buildPersistentSettingsPayload: () => ({}),
broadcastDataUpdate: () => {},
cancelScheduledAutoRun: async () => {},
checkIcloudSession: async () => {},
clearAutoRunTimerAlarm: async () => {},
clearLuckmailRuntimeState: async () => {},
clearStopRequest: () => {},
closeLocalhostCallbackTabs: async () => {},
closeTabsByUrlPrefix: async () => {},
deleteHotmailAccount: async () => {},
deleteHotmailAccounts: async () => {},
deleteIcloudAlias: async () => {},
deleteUsedIcloudAliases: async () => {},
disableUsedLuckmailPurchases: async () => {},
doesStepUseCompletionSignal: () => false,
ensureManualInteractionAllowed: async () => ({}),
executeStep: async () => {},
executeStepViaCompletionSignal: async () => {},
exportSettingsBundle: async () => ({}),
fetchGeneratedEmail: async () => '',
finalizeIcloudAliasAfterSuccessfulFlow: async () => {},
findHotmailAccount: async () => null,
flushCommand: async () => {},
getCurrentLuckmailPurchase: () => null,
getPendingAutoRunTimerPlan: () => null,
getSourceLabel: () => '',
getState: async () => overrides.state || { stepStatuses: { 3: 'pending' } },
getStopRequested: () => false,
handleAutoRunLoopUnhandledError: async () => {},
importSettingsBundle: async () => {},
invalidateDownstreamAfterStepRestart: async () => {},
isAutoRunLockedState: () => false,
isHotmailProvider: () => false,
isLocalhostOAuthCallbackUrl: () => true,
isLuckmailProvider: () => false,
isStopError: () => false,
launchAutoRunTimerPlan: async () => {},
listIcloudAliases: async () => [],
listLuckmailPurchasesForManagement: async () => [],
normalizeHotmailAccounts: (items) => items,
normalizeRunCount: (value) => value,
AUTO_RUN_TIMER_KIND_SCHEDULED_START: 'scheduled',
notifyStepComplete: () => {},
notifyStepError: () => {},
patchHotmailAccount: async () => {},
registerTab: async () => {},
requestStop: async () => {},
resetState: async () => {},
resumeAutoRun: async () => {},
scheduleAutoRun: async () => {},
selectLuckmailPurchase: async () => {},
setCurrentHotmailAccount: async () => {},
setEmailState: async (email) => {
events.emailStates.push(email);
},
setEmailStateSilently: async () => {},
setIcloudAliasPreservedState: async () => {},
setIcloudAliasUsedState: async () => {},
setLuckmailPurchaseDisabledState: async () => {},
setLuckmailPurchasePreservedState: async () => {},
setLuckmailPurchaseUsedState: async () => {},
setPersistentSettings: async () => {},
setState: async () => {},
setStepStatus: async (step, status) => {
events.stepStatuses.push({ step, status });
},
skipAutoRunCountdown: async () => false,
skipStep: async () => {},
startAutoRunLoop: async () => {},
syncHotmailAccounts: async () => {},
testHotmailAccountMailAccess: async () => {},
upsertHotmailAccount: async () => {},
verifyHotmailAccount: async () => {},
});
return { router, events };
}
test('message router skips step 3 when step 2 lands on verification page', async () => {
const { router, events } = createRouter({
state: { stepStatuses: { 3: 'pending' } },
});
await router.handleStepData(2, {
email: 'user@example.com',
skippedPasswordStep: true,
});
assert.deepStrictEqual(events.emailStates, ['user@example.com']);
assert.deepStrictEqual(events.stepStatuses, [{ step: 3, status: 'skipped' }]);
assert.equal(events.logs[0]?.message, '步骤 2:提交邮箱后页面直接进入邮箱验证码页,已自动跳过步骤 3。');
});
test('message router does not overwrite a completed step 3 when step 2 is replayed', async () => {
const { router, events } = createRouter({
state: { stepStatuses: { 3: 'completed' } },
});
await router.handleStepData(2, {
skippedPasswordStep: true,
});
assert.deepStrictEqual(events.stepStatuses, []);
});
@@ -0,0 +1,134 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const step2Source = fs.readFileSync('background/steps/submit-signup-email.js', 'utf8');
const step2GlobalScope = {};
const step2Api = new Function('self', `${step2Source}; return self.MultiPageBackgroundStep2;`)(step2GlobalScope);
const signupFlowSource = fs.readFileSync('background/signup-flow-helpers.js', 'utf8');
const signupFlowGlobalScope = {};
const signupFlowApi = new Function('self', `${signupFlowSource}; return self.MultiPageSignupFlowHelpers;`)(signupFlowGlobalScope);
test('step 2 completes with password step skipped when landing on email verification page', async () => {
const completedPayloads = [];
const executor = step2Api.createStep2Executor({
addLog: async () => {},
chrome: { tabs: { update: async () => {} } },
completeStepFromBackground: async (step, payload) => {
completedPayloads.push({ step, payload });
},
ensureContentScriptReadyOnTab: async () => {},
ensureSignupEntryPageReady: async () => ({ tabId: 11 }),
ensureSignupPostEmailPageReadyInTab: async () => ({
state: 'verification_page',
url: 'https://auth.openai.com/email-verification',
}),
getTabId: async () => 11,
isTabAlive: async () => true,
resolveSignupEmailForFlow: async () => 'user@example.com',
sendToContentScriptResilient: async () => ({ submitted: true }),
SIGNUP_PAGE_INJECT_FILES: [],
});
await executor.executeStep2({ email: 'user@example.com' });
assert.deepStrictEqual(completedPayloads, [
{
step: 2,
payload: {
email: 'user@example.com',
nextSignupState: 'verification_page',
nextSignupUrl: 'https://auth.openai.com/email-verification',
skippedPasswordStep: true,
},
},
]);
});
test('step 2 keeps password flow when landing on password page', async () => {
const completedPayloads = [];
const executor = step2Api.createStep2Executor({
addLog: async () => {},
chrome: { tabs: { update: async () => {} } },
completeStepFromBackground: async (step, payload) => {
completedPayloads.push({ step, payload });
},
ensureContentScriptReadyOnTab: async () => {},
ensureSignupEntryPageReady: async () => ({ tabId: 12 }),
ensureSignupPostEmailPageReadyInTab: async () => ({
state: 'password_page',
url: 'https://auth.openai.com/create-account/password',
}),
getTabId: async () => 12,
isTabAlive: async () => true,
resolveSignupEmailForFlow: async () => 'user@example.com',
sendToContentScriptResilient: async () => ({ submitted: true }),
SIGNUP_PAGE_INJECT_FILES: [],
});
await executor.executeStep2({ email: 'user@example.com' });
assert.deepStrictEqual(completedPayloads, [
{
step: 2,
payload: {
email: 'user@example.com',
nextSignupState: 'password_page',
nextSignupUrl: 'https://auth.openai.com/create-account/password',
skippedPasswordStep: false,
},
},
]);
});
test('signup flow helper recognizes email verification page as post-email landing page', async () => {
let ensureCalls = 0;
let passwordReadyChecks = 0;
const helpers = signupFlowApi.createSignupFlowHelpers({
buildGeneratedAliasEmail: () => '',
chrome: {
tabs: {
get: async () => ({
id: 21,
url: 'https://auth.openai.com/email-verification',
}),
},
},
ensureContentScriptReadyOnTab: async () => {
ensureCalls += 1;
},
ensureHotmailAccountForFlow: async () => ({}),
ensureLuckmailPurchaseForFlow: async () => ({}),
isGeneratedAliasProvider: () => false,
isHotmailProvider: () => false,
isLuckmailProvider: () => false,
isSignupEmailVerificationPageUrl: (url) => /\/email-verification(?:[/?#]|$)/i.test(url || ''),
isSignupPasswordPageUrl: (url) => /\/create-account\/password(?:[/?#]|$)/i.test(url || ''),
reuseOrCreateTab: async () => 21,
sendToContentScriptResilient: async () => {
passwordReadyChecks += 1;
return {};
},
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
waitForTabUrlMatch: async () => ({
id: 21,
url: 'https://auth.openai.com/email-verification',
}),
});
const result = await helpers.ensureSignupPostEmailPageReadyInTab(21, 2);
assert.deepStrictEqual(result, {
ready: true,
state: 'verification_page',
url: 'https://auth.openai.com/email-verification',
});
assert.equal(ensureCalls, 1);
assert.equal(passwordReadyChecks, 0);
});