import assert from 'node:assert/strict'; import test from 'node:test'; import { installLinkRouting } from '../src/linkRouting.ts'; import { installAccountReport, type SeenAccount } from '../src/accountReport.ts'; type Listener = (event: unknown) => void; // Node has no DOM: `instanceof Element` needs the constructor to exist. (globalThis as { Element?: unknown }).Element ??= class {}; function fakeWindow() { const listeners: Listener[] = []; const opened: string[] = []; const win = { location: { href: 'https://site.test/feed' }, addEventListener(_type: string, fn: Listener) { listeners.push(fn); }, removeEventListener(_type: string, fn: Listener) { listeners.splice(listeners.indexOf(fn), 1); }, getSelection: () => ({ toString: () => '' }), open(url: string) { opened.push(url); }, }; const click = (extra: Record = {}) => { const event = { button: 0, defaultPrevented: false, target: null, prevented: false, stopped: false, preventDefault() { this.prevented = true; }, stopPropagation() { this.stopped = true; }, ...extra }; for (const fn of [...listeners]) fn(event); return event; }; return { win, opened, click, listeners }; } test('link routing diverts only site clicks the adapter names, once a mode is set', () => { const { win, opened, click, listeners } = fakeWindow(); const handle = installLinkRouting(win as unknown as Window, { configKey: 'site', clickedUrl: () => 'https://site.test/post/1' }); click(); assert.deepEqual(opened, [], '"same" is the default'); handle.configure({ site: 'popup' }); click(); assert.deepEqual(opened, [], 'unknown modes are ignored'); handle.configure({ site: 'pane' }); const event = click(); assert.deepEqual(opened, ['https://site.test/post/1']); assert.ok(event.prevented && event.stopped); click({ ctrlKey: true }); click({ button: 1 }); assert.equal(opened.length, 1, 'modified and non-primary clicks keep the site meaning'); handle.dispose(); assert.equal(listeners.length, 0); }); test('link routing leaves clicks the adapter does not claim alone', () => { const { win, opened, click } = fakeWindow(); const handle = installLinkRouting(win as unknown as Window, { configKey: 'site', clickedUrl: () => null }); handle.configure({ site: 'tab' }); const event = click(); assert.deepEqual(opened, []); assert.equal(event.prevented, false); handle.dispose(); }); test('account report sends only changes and retries after a failed send', async () => { let account: SeenAccount | null = null; let signedOut = false; const sent: unknown[] = []; let fail = false; const frames: FrameRequestCallback[] = []; const win = { document: { body: {}, addEventListener() {}, removeEventListener() {} }, location: { pathname: '/' }, MutationObserver: class { callback: MutationCallback; constructor(cb: MutationCallback) { this.callback = cb; observers.push(this); } observe() {} disconnect() {} }, requestAnimationFrame(cb: FrameRequestCallback) { frames.push(cb); return 1; }, }; const observers: { callback: MutationCallback }[] = []; const invoke = async (_command: string, args: Record) => { sent.push(args.report); if (fail) throw new Error('offline'); }; const handle = installAccountReport(win as unknown as Window, { read: () => account, signedOut: () => signedOut }, invoke); const run = async () => { observers[0]!.callback([] as unknown as MutationRecord[], {} as MutationObserver); for (const frame of frames.splice(0)) frame(0); await Promise.resolve(); await Promise.resolve(); }; await run(); assert.deepEqual(sent, [], 'nothing seen, nothing sent'); account = { handle: 'fish', name: 'Fish', avatar: null }; await run(); await run(); assert.deepEqual(sent, [{ account }], 'the same account is sent once'); account = null; signedOut = true; fail = true; await run(); fail = false; await run(); assert.deepEqual(sent.slice(1), [{ signedOut: true }, { signedOut: true }], 'a failed send is retried'); handle.dispose(); assert.deepEqual(installAccountReport(win as unknown as Window, { read: () => null, signedOut: () => false }).dispose(), undefined); });