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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241import { describe, it, beforeEach, afterEach } from "node:test";import assert from "node:assert/strict";import { Preferences } from "/js/preferences.js";import { PreferencesProvider } from "/js/dataLayer/preferencesProvider.js";
describe("PreferencesProvider", () => { // The retry waits a second before its second attempt. const realSetTimeout = globalThis.setTimeout; beforeEach(() => { globalThis.setTimeout = (callback) => realSetTimeout(callback, 0); for (const toast of document.querySelectorAll('[data-testid="toast"]')) { toast.remove(); } }); afterEach(() => { globalThis.setTimeout = realSetTimeout; });
it("should fetch preferences when requirePreferences is called before fetch", async () => { let fetchCount = 0; const mockApi = { isAuthenticated: true, getPreferences: async () => { fetchCount++; return []; }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
const preferences = await provider.requirePreferences();
assert.deepEqual(preferences.obj, []); assert.deepEqual(fetchCount, 1); });
it("should not refetch once preferences are loaded", async () => { let fetchCount = 0; const mockApi = { isAuthenticated: true, getPreferences: async () => { fetchCount++; return []; }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
await provider.requirePreferences(); await provider.requirePreferences();
assert.deepEqual(fetchCount, 1); });
it("should share one fetch between concurrent requirePreferences calls", async () => { let fetchCount = 0; const mockApi = { isAuthenticated: true, getPreferences: async () => { fetchCount++; return []; }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
const [first, second] = await Promise.all([ provider.requirePreferences(), provider.requirePreferences(), ]);
assert.deepEqual(fetchCount, 1); assert(first === second); });
it("should retry once when the fetch fails", async () => { let attempts = 0; const mockApi = { isAuthenticated: true, getPreferences: async () => { attempts++; if (attempts === 1) throw new Error("500"); return []; }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
const preferences = await provider.requirePreferences();
assert.deepEqual(attempts, 2); assert.deepEqual(preferences.obj, []); });
it("should reject when both fetch attempts fail", async () => { let attempts = 0; const mockApi = { isAuthenticated: true, getPreferences: async () => { attempts++; throw new Error("500"); }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
await assert.rejects(() => provider.requirePreferences());
assert.deepEqual(attempts, 2); });
it("should not remember a failed fetch", async () => { let attempts = 0; const mockApi = { isAuthenticated: true, getPreferences: async () => { attempts++; throw new Error("500"); }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
await assert.rejects(() => provider.requirePreferences(), /500/); await assert.rejects(() => provider.requirePreferences(), /500/);
assert.deepEqual(attempts, 4); });
it("should recover once a later call succeeds", async () => { let shouldFail = true; const mockApi = { isAuthenticated: true, getPreferences: async () => { if (shouldFail) throw new Error("500"); return []; }, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
await assert.rejects(() => provider.requirePreferences());
shouldFail = false; const preferences = await provider.requirePreferences();
assert.deepEqual(preferences.obj, []); });
it("should create logged out preferences when not authenticated", async () => { const mockApi = { isAuthenticated: false }; const provider = new PreferencesProvider(mockApi);
await provider.fetchPreferences();
const preferences = await provider.requirePreferences(); assert.deepEqual(preferences.obj.length, 1); });
it("should fetch preferences from API when authenticated", async () => { const mockPreferencesObj = [ { $type: "app.bsky.actor.defs#savedFeedsPrefV2", items: [] }, ]; const mockApi = { isAuthenticated: true, getPreferences: async () => mockPreferencesObj, getLabelers: async () => [], }; const provider = new PreferencesProvider(mockApi);
await provider.fetchPreferences();
const preferences = await provider.requirePreferences(); assert.deepEqual(preferences.obj, mockPreferencesObj); });
it("should fall back to empty labeler defs when the labeler service fails", async () => { const mockPreferencesObj = [ { $type: "app.bsky.actor.defs#savedFeedsPrefV2", items: [] }, ]; const mockApi = { isAuthenticated: true, getPreferences: async () => mockPreferencesObj, getLabelers: async () => { throw new Error("502"); }, }; const provider = new PreferencesProvider(mockApi);
await provider.fetchPreferences();
const preferences = await provider.requirePreferences(); assert.deepEqual(preferences.obj, mockPreferencesObj); assert.deepEqual(preferences.labelerDefs, []); });
it("should warn with a toast when the labeler service fails", async () => { // The toast's own auto-dismiss timer shouldn't run on the patched clock. globalThis.setTimeout = realSetTimeout; const mockApi = { isAuthenticated: true, getPreferences: async () => [], getLabelers: async () => { throw new Error("502"); }, }; const provider = new PreferencesProvider(mockApi);
await provider.fetchPreferences();
const toasts = document.querySelectorAll('[data-testid="toast"]'); assert.deepEqual(toasts.length, 1); for (const toast of toasts) { toast.remove(); } });
it("should update preferences via API", async () => { let updatedObj = null; const mockApi = { isAuthenticated: true, getPreferences: async () => [], getLabelers: async () => [], updatePreferences: async (obj) => { updatedObj = obj; }, }; const provider = new PreferencesProvider(mockApi); await provider.fetchPreferences();
const newPreferences = new Preferences( [{ $type: "app.bsky.actor.defs#testPref" }], [], ); await provider.updatePreferences(newPreferences);
assert.deepEqual(updatedObj, newPreferences.obj); assert.deepEqual(await provider.requirePreferences(), newPreferences); });});