import test, { beforeEach, afterEach } from "node:test"; import assert from "node:assert/strict"; import { JSDOM } from "jsdom"; import ProfileModalController from "../../js/ui/profileModalController.js"; const TEMPLATE_HTML = `
`; let dom; let documentRef; let windowRef; let controller; let storedSettings; let moderationService; let callbackPayloads; beforeEach(async () => { dom = new JSDOM("", { pretendToBeVisual: true, url: "https://example.com", }); windowRef = dom.window; documentRef = windowRef.document; global.window = windowRef; global.document = documentRef; global.HTMLElement = windowRef.HTMLElement; global.HTMLInputElement = windowRef.HTMLInputElement; global.Event = windowRef.Event; global.fetch = async (url) => { assert.equal(url, "components/profile-modal.html"); return { ok: true, async text() { return TEMPLATE_HTML; }, }; }; storedSettings = { blurThreshold: 3, autoplayBlockThreshold: 2, trustedMuteHideThreshold: 1, trustedSpamHideThreshold: 3, }; moderationService = { getDefaultModerationSettings() { return { blurThreshold: 3, autoplayBlockThreshold: 2, trustedMuteHideThreshold: 1, trustedSpamHideThreshold: 3, }; }, getActiveModerationSettings() { return { ...storedSettings }; }, updateModerationSettings(partial = {}) { if (Object.prototype.hasOwnProperty.call(partial, "blurThreshold")) { const value = partial.blurThreshold; storedSettings.blurThreshold = value === null ? 3 : value; } if ( Object.prototype.hasOwnProperty.call( partial, "autoplayBlockThreshold", ) ) { const value = partial.autoplayBlockThreshold; storedSettings.autoplayBlockThreshold = value === null ? 2 : value; } if ( Object.prototype.hasOwnProperty.call( partial, "trustedMuteHideThreshold", ) ) { const value = partial.trustedMuteHideThreshold; storedSettings.trustedMuteHideThreshold = value === null ? 1 : value; } if ( Object.prototype.hasOwnProperty.call( partial, "trustedSpamHideThreshold", ) ) { const value = partial.trustedSpamHideThreshold; storedSettings.trustedSpamHideThreshold = value === null ? 3 : value; } return { ...storedSettings }; }, resetModerationSettings() { storedSettings = { blurThreshold: 3, autoplayBlockThreshold: 2, trustedMuteHideThreshold: 1, trustedSpamHideThreshold: 3, }; return { ...storedSettings }; }, }; callbackPayloads = []; controller = new ProfileModalController({ modalContainer: documentRef.body, removeTrackingScripts: () => {}, services: { relayManager: {}, userBlocks: {}, nostrClient: {}, accessControl: {}, getCurrentUserNpub: () => null, moderationSettings: moderationService, loadVideos: async () => undefined, onVideosShouldRefresh: async () => undefined, onAccessControlUpdated: async () => undefined, persistSavedProfiles: () => {}, watchHistoryService: {}, authService: {}, log: () => {}, closeAllMoreMenus: () => {}, }, state: { getSavedProfiles: () => [], setSavedProfiles: () => ({ changed: false, profiles: [] }), persistSavedProfiles: () => {}, getActivePubkey: () => null, setActivePubkey: () => null, }, callbacks: { onModerationSettingsChange: (payload) => callbackPayloads.push(payload), }, }); }); afterEach(() => { if (dom) { dom.window.close(); } dom = null; documentRef = null; windowRef = null; controller = null; storedSettings = null; moderationService = null; callbackPayloads = null; delete global.window; delete global.document; delete global.HTMLElement; delete global.HTMLInputElement; delete global.Event; delete global.fetch; }); test("moderation settings save updates service and disables control", async () => { await controller.load(); assert.equal(controller.moderationBlurInput.value, "3"); assert.equal(controller.moderationAutoplayInput.value, "2"); assert.equal(controller.moderationMuteHideInput.value, "1"); assert.equal(controller.moderationSpamHideInput.value, "3"); assert.equal(controller.moderationSaveButton.disabled, true); controller.moderationBlurInput.value = "5"; controller.moderationBlurInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationAutoplayInput.value = "4"; controller.moderationAutoplayInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationMuteHideInput.value = "2"; controller.moderationMuteHideInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationSpamHideInput.value = "6"; controller.moderationSpamHideInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); assert.equal(controller.moderationSaveButton.disabled, false); const result = await controller.handleModerationSettingsSave(); assert.equal(result.success, true); assert.deepEqual(storedSettings, { blurThreshold: 5, autoplayBlockThreshold: 4, trustedMuteHideThreshold: 2, trustedSpamHideThreshold: 6, }); assert.equal(controller.moderationSaveButton.disabled, true); assert.equal( controller.moderationStatusText.textContent, "Moderation settings saved.", ); assert.equal(callbackPayloads.length, 1); assert.deepEqual(callbackPayloads[0].settings, { blurThreshold: 5, autoplayBlockThreshold: 4, trustedMuteHideThreshold: 2, trustedSpamHideThreshold: 6, }); }); test("moderation reset restores defaults and clearing inputs uses defaults", async () => { storedSettings = { blurThreshold: 6, autoplayBlockThreshold: 4, trustedMuteHideThreshold: 3, trustedSpamHideThreshold: 5, }; await controller.load(); assert.equal(controller.moderationBlurInput.value, "6"); assert.equal(controller.moderationAutoplayInput.value, "4"); assert.equal(controller.moderationMuteHideInput.value, "3"); assert.equal(controller.moderationSpamHideInput.value, "5"); const resetContext = await controller.handleModerationSettingsReset(); assert.equal(resetContext.success, true); assert.deepEqual(storedSettings, { blurThreshold: 3, autoplayBlockThreshold: 2, trustedMuteHideThreshold: 1, trustedSpamHideThreshold: 3, }); assert.equal(controller.moderationBlurInput.value, "3"); assert.equal(controller.moderationAutoplayInput.value, "2"); assert.equal(controller.moderationMuteHideInput.value, "1"); assert.equal(controller.moderationSpamHideInput.value, "3"); assert.equal( controller.moderationStatusText.textContent, "Moderation defaults restored.", ); assert.equal(controller.moderationSaveButton.disabled, true); controller.moderationBlurInput.value = "7.8"; controller.moderationBlurInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationSpamHideInput.value = "8"; controller.moderationSpamHideInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); const saveResult = await controller.handleModerationSettingsSave(); assert.equal(saveResult.success, true); assert.deepEqual(storedSettings, { blurThreshold: 7, autoplayBlockThreshold: 2, trustedMuteHideThreshold: 1, trustedSpamHideThreshold: 8, }); controller.moderationBlurInput.value = ""; controller.moderationAutoplayInput.value = ""; controller.moderationMuteHideInput.value = ""; controller.moderationSpamHideInput.value = ""; controller.moderationBlurInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationAutoplayInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationMuteHideInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); controller.moderationSpamHideInput.dispatchEvent( new windowRef.Event("input", { bubbles: true }), ); assert.equal(controller.moderationSaveButton.disabled, false); const revertContext = await controller.handleModerationSettingsSave(); assert.equal(revertContext.success, true); assert.deepEqual(storedSettings, { blurThreshold: 3, autoplayBlockThreshold: 2, trustedMuteHideThreshold: 1, trustedSpamHideThreshold: 3, }); });