General source code refactoring

This commit is contained in:
2026-09-17 12:07:01 -03:00
parent 4421b28efe
commit ac817a69d4
92 changed files with 18211 additions and 19394 deletions

View File

@@ -1,7 +1,5 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const vm = require("node:vm");
const { loadApplication } = require("./support/application.cjs");
class Element {
constructor(tag) {
@@ -28,24 +26,6 @@ function setup(mockSalesToken = true) {
static now() { return now; }
}
let visibleAlerts = [];
const source = fs.readFileSync(path.join(__dirname, "../src/content.js"), "utf8");
const marker = " if (!isAllowedPage()) {";
assert.ok(source.includes(marker));
const instrumented = source.replace(marker, `
accessToken = "test-bearer-secret";
getXsrfToken = async () => ({ token: "test-xsrf-cookie", cookieName: "XSRF-TOKEN-DYNAMIC", source: "test" });
refreshXsrfCookie = async () => ({ refreshed: false });
${mockSalesToken ? 'requestTokenRelaySalesCommon = async () => "test-sales-common-secret";' : ''}
renderDebugPanel = () => {};
renderOpportunitiesTable = () => {};
globalThis.helpers = {
requestGenerateAiSummary, createProductsCountButton, createProductsDetailRow,
createAiSummaryAlert, aiSummariesByOptyNumber, expandedProductOptyNumbers,
opptyProductsByNumber, requestLog, formatHeaderBlock, requestTokenRelay,
getAccessToken: () => accessToken, requestTokenRelaySalesCommon
};
return;
${marker}`);
const context = {
document: {
createElement: (tag) => new Element(tag),
@@ -54,8 +34,21 @@ ${marker}`);
fetch: (url, options) => new Promise((resolve, reject) => calls.push({ url, options, resolve, reject })),
Intl, Date: TestDate, Map, Set
};
vm.runInNewContext(instrumented, context);
return { ...context.helpers, calls, advance: (ms) => { now += ms; }, show: (alerts) => { visibleAlerts = alerts; } };
const app = loadApplication("content", context);
app.authSessionState.accessToken = "test-bearer-secret";
app.authXsrfContent.getXsrfToken = async () => ({ token: "test-xsrf-cookie", cookieName: "XSRF-TOKEN-DYNAMIC", source: "test" });
app.platformContentMessages.refreshXsrfCookie = async () => ({ refreshed: false });
if (mockSalesToken) app.authSalesCommon.requestTokenRelaySalesCommon = async () => "test-sales-common-secret";
app.debugContentView.renderDebugPanel = () => {};
app.opportunitiesTable.renderOpportunitiesTable = () => {};
return {
...app.aiSummaryContent, ...app.productsDetails, ...app.aiSummaryState,
...app.productsContentState, ...app.debugContentLog, ...app.debugState,
requestTokenRelay: app.authTokenRelay.requestTokenRelay,
requestTokenRelaySalesCommon: app.authSalesCommon.requestTokenRelaySalesCommon,
getAccessToken: () => app.authSessionState.accessToken,
calls, advance: (ms) => { now += ms; }, show: (alerts) => { visibleAlerts = alerts; }
};
}
function json(data, status = 200) {

View File

@@ -0,0 +1,75 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const vm = require("node:vm");
const { createHash } = require("node:crypto");
const { test } = require("node:test");
const { sources, composeScript } = require("../scripts/compose.cjs");
const { loadApplication } = require("./support/application.cjs");
const root = path.resolve(__dirname, "..");
const read = file => fs.readFileSync(path.join(root, file), "utf8");
const json = value => JSON.parse(JSON.stringify(value));
test("every source belongs to an explicit runtime composition, with no duplicate factories", () => {
const actual = fs.readdirSync(path.join(root, "src"), { recursive: true })
.filter(file => file.endsWith(".js")).map(file => `src/${file.replaceAll(path.sep, "/")}`).sort();
assert.deepEqual([...new Set(Object.values(sources).flat())].sort(), actual);
for (const [target, files] of Object.entries(sources)) {
assert.equal(new Set(files).size, files.length);
const factories = files.flatMap(file => [...read(file).matchAll(/^function (\w+)\(/gm)].map(match => match[1]));
assert.equal(new Set(factories).size, factories.length, target);
assert.equal(composeScript(target), composeScript(target));
assert.doesNotThrow(() => new vm.Script(composeScript(target)));
for (const file of files) {
const source = read(file);
assert.doesNotMatch(source, /\beval\s*\(|\bnew\s+Function\s*\(|\bimportScripts\s*\(|\brequire\s*\(|^\s*(?:import|export)\s/m, file);
if (file.endsWith("/application.js") || file.endsWith("/main.js")) continue;
const declaration = source.match(/^function (\w+)\(/m)[1];
for (const factory of factories.filter(name => name !== declaration)) {
assert.doesNotMatch(source, new RegExp(`\\b${factory}\\s*\\(`), `${file} must receive dependencies, not construct ${factory}`);
}
}
}
assert.deepEqual(sources.content.filter(file => sources.background.includes(file)), ["src/shared/messages.js"]);
});
test("Redwood cascade is byte-identical to the characterized original", () => {
const hash = createHash("sha256").update(read("src/content/styles/redwood.css")).digest("hex");
assert.equal(hash, "eaeb7e53757d48a3095f9111c5029d2c923d0dcab92af23454ccd14bdcfc81c4");
});
test("fiscal module can run alone without DOM, browser APIs or network", () => {
const factory = new vm.Script(`${read("src/features/opportunities/fiscal-periods.js")}\ncreateOpportunitiesFiscalPeriods;`).runInNewContext();
assert.deepEqual(json(factory().getFiscalPeriodRange("Current Fiscal Year", new Date(2027, 0, 15))), {
startCloseDate: "2026-06-01T00:00:00.000Z", endCloseDate: "2027-05-31T00:00:00.000Z"
});
});
test("per-frame state and credentials do not leak between application instances", () => {
const a = loadApplication("content");
const b = loadApplication("content");
for (const key of Object.keys(a).filter(key => key.endsWith("State"))) assert.notEqual(a[key], b[key], key);
a.authSessionState.accessToken = "synthetic-general";
a.authSalesCommonState.salesCommonToken = { access_token: "synthetic-scoped", expiresAt: 123 };
a.productsContentState.opptyProductsByNumber.set("TEST", { items: [] });
a.aiSummaryState.aiSummariesByOptyNumber.set("TEST", { summaryText: "Synthetic" });
assert.equal(b.authSessionState.accessToken, "");
assert.equal(b.authSalesCommonState.salesCommonToken, null);
assert.equal(b.productsContentState.opptyProductsByNumber.size, 0);
assert.equal(b.aiSummaryState.aiSummariesByOptyNumber.size, 0);
});
test("blocked localStorage access remains lazy and handled by preference fallbacks", () => {
const context = {};
Object.defineProperty(context, "localStorage", { get() { throw new Error("Storage blocked"); } });
const app = loadApplication("content", context);
assert.doesNotThrow(() => app.preferencesStorage.getSavedOpportunityTypeValues());
assert.equal(app.preferencesStorage.getSavedOpptyProductsCacheHours(), 24);
});
test("visual fixtures load modules without eval or rewriting source markers", () => {
for (const file of ["tests/fixtures/consumption-trend.html", "tests/fixtures/extension.js"]) {
assert.doesNotMatch(read(file), /\beval\s*\(|testableSource|src\/content\.js/);
}
});

View File

@@ -0,0 +1,344 @@
const test = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const vm = require("node:vm");
const { root, plain, response, loadContent, loadBackground } = require("./support/characterization.cjs");
const { composeScript } = require("./support/application.cjs");
const PREFIX = "opportunitiesExtension.";
const FILTER_KEY = "opportunities-extension-filter-preferences";
const TYPE_KEY = "opportunities-extension-opportunity-type-preferences";
const CACHE_KEY = "opportunitiesExtension.opptyProductsCache.v1";
const DAY = 86400000;
const range = { startCloseDate: "2026-06-01T00:00:00.000Z", endCloseDate: "2026-08-31T00:00:00.000Z" };
const drain = () => new Promise(resolve => setImmediate(resolve));
const productRequest = (requestId, optyNumbers = ["SYNTH-1"]) => ({
type: PREFIX + "requestOpptyProducts", requestId, optyNumbers, accessToken: "synthetic-products-token"
});
test("content bootstrap ignores other origins and preserves the existing tile/listener on reinjection", () => {
const source = composeScript("content");
const script = new vm.Script(source);
assert.doesNotThrow(() => script.runInNewContext({
window: { location: { hostname: "unrelated.example", pathname: "/" } },
document: { getElementById() { assert.fail("DOM touched on unrelated origin"); } }
}));
let listeners = 0;
const context = vm.createContext({
window: { location: { hostname: "eeho.fa.us2.oraclecloud.com", pathname: "/fscmUI/faces/FuseWelcome" } },
document: { getElementById: () => ({}) },
chrome: { runtime: { onMessage: { addListener() { listeners++; } } } },
MutationObserver: class { constructor() { assert.fail("Observer unnecessary when tile already exists"); } }
});
script.runInContext(context);
script.runInContext(context);
assert.equal(listeners, 1);
});
test("distribution contracts: manifests, execution boundaries and deterministic composition", () => {
const base = {
manifest_version: 3, name: "Opportunities Extension",
description: "Adiciona um atalho de Opportunities Extension nas paginas Oracle Fusion permitidas.",
version: "1.0.1", permissions: ["cookies", "storage"],
host_permissions: ["https://eeho.fa.us2.oraclecloud.com/*", "https://spa.oracle.com/*"],
content_scripts: [{
matches: ["https://eeho.fa.us2.oraclecloud.com/hcmUI/faces/FuseWelcome*", "https://eeho.fa.us2.oraclecloud.com/fscmUI/faces/FuseWelcome*"],
js: ["content.js"], all_frames: true, run_at: "document_idle"
}]
};
for (const target of ["chromium", "firefox"]) {
const expected = target === "chromium"
? { ...base, background: { service_worker: "background.js" } }
: { ...base, background: { scripts: ["background.js"] }, browser_specific_settings: { gecko: { id: "opportunities-extension@local.dev" } } };
assert.deepEqual(JSON.parse(fs.readFileSync(path.join(root, "dist", target, "manifest.json"))), expected);
for (const file of ["content.js", "background.js"]) {
const output = fs.readFileSync(path.join(root, "dist", target, file), "utf8");
assert.equal(output, composeScript(file.replace(".js", "")));
assert.doesNotThrow(() => new vm.Script(output, { filename: `${target}/${file}` }));
}
}
});
test("fiscal dates preserve quarter/year boundaries, leap year and unsupported renewals", () => {
const h = loadContent();
const cases = [
["Current Quarter", [2026, 5, 1], "2026-06-01", "2026-08-31"],
["Current Quarter", [2026, 7, 31], "2026-06-01", "2026-08-31"],
["Current Quarter", [2026, 8, 1], "2026-09-01", "2026-11-30"],
["Current Quarter", [2028, 1, 29], "2027-12-01", "2028-02-29"],
["Next Quarter", [2026, 10, 30], "2026-12-01", "2027-02-28"],
["Previous Quarter", [2026, 7, 24], "2026-03-01", "2026-05-31"],
["Current Fiscal Year", [2026, 4, 31], "2025-06-01", "2026-05-31"],
["Current Fiscal Year", [2026, 5, 1], "2026-06-01", "2027-05-31"],
["Current Fiscal Year", [2027, 0, 1], "2026-06-01", "2027-05-31"],
["4 Rolling Quarters (CQ + 3)", [2026, 8, 16], "2026-09-01", "2027-08-31"]
];
for (const [period, date, start, end] of cases) {
assert.deepEqual(plain(h.getFiscalPeriodRange(period, new Date(...date))), {
startCloseDate: start + "T00:00:00.000Z", endCloseDate: end + "T00:00:00.000Z"
});
}
assert.equal(h.getFiscalPeriodRange("RENEWALS (Current + Past Due)", new Date()), null);
});
test("opportunity query preserves fields, record-set OR, date AND, and pagination payload", () => {
const h = loadContent();
const types = ["ORA_MYSALESTEAMOPTIES", "ORA_MYASSGTERROPTIES", "ORA_CREDITRECEIVER_ISME"];
h.setTypes(types);
const fields = ["Name", "OptyNumber", "PrimaryRevenue.WinProb", "CustomerAccount", "PrimaryRevenue.RevnAmount", "EffectiveDate", "StatusCode", "DealRisk_c", "Owner", "ForecastGroup_c", "LastUpdateDate", "PrimaryRevenue.RevnAmountCurcyCode", "OptyId"];
assert.deepEqual(plain(h.createOpportunitiesQueryPayload(range, 15)), {
aggregationResults: false, applications: ["ORACLE-ISS-APP"], onlyData: true, entity: "Opportunity",
q: { op: "$and", criteria: [
{ op: "$or", criteria: types.map(value => ({ op: "$eq", attribute: "RecordSet", value })) },
{ op: "$wi", attribute: "EffectiveDate", value1: range.startCloseDate, value2: range.endCloseDate, dynamicDate: false }
] }, keywords: null, keywordsFields: fields,
fields: [...fields, "CustomerAccount.PartyUniqueName", "CustomerAccount.PartyId", "CustomerAccount.PartyNumber"],
sort: [], language: "en", skipInValidFields: true, skipHiddenFromUIFields: true,
copiedFrom: "queries/d5dbcd01-9ff3-40be-9b5b-64744cbf7162", limit: 15, offset: 15
});
});
test("opportunity pagination uses response offsets/count, ignores stale versions and detects stalled pages", async () => {
const calls = [];
const h = loadContent({ fetch: async (url, options) => {
calls.push({ url, options });
return response(calls.length === 1
? { items: [{ OptyNumber: "S1" }], offset: 0, count: 1, hasMore: true }
: { items: [{ OptyNumber: "S2" }], offset: 1, count: 1, hasMore: false });
} });
const items = await h.requestOpportunitiesPage("synthetic-token", "Current Quarter", range, 0, [], 0, 1);
assert.deepEqual(plain(items), [{ OptyNumber: "S1" }, { OptyNumber: "S2" }]);
assert.deepEqual(calls.map(call => JSON.parse(call.options.body).offset), [0, 1]);
assert.equal(calls[0].url, "https://eeho.fa.us2.oraclecloud.com/crmRestApi/searchResources/11.13.18.05/custom-actions/queries");
assert.deepEqual(plain(calls[0].options.headers), {
Accept: "application/json", Authorization: "Bearer synthetic-token", "Content-Type": "application/json",
Origin: "https://eeho.fa.us2.oraclecloud.com", Preference: "transient"
});
assert.equal(calls[0].options.method, "POST");
assert.equal(calls[0].options.credentials, "include");
h.setVersion(1);
assert.equal(await h.requestOpportunitiesPage("unused", "Current Quarter", range, 0, [], 0, 1), null);
assert.equal(calls.length, 2);
const stalled = loadContent({ fetch: async () => response({ items: [], count: 0, hasMore: true }) });
await assert.rejects(stalled.requestOpportunitiesPage("synthetic", "Current Quarter", range, 0, [], 0, 1), /without additional results/);
});
const opportunity = (number, status, amount) => ({
Name: `Synthetic cloud ${number}`, OptyNumber: number, StatusCode: status, ForecastGroup_c: "PIPELINE",
CustomerAccount: { PartyId: "customer-1", PartyUniqueName: "Synthetic Customer", PartyNumber: "party-1" },
Owner: { PartyId: "owner-1", PersonFirstName: "Test", PersonLastName: "Owner", PartyName: "Long Test Owner Name" },
PrimaryRevenue: { RevnAmount: amount, RevnAmountCurcyCode: "USD", WinProb: 60 },
EffectiveDate: "2026-08-28", LastUpdateDate: "2026-08-28T13:40:00"
});
test("filter storage round-trip, WON_PENDING membership, search, sorting and reference resets", () => {
const preferences = { period: "Next Quarter", stages: ["PIPELINE"], statuses: ["WON"],
customerKeys: ["party:customer-1"], ownerKeys: ["party:owner-1"],
customerSearch: "Synthetic", ownerSearch: "Test", tableSearch: "cloud" };
const storage = new Map([[FILTER_KEY, JSON.stringify(preferences)]]);
const h = loadContent({ storage });
const select = { value: "Current Quarter" };
h.restoreFilterPreferences(select);
assert.equal(select.value, "Next Quarter");
const items = [opportunity("S1", "WON", "100"), opportunity("S2", "WON_PENDING", "20"), opportunity("S3", "OPEN", "5")];
h.setTable(items, "revenue");
assert.deepEqual(plain(h.getSortedOpportunities()).map(item => item.OptyNumber), ["S2", "S1"]);
assert.equal(h.getSortedOpportunities()[0].StatusCode, "WON_PENDING");
assert.equal(h.getOwnerName(items[0]), "Test Owner");
assert.equal(h.matchesTableSearch(items[0], "28/08/2026"), true);
assert.equal(h.matchesTableSearch(items[0], "test owner"), true);
h.saveFilterPreferences(select.value);
assert.deepEqual(JSON.parse(storage.get(FILTER_KEY)), preferences);
const reopened = loadContent({ storage });
reopened.restoreFilterPreferences({ value: "Current Quarter" });
assert.equal(reopened.filters().tableSearch, "cloud");
reopened.resetCustomerAndOwnerFiltersForPeriod();
const nextItems = [...items, { ...opportunity("S4", "WON", "30"), CustomerAccount: { PartyId: "customer-2", PartyUniqueName: "Another Customer" }, Owner: { PartyId: "owner-2", PartyName: "Another Owner" } }];
reopened.setTable(nextItems);
assert.deepEqual(plain(reopened.filters().customerKeys).sort(), ["party:customer-1", "party:customer-2"]);
assert.deepEqual(plain(reopened.filters().ownerKeys).sort(), ["party:owner-1", "party:owner-2"]);
assert.equal(reopened.filters().tableSearch, "cloud");
assert.equal(reopened.filters().customerSearch, "");
assert.equal(reopened.filters().ownerSearch, "");
});
test("malformed preferences fall back, while theme/type/cache keys retain their schema", () => {
const storage = new Map([[FILTER_KEY, "not JSON"], ["opportunities-extension-theme", "dark"],
[TYPE_KEY, JSON.stringify({ opportunityTypeValues: ["ORA_MYOPTIES", "UNKNOWN"], opptyProductsCacheHours: 24 })]]);
const h = loadContent({ storage });
assert.doesNotThrow(() => h.restoreFilterPreferences({ value: "Current Quarter" }));
assert.equal(h.getSavedTheme(), "dark");
assert.deepEqual(plain(h.getSavedOpportunityTypeValues()), ["ORA_MYOPTIES"]);
assert.equal(h.getSavedOpptyProductsCacheHours(), 24);
assert.equal(h.filters().tableSearch, "");
});
test("tokenrelay missing-cookie recovery and HTTP failure do not change the request contract", async () => {
let reads = 0;
const calls = [];
const h = loadContent({ xsrf: async () => ({ token: ++reads === 1 ? "" : "synthetic-xsrf", cookieName: "XSRF-TOKEN-SYNTHETIC" }),
fetch: async (url, options) => { calls.push({ url, options }); return response({ access_token: "synthetic-access" }); } });
assert.equal(await h.requestTokenRelay(), "synthetic-access");
assert.equal(h.refreshes.length, 1);
assert.equal(calls.length, 1);
assert.equal(calls[0].options.headers["x-xsrf-token"], "synthetic-xsrf");
assert.equal(calls[0].options.method, "GET");
for (const status of [400, 401, 403]) {
let attempts = 0;
const rejected = loadContent({ xsrf: async () => ({ token: "synthetic-xsrf" }),
fetch: async () => { attempts++; return response({}, status); } });
await assert.rejects(rejected.requestTokenRelay(), /failed with status/);
assert.equal(attempts, 2);
assert.equal(rejected.refreshes.length, 1);
}
});
for (const family of ["chrome", "firefox"]) {
test(`${family}: products paginate, route progress to sender frame and reuse persisted cache after restart`, async () => {
const calls = [];
const storage = {};
const h = loadBackground({ family, storage, fetch: async (url, options) => {
calls.push({ url, options });
const offset = Number(new URL(url).searchParams.get("offset"));
return response({ items: [{ RevnNumber: `SYNTH-${offset}` }], count: 1, offset, hasMore: offset === 0 });
} });
const result = await h.dispatch(productRequest("job-1", ["SYNTH-1", "SYNTH-1"]));
assert.deepEqual(result, { ok: true, requestId: "job-1", completed: 1, total: 1, failed: 0, cacheHits: 0 });
assert.equal(calls.length, 2);
assert.equal(new URL(calls[0].url).pathname, "/crmRestApi/rest/rv:be91c002-2e5d-4ed2-a37e-e0c837bf141f/en/11.13.18.05:9/opportunities/SYNTH-1/child/ChildRevenue");
assert.equal(new URL(calls[0].url).searchParams.get("q"), "(SplitTypeCode!='DETAILCHILDSPLIT') AND (RecurTypeCode!='CHILDRECUR')");
assert.equal(new URL(calls[0].url).searchParams.get("limit"), "15");
assert.equal(new URL(calls[1].url).searchParams.get("offset"), "1");
assert.equal(calls[0].options.headers.Authorization, "Bearer synthetic-products-token");
assert.ok(h.progress.every(event => event.tabId === 7 && event.options.frameId === 2));
assert.equal(h.progress[0].message.type, PREFIX + "opptyProductsProgress");
assert.equal(h.progress[0].message.items.length, 2);
assert.equal(h.progress.at(-1).message.state, "complete");
assert.deepEqual(Object.keys(storage[CACHE_KEY]["SYNTH-1"]).sort(), ["fetchedAt", "items"]);
const restarted = loadBackground({ family, storage });
assert.equal((await restarted.dispatch(productRequest("job-2"))).cacheHits, 1);
h.advance(DAY);
await h.dispatch(productRequest("job-3"));
assert.equal(calls.length, 4, "default TTL expires at 24 hours");
});
test(`${family}: product queue has four workers, shares in-flight results and reports partial failures`, async () => {
const calls = [];
const h = loadBackground({ family, fetch: (url) => new Promise(resolve => calls.push({ url, resolve })) });
const first = h.dispatch(productRequest("concurrent-1", ["S1", "S2", "S3", "S4", "S5"]));
await drain();
assert.equal(calls.length, 4);
const duplicate = h.dispatch(productRequest("concurrent-2", ["S1"]));
await drain();
assert.equal(calls.length, 4);
calls[0].resolve(response({ items: [{ RevnNumber: "R1" }], hasMore: false }));
await drain();
assert.equal(calls.length, 5);
calls[1].resolve(response({}, 500));
for (const call of calls.slice(2)) call.resolve(response({ items: [], hasMore: false }));
assert.equal((await duplicate).ok, true);
const result = await first;
assert.equal(result.failed, 1);
assert.equal(result.completed, 5);
assert.equal(h.progress.filter(event => event.message.requestId === "concurrent-1").at(-1).message.state, "complete-with-errors");
});
test(`${family}: custom product TTL and zero-count pagination failures`, async () => {
let calls = 0;
const h = loadBackground({ family, fetch: async () => { calls++; return response({ items: [], hasMore: false }); } });
const message = { ...productRequest("custom-ttl"), cacheMaxAgeMs: 1000 };
await h.dispatch(message);
h.advance(999);
assert.equal((await h.dispatch(message)).cacheHits, 1);
assert.equal(calls, 1);
h.advance(1);
assert.equal((await h.dispatch(message)).cacheHits, 0);
assert.equal(calls, 2);
const stalled = loadBackground({ family, fetch: async () => response({ items: [], count: 0, hasMore: true }) });
assert.equal((await stalled.dispatch(productRequest("stalled"))).failed, 1);
assert.match(stalled.progress[0].message.error, /without additional results/);
});
test(`${family}: dynamic XSRF cookie lookup, missing-cookie result and temporary-tab cleanup`, async () => {
const cookie = { name: "XSRF-TOKEN-SYNTHETIC", value: "synthetic-xsrf", domain: "eeho.fa.us2.oraclecloud.com", path: "/", storeId: "test-store" };
const h = loadBackground({ family, cookies: () => [cookie] });
const result = await h.dispatch({ type: PREFIX + "getXsrfToken" });
assert.equal(result.ok, true);
assert.equal(result.cookieName, cookie.name);
assert.equal(result.token, cookie.value);
assert.ok(h.cookieQueries.some(query => query.storeId === "test-store"));
const missing = loadBackground({ family });
assert.equal((await missing.dispatch({ type: PREFIX + "getXsrfToken" })).token, "");
const refresh = await missing.dispatch({ type: PREFIX + "refreshXsrfCookie" });
assert.equal(refresh.refreshed, false);
assert.equal(refresh.durationMs, 15000);
assert.deepEqual(missing.removedTabs, [91]);
assert.equal(missing.tabs[0].active, false);
assert.equal(missing.tabs[0].windowId, 3);
assert.equal(missing.tabs[0].cookieStoreId, family === "firefox" ? "test-store" : undefined);
let refreshHarness;
refreshHarness = loadBackground({ family, cookies: () => refreshHarness.tabs.length ? [cookie] : [] });
assert.equal((await refreshHarness.dispatch({ type: PREFIX + "refreshXsrfCookie" })).refreshed, true);
assert.deepEqual(refreshHarness.removedTabs, [91]);
});
test(`${family}: SPA token selection, account POST contracts and debug envelope`, async () => {
const calls = [];
const h = loadBackground({ family, fetch: async (url, options) => {
calls.push({ url, options });
if (calls.length === 1) return response([
{ tokenService: "UNRELATED", uiaasHeader: { value: "synthetic-unused" } },
{ tokenService: "ACCP-TS", uiaasHeader: { value: "synthetic-account" } },
{ tokenService: "OCICONS-TS", uiaasHeader: { value: "synthetic-consumption" } }
]);
if (calls.length === 2) return response({ items: [{ id: "synthetic/id" }] });
return response({ items: [{ planType: "Synthetic", subPlanNum: "plan-1" }] });
} });
const tokens = await h.dispatch({ type: PREFIX + "requestConsumerTokenService" });
const plans = await h.dispatch({ type: PREFIX + "requestAccountPlans", partyNumber: "synthetic-party", authorizations: tokens.authorizations });
assert.equal(plans.ok, true);
assert.equal(plans.accountPlanId, "synthetic/id");
assert.deepEqual(plans.plans, [{ planType: "Synthetic", subPlanNum: "plan-1" }]);
assert.equal(calls[1].url, "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-account/service/accountsummary");
assert.deepEqual(JSON.parse(calls[1].options.body), [{ key: "regId", value: "synthetic-party" }]);
assert.equal(calls[1].options.headers["account-ti-authorization"], "synthetic-account");
assert.equal(calls[2].options.headers["g2m-authorization"], "synthetic-consumption");
assert.equal(calls[2].url, "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-consumption/service/v1/account/accountPlans/synthetic%2Fid");
assert.deepEqual(JSON.parse(calls[2].options.body), {
hierarchy: "single", products: "all", language: "english",
planStatus: ["active", "awaiting_provisioning", "awaiting_provisioning_error", "draft", "expired", "final_billing", "onhold", "provisioned", "signed", "superseded", "superseded_pending_final_billing", "suspended", "terminated", "terminated_pending_final_billing"]
});
assert.ok(calls.slice(1).every(call => call.options.method === "POST" && call.options.credentials === "include"));
assert.deepEqual(plans.debugRequests.map(entry => entry.name), ["requestAccountSummary", "requestAccountPlans"]);
assert.ok(plans.debugRequests.every(entry => entry.httpStatus === 200 && entry.status === "success" && entry.durationMs >= 0));
});
test(`${family}: unknown/invalid messages fail without network; storage failure does not discard fetched products`, async () => {
const h = loadBackground({ family });
assert.equal(h.listener({ type: "unknown" }, {}, () => assert.fail("Unexpected reply")), false);
assert.equal((await h.dispatch({ type: PREFIX + "requestOpptyProducts" })).ok, false);
assert.equal((await h.dispatch({ type: PREFIX + "requestAccountPlans" })).ok, false);
assert.equal((await h.dispatch({ type: PREFIX + "requestConsumptionTrend", subPlanNum: "S1", period: "unsupported" })).ok, false);
const noStorage = loadBackground({ family, storageError: true, fetch: async () => response({ items: [{ RevnNumber: "R1" }], hasMore: false }) });
assert.equal((await noStorage.dispatch(productRequest("no-storage"))).ok, true);
assert.equal(noStorage.progress[0].message.items.length, 1);
});
test(`${family}: consumption network/HTTP/JSON failures retain the response and debug contracts`, async () => {
for (const failure of ["network", "http", "json"]) {
const h = loadBackground({ family, fetch: async () => {
if (failure === "network") throw new Error("Synthetic network error");
return failure === "http" ? response({}, 401) : new Response("<html>Synthetic fallback</html>");
} });
const result = await h.dispatch({ type: PREFIX + "requestConsumptionTrend", subPlanNum: "synthetic-plan", period: "weekly",
authorization: { name: "g2m-authorization", value: "synthetic-token" } });
assert.equal(result.ok, false);
assert.equal(result.debugRequests.length, 1);
assert.equal(result.debugRequests[0].name, "requestConsumptionServiceWeekly");
assert.equal(result.debugRequests[0].status, "failed");
assert.equal(result.debugRequests[0].httpStatus, failure === "network" ? null : failure === "http" ? 401 : 200);
assert.match(result.error, failure === "network" ? /Synthetic network error/ : failure === "http" ? /status 401/ : /invalid JSON/);
}
});
}

View File

@@ -1,9 +1,7 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const vm = require("node:vm");
const { composeScript } = require("./support/application.cjs");
const root = path.resolve(__dirname, "..");
function createResponse(body, contentType) {
return {
@@ -24,7 +22,7 @@ function createResponse(body, contentType) {
function loadBackground(responses, createdTabs, removedTabs, options = {}) {
let listener;
const source = fs.readFileSync(path.join(root, "src", "background.js"), "utf8");
const source = composeScript("background");
const context = {
chrome: {
runtime: {

View File

@@ -1,21 +1,9 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const vm = require("node:vm");
const { loadApplication, composeScript } = require("./support/application.cjs");
const root = path.resolve(__dirname, "..");
function loadContentHelpers() {
const source = fs.readFileSync(path.join(root, "src", "content.js"), "utf8");
const marker = "\n if (insertTile()) {";
const instrumented = source.replace(marker, `
globalThis.__consumptionTrendTest = {
normalizeConsumptionTrendItems,
formatConsumptionPointLabel,
createNiceConsumptionMaximum,
calculateAverageConsumption
};
${marker}`);
const context = {
chrome: { runtime: { onMessage: { addListener() {} } } },
document: { getElementById: () => ({}) },
@@ -36,13 +24,12 @@ ${marker}`);
Math
};
vm.runInNewContext(instrumented, context);
return context.__consumptionTrendTest;
return loadApplication("content", context).consumptionModel;
}
function loadBackground(fetchCalls) {
let listener;
const source = fs.readFileSync(path.join(root, "src", "background.js"), "utf8");
const source = composeScript("background");
const headers = {
get(name) {
return name.toLowerCase() === "content-type" ? "application/json" : null;

View File

@@ -12,25 +12,21 @@
</head>
<body>
<div id="opportunities-extension-modal" class="opportunities-extension-modal" data-theme="light"></div>
<script src="/fixture-runtime.js"></script>
<script>
window.chrome = { runtime: { onMessage: { addListener() {} } } };
async function initialize() {
const source = await fetch("/src/content.js").then((response) => response.text());
const marker = /\r?\n if \(insertTile\(\)\) \{/;
const exposure = `
window.__trendHarness = {
ensureStyles: ensureExtensionStyles,
createAccountPlanDetails,
createConsumptionTrendSection,
renderConsumptionTrendChart
};
return;
`;
const testableSource = source
.replace(" if (!isAllowedPage()) {", " if (false) {")
.replace(marker, `${exposure}\n if (insertTile()) {`);
window.eval(testableSource);
const app = window.createFixtureApplication({
fetch: () => Promise.reject(new Error("Network disabled in fixture")),
browser: undefined,
chrome: undefined
});
const harness = {
ensureStyles: app.contentStyles.ensureExtensionStyles,
createAccountPlanDetails: app.consumptionPlansView.createAccountPlanDetails,
createConsumptionTrendSection: app.consumptionTrendController.createConsumptionTrendSection,
renderConsumptionTrendChart: app.consumptionChart.renderConsumptionTrendChart
};
const plan = {
subPlanNum: "11276098",
@@ -63,7 +59,7 @@
return rows;
});
window.__trendHarness.ensureStyles();
harness.ensureStyles();
const overlay = document.getElementById("opportunities-extension-modal");
overlay.dataset.theme = new URLSearchParams(window.location.search).get("theme") === "dark"
? "dark"
@@ -82,13 +78,13 @@
tabs.innerHTML = '<button class="opportunities-extension-account-plan-tab" aria-selected="true"><span class="opportunities-extension-account-plan-status-indicator" data-active="true"></span>Universal Credits 11276098</button>';
const panel = document.createElement("section");
panel.className = "opportunities-extension-account-plan-panel";
const trend = window.__trendHarness.createConsumptionTrendSection(plan);
panel.append(window.__trendHarness.createAccountPlanDetails(plan), trend);
const trend = harness.createConsumptionTrendSection(plan);
panel.append(harness.createAccountPlanDetails(plan), trend);
content.append(tabs, panel);
dialog.append(header, content);
modal.append(dialog);
overlay.append(modal);
window.__trendHarness.renderConsumptionTrendChart(
harness.renderConsumptionTrendChart(
trend,
trend.querySelector(".opportunities-extension-consumption-chart"),
items,

13
tests/fixtures/extension.html vendored Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Extension Synthetic Integration Fixture</title>
</head>
<body>
<button id="open" type="button">Open Opportunities</button>
<script src="/fixture-runtime.js"></script>
<script src="/extension.js"></script>
</body>
</html>

77
tests/fixtures/extension.js vendored Normal file
View File

@@ -0,0 +1,77 @@
(() => {
const listeners = [];
const storage = new Map();
const opportunities = Array.from({ length: 25 }, (_, index) => ({
OptyNumber: `TEST${index + 1}`,
Name: `Synthetic opportunity ${index + 1} - Database migration`,
CustomerAccount: { PartyUniqueName: "Example public sector customer (Example City, BR)", PartyId: "test-party", PartyNumber: "test-account" },
Owner: { PersonFirstName: "Example", PersonLastName: "Owner" },
PrimaryRevenue: { RevnAmountCurcyCode: "USD", RevnAmount: "25000", WinProb: 60 },
EffectiveDate: "2026-11-15T00:00:00Z",
LastUpdateDate: "2026-09-15T17:00:00Z",
ForecastGroup_c: ["SQL", "PIPELINE", "UPSIDE", "FORECAST", "WON"][index % 5],
StatusCode: ["OPEN", "CLOSED", "LOST", "WON", "WON_PENDING"][index % 5]
}));
const products = [{ ProdGroupName: "Migrate databases to cloud", WorkloadName_c: "Example workload", RevnAmountCurcyCode: "USD", RevnAmount: "25000", TypeCode: "NEWCOMMIT", StatusCode: "OPEN", WinProb: 60, EffectiveDate: "2026-11-15", ConsumptionStartDate_c: "2026-11-15", RampMonths_c: 3 }];
const trend = Array.from({ length: 30 }, (_, index) => ({
fyDate: `2026-09-${String((index % 28) + 1).padStart(2, "0")}`,
fiscalTime: [`${String((index % 28) + 1).padStart(2, "0")}-Sep-26`],
fiscalQtr: "FY27-Q2", currencyCode: "USD", arrCd: 0,
usageCategory: index % 4 ? "Funded Allocation" : "OVERAGE",
usedAmount: 1000 + index * 100
}));
const authorizations = {
accountSummary: { label: "Account-Ti-Authorization", value: "synthetic-accp" },
accountPlans: { label: "G2m-Authorization", value: "synthetic-ocicons" }
};
const runtime = {
onMessage: { addListener: listener => listeners.push(listener) },
async sendMessage(message) {
const operation = message.type.split(".").at(-1);
if (operation === "getXsrfToken") return { ok: true, token: "synthetic-xsrf", cookieName: "XSRF-TOKEN-TEST" };
if (operation === "requestConsumerTokenService") return { ok: true, authorizations, debugRequests: [] };
if (operation === "requestOpptyProducts") {
message.optyNumbers.forEach((optyNumber, index) => listeners.forEach(listener => listener({
type: "opportunitiesExtension.opptyProductsProgress", requestId: message.requestId,
optyNumber, items: products, cachedAt: Date.now(), completed: index + 1,
total: message.optyNumbers.length, state: index + 1 === message.optyNumbers.length ? "complete" : "running"
})));
return { ok: true, requestId: message.requestId };
}
if (operation === "requestAccountPlans") return {
ok: true, debugRequests: [], plans: ["ACTIVE", "EXPIRED"].map((hdrStatusCode, index) => ({
subPlanNum: `PLAN${index + 1}`, planType: "Universal Credits", hdrStatusCode,
bookingCustomer: "Example Customer", soldToPartyName: "Example Sold To",
contractId: "synthetic-contract", startDate: "2026-06-01", endDate: "2027-05-31"
}))
};
if (operation === "requestConsumptionTrend") return { ok: true, items: trend, debugRequests: [] };
throw new Error(`Unexpected synthetic message: ${operation}`);
}
};
const application = window.createFixtureApplication({
browser: { runtime: {
onMessage: runtime.onMessage,
sendMessage(message, callback) {
const result = runtime.sendMessage(message);
if (callback) result.then(callback);
return result;
}
} }, chrome: undefined,
localStorage: { getItem: key => storage.get(key) ?? null, setItem: (key, value) => storage.set(key, value) },
async fetch(input, options) {
const url = new URL(input);
let body;
if (url.pathname === "/fscmRestApi/tokenrelay") body = { access_token: "synthetic-fusion-token", expires_in: 3600 };
else if (url.pathname.endsWith("custom-actions/queries")) {
const { offset, limit } = JSON.parse(options.body);
body = { items: opportunities.slice(offset, offset + limit), totalResults: opportunities.length, count: Math.min(limit, opportunities.length - offset), hasMore: offset + limit < opportunities.length, offset, limit };
} else if (url.pathname.endsWith("summarizations/generate")) body = { summaryText: "Synthetic AI summary.\nText is displayed safely: <script>never executed</script>." };
else throw new Error(`Unexpected synthetic fetch: ${url.pathname}`);
return new Response(JSON.stringify(body), { status: 200, headers: { "Content-Type": "application/json" } });
}
});
application.productsContentJobs.ensureProductsBackgroundMessageHandler();
document.getElementById("open").addEventListener("click", () => application.contentModal.openOpportunitiesModal());
application.contentModal.openOpportunitiesModal();
})();

View File

@@ -0,0 +1,11 @@
const vm = require("node:vm");
const { composeScript } = require("../../scripts/compose.cjs");
function loadApplication(target, globals = {}) {
const factory = new vm.Script(composeScript(target, { bootstrap: false }), {
filename: `${target}.test-bundle.js`
}).runInNewContext(globals);
return factory();
}
module.exports = { loadApplication, composeScript };

View File

@@ -0,0 +1,134 @@
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 = {}, cookies = () => [], storageError = false } = {}) {
const time = clock();
const listeners = [];
const progress = [];
const tabs = [];
const removedTabs = [];
const cookieQueries = [];
const api = { runtime: { onMessage: { addListener: fn => listeners.push(fn) } } };
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) => progress.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" }]); }
};
}
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")), 2000);
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, tabs, removedTabs, cookieQueries, storage, advance: time.advance };
}
module.exports = { root, plain, response, loadContent, loadBackground };