const assert = require("node:assert/strict"); const { test } = require("node:test"); const { loadApplication } = require("./support/application.cjs"); const flush = () => new Promise(resolve => setImmediate(resolve)); test("internal SR form validates Save, maps parent data, prevents duplicate posts and preserves failures", async () => { const h = setup(); h.app.currentUser.getUserId = () => "current@example.invalid"; h.app.opportunitiesTableState.opportunitiesTableState.items = [ { OptyNumber: "OTHER", Name: "Other opportunity", CustomerAccount: { PrimaryAddress: { CountryCode: "US" } } }, { OptyNumber: "OP1", Name: "", OptyId: 456, CountryCode: "CA", CustomerAccount: { PartyId: 789, Region_c_localizedValue: "LAD", CountryCode: "AR", PrimaryAddress: { CountryCode: "BR" } } } ]; h.app.debugContentView.renderDebugPanel = () => {}; h.app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => [{ SrId: "123", SrNumber: "SR123", CustomerDeadline_c: "2026-05-14T00:00:00.000+0000", PrimaryPillarNew_c: "Tech_Cloud_Infrastructure", Title: "", OpportunityName_Id_c: { OptyNumber: "OP1" } }]; const calls = []; h.app.platformContentMessages.sendRuntimeMessage = async message => { calls.push(message); return { ok: true, items: message.type.endsWith("requestLookupTypePlatform") ? [{ code: "OCI", name: "Oracle Cloud" }] : message.type.endsWith("requestInternalSrServices") ? [{ Id: "45", RecordName: "Review", Description_c: "Architecture review" }] : [] }; }; await h.app.opportunityCrmServiceRequests.load("synthetic", 1); const badge = h.app.opportunityCrmServiceRequests.createBadge("OP1"); h.modal.append(badge); badge.listeners.pointerenter(); await flush(); const add = h.walk(h.panel()).find(el => el.title === "Create Internal Service Request"); assert.ok(add); assert.equal(add.parent.children[2].title, "Copy SR number"); add.listeners.click({ preventDefault() {}, stopPropagation() {} }); await flush(); const nodes = h.walk(h.modal); const context = nodes.find(el => el.className === "opportunities-extension-action-form-context"); assert.deepEqual(context.children.map(el => el.textContent), ["SR Number", "SR123", "Opportunity", "", "Title", ""]); assert.equal(nodes.find(el => el.name === "customerDeadline").value, "14/05/2026"); assert.equal(nodes.find(el => el.name === "customerDeadline").readOnly, true); assert.equal(nodes.find(el => el.name === "customerDeadline").required, false); for (const name of ["title", "internalDeadline", "platform", "description", "services"]) { const input = nodes.find(el => el.name === name); assert.equal(input.required, true, name); assert.equal(input.attributes["aria-required"], "true", name); assert.match(input.parent.children[0].textContent, /\(required\)$/); } for (const name of ["platform", "services"]) { const input = nodes.find(el => el.id === `internal-sr-${name}-search`); assert.equal(input.required, true); assert.equal(input.attributes["aria-required"], "true"); } assert.equal(nodes.find(el => el.name === "internalDeadline").type, "date"); assert.equal(nodes.find(el => el.name === "platform").children[1].value, "OCI"); assert.equal(nodes.find(el => el.name === "services").children[1].attributes["data-description"], "Architecture review"); assert.equal(calls.at(-1).parentServiceId, "123"); assert.equal(calls.at(-1).emailAddress, "current@example.invalid"); const form = nodes.find(el => el.tag === "form"); assert.deepEqual(h.walk(form).filter(el => el.tag === "button").map(el => el.textContent), ["Save", "Cancel"]); const submit = () => form.listeners.submit({ preventDefault() {} }); const before = calls.length; await submit(); assert.equal(calls.length, before); assert.equal(nodes.find(el => el.name === "title").attributes["aria-invalid"], "true"); for (const [name, value] of Object.entries({ title: "New internal SR", internalDeadline: "2026-05-01", platform: "OCI", description: "Detailed request", services: "45" })) nodes.find(el => el.name === name).value = value; const posts = [], toasts = []; h.app.opportunitiesLinks.showCopyConfirmation = (message, isError = false) => toasts.push({ message, isError }); h.app.platformContentMessages.sendRuntimeMessage = message => new Promise(resolve => posts.push({ message, resolve })); const saving = submit(); await submit(); assert.equal(posts.length, 1); assert.equal(posts[0].message.type, "opportunitiesExtension.requestPostInternalServiceRequest"); assert.equal(posts[0].message.emailAddress, "current@example.invalid"); assert.deepEqual(JSON.parse(JSON.stringify(posts[0].message.payload)), { Title: "New internal SR", ServiceNew_Id_c: "45", ProblemDescription: "Detailed request", CustomerDeadline_c: "2026-05-14", TechPlatformLookUp_c: "OCI", ServiceRequest_Id_SRToInternalSR: "123", SvcQuestionsCollection_c: [], InternalDeadline_c: "2026-05-01" }); posts[0].resolve({ ok: false, httpStatus: 403, error: "Synthetic save failure" }); await saving; assert.equal(nodes.find(el => el.name === "title").value, "New internal SR"); assert.deepEqual(toasts, [{ message: "Synthetic save failure", isError: true }]); const retry = submit(); posts[1].resolve({ ok: true, result: { SrNumber: "ISR1" } }); await retry; assert.deepEqual(toasts.at(-1), { message: "Internal Service Request created successfully.", isError: false }); assert.ok(!h.walk(h.modal).includes(form)); }); test("internal SR uses Session and offers manual retry after 401 without automatic probes", async () => { const h = setup(); let renewals = 0; const sent = []; h.app.authSessionState.accessToken = "stale-token"; h.app.authTokenRelay.requestTokenRelay = async () => `fresh-${++renewals}`; h.app.debugContentView.renderDebugPanel = () => {}; h.app.platformContentMessages.sendRuntimeMessage = async message => { sent.push(message); return { ok: false, httpStatus: 401, error: "Unauthorized" }; }; h.app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => [{ SrId: "123", SrNumber: "SR123", OpportunityName_Id_c: { OptyNumber: "OP1" } }]; await h.app.opportunityCrmServiceRequests.load("synthetic", 1); const badge = h.app.opportunityCrmServiceRequests.createBadge("OP1"); h.modal.append(badge); badge.listeners.pointerenter(); await flush(); assert.equal(renewals, 0); assert.equal(sent.length, 1); assert.equal(sent[0].token, undefined); assert.ok(sent.every(item => item.type === "opportunitiesExtension.requestInternalServiceRequest")); assert.ok(h.walk(h.panel()).some(node => node.attributes.role === "alert")); const retry = h.walk(h.panel()).find(node => node.attributes["aria-label"] === "Retry loading Internal Service Requests"); assert.ok(retry); retry.listeners.click(); await flush(); assert.equal(sent.length, 2); }); function setup() { const document = { activeElement: null, listeners: new Map() }; const window = { innerWidth: 400, innerHeight: 600, listeners: new Map(), setTimeout, clearTimeout }; for (const target of [document, window]) { target.addEventListener = (key, fn) => target.listeners.set(key, fn); target.removeEventListener = key => target.listeners.delete(key); } const walk = node => [node, ...node.children.flatMap(walk)]; function element(tag) { return { tag, children: [], attributes: {}, listeners: {}, style: {}, textContent: "", append(...nodes) { nodes.forEach(node => { node.parent = this; this.children.push(node); }); }, remove() { this.parent.children = this.parent.children.filter(node => node !== this); this.parent = null; }, setAttribute(key, value) { this.attributes[key] = value; }, addEventListener(key, fn) { this.listeners[key] = fn; }, contains(target) { return walk(this).includes(target); }, get isConnected() { return this === modal || !!this.parent?.isConnected; }, getBoundingClientRect() { return { left: 350, bottom: 550, height: 250 }; }, querySelector() { return walk(this).find(node => node.tag === "a" || node.tag === "button"); }, focus() { document.activeElement = this; this.listeners.focus?.(); } }; } const modal = element("main"); Object.assign(document, { createElement: element, createElementNS: (_, tag) => element(tag), getElementById: id => id === "opportunities-extension-modal" ? modal : null }); const copied = []; const app = loadApplication("content", { document, window, setTimeout, clearTimeout, navigator: { clipboard: { writeText: async value => copied.push(value) } } }); app.currentUser.allowsCrmActivities = () => true; // Keep the copy test independent of the toast's own DOM/timer implementation. modal.querySelector = () => ({ classList: { toggle() {} } }); window.setTimeout = () => 0; app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => [ { SrId: "NOT-THE-URL-ID", SrNumber: "SR /?&1", Title: "", ReportedHours_c: 0, OpportunityName_Id_c: { OptyNumber: "OP1" } }, { SrNumber: "SR2", OpportunityName_Id_c: { OptyNumber: "OP1" } } ]; app.opportunitiesQueryState.periodRequestVersion = 1; const panel = () => modal.children.find(node => node.className === "opportunities-extension-sr-popover"); return { app, document, window, modal, walk, copied, panel }; } test("SR popover uses encoded SrNumber, safe details and existing copy control for every record", async () => { const { app, modal, walk, copied, panel } = setup(); await app.opportunityCrmServiceRequests.load("synthetic", 1); const badge = app.opportunityCrmServiceRequests.createBadge("OP1"); modal.append(badge); badge.listeners.pointerenter(); const nodes = walk(panel()); const links = nodes.filter(node => node.tag === "a"); assert.equal(links.length, 2); for (const link of links) { assert.equal(link.parent.children[0].textContent, "SR Number:"); assert.equal(link.parent.children[2].className, "opportunities-extension-copy-button"); } const details = nodes.filter(node => node.className === "opportunities-extension-sr-details"); assert.equal(details.length, 2); details.forEach(node => assert.doesNotMatch(node.textContent, /SR Number:|SR \/\?&1|SR2/)); assert.equal(links[0].href, "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/service/ec/container/sr/edit?srNumber=SR%20%2F%3F%261"); assert.equal(links[0].rel, "noopener noreferrer"); assert.equal(links[0].target, "_blank"); assert.ok(nodes.some(node => node.textContent.includes(""))); assert.equal(nodes.some(node => node.tag === "script"), false); const copies = nodes.filter(node => node.className === "opportunities-extension-copy-button"); assert.equal(copies.length, 2); assert.equal(copies[0].title, "Copy SR number"); copies[0].listeners.click({ preventDefault() {}, stopPropagation() {} }); await Promise.resolve(); assert.deepEqual(copied, ["SR /?&1"]); assert.equal(panel().style.width, "376px"); app.opportunityCrmServiceRequests.reset(); assert.equal(panel(), undefined); }); test("SR popover pins on click, closes on Escape/outside/scroll/reset and removes global listeners", async () => { const { app, modal, document, window, panel } = setup(); await app.opportunityCrmServiceRequests.load("synthetic", 1); const badge = app.opportunityCrmServiceRequests.createBadge("OP1"); modal.append(badge); badge.listeners.click({ stopPropagation() {} }); assert.equal(badge.attributes["aria-expanded"], "true"); assert.ok(panel().contains(document.activeElement)); document.listeners.get("keydown")({ key: "Escape", preventDefault() {}, stopPropagation() {} }); assert.equal(panel(), undefined); assert.equal(document.activeElement, badge); assert.equal(document.listeners.size, 0); assert.equal(window.listeners.size, 0); badge.listeners.pointerenter(); document.listeners.get("pointerdown")({ target: modal }); assert.equal(panel(), undefined); badge.listeners.pointerenter(); document.listeners.get("scroll")({ target: panel() }); assert.ok(panel(), "scroll inside the tooltip keeps it open"); document.listeners.get("scroll")({ target: modal }); assert.equal(panel(), undefined); badge.listeners.pointerenter(); app.opportunityCrmServiceRequests.reset(); assert.equal(panel(), undefined); assert.equal(document.listeners.size, 0); }); test("SR tooltip loads nested details with copy/link and empty text, discarding stale responses", async () => { const h = setup(); const calls = []; h.app.authSessionState.accessToken = "crm-token"; h.app.authTokenRelay.requestTokenRelay = async () => "fresh-crm-token"; h.app.debugContentView.renderDebugPanel = () => {}; h.app.platformContentMessages.sendRuntimeMessage = message => new Promise(resolve => calls.push({ message, resolve })); await h.app.opportunityCrmServiceRequests.load("synthetic", 1); const badge = h.app.opportunityCrmServiceRequests.createBadge("OP1"); h.modal.append(badge); badge.listeners.pointerenter(); await flush(); assert.equal(calls.length, 2); assert.equal(calls[0].message.token, undefined); assert.equal(calls[0].message.srId, "NOT-THE-URL-ID"); assert.ok(h.walk(h.panel()).some(node => node.textContent === "Loading SR details...")); calls[0].resolve({ ok: true, items: [{ SrId: 99, SrNumber: "SR &child", Title: "", ServiceNew_c: "Queue", LastUpdateDate: "2026-05-14T00:00:00.000+0000", OpenDate: "2026-04-01T23:30:00.000+0000" }] }); calls[1].resolve({ ok: true, items: [] }); await flush(); const nodes = h.walk(h.panel()); const link = nodes.find(node => node.tag === "a" && node.textContent === "SR &child"); assert.equal(link.parent.children[0].textContent, "Internal SR Number:"); assert.ok(link.href.endsWith("srNumber=SR%20%26child&srId=99")); assert.equal(link.parent.children[2].title, "Copy SR number"); assert.ok(nodes.some(node => node.textContent.includes("Service Queue: Queue"))); assert.ok(nodes.some(node => node.textContent.includes("LastUpdateDate: 14/05/2026"))); assert.ok(nodes.some(node => node.textContent.includes("OpenDate: 01/04/2026"))); assert.ok(nodes.some(node => node.textContent === "No further details.")); h.app.crmSrPopover?.close(); h.app.opportunityCrmServiceRequests.reset(); badge.listeners.pointerenter(); const oldPanel = h.panel(); await flush(); h.app.opportunityCrmServiceRequests.reset(); calls[2].resolve({ ok: true, items: [{ SrNumber: "STALE" }] }); calls[3].resolve({ ok: false, error: "Forbidden" }); await flush(); assert.equal(h.panel(), undefined); assert.ok(!h.walk(oldPanel).some(node => node.textContent === "STALE")); });