From 3b7fea6e7cd3575867c4807bb63debc8f66c8265 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:07:33 -0400 Subject: [PATCH] test: cover SimilarContentCard behaviors --- tests/ui/helpers/jsdom-test-helpers.mjs | 19 +++ tests/ui/similar-content-card.test.mjs | 174 +++++++++--------------- 2 files changed, 81 insertions(+), 112 deletions(-) create mode 100644 tests/ui/helpers/jsdom-test-helpers.mjs diff --git a/tests/ui/helpers/jsdom-test-helpers.mjs b/tests/ui/helpers/jsdom-test-helpers.mjs new file mode 100644 index 00000000..c769eead --- /dev/null +++ b/tests/ui/helpers/jsdom-test-helpers.mjs @@ -0,0 +1,19 @@ +import { JSDOM } from "jsdom"; + +export function createUiDom({ + html = "", + url = "https://bitvid.invalid/", + pretendToBeVisual = true, +} = {}) { + const dom = new JSDOM(html, { + url, + pretendToBeVisual, + }); + + const { window } = dom; + return { + window, + document: window.document, + cleanup: () => window.close(), + }; +} diff --git a/tests/ui/similar-content-card.test.mjs b/tests/ui/similar-content-card.test.mjs index 5bdd7182..d4cb9ba0 100644 --- a/tests/ui/similar-content-card.test.mjs +++ b/tests/ui/similar-content-card.test.mjs @@ -1,23 +1,9 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { JSDOM } from "jsdom"; import { SimilarContentCard } from "../../js/ui/components/SimilarContentCard.js"; import { formatShortNpub } from "../../js/utils/formatters.js"; - -function createDom() { - const dom = new JSDOM("", { - pretendToBeVisual: true, - url: "https://example.com/", - }); - const { document } = dom.window; - return { - dom, - document, - window: dom.window, - cleanup: () => dom.window.close(), - }; -} +import { createUiDom } from "./helpers/jsdom-test-helpers.mjs"; function createVideo(overrides = {}) { return { @@ -29,16 +15,26 @@ function createVideo(overrides = {}) { }; } -test("uses cached thumbnails without lazy-loading and preserves backdrop", (t) => { - const { document, cleanup } = createDom(); +function renderCard(t, options = {}) { + const { document, window, cleanup } = createUiDom(); t.after(cleanup); - const cachedSrc = "https://cdn.example.com/thumb.jpg"; - const cache = new Map([["video-123", cachedSrc]]); + const { video: videoOverrides, ...rest } = options; const card = new SimilarContentCard({ document, - video: createVideo(), + video: createVideo(videoOverrides), + ...rest, + }); + + return { card, document, window }; +} + +test("cached thumbnails reuse existing src without lazy-loading", (t) => { + const cachedSrc = "https://cdn.example.com/thumb.jpg"; + const cache = new Map([["video-123", cachedSrc]]); + + const { card } = renderCard(t, { thumbnailCache: cache, fallbackThumbnailSrc: "https://cdn.example.com/fallback.jpg", }); @@ -48,43 +44,39 @@ test("uses cached thumbnails without lazy-loading and preserves backdrop", (t) = assert(img, "thumbnail element should exist"); assert.equal(img.src, cachedSrc); - assert.equal(img.hasAttribute("data-lazy"), false); + assert.equal(img.dataset.lazy, undefined); assert.equal( root.style.getPropertyValue("--similar-card-thumb-url"), `url("${cachedSrc}")`, ); }); -test("lazy loads uncached thumbnails, caches on load, and keeps blur state", (t) => { - const { document, window, cleanup } = createDom(); - t.after(cleanup); - - const fallback = "https://cdn.example.com/fallback.jpg"; +test("uncached thumbnails use fallback, cache on load, and retain blur state", (t) => { + const fallbackSrc = "https://cdn.example.com/fallback.jpg"; const remoteThumb = "https://cdn.example.com/remote.jpg"; const cache = new Map(); - const card = new SimilarContentCard({ - document, - video: createVideo({ + const { card, window } = renderCard(t, { + video: { id: "video-456", thumbnail: remoteThumb, moderation: { blurThumbnail: true }, - }), + }, thumbnailCache: cache, - fallbackThumbnailSrc: fallback, + fallbackThumbnailSrc: fallbackSrc, }); const root = card.getRoot(); const img = root.querySelector("img[data-video-thumbnail]"); assert(img); - assert.equal(img.getAttribute("src"), fallback); + assert.equal(img.getAttribute("src"), fallbackSrc); + assert.equal(img.dataset.fallbackSrc, fallbackSrc); assert.equal(img.dataset.lazy, remoteThumb); - assert.equal(img.dataset.fallbackSrc, fallback); assert.equal(img.dataset.thumbnailState, "blurred"); assert.equal( root.style.getPropertyValue("--similar-card-thumb-url"), - `url("${fallback}")`, + `url("${fallbackSrc}")`, ); img.src = remoteThumb; @@ -98,63 +90,53 @@ test("lazy loads uncached thumbnails, caches on load, and keeps blur state", (t) assert.equal(img.dataset.thumbnailState, "blurred"); }); -test("primary clicks trigger onPlay, modifiers and right clicks do not", (t) => { - const { document, window, cleanup } = createDom(); - t.after(cleanup); - - const card = new SimilarContentCard({ - document, - video: createVideo({ id: "video-play" }), +test("primary clicks trigger onPlay while modifiers and right clicks do not", (t) => { + const { card, window } = renderCard(t, { + video: { id: "video-play" }, }); let callCount = 0; - let lastPayload = null; card.onPlay = (payload) => { callCount += 1; - lastPayload = payload; + assert.equal(payload.video.id, "video-play"); }; const mediaLink = card.getRoot().querySelector("a[data-primary-action=play]"); - assert(mediaLink); - - mediaLink.dispatchEvent( - new window.MouseEvent("click", { bubbles: true, button: 0 }) - ); - assert.equal(callCount, 1); - assert.equal(lastPayload?.video?.id, "video-play"); - - mediaLink.dispatchEvent( - new window.MouseEvent("click", { bubbles: true, button: 0, metaKey: true }) - ); - mediaLink.dispatchEvent( - new window.MouseEvent("click", { bubbles: true, button: 2 }) - ); - assert.equal(callCount, 1); - const titleLink = card.getRoot().querySelector( - ".player-modal__similar-card-title" + ".player-modal__similar-card-title", ); + assert(mediaLink); assert(titleLink); + + mediaLink.dispatchEvent( + new window.MouseEvent("click", { bubbles: true, button: 0 }), + ); + assert.equal(callCount, 1); + + mediaLink.dispatchEvent( + new window.MouseEvent("click", { bubbles: true, button: 0, metaKey: true }), + ); + mediaLink.dispatchEvent( + new window.MouseEvent("click", { bubbles: true, button: 2 }), + ); + assert.equal(callCount, 1); + titleLink.dispatchEvent( - new window.MouseEvent("click", { bubbles: true, button: 0 }) + new window.MouseEvent("click", { bubbles: true, button: 0 }), ); assert.equal(callCount, 2); }); -test("renders author identity fields with provided datasets", (t) => { - const { document, cleanup } = createDom(); - t.after(cleanup); - +test("author identity fields render supplied values and datasets", (t) => { const identity = { - name: "Satoshi", + name: "Satoshi Nakamoto", npub: "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", pubkey: "pubkey-satoshi", picture: "https://cdn.example.com/avatar.png", }; - const card = new SimilarContentCard({ - document, - video: createVideo({ id: "identity" }), + const { card } = renderCard(t, { + video: { id: "identity" }, identity, }); @@ -164,6 +146,7 @@ test("renders author identity fields with provided datasets", (t) => { const avatarEl = root.querySelector( ".player-modal__similar-card-avatar-img", ); + assert(nameEl); assert(npubEl); assert(avatarEl); @@ -174,27 +157,24 @@ test("renders author identity fields with provided datasets", (t) => { const expectedShort = formatShortNpub(identity.npub); assert.equal(npubEl.textContent, expectedShort); assert.equal(npubEl.dataset.pubkey, identity.pubkey); + assert.equal(npubEl.dataset.npub, identity.npub); assert.equal(npubEl.getAttribute("title"), identity.npub); - assert.equal(npubEl.getAttribute("aria-hidden"), "false"); assert.equal(npubEl.hidden, false); + assert.equal(npubEl.getAttribute("aria-hidden"), "false"); assert.equal(avatarEl.dataset.pubkey, identity.pubkey); assert.equal(avatarEl.src, identity.picture); assert.match(avatarEl.alt, /Satoshi/); }); -test("exposes view count element wired to pointer info", (t) => { - const { document, cleanup } = createDom(); - t.after(cleanup); - +test("view counter wiring exposes pointer datasets", (t) => { const pointerInfo = { key: "views:123", - pointer: ["a", "b", "wss://relay.example"], + pointer: ["a", "kind:views", "wss://relay.example"], }; - const card = new SimilarContentCard({ - document, - video: createVideo({ id: "views" }), + const { card } = renderCard(t, { + video: { id: "views" }, pointerInfo, }); @@ -203,40 +183,10 @@ test("exposes view count element wired to pointer info", (t) => { assert.equal(viewEl.dataset.viewPointer, pointerInfo.key); assert.equal(viewEl.dataset.viewCount, ""); assert.equal(viewEl.textContent, "– views"); -}); - -test("hides duplicate npub pill and falls back to default avatar", (t) => { - const { document, cleanup } = createDom(); - t.after(cleanup); - - const npub = "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; - - const card = new SimilarContentCard({ - document, - video: createVideo({ id: "duplicates" }), - identity: { npub }, - }); const root = card.getRoot(); - const nameEl = root.querySelector(".author-name"); - const npubEl = root.querySelector(".author-npub"); - const avatarEl = root.querySelector( - ".player-modal__similar-card-avatar-img", - ); - assert(nameEl); - assert(npubEl); - assert(avatarEl); - - const expectedShort = formatShortNpub(npub); - assert.equal(nameEl.textContent, expectedShort); - assert.equal(npubEl.textContent, expectedShort); - assert.equal(npubEl.hidden, true); - assert.equal(npubEl.getAttribute("aria-hidden"), "true"); - assert.equal(npubEl.hasAttribute("title"), false); - - assert.ok( - avatarEl.src.endsWith("/assets/svg/default-profile.svg"), - "avatar should fall back to default profile icon", - ); - assert.match(avatarEl.alt, /avatar$/i); + assert.equal(root.dataset.pointerKey, pointerInfo.key); + assert.equal(root.dataset.pointerType, pointerInfo.pointer[0]); + assert.equal(root.dataset.pointerValue, pointerInfo.pointer[1]); + assert.equal(root.dataset.pointerRelay, pointerInfo.pointer[2]); });