Implemented the functionality to create Service Requests

This commit is contained in:
2026-10-05 16:30:29 -03:00
parent e9c295be91
commit 7a7143b6e7
24 changed files with 1579 additions and 46 deletions

View File

@@ -118,6 +118,129 @@ test("missing SR plus opens pillar form, handles retry, ignores closed requests
assert.equal(h.host.children.length, 0);
});
test("SR Save validates form, maps dynamic values, locks duplicate clicks and preserves failures", async () => {
const h = setup();
h.app.opportunitiesQueryState.periodRequestVersion = 1;
h.app.fusionCrmServiceRequests.requestCrmServiceRequest = async () => [];
h.app.debugContentView.renderDebugPanel = () => {};
await h.app.opportunityCrmServiceRequests.load("test", 1);
h.app.currentUser.getUserId = () => "current@example.invalid";
h.app.opportunitiesTableState.opportunitiesTableState.items = [{ OptyNumber: "B", CustomerAccount: { PartyId: 999, Region_c: "EMEA" } }, {
OptyNumber: "A", OptyId: 202, PartyId: 888, AccountPartyId: 777, Owner: { PartyId: 666 }, Region_c: "EMEA", RegionText_c: "LAD", Name: "Opportunity",
CustomerAccount: { PartyId: 100000000000101, PartyUniqueName: "Customer", Region_c: "APAC", PrimaryAddress: { CountryCode: "BR" } }
}];
const posts = [];
const notices = [];
h.app.opportunitiesLinks.showCopyConfirmation = (...args) => notices.push(args);
h.app.platformContentMessages.sendRuntimeMessage = message => {
if (message.type.endsWith("requestPostServiceRequest")) return new Promise(resolve => posts.push({ message, resolve }));
return Promise.resolve({ ok: true, items: message.type.endsWith("requestSrLookupOpportunityBusiness")
? [{ Id: "404", RecordName: "Opportunity Pursuit", Abstract_c: "Description" }] : [{ code: "CODE", name: "Label" }] });
};
const plus = h.app.opportunityCrmServiceRequests.createBadge("A").children.find(node => node.tag === "button");
plus.click();
await flush();
const save = () => h.nodes().find(node => node.tag === "button" && ["Save", "Preparing...", "Saving..."].includes(node.textContent));
const submit = () => h.form().listeners.submit({ preventDefault() {} });
assert.equal(save().disabled, false);
await submit();
assert.equal(posts.length, 0);
assert.ok(h.nodes().some(node => node.attributes["aria-invalid"] === "true"));
for (const name of ["primaryPillar", "language", "platform"]) h.nodes().find(node => node.name === name).value = "CODE";
h.nodes().find(node => node.name === "customerDeadline").value = "2027-01-31";
h.nodes().find(node => node.name === "description").value = "Dynamic description";
h.nodes().find(node => node.name === "service").checked = true;
const saving = submit();
await submit();
assert.equal(posts.length, 1);
assert.equal(save().textContent, "Preparing...");
assert.equal(save().disabled, true);
h.nodes().find(node => node.textContent === "Cancel").click();
assert.ok(h.form(), "cannot dismiss while Save is in flight");
const payload = posts[0].message.payload;
assert.equal(payload.AccountPartyId, 100000000000101, "uses only the selected item's CustomerAccount.PartyId");
assert.equal(payload.OpportunityName_Id_c, 202);
assert.equal(payload.ReportedByPartyId, undefined);
assert.equal(posts[0].message.emailAddress, "current@example.invalid");
assert.equal(payload.Title, "Opportunity Pursuit-A-Customer");
assert.equal(payload.ServiceNew_Id_c, "404");
assert.equal(payload.PrimaryPillarNew_c, "CODE");
assert.equal(payload.Language_c, "CODE");
assert.equal(payload.TechPlatformLookUp_c, "CODE");
assert.equal(payload.CountryText_c, "BR");
assert.equal(payload.RegionText_c, "APAC", "uses CustomerAccount.Region_c from the selected opportunity, not a root-level region or another opportunity");
assert.equal(payload.ProblemDescription, "Dynamic description");
assert.equal(payload.CustomerDeadline_c, "2027-01-31");
assert.equal(posts[0].message.phase, "prepare");
const preparedPayload = { ...payload, ReportedByPartyId: 303, SvcQuestionsCollection_c: [],
SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" };
const preparedHeaders = {
requestSrTokenCatalog: { Authorization: "Session", "Content-Type": "application/vnd.oracle.adf.action+json", Origin: "https://example.invalid" },
requestPostServiceRequest: { Authorization: "Session", "Content-Type": "application/vnd.oracle.adf.action+json", Origin: "https://example.invalid", "vb-proxy-header-x-oracle-ectoken": "(generated after confirmation)" }
};
posts[0].resolve({ ok: true, payload: preparedPayload, headers: preparedHeaders });
await flush();
const confirmation = () => h.nodes().find(node => node.className?.includes("opportunities-extension-sr-confirmation"));
const confirm = () => h.nodes().find(node => node.textContent === "Confirm Save");
assert.ok(confirmation());
const preview = h.nodes().find(node => node.tag === "pre");
assert.equal(preview.textContent, JSON.stringify(preparedPayload, null, 2));
const previews = h.nodes().filter(node => node.tag === "pre");
assert.equal(previews.length, 3);
assert.equal(previews[1].textContent, JSON.stringify(preparedHeaders.requestSrTokenCatalog, null, 2));
assert.equal(previews[2].textContent, JSON.stringify(preparedHeaders.requestPostServiceRequest, null, 2));
assert.ok(h.nodes().some(node => node.textContent === "x-appbuilder-client-id: not yet observed in SeaaS requests."));
assert.equal(h.form().parent.inert, true);
assert.equal(h.document.activeElement.textContent, "Cancel");
await submit();
assert.equal(posts.length, 1, "awaiting confirmation must never send a creation request");
confirm().focus();
confirmation().listeners.keydown({ key: "Tab", preventDefault() {}, stopPropagation() {} });
assert.equal(h.document.activeElement, preview);
h.nodes().filter(node => node.tag === "button" && node.textContent === "Cancel").at(-1).click();
await saving;
assert.equal(posts.length, 1, "Cancel never creates SR");
assert.equal(confirmation(), undefined);
assert.equal(h.form().parent.inert, false);
assert.equal(save().disabled, false);
assert.equal(h.nodes().find(node => node.name === "description").value, "Dynamic description");
assert.equal(h.document.activeElement, save());
assert.equal(notices.length, 0);
const escaped = submit();
posts[1].resolve({ ok: true, payload: preparedPayload });
await flush();
confirmation().listeners.keydown({ key: "Escape", preventDefault() {}, stopPropagation() {} });
await escaped;
assert.equal(posts.length, 2);
assert.ok(h.form(), "Escape closes only the confirmation");
const failed = submit();
posts[2].resolve({ ok: true, payload: preparedPayload });
await flush();
const confirmButton = confirm();
confirmButton.click();
confirmButton.click();
await flush();
assert.equal(posts.length, 4, "double confirm sends only one creation request");
assert.equal(posts[3].message.phase, "confirm");
assert.deepEqual(posts[3].message.payload, preparedPayload);
assert.equal(posts[3].message.payload.AccountPartyId, 100000000000101);
assert.equal(save().textContent, "Saving...");
posts[3].resolve({ ok: false, error: "Synthetic failure" });
await failed;
assert.equal(notices.at(-1)[1], true);
const retry = submit();
posts[4].resolve({ ok: true, payload: preparedPayload });
await flush();
confirm().click();
await flush();
posts[5].resolve({ ok: true, result: { SrId: 505, SrNumber: "SR505" } });
await retry;
assert.equal(h.form(), undefined);
assert.equal(notices.at(-1)[0], "Service Request saved successfully.");
});
test("only plus opens the nine-field form; a missing workload stays disabled without a save operation", () => {
const h = setup();
const badge = h.app.opportunityWorkloadActions.createBadge("A");