修复手机号注册登录密码页与成功清理
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadAutoRunControllerApi() {
|
||||
const source = fs.readFileSync('background/auto-run-controller.js', 'utf8');
|
||||
const globalScope = {};
|
||||
return new Function('self', `${source}; return self.MultiPageBackgroundAutoRunController;`)(globalScope);
|
||||
}
|
||||
|
||||
const FULL_NODE_IDS = [
|
||||
'open-chatgpt',
|
||||
'submit-signup-email',
|
||||
'fill-password',
|
||||
'fetch-signup-code',
|
||||
'fill-profile',
|
||||
'wait-registration-success',
|
||||
'oauth-login',
|
||||
'fetch-login-code',
|
||||
'confirm-oauth',
|
||||
'platform-verify',
|
||||
];
|
||||
|
||||
const EMPTY_REGISTRATION_EMAIL_STATE = {
|
||||
current: '',
|
||||
previous: '',
|
||||
source: '',
|
||||
updatedAt: 0,
|
||||
};
|
||||
|
||||
const PHONE_NUMBER = '+6612345';
|
||||
const PHONE_ACTIVATION = {
|
||||
activationId: 'signup-completed',
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
};
|
||||
|
||||
function createNodeStatuses(doneNodeIds = []) {
|
||||
const doneSet = new Set(doneNodeIds);
|
||||
return Object.fromEntries(FULL_NODE_IDS.map((nodeId) => [
|
||||
nodeId,
|
||||
doneSet.has(nodeId) ? 'completed' : 'pending',
|
||||
]));
|
||||
}
|
||||
|
||||
function createBaseState() {
|
||||
return {
|
||||
activeFlowId: 'openai',
|
||||
flowId: 'openai',
|
||||
signupMethod: 'phone',
|
||||
resolvedSignupMethod: 'phone',
|
||||
autoRunFallbackThreadIntervalMinutes: 0,
|
||||
autoRunSkipFailures: false,
|
||||
nodeStatuses: createNodeStatuses([]),
|
||||
stepStatuses: {},
|
||||
};
|
||||
}
|
||||
|
||||
function createHarness({ completedNodeIds = [], initialState = {} } = {}) {
|
||||
const api = loadAutoRunControllerApi();
|
||||
let currentState = {
|
||||
...createBaseState(),
|
||||
...initialState,
|
||||
};
|
||||
let runCalls = 0;
|
||||
let sessionSeed = 1000;
|
||||
const clone = (value) => JSON.parse(JSON.stringify(value));
|
||||
|
||||
async function getState() {
|
||||
return clone(currentState);
|
||||
}
|
||||
|
||||
async function setState(updates = {}) {
|
||||
currentState = {
|
||||
...currentState,
|
||||
...updates,
|
||||
nodeStatuses: updates.nodeStatuses ? { ...updates.nodeStatuses } : currentState.nodeStatuses,
|
||||
stepStatuses: updates.stepStatuses ? { ...updates.stepStatuses } : currentState.stepStatuses,
|
||||
};
|
||||
}
|
||||
|
||||
async function resetState() {
|
||||
currentState = {
|
||||
activeFlowId: currentState.activeFlowId,
|
||||
flowId: currentState.flowId,
|
||||
signupMethod: currentState.signupMethod,
|
||||
resolvedSignupMethod: currentState.resolvedSignupMethod,
|
||||
autoRunFallbackThreadIntervalMinutes: currentState.autoRunFallbackThreadIntervalMinutes,
|
||||
autoRunSkipFailures: currentState.autoRunSkipFailures,
|
||||
nodeStatuses: createNodeStatuses([]),
|
||||
stepStatuses: {},
|
||||
currentPhoneActivation: currentState.currentPhoneActivation,
|
||||
phoneNumber: currentState.phoneNumber,
|
||||
accountIdentifierType: currentState.accountIdentifierType,
|
||||
accountIdentifier: currentState.accountIdentifier,
|
||||
signupPhoneNumber: currentState.signupPhoneNumber,
|
||||
signupPhoneActivation: currentState.signupPhoneActivation,
|
||||
signupPhoneCompletedActivation: currentState.signupPhoneCompletedActivation,
|
||||
signupPhoneVerificationRequestedAt: currentState.signupPhoneVerificationRequestedAt,
|
||||
signupPhoneVerificationPurpose: currentState.signupPhoneVerificationPurpose,
|
||||
currentPhoneVerificationCode: currentState.currentPhoneVerificationCode,
|
||||
currentPhoneVerificationCountdownEndsAt: currentState.currentPhoneVerificationCountdownEndsAt,
|
||||
currentPhoneVerificationCountdownWindowIndex: currentState.currentPhoneVerificationCountdownWindowIndex,
|
||||
currentPhoneVerificationCountdownWindowTotal: currentState.currentPhoneVerificationCountdownWindowTotal,
|
||||
email: currentState.email,
|
||||
registrationEmailState: currentState.registrationEmailState,
|
||||
step8VerificationTargetEmail: currentState.step8VerificationTargetEmail,
|
||||
lastEmailTimestamp: currentState.lastEmailTimestamp,
|
||||
lastSignupCode: currentState.lastSignupCode,
|
||||
lastLoginCode: currentState.lastLoginCode,
|
||||
bindEmailSubmitted: currentState.bindEmailSubmitted,
|
||||
};
|
||||
}
|
||||
|
||||
async function runAutoSequenceFromNode() {
|
||||
runCalls += 1;
|
||||
if (runCalls === 2) {
|
||||
assert.equal(currentState.email, null);
|
||||
assert.equal(currentState.currentPhoneActivation, null);
|
||||
assert.equal(currentState.phoneNumber, '');
|
||||
assert.equal(currentState.signupPhoneNumber, '');
|
||||
assert.equal(currentState.accountIdentifierType, null);
|
||||
assert.equal(currentState.accountIdentifier, '');
|
||||
}
|
||||
|
||||
await setState({
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: PHONE_NUMBER,
|
||||
currentPhoneActivation: PHONE_ACTIVATION,
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
signupPhoneNumber: PHONE_NUMBER,
|
||||
signupPhoneActivation: PHONE_ACTIVATION,
|
||||
signupPhoneCompletedActivation: PHONE_ACTIVATION,
|
||||
signupPhoneVerificationRequestedAt: 123,
|
||||
signupPhoneVerificationPurpose: 'signup',
|
||||
currentPhoneVerificationCode: '222222',
|
||||
currentPhoneVerificationCountdownEndsAt: Date.now() + 60000,
|
||||
currentPhoneVerificationCountdownWindowIndex: 1,
|
||||
currentPhoneVerificationCountdownWindowTotal: 2,
|
||||
email: 'bound.user@example.com',
|
||||
registrationEmailState: {
|
||||
current: 'bound.user@example.com',
|
||||
previous: 'old.bound@example.com',
|
||||
source: 'bind_email',
|
||||
updatedAt: 123,
|
||||
},
|
||||
step8VerificationTargetEmail: 'bound.user@example.com',
|
||||
lastEmailTimestamp: 456,
|
||||
lastSignupCode: '111111',
|
||||
lastLoginCode: '222222',
|
||||
bindEmailSubmitted: true,
|
||||
nodeStatuses: createNodeStatuses(completedNodeIds),
|
||||
});
|
||||
}
|
||||
|
||||
const runtime = {
|
||||
state: {},
|
||||
get() {
|
||||
return { ...this.state };
|
||||
},
|
||||
set(updates = {}) {
|
||||
this.state = { ...this.state, ...updates };
|
||||
},
|
||||
};
|
||||
|
||||
const controller = api.createAutoRunController({
|
||||
addLog: async () => {},
|
||||
AUTO_RUN_MAX_RETRIES_PER_ROUND: 3,
|
||||
AUTO_RUN_RETRY_DELAY_MS: 3000,
|
||||
AUTO_RUN_TIMER_KIND_BEFORE_RETRY: 'before_retry',
|
||||
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS: 'between_rounds',
|
||||
broadcastAutoRunStatus: async () => {},
|
||||
broadcastStopToContentScripts: async () => {},
|
||||
cancelPendingCommands: () => {},
|
||||
clearStopRequest: () => {},
|
||||
createAutoRunSessionId: () => {
|
||||
sessionSeed += 1;
|
||||
return sessionSeed;
|
||||
},
|
||||
getAutoRunStatusPayload: () => ({}),
|
||||
getErrorMessage: (error) => error?.message || String(error || ''),
|
||||
getFirstUnfinishedNodeId: () => 'open-chatgpt',
|
||||
getNodeIdsForState: () => FULL_NODE_IDS.slice(),
|
||||
getPendingAutoRunTimerPlan: () => null,
|
||||
getRunningNodeIds: () => [],
|
||||
getState,
|
||||
getStopRequested: () => false,
|
||||
hasSavedNodeProgress: () => false,
|
||||
isRestartCurrentAttemptError: () => false,
|
||||
isStopError: () => false,
|
||||
launchAutoRunTimerPlan: async () => false,
|
||||
normalizeAutoRunFallbackThreadIntervalMinutes: () => 0,
|
||||
persistAutoRunTimerPlan: async () => {},
|
||||
resetState,
|
||||
runAutoSequenceFromNode,
|
||||
runtime,
|
||||
setState,
|
||||
sleepWithStop: async () => {},
|
||||
throwIfAutoRunSessionStopped: () => {},
|
||||
waitForRunningNodesToFinish: getState,
|
||||
chrome: {
|
||||
runtime: {
|
||||
sendMessage: () => Promise.resolve(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
controller,
|
||||
getState: () => currentState,
|
||||
};
|
||||
}
|
||||
|
||||
test('auto-run clears phone signup and bound email runtime only after full workflow success', async () => {
|
||||
const { controller, getState } = createHarness({ completedNodeIds: FULL_NODE_IDS });
|
||||
|
||||
await controller.autoRunLoop(1, { mode: 'restart', autoRunSkipFailures: false });
|
||||
|
||||
const state = getState();
|
||||
assert.equal(state.email, null);
|
||||
assert.deepEqual(state.registrationEmailState, EMPTY_REGISTRATION_EMAIL_STATE);
|
||||
assert.equal(state.step8VerificationTargetEmail, '');
|
||||
assert.equal(state.lastEmailTimestamp, null);
|
||||
assert.equal(state.lastSignupCode, '');
|
||||
assert.equal(state.lastLoginCode, '');
|
||||
assert.equal(state.bindEmailSubmitted, false);
|
||||
assert.equal(state.currentPhoneActivation, null);
|
||||
assert.equal(state.currentPhoneVerificationCode, '');
|
||||
assert.equal(state.currentPhoneVerificationCountdownEndsAt, 0);
|
||||
assert.equal(state.currentPhoneVerificationCountdownWindowIndex, 0);
|
||||
assert.equal(state.currentPhoneVerificationCountdownWindowTotal, 0);
|
||||
assert.equal(state.accountIdentifierType, null);
|
||||
assert.equal(state.accountIdentifier, '');
|
||||
assert.equal(state.phoneNumber, '');
|
||||
assert.equal(state.signupPhoneNumber, '');
|
||||
assert.equal(state.signupPhoneActivation, null);
|
||||
assert.equal(state.signupPhoneCompletedActivation, null);
|
||||
});
|
||||
|
||||
test('auto-run keeps phone signup and bound email runtime when workflow is only partially done', async () => {
|
||||
const { controller, getState } = createHarness({
|
||||
completedNodeIds: ['open-chatgpt', 'submit-signup-email', 'fill-password'],
|
||||
});
|
||||
|
||||
await controller.autoRunLoop(1, { mode: 'restart', autoRunSkipFailures: false });
|
||||
|
||||
const state = getState();
|
||||
assert.equal(state.email, 'bound.user@example.com');
|
||||
assert.deepEqual(state.registrationEmailState, {
|
||||
current: 'bound.user@example.com',
|
||||
previous: 'old.bound@example.com',
|
||||
source: 'bind_email',
|
||||
updatedAt: 123,
|
||||
});
|
||||
assert.equal(state.step8VerificationTargetEmail, 'bound.user@example.com');
|
||||
assert.equal(state.lastEmailTimestamp, 456);
|
||||
assert.equal(state.lastSignupCode, '111111');
|
||||
assert.equal(state.lastLoginCode, '222222');
|
||||
assert.equal(state.bindEmailSubmitted, true);
|
||||
assert.deepEqual(state.currentPhoneActivation, PHONE_ACTIVATION);
|
||||
assert.equal(state.currentPhoneVerificationCode, '222222');
|
||||
assert.equal(state.accountIdentifierType, 'phone');
|
||||
assert.equal(state.accountIdentifier, PHONE_NUMBER);
|
||||
assert.equal(state.phoneNumber, PHONE_NUMBER);
|
||||
assert.equal(state.signupPhoneNumber, PHONE_NUMBER);
|
||||
assert.deepEqual(state.signupPhoneActivation, PHONE_ACTIVATION);
|
||||
assert.deepEqual(state.signupPhoneCompletedActivation, PHONE_ACTIVATION);
|
||||
});
|
||||
|
||||
test('auto-run cleanup prevents next phone signup run from reusing previous identity', async () => {
|
||||
const { controller, getState } = createHarness({ completedNodeIds: FULL_NODE_IDS });
|
||||
|
||||
await controller.autoRunLoop(2, { mode: 'restart', autoRunSkipFailures: false });
|
||||
|
||||
const state = getState();
|
||||
assert.equal(state.email, null);
|
||||
assert.equal(state.currentPhoneActivation, null);
|
||||
assert.equal(state.phoneNumber, '');
|
||||
assert.equal(state.signupPhoneNumber, '');
|
||||
assert.equal(state.accountIdentifierType, null);
|
||||
assert.equal(state.accountIdentifier, '');
|
||||
assert.equal(state.signupPhoneActivation, null);
|
||||
assert.equal(state.signupPhoneCompletedActivation, null);
|
||||
assert.equal(state.bindEmailSubmitted, false);
|
||||
});
|
||||
|
||||
test('auto-run cleanup uses frozen resolved signup method instead of stale setting', async () => {
|
||||
const emailRun = createHarness({
|
||||
completedNodeIds: FULL_NODE_IDS,
|
||||
initialState: {
|
||||
signupMethod: 'phone',
|
||||
resolvedSignupMethod: 'email',
|
||||
},
|
||||
});
|
||||
|
||||
await emailRun.controller.autoRunLoop(1, { mode: 'restart', autoRunSkipFailures: false });
|
||||
|
||||
assert.equal(emailRun.getState().email, 'bound.user@example.com');
|
||||
assert.equal(emailRun.getState().accountIdentifierType, 'phone');
|
||||
|
||||
const phoneRun = createHarness({
|
||||
completedNodeIds: FULL_NODE_IDS,
|
||||
initialState: {
|
||||
signupMethod: 'email',
|
||||
resolvedSignupMethod: 'phone',
|
||||
},
|
||||
});
|
||||
|
||||
await phoneRun.controller.autoRunLoop(1, { mode: 'restart', autoRunSkipFailures: false });
|
||||
|
||||
assert.equal(phoneRun.getState().email, null);
|
||||
assert.equal(phoneRun.getState().accountIdentifierType, null);
|
||||
assert.equal(phoneRun.getState().signupPhoneNumber, '');
|
||||
});
|
||||
@@ -87,6 +87,7 @@ function createRouter(overrides = {}) {
|
||||
}
|
||||
return next;
|
||||
};
|
||||
let currentState = normalizeState(overrides.state || { nodeStatuses: { 'fill-password': 'pending' } });
|
||||
|
||||
const router = api.createMessageRouter({
|
||||
addLog: async (message, level, options = {}) => {
|
||||
@@ -138,13 +139,13 @@ function createRouter(overrides = {}) {
|
||||
getCurrentLuckmailPurchase: () => null,
|
||||
getPendingAutoRunTimerPlan: () => null,
|
||||
getSourceLabel: () => '',
|
||||
getState: async () => normalizeState(overrides.state || { nodeStatuses: { 'fill-password': 'pending' } }),
|
||||
getState: async () => normalizeState(currentState),
|
||||
getNodeIdsForState: overrides.getNodeIdsForState || (() => ['open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'oauth-login', 'fetch-login-code', 'confirm-oauth', 'platform-verify']),
|
||||
getStepIdByNodeIdForState: overrides.getStepIdByNodeIdForState || ((nodeId, state = {}) => (stepByNode && Object.prototype.hasOwnProperty.call(stepByNode, nodeId))
|
||||
? stepByNode[nodeId] || 0
|
||||
: (state.plusModeEnabled ? plusStepByNode : normalStepByNode)[nodeId] || 0),
|
||||
getStepDefinitionForState: overrides.getStepDefinitionForState,
|
||||
getStepIdsForState: overrides.getStepIdsForState,
|
||||
getStepIdsForState: overrides.getStepIdsForState || (() => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
|
||||
getLastStepIdForState: overrides.getLastStepIdForState,
|
||||
getTabId: overrides.getTabId || (async () => null),
|
||||
getStopRequested: () => false,
|
||||
@@ -204,11 +205,24 @@ function createRouter(overrides = {}) {
|
||||
setPersistentSettings: async () => {},
|
||||
setState: async (updates) => {
|
||||
events.stateUpdates.push(updates);
|
||||
currentState = normalizeState({
|
||||
...currentState,
|
||||
...updates,
|
||||
nodeStatuses: updates.nodeStatuses ? { ...updates.nodeStatuses } : currentState.nodeStatuses,
|
||||
stepStatuses: updates.stepStatuses ? { ...updates.stepStatuses } : currentState.stepStatuses,
|
||||
});
|
||||
},
|
||||
setNodeStatus: async (nodeId, status) => {
|
||||
events.nodeStatuses.push({ nodeId, status });
|
||||
const step = getStepForNode(nodeId);
|
||||
events.stepStatuses.push({ step, status });
|
||||
currentState = normalizeState({
|
||||
...currentState,
|
||||
nodeStatuses: {
|
||||
...(currentState.nodeStatuses || {}),
|
||||
[nodeId]: status,
|
||||
},
|
||||
});
|
||||
},
|
||||
skipAutoRunCountdown: async () => false,
|
||||
skipNode: async () => {},
|
||||
@@ -223,7 +237,7 @@ function createRouter(overrides = {}) {
|
||||
}),
|
||||
});
|
||||
|
||||
return { router, events };
|
||||
return { router, events, getState: () => normalizeState(currentState) };
|
||||
}
|
||||
|
||||
test('message router skips step 3 when step 2 lands on verification page', async () => {
|
||||
@@ -574,6 +588,159 @@ test('message router marks step 3 failed when post-submit finalize fails', async
|
||||
assert.deepStrictEqual(response, { ok: true, error: '步骤 3 提交后仍停留在密码页。' });
|
||||
});
|
||||
|
||||
test('message router skips signup tail after finalize confirms phone login password page advanced', async () => {
|
||||
const finalizeResult = { ready: true, state: 'phone_verification_page' };
|
||||
const { router, events } = createRouter({
|
||||
state: {
|
||||
signupMethod: 'phone',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
signupPhoneNumber: '+66959916439',
|
||||
nodeStatuses: {
|
||||
'fill-password': 'running',
|
||||
'fetch-signup-code': 'pending',
|
||||
'fill-profile': 'pending',
|
||||
'wait-registration-success': 'pending',
|
||||
},
|
||||
},
|
||||
finalizeStep3Completion: async (payload) => {
|
||||
events.finalizePayloads.push(payload);
|
||||
return finalizeResult;
|
||||
},
|
||||
});
|
||||
|
||||
const response = await router.handleMessage({
|
||||
type: 'NODE_COMPLETE',
|
||||
nodeId: 'fill-password',
|
||||
source: 'openai-auth',
|
||||
payload: {
|
||||
nodeId: 'fill-password',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
signupPhoneNumber: '+66959916439',
|
||||
signupVerificationRequestedAt: 123456,
|
||||
deferredSubmit: true,
|
||||
passwordPageUrl: 'https://auth.openai.com/log-in/password',
|
||||
passwordPagePath: '/log-in/password',
|
||||
passwordPageMode: 'login',
|
||||
},
|
||||
}, {});
|
||||
|
||||
assert.deepStrictEqual(response, { ok: true });
|
||||
assert.deepStrictEqual(events.finalizePayloads.map((payload) => payload.passwordPageMode), ['login']);
|
||||
assert.deepStrictEqual(events.stepStatuses, [
|
||||
{ step: 3, status: 'completed' },
|
||||
{ step: 4, status: 'skipped' },
|
||||
{ step: 5, status: 'skipped' },
|
||||
{ step: 6, status: 'skipped' },
|
||||
]);
|
||||
assert.equal(events.stateUpdates.some((updates) => updates.signupVerificationRequestedAt === 123456), false);
|
||||
assert.equal(events.stateUpdates.some((updates) => updates.signupVerificationRequestedAt === null), true);
|
||||
assert.equal(events.logs.some(({ message }) => /手机号密码提交后已确认账号进入登录后续状态/.test(message)), true);
|
||||
assert.equal(events.notifyCompletions[0].payload.passwordLoginFlow, true);
|
||||
assert.equal(events.notifyCompletions[0].payload.skipRegistrationFlow, true);
|
||||
assert.equal(events.notifyCompletions[0].payload.signupVerificationRequestedAt, null);
|
||||
assert.equal(events.notifyCompletions[0].payload.state, 'phone_verification_page');
|
||||
});
|
||||
|
||||
test('message router keeps signup tail when phone password submit used create-account page', async () => {
|
||||
const finalizeResult = { ready: true, state: 'verification' };
|
||||
const { router, events } = createRouter({
|
||||
state: {
|
||||
signupMethod: 'phone',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
signupPhoneNumber: '+66959916439',
|
||||
nodeStatuses: {
|
||||
'fill-password': 'running',
|
||||
'fetch-signup-code': 'pending',
|
||||
'fill-profile': 'pending',
|
||||
'wait-registration-success': 'pending',
|
||||
},
|
||||
},
|
||||
finalizeStep3Completion: async (payload) => {
|
||||
events.finalizePayloads.push(payload);
|
||||
return finalizeResult;
|
||||
},
|
||||
});
|
||||
|
||||
const response = await router.handleMessage({
|
||||
type: 'NODE_COMPLETE',
|
||||
nodeId: 'fill-password',
|
||||
source: 'openai-auth',
|
||||
payload: {
|
||||
nodeId: 'fill-password',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
signupPhoneNumber: '+66959916439',
|
||||
signupVerificationRequestedAt: 123456,
|
||||
deferredSubmit: true,
|
||||
passwordPageUrl: 'https://auth.openai.com/create-account/password',
|
||||
passwordPagePath: '/create-account/password',
|
||||
passwordPageMode: 'signup',
|
||||
},
|
||||
}, {});
|
||||
|
||||
assert.deepStrictEqual(response, { ok: true });
|
||||
assert.deepStrictEqual(events.finalizePayloads.map((payload) => payload.passwordPageMode), ['signup']);
|
||||
assert.deepStrictEqual(events.stepStatuses, [
|
||||
{ step: 3, status: 'completed' },
|
||||
]);
|
||||
assert.equal(events.stateUpdates.some((updates) => updates.signupVerificationRequestedAt === 123456), true);
|
||||
assert.equal(events.stateUpdates.some((updates) => updates.signupVerificationRequestedAt === null), false);
|
||||
assert.equal(events.notifyCompletions[0].payload.state, 'verification');
|
||||
assert.equal(events.notifyCompletions[0].payload.skipRegistrationFlow, undefined);
|
||||
});
|
||||
|
||||
test('message router does not skip signup tail when login password finalize fails', async () => {
|
||||
const { router, events } = createRouter({
|
||||
state: {
|
||||
signupMethod: 'phone',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
signupPhoneNumber: '+66959916439',
|
||||
nodeStatuses: {
|
||||
'fill-password': 'running',
|
||||
'fetch-signup-code': 'pending',
|
||||
'fill-profile': 'pending',
|
||||
'wait-registration-success': 'pending',
|
||||
},
|
||||
},
|
||||
finalizeStep3Completion: async (payload) => {
|
||||
events.finalizePayloads.push(payload);
|
||||
throw new Error('步骤 3 收尾仍停留在登录密码页。');
|
||||
},
|
||||
});
|
||||
|
||||
const response = await router.handleMessage({
|
||||
type: 'NODE_COMPLETE',
|
||||
nodeId: 'fill-password',
|
||||
source: 'openai-auth',
|
||||
payload: {
|
||||
nodeId: 'fill-password',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
signupPhoneNumber: '+66959916439',
|
||||
signupVerificationRequestedAt: null,
|
||||
deferredSubmit: true,
|
||||
passwordPageUrl: 'https://auth.openai.com/log-in/password',
|
||||
passwordPagePath: '/log-in/password',
|
||||
passwordPageMode: 'login',
|
||||
passwordLoginFlow: true,
|
||||
},
|
||||
}, {});
|
||||
|
||||
assert.deepStrictEqual(response, { ok: true, error: '步骤 3 收尾仍停留在登录密码页。' });
|
||||
assert.deepStrictEqual(events.finalizePayloads.map((payload) => payload.passwordPageMode), ['login']);
|
||||
assert.deepStrictEqual(events.stepStatuses, [
|
||||
{ step: 3, status: 'failed' },
|
||||
]);
|
||||
assert.equal(events.stepStatuses.some(({ step, status }) => step === 4 && status === 'skipped'), false);
|
||||
assert.equal(events.stepStatuses.some(({ step, status }) => step === 5 && status === 'skipped'), false);
|
||||
assert.equal(events.stepStatuses.some(({ step, status }) => step === 6 && status === 'skipped'), false);
|
||||
assert.equal(events.notifyCompletions.length, 0);
|
||||
});
|
||||
|
||||
test('message router does not duplicate step 3 mismatch failure log after finalize already failed', async () => {
|
||||
const mismatchError = 'SIGNUP_PHONE_PASSWORD_MISMATCH::步骤 3:检测到注册手机号或密码不正确,需要重新开始当前轮。页面提示:Incorrect phone number or password';
|
||||
const state = {
|
||||
|
||||
@@ -211,6 +211,9 @@ return {
|
||||
assert.deepStrictEqual(result, beforeSubmit.completions[0].payload);
|
||||
assert.equal(result.email, 'user@example.com');
|
||||
assert.equal(result.deferredSubmit, true);
|
||||
assert.equal(result.passwordPageUrl, 'https://auth.openai.com/create-account/password');
|
||||
assert.equal(result.passwordPagePath, '/create-account/password');
|
||||
assert.equal(result.passwordPageMode, 'signup');
|
||||
assert.equal(typeof result.signupVerificationRequestedAt, 'number');
|
||||
assert.equal(beforeSubmit.events.includes('report:true'), true);
|
||||
assert.equal(beforeSubmit.events.includes('operation:submit-signup-password:start'), false);
|
||||
@@ -233,3 +236,71 @@ return {
|
||||
assert.deepStrictEqual(afterSubmit.clicks, ['Continue']);
|
||||
assert.equal(afterSubmit.events.includes('delay:submit-signup-password:2000'), true);
|
||||
});
|
||||
|
||||
test('step 3 marks login password page from log-in URL', async () => {
|
||||
const api = new Function(`
|
||||
const completions = [];
|
||||
const scheduled = [];
|
||||
const snapshot = {
|
||||
state: 'password_page',
|
||||
passwordInput: { value: '', hidden: false },
|
||||
submitButton: { textContent: 'Continue', hidden: false },
|
||||
displayedEmail: '',
|
||||
url: 'https://auth.openai.com/log-in/password',
|
||||
};
|
||||
const window = {
|
||||
setTimeout(fn, ms) {
|
||||
scheduled.push({ fn, ms });
|
||||
return scheduled.length;
|
||||
},
|
||||
CodexOperationDelay: {
|
||||
async performOperationWithDelay(metadata, operation) {
|
||||
return operation();
|
||||
},
|
||||
},
|
||||
};
|
||||
const location = {
|
||||
href: 'https://auth.openai.com/log-in/password',
|
||||
pathname: '/log-in/password',
|
||||
};
|
||||
function inspectSignupEntryState() { return snapshot; }
|
||||
function ensureSignupPasswordPageReady() { return { ready: true }; }
|
||||
function getSignupPasswordSubmitButton() { return snapshot.submitButton; }
|
||||
async function waitForElementByText() { return null; }
|
||||
function fillInput(input, value) { input.value = value; }
|
||||
async function humanPause() {}
|
||||
async function sleep() {}
|
||||
function throwIfStopped() {}
|
||||
function isStopError() { return false; }
|
||||
function log() {}
|
||||
function logSignupPasswordDiagnostics() {}
|
||||
function reportComplete(step, payload) { completions.push({ step, payload }); }
|
||||
function simulateClick() {}
|
||||
function getOperationDelayRunner() { return window.CodexOperationDelay.performOperationWithDelay; }
|
||||
|
||||
${extractFunction('step3_fillEmailPassword')}
|
||||
|
||||
return {
|
||||
run() {
|
||||
return step3_fillEmailPassword({
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+66959916439',
|
||||
phoneNumber: '+66959916439',
|
||||
password: 'Secret123!',
|
||||
});
|
||||
},
|
||||
completions,
|
||||
};
|
||||
`)();
|
||||
|
||||
const result = await api.run();
|
||||
|
||||
assert.equal(result.accountIdentifierType, 'phone');
|
||||
assert.equal(result.accountIdentifier, '+66959916439');
|
||||
assert.equal(result.passwordPageUrl, 'https://auth.openai.com/log-in/password');
|
||||
assert.equal(result.passwordPagePath, '/log-in/password');
|
||||
assert.equal(result.passwordPageMode, 'login');
|
||||
assert.equal(result.passwordLoginFlow, true);
|
||||
assert.equal(result.signupVerificationRequestedAt, null);
|
||||
assert.equal(api.completions[0].payload.passwordPageMode, 'login');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user