Some visual adjustments to the badges
This commit is contained in:
@@ -44,6 +44,41 @@ function setup(family, saved = {}, principal = "user@example.invalid") {
|
||||
}
|
||||
return { app, calls, load, saved, local };
|
||||
}
|
||||
|
||||
test("all cached opportunities load missing Actions despite filters, then reuse their completed cache", async () => {
|
||||
const h = setup("firefox");
|
||||
const stamp = Date.now() - 1000;
|
||||
const app = h.app;
|
||||
app.opportunitiesFilterState.tableSearch = "hide every opportunity";
|
||||
app.productsContentJobs.renderProductsProgress = () => {};
|
||||
const products = Array.from({ length: 7 }, (_, index) => [String(index + 1), {
|
||||
items: [{ RevnId: String(index + 1) }], cachedAt: stamp, fromCache: true
|
||||
}]);
|
||||
app.productsContentState.opptyProductsByNumber = new Map(products);
|
||||
await app.activityCache.write("actions", "1", stamp, []);
|
||||
await app.activityCache.write("actions", "2", stamp - 1, []);
|
||||
const queried = [];
|
||||
app.platformContentMessages.sendRuntimeMessage = async message => {
|
||||
queried.push(message.revnId);
|
||||
return { ok: true, actions: message.revnId === "3" ? [{ owner: app.authSessionState.principal }] : [], hasNext: false };
|
||||
};
|
||||
async function cycle(id) {
|
||||
app.productsContentState.currentProductsRequestId = id;
|
||||
app.productActivities.begin(id, "synthetic-token");
|
||||
await app.productActivities.complete(id);
|
||||
}
|
||||
await cycle("first");
|
||||
assert.deepEqual(queried.sort(), ["2", "3", "4", "5", "6", "7"]);
|
||||
assert.deepEqual(h.calls, ["sr"], "a missing SR cache must not block the Actions queue");
|
||||
for (const [number] of products) {
|
||||
assert.equal(app.opportunityWorkloadActions.getSummary(number).state, number === "3" ? "matched" : "missing");
|
||||
}
|
||||
app.opportunityWorkloadActions.reset();
|
||||
app.opportunityCrmServiceRequests.reset();
|
||||
await cycle("second");
|
||||
assert.equal(queried.length, 6, "valid matching action cache avoids all repeat requests");
|
||||
assert.deepEqual(h.calls, ["sr"]);
|
||||
});
|
||||
for (const family of ["chrome", "firefox"]) {
|
||||
test(`${family}: Current Quarter -> Next Quarter -> Current Quarter restores SR hours and filters without requests`, async () => {
|
||||
const h = setup(family);
|
||||
|
||||
@@ -56,7 +56,7 @@ test("consistent inline vectors replace raster icons without parentheses or lost
|
||||
assert.equal(count.title, "3 products");
|
||||
assert.equal(typeof count.listeners.click, "function");
|
||||
const ramp = app.productsDetails.createProductsRampBadge(products);
|
||||
checkIcon(ramp, "ramp", "1,2,3");
|
||||
checkIcon(ramp, "ramp", "1 | 2 | 3");
|
||||
assert.equal(ramp.title, "Ramp Months: 1, 2, 3");
|
||||
const missingRamp = app.productsDetails.createProductsRampBadge({ items: [{ RampMonths_c: null }] });
|
||||
checkIcon(missingRamp, "ramp", "N/D");
|
||||
|
||||
@@ -94,8 +94,8 @@ function setup() {
|
||||
app.opportunityWorkloadActions.loadProducts = (products, refresh, options) => { if (!options?.cacheOnly) calls.push(products.items[0]?.RevnId || "empty products"); };
|
||||
app.platformContentMessages.sendRuntimeMessage = async () => ({ ok: true });
|
||||
app.productsContentJobs.ensureProductsBackgroundMessageHandler();
|
||||
function start(token = "token") {
|
||||
app.productsContentJobs.requestOpptyProductsInBackground([{ OptyNumber: "A" }], token);
|
||||
function start(token = "token", numbers = ["A"]) {
|
||||
app.productsContentJobs.requestOpptyProductsInBackground(numbers.map(OptyNumber => ({ OptyNumber })), token);
|
||||
return app.productsContentState.currentProductsRequestId;
|
||||
}
|
||||
function progress(requestId, data) {
|
||||
@@ -147,19 +147,33 @@ test("partial failures use successful products only; closed, failed and empty cy
|
||||
assert.equal(h.calls.includes("closed"), false);
|
||||
});
|
||||
|
||||
test("cached products restore activities without network; mixed cycles fetch only fresh products", async () => {
|
||||
test("cached and mixed product cycles both allow activity cache misses to be fetched", async () => {
|
||||
const h = setup();
|
||||
const cached = h.start();
|
||||
h.progress(cached, { optyNumber: "A", items: [{ RevnId: "cached" }], fromCache: true });
|
||||
h.progress(cached, { state: "complete" });
|
||||
await flush();
|
||||
assert.deepEqual(h.calls, []);
|
||||
const mixed = h.start();
|
||||
assert.deepEqual(h.calls, ["SR token", "cached"]);
|
||||
h.calls.length = 0;
|
||||
const mixed = h.start("token", ["A", "B"]);
|
||||
h.progress(mixed, { optyNumber: "A", items: [{ RevnId: "cached" }], fromCache: true });
|
||||
h.progress(mixed, { optyNumber: "B", items: [{ RevnId: "fresh" }], fromCache: false });
|
||||
h.progress(mixed, { state: "complete" });
|
||||
await flush();
|
||||
assert.deepEqual(h.calls, ["SR token", "fresh"]);
|
||||
assert.deepEqual(h.calls, ["SR token", "cached", "fresh"]);
|
||||
});
|
||||
|
||||
test("activity scope includes hidden reference opportunities and excludes entries from other references", async () => {
|
||||
const h = setup();
|
||||
h.app.opportunitiesFilterState.tableSearch = "hide everything";
|
||||
const id = h.start("token", ["A", "B"]);
|
||||
h.progress(id, { optyNumber: "A", items: [{ RevnId: "1" }, { RevnId: "2" }] });
|
||||
h.progress(id, { optyNumber: "B", items: [{ RevnId: "3" }] });
|
||||
h.progress(id, { optyNumber: "OTHER_REFERENCE", items: [{ RevnId: "outside" }] });
|
||||
h.progress(id, { state: "complete" });
|
||||
await flush();
|
||||
assert.deepEqual(h.calls, ["SR token", "1", "3"]);
|
||||
assert.equal(h.calls.includes("outside"), false);
|
||||
});
|
||||
|
||||
test("reopening an existing modal does not reload products or reset/query activities", async () => {
|
||||
|
||||
@@ -64,6 +64,29 @@ function contentFixture(send) {
|
||||
return app;
|
||||
}
|
||||
|
||||
test("Actions progress counts product workloads, not opportunities, even with filters", async () => {
|
||||
const pending = new Map();
|
||||
const app = contentFixture(message => new Promise(resolve => pending.set(message.revnId, resolve)));
|
||||
const progress = [];
|
||||
app.activityProgress.update = (...args) => progress.push(args);
|
||||
app.opportunitiesFilterState.tableSearch = "hidden";
|
||||
const a = { items: [{ RevnId: "1" }, { RevnId: "2" }] };
|
||||
const b = { items: [{ RevnId: "3" }] };
|
||||
app.productsContentState.opptyProductsByNumber = new Map([["A", a], ["B", b]]);
|
||||
const loading = [app.opportunityWorkloadActions.loadProducts(a), app.opportunityWorkloadActions.loadProducts(b)];
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.equal(pending.size, 3);
|
||||
pending.get("1")({ ok: true, actions: [], hasNext: false });
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.deepEqual(progress.at(-1), ["actions", "running", 1, 3]);
|
||||
pending.get("2")({ ok: true, actions: [], hasNext: false });
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
assert.deepEqual(progress.at(-1), ["actions", "running", 2, 3]);
|
||||
pending.get("3")({ ok: true, actions: [], hasNext: false });
|
||||
await Promise.all(loading);
|
||||
assert.deepEqual(progress.at(-1), ["actions", "complete", 3, 3]);
|
||||
});
|
||||
|
||||
test("saved response updates the workload badge, tooltip and matching cache without fetching", async () => {
|
||||
const app = contentFixture(() => assert.fail("Save response must not trigger a GET"));
|
||||
const writes = [];
|
||||
|
||||
Reference in New Issue
Block a user