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.
88 lines
4.1 KiB
TypeScript
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);
|
|
});
|