Files
bitvid/tests/app/similar-content.test.mjs
T

201 lines
4.6 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import Application from "../../js/app.js";
const computeSimilarContent = Application.prototype.computeSimilarContentCandidates;
function createAppStub({ videos = [] } = {}) {
const videosMap = new Map();
for (const video of videos) {
if (video && typeof video.id === "string") {
videosMap.set(video.id, video);
}
}
return {
videoListView: { currentVideos: videos },
videosMap,
getKnownVideoPostedAt(video) {
if (Number.isFinite(video?.rootCreatedAt)) {
return Math.floor(video.rootCreatedAt);
}
if (Number.isFinite(video?.created_at)) {
return Math.floor(video.created_at);
}
return null;
},
buildShareUrlFromEventId(id) {
return id ? `https://bitvid.invalid/${id}` : "";
},
formatTimeAgo(timestamp) {
return Number.isFinite(timestamp) ? `${timestamp}s` : "";
},
deriveVideoPointerInfo(video) {
if (video?.pointerInfo) {
return video.pointerInfo;
}
if (typeof video?.pointerKey === "string" && video.pointerKey.trim()) {
return { key: video.pointerKey.trim() };
}
return null;
},
isAuthorBlocked() {
return false;
},
};
}
function createVideo({
id,
pubkey = "author",
displayTags,
tags,
rootCreatedAt,
created_at,
pointerKey,
pointerInfo,
shareUrl,
isPrivate = false,
isNsfw = false,
} = {}) {
const video = {
id,
pubkey,
isPrivate,
isNsfw,
};
if (Array.isArray(displayTags)) {
video.displayTags = displayTags.slice();
}
if (Array.isArray(tags)) {
video.tags = tags.slice();
}
if (Number.isFinite(rootCreatedAt)) {
video.rootCreatedAt = Math.floor(rootCreatedAt);
}
if (Number.isFinite(created_at)) {
video.created_at = Math.floor(created_at);
}
if (typeof pointerKey === "string") {
video.pointerKey = pointerKey;
}
if (pointerInfo) {
video.pointerInfo = pointerInfo;
}
if (typeof shareUrl === "string") {
video.shareUrl = shareUrl;
}
return video;
}
test("orders similar videos by shared tags then timestamp", () => {
const activeVideo = createVideo({
id: "root-video",
displayTags: ["Art", "Music", "News"],
});
const candidateA = createVideo({
id: "video-a",
displayTags: ["art", "music"],
rootCreatedAt: 100,
});
const candidateB = createVideo({
id: "video-b",
displayTags: ["art"],
rootCreatedAt: 200,
});
const candidateC = createVideo({
id: "video-c",
displayTags: ["art", "music"],
rootCreatedAt: 50,
});
const app = createAppStub({
videos: [candidateA, candidateB, candidateC],
});
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
assert.equal(matches.length, 3);
assert.deepEqual(
matches.map((entry) => entry.video.id),
["video-a", "video-c", "video-b"],
);
assert.deepEqual(
matches.map((entry) => entry.sharedTagCount),
[2, 2, 1],
);
});
test("returns no matches when the active video lacks tags", () => {
const activeVideo = createVideo({
id: "root-video",
});
const candidate = createVideo({
id: "video-a",
displayTags: ["art"],
});
const app = createAppStub({ videos: [candidate] });
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
assert.deepEqual(matches, []);
});
test("skips candidates without tag metadata", () => {
const activeVideo = createVideo({
id: "root-video",
displayTags: ["art"],
});
const candidateWithTags = createVideo({
id: "video-a",
displayTags: ["art"],
});
const candidateWithoutTags = createVideo({
id: "video-b",
});
const app = createAppStub({
videos: [candidateWithTags, candidateWithoutTags],
});
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
assert.equal(matches.length, 1);
assert.equal(matches[0].video.id, "video-a");
});
test("filters NSFW and private videos when NSFW content is disabled", () => {
const activeVideo = createVideo({
id: "root-video",
displayTags: ["art"],
});
const publicVideo = createVideo({
id: "video-public",
displayTags: ["art"],
});
const nsfwVideo = createVideo({
id: "video-nsfw",
displayTags: ["art"],
isNsfw: true,
});
const privateVideo = createVideo({
id: "video-private",
displayTags: ["art"],
isPrivate: true,
});
const app = createAppStub({
videos: [publicVideo, nsfwVideo, privateVideo],
});
const matches = computeSimilarContent.call(app, { activeVideo, maxItems: 5 });
assert.deepEqual(
matches.map((entry) => entry.video.id),
["video-public"],
);
});