181 lines
10 KiB
JavaScript
181 lines
10 KiB
JavaScript
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: {}, append() {}, addEventListener() {}, setAttribute(key, value) { this.attributes[key] = value; } }) };
|
|
document.createElementNS = (_, tag) => document.createElement(tag);
|
|
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: "<script>synthetic</script>", ReportedHours_c: 0,
|
|
OpportunityName_Id_c: { OptyNumber: number }, Owner: { PersonFirstName: "Test", PersonLastName: "Owner",
|
|
PrimaryEmail: { EmailAddress: "test@example.invalid" } }, DoNotCallFlag: false });
|
|
|
|
test("SR badge shows reported hours, sums linked SRs and preserves missing values", async () => {
|
|
for (const [values, expected] of [[[4], "4h"], [[0], "0h"], [[2, "1.5"], "3.5h"], [[0.1, 0.2], "0.3h"], [[null], "N/A"], [[4, null], "N/A"], [[""], "N/A"], [["invalid"], "N/A"]]) {
|
|
const app = setup(async () => response({ items: values.map((value, index) => ({ ...sr("A", `SR-${index}`), ReportedHours_c: value })), hasMore: false }));
|
|
await app.opportunityCrmServiceRequests.load("synthetic", 1);
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("A").textContent, expected);
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("OTHER").textContent, "0h");
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("A").title, "Whether a Service Request has been created for the opportunities.");
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("OTHER").title, "Whether a Service Request has been created for the opportunities.");
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("A").attributes["data-state"], "matched");
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("OTHER").attributes["data-state"], "missing");
|
|
}
|
|
});
|
|
|
|
test("SR tooltip formats customer deadlines without timezone day shifts", async () => {
|
|
for (const [value, expected] of [["2026-05-14T00:00:00.000+0000", "14/05/2026"], [null, "N/A"], [undefined, "N/A"], ["", "N/A"]]) {
|
|
const app = setup(async () => response({ items: [{ ...sr("A"), CustomerDeadline_c: value }], hasMore: false }));
|
|
await app.opportunityCrmServiceRequests.load("synthetic", 1);
|
|
assert.ok(app.opportunityCrmServiceRequests.createBadge("A").attributes["aria-label"].includes(`Customer Deadline: ${expected}`));
|
|
}
|
|
});
|
|
|
|
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 and pagination after 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, 0, "CRM must not run before product loading");
|
|
return [{ OptyNumber: "TEST-1" }];
|
|
};
|
|
await app.opportunitiesController.requestOpportunities("synthetic-token", "Current Quarter", {}, 1);
|
|
assert.equal(states.at(-1).status, "ready");
|
|
await app.opportunityCrmServiceRequests.load("synthetic-token", 1);
|
|
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", "CustomerDeadline_c", "PrimaryPillarNew_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.equal(log.requestHeaders.Authorization, "Bearer synthetic-token");
|
|
assert.doesNotMatch(app.debugContentLog.formatHeaderBlock(log.requestHeaders), /synthetic-token/);
|
|
app.debugState.debugSecretsVisible = true;
|
|
assert.match(app.debugContentLog.formatHeaderBlock(log.requestHeaders), /Bearer synthetic-token/);
|
|
app.debugState.debugSecretsVisible = false;
|
|
assert.equal(app.debugContentView.getRequestCategory(log), "serviceRequests");
|
|
}
|
|
const found = app.opportunityCrmServiceRequests.createBadge("TEST-1");
|
|
assert.equal(found.textContent, "0h");
|
|
assert.equal(found.attributes["data-state"], "matched");
|
|
for (const detail of ["SR-0", "SR-1", "Reported Hours: 0", "Test Owner", "test@example.invalid", "<script>synthetic</script>"])
|
|
assert.ok(found.attributes["aria-label"].includes(detail));
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("OTHER").textContent, "0h");
|
|
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("<html>Not JSON</html>"),
|
|
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);
|
|
await app.opportunityCrmServiceRequests.load("token", 1);
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("TEST").textContent, "?");
|
|
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, "0h");
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("OLD").textContent, "0h");
|
|
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, "0h");
|
|
assert.equal(app.opportunityCrmServiceRequests.createBadge("B").textContent, "0h");
|
|
app.opportunityCrmServiceRequests.reset();
|
|
await app.opportunityCrmServiceRequests.load("token", 3);
|
|
assert.equal(calls, 3);
|
|
});
|