Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/improsocial/impro
An extensible Bluesky client for web impro.social
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445import { describe, it } from "node:test";import assert from "node:assert/strict";import { PluginPreferencesManager } from "/js/plugins/pluginPreferencesManager.js";import { Signal } from "/js/signals.js";
// A minimal fake of the Preferences object the manager interacts with.// The real Preferences clones on every write and the provider re-publishes// the result through $preferences, so dependent signals invalidate; the fake// mirrors that contract.class FakePreferences { constructor(state) { this.state = state; }
getInstalledPlugins() { return this.state.installedPlugins; }
setInstalledPlugins(plugins) { this.state.installedPlugins = plugins; // Return a fresh identity to mirror the real provider, which clones // preferences on every write so dependent signals invalidate. return new FakePreferences(this.state); }
getPluginSettings(pluginId) { return this.state.pluginSettings[pluginId]; }
setPluginSettings(pluginId, data) { this.state.pluginSettings[pluginId] = data; return this; }
clearPluginSettings(pluginId) { delete this.state.pluginSettings[pluginId]; return this; }}
// Mirrors a provider whose preferences haven't loaded yet: the signal reads// null (so the manager's signals read empty) until requirePreferences resolves.function makeUnloadedProvider({ installedPlugins = [] } = {}) { const state = { installedPlugins, pluginSettings: {} }; const preferences = new FakePreferences(state); const $preferences = new Signal.State(null); return { state, provider: { $preferences, requirePreferences: async () => { $preferences.set(preferences); return preferences; }, updatePreferences: async (prefs) => { $preferences.set(prefs); }, }, };}
function makeProvider({ installedPlugins = [], pluginSettings = {} } = {}) { const state = { installedPlugins, pluginSettings }; const preferences = new FakePreferences(state); const $preferences = new Signal.State(preferences); const saveCalls = []; return { state, preferences, saveCalls, provider: { $preferences, requirePreferences: () => preferences, updatePreferences: async (prefs) => { saveCalls.push(prefs); $preferences.set(prefs); }, }, };}
describe("installed plugins", () => { it("returns installed plugins from preferences", () => { const { provider } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.$installedPlugins.get(), [ { id: "a", enabled: true }, ]); });
it("setInstalledPlugins persists via updatePreferences", async () => { const { provider, saveCalls, state } = makeProvider(); const manager = new PluginPreferencesManager(provider); await manager.setInstalledPlugins([{ id: "a", enabled: true }]); assert.deepEqual(state.installedPlugins, [{ id: "a", enabled: true }]); assert.deepEqual(saveCalls.length, 1); assert.deepEqual(saveCalls[0].getInstalledPlugins(), [ { id: "a", enabled: true }, ]); });
it("$installedPlugin finds by id", () => { const { provider } = makeProvider({ installedPlugins: [ { id: "a", enabled: true }, { id: "b", enabled: false }, ], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.$installedPlugin.get("b"), { id: "b", enabled: false, }); assert.deepEqual(manager.$installedPlugin.get("missing"), null); });
it("$enabledPlugins filters to enabled entries", () => { const { provider } = makeProvider({ installedPlugins: [ { id: "a", enabled: true }, { id: "b", enabled: false }, { id: "c", enabled: true }, ], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.$enabledPlugins.get(), [ { id: "a", enabled: true }, { id: "c", enabled: true }, ]); });
it("derived signals recompute after a mutation", async () => { const { provider } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.$enabledPlugins.get(), [ { id: "a", enabled: true }, ]); assert.deepEqual(manager.$installedPlugin.get("b"), null);
await manager.addInstalledPlugin({ id: "b", enabled: false }); assert.deepEqual(manager.$installedPlugin.get("b"), { id: "b", enabled: false, }); assert.deepEqual(manager.$enabledPlugins.get(), [ { id: "a", enabled: true }, ]);
await manager.setPluginEnabled("b"); assert.deepEqual(manager.$enabledPlugins.get(), [ { id: "a", enabled: true }, { id: "b", enabled: true }, ]); });
it("addInstalledPlugin appends and saves", async () => { const { provider, state, saveCalls } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); await manager.addInstalledPlugin({ id: "b", enabled: false }); assert.deepEqual(state.installedPlugins, [ { id: "a", enabled: true }, { id: "b", enabled: false }, ]); assert.deepEqual(saveCalls.length, 1); });
it("removeInstalledPlugin removes by id and saves", async () => { const { provider, state, saveCalls } = makeProvider({ installedPlugins: [ { id: "a", enabled: true }, { id: "b", enabled: false }, ], }); const manager = new PluginPreferencesManager(provider); await manager.removeInstalledPlugin("a"); assert.deepEqual(state.installedPlugins, [{ id: "b", enabled: false }]); assert.deepEqual(saveCalls.length, 1); });
it("removeInstalledPlugin is a no-op when id is absent", async () => { const { provider, state } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); await manager.removeInstalledPlugin("missing"); assert.deepEqual(state.installedPlugins, [{ id: "a", enabled: true }]); });});
describe("updateInstalledPlugin", () => { it("applies updateFunc to the matching entry only", async () => { const { provider, state } = makeProvider({ installedPlugins: [ { id: "a", enabled: true, version: "1.0.0" }, { id: "b", enabled: false, version: "1.0.0" }, ], }); const manager = new PluginPreferencesManager(provider); await manager.updateInstalledPlugin("a", (entry) => ({ ...entry, version: "2.0.0", })); assert.deepEqual(state.installedPlugins, [ { id: "a", enabled: true, version: "2.0.0" }, { id: "b", enabled: false, version: "1.0.0" }, ]); });
it("throws when the plugin is not installed", async () => { const { provider } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); let caught = null; try { await manager.updateInstalledPlugin("missing", (entry) => entry); } catch (error) { caught = error; } assert(caught instanceof Error); assert(caught.message.includes("missing")); });
it("setPluginDisabled flips enabled to false", async () => { const { provider, state } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); await manager.setPluginDisabled("a"); assert.deepEqual(state.installedPlugins, [{ id: "a", enabled: false }]); });
it("setPluginEnabled flips enabled to true", async () => { const { provider, state } = makeProvider({ installedPlugins: [{ id: "a", enabled: false }], }); const manager = new PluginPreferencesManager(provider); await manager.setPluginEnabled("a"); assert.deepEqual(state.installedPlugins, [{ id: "a", enabled: true }]); });
it("setPluginsDisabled flips enabled to false for each given id in one save", async () => { const { provider, state, saveCalls } = makeProvider({ installedPlugins: [ { id: "a", enabled: true }, { id: "b", enabled: true }, { id: "c", enabled: true }, ], }); const manager = new PluginPreferencesManager(provider); await manager.setPluginsDisabled(["a", "c"]); assert.deepEqual(state.installedPlugins, [ { id: "a", enabled: false }, { id: "b", enabled: true }, { id: "c", enabled: false }, ]); assert.deepEqual(saveCalls.length, 1); });
it("setPluginsDisabled is a no-op (no save) for an empty list", async () => { const { provider, state, saveCalls } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); await manager.setPluginsDisabled([]); assert.deepEqual(state.installedPlugins, [{ id: "a", enabled: true }]); assert.deepEqual(saveCalls.length, 0); });
it("setPluginsDisabled throws when any id is not installed", async () => { const { provider, state } = makeProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider); let caught = null; try { await manager.setPluginsDisabled(["a", "missing"]); } catch (error) { caught = error; } assert(caught instanceof Error); assert(caught.message.includes("missing")); // Should not have mutated state when any id is invalid assert.deepEqual(state.installedPlugins, [{ id: "a", enabled: true }]); });});
describe("reads before preferences load", () => { it("getInstalledPlugins waits for the load instead of reading empty", async () => { const { provider } = makeUnloadedProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider);
assert.deepEqual(manager.$installedPlugins.get(), []); assert.deepEqual(await manager.getInstalledPlugins(), [ { id: "a", enabled: true }, ]); });
it("getEnabledPlugins waits for the load instead of reading empty", async () => { const { provider } = makeUnloadedProvider({ installedPlugins: [ { id: "a", enabled: true }, { id: "b", enabled: false }, ], }); const manager = new PluginPreferencesManager(provider);
const enabled = await manager.getEnabledPlugins(); assert.deepEqual( enabled.map((entry) => entry.id), ["a"], ); });
it("addInstalledPlugin keeps stored plugins it hasn't loaded yet", async () => { const { state, provider } = makeUnloadedProvider({ installedPlugins: [{ id: "a", enabled: true }], }); const manager = new PluginPreferencesManager(provider);
await manager.addInstalledPlugin({ id: "b", enabled: true });
assert.deepEqual( state.installedPlugins.map((entry) => entry.id), ["a", "b"], ); });
it("removeInstalledPlugin keeps stored plugins it hasn't loaded yet", async () => { const { state, provider } = makeUnloadedProvider({ installedPlugins: [ { id: "a", enabled: true }, { id: "b", enabled: true }, ], }); const manager = new PluginPreferencesManager(provider);
await manager.removeInstalledPlugin("a");
assert.deepEqual( state.installedPlugins.map((entry) => entry.id), ["b"], ); });});
describe("plugin settings", () => { it("readSettingsForPlugin returns stored settings", async () => { const { provider } = makeProvider({ pluginSettings: { a: { color: "red" } }, }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(await manager.readSettingsForPlugin("a"), { color: "red", }); assert.deepEqual(await manager.readSettingsForPlugin("missing"), undefined); });
it("writeSettingsForPlugin persists and saves", async () => { const { provider, state, saveCalls } = makeProvider(); const manager = new PluginPreferencesManager(provider); await manager.writeSettingsForPlugin("a", { color: "blue" }); assert.deepEqual(state.pluginSettings, { a: { color: "blue" } }); assert.deepEqual(saveCalls.length, 1); });
it("clearSettingsForPlugin removes settings and saves", async () => { const { provider, state, saveCalls } = makeProvider({ pluginSettings: { a: { color: "blue" }, b: { count: 2 } }, }); const manager = new PluginPreferencesManager(provider); await manager.clearSettingsForPlugin("a"); assert.deepEqual(state.pluginSettings, { b: { count: 2 } }); assert.deepEqual(saveCalls.length, 1); });});
describe("getUserGrantedFetchOrigins", () => { it("normalizes and de-duplicates stored origins", () => { const { provider } = makeProvider({ installedPlugins: [ { id: "alpha", enabled: true, userGrantedFetchOrigins: [ "https://api.example.com/v1", "https://api.example.com/v2", "http://localhost:11434/api", ], }, ], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.getUserGrantedFetchOrigins("alpha"), [ "https://api.example.com/*", "http://localhost:11434/*", ]); });
// The preferences record is writable by anything holding the account's // credentials, so stored values are untrusted input it("drops anything that isn't a normalizable origin", () => { const { provider } = makeProvider({ installedPlugins: [ { id: "alpha", enabled: true, userGrantedFetchOrigins: [ "https://*/*", "http://evil.example.com/*", "not a url", 42, null, ], }, ], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.getUserGrantedFetchOrigins("alpha"), []); });
it("returns an empty list for a missing entry or malformed field", () => { const { provider } = makeProvider({ installedPlugins: [ { id: "alpha", enabled: true, userGrantedFetchOrigins: "https://a.com/*", }, ], }); const manager = new PluginPreferencesManager(provider); assert.deepEqual(manager.getUserGrantedFetchOrigins("alpha"), []); assert.deepEqual(manager.getUserGrantedFetchOrigins("missing"), []); });});