Skip to content

test(ui): add smoke test instantiating RemoteClawApp and asserting required host-interface fields (#2495)#2499

Merged
alexey-pelykh merged 1 commit intomainfrom
test/2495-app-smoke-test
Apr 23, 2026
Merged

test(ui): add smoke test instantiating RemoteClawApp and asserting required host-interface fields (#2495)#2499
alexey-pelykh merged 1 commit intomainfrom
test/2495-app-smoke-test

Conversation

@alexey-pelykh
Copy link
Copy Markdown

@alexey-pelykh alexey-pelykh commented Apr 23, 2026

Closes #2495.

Summary

Adds ui/src/ui/app.smoke.test.ts — a smoke test that instantiates the real RemoteClawApp Lit class via document.createElement("remoteclaw-app") and asserts every required field of LifecycleHost, GatewayHost, and ToolStreamHost is defined on the fresh instance.

Problem this defends against

The gateway-disconnect regression in #2493 shipped because:

  • All existing app-*.node.test.ts fixtures construct synthetic plain-object hosts that happen to match the three host interfaces.
  • Zero tests instantiated the actual RemoteClawApp class.
  • TypeScript assertions on this as unknown as Parameters<typeof fn>[0] (cast through unknown) 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

  • Import ./app.ts once → registers the remoteclaw-app custom element via @customElement decorator
  • document.createElement("remoteclaw-app") → instantiates the real class (runs all property initializers)
  • Three it blocks — one per host interface — assert every required field is !== undefined
  • Field lists are enumerated statically from each host interface's required (non-optional) members; optional fields (client?, connected? in LifecycleHost; onboarding? in GatewayHost) are correctly excluded

as unknown as Record<string, unknown> cast (not as anytypescript/no-explicit-any is an oxlint error per .oxlintrc.json) enables dynamic field access without type-safety loss at the boundary we're testing.

Acceptance criteria verification

  • New file ui/src/ui/app.smoke.test.ts exists — 105 LOC
  • Test asserts every required field of LifecycleHost (18), GatewayHost (31), ToolStreamHost (9)
  • Test PASSES on current HEAD (3 / 3 green)
  • Test FAILS if any restored field is removed — verified interactively for all three:
    • Removing serverVersion → 2 test failures (Lifecycle + Gateway)
    • Removing chatStreamSegments → 1 failure (ToolStream)
    • Removing connectGeneration → 1 failure (Lifecycle)
  • Test runs under the existing Playwright browser config (ui/vitest.config.ts) — the superset of the JSDOM environment the issue contemplated; no separate JSDOM plumbing was added
  • No Playwright-specific APIs used — the test only needs document.createElement and standard custom-element registration, and would run identically under a JSDOM config if one existed
  • tsgo clean, oxfmt --check clean, oxlint --type-aware clean

Out of scope (future follow-ups)

The issue mentions a "stronger variant" that auto-derives the field list from the TypeScript interface via tsc --emitDeclarationOnly or 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

🤖 Generated with Claude Code

…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.
@alexey-pelykh alexey-pelykh merged commit 7d49f34 into main Apr 23, 2026
16 checks passed
@alexey-pelykh alexey-pelykh deleted the test/2495-app-smoke-test branch April 23, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(ui): add smoke test instantiating RemoteClawApp and asserting required host-interface fields

1 participant