mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Diagnosis (live, verified): the zap-totals pipeline is correct end to end — a seeded receipt queries and sums to the right sats — but of 310 kind-9735 receipts reachable on the relays, ZERO reference any bitvid video, so every badge was legitimately hidden. That reads as "broken" and profile zaps to a Lightning address never attach to a video, so hidden-when-zero left the feature invisible in practice. Show the counter always, like the view counter it sits beside: "0 sats" until a receipt lands, then the summed total. Card binder and modal render no longer gate on sats>0; the badge is revealed on bind/open and re-hidden on modal close (no video open). Live-checked: 158/158 cards render the badge. Tests spec-corrected with integrity notes. Co-Authored-By: Claude Fable 5 <[email protected]>
90 lines
3.5 KiB
JavaScript
90 lines
3.5 KiB
JavaScript
// #47 follow-up: video cards show the zap total (sats, orange, right-aligned)
|
|
// next to the view count. The badge is ALWAYS visible once bound (mirroring the
|
|
// view counter — a zero is a real, informative value), showing "0 sats" until a
|
|
// receipt batch lands, then the summed total.
|
|
//
|
|
// test_integrity_note:
|
|
// change_type: ["spec_correction"]
|
|
// scenarios:
|
|
// - id: SCN-card-zap-badge
|
|
// given: "cards bound to pointers with zero then nonzero cached totals"
|
|
// when: "bind() runs and a later zapTotals change fires"
|
|
// then: "badge visible from bind showing 0 sats; updates to the summed total in place; detached cards pruned"
|
|
// observable_outcomes:
|
|
// - "badge textContent per state; wrapper visible after bind"
|
|
// determinism_controls:
|
|
// - "JSDOM; the real zapTotals singleton driven through its store test hook; scripted subscription-manager list double"
|
|
// anti_cheat_rationale:
|
|
// prevents: ["hard-coded return value", "over-mocking internal logic"]
|
|
// relaxation:
|
|
// did_relax_any_assertion: false
|
|
// if_true_explain_spec_basis: "hidden-when-zero replaced with always-visible (matches the view counter the feature sits beside; the maintainer wants a persistent counter). Equally strict: still asserts exact textContent per state."
|
|
|
|
import "./test-helpers/setup-localstorage.mjs";
|
|
import test from "node:test";
|
|
import { strict as assert } from "node:assert";
|
|
import { JSDOM } from "jsdom";
|
|
import { createZapTotalBinder } from "../js/ui/views/videoCardZapTotals.js";
|
|
import zapTotalsStore, {
|
|
initZapTotals,
|
|
} from "../js/zapTotals.js";
|
|
|
|
const A1 = "30078:aaaa:video-1";
|
|
|
|
function makeCard(doc) {
|
|
const card = doc.createElement("div");
|
|
card.innerHTML =
|
|
'<div class="video-card__zaps hidden"><span data-zap-total></span></div>';
|
|
doc.body.appendChild(card);
|
|
return card;
|
|
}
|
|
|
|
test("binder: zero stays hidden, receipts reveal and format, prune drops detached", async () => {
|
|
const dom = new JSDOM("<!DOCTYPE html><body></body>", { pretendToBeVisual: true });
|
|
const doc = dom.window.document;
|
|
|
|
// Drive the REAL singleton store: scripted receipts arrive via its batched
|
|
// fetch (subscription-manager list double), so the binder sees the same
|
|
// cache + change signal production uses.
|
|
let receipts = [];
|
|
initZapTotals({
|
|
nostrClient: {
|
|
relays: ["wss://relay.example"],
|
|
getSubscriptionManager: () => ({ list: async () => receipts }),
|
|
},
|
|
tools: { nip57: { getSatoshisAmountFromBolt11: () => NaN } },
|
|
});
|
|
|
|
const binder = createZapTotalBinder();
|
|
const card = makeCard(doc);
|
|
const badge = card.querySelector("[data-zap-total]");
|
|
const wrapper = card.querySelector(".video-card__zaps");
|
|
|
|
// Tag-style ARRAY pointer — the shape VideoListView actually passes.
|
|
const pointerInfo = { key: `a:${A1}`, pointer: ["a", A1] };
|
|
binder.bind(card, pointerInfo);
|
|
assert.equal(wrapper.classList.contains("hidden"), false, "visible from bind");
|
|
assert.match(badge.textContent, /^0 sats$/, "shows 0 sats until receipts arrive");
|
|
|
|
// A receipt batch lands: 2100 sats via the zap request's amount tag.
|
|
receipts = [
|
|
{
|
|
id: "r1",
|
|
kind: 9735,
|
|
tags: [
|
|
["a", A1],
|
|
["description", JSON.stringify({ tags: [["amount", "2100000"]] })],
|
|
],
|
|
},
|
|
];
|
|
await zapTotalsStore.flush();
|
|
|
|
assert.equal(wrapper.classList.contains("hidden"), false, "stays visible");
|
|
assert.match(badge.textContent, /2,?100 sats|2\.1K sats/);
|
|
|
|
// Detached cards are dropped by prune (no leak, no stale writes).
|
|
card.remove();
|
|
binder.prune();
|
|
binder.destroy();
|
|
});
|