diff --git a/tests/utils/linkPreviewSettings.test.mjs b/tests/utils/linkPreviewSettings.test.mjs new file mode 100644 index 00000000..252de889 --- /dev/null +++ b/tests/utils/linkPreviewSettings.test.mjs @@ -0,0 +1,195 @@ +import { describe, it, beforeEach, afterEach } from "node:test"; +import assert from "node:assert/strict"; +import "../test-setup.mjs"; + +// Ensure JSDOM Event constructors are available globally so the module uses them +globalThis.CustomEvent = window.CustomEvent; +globalThis.Event = window.Event; + +import { + getLinkPreviewSettings, + setLinkPreviewAutoFetch, + allowLinkPreviewDomain, + isLinkPreviewDomainAllowed, + subscribeToLinkPreviewSettings, +} from "../../js/utils/linkPreviewSettings.js"; + +const STORAGE_KEY = "bitvid:linkPreviewSettings:v1"; + +describe("linkPreviewSettings", () => { + beforeEach(() => { + window.localStorage.clear(); + }); + + afterEach(() => { + window.localStorage.clear(); + }); + + describe("getLinkPreviewSettings", () => { + it("returns defaults when storage is empty", () => { + const settings = getLinkPreviewSettings(); + assert.deepEqual(settings, { + autoFetchUnknownDomains: true, + allowedDomains: [], + }); + }); + + it("returns stored settings", () => { + const stored = { + autoFetchUnknownDomains: false, + allowedDomains: ["example.com"], + }; + window.localStorage.setItem(STORAGE_KEY, JSON.stringify(stored)); + const settings = getLinkPreviewSettings(); + assert.deepEqual(settings, stored); + }); + + it("returns defaults when storage is invalid", () => { + window.localStorage.setItem(STORAGE_KEY, "invalid-json"); + const settings = getLinkPreviewSettings(); + assert.deepEqual(settings, { + autoFetchUnknownDomains: true, + allowedDomains: [], + }); + }); + + it("sanitizes settings from storage", () => { + const stored = { + autoFetchUnknownDomains: "invalid-boolean", // Should default to true + allowedDomains: ["example.com", 123, null], // Should filter non-strings + }; + window.localStorage.setItem(STORAGE_KEY, JSON.stringify(stored)); + const settings = getLinkPreviewSettings(); + assert.equal(settings.autoFetchUnknownDomains, true); + assert.deepEqual(settings.allowedDomains, ["example.com"]); + }); + }); + + describe("setLinkPreviewAutoFetch", () => { + it("updates settings and persists to storage", () => { + const newSettings = setLinkPreviewAutoFetch(false); + assert.equal(newSettings.autoFetchUnknownDomains, false); + + const stored = JSON.parse(window.localStorage.getItem(STORAGE_KEY)); + assert.equal(stored.autoFetchUnknownDomains, false); + }); + + it("emits an event on change", (t) => { + let emittedSettings; + const handler = (e) => { + emittedSettings = e.detail.settings; + }; + window.addEventListener("bitvid:link-preview-settings", handler); + + setLinkPreviewAutoFetch(false); + + assert.ok(emittedSettings); + assert.equal(emittedSettings.autoFetchUnknownDomains, false); + + window.removeEventListener("bitvid:link-preview-settings", handler); + }); + }); + + describe("allowLinkPreviewDomain", () => { + it("adds a domain and persists to storage", () => { + const domain = "example.com"; + const newSettings = allowLinkPreviewDomain(domain); + assert.ok(newSettings.allowedDomains.includes(domain)); + + const stored = JSON.parse(window.localStorage.getItem(STORAGE_KEY)); + assert.ok(stored.allowedDomains.includes(domain)); + }); + + it("handles duplicates", () => { + allowLinkPreviewDomain("example.com"); + const newSettings = allowLinkPreviewDomain("example.com"); + assert.equal(newSettings.allowedDomains.length, 1); + }); + + it("normalizes domains", () => { + const domain = " EXAMPLE.COM "; + const newSettings = allowLinkPreviewDomain(domain); + assert.ok(newSettings.allowedDomains.includes("example.com")); + }); + + it("emits an event on change", (t) => { + let emittedSettings; + const handler = (e) => { + emittedSettings = e.detail.settings; + }; + window.addEventListener("bitvid:link-preview-settings", handler); + + allowLinkPreviewDomain("example.com"); + + assert.ok(emittedSettings); + assert.ok(emittedSettings.allowedDomains.includes("example.com")); + + window.removeEventListener("bitvid:link-preview-settings", handler); + }); + + it("respects silent option", (t) => { + let emitted = false; + const handler = () => { + emitted = true; + }; + window.addEventListener("bitvid:link-preview-settings", handler); + + allowLinkPreviewDomain("silent.com", { silent: true }); + + assert.equal(emitted, false); + + window.removeEventListener("bitvid:link-preview-settings", handler); + }); + }); + + describe("isLinkPreviewDomainAllowed", () => { + it("returns true for allowed domains", () => { + allowLinkPreviewDomain("example.com"); + assert.equal(isLinkPreviewDomainAllowed("example.com"), true); + }); + + it("returns false for disallowed domains", () => { + assert.equal(isLinkPreviewDomainAllowed("example.com"), false); + }); + + it("handles normalization", () => { + allowLinkPreviewDomain("example.com"); + assert.equal(isLinkPreviewDomainAllowed("EXAMPLE.COM"), true); + }); + + it("accepts settings object as second argument", () => { + const settings = { allowedDomains: ["manual.com"] }; + assert.equal(isLinkPreviewDomainAllowed("manual.com", settings), true); + }); + }); + + describe("subscribeToLinkPreviewSettings", () => { + it("calls callback on event emission", (t) => { + let receivedSettings; + const unsubscribe = subscribeToLinkPreviewSettings((e) => { + receivedSettings = e.detail.settings; + }); + + setLinkPreviewAutoFetch(false); + + assert.ok(receivedSettings); + assert.equal(receivedSettings.autoFetchUnknownDomains, false); + + unsubscribe(); + }); + + it("unsubscribes correctly", (t) => { + let callCount = 0; + const unsubscribe = subscribeToLinkPreviewSettings(() => { + callCount++; + }); + + setLinkPreviewAutoFetch(false); + assert.equal(callCount, 1); + + unsubscribe(); + setLinkPreviewAutoFetch(true); + assert.equal(callCount, 1); + }); + }); +});