const assert = require("node:assert/strict");
const { test } = require("node:test");
const fs = require("node:fs");
const path = require("node:path");
const { loadBackground, response, plain } = require("./support/characterization.cjs");
const { loadApplication } = require("./support/application.cjs");
const detail = {
projectedStatus: "Forfeit", pricingModel: "Funded Allocation", lineStatus: "ACTIVE",
startDate: "17-Aug-2023", endDate: "16-Aug-2027", committedCredits: 7488782,
consumed: 4106739, consumptionL30Days: 152200, monthsRemaining: null,
projectedConsumption: null, projectedDelta: null, currencyCode: "BRL", orderId: "SYNTH-ORDER",
orderTrackerURL: "https://accountdashboard.oracle.com/app/udb/?appId=12#/ordertracker/orderNumber/SYNTH-ORDER",
clines: [{ projectedStatus: "Overage", pricingModel: "Funded Allocation", monthsRemaining: 0,
committedCredits: 100, consumed: 110, projectedConsumption: 120, projectedDelta: -20,
orderId: "SYNTH-CHILD", orderTrackerURL: "https://accountdashboard.oracle.com/app/udb/#/ordertracker/orderNumber/SYNTH-CHILD" }]
};
const type = "opportunitiesExtension.requestConsumptionPlanDetail";
for (const family of ["chrome", "firefox"]) {
test(`${family}: plan details use OCICONS value in G2m-Authorization, encoded subPlanNum, and debug`, async () => {
const calls = [];
const app = loadBackground({ family, fetch: async (url, options) => {
calls.push({ url, options });
if (url.endsWith("consumerTokenService")) return response([
{ tokenService: "ACCP-TS", uiaasHeader: { value: "synthetic-account" } },
{ tokenService: "OCICONS-TS", uiaasHeader: { label: "unused-label", value: "synthetic-plan" } }
]);
return response([detail]);
} });
const auth = await app.dispatch({ type: "opportunitiesExtension.requestConsumerTokenService" });
const result = await app.dispatch({ type, subPlanNum: "plan/test", authorization: auth.authorizations.accountPlans });
assert.equal(result.ok, true);
assert.deepEqual(result.items, [detail]);
assert.equal(result.subPlanNum, "plan/test");
assert.equal(calls[1].url, "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-consumption/service/v1/plan/plandetail/plan%2Ftest?active=true");
assert.equal(calls[1].options.method, "GET");
assert.equal(calls[1].options.credentials, "include");
assert.equal(calls[1].options.cache, "no-store");
assert.equal(calls[1].options.body, undefined);
assert.deepEqual(plain(calls[1].options.headers), { Accept: "application/json", "G2m-Authorization": "synthetic-plan" });
assert.equal(result.debugRequests[0].name, "requestConsumptionPlanDetail");
assert.equal(result.debugRequests[0].httpStatus, 200);
const content = loadApplication("content");
assert.doesNotMatch(content.debugContentLog.formatHeaderBlock(result.debugRequests[0].requestHeaders), /synthetic-plan/);
assert.equal(content.debugContentView.getRequestCategory(result.debugRequests[0]), "consumption");
});
}
test("missing token/subPlanNum, HTTP, network, invalid JSON and malformed nested lines do not silently succeed", async () => {
for (const kind of ["token", "id", "http", "network", "json", "array", "clines"]) {
let calls = 0;
const app = loadBackground({ fetch: async () => {
calls += 1;
if (kind === "http") return response({}, 403);
if (kind === "network") throw new Error("Synthetic network failure");
if (kind === "json") return new Response("fallback");
return response(kind === "array" ? {} : [{ clines: {} }]);
} });
const result = await app.dispatch({ type, subPlanNum: kind === "id" ? "" : "test",
authorization: { value: kind === "token" ? "" : "synthetic-plan" } });
assert.equal(result.ok, false, kind);
assert.ok(result.error, kind);
assert.equal(calls, ["token", "id"].includes(kind) ? 0 : 1);
if (calls) assert.equal(result.debugRequests[0].status, "failed");
}
});
function createDom() {
const document = { activeElement: null, listeners: new Map(), documentElement: {} };
const window = { innerWidth: 420, innerHeight: 640, listeners: new Map() };
for (const target of [document, window]) {
target.addEventListener = (name, fn) => { if (!target.listeners.has(name)) target.listeners.set(name, new Set()); target.listeners.get(name).add(fn); };
target.removeEventListener = (name, fn) => target.listeners.get(name)?.delete(fn);
}
const walk = node => [node, ...node.children.flatMap(walk)];
class Element {
constructor(tag) {
this.tag = tag; this.children = []; this.attributes = {}; this.listeners = {}; this.style = {}; this.dataset = {}; this.hidden = false; this.text = "";
this.classList = { add: name => { this.className = `${this.className || ""} ${name}`.trim(); } };
}
append(...nodes) { nodes.forEach(node => { node.parent = this; }); this.children.push(...nodes); }
set textContent(value) { this.text = String(value); this.children.forEach(node => { node.parent = null; }); this.children = []; }
get textContent() { return this.text; }
setAttribute(key, value) { this.attributes[key] = value; }
getAttribute(key) { return this.attributes[key]; }
addEventListener(name, fn) { this.listeners[name] = fn; }
removeEventListener(name) { delete this.listeners[name]; }
focus() { document.activeElement = this; }
contains(node) { return !!node && walk(this).includes(node); }
remove() { if (this.parent) this.parent.children = this.parent.children.filter(child => child !== this); this.parent = null; }
get isConnected() { return this === root || !!this.parent?.isConnected; }
querySelectorAll(selector) {
return walk(this).slice(1).filter(node => selector.split(",").some(part => {
const value = part.trim();
if (value.startsWith(".")) return (node.className || "").split(" ").includes(value.slice(1));
const attr = value.match(/^\[([^=]+)='([^']+)'\]$/);
return attr ? node.attributes[attr[1]] === attr[2] : node.tag === value;
}));
}
querySelector(selector) { return this.querySelectorAll(selector)[0] || null; }
getBoundingClientRect() { return this.tag === "button" ? { left: 350, top: 530, bottom: 550, height: 20 } : { height: 500 }; }
}
const root = new Element("main");
document.createElement = tag => new Element(tag);
document.createElementNS = (_, tag) => new Element(tag);
document.createTextNode = text => { const node = new Element("#text"); node.textContent = text; return node; };
document.getElementById = id => walk(root).find(node => node.id === id) || null;
const timers = new Map();
let timerId = 0;
return { root, document, window, walk, timers,
setTimeout: fn => { timers.set(++timerId, fn); return timerId; }, clearTimeout: id => timers.delete(id) };
}
function setup() {
const dom = createDom();
const app = loadApplication("content", { ...dom, URL, Intl, requestAnimationFrame: () => {} });
dom.root.id = app.contracts.MODAL_ID;
app.debugContentLog.appendBackgroundRequestLog = () => {};
return { ...dom, app };
}
test("nested tooltip preserves values/null/zero and restricts order links to the expected HTTPS origin", () => {
const { app, root, walk } = setup();
app.consumptionPlanDetailView.renderPlanDetailBadges(root, [detail, { projectedStatus: "Other", pricingModel: "
", orderId: "bad", orderTrackerURL: "javascript:bad()" }]);
assert.equal(root.children.length, 2);
assert.equal(root.children[0].children[0].dataset.status, "forfeit");
const tooltip = root.children[0].children[1];
assert.equal(tooltip.hidden, true);
const nesting = tooltip.querySelector(".opportunities-extension-projection-children");
assert.ok(nesting);
assert.equal(nesting.querySelector(".opportunities-extension-projection-badge").dataset.status, "overage");
const terms = tooltip.querySelectorAll("dt");
assert.equal(terms.length, 22);
assert.ok(terms.some(term => term.textContent === "Order number"));
const values = tooltip.querySelectorAll("dd").map(value => value.textContent);
assert.ok(values.includes("7,488,782.00"));
assert.ok(values.includes("-20.00"));
assert.ok(values.includes("0"));
assert.ok(values.includes("-"));
assert.ok(values.includes("17-Aug-2023"));
assert.equal(root.querySelectorAll("a").length, 2);
assert.ok(root.querySelectorAll("a").every(link => link.target === "_blank" && link.rel === "noopener noreferrer"));
assert.ok(walk(root).some(node => node.textContent === "
"));
for (const orderTrackerURL of ["https://accountdashboard.oracle.com.evil.example/x", "https://user@accountdashboard.oracle.com/x", "http://accountdashboard.oracle.com/x"]) {
assert.equal(app.consumptionPlanDetailView.createLineDetails({ orderId: "test", orderTrackerURL }).querySelectorAll("a").length, 0);
}
});
test("hover, click, keyboard, outside dismissal and cleanup keep the popover within the viewport", () => {
const { app, root, document, window, timers } = setup();
app.consumptionPlanDetailView.renderPlanDetailBadges(root, [detail]);
const wrapper = root.children[0];
const [button, tooltip] = wrapper.children;
wrapper.listeners.pointerenter();
assert.equal(tooltip.hidden, false);
assert.equal(button.attributes["aria-expanded"], "true");
assert.ok(parseFloat(tooltip.style.left) + parseFloat(tooltip.style.width) <= window.innerWidth);
assert.ok(parseFloat(tooltip.style.top) >= 12);
wrapper.listeners.pointerleave();
assert.equal(timers.size, 1);
wrapper.listeners.pointerenter();
assert.equal(timers.size, 0);
button.listeners.click();
wrapper.listeners.pointerleave();
assert.equal(timers.size, 0, "click pins the tooltip so the order links remain usable");
let stopped = false;
wrapper.listeners.keydown({ key: "Escape", preventDefault() {}, stopPropagation() { stopped = true; } });
assert.equal(stopped, true);
assert.equal(tooltip.hidden, true);
assert.equal(document.activeElement, button);
assert.equal(document.listeners.get("pointerdown").size, 0);
assert.equal(window.listeners.get("resize").size, 0);
wrapper.listeners.focusin();
for (const listener of document.listeners.get("pointerdown")) listener({ target: root });
assert.equal(tooltip.hidden, true);
});
test("controller shares in-flight requests, preserves per-panel identity and ignores removed panels", async () => {
const { app, root, document } = setup();
const calls = [];
app.authContentSession.requestConsumerTokenServiceOnce = async () => ({ accountPlans: { value: "synthetic-plan" } });
app.platformContentMessages.sendRuntimeMessage = message => new Promise(resolve => calls.push({ message, resolve }));
const panel = document.createElement("section");
panel.append(app.consumptionPlanDetailController.createPlanDetailSection({ subPlanNum: "A" }));
root.append(panel);
const first = app.consumptionPlanDetailController.loadPlanDetailForPanel(panel, { subPlanNum: "A" });
const repeat = app.consumptionPlanDetailController.loadPlanDetailForPanel(panel, { subPlanNum: "A" });
assert.equal(first, repeat);
assert.equal(panel.children[0].attributes["aria-busy"], "true");
const other = document.createElement("section");
other.append(app.consumptionPlanDetailController.createPlanDetailSection({ subPlanNum: "B" }));
root.append(other);
const second = app.consumptionPlanDetailController.loadPlanDetailForPanel(other, { subPlanNum: "B" });
await new Promise(resolve => setImmediate(resolve));
assert.equal(calls.length, 2);
assert.deepEqual(plain(calls[0].message), { type, subPlanNum: "A", authorization: { value: "synthetic-plan" } });
calls[1].resolve({ ok: true, items: [{ projectedStatus: "Overage" }] });
await second;
panel.remove();
calls[0].resolve({ ok: true, items: [detail] });
await first;
assert.equal(other.querySelector(".opportunities-extension-projection-badge").textContent, "Overage");
assert.equal(panel.querySelector(".opportunities-extension-projection-badge"), null);
await app.consumptionPlanDetailController.loadPlanDetailForPanel(other, { subPlanNum: "B" });
assert.equal(calls.length, 2, "ready details remain in the current modal only");
});
test("Cloud Consumption loads details for the first tab and each newly selected plan, without delaying trends", () => {
const { app, root, document } = setup();
const calls = [];
const trends = [];
app.consumptionPlanDetailController.loadPlanDetailForPanel = (_, plan) => calls.push(plan.subPlanNum);
app.consumptionTrendController.loadConsumptionTrendForPanel = (_, plan) => trends.push(plan.subPlanNum);
app.consumptionPlansView.openAccountPlansModal({ OptyNumber: "SYNTH" }, [], "loading");
app.consumptionPlansView.openAccountPlansModal({ OptyNumber: "SYNTH" }, [{ subPlanNum: "A" }, { subPlanNum: "B" }], "ready");
assert.deepEqual(calls, ["A"]);
assert.deepEqual(trends, ["A"]);
const badges = root.querySelector(".opportunities-extension-plan-detail-badges");
const trendHeader = root.querySelector(".opportunities-extension-consumption-trend-header");
assert.equal(trendHeader.children[0], badges, "projection badges are left of the period controls");
assert.equal(trendHeader.children[1].className, "opportunities-extension-consumption-periods");
assert.deepEqual(trendHeader.children[1].children.map(button => button.textContent), ["Quarterly", "Monthly", "Weekly", "Daily"]);
assert.equal(root.querySelector(".opportunities-extension-account-plan-details").querySelector(".opportunities-extension-plan-detail-badges"), null);
app.consumptionPlanDetailView.renderPlanDetailBadges(badges, [detail]);
const wrapper = badges.children[0];
wrapper.listeners.pointerenter();
root.querySelectorAll("[role='tab']")[1].listeners.click();
assert.equal(wrapper.children[1].hidden, true, "switching tabs closes the previous tooltip");
assert.deepEqual(calls, ["A", "B"]);
assert.deepEqual(trends, ["A", "B"]);
assert.equal(root.querySelectorAll(".opportunities-extension-plan-detail-badges").length, 2);
app.consumptionPlansView.closeAccountPlansModal();
assert.equal(document.getElementById(app.contracts.ACCOUNT_PLANS_MODAL_ID).hidden, true);
assert.equal(document.listeners.get("pointerdown").size, 0);
});
test("a detail failure stays local and the next activation can retry and display the empty state", async () => {
const { app, root, document } = setup();
const panel = document.createElement("section");
const section = app.consumptionPlanDetailController.createPlanDetailSection({ subPlanNum: "A" });
panel.append(section);
root.append(panel);
app.authContentSession.requestConsumerTokenServiceOnce = async () => ({ accountPlans: { value: "synthetic-plan" } });
let count = 0;
app.platformContentMessages.sendRuntimeMessage = async () => ++count === 1
? { ok: false, error: "Synthetic detail failure" } : { ok: true, items: [] };
await app.consumptionPlanDetailController.loadPlanDetailForPanel(panel, { subPlanNum: "A" });
assert.equal(section.attributes["aria-busy"], "false");
assert.equal(section.children[0].attributes.role, "alert");
assert.equal(section.children[0].textContent, "Synthetic detail failure");
await app.consumptionPlanDetailController.loadPlanDetailForPanel(panel, { subPlanNum: "A" });
assert.equal(count, 2);
assert.equal(section.children[0].textContent, "No projection data");
});
test("plan projection CSS is scoped, has red/blue states in both themes and bounds only the tooltip scrolling", () => {
const css = fs.readFileSync(path.join(__dirname, "../src/features/consumption/plan-detail.css"), "utf8");
assert.match(css, /data-status="forfeit"/);
assert.match(css, /data-status="overage"/);
assert.match(css, /data-theme="dark"/);
assert.match(css, /position: fixed/);
const headerRule = css.match(/\.opportunities-extension-consumption-trend-header \{([^}]+)\}/)[1];
assert.match(headerRule, /justify-content: space-between/, "badges align with the chart's left edge while period controls stay right");
assert.match(css, /prefers-reduced-motion/);
assert.doesNotMatch(css, /\.opportunities-extension-account-plans-dialog\s*\{/);
});