324 lines · 11.2 KB · TypeScript
RawDownload
import type { Discovery } from "@t3tools/client-runtime/relay";import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime";import { EnvironmentId } from "@t3tools/contracts";import * as Option from "effect/Option";import { AsyncResult } from "effect/unstable/reactivity";import { act, useState, type ButtonHTMLAttributes, type ReactNode } from "react";import { create, type ReactTestRenderer } from "react-test-renderer";import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; type DiscoveredEnvironments = Discovery.RelayEnvironmentDiscoveryState["environments"]; const discovery = vi.hoisted(() => ({  state: null as Discovery.RelayEnvironmentDiscoveryState | null,  listeners: new Set<() => void>(),  refreshCommand: Symbol("refresh"),  registerCommand: Symbol("register"),  refresh: vi.fn<() => Promise<AtomCommandResult<void, never>>>(),  register: vi.fn(),  listEnvironments: vi.fn<() => Promise<DiscoveredEnvironments>>(),})); vi.mock("~/state/relay", () => ({  relayEnvironmentDiscovery: { refresh: discovery.refreshCommand },}));vi.mock("~/connection/catalog", () => ({  environmentCatalog: { register: discovery.registerCommand },}));vi.mock("~/state/use-atom-command", () => ({  useAtomCommand: (command: unknown) =>    command === discovery.refreshCommand ? discovery.refresh : discovery.register,}));vi.mock("~/state/environments", async () => {  const { useSyncExternalStore } = await import("react");  const subscribe = (listener: () => void) => {    discovery.listeners.add(listener);    return () => discovery.listeners.delete(listener);  };  const read = () => {    if (discovery.state === null) throw new Error("Discovery fixture is not initialized");    return discovery.state;  };  return { useRelayEnvironmentDiscovery: () => useSyncExternalStore(subscribe, read, read) };});vi.mock("../ConnectionStatusDot", () => ({ ConnectionStatusDot: () => null }));vi.mock("../ui/tooltip", () => ({  Tooltip: ({ children }: { children: ReactNode }) => children,  TooltipTrigger: ({ children }: { children: ReactNode }) => <span>{children}</span>,  TooltipPopup: () => null,}));vi.mock("../ui/checkbox", () => ({  Checkbox: (props: {    checked: boolean;    disabled: boolean;    onCheckedChange: (checked: boolean) => void;  }) => (    <input      type="checkbox"      checked={props.checked}      disabled={props.disabled}      onChange={(event) => props.onCheckedChange(event.target.checked)}    />  ),}));vi.mock("../ui/button", () => ({  Button: ({ children, ...props }: ButtonHTMLAttributes<HTMLButtonElement>) => (    <button {...props}>{children}</button>  ),}));vi.mock("../ui/toast", () => ({ toastManager: { add: vi.fn() } })); import { CloudEnvironmentConnectRows } from "./CloudEnvironmentConnectList"; const newMachineId = EnvironmentId.make("new-computer");const linkedMachines: DiscoveredEnvironments = new Map([  [    newMachineId,    {      environment: {        environmentId: newMachineId,        label: "Work laptop",        endpoint: {          httpBaseUrl: "https://relay.example.test",          wsBaseUrl: "wss://relay.example.test/ws",          providerKind: "manual",        },        linkedAt: "2026-09-05T12:00:00.000Z",      },      availability: "online",      status: Option.none(),      error: Option.none(),    },  ],]); let renderer: ReactTestRenderer | null;let page: EventTarget & { visibilityState: DocumentVisibilityState };let browserWindow: EventTarget; function publish(state: Discovery.RelayEnvironmentDiscoveryState) {  discovery.state = state;  for (const listener of discovery.listeners) listener();} async function mount(refreshWhileEmpty = true) {  await act(async () => {    renderer = create(      <CloudEnvironmentConnectRows        primaryEnvironmentId={null}        savedEnvironments={[]}        showSavedEnvironments        refreshWhileEmpty={refreshWhileEmpty}        empty={<p>Waiting for your computer to connect.</p>}      />,    );  });} async function advance(milliseconds: number) {  await act(async () => {    await vi.advanceTimersByTimeAsync(milliseconds);  });} beforeEach(() => {  vi.useFakeTimers();  vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);  page = Object.assign(new EventTarget(), { visibilityState: "visible" as const });  browserWindow = new EventTarget();  vi.stubGlobal("document", page);  vi.stubGlobal("window", browserWindow);  renderer = null;  discovery.listeners.clear();  discovery.state = {    environments: new Map(),    refreshing: false,    offline: false,    error: Option.none(),  };  discovery.listEnvironments.mockReset().mockResolvedValue(new Map());  discovery.register.mockReset().mockResolvedValue(AsyncResult.success(undefined));  discovery.refresh.mockReset().mockImplementation(async () => {    publish({ environments: new Map(), refreshing: true, offline: false, error: Option.none() });    const environments = await discovery.listEnvironments();    publish({ environments, refreshing: false, offline: false, error: Option.none() });    return AsyncResult.success(undefined);  });}); afterEach(async () => {  await act(async () => renderer?.unmount());  vi.useRealTimers();  vi.unstubAllGlobals();}); describe("cloud onboarding discovery", () => {  it("signals that the section can expand after initial discovery settles", async () => {    let finishDiscovery!: (environments: DiscoveredEnvironments) => void;    discovery.listEnvironments.mockReturnValue(      new Promise((resolve) => {        finishDiscovery = resolve;      }),    );    const onDiscoveryReady = vi.fn();    await act(async () => {      renderer = create(        <CloudEnvironmentConnectRows          primaryEnvironmentId={null}          savedEnvironments={[]}          onDiscoveryReady={onDiscoveryReady}        />,      );    });    expect(onDiscoveryReady).not.toHaveBeenCalled();    await act(async () => {      finishDiscovery(linkedMachines);    });    expect(onDiscoveryReady).toHaveBeenCalledTimes(1);    expect(renderer!.root.findByType("button").children).toEqual(["Connect"]);  });   it("connects and selects discovered computers by default without overwriting deselection", async () => {    discovery.listEnvironments.mockResolvedValue(linkedMachines);    const autoSelectedComputers = new Set<EnvironmentId>();    function Setup() {      const [selectedIds, setSelectedIds] = useState<ReadonlySet<EnvironmentId>>(new Set());      return (        <CloudEnvironmentConnectRows          primaryEnvironmentId={null}          savedEnvironments={[]}          showSavedEnvironments          selection={{            autoSelectedComputers,            selectedIds,            onChange: (id, checked) =>              setSelectedIds((current) => {                const next = new Set(current);                if (checked) next.add(id);                else next.delete(id);                return next;              }),          }}        />      );    }    await act(async () => {      renderer = create(<Setup />);    });     expect(discovery.register).toHaveBeenCalledTimes(1);    expect(renderer!.root.findByType("input").props.checked).toBe(true);    await act(async () => {      await renderer!.root.findByType("input").props.onChange({ target: { checked: false } });    });    await act(async () => {      publish({ ...discovery.state!, environments: new Map(linkedMachines) });    });    expect(renderer!.root.findByType("input").props.checked).toBe(false);    expect(discovery.register).toHaveBeenCalledTimes(1);  });   it("shows a newly linked computer without remounting and stops polling once found", async () => {    discovery.listEnvironments      .mockResolvedValueOnce(new Map())      .mockResolvedValueOnce(linkedMachines);    await mount();    expect(renderer!.root.findByType("p").children).toEqual([      "Waiting for your computer to connect.",    ]);     await advance(5_000);     expect(renderer!.root.findAllByType("p").map((node) => node.children)).toContainEqual([      "Work laptop",    ]);    expect(renderer!.root.findByType("button").children).toEqual(["Connect"]);    await advance(30_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(2);  });   it("keeps a discovered computer visible when it is added to the browser", async () => {    discovery.listEnvironments.mockResolvedValue(linkedMachines);    await mount();    expect(renderer!.root.findByType("button").children).toEqual(["Connect"]);    await act(async () => {      renderer!.update(        <CloudEnvironmentConnectRows          primaryEnvironmentId={null}          savedEnvironments={[            {              environmentId: newMachineId,              connection: { phase: "connected", error: null, traceId: null },            },          ]}          showSavedEnvironments          refreshWhileEmpty        />,      );    });    expect(renderer!.root.findAllByType("p").map((node) => node.children)).toContainEqual([      "Work laptop",    ]);    expect(renderer!.root.findByType("button").children).toEqual(["Connected"]);  });   it("waits while hidden and refreshes immediately when visible again", async () => {    page.visibilityState = "hidden";    await mount();    await advance(30_000);    expect(discovery.listEnvironments).not.toHaveBeenCalled();     await act(async () => {      page.visibilityState = "visible";      page.dispatchEvent(new Event("visibilitychange"));    });    expect(discovery.listEnvironments).toHaveBeenCalledTimes(1);     page.visibilityState = "hidden";    page.dispatchEvent(new Event("visibilitychange"));    browserWindow.dispatchEvent(new Event("focus"));    await advance(30_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(1);  });   it("does not overlap a slow refresh or restart polling after unmount", async () => {    let resolveRefresh!: (environments: DiscoveredEnvironments) => void;    const pending = new Promise<DiscoveredEnvironments>((resolve) => {      resolveRefresh = resolve;    });    discovery.listEnvironments.mockResolvedValueOnce(new Map()).mockReturnValueOnce(pending);    await mount();    await advance(5_000);    expect(renderer!.root.findByType("p").children).toEqual([      "Waiting for your computer to connect.",    ]);     browserWindow.dispatchEvent(new Event("focus"));    page.dispatchEvent(new Event("visibilitychange"));    await advance(30_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(2);     await act(async () => renderer!.unmount());    renderer = null;    await act(async () => resolveRefresh(new Map()));    await advance(30_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(2);  });   it("pauses while offline and resumes when discovery is online", async () => {    await mount();    await act(async () => publish({ ...discovery.state!, offline: true }));    await advance(30_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(1);     await act(async () => publish({ ...discovery.state!, offline: false }));    await advance(5_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(2);  });   it("does not add polling to other cloud lists", async () => {    await mount(false);    await advance(30_000);    expect(discovery.listEnvironments).toHaveBeenCalledTimes(1);  });});