diff --git a/background/steps/create-plus-checkout.js b/background/steps/create-plus-checkout.js index 5ac76a5..e7e334f 100644 --- a/background/steps/create-plus-checkout.js +++ b/background/steps/create-plus-checkout.js @@ -16,6 +16,7 @@ addLog: rawAddLog = async () => {}, chrome, completeStepFromBackground, + createAutomationTab = null, ensureContentScriptReadyOnTabUntilStopped, fetch: fetchImpl = null, registerTab, @@ -63,7 +64,9 @@ } async function openFreshChatGptTabForCheckoutCreate() { - const tab = await chrome.tabs.create({ url: PLUS_CHECKOUT_ENTRY_URL, active: true }); + const tab = typeof createAutomationTab === 'function' + ? await createAutomationTab({ url: PLUS_CHECKOUT_ENTRY_URL, active: true }) + : 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 页面失败,无法创建订阅页。'); diff --git a/background/steps/fill-plus-checkout.js b/background/steps/fill-plus-checkout.js index a6a5766..43748aa 100644 --- a/background/steps/fill-plus-checkout.js +++ b/background/steps/fill-plus-checkout.js @@ -88,6 +88,7 @@ getTabId, isTabAlive, markCurrentRegistrationAccountUsed, + queryTabsInAutomationWindow = null, setState, sleepWithStop, waitForTabCompleteUntilStopped, @@ -1364,13 +1365,16 @@ return null; } - const activeTabs = await chrome.tabs.query({ active: true, currentWindow: true }).catch(() => []); + const queryTabs = typeof queryTabsInAutomationWindow === 'function' + ? queryTabsInAutomationWindow + : (queryInfo) => chrome.tabs.query(queryInfo); + const activeTabs = await queryTabs({ active: true, currentWindow: true }).catch(() => []); const activeCheckoutTab = activeTabs.find((tab) => Number.isInteger(tab?.id) && isPlusCheckoutUrl(tab.url)); if (activeCheckoutTab) { return activeCheckoutTab.id; } - const checkoutTabs = await chrome.tabs.query({ url: 'https://chatgpt.com/checkout/*' }).catch(() => []); + const checkoutTabs = await queryTabs({ url: 'https://chatgpt.com/checkout/*' }).catch(() => []); const checkoutTab = checkoutTabs.find((tab) => Number.isInteger(tab?.id) && isPlusCheckoutUrl(tab.url)); return checkoutTab?.id || null; } diff --git a/background/steps/gopay-approve.js b/background/steps/gopay-approve.js index 4f3d085..5505679 100644 --- a/background/steps/gopay-approve.js +++ b/background/steps/gopay-approve.js @@ -21,6 +21,7 @@ ensureContentScriptReadyOnTabUntilStopped, getTabId, isTabAlive, + queryTabsInAutomationWindow = null, registerTab, sendTabMessageUntilStopped, setState, @@ -110,7 +111,10 @@ if (!chrome?.tabs?.query) { return 0; } - const tabs = await chrome.tabs.query({}).catch(() => []); + const queryTabs = typeof queryTabsInAutomationWindow === 'function' + ? queryTabsInAutomationWindow + : (queryInfo) => chrome.tabs.query(queryInfo); + const tabs = await queryTabs({}).catch(() => []); const candidates = (Array.isArray(tabs) ? tabs : []) .filter((tab) => Number.isInteger(tab?.id) && predicate(tab.url || '')); if (!candidates.length) { diff --git a/background/steps/gopay-manual-confirm.js b/background/steps/gopay-manual-confirm.js index f33724c..0fe4a7a 100644 --- a/background/steps/gopay-manual-confirm.js +++ b/background/steps/gopay-manual-confirm.js @@ -10,6 +10,7 @@ addLog, broadcastDataUpdate, chrome, + createAutomationTab = null, getTabId, isTabAlive, registerTab, @@ -48,7 +49,9 @@ throw new Error('步骤 7:无法自动重新打开 GoPay 订阅页。'); } - const tab = await chrome.tabs.create({ url: checkoutUrl, active: true }); + const tab = typeof createAutomationTab === 'function' + ? await createAutomationTab({ url: checkoutUrl, active: true }) + : await chrome.tabs.create({ url: checkoutUrl, active: true }); const tabId = Number(tab?.id) || 0; if (!tabId) { throw new Error('步骤 7:重新打开 GoPay 订阅页失败。'); diff --git a/background/steps/paypal-approve.js b/background/steps/paypal-approve.js index f385599..64c518f 100644 --- a/background/steps/paypal-approve.js +++ b/background/steps/paypal-approve.js @@ -15,6 +15,7 @@ ensureContentScriptReadyOnTabUntilStopped, getTabId, isTabAlive, + queryTabsInAutomationWindow = null, sendTabMessageUntilStopped, setState, sleepWithStop, @@ -48,7 +49,10 @@ return 0; } - const tabs = await chrome.tabs.query({}).catch(() => []); + const queryTabs = typeof queryTabsInAutomationWindow === 'function' + ? queryTabsInAutomationWindow + : (queryInfo) => chrome.tabs.query(queryInfo); + const tabs = await queryTabs({}).catch(() => []); const candidates = (Array.isArray(tabs) ? tabs : []) .filter((tab) => Number.isInteger(tab?.id) && isPayPalUrl(tab.url || '')); if (!candidates.length) { diff --git a/tests/plus-checkout-billing-tab-resolution.test.js b/tests/plus-checkout-billing-tab-resolution.test.js index 648731e..c279698 100644 --- a/tests/plus-checkout-billing-tab-resolution.test.js +++ b/tests/plus-checkout-billing-tab-resolution.test.js @@ -82,6 +82,7 @@ function createExecutorHarness({ fetchImpl = null, getAddressSeedForCountry = () => createAddressSeed(), getState = null, + queryTabsInAutomationWindow = null, markCurrentRegistrationAccountUsed = async () => {}, probeIpProxyExit = null, onSetState = null, @@ -161,6 +162,7 @@ function createExecutorHarness({ getTabId: async () => null, isTabAlive: async () => false, markCurrentRegistrationAccountUsed, + ...(typeof queryTabsInAutomationWindow === 'function' ? { queryTabsInAutomationWindow } : {}), setState: async (updates) => { events.states.push(updates); if (typeof onSetState === 'function') { @@ -240,6 +242,38 @@ test('Plus checkout billing uses the current checkout tab when step 6 did not re assert.equal(events.logs.some((entry) => /当前已在 Plus Checkout 页面/.test(entry.message)), true); }); +test('Plus checkout billing searches checkout tabs inside the locked automation window', async () => { + const queries = []; + const { checkoutTab, executor } = createExecutorHarness({ + frames: [{ frameId: 0, url: 'https://chatgpt.com/checkout/openai_ie/cs_test' }], + queryTabsInAutomationWindow: async (queryInfo) => { + queries.push(queryInfo); + if (queryInfo?.active) { + return []; + } + if (queryInfo?.url === 'https://chatgpt.com/checkout/*') { + return [checkoutTab]; + } + return []; + }, + stateByFrame: { + 0: { + hasPayPal: true, + paypalCandidates: [{ tag: 'button', text: 'PayPal' }], + billingFieldsVisible: true, + hasSubscribeButton: true, + }, + }, + }); + + await executor.executePlusCheckoutBilling({}); + + assert.deepEqual(queries, [ + { active: true, currentWindow: true }, + { url: 'https://chatgpt.com/checkout/*' }, + ]); +}); + test('Plus checkout billing sends the billing command to the iframe that contains PayPal', async () => { const { events, executor } = createExecutorHarness({ frames: [