新增手机号绑定邮箱后邮箱模式重登流程

This commit is contained in:
QLHazyCoder
2026-05-16 03:23:28 +08:00
parent 727d1523dd
commit efad1f94d7
16 changed files with 715 additions and 35 deletions
+115 -6
View File
@@ -73,6 +73,12 @@ const NORMAL_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.(
plusModeEnabled: false,
signupMethod: 'phone',
}) || NORMAL_STEP_DEFINITIONS;
const NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: false,
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
}) || NORMAL_PHONE_STEP_DEFINITIONS;
const PLUS_PAYPAL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: true,
@@ -84,6 +90,13 @@ const PLUS_PAYPAL_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSte
plusPaymentMethod: 'paypal',
signupMethod: 'phone',
}) || PLUS_PAYPAL_STEP_DEFINITIONS;
const PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: true,
plusPaymentMethod: 'paypal',
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
}) || PLUS_PAYPAL_PHONE_STEP_DEFINITIONS;
const PLUS_GOPAY_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: true,
@@ -95,6 +108,13 @@ const PLUS_GOPAY_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getStep
plusPaymentMethod: 'gopay',
signupMethod: 'phone',
}) || PLUS_GOPAY_STEP_DEFINITIONS;
const PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: true,
plusPaymentMethod: 'gopay',
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
}) || PLUS_GOPAY_PHONE_STEP_DEFINITIONS;
const PLUS_GPC_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: true,
@@ -106,18 +126,29 @@ const PLUS_GPC_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?
plusPaymentMethod: 'gpc-helper',
signupMethod: 'phone',
}) || PLUS_GPC_STEP_DEFINITIONS;
const PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
plusModeEnabled: true,
plusPaymentMethod: 'gpc-helper',
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
}) || PLUS_GPC_PHONE_STEP_DEFINITIONS;
const PLUS_STEP_DEFINITIONS = PLUS_PAYPAL_STEP_DEFINITIONS;
const ALL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getAllSteps?.({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
}) || [
...NORMAL_STEP_DEFINITIONS,
...NORMAL_PHONE_STEP_DEFINITIONS,
...NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_PAYPAL_STEP_DEFINITIONS,
...PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
...PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_GOPAY_STEP_DEFINITIONS,
...PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
...PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_GPC_STEP_DEFINITIONS,
...PLUS_GPC_PHONE_STEP_DEFINITIONS,
...PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
];
const STEP_IDS = Array.from(new Set(ALL_STEP_DEFINITIONS
.map((definition) => Number(definition?.id))
@@ -644,6 +675,7 @@ function getStepDefinitionsForState(state = {}) {
plusModeEnabled: isPlusModeState(state),
plusPaymentMethod: normalizePlusPaymentMethod(state?.plusPaymentMethod),
signupMethod: getSignupMethodForStepDefinitions(state),
phoneSignupReloginAfterBindEmailEnabled: Boolean(state?.phoneSignupReloginAfterBindEmailEnabled),
});
if (Array.isArray(definitions)) {
return definitions;
@@ -908,6 +940,7 @@ const PERSISTED_SETTING_DEFAULTS = {
autoStepDelaySeconds: null,
step6CookieCleanupEnabled: false,
phoneVerificationEnabled: false,
phoneSignupReloginAfterBindEmailEnabled: false,
phoneSmsReuseEnabled: DEFAULT_HERO_SMS_REUSE_ENABLED,
freePhoneReuseEnabled: true,
freePhoneReuseAutoEnabled: true,
@@ -2799,6 +2832,7 @@ function normalizePersistentSettingValue(key, value) {
return typeof value === 'boolean' ? value : true;
case 'step6CookieCleanupEnabled':
case 'phoneVerificationEnabled':
case 'phoneSignupReloginAfterBindEmailEnabled':
case 'phoneSmsReuseEnabled':
case 'freePhoneReuseEnabled':
case 'freePhoneReuseAutoEnabled':
@@ -8600,7 +8634,12 @@ function getDownstreamStateResets(step, state = {}) {
currentPhoneVerificationCountdownWindowTotal: 0,
};
}
if (stepKey === 'oauth-login' || stepKey === 'fetch-login-code') {
if (
stepKey === 'oauth-login'
|| stepKey === 'fetch-login-code'
|| stepKey === 'relogin-bound-email'
|| stepKey === 'fetch-bound-email-login-code'
) {
return {
lastLoginCode: null,
loginVerificationRequestedAt: null,
@@ -9626,6 +9665,9 @@ const AUTO_RUN_BACKGROUND_COMPLETED_STEP_KEYS = new Set([
'post-login-phone-verification',
'bind-email',
'fetch-bind-email-code',
'relogin-bound-email',
'fetch-bound-email-login-code',
'post-bound-email-phone-verification',
'confirm-oauth',
]);
const STEP_COMPLETION_SIGNAL_STEP_KEYS = new Set([
@@ -10081,6 +10123,9 @@ const AUTH_CHAIN_NODE_IDS = new Set([
'post-login-phone-verification',
'bind-email',
'fetch-bind-email-code',
'relogin-bound-email',
'fetch-bound-email-login-code',
'post-bound-email-phone-verification',
'confirm-oauth',
'platform-verify',
]);
@@ -12394,6 +12439,39 @@ const step10Executor = self.MultiPageBackgroundStep10?.createStep10Executor({
DEFAULT_SUB2API_GROUP_NAME,
SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
});
function resolveBoundEmailForReloginState(state = {}) {
return String(
state?.step8VerificationTargetEmail
|| state?.email
|| state?.registrationEmailState?.current
|| ''
).trim();
}
async function executeReloginBoundEmail(state = {}) {
const visibleStep = Math.floor(Number(state?.visibleStep) || 0) || 10;
const boundEmail = resolveBoundEmailForReloginState(state);
if (!boundEmail) {
throw new Error(`步骤 ${visibleStep}:缺少绑定邮箱,无法在绑定邮箱后切入邮箱模式 OAuth 登录。`);
}
await addLog(`步骤 ${visibleStep}:绑定邮箱已提交,正在刷新 OAuth 并使用绑定邮箱 ${boundEmail} 登录...`, 'info', {
step: visibleStep,
stepKey: 'relogin-bound-email',
});
return step7Executor.executeStep7({
...state,
forceLoginIdentifierType: 'email',
forceEmailLogin: true,
signupMethod: 'email',
resolvedSignupMethod: 'email',
accountIdentifierType: 'email',
accountIdentifier: boundEmail,
email: boundEmail,
step8VerificationTargetEmail: boundEmail,
});
}
const stepExecutorsByKey = {
'open-chatgpt': () => step1Executor.executeStep1(),
'submit-signup-email': (state) => step2Executor.executeStep2(state),
@@ -12413,6 +12491,9 @@ const stepExecutorsByKey = {
'post-login-phone-verification': (state) => step8Executor.executePostLoginPhoneVerification(state),
'bind-email': (state) => step8Executor.executeBindEmail(state),
'fetch-bind-email-code': (state) => step8Executor.executeFetchBindEmailCode(state),
'relogin-bound-email': (state) => executeReloginBoundEmail(state),
'fetch-bound-email-login-code': (state) => step8Executor.executeBoundEmailLoginCode(state),
'post-bound-email-phone-verification': (state) => step8Executor.executeBoundEmailPostLoginPhoneVerification(state),
'confirm-oauth': (state) => step9Executor.executeStep9(state),
'platform-verify': (state) => executeStep10(state),
};
@@ -12590,12 +12671,16 @@ async function acquireTopLevelAuthChainExecution(step, state = {}) {
const normalStepRegistry = buildStepRegistry(NORMAL_STEP_DEFINITIONS);
const normalPhoneStepRegistry = buildStepRegistry(NORMAL_PHONE_STEP_DEFINITIONS);
const normalPhoneBoundEmailReloginStepRegistry = buildStepRegistry(NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
const plusPayPalStepRegistry = buildStepRegistry(PLUS_PAYPAL_STEP_DEFINITIONS);
const plusPayPalPhoneStepRegistry = buildStepRegistry(PLUS_PAYPAL_PHONE_STEP_DEFINITIONS);
const plusPayPalPhoneBoundEmailReloginStepRegistry = buildStepRegistry(PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
const plusGoPayStepRegistry = buildStepRegistry(PLUS_GOPAY_STEP_DEFINITIONS);
const plusGoPayPhoneStepRegistry = buildStepRegistry(PLUS_GOPAY_PHONE_STEP_DEFINITIONS);
const plusGoPayPhoneBoundEmailReloginStepRegistry = buildStepRegistry(PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
const plusGpcStepRegistry = buildStepRegistry(PLUS_GPC_STEP_DEFINITIONS);
const plusGpcPhoneStepRegistry = buildStepRegistry(PLUS_GPC_PHONE_STEP_DEFINITIONS);
const plusGpcPhoneBoundEmailReloginStepRegistry = buildStepRegistry(PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
function getStepRegistryForState(state = {}) {
const activeFlowId = String(state?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
@@ -12603,17 +12688,31 @@ function getStepRegistryForState(state = {}) {
throw new Error(`当前尚未注册 flow=${activeFlowId} 的步骤执行器。`);
}
const signupMethod = getSignupMethodForStepDefinitions(state);
const useBoundEmailRelogin = signupMethod === SIGNUP_METHOD_PHONE
&& Boolean(state?.phoneSignupReloginAfterBindEmailEnabled);
if (!isPlusModeState(state)) {
return signupMethod === SIGNUP_METHOD_PHONE ? normalPhoneStepRegistry : normalStepRegistry;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return useBoundEmailRelogin ? normalPhoneBoundEmailReloginStepRegistry : normalPhoneStepRegistry;
}
return normalStepRegistry;
}
const paymentMethod = normalizePlusPaymentMethod(state?.plusPaymentMethod);
if (paymentMethod === PLUS_PAYMENT_METHOD_GPC_HELPER) {
return signupMethod === SIGNUP_METHOD_PHONE ? plusGpcPhoneStepRegistry : plusGpcStepRegistry;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return useBoundEmailRelogin ? plusGpcPhoneBoundEmailReloginStepRegistry : plusGpcPhoneStepRegistry;
}
return plusGpcStepRegistry;
}
if (paymentMethod === PLUS_PAYMENT_METHOD_GOPAY) {
return signupMethod === SIGNUP_METHOD_PHONE ? plusGoPayPhoneStepRegistry : plusGoPayStepRegistry;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return useBoundEmailRelogin ? plusGoPayPhoneBoundEmailReloginStepRegistry : plusGoPayPhoneStepRegistry;
}
return plusGoPayStepRegistry;
}
return signupMethod === SIGNUP_METHOD_PHONE ? plusPayPalPhoneStepRegistry : plusPayPalStepRegistry;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return useBoundEmailRelogin ? plusPayPalPhoneBoundEmailReloginStepRegistry : plusPayPalPhoneStepRegistry;
}
return plusPayPalStepRegistry;
}
async function requestOAuthUrlFromPanel(state, options = {}) {
@@ -13043,12 +13142,22 @@ async function getPostStep6AutoRestartDecision(step, error) {
: (typeof LAST_STEP_ID === 'number' ? LAST_STEP_ID : 10);
const currentNodeKey = resolveStepKey(normalizedStep, latestState);
const confirmOauthStep = findStepIdByKeyForState('confirm-oauth', latestState);
const boundEmailReloginStep = findStepIdByKeyForState('relogin-bound-email', latestState);
const isBoundEmailReloginTailStep = [
'relogin-bound-email',
'fetch-bound-email-login-code',
'post-bound-email-phone-verification',
].includes(currentNodeKey);
const shouldRetryFromConfirmStep = currentNodeKey === 'platform-verify'
&& Number.isFinite(confirmOauthStep)
&& confirmOauthStep > 0
&& confirmOauthStep < normalizedStep
&& isPlatformVerifyTransientRetryError(errorMessage);
const restartAnchorStep = shouldRetryFromConfirmStep ? confirmOauthStep : authChainStartStep;
const restartAnchorStep = shouldRetryFromConfirmStep
? confirmOauthStep
: (isBoundEmailReloginTailStep && Number.isFinite(boundEmailReloginStep) && boundEmailReloginStep > 0
? boundEmailReloginStep
: authChainStartStep);
if (isPhoneSmsPlatformRateLimitFailure(errorMessage)) {
return {
shouldRestart: false,
+18 -6
View File
@@ -270,6 +270,8 @@
13: 'confirm-oauth',
14: 'platform-verify',
15: 'platform-verify',
16: 'confirm-oauth',
17: 'platform-verify',
});
function getStepKeyForState(step, state = {}) {
@@ -613,12 +615,18 @@
const stateForStep = await getState();
const stepKey = getStepKeyForState(step, stateForStep);
if (stepKey === 'oauth-login') {
await syncStepAccountIdentityFromPayload(payload);
if (stepKey === 'oauth-login' || stepKey === 'relogin-bound-email') {
if (stepKey === 'oauth-login') {
await syncStepAccountIdentityFromPayload(payload);
}
if (payload.skipLoginVerificationStep) {
await setState({ loginVerificationRequestedAt: null });
const latestState = await getState();
const loginCodeStep = findStepByKeyAfter(step, 'fetch-login-code', latestState);
const loginCodeStep = findStepByKeyAfter(
step,
stepKey === 'relogin-bound-email' ? 'fetch-bound-email-login-code' : 'fetch-login-code',
latestState
);
if (loginCodeStep) {
const currentStatus = getNodeStatusByStep(loginCodeStep, latestState);
if (!isStepProtectedFromAutoSkip(currentStatus)) {
@@ -635,7 +643,7 @@
return;
}
if (stepKey === 'fetch-login-code') {
if (stepKey === 'fetch-login-code' || stepKey === 'fetch-bound-email-login-code') {
await setState({
...(payload.phoneVerification || payload.loginPhoneVerification ? {
currentPhoneVerificationCode: '',
@@ -649,7 +657,7 @@
return;
}
if (stepKey === 'post-login-phone-verification') {
if (stepKey === 'post-login-phone-verification' || stepKey === 'post-bound-email-phone-verification') {
await setState({
currentPhoneVerificationCode: '',
signupPhoneVerificationRequestedAt: null,
@@ -1333,10 +1341,14 @@
const plusPaymentChanged = Object.prototype.hasOwnProperty.call(updates, 'plusPaymentMethod')
&& normalizePlusPaymentMethodForDisplay(currentState?.plusPaymentMethod || 'paypal')
!== normalizePlusPaymentMethodForDisplay(updates.plusPaymentMethod || 'paypal');
const phoneSignupReloginAfterBindEmailChanged = Object.prototype.hasOwnProperty.call(updates, 'phoneSignupReloginAfterBindEmailEnabled')
&& Boolean(currentState?.phoneSignupReloginAfterBindEmailEnabled) !== Boolean(updates.phoneSignupReloginAfterBindEmailEnabled);
const nextPlusModeEnabled = Object.prototype.hasOwnProperty.call(updates, 'plusModeEnabled')
? Boolean(updates.plusModeEnabled)
: Boolean(currentState?.plusModeEnabled);
const stepModeChanged = modeChanged || (nextPlusModeEnabled && plusPaymentChanged);
const stepModeChanged = modeChanged
|| (nextPlusModeEnabled && plusPaymentChanged)
|| phoneSignupReloginAfterBindEmailChanged;
const oauthFlowTimeoutDisabled = Object.prototype.hasOwnProperty.call(updates, 'oauthFlowTimeoutEnabled')
&& updates.oauthFlowTimeoutEnabled === false;
await setPersistentSettings(updates);
+105 -8
View File
@@ -124,6 +124,34 @@
return String(value || '').trim().toLowerCase();
}
function resolveBoundEmailLoginTarget(state = {}, visibleStep = 0) {
const email = String(
state?.step8VerificationTargetEmail
|| state?.email
|| state?.registrationEmailState?.current
|| ''
).trim();
if (!email) {
throw new Error(`步骤 ${visibleStep || 0}:缺少绑定邮箱,无法使用邮箱模式重新发起 OAuth 登录。`);
}
return email;
}
function buildBoundEmailLoginState(state = {}, visibleStep = 0) {
const email = resolveBoundEmailLoginTarget(state, visibleStep);
return {
...state,
forceLoginIdentifierType: 'email',
forceEmailLogin: true,
signupMethod: 'email',
resolvedSignupMethod: 'email',
accountIdentifierType: 'email',
accountIdentifier: email,
email,
step8VerificationTargetEmail: normalizeStep8VerificationTargetEmail(email),
};
}
async function getLoginAuthStateFromContent(visibleStep, options = {}) {
if (typeof sendToContentScriptResilient !== 'function') {
return {};
@@ -142,7 +170,7 @@
retryDelayMs: 600,
logMessage: options.logMessage || `步骤 ${visibleStep}:认证页正在切换,等待页面重新就绪...`,
logStep: visibleStep,
logStepKey: 'fetch-login-code',
logStepKey: options.logStepKey || activeFetchLoginCodeStepKey || 'fetch-login-code',
}
);
if (result?.error) {
@@ -240,9 +268,11 @@
loginVerificationRequestedAt: null,
});
const fromRecovery = Boolean(options.fromRecovery);
const stepKey = options.stepKey || activeFetchLoginCodeStepKey || 'fetch-login-code';
await addLog(
`步骤 ${visibleStep}:当前认证页已进入 OAuth 授权页${fromRecovery ? '(轮询失败后复核)' : ''},跳过登录验证码拉取并继续后续流程。`,
'warn'
'warn',
{ step: visibleStep, stepKey }
);
if (typeof completeNodeFromBackground === 'function') {
await completeNodeFromBackground(options.nodeId || 'fetch-login-code', {
@@ -258,9 +288,11 @@
step8VerificationTargetEmail: '',
loginVerificationRequestedAt: null,
});
const stepKey = options.stepKey || activeFetchLoginCodeStepKey || 'fetch-login-code';
await addLog(
`步骤 ${visibleStep}:当前认证页已进入手机号验证流程,跳过登录邮箱验证码,交给后续“手机号验证”步骤处理。`,
'warn'
'warn',
{ step: visibleStep, stepKey }
);
if (typeof completeNodeFromBackground === 'function') {
await completeNodeFromBackground(options.nodeId || 'fetch-login-code', {
@@ -416,9 +448,10 @@
}
async function completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, options = {}) {
const stepKey = options.stepKey || 'post-login-phone-verification';
await addLog(`步骤 ${visibleStep}:当前认证页已进入 OAuth 授权页,跳过手机号验证步骤。`, 'warn', {
step: visibleStep,
stepKey: 'post-login-phone-verification',
stepKey,
});
if (typeof completeNodeFromBackground === 'function') {
await completeNodeFromBackground(options.nodeId || 'post-login-phone-verification', {
@@ -428,18 +461,22 @@
}
}
async function executePostLoginPhoneVerification(state) {
async function executePostLoginPhoneVerification(state, runtime = {}) {
const visibleStep = getVisibleStep(state, 9);
activeFetchLoginCodeStep = visibleStep;
activeFetchLoginCodeStepKey = 'post-login-phone-verification';
activeFetchLoginCodeStepKey = runtime.stepKey || 'post-login-phone-verification';
const authTabId = await ensureAuthTabForPostLoginStep(state, visibleStep);
const pageState = await getLoginAuthStateFromContent(visibleStep, {
timeoutMs: await getStep8ReadyTimeoutMs('确认手机号验证页或 OAuth 授权页已就绪', state?.oauthUrl || '', visibleStep),
logMessage: `步骤 ${visibleStep}:正在确认是否需要手机号验证...`,
logStepKey: activeFetchLoginCodeStepKey,
});
if (pageState?.state === 'oauth_consent_page') {
await completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, { nodeId: state?.nodeId });
await completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, {
nodeId: state?.nodeId || runtime.fallbackNodeId,
stepKey: activeFetchLoginCodeStepKey,
});
return;
}
if (pageState?.state !== 'add_phone_page' && pageState?.state !== 'phone_verification_page') {
@@ -457,7 +494,7 @@
visibleStep,
});
if (typeof completeNodeFromBackground === 'function') {
await completeNodeFromBackground(state?.nodeId || 'post-login-phone-verification', {
await completeNodeFromBackground(state?.nodeId || runtime.fallbackNodeId || 'post-login-phone-verification', {
phoneVerification: true,
postLoginPhoneVerification: true,
code: result?.code || '',
@@ -663,6 +700,64 @@
});
}
async function executeBoundEmailLoginCode(state) {
const visibleStep = getVisibleStep(state, 11);
activeFetchLoginCodeStep = visibleStep;
activeFetchLoginCodeStepKey = 'fetch-bound-email-login-code';
const preparedState = buildBoundEmailLoginState(state, visibleStep);
const authTabId = await getTabId('signup-page');
if (authTabId) {
await chrome.tabs.update(authTabId, { active: true });
} else {
if (!preparedState.oauthUrl) {
throw new Error(`步骤 ${visibleStep}:缺少登录用 OAuth 链接,请先完成绑定邮箱后刷新 OAuth 并登录。`);
}
await reuseOrCreateTab('signup-page', preparedState.oauthUrl);
}
throwIfStopped();
const pageState = await ensureStep8VerificationPageReady({
visibleStep,
authLoginStep: Math.max(1, visibleStep - 1),
allowPhoneVerificationPage: true,
allowAddEmailPage: false,
timeoutMs: await getStep8ReadyTimeoutMs('确认绑定邮箱登录验证码页已就绪', preparedState?.oauthUrl || '', visibleStep),
});
if (pageState?.state === 'oauth_consent_page') {
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, {
nodeId: state?.nodeId || 'fetch-bound-email-login-code',
stepKey: 'fetch-bound-email-login-code',
});
return;
}
if (pageState?.state === 'add_phone_page' || pageState?.state === 'phone_verification_page') {
await completeStep8WhenDeferredToPostLoginPhone(visibleStep, pageState, {
nodeId: state?.nodeId || 'fetch-bound-email-login-code',
stepKey: 'fetch-bound-email-login-code',
});
return;
}
if (pageState?.state === 'add_email_page') {
throw new Error(`步骤 ${visibleStep}:绑定邮箱后邮箱模式登录不应再进入添加邮箱页。URL: ${pageState?.url || ''}`.trim());
}
if (pageState?.state !== 'verification_page') {
throw new Error(`步骤 ${visibleStep}:绑定邮箱后获取登录验证码只处理邮箱登录验证码页,当前状态:${pageState?.state || 'unknown'}。URL: ${pageState?.url || ''}`.trim());
}
return pollEmailVerificationCode(preparedState, pageState, visibleStep, {
stickyLastResendAt: Number(preparedState?.loginVerificationRequestedAt) || 0,
});
}
async function executeBoundEmailPostLoginPhoneVerification(state) {
return executePostLoginPhoneVerification(state, {
stepKey: 'post-bound-email-phone-verification',
fallbackNodeId: 'post-bound-email-phone-verification',
});
}
async function runStep8Attempt(state, runtime = {}) {
const visibleStep = getVisibleStep(state, 8);
activeFetchLoginCodeStep = visibleStep;
@@ -855,6 +950,8 @@
executePostLoginPhoneVerification,
executeBindEmail,
executeFetchBindEmailCode,
executeBoundEmailLoginCode,
executeBoundEmailPostLoginPhoneVerification,
};
}
+22 -1
View File
@@ -50,6 +50,11 @@
return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email';
}
function shouldForceStep7EmailLogin(state = {}) {
return normalizeStep7IdentifierType(state?.forceLoginIdentifierType) === 'email'
|| Boolean(state?.forceEmailLogin);
}
function canUseConfiguredPhoneSignup(state = {}) {
return normalizeStep7SignupMethod(state?.signupMethod) === 'phone'
&& Boolean(state?.phoneVerificationEnabled)
@@ -75,6 +80,10 @@
}
function resolveStep7LoginIdentifierType(state = {}, fallbackType = '') {
if (shouldForceStep7EmailLogin(state)) {
return 'email';
}
if (shouldPreferStep7PhoneSignupIdentity(state)) {
return 'phone';
}
@@ -234,7 +243,19 @@
throwIfStopped();
attempt += 1;
try {
const currentState = attempt === 1 ? state : await getState();
const rawCurrentState = attempt === 1 ? state : await getState();
const currentState = shouldForceStep7EmailLogin(state)
? {
...rawCurrentState,
forceLoginIdentifierType: 'email',
forceEmailLogin: true,
signupMethod: 'email',
resolvedSignupMethod: 'email',
accountIdentifierType: 'email',
accountIdentifier: email,
email,
}
: rawCurrentState;
const password = currentState.password || currentState.customPassword || '';
const currentIdentifierType = resolveStep7LoginIdentifierType(currentState, resolvedIdentifierType);
const currentPhoneNumber = currentIdentifierType === 'phone'
+2 -1
View File
@@ -91,7 +91,8 @@ function resolveCommandNodeId(message = {}) {
if (visibleStep === 8 || visibleStep === 11) return 'fetch-login-code';
if (visibleStep === 9 || visibleStep === 12) return 'post-login-phone-verification';
if (visibleStep === 10 || visibleStep === 13) return 'confirm-oauth';
if (visibleStep === 14 || visibleStep === 15) return 'platform-verify';
if (visibleStep === 16) return 'confirm-oauth';
if (visibleStep === 14 || visibleStep === 15 || visibleStep === 17) return 'platform-verify';
if (visibleStep === 7) return 'oauth-login';
if (visibleStep === 5) return 'fill-profile';
if (visibleStep === 3) return 'fill-password';
+2
View File
@@ -290,6 +290,8 @@ const DEFAULT_OPENAI_NODE_BY_STEP = Object.freeze({
13: 'confirm-oauth',
14: 'platform-verify',
15: 'platform-verify',
16: 'confirm-oauth',
17: 'platform-verify',
});
function resolveReportNodeId(stepOrNodeId, data = {}) {
+56 -7
View File
@@ -50,7 +50,11 @@
{ id: 7, order: 70, key: 'plus-checkout-billing', title: '等待 GPC 任务完成', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-billing' },
];
function createOpenAiAuthTail(startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL) {
function isPhoneSignupReloginAfterBindEmailEnabled(options = {}) {
return Boolean(options?.phoneSignupReloginAfterBindEmailEnabled);
}
function createOpenAiAuthTail(startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
const id = Number(startId) || 7;
const order = Number(startOrder) || id * 10;
const commonStart = [
@@ -59,6 +63,17 @@
];
if (signupMethod === SIGNUP_METHOD_PHONE) {
if (isPhoneSignupReloginAfterBindEmailEnabled(options)) {
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
{ id: id + 3, order: order + 30, key: 'relogin-bound-email', title: '绑定邮箱后刷新 OAuth 并登录(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'oauth-login' },
{ id: id + 4, order: order + 40, key: 'fetch-bound-email-login-code', title: '获取登录验证码(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-login-code' },
{ id: id + 5, order: order + 50, key: 'post-bound-email-phone-verification', title: '手机号验证', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'post-login-phone-verification' },
{ id: id + 6, order: order + 60, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 7, order: order + 70, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
}
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
@@ -76,21 +91,25 @@
];
}
function createOpenAiSteps(prefixSteps, startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL) {
function createOpenAiSteps(prefixSteps, startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
return [
...prefixSteps,
...createOpenAiAuthTail(startId, startOrder, signupMethod),
...createOpenAiAuthTail(startId, startOrder, signupMethod, options),
];
}
const NORMAL_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_EMAIL);
const NORMAL_PHONE_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_PHONE);
const NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_PAYPAL_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
const PLUS_PAYPAL_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
const PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_GOPAY_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
const PLUS_GOPAY_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
const PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_GPC_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
const PLUS_GPC_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
const PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PHONE_SIGNUP_TITLE_OVERRIDES = Object.freeze({
'submit-signup-email': '注册并输入手机号',
@@ -130,17 +149,39 @@
function getOpenAiModeStepDefinitions(options = {}) {
const signupMethod = getResolvedSignupMethod(options);
const reloginAfterBindEmail = signupMethod === SIGNUP_METHOD_PHONE
&& isPhoneSignupReloginAfterBindEmailEnabled(options);
if (!isPlusModeEnabled(options)) {
return signupMethod === SIGNUP_METHOD_PHONE ? NORMAL_PHONE_STEP_DEFINITIONS : NORMAL_STEP_DEFINITIONS;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: NORMAL_PHONE_STEP_DEFINITIONS;
}
return NORMAL_STEP_DEFINITIONS;
}
const paymentMethod = normalizePlusPaymentMethod(options?.plusPaymentMethod || options?.paymentMethod);
if (paymentMethod === PLUS_PAYMENT_METHOD_GPC_HELPER) {
return signupMethod === SIGNUP_METHOD_PHONE ? PLUS_GPC_PHONE_STEP_DEFINITIONS : PLUS_GPC_STEP_DEFINITIONS;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_GPC_PHONE_STEP_DEFINITIONS;
}
return PLUS_GPC_STEP_DEFINITIONS;
}
if (paymentMethod === PLUS_PAYMENT_METHOD_GOPAY) {
return signupMethod === SIGNUP_METHOD_PHONE ? PLUS_GOPAY_PHONE_STEP_DEFINITIONS : PLUS_GOPAY_STEP_DEFINITIONS;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_GOPAY_PHONE_STEP_DEFINITIONS;
}
return PLUS_GOPAY_STEP_DEFINITIONS;
}
return signupMethod === SIGNUP_METHOD_PHONE ? PLUS_PAYPAL_PHONE_STEP_DEFINITIONS : PLUS_PAYPAL_STEP_DEFINITIONS;
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_PAYPAL_PHONE_STEP_DEFINITIONS;
}
return PLUS_PAYPAL_STEP_DEFINITIONS;
}
function getOpenAiPlusPaymentStepTitle(options = {}) {
@@ -172,12 +213,16 @@
for (const step of [
...NORMAL_STEP_DEFINITIONS,
...NORMAL_PHONE_STEP_DEFINITIONS,
...NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_PAYPAL_STEP_DEFINITIONS,
...PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
...PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_GOPAY_STEP_DEFINITIONS,
...PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
...PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_GPC_STEP_DEFINITIONS,
...PLUS_GPC_PHONE_STEP_DEFINITIONS,
...PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
]) {
keyed.set(`${step.id}:${step.key}`, step);
}
@@ -352,13 +397,17 @@
STEP_DEFINITIONS: NORMAL_STEP_DEFINITIONS,
NORMAL_STEP_DEFINITIONS,
NORMAL_PHONE_STEP_DEFINITIONS,
NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_STEP_DEFINITIONS: PLUS_PAYPAL_STEP_DEFINITIONS,
PLUS_PAYPAL_STEP_DEFINITIONS,
PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_GOPAY_STEP_DEFINITIONS,
PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_GPC_STEP_DEFINITIONS,
PLUS_GPC_PHONE_STEP_DEFINITIONS,
PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
SIGNUP_METHOD_EMAIL,
SIGNUP_METHOD_PHONE,
getAllSteps,
+11
View File
@@ -1295,6 +1295,17 @@
<button type="button" class="choice-btn" data-signup-method="phone">手机号注册</button>
</div>
</div>
<div class="data-row" id="row-phone-signup-relogin-after-bind-email" style="display:none;">
<span class="data-label">绑定后重登</span>
<label class="toggle-switch" for="input-phone-signup-relogin-after-bind-email"
title="手机号注册绑定邮箱后,跳过绑定邮箱验证码,重新刷新 OAuth 并使用绑定邮箱登录">
<input type="checkbox" id="input-phone-signup-relogin-after-bind-email" />
<span class="toggle-switch-track" aria-hidden="true">
<span class="toggle-switch-thumb"></span>
</span>
</label>
<span class="data-value">绑定邮箱后切入邮箱注册模式 OAuth 尾部</span>
</div>
<div class="data-row" id="row-account-run-history-helper-base-url" style="display:none;">
<span class="data-label">同步服务</span>
<input type="text" id="input-account-run-history-helper-base-url" class="data-input mono"
+83
View File
@@ -387,6 +387,8 @@ const btnTogglePhoneVerificationSection = document.getElementById('btn-toggle-ph
const rowPhoneVerificationFold = document.getElementById('row-phone-verification-fold');
const inputPhoneVerificationEnabled = document.getElementById('input-phone-verification-enabled');
const rowSignupMethod = document.getElementById('row-signup-method');
const rowPhoneSignupReloginAfterBindEmail = document.getElementById('row-phone-signup-relogin-after-bind-email');
const inputPhoneSignupReloginAfterBindEmail = document.getElementById('input-phone-signup-relogin-after-bind-email');
const rowSignupPhone = document.getElementById('row-signup-phone');
const signupMethodButtons = Array.from(document.querySelectorAll('[data-signup-method]'));
const selectPhoneSmsProvider = document.getElementById('select-phone-sms-provider');
@@ -519,11 +521,13 @@ const SIGNUP_METHOD_EMAIL = 'email';
const SIGNUP_METHOD_PHONE = 'phone';
const DEFAULT_SIGNUP_METHOD = SIGNUP_METHOD_EMAIL;
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
const DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED = false;
const PHONE_SIGNUP_REUSE_LOCK_TITLE = '手机号注册流程不使用号码复用,切回邮箱注册后会恢复原设置';
let latestState = null;
let currentPlusModeEnabled = false;
let currentPlusPaymentMethod = DEFAULT_PLUS_PAYMENT_METHOD;
let currentSignupMethod = DEFAULT_SIGNUP_METHOD;
let currentPhoneSignupReloginAfterBindEmailEnabled = DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED;
let phoneSignupReuseUiWasLocked = false;
let lastConfirmedOperationDelayEnabled = true;
let heroSmsCountrySelectionOrder = [];
@@ -544,10 +548,12 @@ const nexSmsCountrySearchTextById = new Map();
let stepDefinitions = getStepDefinitionsForMode(false, {
plusPaymentMethod: currentPlusPaymentMethod,
signupMethod: currentSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
});
let workflowNodes = getWorkflowNodesForMode(false, {
plusPaymentMethod: currentPlusPaymentMethod,
signupMethod: currentSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
});
let STEP_IDS = stepDefinitions.map((step) => Number(step.id)).filter(Number.isFinite);
let STEP_DEFAULT_STATUSES = Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending']));
@@ -808,6 +814,9 @@ function getStepDefinitionsForMode(plusModeEnabled = false, options = {}) {
const rawSignupMethod = typeof options === 'string'
? currentSignupMethod
: (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string'
? currentPhoneSignupReloginAfterBindEmailEnabled
: Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled);
const activeFlowId = typeof options === 'string'
? ((typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId)
: (options.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId);
@@ -816,6 +825,7 @@ function getStepDefinitionsForMode(plusModeEnabled = false, options = {}) {
plusModeEnabled,
plusPaymentMethod: normalizePlusPaymentMethod(rawPaymentMethod),
signupMethod: normalizeSignupMethod(rawSignupMethod),
phoneSignupReloginAfterBindEmailEnabled,
}) || [])
.sort((left, right) => {
const leftOrder = Number.isFinite(left.order) ? left.order : left.id;
@@ -834,6 +844,9 @@ function getWorkflowNodesForMode(plusModeEnabled = false, options = {}) {
const rawSignupMethod = typeof options === 'string'
? currentSignupMethod
: (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string'
? currentPhoneSignupReloginAfterBindEmailEnabled
: Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled);
const activeFlowId = typeof options === 'string'
? ((typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId)
: (options.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId);
@@ -842,6 +855,7 @@ function getWorkflowNodesForMode(plusModeEnabled = false, options = {}) {
plusModeEnabled,
plusPaymentMethod: normalizePlusPaymentMethod(rawPaymentMethod),
signupMethod: normalizeSignupMethod(rawSignupMethod),
phoneSignupReloginAfterBindEmailEnabled,
});
if (Array.isArray(nodes) && nodes.length) {
return nodes.slice().sort((left, right) => {
@@ -902,18 +916,24 @@ function rebuildStepDefinitionState(plusModeEnabled = false, options = {}) {
const rawSignupMethod = typeof options === 'string'
? currentSignupMethod
: (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string'
? currentPhoneSignupReloginAfterBindEmailEnabled
: Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled);
currentPlusPaymentMethod = normalizePlusPaymentMethod(rawPaymentMethod);
currentSignupMethod = normalizeSignupMethod(rawSignupMethod);
currentPhoneSignupReloginAfterBindEmailEnabled = phoneSignupReloginAfterBindEmailEnabled;
stepDefinitions = getStepDefinitionsForMode(currentPlusModeEnabled, {
activeFlowId: options?.activeFlowId,
plusPaymentMethod: currentPlusPaymentMethod,
signupMethod: currentSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
});
const nextWorkflowNodes = typeof getWorkflowNodesForMode === 'function'
? getWorkflowNodesForMode(currentPlusModeEnabled, {
activeFlowId: options?.activeFlowId,
plusPaymentMethod: currentPlusPaymentMethod,
signupMethod: currentSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
})
: stepDefinitions.map((step) => ({
legacyStepId: Number(step.id),
@@ -3930,6 +3950,9 @@ function collectSettingsPayload() {
: true,
phoneVerificationEnabled: effectivePhoneVerificationEnabled,
signupMethod: effectiveSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail
? Boolean(inputPhoneSignupReloginAfterBindEmail.checked)
: false,
phoneSmsProvider: phoneSmsProviderValue,
phoneSmsProviderOrder: phoneSmsProviderOrderValue,
verificationResendCount: normalizeVerificationResendCount(
@@ -7842,6 +7865,9 @@ function updateSignupMethodUI(options = {}) {
syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, {
plusPaymentMethod: getSelectedPlusPaymentMethod(latestState),
signupMethod: selectedMethod,
phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail
? Boolean(inputPhoneSignupReloginAfterBindEmail.checked)
: currentPhoneSignupReloginAfterBindEmailEnabled,
});
if (typeof syncSignupPhoneInputFromState === 'function') {
syncSignupPhoneInputFromState(latestState);
@@ -7886,6 +7912,11 @@ function updatePhoneVerificationSettingsUI() {
: true;
const enabled = canShowPhoneSettings && rawEnabled;
const showSettings = enabled && phoneVerificationSectionExpanded;
const selectedSignupMethodForPhoneSettings = typeof getSelectedSignupMethod === 'function'
? getSelectedSignupMethod()
: normalizeSignupMethod(latestState?.signupMethod || DEFAULT_SIGNUP_METHOD);
const showPhoneSignupReloginAfterBindEmail = showSettings
&& selectedSignupMethodForPhoneSettings === SIGNUP_METHOD_PHONE;
const normalizeProvider = typeof normalizePhoneSmsProviderValue === 'function'
? normalizePhoneSmsProviderValue
: ((value = '') => {
@@ -7958,6 +7989,9 @@ function updatePhoneVerificationSettingsUI() {
row.style.display = showSettings ? '' : 'none';
}
});
if (typeof rowPhoneSignupReloginAfterBindEmail !== 'undefined' && rowPhoneSignupReloginAfterBindEmail) {
rowPhoneSignupReloginAfterBindEmail.style.display = showPhoneSignupReloginAfterBindEmail ? '' : 'none';
}
if (rowHeroSmsCountry) rowHeroSmsCountry.style.display = showSettings && heroProvider ? '' : 'none';
if (rowHeroSmsCountryFallback) rowHeroSmsCountryFallback.style.display = showSettings && heroProvider ? '' : 'none';
if (rowHeroSmsAcquirePriority) rowHeroSmsAcquirePriority.style.display = showSettings && heroProvider ? '' : 'none';
@@ -8003,6 +8037,12 @@ function updatePhoneVerificationSettingsUI() {
}
phoneSignupReuseUiWasLocked = phoneSignupReuseLocked;
const settingsLocked = isAutoRunLockedPhase() || isAutoRunScheduledPhase();
if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) {
inputPhoneSignupReloginAfterBindEmail.disabled = settingsLocked || !showPhoneSignupReloginAfterBindEmail;
}
if (typeof rowPhoneSignupReloginAfterBindEmail !== 'undefined' && rowPhoneSignupReloginAfterBindEmail) {
rowPhoneSignupReloginAfterBindEmail.classList.toggle('is-disabled', settingsLocked || !showPhoneSignupReloginAfterBindEmail);
}
const freePhoneReuseEnabled = Boolean(
!phoneSignupReuseLocked
&& typeof inputFreePhoneReuseEnabled !== 'undefined'
@@ -8958,6 +8998,12 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethodOr
? plusPaymentMethodOrOptions
: (options.plusPaymentMethod || getSelectedPlusPaymentMethod(latestState));
const nextSignupMethod = normalizeSignupMethod(options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
const nextPhoneSignupReloginAfterBindEmailEnabled = Boolean(
options.phoneSignupReloginAfterBindEmailEnabled
?? (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail
? inputPhoneSignupReloginAfterBindEmail.checked
: currentPhoneSignupReloginAfterBindEmailEnabled)
);
const nextPaymentMethod = normalizePlusPaymentMethod(rawPaymentMethod);
const nextActiveFlowId = String(
options.activeFlowId
@@ -8971,12 +9017,14 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethodOr
plusModeEnabled: nextPlusModeEnabled,
plusPaymentMethod: nextPaymentMethod,
signupMethod: nextSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: nextPhoneSignupReloginAfterBindEmailEnabled,
});
const paymentTitleChanged = Boolean(nextPlusModeEnabled && currentPaymentStep && nextPaymentTitle && currentPaymentStep.title !== nextPaymentTitle);
const shouldRender = Boolean(options.render)
|| nextPlusModeEnabled !== currentPlusModeEnabled
|| nextPaymentMethod !== currentPlusPaymentMethod
|| nextSignupMethod !== currentSignupMethod
|| nextPhoneSignupReloginAfterBindEmailEnabled !== currentPhoneSignupReloginAfterBindEmailEnabled
|| paymentTitleChanged;
if (!shouldRender) {
return;
@@ -8986,6 +9034,7 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethodOr
activeFlowId: nextActiveFlowId,
plusPaymentMethod: nextPaymentMethod,
signupMethod: nextSignupMethod,
phoneSignupReloginAfterBindEmailEnabled: nextPhoneSignupReloginAfterBindEmailEnabled,
});
renderStepsList();
}
@@ -9008,6 +9057,7 @@ function applySettingsState(state) {
activeFlowId: state?.flowId || state?.activeFlowId,
plusPaymentMethod: state?.plusPaymentMethod,
signupMethod: stepDefinitionState.signupMethod,
phoneSignupReloginAfterBindEmailEnabled: Boolean(state?.phoneSignupReloginAfterBindEmailEnabled),
});
}
const fallbackIpProxyService = '711proxy';
@@ -9359,6 +9409,11 @@ function applySettingsState(state) {
if (typeof setSignupMethod === 'function') {
setSignupMethod(state?.signupMethod || DEFAULT_SIGNUP_METHOD);
}
if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) {
inputPhoneSignupReloginAfterBindEmail.checked = state?.phoneSignupReloginAfterBindEmailEnabled !== undefined
? Boolean(state.phoneSignupReloginAfterBindEmailEnabled)
: DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED;
}
const restoredPhoneSmsProvider = normalizePhoneSmsProvider(state?.phoneSmsProvider);
const previousPhoneSmsProvider = selectPhoneSmsProvider ? normalizePhoneSmsProvider(selectPhoneSmsProvider.value) : restoredPhoneSmsProvider;
const defaultFiveSimProduct = typeof DEFAULT_FIVE_SIM_PRODUCT !== 'undefined'
@@ -14034,6 +14089,34 @@ signupMethodButtons.forEach((button) => {
});
});
inputPhoneSignupReloginAfterBindEmail?.addEventListener('change', () => {
const stepDefinitionState = typeof resolveStepDefinitionCapabilityState === 'function'
? resolveStepDefinitionCapabilityState({
...(latestState || {}),
plusModeEnabled: typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled
? Boolean(inputPlusModeEnabled.checked)
: Boolean(latestState?.plusModeEnabled),
signupMethod: getSelectedSignupMethod(),
phoneSignupReloginAfterBindEmailEnabled: Boolean(inputPhoneSignupReloginAfterBindEmail.checked),
}, {
signupMethod: getSelectedSignupMethod(),
})
: {
plusModeEnabled: typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled
? Boolean(inputPlusModeEnabled.checked)
: Boolean(latestState?.plusModeEnabled),
signupMethod: getSelectedSignupMethod(),
};
syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, {
plusPaymentMethod: getSelectedPlusPaymentMethod(latestState),
signupMethod: stepDefinitionState.signupMethod,
phoneSignupReloginAfterBindEmailEnabled: Boolean(inputPhoneSignupReloginAfterBindEmail.checked),
});
updatePhoneVerificationSettingsUI();
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
selectPhoneSmsProvider?.addEventListener('change', async () => {
if (selectPhoneSmsProvider) {
selectPhoneSmsProvider.value = normalizePhoneSmsProviderValue(selectPhoneSmsProvider.value);
@@ -199,6 +199,8 @@ return {
assert.equal(api.normalizePersistentSettingValue('accountRunHistoryTextEnabled', 1), true);
assert.equal(api.normalizePersistentSettingValue('phoneVerificationEnabled', 1), true);
assert.equal(api.normalizePersistentSettingValue('phoneSignupReloginAfterBindEmailEnabled', 1), true);
assert.equal(api.normalizePersistentSettingValue('phoneSignupReloginAfterBindEmailEnabled', 0), false);
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'gopay'), 'gopay');
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'gpc-helper'), 'gpc-helper');
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'paypal'), 'paypal');
@@ -181,6 +181,7 @@ return {
plusModeEnabled: true,
plusPaymentMethod: 'gopay',
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: false,
}]);
assert.equal(steps[0].title, '注册并输入手机号');
});
+16 -4
View File
@@ -13,25 +13,37 @@ test('background imports node registry and shared workflow definitions', () => {
assert.match(source, /PLUS_PAYPAL_STEP_DEFINITIONS/);
assert.match(source, /PLUS_GOPAY_STEP_DEFINITIONS/);
assert.match(source, /NORMAL_PHONE_STEP_DEFINITIONS/);
assert.match(source, /NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
assert.match(source, /PLUS_PAYPAL_PHONE_STEP_DEFINITIONS/);
assert.match(source, /PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
assert.match(source, /PLUS_GOPAY_PHONE_STEP_DEFINITIONS/);
assert.match(source, /PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
assert.match(source, /PLUS_GPC_PHONE_STEP_DEFINITIONS/);
assert.match(source, /PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
assert.match(source, /plusPayPalStepRegistry/);
assert.match(source, /plusGoPayStepRegistry/);
assert.match(source, /normalPhoneStepRegistry/);
assert.match(source, /normalPhoneBoundEmailReloginStepRegistry/);
assert.match(source, /plusPayPalPhoneStepRegistry/);
assert.match(source, /plusPayPalPhoneBoundEmailReloginStepRegistry/);
assert.match(source, /plusGoPayPhoneStepRegistry/);
assert.match(source, /plusGoPayPhoneBoundEmailReloginStepRegistry/);
assert.match(source, /plusGpcPhoneStepRegistry/);
assert.match(source, /plusGpcPhoneBoundEmailReloginStepRegistry/);
assert.match(source, /const signupMethod = getSignupMethodForStepDefinitions\(state\);/);
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? normalPhoneStepRegistry : normalStepRegistry/);
assert.match(source, /const useBoundEmailRelogin = signupMethod === SIGNUP_METHOD_PHONE/);
assert.match(source, /useBoundEmailRelogin \? normalPhoneBoundEmailReloginStepRegistry : normalPhoneStepRegistry/);
assert.match(source, /const paymentMethod = normalizePlusPaymentMethod\(state\?\.plusPaymentMethod\);/);
assert.match(source, /paymentMethod === PLUS_PAYMENT_METHOD_GOPAY/);
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? plusGoPayPhoneStepRegistry : plusGoPayStepRegistry/);
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? plusPayPalPhoneStepRegistry : plusPayPalStepRegistry/);
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? plusGpcPhoneStepRegistry : plusGpcStepRegistry/);
assert.match(source, /useBoundEmailRelogin \? plusGoPayPhoneBoundEmailReloginStepRegistry : plusGoPayPhoneStepRegistry/);
assert.match(source, /useBoundEmailRelogin \? plusPayPalPhoneBoundEmailReloginStepRegistry : plusPayPalPhoneStepRegistry/);
assert.match(source, /useBoundEmailRelogin \? plusGpcPhoneBoundEmailReloginStepRegistry : plusGpcPhoneStepRegistry/);
assert.match(source, /activeStepRegistry\.executeNode\(normalizedNodeId,\s*\{/);
assert.match(source, /'bind-email': \(state\) => step8Executor\.executeBindEmail\(state\)/);
assert.match(source, /'fetch-bind-email-code': \(state\) => step8Executor\.executeFetchBindEmailCode\(state\)/);
assert.match(source, /'relogin-bound-email': \(state\) => executeReloginBoundEmail\(state\)/);
assert.match(source, /'fetch-bound-email-login-code': \(state\) => step8Executor\.executeBoundEmailLoginCode\(state\)/);
assert.match(source, /'post-bound-email-phone-verification': \(state\) => step8Executor\.executeBoundEmailPostLoginPhoneVerification\(state\)/);
assert.match(source, /background\/steps\/create-plus-checkout\.js/);
assert.match(source, /background\/steps\/fill-plus-checkout\.js/);
assert.match(source, /background\/steps\/gopay-manual-confirm\.js/);
+224
View File
@@ -797,6 +797,146 @@ test('fetch-bind-email-code rejects unexpected pages after bind-email submitted'
]);
});
test('fetch-bound-email-login-code uses bound email identity and does not allow add-email', async () => {
const calls = {
ensureOptions: null,
resolveState: null,
resolveOptions: null,
setStates: [],
};
const realDateNow = Date.now;
Date.now = () => 333000;
const executor = api.createStep8Executor({
addLog: async () => {},
chrome: {
tabs: {
update: async () => {},
},
},
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
confirmCustomVerificationStepBypass: async () => {},
ensureStep8VerificationPageReady: async (options) => {
calls.ensureOptions = options;
return {
state: 'verification_page',
displayedEmail: 'bind.user@example.com',
url: 'https://auth.openai.com/email-verification',
};
},
getOAuthFlowRemainingMs: async () => 9000,
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getMailConfig: () => ({
provider: 'qq',
label: 'QQ 邮箱',
source: 'mail-qq',
url: 'https://mail.qq.com',
navigateOnReuse: false,
}),
getTabId: async () => 1,
HOTMAIL_PROVIDER: 'hotmail-api',
isTabAlive: async () => true,
LUCKMAIL_PROVIDER: 'luckmail-api',
resolveVerificationStep: async (_step, state, _mail, options) => {
calls.resolveState = state;
calls.resolveOptions = options;
},
reuseOrCreateTab: async () => 1,
setState: async (payload) => {
calls.setStates.push(payload);
},
shouldUseCustomRegistrationEmail: () => false,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
throwIfStopped: () => {},
});
try {
await executor.executeBoundEmailLoginCode({
visibleStep: 11,
nodeId: 'fetch-bound-email-login-code',
signupMethod: 'phone',
resolvedSignupMethod: 'phone',
accountIdentifierType: 'phone',
accountIdentifier: '+447780579093',
signupPhoneNumber: '+447780579093',
email: 'bind.user@example.com',
step8VerificationTargetEmail: 'bind.user@example.com',
oauthUrl: 'https://oauth.example/latest',
});
} finally {
Date.now = realDateNow;
}
assert.equal(calls.ensureOptions.allowAddEmailPage, false);
assert.equal(calls.ensureOptions.allowPhoneVerificationPage, true);
assert.equal(calls.resolveState.signupMethod, 'email');
assert.equal(calls.resolveState.resolvedSignupMethod, 'email');
assert.equal(calls.resolveState.accountIdentifierType, 'email');
assert.equal(calls.resolveState.accountIdentifier, 'bind.user@example.com');
assert.equal(calls.resolveOptions.completionStep, 11);
assert.equal(calls.resolveOptions.sessionKey, '11:333000');
assert.equal(calls.resolveOptions.targetEmail, 'bind.user@example.com');
assert.deepStrictEqual(calls.setStates, [
{
step8VerificationTargetEmail: 'bind.user@example.com',
},
]);
});
test('fetch-bound-email-login-code defers phone pages to bound-email phone verification step', async () => {
const completions = [];
const setStates = [];
const executor = api.createStep8Executor({
addLog: async () => {},
chrome: {
tabs: {
update: async () => {},
},
},
completeNodeFromBackground: async (step, payload) => {
completions.push({ step, payload });
},
ensureStep8VerificationPageReady: async () => ({
state: 'add_phone_page',
addPhonePage: true,
url: 'https://auth.openai.com/add-phone',
}),
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getTabId: async () => 1,
reuseOrCreateTab: async () => 1,
setState: async (payload) => {
setStates.push(payload);
},
throwIfStopped: () => {},
});
await executor.executeBoundEmailLoginCode({
visibleStep: 11,
nodeId: 'fetch-bound-email-login-code',
email: 'bind.user@example.com',
step8VerificationTargetEmail: 'bind.user@example.com',
oauthUrl: 'https://oauth.example/latest',
});
assert.deepStrictEqual(setStates, [
{
step8VerificationTargetEmail: '',
loginVerificationRequestedAt: null,
},
]);
assert.deepStrictEqual(completions, [
{
step: 'fetch-bound-email-login-code',
payload: {
loginVerificationRequestedAt: null,
skipLoginVerificationStep: true,
addPhonePage: true,
phoneVerificationPage: false,
},
},
]);
});
test('step 8 does not submit or recover add-email inside fetch-login-code', async () => {
const calls = {
ensureCalls: 0,
@@ -1189,6 +1329,90 @@ test('Plus login-code step reuses step 8 verification logic but completes visibl
assert.deepStrictEqual(remainingStepCalls, [11, 11]);
});
test('bound-email relogin code step points recovery to the relogin step in Plus mode', async () => {
let resolvedStep = null;
let resolvedOptions = null;
let readyOptions = null;
const remainingStepCalls = [];
const executor = api.createStep8Executor({
addLog: async () => {},
chrome: {
tabs: {
update: async () => {},
},
},
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
confirmCustomVerificationStepBypass: async () => {},
ensureStep8VerificationPageReady: async (options) => {
readyOptions = options;
return { state: 'verification_page', displayedEmail: 'bound.user@example.com' };
},
rerunStep7ForStep8Recovery: async () => {},
getOAuthFlowRemainingMs: async (details) => {
remainingStepCalls.push(details.step);
return 9000;
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs, details) => {
remainingStepCalls.push(details.step);
return Math.min(defaultTimeoutMs, 9000);
},
getMailConfig: () => ({
provider: 'qq',
label: 'QQ 邮箱',
source: 'mail-qq',
url: 'https://mail.qq.com',
navigateOnReuse: false,
}),
getState: async () => ({
email: 'bound.user@example.com',
password: 'secret',
plusModeEnabled: true,
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
}),
getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
HOTMAIL_PROVIDER: 'hotmail-api',
isTabAlive: async () => true,
isVerificationMailPollingError: () => false,
LUCKMAIL_PROVIDER: 'luckmail-api',
resolveVerificationStep: async (step, _state, _mail, options) => {
resolvedStep = step;
resolvedOptions = options;
await options.getRemainingTimeMs({ actionLabel: '绑定邮箱后登录验证码流程' });
},
reuseOrCreateTab: async () => {},
setState: async () => {},
shouldUseCustomRegistrationEmail: () => false,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 8,
throwIfStopped: () => {},
});
await executor.executeBoundEmailLoginCode({
visibleStep: 14,
plusModeEnabled: true,
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
email: 'bound.user@example.com',
password: 'secret',
oauthUrl: 'https://oauth.example/latest',
step8VerificationTargetEmail: 'bound.user@example.com',
});
assert.equal(resolvedStep, 8);
assert.equal(resolvedOptions.completionStep, 14);
assert.equal(resolvedOptions.targetEmail, 'bound.user@example.com');
assert.deepStrictEqual(readyOptions, {
visibleStep: 14,
authLoginStep: 13,
allowPhoneVerificationPage: true,
allowAddEmailPage: false,
timeoutMs: 9000,
});
assert.deepStrictEqual(remainingStepCalls, [14, 14]);
});
test('step 8 completes directly when auth page is already on OAuth consent page', async () => {
const events = {
resolveCalls: 0,
@@ -7,6 +7,7 @@ const {
} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
const sidepanelHtml = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
function extractFunction(name) {
const markers = [`async function ${name}(`, `function ${name}(`];
@@ -347,6 +348,14 @@ return {
assert.equal(api.shouldWarnCpaPhoneSignup('phone', 'cpa'), true);
});
test('phone signup relogin-after-bind-email switch is wired into UI and step definitions', () => {
assert.match(sidepanelHtml, /row-phone-signup-relogin-after-bind-email/);
assert.match(sidepanelHtml, /input-phone-signup-relogin-after-bind-email/);
assert.match(sidepanelSource, /phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined'/);
assert.match(sidepanelSource, /phoneSignupReloginAfterBindEmailEnabled: Boolean\(state\?\.phoneSignupReloginAfterBindEmailEnabled\)/);
assert.match(sidepanelSource, /nextPhoneSignupReloginAfterBindEmailEnabled !== currentPhoneSignupReloginAfterBindEmailEnabled/);
});
test('manual step 3 uses phone identity without requiring registration email', () => {
const api = new Function(`
let latestState = { signupMethod: 'phone', phoneVerificationEnabled: true, signupPhoneNumber: '+441111111111', accountIdentifierType: 'phone', accountIdentifier: '+441111111111' };
+4 -2
View File
@@ -83,6 +83,7 @@ const window = {
let currentPlusModeEnabled = false;
let currentPlusPaymentMethod = 'paypal';
let currentSignupMethod = 'email';
let currentPhoneSignupReloginAfterBindEmailEnabled = false;
const DEFAULT_SIGNUP_METHOD = 'email';
let stepDefinitions = [];
let STEP_IDS = [];
@@ -106,7 +107,7 @@ return {
assert.deepEqual(api.getStepIds(), [7]);
assert.deepEqual(api.calls[0], {
type: 'getSteps',
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gopay', signupMethod: 'email' },
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gopay', signupMethod: 'email', phoneSignupReloginAfterBindEmailEnabled: false },
});
assert.deepEqual(api.calls[1], { type: 'render', stepIds: [7] });
});
@@ -252,6 +253,7 @@ const window = {
let currentPlusModeEnabled = false;
let currentPlusPaymentMethod = 'paypal';
let currentSignupMethod = 'email';
let currentPhoneSignupReloginAfterBindEmailEnabled = false;
const DEFAULT_SIGNUP_METHOD = 'email';
let stepDefinitions = [];
let STEP_IDS = [];
@@ -275,7 +277,7 @@ return {
assert.deepEqual(api.getStepIds(), [13]);
assert.deepEqual(api.calls[0], {
type: 'getSteps',
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gpc-helper', signupMethod: 'email' },
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gpc-helper', signupMethod: 'email', phoneSignupReloginAfterBindEmailEnabled: false },
});
});
+45
View File
@@ -9,8 +9,17 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
const api = new Function('self', `${source}; return self.MultiPageStepDefinitions;`)(globalScope);
const steps = api.getSteps();
const phoneSteps = api.getSteps({ signupMethod: 'phone' });
const phoneReloginSteps = api.getSteps({
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
});
const plusSteps = api.getSteps({ plusModeEnabled: true });
const plusPhoneSteps = api.getSteps({ plusModeEnabled: true, signupMethod: 'phone' });
const plusPhoneReloginSteps = api.getSteps({
plusModeEnabled: true,
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: true,
});
const goPaySteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gopay' });
const gpcSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gpc-helper' });
@@ -58,6 +67,27 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
'platform-verify',
]
);
assert.deepStrictEqual(
phoneReloginSteps.map((step) => step.key),
[
'open-chatgpt',
'submit-signup-email',
'fill-password',
'fetch-signup-code',
'fill-profile',
'wait-registration-success',
'oauth-login',
'fetch-login-code',
'bind-email',
'relogin-bound-email',
'fetch-bound-email-login-code',
'post-bound-email-phone-verification',
'confirm-oauth',
'platform-verify',
]
);
assert.equal(phoneReloginSteps.find((step) => step.key === 'relogin-bound-email')?.title, '绑定邮箱后刷新 OAuth 并登录(邮箱)');
assert.equal(phoneReloginSteps.some((step) => step.key === 'fetch-bind-email-code'), false);
assert.deepStrictEqual(
plusSteps.map((step) => step.key),
@@ -103,6 +133,19 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
'platform-verify',
]
);
assert.deepStrictEqual(
plusPhoneReloginSteps.map((step) => step.key).slice(-8),
[
'oauth-login',
'fetch-login-code',
'bind-email',
'relogin-bound-email',
'fetch-bound-email-login-code',
'post-bound-email-phone-verification',
'confirm-oauth',
'platform-verify',
]
);
assert.equal(goPaySteps.some((step) => step.key === 'paypal-approve'), false);
assert.equal(api.getStepById(8, { plusModeEnabled: true, plusPaymentMethod: 'gopay' }), null);
assert.equal(api.getPlusPaymentStepTitle({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), '');
@@ -110,6 +153,8 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
assert.equal(api.getLastStepId({ plusModeEnabled: true }), 14);
assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, signupMethod: 'phone' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
assert.equal(api.getLastStepId({ plusModeEnabled: true, signupMethod: 'phone' }), 15);
assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);
assert.equal(api.getLastStepId({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true }), 17);
assert.equal(api.hasFlow('openai'), true);
assert.equal(api.hasFlow('site-a'), false);
assert.deepStrictEqual(api.getRegisteredFlowIds(), ['openai']);