waybetter/packages/runtime/tests/linkRouting.test.ts
CRT 43a9e2e1a7 Extract the wayBetter framework core from wayBetter for X
Site-agnostic parts of the X app, behind a small site adapter:

- crates/waybetter-host: webview-label security policy and tab rect
  clamp, account profiles with native-owned accounts (AccountRules),
  plugin store with bundled-plugin upgrades (PluginHost), signed
  data-only hotfix channel, update scheduling and crash handshake, tab
  payloads/link routing/tab-chrome validation, and (feature "tauri")
  webview helpers and the Windows system menu.
- packages/runtime: DOM hub, request hub, plugin API core, OKLab theme
  color model, split-view rail hiding, link routing and account
  reporting, each configured by a SiteAdapter.
- packages/shell: address bar, pane, link-drop, profile, palette and
  update-chip models, and window controls with the Tauri API injected.

Tests moved/adapted from the app: 45 Rust (headless), 40 TypeScript.
2026-09-28 03:01:33 +07:00

88 lines
4.1 KiB
TypeScript

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<string, unknown> = {}) => {
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<string, unknown>) => { 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);
});