mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 00:12:25 +00:00
test: cover SimilarContentCard behaviors
This commit is contained in:
1 parent
9e283c1c3a
commit
3b7fea6e7c
2 files changed
+81
-112
No files matched your search
@@ -0,0 +1,19 @@
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
export function createUiDom({
|
||||
html = "<!DOCTYPE html><html><body></body></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(),
|
||||
};
|
||||
}
|
||||
@@ -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("<!DOCTYPE html><html><body></body></html>", {
|
||||
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]);
|
||||
});
|
||||
Reference in new issue
Block a user