Skip to content

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

@alexey-pelykh

Description

@alexey-pelykh

Problem

The gateway-disconnect regression (Issue A) occurred because RemoteClawApp was missing 3 required host-interface fields, and no test exercised the real class. The existing tests in ui/src/ui/app-lifecycle.node.test.ts and ui/src/ui/app-lifecycle-connect.node.test.ts construct synthetic plain-object hosts with connectGeneration: 0 hand-set in fixtures:

function createHost() {
  return {
    basePath: "",
    client: { stop: vi.fn() },
    connectGeneration: 0,  // hand-set — matches type contract
    ...
  };
}

Zero tests instantiate the actual RemoteClawApp Lit class. Fixture matches the interface; production class may not. Result: fixture-passes-class-fails gap invisible to grep, coverage tools, and type-checker.

Fix

Add a smoke test at ui/src/ui/app.smoke.test.ts (or equivalent) that:

  1. Registers the custom element by importing app.ts
  2. Creates a RemoteClawApp instance via document.createElement("remoteclaw-app")
  3. Asserts every required field of LifecycleHost, GatewayHost, and ToolStreamHost is !== undefined on the instance

Example

// ui/src/ui/app.smoke.test.ts
import "./app.ts"; // registers the custom element
import { describe, it, expect } from "vitest";

describe("RemoteClawApp instance — host interface compliance", () => {
  it("initializes every required LifecycleHost field", () => {
    const el = document.createElement("remoteclaw-app") as any;
    const required = [
      "basePath", "connectGeneration", "tab", "assistantName",
      "assistantAvatar", "assistantAgentId", "serverVersion",
      "chatHasAutoScrolled", "chatManualRefreshInFlight", "chatLoading",
      "chatMessages", "chatToolMessages", "chatStream",
      "logsAutoFollow", "logsAtBottom", "logsEntries",
      "popStateHandler", "topbarObserver",
    ];
    for (const field of required) {
      expect(el[field], `missing LifecycleHost field: ${field}`).not.toBeUndefined();
    }
  });

  it("initializes every required GatewayHost field", () => {
    const el = document.createElement("remoteclaw-app") as any;
    const required = [
      "settings", "password", "clientInstanceId", "client", "connected",
      "hello", "lastError", "lastErrorCode", "eventLogBuffer", "eventLog",
      "tab", "presenceEntries", "presenceError", "presenceStatus",
      "agentsLoading", "agentsList", "agentsError",
      "toolsCatalogLoading", "toolsCatalogError", "toolsCatalogResult",
      "debugHealth", "assistantName", "assistantAvatar", "assistantAgentId",
      "serverVersion", "sessionKey", "chatRunId", "refreshSessionsAfterChat",
      "execApprovalQueue", "execApprovalError", "updateAvailable",
    ];
    for (const field of required) {
      expect(el[field], `missing GatewayHost field: ${field}`).not.toBeUndefined();
    }
  });

  it("initializes every required ToolStreamHost field", () => {
    const el = document.createElement("remoteclaw-app") as any;
    const required = [
      "sessionKey", "chatRunId", "chatStream", "chatStreamStartedAt",
      "chatStreamSegments", "toolStreamById", "toolStreamOrder",
      "chatToolMessages", "toolStreamSyncTimer",
    ];
    for (const field of required) {
      expect(el[field], `missing ToolStreamHost field: ${field}`).not.toBeUndefined();
    }
  });
});

Stronger variant (optional)

Parse the host interface at test time (via tsc --emitDeclarationOnly or a type-introspection helper) and auto-derive the required-field list. This way new fields added to the interface are automatically asserted without a test update — closing the gap permanently. Out of scope for the initial PR; consider as a follow-up.

Acceptance criteria

  • New test file ui/src/ui/app.smoke.test.ts exists.
  • Test asserts every required field of LifecycleHost, GatewayHost, ToolStreamHost is defined on a fresh RemoteClawApp instance.
  • Test PASSES after Issue A is merged (fields restored).
  • Test would FAIL if any of the three restored fields is removed (confirms defense).
  • pnpm test passes.
  • JSDOM (or equivalent) used — no Playwright required for this smoke test.

Commit message

test(ui): add smoke test instantiating RemoteClawApp and asserting required host-interface fields — defense-in-depth against sync regressions

References

  • Related: Issue A (fix the underlying regression)
  • Related: Issue B (remove type-erasure casts that enabled the regression)
  • Related: Issue H (extend assertion list when auditing additional host interfaces)

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions