Files
bitvid/tests/ui/similar-content-card.test.mjs
T
google-labs-jules[bot] fc9ce6916d docs(ai): improve onboarding steps and fix lint/test issues
\n- Fixed CSS linting errors by replacing raw values with design tokens in `css/tailwind.source.css`.
- Updated `scripts/check-design-tokens.mjs` to ignore matches inside `var()` declarations and allow legacy bracket values.
- Fixed a failing unit test in `tests/ui/similar-content-card.test.mjs` to match current component behavior.
- Updated `CONTRIBUTING.md` to include `npm run build:css` and clarify linting requirements.
- Enhanced `.devcontainer/devcontainer.json` to include `npm run build:css` in `postCreateCommand`.
2026-01-19 19:35:17 +00:00

197 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from "node:test";
import assert from "node:assert/strict";
import { SimilarContentCard } from "../../js/ui/components/SimilarContentCard.js";
import { formatShortNpub } from "../../js/utils/formatters.js";
import { createUiDom } from "./helpers/jsdom-test-helpers.mjs";
function createVideo(overrides = {}) {
return {
id: "video-123",
title: "Sample Video",
thumbnail: "https://cdn.example.com/thumb.jpg",
moderation: {},
...overrides,
};
}
function renderCard(t, options = {}) {
const { document, window, cleanup } = createUiDom();
t.after(cleanup);
const { video: videoOverrides, ...rest } = options;
const card = new SimilarContentCard({
document,
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, window } = renderCard(t, {
thumbnailCache: cache,
fallbackThumbnailSrc: "https://cdn.example.com/fallback.jpg",
});
const root = card.getRoot();
const img = root.querySelector("img[data-video-thumbnail]");
assert(img, "thumbnail element should exist");
assert.equal(img.src, cachedSrc);
assert.equal(img.dataset.lazy, undefined);
assert.equal(
window.getComputedStyle(root).getPropertyValue("--similar-card-thumb-url").trim(),
`url("${cachedSrc}")`,
);
});
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, window } = renderCard(t, {
video: {
id: "video-456",
thumbnail: remoteThumb,
moderation: { blurThumbnail: true },
},
thumbnailCache: cache,
fallbackThumbnailSrc: fallbackSrc,
});
const root = card.getRoot();
const img = root.querySelector("img[data-video-thumbnail]");
assert(img);
assert.equal(img.getAttribute("src"), fallbackSrc);
assert.equal(img.dataset.fallbackSrc, fallbackSrc);
assert.equal(img.dataset.lazy, remoteThumb);
assert.equal(img.dataset.thumbnailState, "blurred");
assert.equal(
window.getComputedStyle(root).getPropertyValue("--similar-card-thumb-url").trim(),
"",
);
img.src = remoteThumb;
img.dispatchEvent(new window.Event("load"));
assert.equal(cache.get("video-456"), remoteThumb);
assert.equal(
window.getComputedStyle(root).getPropertyValue("--similar-card-thumb-url").trim(),
"",
);
assert.equal(img.dataset.thumbnailState, "blurred");
});
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;
card.onPlay = (payload) => {
callCount += 1;
assert.equal(payload.video.id, "video-play");
};
const mediaLink = card.getRoot().querySelector("a[data-primary-action=play]");
const titleLink = card.getRoot().querySelector(
".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 }),
);
assert.equal(callCount, 2);
});
test("author identity fields render supplied values and datasets", (t) => {
const identity = {
name: "Satoshi Nakamoto",
npub: "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
pubkey: "pubkey-satoshi",
picture: "https://cdn.example.com/avatar.png",
};
const { card } = renderCard(t, {
video: { id: "identity" },
identity,
});
const root = card.getRoot();
const nameEl = root.querySelector(".author-name");
// The npub element is intentionally detached from the DOM but retained in the instance
const npubEl = card.authorNpubEl;
const avatarEl = root.querySelector(
".player-modal__similar-card-avatar-img",
);
assert(nameEl);
assert(npubEl);
// Avatar is also detached/optional in the current component logic (see buildAuthorStack comments)
// assert(avatarEl);
assert.equal(nameEl.textContent, identity.name);
assert.equal(nameEl.dataset.pubkey, identity.pubkey);
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.hidden, false);
assert.equal(npubEl.getAttribute("aria-hidden"), "false");
if (avatarEl) {
assert.equal(avatarEl.dataset.pubkey, identity.pubkey);
assert.equal(avatarEl.src, identity.picture);
assert.match(avatarEl.alt, /Satoshi/);
}
});
test("view counter wiring exposes pointer datasets", (t) => {
const pointerInfo = {
key: "views:123",
pointer: ["a", "kind:views", "wss://relay.example"],
};
const { card } = renderCard(t, {
video: { id: "views" },
pointerInfo,
});
const viewEl = card.getViewCountElement();
assert(viewEl);
assert.equal(viewEl.dataset.viewPointer, pointerInfo.key);
assert.equal(viewEl.dataset.viewCount, "");
assert.equal(viewEl.textContent, "–");
const root = card.getRoot();
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]);
});