Files
orca/src/shared/plugins/frames/plugin-frame-facades.test.ts
T

78 lines
3.1 KiB
TypeScript

// @vitest-environment happy-dom
import { afterEach, describe, expect, it, vi } from 'vitest'
import { buildPluginPanelShellHtml } from './plugin-panel-shell'
type Disposable = { dispose: () => void }
type FrameOrca = {
on: (event: string, handler: (payload: unknown) => unknown) => Disposable
plugin: {
request: (name: string, input?: unknown, options?: { timeoutMs?: number }) => Promise<unknown>
on: (event: string, handler: (payload: unknown) => unknown) => Disposable
}
storage: {
get: (key: string) => Promise<unknown>
set: (key: string, value: unknown) => Promise<void>
onDidChange: (handler: (change: { key: string }) => void) => Disposable
}
settings: { get: (key?: string) => Promise<unknown> }
}
const realParent = window.parent
afterEach(() => {
Object.defineProperty(window, 'parent', { value: realParent, configurable: true })
})
function mountShell() {
const parent = { postMessage: vi.fn() }
Object.defineProperty(window, 'parent', { value: parent, configurable: true })
const script = buildPluginPanelShellHtml('<main></main>').match(
/<script>\n([\s\S]*?)<\/script>/
)?.[1]
window.eval(script!.replaceAll('configurable: false', 'configurable: true'))
const orca: FrameOrca = Object.getOwnPropertyDescriptor(window, 'orca')?.value
const fromHost = (data: unknown) =>
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the shim only compares source identity.
window.dispatchEvent(new MessageEvent('message', { data, source: parent as never as Window }))
const requests = () =>
parent.postMessage.mock.calls
.map(([message]) => message)
.filter((message) => message.kind === 'req')
const answer = (id: number, result: unknown) => fromHost({ orca: 1, kind: 'res', id, result })
const push = (payload: unknown) =>
fromHost({ orca: 1, kind: 'evt', event: 'plugin.push', payload })
return { orca, requests, answer, push }
}
describe('window.orca.plugin', () => {
it('asks the worker through plugin.request and resolves with its output', async () => {
const { orca, requests, answer } = mountShell()
const todos = orca.plugin.request('todos.list', { done: false }, { timeoutMs: 999_999 })
const [sent] = requests()
expect(sent).toMatchObject({
method: 'plugin.request',
params: { name: 'todos.list', input: { done: false }, timeoutMs: 120_000 }
})
answer(sent.id, { output: [{ id: '1' }] })
await expect(todos).resolves.toEqual([{ id: '1' }])
})
})
describe('window.orca events and data', () => {
it('subscribes to a workspace or agent event once, on the first listener', () => {
const { orca, requests, push } = mountShell()
const first = vi.fn()
orca.on('agent.status.changed', first)
orca.on('agent.status.changed', vi.fn())
orca.on('theme', vi.fn())
expect(requests()).toEqual([
expect.objectContaining({
method: 'events.subscribe',
params: { event: 'agent.status.changed' }
})
])
push({ kind: 'event', event: 'agent.status.changed', payload: { state: 'done' } })
expect(first).toHaveBeenCalledWith({ state: 'done' })
})
})