const assert = require("node:assert/strict"); const { test } = require("node:test"); const { loadBackground, response, plain } = require("./support/characterization.cjs"); const { loadApplication } = require("./support/application.cjs"); test("installed base compact layout retains all columns and scopes toolbar sizing to its dialog", () => { const css = require("node:fs").readFileSync(require("node:path").join(__dirname, "../src/features/install-base/styles.css"), "utf8"); assert.match(css, /min-width: 1120px/); assert.match(css, /\.opportunities-extension-install-base-table\s*\{[^}]*font-size: 11px/); assert.match(css, /\.opportunities-extension-install-base-dialog \.opportunities-extension-customer-table-search\s*\{[^}]*height: 32px/); assert.match(css, /\.opportunities-extension-install-base-dialog \.opportunities-extension-account-plans-header h2\s*\{[^}]*font-size: 16px/); assert.match(css, /overflow-wrap: anywhere/); assert.doesNotMatch(css, /text-overflow: ellipsis|overflow: hidden|display: none/); }); const authorizations = { accountSummary: { name: "Account-Ti-Authorization", value: "synthetic-summary" }, installBase: { name: "synthetic-provider-header", value: "synthetic-installed" } }; const message = { type: "opportunitiesExtension.requestInstallBase", partyNumber: "customer-123", authorizations }; for (const family of ["chrome", "firefox"]) { test(`${family}: summary registryId, SAIB header, payload, pagination and protected diagnostics`, async () => { const calls = []; const h = loadBackground({ family, fetch: async (url, options) => { calls.push({ url, options }); if (url.endsWith("consumerTokenService")) return response([ { tokenService: "ACCP-TS", uiaasHeader: { label: "Account-Ti-Authorization", value: "synthetic-summary" } }, { tokenService: "OCICONS-TS", uiaasHeader: { label: "G2m-Authorization", value: "synthetic-consumption" } }, { tokenService: "SAIB-TS", uiaasHeader: { label: "synthetic-provider-header", value: "synthetic-installed" } } ]); if (url.endsWith("accountsummary")) return response({ id: "NOT-THE-REGISTRY", registryId: "registry/test" }); const offset = Number(new URL(url).searchParams.get("offset")); return response({ items: [{ productName: `Product ${offset}` }], offset, limit: 25, hasMore: offset === 0 }); } }); const tokens = await h.dispatch({ type: "opportunitiesExtension.requestConsumerTokenService" }); assert.deepEqual(tokens.authorizations.installBase, { ...authorizations.installBase, expires: "" }); const result = await h.dispatch({ ...message, authorizations: tokens.authorizations }); assert.equal(result.ok, true); assert.equal(result.items.length, 2); assert.deepEqual(JSON.parse(calls[1].options.body), [{ key: "regId", value: "customer-123" }]); assert.equal(calls[1].options.headers[authorizations.accountSummary.name], "synthetic-summary"); for (const [index, call] of calls.slice(2).entries()) { assert.ok(call.url.includes(`/account/registry%2Ftest?orderBy=currentSupport%3Adesc&limit=25&offset=${index}`)); assert.equal(call.options.method, "POST"); assert.equal(call.options.credentials, "include"); assert.equal(call.options.headers[authorizations.installBase.name], "synthetic-installed"); assert.deepEqual(JSON.parse(call.options.body), { text: "", accountHierarchy: "single", accountScope: "all", productScope: "all", installStatus: ["active", "inactive", "unsupported"], productIds: null, contractEndDate: { to: null, from: null }, contractStartDate: { to: null, from: null }, purchaseStartDate: { to: null, from: null }, registryIds: [], gecPartyNumbers: null }); } assert.deepEqual(result.debugRequests.map(entry => entry.name), ["requestAccountSummary", "requestInstallBase", "requestInstallBase"]); assert.doesNotMatch(JSON.stringify(result.debugRequests), /synthetic-installed|synthetic-summary/); }); } test("missing optional SAIB does not break existing consumer token flow", async () => { const h = loadBackground({ fetch: async () => response([ { tokenService: "ACCP-TS", uiaasHeader: { value: "synthetic-summary" } }, { tokenService: "OCICONS-TS", uiaasHeader: { value: "synthetic-consumption" } } ]) }); const tokens = await h.dispatch({ type: "opportunitiesExtension.requestConsumerTokenService" }); assert.equal(tokens.ok, true); const result = await h.dispatch({ ...message, authorizations: tokens.authorizations }); assert.equal(result.ok, false); assert.match(result.error, /SAIB-TS/); assert.equal(result.debugRequests.length, 0); }); test("invalid registry, pagination, HTTP, JSON and network failures preserve error/debug envelopes", async () => { for (const kind of ["registry", "stalled", "offset", "collection", "http", "json", "network"]) { const h = loadBackground({ fetch: async url => { if (url.endsWith("accountsummary")) return response(kind === "registry" ? { id: "wrong" } : { registryId: "test" }); if (kind === "network") throw new Error("Synthetic network failure"); if (kind === "http") return response({}, 403); if (kind === "json") return new Response("fallback"); if (kind === "collection") return response({ items: "invalid", hasMore: false }); return response({ items: [], offset: kind === "offset" ? 25 : 0, hasMore: true }); } }); const result = await h.dispatch(message); assert.equal(result.ok, false, kind); assert.ok(result.error, kind); assert.equal(result.debugRequests.length, kind === "registry" ? 1 : 2); assert.doesNotMatch(JSON.stringify(result.debugRequests), /synthetic-installed|synthetic-summary/); } }); class Element { constructor(tag, document) { this.tag = tag; this.document = document; this.children = []; this.dataset = {}; this.attributes = {}; this.listeners = {}; } append(...nodes) { nodes.forEach(node => { node.parent = this; }); this.children.push(...nodes); } set textContent(value) { this.text = value; this.children = []; } get textContent() { return (this.text || "") + this.children.map(child => child.textContent).join(""); } querySelectorAll(selector) { return walk(this).filter(node => node !== this && node.tag === selector); } querySelector(selector) { return walk(this).find(node => node.attributes["data-sort-key"] === selector.match(/data-sort-key="([^"]+)"/)?.[1]); } setAttribute(key, value) { this.attributes[key] = value; } addEventListener(key, fn) { this.listeners[key] = fn; } focus() { this.document.activeElement = this; } remove() { if (this.parent) this.parent.children = this.parent.children.filter(node => node !== this); this.parent = null; } get isConnected() { return Boolean(this.parent); } click() { return this.listeners.click(); } } function setup(localStorage) { const document = { createElement: tag => new Element(tag, document), createElementNS: (_, tag) => new Element(tag, document), getElementById: id => id === app.contracts.MODAL_ID ? overlay : null }; const overlay = new Element("main", document); const app = loadApplication("content", { document, Intl, Date, localStorage }); return { app, overlay, document }; } test("installBase sorts typed values and restores the selected header across application instances", () => { const saved = new Map(); const storage = { getItem: key => saved.get(key), setItem: (key, value) => saved.set(key, value) }; const items = [{ productName: "Ten", quantity: 10 }, { productName: "Two", quantity: 2 }] ; const first = setup(storage); first.app.installBaseView.openInstallBaseModal(customer).render(items); walk(first.overlay).find(node => node.attributes["data-sort-key"] === "quantity").click(); const second = setup(storage); second.app.installBaseView.openInstallBaseModal(customer).render(items); const rows = walk(second.overlay).find(node => node.tag === "tbody").children; assert.equal(rows[0].children[7].textContent, "2"); assert.equal(walk(second.overlay).find(node => node.attributes["data-sort-key"] === "quantity").parent.attributes["aria-sort"], "ascending"); assert.equal(items[0].quantity, 10); }); const customer = { CustomerAccount: { PartyNumber: "customer-123", PartyUniqueName: "Synthetic customer" } }; const walk = node => [node, ...node.children.flatMap(walk)]; test("installed base searches every displayed column and formatted values, including text entered during loading", () => { const { app, overlay, document } = setup(); const view = app.installBaseView.openInstallBaseModal(customer); const modal = overlay.children[0]; const input = walk(modal).find(node => node.tag === "input"); const row = { partyName: "S\u00e3o Customer", productGroupName: "Group Alpha", productName: "Database Beta", currentSupport: 1234.56, orderNumber: "ORD-321", supportStatus: "ACTIVE", quantityType: "Processor", quantity: 7, supportStartDate: "2026-03-12", supportEndDate: "2027-04-13", licenseValue: 9876.54, shipDate: "2020-05-14", hiddenField: "secretword" }; input.value = "sao beta"; input.listeners.input(); assert.equal(modal.attributes["aria-busy"], "true"); view.render([row, { productName: "Other product" }]); const rows = () => walk(modal).filter(node => node.tag === "td").length / 12; assert.equal(rows(), 1); for (const query of [...Object.values(row).slice(0, 12), "1,234.56", "9,876.54", "12/03/2026", "13/04/2027", "14/05/2020"]) { input.value = String(query); input.listeners.input(); assert.equal(rows(), 1, String(query)); assert.equal(walk(modal).find(node => node.tag === "input"), input, "input is not replaced while typing"); } input.value = "secretword"; input.listeners.input(); assert.equal(rows(), 0); assert.ok(walk(modal).some(node => node.textContent.includes("No matching records."))); input.value = " "; input.listeners.input(); assert.equal(rows(), 2); input.focus(); modal.listeners.keydown({ key: "Tab", shiftKey: true, preventDefault() {} }); assert.equal(document.activeElement.tag, "button"); view.render([], "Synthetic failure"); input.value = "other"; input.listeners.input(); assert.ok(walk(modal).some(node => node.textContent === "Synthetic failure")); app.installBaseView.closeInstallBaseModal(); app.installBaseView.openInstallBaseModal(customer).render([row]); assert.equal(walk(overlay).filter(node => node.tag === "td").length, 12, "reopening starts without an invisible filter"); }); test("modal shows skeleton, renders only requested fields safely and ignores closed/replaced responses", () => { const { app, overlay, document } = setup(); const first = app.installBaseView.openInstallBaseModal(customer); const modal = overlay.children[0]; assert.equal(modal.attributes["aria-busy"], "true"); assert.equal(walk(modal).filter(node => node.tag === "th").length, 12); const second = app.installBaseView.openInstallBaseModal(customer); first.render([{ productName: "Stale product" }]); assert.equal(overlay.children[0].attributes["aria-busy"], "true"); second.render([{ productName: "", supportStatus: "ACTIVE", quantity: 0, currentSupport: 1234.56, licenseValue: 0, supportStartDate: "2026-03-12", secret: "not-visible" }]); const current = overlay.children[0]; const cells = walk(current).filter(node => node.tag === "td"); assert.equal(cells.length, 12); assert.equal(cells[2].textContent, ""); assert.equal(cells[2].children.length, 0); assert.equal(cells[3].textContent, "1,234.56"); assert.equal(cells[5].children[0].dataset.status, "ACTIVE"); assert.equal(cells[7].textContent, "0"); assert.equal(cells[10].textContent, "0.00"); assert.ok(cells[8].textContent.includes("2026")); current.listeners.keydown({ key: "Tab", preventDefault() {} }); assert.equal(document.activeElement.type, "search"); current.listeners.keydown({ key: "Tab", preventDefault() {} }); assert.equal(document.activeElement.attributes.role, "region"); assert.equal(app.installBaseView.closeInstallBaseModal(), true); second.render([], "Late error"); assert.equal(overlay.children.length, 0); assert.equal(app.installBaseView.closeInstallBaseModal(), false); }); test("action opens before auth, deduplicates in-flight customer queries, and allows retry after failure", async () => { const { app, overlay } = setup(); let resolveAuth; let calls = 0; const logs = []; app.authContentSession.requestConsumerTokenServiceOnce = () => new Promise(resolve => { resolveAuth = resolve; }); app.platformContentMessages.sendRuntimeMessage = async message => { calls += 1; assert.equal(message.type, "opportunitiesExtension.requestInstallBase"); assert.equal(message.partyNumber, "customer-123"); assert.deepEqual(plain(message.authorizations), authorizations); return { ok: false, error: "Synthetic failure", debugRequests: ["debug"] }; }; app.debugContentLog.appendBackgroundRequestLog = entries => logs.push(entries); const button = app.installBaseAction.createInstallBaseButton(customer); const first = button.click(); assert.equal(overlay.children[0].attributes["aria-busy"], "true"); const second = button.click(); resolveAuth(authorizations); await Promise.all([first, second]); assert.equal(calls, 1); assert.equal(logs.length, 1); assert.ok(walk(overlay.children[0]).some(node => node.textContent === "Synthetic failure")); const retry = button.click(); resolveAuth(authorizations); await retry; assert.equal(calls, 2); });