const assert = require("node:assert/strict"); const path = require("node:path"); const vm = require("node:vm"); const { loadApplication, composeScript } = require("./application.cjs"); const root = path.resolve(__dirname, "../.."); const plain = (value) => JSON.parse(JSON.stringify(value)); const response = (body, status = 200) => new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } }); const unexpectedFetch = async () => { throw new Error("Unexpected network request in isolated test"); }; function clock() { let now = Date.UTC(2026, 8, 16, 12); class ClockDate extends Date { constructor(...args) { super(...(args.length ? args : [now])); } static now() { return now; } } return { Date: ClockDate, advance: (ms) => { now += ms; } }; } function loadContent({ fetch = unexpectedFetch, storage = new Map(), xsrf = null } = {}) { const refreshes = []; const context = { fetch, testXsrf: xsrf, refreshes, document: { querySelector: () => ({ value: "Current Quarter" }) }, localStorage: { getItem: key => storage.get(key) ?? null, setItem: (key, value) => storage.set(key, value) }, Date, Intl, URL }; const app = loadApplication("content", context); app.debugContentView.renderDebugPanel = () => {}; if (xsrf) app.authXsrfContent.getXsrfToken = xsrf; app.platformContentMessages.refreshXsrfCookie = async () => { refreshes.push(true); return { refreshed: true }; }; return { ...app.opportunitiesFiscalPeriods, ...app.fusionOpportunities, ...app.authTokenRelay, ...app.authSalesCommon, ...app.preferencesStorage, ...app.opportunitiesFilters, ...app.opportunitiesFormatters, ...app.contentTheme, ...app.debugState, setVersion: value => { app.opportunitiesQueryState.periodRequestVersion = value; }, setTypes: values => { app.preferencesState.selectedOpportunityTypeValues = new Set(values); }, setTable: (items, sortKey = "", sortDirection = "ascending") => { app.opportunitiesTableState.opportunitiesTableState = { items, sortKey, sortDirection }; app.opportunitiesFilters.resetCustomerFilter(items); app.opportunitiesFilters.resetOwnerFilter(items); }, filters: () => { const state = app.opportunitiesFilterState; return { stages: Array.from(state.selectedStages), statuses: Array.from(state.selectedStatuses), customerKeys: Array.from(state.selectedCustomerKeys), ownerKeys: Array.from(state.selectedOwnerKeys), customerSearch: state.customerSearch, ownerSearch: state.ownerSearch, tableSearch: state.tableSearch }; }, storage, refreshes }; } function loadBackground({ family = "chrome", fetch = unexpectedFetch, storage = {}, sessionStorage = {}, cookies = () => [], storageError = false, ruleError = false, messageTimeout = 2000 } = {}) { const time = clock(); const listeners = []; const progress = []; const requestDebug = []; const tabs = []; const removedTabs = []; const cookieQueries = []; const headerListeners = []; const responseHeaderListeners = []; const headerRules = []; const api = { runtime: { onMessage: { addListener: fn => listeners.push(fn) }, getURL: path => `${family === "firefox" ? "moz" : "chrome"}-extension://synthetic-extension${path}` }, webRequest: { onBeforeSendHeaders: { addListener: (listener, filter, options) => headerListeners.push({ listener, filter: plain(filter), options: plain(options) }) }, onHeadersReceived: { addListener: (listener, filter, options) => responseHeaderListeners.push({ listener, filter: plain(filter), options: plain(options) }) } }, declarativeNetRequest: { updateSessionRules: async rules => { if (ruleError) throw new Error("Rule permission denied"); headerRules.push(plain(rules)); } } }; const readCookies = details => { cookieQueries.push(plain(details)); return cookies(details); }; const getStored = key => ({ [key]: storage[key] === undefined ? undefined : plain(storage[key]) }); const store = value => Object.assign(storage, plain(value)); const openTab = details => { tabs.push(plain(details)); return { id: 91 }; }; const send = (tabId, message, options) => { const events = message.type === "opportunitiesExtension.opptyProductsRequestDebug" ? requestDebug : progress; events.push({ tabId, message: plain(message), options: plain(options || {}) }); }; if (family === "firefox") { api.runtime.getBrowserInfo = async () => ({ name: "Firefox" }); api.storage = { local: { get: async key => { if (storageError) throw new Error("Storage unavailable"); return getStored(key); }, set: async value => { if (storageError) throw new Error("Storage unavailable"); store(value); } } }; api.tabs = { create: async details => openTab(details), remove: async id => removedTabs.push(id), sendMessage: async (tabId, message, options) => send(tabId, message, options) }; api.cookies = { get: async details => readCookies(details).find(cookie => cookie.name === details.name) || null, getAll: async details => readCookies(details), getAllCookieStores: async () => [{ id: "test-store" }] }; } else { const withStorageError = callback => { api.runtime.lastError = storageError ? { message: "Storage unavailable" } : undefined; try { callback(); } finally { delete api.runtime.lastError; } }; api.storage = { local: { get(key, callback) { withStorageError(() => callback(getStored(key))); }, set(value, callback) { withStorageError(() => { if (!storageError) store(value); callback(); }); } } }; api.tabs = { create(details, callback) { callback(openTab(details)); }, remove(id, callback) { removedTabs.push(id); callback(); }, sendMessage(tabId, message, options, callback) { if (typeof options === "function") { callback = options; options = undefined; } send(tabId, message, options); callback(); } }; api.cookies = { get(details, callback) { callback(readCookies(details).find(cookie => cookie.name === details.name) || null); }, getAll(details, callback) { callback(readCookies(details)); }, getAllCookieStores(callback) { callback([{ id: "test-store" }]); } }; } api.storage.session = { get: async key => ({ [key]: sessionStorage[key] === undefined ? undefined : plain(sessionStorage[key]) }), set: async value => Object.assign(sessionStorage, plain(value)) }; const context = { [family === "firefox" ? "browser" : "chrome"]: api, fetch, URL, Date: time.Date, setTimeout: (callback, ms) => { time.advance(ms); return setTimeout(callback, 0); } }; new vm.Script(composeScript("background"), { filename: "background.js" }) .runInNewContext(context); assert.equal(listeners.length, 1, "Background must register one listener synchronously"); const sender = { id: "synthetic-extension", url: "https://eeho.fa.us2.oraclecloud.com/fscmUI/faces/FuseWelcome", tab: { id: 7, windowId: 3, cookieStoreId: "test-store" }, frameId: 2 }; function dispatch(message) { return new Promise((resolve, reject) => { let replies = 0; const timer = setTimeout(() => reject(new Error("Message did not receive a response")), messageTimeout); const pending = listeners[0](message, sender, result => { clearTimeout(timer); assert.equal(++replies, 1, "Message must receive exactly one response"); resolve(plain(result)); }); if (pending !== true) { clearTimeout(timer); reject(new Error("Expected asynchronous response contract")); } }); } return { dispatch, listener: listeners[0], progress, requestDebug, tabs, removedTabs, cookieQueries, storage, sessionStorage, headerRules, headerListeners, responseHeaderListeners, advance: time.advance }; } module.exports = { root, plain, response, loadContent, loadBackground };