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); });