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"); const fs = require("node:fs"); const path = require("node:path"); const authorizations = { accountSummary: { name: "Account-Ti-Authorization", value: "synthetic-summary-token" }, customerServiceRequests: { name: "lead-auth", value: "synthetic-sr-token" } }; const message = { type: "opportunitiesExtension.requestCustomerServiceRequest", partyNumber: "customer-test", authorizations }; const tokens = [ { tokenService: "ACCP-TS", uiaasHeader: { label: "Account-Ti-Authorization", value: "synthetic-summary-token" } }, { tokenService: "OCICONS-TS", uiaasHeader: { value: "synthetic-consumption-token" } }, { tokenService: "SAIB-TS", uiaasHeader: { value: "synthetic-install-token" } }, { tokenService: "SPA-ORANGE", uiaasHeader: { label: "lead-auth", value: "synthetic-sr-token" } } ]; for (const family of ["chrome", "firefox"]) { test(`${family}: SPA-ORANGE selection, summary id (not registryId), GET and pagination`, async () => { const calls = []; const h = loadBackground({ family, fetch: async (url, options) => { calls.push({ url, options }); if (url.endsWith("consumerTokenService")) return response(tokens); if (url.endsWith("accountsummary")) return response({ id: "account/id", registryId: "WRONG-ID" }); const offset = Number(new URL(url).searchParams.get("offset")); const count = offset === 0 ? 40 : 1; return response({ totalResults: 41, offset, count, limit: 40, objects: Array.from({ length: count }, (_, i) => ({ id: `SR-${offset + i}` })) }); } }); const auth = await h.dispatch({ type: "opportunitiesExtension.requestConsumerTokenService" }); assert.deepEqual(auth.authorizations.customerServiceRequests, { ...authorizations.customerServiceRequests, expires: "" }); const result = await h.dispatch({ ...message, authorizations: auth.authorizations }); assert.equal(result.ok, true); assert.equal(result.items.length, 41); assert.equal(calls[1].options.method, "POST"); assert.equal(calls[1].options.headers[authorizations.accountSummary.name], authorizations.accountSummary.value); assert.deepEqual(JSON.parse(calls[1].options.body), [{ key: "regId", value: "customer-test" }]); for (const [index, call] of calls.slice(2).entries()) { const url = new URL(call.url); assert.equal(url.pathname, "/oalcrm/web/api/provider-proxy/g2m-custsr-provider/service/oscSr/account%2Fid"); assert.deepEqual(JSON.parse(url.searchParams.get("customerSRFilter")), { hierarchy: "single", status: "open" }); assert.equal(url.searchParams.get("orderBy"), "creationDate"); assert.equal(url.searchParams.get("orderByMode"), "desc"); assert.equal(url.searchParams.get("limit"), "40"); assert.equal(url.searchParams.get("offset"), String(index * 40)); assert.equal(call.options.method, "GET"); assert.equal(call.options.body, undefined); assert.equal(call.options.credentials, "include"); assert.deepEqual(plain(call.options.headers), { Accept: "application/json", "custsr-auth": authorizations.customerServiceRequests.value }, "send the raw SPA-ORANGE value in custsr-auth, never lead-auth or Authorization/Bearer"); } assert.deepEqual(result.debugRequests.map(entry => entry.name), ["requestAccountSummary", "requestCustomerServiceRequest", "requestCustomerServiceRequest"]); assert.doesNotMatch(JSON.stringify(result.debugRequests), /synthetic-(sr|summary)-token/); assert.ok(result.debugRequests.every(entry => entry.httpStatus === 200)); assert.equal(result.debugRequests[1].requestHeaders["custsr-auth"], "(present)"); }); } test("SR authentication uses SPA-ORANGE value even when its header label is absent", async () => { let srCalls = 0; const h = loadBackground({ fetch: async (url, options) => { if (url.endsWith("consumerTokenService")) return response(tokens.map(token => token.tokenService === "SPA-ORANGE" ? { tokenService: "SPA-ORANGE", uiaasHeader: { value: "synthetic-sr-token" } } : token)); if (url.endsWith("accountsummary")) return response({ id: "account-test" }); srCalls += 1; assert.deepEqual(plain(options.headers), { Accept: "application/json", "custsr-auth": "synthetic-sr-token" }); return response({ totalResults: 0, offset: 0, count: 0, objects: [] }); } }); const auth = await h.dispatch({ type: "opportunitiesExtension.requestConsumerTokenService" }); const result = await h.dispatch({ ...message, authorizations: auth.authorizations }); assert.equal(result.ok, true); assert.equal(srCalls, 1); }); test("zero and exact page-size totals stop without an extra request", async () => { for (const total of [0, 40]) { let calls = 0; const h = loadBackground({ fetch: async url => { calls += 1; if (url.endsWith("accountsummary")) return response([{ id: "summary-id" }]); return response({ totalResults: total, offset: 0, count: total, objects: Array.from({ length: total }, (_, i) => ({ id: `SR-${i}` })) }); } }); const result = await h.dispatch(message); assert.equal(result.ok, true); assert.equal(result.items.length, total); assert.equal(calls, 2); } }); test("missing SPA-ORANGE stays optional for existing features and fails only the new action", async () => { const h = loadBackground({ fetch: async () => response(tokens.filter(token => token.tokenService !== "SPA-ORANGE")) }); const auth = await h.dispatch({ type: "opportunitiesExtension.requestConsumerTokenService" }); assert.equal(auth.ok, true); const result = await h.dispatch({ ...message, authorizations: auth.authorizations }); assert.equal(result.ok, false); assert.match(result.error, /SPA-ORANGE/); assert.equal(result.debugRequests.length, 0); }); test("missing identifiers, stalled pages, malformed collections, network and HTTP failures are visible", async () => { for (const kind of ["id", "stalled", "offset", "count", "total", "objects", "http", "json", "network"]) { const h = loadBackground({ fetch: async url => { if (url.endsWith("accountsummary")) return response(kind === "id" ? { registryId: "wrong" } : { id: "account-id" }); if (kind === "http") return response({}, 401); if (kind === "json") return new Response("fallback"); if (kind === "network") throw new Error("Synthetic network failure"); const page = { totalResults: 1, offset: 0, count: 0, objects: [] }; if (kind === "offset") page.offset = 40; if (kind === "count") page.count = 1; if (kind === "total") delete page.totalResults; if (kind === "objects") page.objects = null; return response(page); } }); const result = await h.dispatch(message); assert.equal(result.ok, false, kind); assert.ok(result.error, kind); assert.equal(result.debugRequests.length, kind === "id" ? 1 : 2); assert.doesNotMatch(JSON.stringify(result.debugRequests), /synthetic-(sr|summary)-token/); } }); 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 || ""; } 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(); } } const walk = node => [node, ...node.children.flatMap(walk)]; function setup() { 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 }); return { app, overlay, document }; } const customer = { CustomerAccount: { PartyNumber: "customer-test", PartyUniqueName: "Synthetic customer" } }; test("skeleton, exact field mapping, safe text, dates, badge, empty/error and focus states", () => { const { app, overlay, document } = setup(); const trigger = new Element("button", document); overlay.append(trigger); const view = app.serviceRequestsView.openServiceRequestsModal(customer, trigger); const modal = overlay.children[1]; assert.equal(modal.attributes["aria-busy"], "true"); assert.equal(walk(modal).filter(node => node.tag === "td").length, 88); assert.deepEqual(walk(modal).filter(node => node.tag === "th").map(node => node.textContent), [ "Name", "Registry ID", "SR Number", "Severity", "Status", "Substatus", "Product Name", "OS", "CSI", "Creation date", "Modified" ]); view.render([{ customer: { name: "", registryId: "reg-1" }, id: "SR-1", severity: "3", status: "Open", subStatus: "Work In Progress", component: { productName: null }, os: ": ", csi: "Unspecified", created: "2026-03-04", lastUpdated: "2026-09-17" }]); assert.equal(modal.attributes["aria-busy"], "false"); const cells = walk(modal).filter(node => node.tag === "td"); assert.equal(cells.length, 11); assert.equal(cells[0].textContent, ""); assert.equal(cells[0].children.length, 0); assert.equal(cells[1].textContent, "reg-1"); assert.equal(cells[2].textContent, "SR-1"); assert.equal(cells[4].children[0].dataset.status, "OPEN"); assert.equal(cells[5].textContent, "Work In Progress"); assert.equal(cells[6].textContent, "-"); assert.equal(cells[9].textContent, app.opportunitiesFormatters.formatOracleResponseDate("2026-03-04")); assert.equal(cells[10].textContent, app.opportunitiesFormatters.formatOracleResponseDate("2026-09-17")); modal.listeners.keydown({ key: "Tab", preventDefault() {} }); assert.equal(document.activeElement.attributes.role, "region"); view.render([]); assert.ok(walk(modal).some(node => node.textContent === "No open service requests found for this customer.")); view.render([], "Synthetic failure"); assert.ok(walk(modal).some(node => node.attributes.role === "alert" && node.textContent === "Synthetic failure")); assert.equal(app.serviceRequestsView.closeServiceRequestsModal(), true); assert.equal(document.activeElement, trigger); view.render([{ id: "Late" }]); assert.equal(overlay.children.length, 1); }); test("rapid clicks share the query; different customers and late responses stay isolated", async () => { const { app, overlay } = setup(); const requests = []; const logs = []; app.authContentSession.requestConsumerTokenServiceOnce = async () => authorizations; app.platformContentMessages.sendRuntimeMessage = request => new Promise(resolve => requests.push({ request, resolve })); app.debugContentLog.appendBackgroundRequestLog = entries => logs.push(entries); const button = app.serviceRequestsAction.createServiceRequestsButton(customer); const first = button.click(); assert.equal(overlay.children[0].attributes["aria-busy"], "true"); const repeated = button.click(); const otherButton = app.serviceRequestsAction.createServiceRequestsButton({ CustomerAccount: { PartyNumber: "other-customer" } }); const second = otherButton.click(); await new Promise(resolve => setImmediate(resolve)); assert.equal(requests.length, 2); assert.deepEqual(plain(requests[0].request), message); requests[1].resolve({ ok: true, items: [{ id: "Other SR" }], debugRequests: [] }); await second; requests[0].resolve({ ok: true, items: [{ id: "Wrong customer" }], debugRequests: [] }); await Promise.all([first, repeated]); assert.ok(walk(overlay.children[0]).some(node => node.textContent === "Other SR")); assert.equal(walk(overlay.children[0]).some(node => node.textContent === "Wrong customer"), false); assert.equal(logs.length, 2); const retry = button.click(); await new Promise(resolve => setImmediate(resolve)); assert.equal(requests.length, 3, "results are not cached after completion"); requests[2].resolve({ ok: false, error: "Synthetic request failure", debugRequests: [] }); await retry; assert.ok(walk(overlay.children[0]).some(node => node.textContent === "Synthetic request failure")); }); test("styles allocate 100 percent, support dark mode and reduced motion, without hiding values", () => { const css = fs.readFileSync(path.join(__dirname, "../src/features/service-requests/styles.css"), "utf8"); const overrides = [...css.matchAll(/\.service-requests-col-\w+ \{ width: (\d+)%; \}/g)].map(match => Number(match[1])); assert.equal(overrides.reduce((a, b) => a + b, 0) + (11 - overrides.length) * 7, 100); assert.match(css, /min-width: 1200px/); assert.match(css, /data-theme="dark"/); assert.match(css, /prefers-reduced-motion/); assert.doesNotMatch(css, /text-overflow: ellipsis/); });