/** * Tests for the browser half. * * There is no browser here, so the bundle is evaluated the way the real module * loader would — inside `window.__ModuleLoader__.load` — with a fake `require` * that supplies React and THROWS on anything else. That last part is the useful * one: it proves the bundle stays inside the platform's seed table, which is the * constraint a hand-written client half is most likely to break. * * The card is then rendered by calling the registered component and expanding * the elements it returns, and its handlers are driven directly. That is not a * substitute for looking at it in a browser, but it does check the wiring the * card cannot work without — including the disclosure chrome, which is what * makes it look like the other cards in the tab. */ import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { test } from 'node:test' const SOURCE = readFileSync(new URL('../client/client.js', import.meta.url), 'utf8') /** * Minimal React: createElement, the three hooks the card uses, and an effect * runner that actually invokes its callback rather than ignoring it. * * `useState` consumes one ordered slot per call, as real hooks do. */ function makeFakeReact() { const slots = [] let cursor = 0 return { reset() { cursor = 0 }, api: { createElement: (type, props, ...children) => ({ type, props: props ?? {}, children }), useState(initial) { const index = cursor cursor += 1 if (!(index in slots)) slots[index] = typeof initial === 'function' ? initial() : initial return [ slots[index], (next) => { slots[index] = typeof next === 'function' ? next(slots[index]) : next }, ] }, useEffect(effect) { effect() }, useMemo(factory) { return factory() }, }, } } /** Evaluate the bundle against a fake loader and return its exports. */ function loadBundle() { let loaded const window = { __ModuleLoader__: { load: (spec) => { loaded = spec } } } new Function('window', SOURCE)(window) assert.ok(loaded !== undefined, 'the bundle must call window.__ModuleLoader__.load') const react = makeFakeReact() const exported = loaded.factory((name) => { if (name === 'react') return react.api throw new Error(`the client bundle required "${name}", which is not in the platform seed table`) }) return { id: loaded.id, exported, react } } /** Resolve function components by calling them, until a host element or null. */ function expand(node, depth = 0) { if (depth > 50) throw new Error('the component tree did not settle') if (node === null || node === undefined) return null if (typeof node !== 'object') return node if (Array.isArray(node)) return node if (typeof node.type === 'function') return expand(node.type(node.props), depth + 1) return node } /** Depth-first walk over a rendered tree, expanding components as it goes. */ function findAll(node, predicate, out = []) { if (node === null || node === undefined) return out if (Array.isArray(node)) { for (const child of node) findAll(child, predicate, out) return out } if (typeof node !== 'object') return out if (typeof node.type === 'function') { findAll(expand(node), predicate, out) return out } if (predicate(node)) out.push(node) for (const child of node.children ?? []) findAll(child, predicate, out) return out } function textOf(node) { if (typeof node === 'string') return node if (node === null || node === undefined || typeof node !== 'object') return '' if (Array.isArray(node)) return node.map(textOf).join('') return (node.children ?? []).map(textOf).join('') } /** A SettingsScope stand-in recording the writes it was asked to make. */ function fakeScope(snapshot) { const calls = { set: [], unset: [] } return { calls, getSnapshot: () => snapshot, subscribe: () => () => {}, set: async (field, value) => { calls.set.push([field, value]) }, unset: async (field) => { calls.unset.push(field) }, } } const READY = { status: 'ready', value: { embeddingModel: '', embeddingDimensions: 0 }, user: {}, base: {}, revision: 1, writable: true, mode: 'host', } /** Register the card the way the host would, and hand back its component. */ function registered() { const { id, exported, react } = loadBundle() let registration let render const ctx = { inject(deps, callback) { assert.deepEqual(deps, ['settingsScope']) callback({ settingsScope: { bind: () => fakeScope(READY) }, slots: { inject(_name, produce) { produce() }, register(options, component) { registration = options render = component }, }, }) }, } exported.apply(ctx) const draw = (scope) => { react.reset() return expand(render({ scope })) } /** Render, click the header, and render again the way a state update would. */ const drawOpen = (scope) => { const header = headerOf(draw(scope)) header.props.onClick() return draw(scope) } return { id, exported, react, registration, draw, drawOpen } } const headerOf = (tree) => findAll(tree, (node) => node.type === 'button' && node.props['aria-expanded'] !== undefined)[0] const inputFor = (tree, label) => findAll(tree, (node) => node.type === 'input' && node.props['aria-label'] === label)[0] const buttonStartingWith = (tree, prefix) => findAll(tree, (node) => node.type === 'button' && textOf(node).startsWith(prefix))[0] test('the bundle id matches the package name the loader is told about', () => { const { id } = loadBundle() assert.equal(id, 'dsh-slopo') }) test('the client bundle requires nothing outside the platform seed table', () => { // loadBundle() throws on any specifier other than react. assert.doesNotThrow(() => loadBundle()) }) test('applying registers exactly one settings.plugin.item card, keyed on the namespace', () => { const { registration, exported } = registered() assert.equal(exported.name, 'dsh-slopo') assert.deepEqual(exported.inject, ['slots']) assert.equal(registration.name, 'settings.plugin.item') // The key must equal the Host namespace, or the tab never dispatches the card. assert.equal(registration.key, 'dsh-slopo') }) test('the root is an
  • , because the tab renders every card into a