Files
bitvid/tests/unit/ui/videoModalController.test.mjs
T
google-labs-jules[bot]andPR0M3TH3AN 9b95a38d34 refactor(ai): extract video modal events to controller (refactor-agent)
Extracts video modal event handling logic from `js/app.js` to
`js/ui/videoModalController.js`.

- Moves `_bindVideoModalEvents` logic to `VideoModalController.bindEvents`.
- Adds unit tests for the controller.
- Follows AGENTS.md refactor guidelines.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-15 01:10:47 +00:00

150 lines
4.5 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
// Mock globals before importing modules that might use them
if (!globalThis.window) {
globalThis.window = {
addEventListener: () => {},
removeEventListener: () => {},
};
}
if (!globalThis.navigator) {
globalThis.navigator = {
userAgent: "node",
clipboard: {
writeText: async () => {},
}
};
}
if (!globalThis.localStorage) {
globalThis.localStorage = {
getItem: () => null,
setItem: () => {},
};
}
// Now import the controller
import VideoModalController from "../../../js/ui/videoModalController.js";
test("VideoModalController bindEvents attaches listeners", (t) => {
const listeners = {};
const mockVideoModal = {
addEventListener: (event, handler) => {
listeners[event] = handler;
},
getRoot: () => ({}),
};
const controller = new VideoModalController({
getVideoModal: () => mockVideoModal,
callbacks: {
attachMoreMenuHandlers: () => {},
}
});
controller.bindEvents();
assert.ok(listeners["video:share-nostr"], "should attach share handler");
assert.ok(listeners["video:copy-cdn"], "should attach copy-cdn handler");
assert.ok(listeners["video:copy-magnet"], "should attach copy-magnet handler");
assert.ok(listeners["playback:switch-source"], "should attach switch-source handler");
});
test("VideoModalController handleShareNostr triggers callback", (t) => {
let called = false;
let args = null;
const controller = new VideoModalController({
getVideoModal: () => ({}),
callbacks: {
openShareNostrModal: (opts) => {
called = true;
args = opts;
},
},
});
controller.handleShareNostr({ detail: { video: { id: "1" } } });
assert.equal(called, true);
assert.equal(args.video.id, "1");
});
test("VideoModalController handleCopyCdn triggers clipboard write", async (t) => {
let clipboardText = "";
if (!globalThis.navigator.clipboard) {
Object.defineProperty(globalThis.navigator, 'clipboard', {
value: {},
writable: true,
configurable: true
});
}
const originalWriteText = globalThis.navigator.clipboard.writeText;
globalThis.navigator.clipboard.writeText = async (text) => {
clipboardText = text;
};
t.after(() => {
if (originalWriteText) {
globalThis.navigator.clipboard.writeText = originalWriteText;
} else {
delete globalThis.navigator.clipboard.writeText;
}
});
let successCalled = false;
const controller = new VideoModalController({
getVideoModal: () => ({}),
callbacks: {
getCurrentVideo: () => ({ url: "https://example.com/video.mp4" }),
showSuccess: () => { successCalled = true; }
}
});
await controller.handleCopyCdn({});
assert.equal(clipboardText, "https://example.com/video.mp4");
// We can't easily await the promise chain inside handleCopyCdn because it doesn't return the promise.
// But since the mock writeText is synchronous (returns resolved promise), the callback should fire in microtask.
// We can wait a bit.
await new Promise(resolve => setTimeout(resolve, 0));
assert.equal(successCalled, true);
});
test("VideoModalController handleCopyMagnet triggers callback", (t) => {
let called = false;
const controller = new VideoModalController({
getVideoModal: () => ({}),
callbacks: {
handleCopyMagnet: () => { called = true; }
}
});
// Manually trigger the logic that would be called by the event listener
if (controller.handleCopyMagnetCallback) {
controller.handleCopyMagnetCallback();
}
assert.equal(called, true);
});
test("VideoModalController handleSourceSwitch calls playVideoWithFallback", async (t) => {
let called = false;
let args = null;
const controller = new VideoModalController({
getVideoModal: () => ({}),
callbacks: {
getCurrentVideo: () => ({ id: "vid1", url: "http://url", magnet: "magnet:?" }),
getStreamHealthSnapshots: () => new Map([["vid1", { peers: 5 }]]),
getCachedUrlHealth: () => ({ status: "ok" }),
playVideoWithFallback: async (opts) => {
called = true;
args = opts;
}
}
});
controller.handleSourceSwitch({ detail: { source: "torrent", video: { id: "vid1" } } });
await new Promise(resolve => setTimeout(resolve, 0));
assert.equal(called, true);
assert.equal(args.forcedSource, "torrent");
});