From 439268b4b73177c759d4cf177482cdb2e5321909 Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Thu, 16 Apr 2026 20:21:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20isSignupEntryHost?= =?UTF-8?q?=20=E5=87=BD=E6=95=B0=E4=BB=A5=E6=94=AF=E6=8C=81=20chatgpt.com?= =?UTF-8?q?=20=E5=92=8C=20chat.openai.com=20=E7=9A=84=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=AF=86=E5=88=AB=EF=BC=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 6 +- tests/cloudflare-temp-email-provider.test.js | 7 +- tests/signup-page-tab-cleanup.test.js | 206 +++++++++++++++++++ tests/step8-stop-cleanup.test.js | 4 + tests/step9-localhost-cleanup-scope.test.js | 1 + 5 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 tests/signup-page-tab-cleanup.test.js diff --git a/background.js b/background.js index 7b6f5a2..8e8906d 100644 --- a/background.js +++ b/background.js @@ -3210,6 +3210,10 @@ function isSignupPageHost(hostname = '') { return ['auth0.openai.com', 'auth.openai.com', 'accounts.openai.com'].includes(hostname); } +function isSignupEntryHost(hostname = '') { + return ['chatgpt.com', 'chat.openai.com'].includes(hostname); +} + function isSignupPasswordPageUrl(rawUrl) { const parsed = parseUrlSafely(rawUrl); if (!parsed) return false; @@ -3261,7 +3265,7 @@ function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) { switch (source) { case 'signup-page': - return isSignupPageHost(candidate.hostname); + return isSignupPageHost(candidate.hostname) || isSignupEntryHost(candidate.hostname); case 'duck-mail': return candidate.hostname === 'duckduckgo.com' && candidate.pathname.startsWith('/email/'); case 'qq-mail': diff --git a/tests/cloudflare-temp-email-provider.test.js b/tests/cloudflare-temp-email-provider.test.js index 4bbdc55..845c954 100644 --- a/tests/cloudflare-temp-email-provider.test.js +++ b/tests/cloudflare-temp-email-provider.test.js @@ -125,7 +125,12 @@ return { return { logs, listCalls }; }, }; -`)(options); +`)({ + ...options, + receiveMailbox, + messages, + deleteShouldFail, +}); } test('pollCloudflareTempEmailVerificationCode returns code even if delete fails', async () => { diff --git a/tests/signup-page-tab-cleanup.test.js b/tests/signup-page-tab-cleanup.test.js new file mode 100644 index 0000000..ebb05a3 --- /dev/null +++ b/tests/signup-page-tab-cleanup.test.js @@ -0,0 +1,206 @@ +const assert = require('assert'); +const fs = require('fs'); + +const source = fs.readFileSync('background.js', 'utf8'); + +function extractFunction(name) { + const markers = [`async function ${name}(`, `function ${name}(`]; + const start = markers + .map(marker => source.indexOf(marker)) + .find(index => index >= 0); + if (start < 0) { + throw new Error(`missing function ${name}`); + } + + let parenDepth = 0; + let signatureEnded = false; + let braceStart = -1; + for (let i = start; i < source.length; i += 1) { + const ch = source[i]; + if (ch === '(') { + parenDepth += 1; + } else if (ch === ')') { + parenDepth -= 1; + if (parenDepth === 0) { + signatureEnded = true; + } + } else if (ch === '{' && signatureEnded) { + braceStart = i; + break; + } + } + if (braceStart < 0) { + throw new Error(`missing body for function ${name}`); + } + + let depth = 0; + let end = braceStart; + for (; end < source.length; end += 1) { + const ch = source[end]; + if (ch === '{') depth += 1; + if (ch === '}') { + depth -= 1; + if (depth === 0) { + end += 1; + break; + } + } + } + + return source.slice(start, end); +} + +const bundle = [ + extractFunction('getTabRegistry'), + extractFunction('parseUrlSafely'), + extractFunction('isSignupPageHost'), + extractFunction('isSignupEntryHost'), + extractFunction('matchesSourceUrlFamily'), + extractFunction('closeConflictingTabsForSource'), +].join('\n'); + +const api = new Function(` +let currentState = { + sourceLastUrls: {}, + tabRegistry: {}, +}; +let currentTabs = []; +const removedBatches = []; +const logMessages = []; + +const chrome = { + tabs: { + async query() { + return currentTabs; + }, + async remove(ids) { + removedBatches.push(ids); + currentTabs = currentTabs.filter((tab) => !ids.includes(tab.id)); + }, + }, +}; + +async function getState() { + return currentState; +} + +async function setState(updates) { + currentState = { ...currentState, ...updates }; +} + +async function addLog(message, level = 'info') { + logMessages.push({ message, level }); +} + +function getSourceLabel(source) { + return source; +} + +${bundle} + +return { + matchesSourceUrlFamily, + closeConflictingTabsForSource, + reset({ tabs, state }) { + currentTabs = tabs; + removedBatches.length = 0; + logMessages.length = 0; + currentState = { + sourceLastUrls: {}, + tabRegistry: {}, + ...(state || {}), + }; + }, + snapshot() { + return { + currentState, + currentTabs, + removedBatches, + logMessages, + }; + }, +}; +`)(); + +(async () => { + assert.strictEqual( + api.matchesSourceUrlFamily('signup-page', 'https://chatgpt.com/', 'https://chatgpt.com/'), + true, + 'signup-page family should include chatgpt.com' + ); + assert.strictEqual( + api.matchesSourceUrlFamily('signup-page', 'https://chat.openai.com/', 'https://auth.openai.com/authorize'), + true, + 'signup-page family should include legacy chat.openai.com' + ); + + api.reset({ + tabs: [ + { id: 1, url: 'https://chatgpt.com/' }, + { id: 2, url: 'https://chat.openai.com/' }, + { id: 3, url: 'https://auth.openai.com/authorize?client_id=test' }, + { id: 4, url: 'https://example.com/' }, + ], + state: { + sourceLastUrls: { + 'signup-page': 'https://chatgpt.com/', + }, + tabRegistry: { + 'signup-page': { tabId: 3, ready: true }, + }, + }, + }); + + await api.closeConflictingTabsForSource('signup-page', 'https://auth.openai.com/authorize', { + excludeTabIds: [3], + }); + + let snapshot = api.snapshot(); + assert.deepStrictEqual( + snapshot.removedBatches, + [[1, 2]], + 'opening auth page should clean up stale ChatGPT entry tabs' + ); + assert.deepStrictEqual( + snapshot.currentTabs, + [ + { id: 3, url: 'https://auth.openai.com/authorize?client_id=test' }, + { id: 4, url: 'https://example.com/' }, + ], + 'non-signup tabs and excluded current tab should remain' + ); + + api.reset({ + tabs: [ + { id: 11, url: 'https://chatgpt.com/' }, + { id: 12, url: 'https://auth.openai.com/authorize?client_id=test' }, + ], + state: { + sourceLastUrls: { + 'signup-page': 'https://auth.openai.com/authorize?client_id=test', + }, + tabRegistry: { + 'signup-page': { tabId: 11, ready: true }, + }, + }, + }); + + await api.closeConflictingTabsForSource('signup-page', 'https://chatgpt.com/'); + + snapshot = api.snapshot(); + assert.deepStrictEqual( + snapshot.removedBatches, + [[11, 12]], + 'opening ChatGPT entry should remove older signup-family tabs' + ); + assert.strictEqual( + snapshot.currentState.tabRegistry['signup-page'], + null, + 'registry should be cleared when the tracked signup tab is removed' + ); + + console.log('signup page tab cleanup tests passed'); +})().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/tests/step8-stop-cleanup.test.js b/tests/step8-stop-cleanup.test.js index baa018c..7931295 100644 --- a/tests/step8-stop-cleanup.test.js +++ b/tests/step8-stop-cleanup.test.js @@ -70,6 +70,7 @@ let autoRunActive = true; let autoRunCurrentRun = 2; let autoRunTotalRuns = 3; let autoRunAttemptRun = 4; +const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start'; const added = { beforeNavigate: 0, @@ -128,6 +129,9 @@ async function broadcastAutoRunStatus() {} async function getState() { return { autoRunning: false }; } +function getPendingAutoRunTimerPlan() { + return null; +} function isAutoRunScheduledState() { return false; } diff --git a/tests/step9-localhost-cleanup-scope.test.js b/tests/step9-localhost-cleanup-scope.test.js index 97611f4..547a6ee 100644 --- a/tests/step9-localhost-cleanup-scope.test.js +++ b/tests/step9-localhost-cleanup-scope.test.js @@ -72,6 +72,7 @@ const api = new Function(` const HOTMAIL_PROVIDER = 'hotmail-api'; const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email'; +const GMAIL_PROVIDER = 'gmail'; const MAIL_2925_MODE_PROVIDE = 'provide'; const MAIL_2925_MODE_RECEIVE = 'receive'; const DEFAULT_MAIL_2925_MODE = MAIL_2925_MODE_PROVIDE;