test(ui): add smoke test instantiating RemoteClawApp and asserting required host-interface fields (#2495)#2499
Merged
alexey-pelykh merged 1 commit intomainfrom Apr 23, 2026
Merged
Conversation
…quired host-interface fields — defense-in-depth against sync regressions (#2495) Defends against the regression class fixed in #2493: fixtures in app-lifecycle.node.test.ts / app-lifecycle-connect.node.test.ts construct synthetic plain-object hosts that happen to match LifecycleHost / GatewayHost / ToolStreamHost interfaces, while the production RemoteClawApp class may diverge. No existing test instantiated the real class, so fixture-passes-class-fails gaps went undetected. This smoke test registers the custom element, instantiates RemoteClawApp via document.createElement, and asserts every required field of each host interface is !== undefined on the fresh instance. Verified (by temporarily removing each field) that the test correctly catches the absence of connectGeneration, serverVersion, and chatStreamSegments — the three fields #2493 restored. Runs under the existing Playwright browser config (ui/vitest.config.ts) — no JSDOM plumbing required.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2495.
Summary
Adds
ui/src/ui/app.smoke.test.ts— a smoke test that instantiates the realRemoteClawAppLit class viadocument.createElement("remoteclaw-app")and asserts every required field ofLifecycleHost,GatewayHost, andToolStreamHostis defined on the fresh instance.Problem this defends against
The gateway-disconnect regression in #2493 shipped because:
app-*.node.test.tsfixtures construct synthetic plain-object hosts that happen to match the three host interfaces.RemoteClawAppclass.this as unknown as Parameters<typeof fn>[0](cast throughunknown) erased the structural check between the class and the host types.Result: fixtures passed, class was missing
connectGeneration/serverVersion/chatStreamSegments, gateway connect silently broke, no test caught it.Approach
./app.tsonce → registers theremoteclaw-appcustom element via@customElementdecoratordocument.createElement("remoteclaw-app")→ instantiates the real class (runs all property initializers)itblocks — one per host interface — assert every required field is!== undefinedclient?,connected?inLifecycleHost;onboarding?inGatewayHost) are correctly excludedas unknown as Record<string, unknown>cast (notas any—typescript/no-explicit-anyis an oxlint error per.oxlintrc.json) enables dynamic field access without type-safety loss at the boundary we're testing.Acceptance criteria verification
ui/src/ui/app.smoke.test.tsexists — 105 LOCLifecycleHost(18),GatewayHost(31),ToolStreamHost(9)serverVersion→ 2 test failures (Lifecycle + Gateway)chatStreamSegments→ 1 failure (ToolStream)connectGeneration→ 1 failure (Lifecycle)ui/vitest.config.ts) — the superset of the JSDOM environment the issue contemplated; no separate JSDOM plumbing was addeddocument.createElementand standard custom-element registration, and would run identically under a JSDOM config if one existedtsgoclean,oxfmt --checkclean,oxlint --type-awarecleanOut of scope (future follow-ups)
The issue mentions a "stronger variant" that auto-derives the field list from the TypeScript interface via
tsc --emitDeclarationOnlyor type introspection. That would close the gap permanently (new host-interface fields auto-asserted without test updates). Explicitly deferred in the issue — worth a follow-up once this lands.References
as unknown as Parameters<typeof X>[0]casts — restore structural type-checking #2494 (cast removal eliminates the type-erasure that hid the bug)🤖 Generated with Claude Code