Files
opportunities-extension/tests/productsProgressFade.test.cjs

110 lines
3.9 KiB
JavaScript

const assert = require("node:assert/strict");
const { test } = require("node:test");
const { loadApplication } = require("./support/application.cjs");
function fixture(reduced = false) {
let now = 0, nextId = 0;
const timers = new Map();
const badge = { hidden: false, attributes: {}, setAttribute(key, value) { this.attributes[key] = value; } };
const timestamp = {};
const app = loadApplication("content", {
document: { getElementById: id => id === app.contracts.PRODUCTS_PROGRESS_ID ? badge : timestamp, querySelector: () => badge },
setTimeout: (fn, delay) => { const id = ++nextId; timers.set(id, { fn, at: now + delay }); return id; },
clearTimeout: id => timers.delete(id), matchMedia: () => ({ matches: reduced })
});
const render = state => {
app.productsContentState.productsProgress = { state, completed: 2, total: 2, failed: 0, cacheHits: 0 };
app.productsContentJobs.renderProductsProgress();
};
function advance(ms) {
const end = now + ms;
while (true) {
const entry = [...timers].filter(([, timer]) => timer.at <= end).sort((a, b) => a[1].at - b[1].at)[0];
if (!entry) break;
now = entry[1].at;
timers.delete(entry[0]);
entry[1].fn();
}
now = end;
}
app.productsContentState.currentProductsRequestId = "first";
app.productsContentState.productsLastCachedAt = 1;
return { app, badge, timestamp, render, advance, timers };
}
test("Products ready stays for five seconds then fades for 300ms before hidden", () => {
const h = fixture();
h.render("complete");
h.advance(4999);
assert.equal(h.badge.hidden, false);
assert.equal(h.badge.attributes["data-fading"], "false");
h.render("complete");
h.advance(1);
assert.equal(h.badge.attributes["data-fading"], "true");
assert.equal(h.badge.hidden, false);
h.advance(300);
assert.equal(h.badge.hidden, true);
h.render("complete");
assert.equal(h.badge.hidden, true, "rerenders do not restart the completion timer");
assert.equal(h.timestamp.hidden, false, "Last update remains visible");
});
test("new product jobs cancel a pending fade and errors remain visible", () => {
const h = fixture();
h.render("complete");
h.advance(5000);
h.app.productsContentState.currentProductsRequestId = "second";
h.render("running");
h.advance(10000);
assert.equal(h.badge.hidden, false);
assert.equal(h.badge.attributes["data-fading"], "false");
assert.equal(h.timers.size, 0);
for (const state of ["complete-with-errors", "error"]) {
h.render(state);
h.advance(10000);
assert.equal(h.badge.hidden, false);
}
h.render("complete");
h.advance(5300);
assert.equal(h.badge.hidden, true);
});
test("reduced motion hides at five seconds without an animated delay", () => {
const h = fixture(true);
h.render("complete");
h.advance(5000);
assert.equal(h.badge.hidden, true);
});
test("Authenticated fades after five seconds without restarting on repeated status updates", () => {
const h = fixture();
const update = () => h.app.contentModal.updateAuthBadge(h.app.contracts.AUTH_STATUS.authenticated);
update();
h.advance(4999);
assert.equal(h.badge.hidden, false);
update();
h.advance(1);
assert.equal(h.badge.attributes["data-fading"], "true");
h.advance(300);
assert.equal(h.badge.hidden, true);
update();
assert.equal(h.badge.hidden, true);
h.app.contentModal.updateAuthBadge(h.app.contracts.AUTH_STATUS.unauthenticated);
h.advance(10000);
assert.equal(h.badge.hidden, false);
assert.equal(h.badge.attributes["data-fading"], "false");
update();
h.advance(5000);
h.app.contentModal.updateAuthBadge(h.app.contracts.AUTH_STATUS.pending);
h.advance(300);
assert.equal(h.badge.hidden, false);
assert.equal(h.badge.textContent, "Authenticating");
});
test("Authenticated respects reduced motion", () => {
const h = fixture(true);
h.app.contentModal.updateAuthBadge(h.app.contracts.AUTH_STATUS.authenticated);
h.advance(5000);
assert.equal(h.badge.hidden, true);
});