Files
backnotprop__plannotator/packages/ui/webmcp/modelContext.test.ts
Michael Ramos 6903d7a3dd feat(webmcp): expose plan review and annotate as WebMCP tools for browser agents (#1393)
Phase 1 of WebMCP support: a zero-dependency, feature-detected engine in packages/ui/webmcp plus a read-and-comment tool catalog for plan review and annotate (read_document, add_comments, update_comment, remove_comments, reveal, nudge_user, list_documents). No decision tools; the human approves. Zero footprint in browsers without document.modelContext (DOM, network, console, timers, and cookies identical to main), idle until called where the API exists, and never registered inside the annotate iframes. Adds an optional inReplyTo field on annotations for threaded replies. Client-only; no server changes.

AI-assisted (Claude) under maintainer direction.
2026-08-26 14:39:37 -07:00

31 lines
1.2 KiB
TypeScript

/**
* Feature detection: the only gate between a browser without WebMCP and
* the whole provider. It must return null for every absent or hostile shape
* and never throw.
*/
import { describe, expect, test } from 'bun:test';
import { resolveModelContext } from './modelContext';
describe('resolveModelContext', () => {
test('null when there is no document', () => {
expect(resolveModelContext(null)).toBeNull();
});
test('null when the property is absent, not an object, or lacks registerTool', () => {
expect(resolveModelContext({} as Document)).toBeNull();
expect(resolveModelContext({ modelContext: 'yes' } as unknown as Document)).toBeNull();
expect(resolveModelContext({ modelContext: {} } as unknown as Document)).toBeNull();
});
test('null, not a throw, when the property access itself throws (restricted contexts)', () => {
const hostile = {};
Object.defineProperty(hostile, 'modelContext', { get() { throw new Error('denied'); } });
expect(resolveModelContext(hostile as Document)).toBeNull();
});
test('returns the context when registerTool is callable', () => {
const ctx = { registerTool: async () => undefined };
expect(resolveModelContext({ modelContext: ctx } as unknown as Document)).toBe(ctx);
});
});