diff --git a/background.js b/background.js index bddd1d3..ac3edd5 100644 --- a/background.js +++ b/background.js @@ -7067,10 +7067,7 @@ const plusCheckoutCreateExecutor = self.MultiPageBackgroundPlusCheckoutCreate?.c chrome, completeStepFromBackground, ensureContentScriptReadyOnTabUntilStopped, - getTabId, - isTabAlive, registerTab, - reuseOrCreateTab, sendTabMessageUntilStopped, setState, sleepWithStop, diff --git a/background/steps/create-plus-checkout.js b/background/steps/create-plus-checkout.js index 8686f95..cc319e4 100644 --- a/background/steps/create-plus-checkout.js +++ b/background/steps/create-plus-checkout.js @@ -2,21 +2,8 @@ root.MultiPageBackgroundPlusCheckoutCreate = factory(); })(typeof self !== 'undefined' ? self : globalThis, function createBackgroundPlusCheckoutCreateModule() { const PLUS_CHECKOUT_SOURCE = 'plus-checkout'; - const SIGNUP_PAGE_SOURCE = 'signup-page'; const PLUS_CHECKOUT_ENTRY_URL = 'https://chatgpt.com/'; const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/plus-checkout.js']; - const PLUS_CHECKOUT_ONLY_INJECT_FILES = ['content/plus-checkout.js']; - - function isReusableChatGptTabUrl(url = '') { - try { - const parsed = new URL(String(url || '')); - const hostname = parsed.hostname.toLowerCase(); - return ['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com'].includes(hostname) - && !/^\/checkout(?:\/|$)/i.test(parsed.pathname || ''); - } catch { - return false; - } - } function createPlusCheckoutCreateExecutor(deps = {}) { const { @@ -24,59 +11,33 @@ chrome, completeStepFromBackground, ensureContentScriptReadyOnTabUntilStopped, - getTabId, - isTabAlive, registerTab, - reuseOrCreateTab, sendTabMessageUntilStopped, setState, sleepWithStop, waitForTabCompleteUntilStopped, } = deps; - async function getReusableSignupChatGptTabId() { - if (typeof getTabId !== 'function' || typeof isTabAlive !== 'function') { - return null; + async function openFreshChatGptTabForCheckoutCreate() { + const tab = await chrome.tabs.create({ url: PLUS_CHECKOUT_ENTRY_URL, active: true }); + const tabId = Number(tab?.id); + if (!Number.isInteger(tabId)) { + throw new Error('步骤 6:打开 ChatGPT 页面失败,未获取到有效标签页 ID。'); } - const tabId = await getTabId(SIGNUP_PAGE_SOURCE); - if (!tabId || !(await isTabAlive(SIGNUP_PAGE_SOURCE))) { - return null; + if (typeof registerTab === 'function') { + await registerTab(PLUS_CHECKOUT_SOURCE, tabId); } - const tab = await chrome.tabs.get(tabId).catch(() => null); - return tab && isReusableChatGptTabUrl(tab.url) ? tabId : null; - } - - async function resolveChatGptTabForCheckoutCreate() { - const existingSignupTabId = await getReusableSignupChatGptTabId(); - if (existingSignupTabId) { - await chrome.tabs.update(existingSignupTabId, { active: true }); - if (typeof registerTab === 'function') { - await registerTab(PLUS_CHECKOUT_SOURCE, existingSignupTabId); - } - await addLog('步骤 6:检测到第 5 步已打开 ChatGPT 页面,直接接管当前标签页创建 Plus Checkout。', 'info'); - return { - tabId: existingSignupTabId, - injectFiles: PLUS_CHECKOUT_ONLY_INJECT_FILES, - }; - } - - const tabId = await reuseOrCreateTab(PLUS_CHECKOUT_SOURCE, PLUS_CHECKOUT_ENTRY_URL, { - reloadIfSameUrl: false, - }); - return { - tabId, - injectFiles: PLUS_CHECKOUT_INJECT_FILES, - }; + return tabId; } async function executePlusCheckoutCreate() { - await addLog('步骤 6:正在打开 ChatGPT 会话页,准备创建 Plus Checkout...', 'info'); - const { tabId, injectFiles } = await resolveChatGptTabForCheckoutCreate(); + await addLog('步骤 6:正在新打开 ChatGPT 会话页,准备创建 Plus Checkout...', 'info'); + const tabId = await openFreshChatGptTabForCheckoutCreate(); await waitForTabCompleteUntilStopped(tabId); await sleepWithStop(1000); await ensureContentScriptReadyOnTabUntilStopped(PLUS_CHECKOUT_SOURCE, tabId, { - inject: injectFiles, + inject: PLUS_CHECKOUT_INJECT_FILES, injectSource: PLUS_CHECKOUT_SOURCE, logMessage: '步骤 6:ChatGPT 页面仍在加载,等待 Plus Checkout 脚本就绪...', }); diff --git a/tests/plus-checkout-create-wait.test.js b/tests/plus-checkout-create-wait.test.js index 02325fa..6f606ac 100644 --- a/tests/plus-checkout-create-wait.test.js +++ b/tests/plus-checkout-create-wait.test.js @@ -14,6 +14,10 @@ test('Plus checkout create does not wait 20 seconds after opening checkout page' }, chrome: { tabs: { + create: async (payload) => { + events.push({ type: 'tab-create', payload }); + return { id: 42 }; + }, update: async (tabId, payload) => { events.push({ type: 'tab-update', tabId, payload }); }, @@ -25,9 +29,8 @@ test('Plus checkout create does not wait 20 seconds after opening checkout page' ensureContentScriptReadyOnTabUntilStopped: async () => { events.push({ type: 'ready' }); }, - reuseOrCreateTab: async (source, url, options) => { - events.push({ type: 'reuse-tab', source, url, options }); - return 42; + registerTab: async (source, tabId) => { + events.push({ type: 'register', source, tabId }); }, sendTabMessageUntilStopped: async () => ({ checkoutUrl: 'https://checkout.stripe.com/c/pay/session', @@ -47,10 +50,14 @@ test('Plus checkout create does not wait 20 seconds after opening checkout page' await executor.executePlusCheckoutCreate(); - const reuseEvent = events.find((event) => event.type === 'reuse-tab'); - assert.equal(reuseEvent.source, 'plus-checkout'); - assert.equal(reuseEvent.options.reloadIfSameUrl, false); - assert.equal(Object.hasOwn(reuseEvent.options, 'inject'), false); + assert.deepEqual( + events.find((event) => event.type === 'tab-create'), + { type: 'tab-create', payload: { url: 'https://chatgpt.com/', active: true } } + ); + assert.deepEqual( + events.find((event) => event.type === 'register'), + { type: 'register', source: 'plus-checkout', tabId: 42 } + ); const sleepEvents = events.filter((event) => event.type === 'sleep'); assert.deepStrictEqual(sleepEvents.map((event) => event.ms), [1000, 1000]); @@ -62,74 +69,3 @@ test('Plus checkout create does not wait 20 seconds after opening checkout page' assert.equal(events.some((event) => event.type === 'sleep' && event.ms === 20000), false); assert.equal(events.some((event) => event.type === 'log' && /固定等待 20 秒后继续下一步/.test(event.message)), false); }); - -test('Plus checkout create reuses the ChatGPT tab from signup step 5', async () => { - const events = []; - const executor = api.createPlusCheckoutCreateExecutor({ - addLog: async (message, level = 'info') => { - events.push({ type: 'log', message, level }); - }, - chrome: { - tabs: { - get: async (tabId) => { - events.push({ type: 'tab-get', tabId }); - return { id: tabId, url: 'https://chatgpt.com/' }; - }, - update: async (tabId, payload) => { - events.push({ type: 'tab-update', tabId, payload }); - }, - }, - }, - completeStepFromBackground: async (step, payload) => { - events.push({ type: 'complete', step, payload }); - }, - ensureContentScriptReadyOnTabUntilStopped: async (source, tabId, options) => { - events.push({ type: 'ready', source, tabId, options }); - }, - getTabId: async (source) => { - events.push({ type: 'get-tab-id', source }); - return source === 'signup-page' ? 55 : null; - }, - isTabAlive: async (source) => { - events.push({ type: 'alive', source }); - return source === 'signup-page'; - }, - registerTab: async (source, tabId) => { - events.push({ type: 'register', source, tabId }); - }, - reuseOrCreateTab: async () => { - events.push({ type: 'reuse-tab' }); - return 42; - }, - sendTabMessageUntilStopped: async () => ({ - checkoutUrl: 'https://checkout.stripe.com/c/pay/session', - country: 'US', - currency: 'USD', - }), - setState: async (payload) => { - events.push({ type: 'set-state', payload }); - }, - sleepWithStop: async (ms) => { - events.push({ type: 'sleep', ms }); - }, - waitForTabCompleteUntilStopped: async (tabId) => { - events.push({ type: 'tab-complete', tabId }); - }, - }); - - await executor.executePlusCheckoutCreate(); - - assert.equal(events.some((event) => event.type === 'reuse-tab'), false); - assert.deepEqual( - events.find((event) => event.type === 'register'), - { type: 'register', source: 'plus-checkout', tabId: 55 } - ); - assert.deepEqual( - events.find((event) => event.type === 'ready').options.inject, - ['content/plus-checkout.js'] - ); - assert.equal( - events.some((event) => event.type === 'log' && /直接接管当前标签页/.test(event.message)), - true - ); -});