const assert = require("node:assert/strict"); const { test } = require("node:test"); const { loadApplication } = require("./support/application.cjs"); const response = data => new Response(JSON.stringify(data), { status: 200 }); function setup(fetch) { const document = { createElement: () => ({ attributes: {}, addEventListener() {}, setAttribute(key, value) { this.attributes[key] = value; } }) }; const app = loadApplication("content", { fetch, document, Intl, Date }); app.currentUser.allowsCrmActivities = () => true; app.debugContentView.renderDebugPanel = () => {}; app.opportunitiesQueryState.periodRequestVersion = 1; return app; } const sr = (number, id = "SR-1") => ({ SrNumber: id, Title: "", ReportedHours_c: 0, OpportunityName_Id_c: { OptyNumber: number }, Owner: { PersonFirstName: "Test", PersonLastName: "Owner", PrimaryEmail: { EmailAddress: "test@example.invalid" } }, DoNotCallFlag: false }); test("CRM fetch is called without the environment object as its receiver", async () => { let calls = 0; const app = setup(async function (url, options) { "use strict"; assert.equal(this, undefined, "Browser fetch must not receive the dependency container as this"); calls += 1; const { offset } = JSON.parse(options.body); return response({ items: [sr("TEST")], count: 1, offset, hasMore: offset === 0 }); }); const items = await app.fusionCrmServiceRequests.requestCrmServiceRequest("synthetic-token", 1); assert.equal(calls, 2); assert.equal(items.length, 2); }); test("CRM query preserves payload, paginates before opportunities, uses tokenrelay Bearer and logs each page", async () => { const calls = []; const app = setup(async (url, options) => { const payload = JSON.parse(options.body); calls.push({ url, options, payload }); return response({ items: [sr("TEST-1", `SR-${payload.offset}`)], count: 1, offset: payload.offset, hasMore: payload.offset === 0 }); }); const states = []; app.opportunitiesTable.setOpportunitiesTableState = state => states.push(state); app.productsContentJobs.renderProductsProgress = () => {}; app.productsContentJobs.requestOpptyProductsInBackground = () => {}; app.fusionOpportunities.requestOpportunitiesPage = async () => { assert.equal(calls.length, 2, "all CRM pages must finish first"); return [{ OptyNumber: "TEST-1" }]; }; await app.opportunitiesController.requestOpportunities("synthetic-token", "Current Quarter", {}, 1); assert.equal(states.at(-1).status, "ready"); assert.deepEqual(calls.map(call => call.payload.offset), [0, 1]); const { payload, options, url } = calls[0]; assert.equal(url, "https://eeho.fa.us2.oraclecloud.com/crmRestApi/searchResources/latest/custom-actions/queries"); assert.equal(options.method, "POST"); assert.equal(options.credentials, "include"); assert.equal(options.headers.Authorization, "Bearer synthetic-token"); assert.equal(options.headers["Content-Type"], "application/json"); assert.equal(options.headers.Origin, "https://eeho.fa.us2.oraclecloud.com"); assert.equal(options.headers.Preference, "transient"); assert.deepEqual(payload, { aggregationResults: false, applications: ["ORACLE-ISS-APP"], onlyData: true, entity: "CRMServiceRequest", copiedFrom: "queries/c521309c-1310-49a0-9723-da2eb1988205", q: { op: "$and", criteria: [ { op: "$in", attribute: "StatusCd", values: ["SVC_PENDING_DELIVERY"] }, { op: "$eq", attribute: "RecordSet", value: "ORA_SVC_MY_SR_TEAM_SRS" } ] }, keywords: null, fields: ["SrNumber", "OpportunityName_Id_c.OptyNumber", "ReportedHours_c", "_actions", "Title", "Owner.PersonFirstName", "Owner.PersonLastName", "Owner.PrimaryEmail.EmailAddress", "DoNotCallFlag", "DoNotEmailFlag"], sort: [{ attribute: "_score", direction: "descending" }], language: "en", keywordsFields: ["SrNumber", "ServiceNew_Id_c.RecordName", "CustomerAccount", "OpportunityName_Id_c", "OpportunityName_Id_c.OptyNumber", "Owner"], skipInValidFields: true, limit: 15, offset: 0 }); assert.equal(app.debugState.requestLog.length, 2); for (const log of app.debugState.requestLog) { assert.equal(log.name, "resquestCrmServiceRequest"); assert.equal(log.requestHeaders.Origin, "https://eeho.fa.us2.oraclecloud.com"); assert.equal(log.requestHeaders.Preference, "transient"); assert.equal(log.status, "success"); assert.ok(log.responseBody); assert.ok(log.durationMs >= 0); assert.doesNotMatch(log.requestHeaders.Authorization, /synthetic-token/); assert.equal(app.debugContentView.getRequestCategory(log), "serviceRequests"); } const found = app.opportunityCrmServiceRequests.createBadge("TEST-1"); assert.equal(found.textContent, "\u2705 SR"); assert.equal(found.attributes["data-state"], "matched"); for (const detail of ["SR-0", "SR-1", "Reported Hours: 0", "Test Owner", "test@example.invalid", ""]) assert.ok(found.attributes["aria-label"].includes(detail)); assert.equal(app.opportunityCrmServiceRequests.createBadge("OTHER").textContent, "\u26d4 SR"); assert.equal(app.opportunityCrmServiceRequests.createBadge(null), null); app.opportunityCrmServiceRequests.reset(); assert.equal(app.opportunityCrmServiceRequests.createBadge("TEST-1"), null); }); test("CRM failures do not mislabel missing SRs or block the opportunity list", async () => { for (const fetch of [ async () => { throw new Error("Synthetic network failure"); }, async () => new Response("Denied", { status: 401 }), async () => new Response("Forbidden", { status: 403 }), async () => new Response("Not JSON"), async () => response({ items: [] }), async () => response({ items: [], count: 0, hasMore: true }), async () => response({ items: [sr("TEST")], count: 1, offset: -1, hasMore: true }) ]) { const app = setup(fetch); app.productsContentJobs.renderProductsProgress = () => {}; app.productsContentJobs.requestOpptyProductsInBackground = () => {}; let loaded = false; app.opportunitiesTable.setOpportunitiesTableState = () => {}; app.fusionOpportunities.requestOpportunitiesPage = async () => { loaded = true; return []; }; await app.opportunitiesController.requestOpportunities("token", "Current Quarter", {}, 1); assert.equal(loaded, true); assert.equal(app.opportunityCrmServiceRequests.createBadge("TEST").textContent, "SR ?"); assert.equal(app.debugState.requestLog.at(-1).status, "failed"); } }); test("stale CRM results cannot overwrite a reopened modal or continue pagination", async () => { let resolveOld; const app = setup(() => new Promise(resolve => { resolveOld = resolve; })); const old = app.opportunityCrmServiceRequests.load("token", 1); app.opportunitiesQueryState.periodRequestVersion = 2; app.opportunityCrmServiceRequests.reset(); app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => [sr("NEW")]; await app.opportunityCrmServiceRequests.load("token", 2); resolveOld(response({ items: [sr("OLD")], hasMore: true, count: 1, offset: 0 })); await old; assert.equal(app.opportunityCrmServiceRequests.createBadge("NEW").textContent, "\u2705 SR"); assert.equal(app.opportunityCrmServiceRequests.createBadge("OLD").textContent, "\u26d4 SR"); assert.equal(app.debugState.requestLog.length, 1); }); test("Reference changes share pending pagination and reuse completed CRM results", async () => { let finish; let calls = 0; const app = setup(async () => { calls += 1; if (calls === 1) return new Promise(resolve => { finish = resolve; }); return response({ items: [sr("B")], hasMore: false }); }); const first = app.opportunityCrmServiceRequests.load("token", 1); app.opportunitiesQueryState.periodRequestVersion = 2; const second = app.opportunityCrmServiceRequests.load("token", 2); assert.equal(first, second); finish(response({ items: [sr("A")], count: 1, offset: 0, hasMore: true })); await second; app.opportunitiesQueryState.periodRequestVersion = 3; await app.opportunityCrmServiceRequests.load("token", 3); assert.equal(calls, 2); assert.equal(app.opportunityCrmServiceRequests.createBadge("A").textContent, "\u2705 SR"); assert.equal(app.opportunityCrmServiceRequests.createBadge("B").textContent, "\u2705 SR"); app.opportunityCrmServiceRequests.reset(); await app.opportunityCrmServiceRequests.load("token", 3); assert.equal(calls, 3); });