feat: Enhance Step 9 diagnostics and error handling

- Refactor getStatusBadgeEntries to use createStep9Entry for better encapsulation.
- Introduce error visual signals detection in createStep9Entry.
- Add functions to identify OAuth callback timeout failures and step 9 failure texts.
- Update buildStep9StatusDiagnostics to include error visual summaries and handle conflicts between success and failure states.
- Implement new tests for step 9 diagnostics, ensuring correct behavior with success badges and error banners.
- Remove outdated tests related to step 5 onboarding and redirect race conditions.
- Add new tests for auto-run behavior in step 6 restart scenarios.
This commit is contained in:
QLHazyCoder
2026-04-17 04:33:14 +08:00
parent 1a5f569d16
commit c745405f90
17 changed files with 772 additions and 691 deletions
+1 -1
View File
@@ -94,7 +94,7 @@
function isLegacyStep9RecoverableAuthError(error) {
const message = String(typeof error === 'string' ? error : error?.message || '');
return /STEP9_OAUTH_TIMEOUT::|认证失败:\s*Timeout waiting for OAuth callback/i.test(message);
return /STEP9_OAUTH_TIMEOUT::|认证失败:\s*(?:Timeout waiting for OAuth callback|timeout of \d+ms exceeded)/i.test(message);
}
function isStepDoneStatus(status) {
+6 -59
View File
@@ -6,74 +6,21 @@
addLog,
generateRandomBirthday,
generateRandomName,
getState,
getTabId,
handleChatgptOnboardingSkip,
isRetryableContentScriptTransportError,
LOG_PREFIX,
sendToContentScript,
waitForStep5ChatgptRedirect,
} = deps;
async function isStepAlreadyCompleted(step) {
if (typeof getState !== 'function') {
return false;
}
const latestState = await getState().catch(() => null);
return latestState?.stepStatuses?.[step] === 'completed';
}
async function executeStep5() {
const { firstName, lastName } = generateRandomName();
const { year, month, day } = generateRandomBirthday();
await addLog(`步骤 5:已生成姓名 ${firstName} ${lastName},生日 ${year}-${month}-${day}`);
let step5Result = null;
let step5TransportError = null;
try {
step5Result = await sendToContentScript('signup-page', {
type: 'EXECUTE_STEP',
step: 5,
source: 'background',
payload: { firstName, lastName, year, month, day },
});
} catch (err) {
if (isRetryableContentScriptTransportError(err)) {
step5TransportError = err;
console.log(LOG_PREFIX, '步骤 5:内容脚本通信中断,正在检查是否跳转到 ChatGPT 引导页...', err?.message);
} else {
throw err;
}
}
if (step5TransportError && await isStepAlreadyCompleted(5)) {
await addLog('步骤 5:提交后的页面跳转打断了响应,但已收到完成信号,直接结束当前步骤。', 'info');
return;
}
if (step5Result?.chatgptOnboarding) {
await addLog('步骤 5:检测到 ChatGPT 引导页跳转,正在处理引导页跳过...');
await handleChatgptOnboardingSkip();
return;
}
if (step5Result?.chatgptHome) {
await addLog('步骤 5:检测到已进入 ChatGPT 页面,注册成功。', 'ok');
return;
}
if (step5TransportError) {
const signupTabId = await getTabId('signup-page');
const redirectedTab = await waitForStep5ChatgptRedirect(signupTabId);
if (redirectedTab) {
await addLog('步骤 5:内容脚本因页面跳转到 ChatGPT 而断开,正在处理引导页跳过...');
await handleChatgptOnboardingSkip(redirectedTab.id);
return;
}
throw step5TransportError;
}
await sendToContentScript('signup-page', {
type: 'EXECUTE_STEP',
step: 5,
source: 'background',
payload: { firstName, lastName, year, month, day },
});
}
return { executeStep5 };
+24
View File
@@ -202,6 +202,29 @@
return null;
}
async function waitForTabComplete(tabId, options = {}) {
const { timeoutMs = 15000, retryDelayMs = 300 } = options;
const start = Date.now();
while (Date.now() - start < timeoutMs) {
try {
const tab = await chrome.tabs.get(tabId);
if (tab?.status === 'complete') {
return tab;
}
} catch {
return null;
}
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
}
try {
return await chrome.tabs.get(tabId);
} catch {
return null;
}
}
async function ensureContentScriptReadyOnTab(source, tabId, options = {}) {
const {
inject = null,
@@ -619,6 +642,7 @@
sendToContentScriptResilient,
sendToMailContentScriptResilient,
summarizeMessageResultForDebug,
waitForTabComplete,
waitForTabUrlFamily,
waitForTabUrlMatch,
};