mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 21:02:28 +00:00
update
This commit is contained in:
1 parent
000127f88c
commit
071d78dd6c
194 files changed
+28793
-3944
No files matched your search
@@ -0,0 +1,58 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import AppChromeController from "../../js/ui/appChromeController.js";
|
||||
|
||||
test("AppChromeController binds upload button when elements hydrate later", () => {
|
||||
const dom = new JSDOM(`<!DOCTYPE html><body><button id="upload"></button></body>`, {
|
||||
url: "https://example.com",
|
||||
});
|
||||
const { window } = dom;
|
||||
|
||||
const previousWindow = global.window;
|
||||
const previousDocument = global.document;
|
||||
|
||||
global.window = window;
|
||||
global.document = window.document;
|
||||
|
||||
try {
|
||||
const openCalls = [];
|
||||
const controller = new AppChromeController({
|
||||
callbacks: {
|
||||
openUploadModal: (detail) => {
|
||||
openCalls.push(detail);
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
error: () => {},
|
||||
log: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
controller.initialize();
|
||||
|
||||
const uploadButton = window.document.getElementById("upload");
|
||||
controller.setElements({ uploadButton });
|
||||
|
||||
uploadButton.dispatchEvent(new window.Event("click", { bubbles: true }));
|
||||
|
||||
assert.equal(openCalls.length, 1);
|
||||
assert.equal(openCalls[0]?.triggerElement, uploadButton);
|
||||
|
||||
const replacement = window.document.createElement("button");
|
||||
window.document.body.appendChild(replacement);
|
||||
|
||||
controller.setElements({ uploadButton: replacement });
|
||||
|
||||
uploadButton.dispatchEvent(new window.Event("click", { bubbles: true }));
|
||||
assert.equal(openCalls.length, 1);
|
||||
|
||||
replacement.dispatchEvent(new window.Event("click", { bubbles: true }));
|
||||
assert.equal(openCalls.length, 2);
|
||||
assert.equal(openCalls[1]?.triggerElement, replacement);
|
||||
} finally {
|
||||
global.window = previousWindow;
|
||||
global.document = previousDocument;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
import { ProfileModalController } from '../../js/ui/profileModalController.js'; import { userLogger } from '../../js/utils/logger.js'; describe('ProfileModalController - Direct Messages Error Handling', () => { let controller; let mockUserLoggerWarn; beforeEach(() => { jest.spyOn(userLogger, 'warn').mockImplementation(() => {}); mockUserLoggerWarn = jest.spyOn(userLogger, 'warn'); controller = new ProfileModalController({ /* minimal mocks for services/state */ }); jest.spyOn(controller, 'setMessagesLoadingState'); jest.spyOn(controller, 'setMessagesAnnouncement'); jest.spyOn(controller, 'updateMessagesReloadState'); controller.directMessagesCache = []; // Empty for error state }); afterEach(() => { jest.restoreAllMocks(); }); it('handles "no-decryptors" with specific message, no warn log, and enables reload', () => { const detail = { context: { reason: 'no-decryptors' }, error: new Error('Mock signer unlock error') }; controller.handleDirectMessagesError(detail); expect(controller.setMessagesLoadingState).toHaveBeenCalledWith('error', { message: 'Unlock your signer to read direct messages.' }); expect(mockUserLoggerWarn).not.toHaveBeenCalled(); // No log for this reason expect(controller.updateMessagesReloadState).toHaveBeenCalled(); // Reload affordance }); it('handles other reasons with generic message, warn log, and enables reload', () => { const detail = { context: { reason: 'relay-failed' }, // Mock real transport error error: new Error('Mock relay error') }; controller.handleDirectMessagesError(detail); expect(controller.setMessagesLoadingState).toHaveBeenCalledWith('error', { message: 'Unable to sync direct messages right now.' }); expect(mockUserLoggerWarn).toHaveBeenCalledWith( '[profileModal] Direct message sync issue detected:', detail.error ); // Warn for transport errors expect(controller.updateMessagesReloadState).toHaveBeenCalled(); // Reload affordance }); it('handles non-empty cache for "no-decryptors" (announcement only, no error state)', () => { controller.directMessagesCache = ['mock-message']; // Non-empty const detail = { context: { reason: 'no-decryptors' } }; controller.handleDirectMessagesError(detail); // Uses announcement, not error state expect(controller.setMessagesAnnouncement).toHaveBeenCalledWith('Unable to sync direct messages right now.'); expect(mockUserLoggerWarn).not.toHaveBeenCalled(); }); it('maintains behavior for active requests (ignores detail, no state change)', () => { controller.activeMessagesRequest = Symbol('active'); const detail = { context: { reason: 'no-decryptors' } }; controller.handleDirectMessagesError(detail); expect(controller.setMessagesLoadingState).not.toHaveBeenCalled(); expect(controller.setMessagesAnnouncement).not.toHaveBeenCalled(); expect(mockUserLoggerWarn).toHaveBeenCalled(); // Still logs for diagnostics }); }); // Adjust existing tests to cover "no-decryptors" in DM flows (e.g., error simulation in populateProfileMessages)
|
||||
@@ -59,6 +59,97 @@ test("VideoListView renders sorted popular tag pills", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("VideoListView trims popular tags to available width", () => {
|
||||
const { dom, document, listRoot, tagsRoot } = createViewDom();
|
||||
const view = new VideoListView({ document });
|
||||
view.mount(listRoot);
|
||||
view.setPopularTagsContainer(tagsRoot);
|
||||
|
||||
const prototype = dom.window.HTMLElement.prototype;
|
||||
const originalScrollWidth = Object.getOwnPropertyDescriptor(
|
||||
prototype,
|
||||
"scrollWidth",
|
||||
);
|
||||
|
||||
Object.defineProperty(tagsRoot, "clientWidth", {
|
||||
configurable: true,
|
||||
value: 160,
|
||||
});
|
||||
|
||||
Object.defineProperty(prototype, "scrollWidth", {
|
||||
configurable: true,
|
||||
get() {
|
||||
if (this.classList && this.classList.contains("video-tag-strip")) {
|
||||
return this.childElementCount * 80;
|
||||
}
|
||||
|
||||
if (originalScrollWidth && typeof originalScrollWidth.get === "function") {
|
||||
return originalScrollWidth.get.call(this);
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const videos = [
|
||||
{
|
||||
id: "v1",
|
||||
title: "First",
|
||||
pubkey: "pub1",
|
||||
created_at: 3,
|
||||
nip71: { hashtags: ["Nostr", "Video"] },
|
||||
},
|
||||
{
|
||||
id: "v2",
|
||||
title: "Second",
|
||||
pubkey: "pub2",
|
||||
created_at: 2,
|
||||
tags: [
|
||||
["t", "nostr"],
|
||||
["t", "Learning"],
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "v3",
|
||||
title: "Third",
|
||||
pubkey: "pub3",
|
||||
created_at: 1,
|
||||
nip71: { hashtags: ["video"] },
|
||||
},
|
||||
];
|
||||
|
||||
view.render(videos);
|
||||
|
||||
let buttons = tagsRoot.querySelectorAll("button");
|
||||
assert.equal(buttons.length, 2);
|
||||
assert.deepEqual(
|
||||
Array.from(buttons, (button) => button.dataset.tag),
|
||||
["Nostr", "Video"],
|
||||
);
|
||||
|
||||
Object.defineProperty(tagsRoot, "clientWidth", {
|
||||
configurable: true,
|
||||
value: 400,
|
||||
});
|
||||
|
||||
view.renderPopularTagStrip();
|
||||
|
||||
buttons = tagsRoot.querySelectorAll("button");
|
||||
assert.equal(buttons.length, 3);
|
||||
assert.deepEqual(
|
||||
Array.from(buttons, (button) => button.dataset.tag),
|
||||
["Nostr", "Video", "Learning"],
|
||||
);
|
||||
} finally {
|
||||
if (originalScrollWidth) {
|
||||
Object.defineProperty(prototype, "scrollWidth", originalScrollWidth);
|
||||
} else {
|
||||
delete prototype.scrollWidth;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("VideoListView applies tag preference variants to popular tags", () => {
|
||||
const { document, listRoot, tagsRoot } = createViewDom();
|
||||
const view = new VideoListView({ document });
|
||||
|
||||
Reference in new issue
Block a user