Testing & debugging
Test UI behavior with an explicit mock bridge, validate your Backend protocol, and inspect real Host logs.
Verified September 11, 2026
On this page
Test at the right boundary#
Use browser tests for your own UI behavior, real Host sessions for native integration, and installed packages for distribution checks. A mock returning your expected value does not prove a native service exists or that a release package contains its assets.
| Layer | Verify |
|---|---|
| UI with a mock bridge | Loading, success, empty state, structured failure, and user cancellation |
| Real Host | Manifest compatibility, service contracts, permissions, and subscription cleanup |
| Backend protocol | Framing, handshake, concurrent messages, errors, and shutdown |
| Installed package | Production assets, native entry, and behavior without development servers |
An explicit browser mock#
Install the matching SDK in the test environment, create an explicit transport and inject it into the Host facade:
import { createHost, TroveError } from "@trove/plugin-sdk";
import { createMockBridge, createTransport } from "@trove/plugin-sdk/transport";
const bridge = createMockBridge({
"host.clipboard.readText": () => ({ text: "hello" }),
"host.notification.show": () => {
throw new TroveError({ code: "example.unavailable", message: "Try again" });
},
});
const transport = createTransport({ bridge });
const host = createHost(transport);
try {
console.log((await host.clipboard.readText()).text);
const subscription = await host.shortcut.events.triggered(event => {
console.log(event.registrationId);
});
bridge.emit("host.shortcut", "triggered", { registrationId: "demo" });
await subscription.close();
console.log((await subscription.closed).code);
// A separate failure test should call host.notification.show and assert its error UI.
} finally {
transport.disconnect();
}
Mock handlers receive (params, { signal }); unregistered methods return service.not_found, and emit routes by service and topic. The mock does not validate real contracts, emulate macOS permissions or reproduce native-kit parameter filtering. Verify those behaviors with real Host or protocol tests.
Validate a native Backend#
The first command reads the project’s Backend command and runs basic interoperability checks. The second also validates supplied business responses, independent concurrency, execution cancellation, reverse parent-context/deadline propagation, events and unsubscribe:
trove test
trove test --cases protocol-cases.json
Basic checks need no case file. Full cases contain success, error, cancel and reverse; a provider advertising subscribe also needs subscription. Download the complete cases and matching Rust fixture.
In a separate Rust starter, save the fixture as backend/src/main.rs and cases as protocol-cases.json, then run these commands. The fixture calls the tester’s host.test service, which is not a production Host capability. Adapt cases to your own operations and expectations before testing your application.
Tests own a temporary isolated process and need no desktop Host. Protocol checks have a 20-second overall limit; compile the Backend first if a cold toolchain build would exceed it.
Use Trove’s debug console#
Open My Curios, select a Curio, and use View logs to open its output in the Development page. You can also open Settings → Developer tools and select a project. Expand the run details to check the Run ID, version and development directory or installed artifact digest. During development, the trove dev terminal and this view consume the same feed. Installed Curios use it directly without a dev session.
| Source | Visible output |
|---|---|
| Host runtime diagnostics | Start, connection, call start/end, duration, errors, trace ID and exit |
| WebView console | log/info/debug/warn/error, uncaught errors and unhandled rejections |
| Backend stdout/stderr | Bytes actually written by the process |
| UI development process | Output from the dev service started by the Host |
Filter by source, search methods/error codes/trace IDs, pause, resume following or copy current text. Clear only advances the view’s read position. The Host does not record call parameters, result data or file contents by default; stderr is not itself a business error severity.
Know the log limits#
Plugin output stays in memory. Each source is capped at 32 KiB, with at most 128 Curios’ latest-run views; the console displays up to 400 lines per source. Overflow is visibly marked in both the console and CLI.
A new Run replaces the Curio’s previous output. An ended run retains only its bounded temporary view, marked ended, until replacement or eviction. It disappears when the Host exits. There is no plugin log directory, persistence, rotation, history browser, archive or file export. host.runtime.getPaths() returns data/cache/temp only.
Normal shutdown gives pipes a bounded drain opportunity. The Host cannot read C stdio that the process has not flushed. If output appears only on exit, check Backend buffering and where it actually prints. Crashes and forced termination cannot guarantee final output.
Reproduce before shipping#
- Start a clean development session and exercise a real native call.
- Trigger a recoverable failure and verify the UI leaves its loading state.
- Close the screen during pending work; check cancellation and subscription cleanup.
- Stop development, package and install the Curio, and repeat the core flow.
- For a Backend, close and reopen the Curio and check the configured lifecycle.
If a check fails, use troubleshooting to narrow the cause before publishing.