Skip to main content
The host-only registry API (loadPlugin / getContributions) and the test-only reset.

LoadedPlugin

Identity of a successfully-loaded plugin, for host chrome that must attribute a contribution to its origin (e.g. the shell’s provenance badge on plugin nav). The name is the same id the registry stamps on every contribution (pluginId); the version comes from the plugin’s manifest. Properties

PluginContributionsSnapshot

What usePluginContributions returns: the load status and the registry. Properties Related: PluginContributions

PluginLoaderState

The plugin load pass’s public state, mirrored for feature pages to read. It is the shape the app shell’s loader store already exposes: status gates when the contributions are complete, and the error fields carry the loud failures. Properties Related: LoadedPlugin

StaticToolDisplayNamesProvider

Serve a FIXED display-name map — the test seam for a consumer that renders a tool picker without a live fetch. reload defaults to a no-op; a test that exercises the reload wiring passes a spy. Re-exported from the testing entry. Parameters Related: ToolDisplayNamesContextValue

__resetContributions

Tests reset the module-level registry between cases.

__resetPluginHostState

Tests reset the module-level host state between cases.

deferred

A promise whose settlement the test controls, returned alongside its own resolve/reject. It pins a component’s loading state for assertion: hand the unsettled promise to the code under test (e.g. as a mocked api call’s return value), assert the in-flight UI while it hangs, then call resolve/reject to drive the success or failure branch and assert the settled UI. Each call returns a fresh, independent deferred.

flushResizeObservers

Fires every live ResizeObserver stub’s callback for the elements it is observing. Pair it with setElementOverflow to drive a component that measures itself (ScrollRegion and useProseScrollRegions re-read scrollWidth/clientWidth from their observer callbacks): setElementOverflow(region, true); flushResizeObservers(); Only meaningful once installJsdomStubs has installed the stub; with a real ResizeObserver present (a browser environment) there is nothing to flush.

flushResizeObserversFor

Delivers a resize for ONE element, to every stub observing it — the narrow form of flushResizeObservers. Use it to prove which element a component is watching: a measurement driven by a resize of a CHILD only arrives if that child is observed, so the assertion fails the moment a component narrows its observation to its own box. Parameters

getContributions

The shell reads this after loading every installed plugin bundle. Related: PluginContributions

getPluginHostState

The current mirrored host state — the snapshot usePluginContributions reads. Related: PluginLoaderState

installJsdomStubs

jsdom omits a handful of browser APIs the Studio components reach for while a test drives them. Installing every stub in one place keeps each feature’s test-setup.ts identical and lets interaction tests exercise the real components instead of mocking them out:
  • Radix primitives observe element size (ResizeObserver), scroll the active item into view, and capture the pointer while opening.
  • Export/download paths create and revoke object URLs.
  • File-import flows read the picked file via Blob.text().
  • Router scroll restoration calls scrollTo on navigation.
The ResizeObserver stub is a WORKING observer, not an inert one: it keeps a registry of the observers still in use, delivers an entry to the callback as each element is observed (as a real observer does for its initial observation), and lets a test re-deliver on demand via flushResizeObservers. Components that size themselves — ScrollRegion and useProseScrollRegions — are therefore exercised for real, with setElementOverflow supplying the scroll metrics jsdom cannot compute. Each stub only fills a MISSING API, so installing the whole set everywhere is harmless — the one exception is window.scrollTo, which jsdom ships as a “Not implemented” stub that logs loudly on every navigation, so it is replaced unconditionally. Call once from a package’s Vitest setupFiles entry.

loadPlugin

Load one plugin: call its register entry with a context bound to pluginId, then commit everything it staged into the global registry. Contributions are STAGED into local arrays (identity captured in the closure, never read from ambient state) and only committed after entry settles successfully. entry is AWAITED before commit, so a synchronous or an async entry is fully done first: a post-await throw skips the commit (atomicity holds for async too) and a post-await registration cannot slip past the batch. The seal is set after entry has SETTLED — for a sync entry, after it returns and the await continuation runs; for an async entry, after its returned promise resolves. A registration attempted from that point on (a deferred timer, a callback scheduled after settle) throws loudly instead of pushing into an orphaned staged array that never commits. Registrations made during entry’s synchronous body — including a microtask it enqueues then, which runs before the await continuation — are still captured and committed with the batch. A register that throws commits nothing and leaves the registry untouched. Duplicate guards run against BOTH the staged contributions and what is already committed, and fail loudly. Parameters Related: PluginEntry

setElementOverflow

Makes element report itself as horizontally overflowing (or not). jsdom runs no layout, so scrollWidth and clientWidth are both 0 and every overflow test reads as “fits”; this shadows the prototype getters with own, configurable values so a test can drive either state. Call it again with the opposite flag to flip the element back. Parameters

setPluginHostState

The host loader pushes every load-pass transition here (it stays the single driver); every subscriber is notified so feature pages re-render off it. Props Related: PluginLoaderState

subscribePluginHost

Subscribe to host-state transitions; returns the unsubscribe function useSyncExternalStore requires so a subscriber detaches on unmount. Parameters

usePluginContributions

Subscribe to the plugin host state and read the committed contributions. The status drives re-renders as the load pass advances; contributions is only meaningful once status === 'ready' (before then the pass has committed nothing, so callers render their non-plugin content). Related: PluginContributionsSnapshot