import "../../index.css"; import { type AuthAccessStreamEvent, type AuthAccessSnapshot, AuthSessionId, DEFAULT_SERVER_SETTINGS, EnvironmentId, type DesktopBridge, type DesktopUpdateState, type LocalApi, type ServerConfig, } from "@t3tools/contracts"; import { DateTime } from "effect"; import { page } from "vitest/browser"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { render } from "vitest-browser-react"; import { __resetLocalApiForTests } from "../../localApi"; import { AppAtomRegistryProvider } from "../../rpc/atomRegistry"; import { resetServerStateForTests, setServerConfigSnapshot } from "../../rpc/serverState"; import { ConnectionsSettings } from "./ConnectionsSettings"; import { GeneralSettingsPanel } from "./SettingsPanels"; const authAccessHarness = vi.hoisted(() => { type Snapshot = AuthAccessSnapshot; let snapshot: Snapshot = { pairingLinks: [], clientSessions: [], }; let revision = 1; const listeners = new Set<(event: AuthAccessStreamEvent) => void>(); const emitEvent = (event: AuthAccessStreamEvent) => { for (const listener of listeners) { listener(event); } }; return { reset() { snapshot = { pairingLinks: [], clientSessions: [], }; revision = 1; listeners.clear(); }, setSnapshot(next: Snapshot) { snapshot = next; }, emitSnapshot() { emitEvent({ version: 1 as const, revision, type: "snapshot" as const, payload: snapshot, }); revision += 1; }, emitEvent, emitPairingLinkUpserted(pairingLink: Snapshot["pairingLinks"][number]) { emitEvent({ version: 1, revision, type: "pairingLinkUpserted", payload: pairingLink, }); revision += 1; }, emitPairingLinkRemoved(id: string) { emitEvent({ version: 1, revision, type: "pairingLinkRemoved", payload: { id }, }); revision += 1; }, emitClientUpserted(clientSession: Snapshot["clientSessions"][number]) { emitEvent({ version: 1, revision, type: "clientUpserted", payload: clientSession, }); revision += 1; }, emitClientRemoved(sessionId: string) { emitEvent({ version: 1, revision, type: "clientRemoved", payload: { sessionId: AuthSessionId.makeUnsafe(sessionId), }, }); revision += 1; }, subscribe(listener: (event: AuthAccessStreamEvent) => void) { listeners.add(listener); listener({ version: 1, revision: 1, type: "snapshot", payload: snapshot, }); return () => { listeners.delete(listener); }; }, }; }); vi.mock("../../environments/runtime", () => { const primaryConnection = { kind: "primary" as const, knownEnvironment: { id: "environment-local", label: "Local environment", source: "manual" as const, environmentId: EnvironmentId.makeUnsafe("environment-local"), target: { httpBaseUrl: "http://localhost:3000", wsBaseUrl: "ws://localhost:3000", }, }, environmentId: EnvironmentId.makeUnsafe("environment-local"), client: { server: { subscribeAuthAccess: (listener: Parameters[0]) => authAccessHarness.subscribe(listener), }, }, ensureBootstrapped: async () => undefined, reconnect: async () => undefined, dispose: async () => undefined, }; return { getEnvironmentHttpBaseUrl: () => "http://localhost:3000", getSavedEnvironmentRecord: () => null, getSavedEnvironmentRuntimeState: () => null, hasSavedEnvironmentRegistryHydrated: () => true, listSavedEnvironmentRecords: () => [], resetSavedEnvironmentRegistryStoreForTests: () => undefined, resetSavedEnvironmentRuntimeStoreForTests: () => undefined, resolveEnvironmentHttpUrl: (_environmentId: unknown, path: string) => new URL(path, "http://localhost:3000").toString(), waitForSavedEnvironmentRegistryHydration: async () => undefined, addSavedEnvironment: vi.fn(), disconnectSavedEnvironment: vi.fn(), ensureEnvironmentConnectionBootstrapped: async () => undefined, getPrimaryEnvironmentConnection: () => primaryConnection, readEnvironmentConnection: () => primaryConnection, reconnectSavedEnvironment: vi.fn(), removeSavedEnvironment: vi.fn(), requireEnvironmentConnection: () => primaryConnection, resetEnvironmentServiceForTests: () => undefined, startEnvironmentConnectionService: () => undefined, subscribeEnvironmentConnections: () => () => {}, useSavedEnvironmentRegistryStore: ( selector: (state: { byId: Record }) => unknown, ) => selector({ byId: {} }), useSavedEnvironmentRuntimeStore: ( selector: (state: { byId: Record }) => unknown, ) => selector({ byId: {} }), }; }); function createBaseServerConfig(): ServerConfig { return { environment: { environmentId: EnvironmentId.makeUnsafe("environment-local"), label: "Local environment", platform: { os: "darwin" as const, arch: "arm64" as const }, serverVersion: "0.0.0-test", capabilities: { repositoryIdentity: true }, }, auth: { policy: "loopback-browser", bootstrapMethods: ["one-time-token"], sessionMethods: ["browser-session-cookie", "bearer-session-token"], sessionCookieName: "t3_session", }, cwd: "/repo/project", keybindingsConfigPath: "/repo/project/.t3code-keybindings.json", keybindings: [], issues: [], providers: [], availableEditors: ["cursor"], observability: { logsDirectoryPath: "/repo/project/.t3/logs", localTracingEnabled: true, otlpTracesUrl: "http://localhost:4318/v1/traces", otlpTracesEnabled: true, otlpMetricsEnabled: false, }, settings: DEFAULT_SERVER_SETTINGS, }; } function makeUtc(value: string) { return DateTime.makeUnsafe(Date.parse(value)); } function makePairingLink(input: { readonly id: string; readonly credential: string; readonly role: "owner" | "client"; readonly subject: string; readonly label?: string; readonly createdAt: string; readonly expiresAt: string; }): AuthAccessSnapshot["pairingLinks"][number] { return { ...input, createdAt: makeUtc(input.createdAt), expiresAt: makeUtc(input.expiresAt), }; } function makeClientSession(input: { readonly sessionId: string; readonly subject: string; readonly role: "owner" | "client"; readonly method: "browser-session-cookie"; readonly client?: { readonly label?: string; readonly ipAddress?: string; readonly userAgent?: string; readonly deviceType?: "desktop" | "mobile" | "tablet" | "bot" | "unknown"; readonly os?: string; readonly browser?: string; }; readonly issuedAt: string; readonly expiresAt: string; readonly lastConnectedAt?: string | null; readonly connected: boolean; readonly current: boolean; }): AuthAccessSnapshot["clientSessions"][number] { return { ...input, client: { deviceType: "unknown", ...input.client, }, sessionId: AuthSessionId.makeUnsafe(input.sessionId), issuedAt: makeUtc(input.issuedAt), expiresAt: makeUtc(input.expiresAt), lastConnectedAt: input.lastConnectedAt === undefined || input.lastConnectedAt === null ? null : makeUtc(input.lastConnectedAt), }; } const createDesktopBridgeStub = (overrides?: { readonly serverExposureState?: Awaited>; readonly setServerExposureMode?: DesktopBridge["setServerExposureMode"]; }): DesktopBridge => { const idleUpdateState: DesktopUpdateState = { enabled: false, status: "idle", currentVersion: "0.0.0-test", hostArch: "arm64", appArch: "arm64", runningUnderArm64Translation: false, availableVersion: null, downloadedVersion: null, downloadPercent: null, checkedAt: null, message: null, errorContext: null, canRetry: false, }; return { getLocalEnvironmentBootstrap: () => ({ label: "Local environment", httpBaseUrl: "http://127.0.0.1:3773", wsBaseUrl: "ws://127.0.0.1:3773", bootstrapToken: "desktop-bootstrap-token", }), getClientSettings: vi.fn().mockResolvedValue(null), setClientSettings: vi.fn().mockResolvedValue(undefined), getSavedEnvironmentRegistry: vi.fn().mockResolvedValue([]), setSavedEnvironmentRegistry: vi.fn().mockResolvedValue(undefined), getSavedEnvironmentSecret: vi.fn().mockResolvedValue(null), setSavedEnvironmentSecret: vi.fn().mockResolvedValue(true), removeSavedEnvironmentSecret: vi.fn().mockResolvedValue(undefined), getServerExposureState: vi.fn().mockResolvedValue( overrides?.serverExposureState ?? { mode: "local-only", endpointUrl: null, advertisedHost: null, }, ), setServerExposureMode: overrides?.setServerExposureMode ?? vi.fn().mockImplementation(async (mode) => ({ mode, endpointUrl: mode === "network-accessible" ? "http://192.168.1.44:3773" : null, advertisedHost: mode === "network-accessible" ? "192.168.1.44" : null, })), pickFolder: vi.fn().mockResolvedValue(null), confirm: vi.fn().mockResolvedValue(false), setTheme: vi.fn().mockResolvedValue(undefined), showContextMenu: vi.fn().mockResolvedValue(null), openExternal: vi.fn().mockResolvedValue(true), onMenuAction: () => () => {}, getUpdateState: vi.fn().mockResolvedValue(idleUpdateState), checkForUpdate: vi.fn().mockResolvedValue({ checked: false, state: idleUpdateState }), downloadUpdate: vi .fn() .mockResolvedValue({ accepted: false, completed: false, state: idleUpdateState }), installUpdate: vi .fn() .mockResolvedValue({ accepted: false, completed: false, state: idleUpdateState }), onUpdateState: () => () => {}, }; }; describe("GeneralSettingsPanel observability", () => { let mounted: | (Awaited> & { cleanup?: () => Promise; unmount?: () => Promise; }) | null = null; beforeEach(async () => { resetServerStateForTests(); await __resetLocalApiForTests(); localStorage.clear(); authAccessHarness.reset(); }); afterEach(async () => { if (mounted) { const teardown = mounted.cleanup ?? mounted.unmount; await teardown?.call(mounted).catch(() => {}); } mounted = null; vi.unstubAllGlobals(); Reflect.deleteProperty(window, "desktopBridge"); Reflect.deleteProperty(window, "nativeApi"); document.body.innerHTML = ""; resetServerStateForTests(); await __resetLocalApiForTests(); authAccessHarness.reset(); }); it("hides owner pairing tools in browser-served loopback builds without remote exposure", async () => { Reflect.deleteProperty(window, "desktopBridge"); authAccessHarness.setSnapshot({ pairingLinks: [], clientSessions: [ makeClientSession({ sessionId: "session-owner", subject: "browser-owner", role: "owner", method: "browser-session-cookie", client: { label: "Chrome on Mac", deviceType: "desktop", os: "macOS", browser: "Chrome", ipAddress: "127.0.0.1", }, issuedAt: "2036-04-07T00:00:00.000Z", expiresAt: "2036-05-07T00:00:00.000Z", connected: true, current: true, }), ], }); const fetchMock = vi.fn().mockImplementation(async (input) => { const url = String(input); if (url.endsWith("/api/auth/session")) { return new Response( JSON.stringify({ authenticated: true, auth: createBaseServerConfig().auth, role: "owner", sessionMethod: "browser-session-cookie", expiresAt: "2036-05-07T00:00:00.000Z", }), { status: 200, headers: { "content-type": "application/json" }, }, ); } throw new Error(`Unhandled fetch GET ${url}`); }); vi.stubGlobal("fetch", fetchMock); mounted = await render( , ); await expect.element(page.getByText("Manage local backend")).toBeInTheDocument(); await expect.element(page.getByLabelText("Enable network access")).toBeDisabled(); await expect .element( page.getByText( "This backend is only reachable on this machine. Restart it with a non-loopback host to enable remote pairing.", ), ) .toBeInTheDocument(); await expect.element(page.getByText("Authorized clients")).not.toBeInTheDocument(); await expect.element(page.getByText("Chrome on Mac")).not.toBeInTheDocument(); await expect .element(page.getByRole("heading", { name: "Remote environments", exact: true })) .toBeInTheDocument(); }); it("shows diagnostics inside About with a single logs-folder action", async () => { setServerConfigSnapshot(createBaseServerConfig()); mounted = await render( , ); await expect.element(page.getByText("About")).toBeInTheDocument(); await expect.element(page.getByText("Diagnostics")).toBeInTheDocument(); await expect.element(page.getByText("Open logs folder")).toBeInTheDocument(); await expect .element(page.getByText("/repo/project/.t3/logs", { exact: true })) .toBeInTheDocument(); await expect .element( page.getByText( "Local trace file. OTLP exporting traces to http://localhost:4318/v1/traces.", ), ) .toBeInTheDocument(); }); it("creates and shows a pairing link when network access is enabled", async () => { window.desktopBridge = createDesktopBridgeStub({ serverExposureState: { mode: "network-accessible", endpointUrl: "http://192.168.1.44:3773", advertisedHost: "192.168.1.44", }, }); let pairingLinks: Array = []; let clientSessions: Array = [ makeClientSession({ sessionId: "session-owner", subject: "desktop-bootstrap", role: "owner", method: "browser-session-cookie", client: { label: "This Mac", deviceType: "desktop", os: "macOS", browser: "Electron", ipAddress: "127.0.0.1", }, issuedAt: "2036-04-07T00:00:00.000Z", expiresAt: "2036-05-07T00:00:00.000Z", connected: true, current: true, }), ]; authAccessHarness.setSnapshot({ pairingLinks, clientSessions, }); vi.stubGlobal( "fetch", vi.fn().mockImplementation(async (input, init) => { const url = String(input); const method = init?.method ?? "GET"; if (url.endsWith("/api/auth/pairing-token") && method === "POST") { pairingLinks = [ makePairingLink({ id: "pairing-link-1", credential: "pairing-token", role: "client", subject: "one-time-token", label: "Julius iPhone", createdAt: "2036-04-07T00:00:00.000Z", expiresAt: "2036-04-10T00:05:00.000Z", }), ]; clientSessions = [ ...clientSessions, makeClientSession({ sessionId: "session-client", subject: "one-time-token", role: "client", method: "browser-session-cookie", client: { label: "Julius iPhone", deviceType: "mobile", os: "iOS", browser: "Safari", ipAddress: "192.168.1.88", }, issuedAt: "2036-04-07T00:01:00.000Z", expiresAt: "2036-05-07T00:01:00.000Z", connected: false, current: false, }), ]; authAccessHarness.setSnapshot({ pairingLinks, clientSessions, }); return new Response( JSON.stringify({ id: "pairing-link-1", credential: "pairing-token", label: "Julius iPhone", expiresAt: "2036-04-10T00:05:00.000Z", }), { status: 200, headers: { "content-type": "application/json" }, }, ); } throw new Error(`Unhandled fetch ${method} ${url}`); }), ); setServerConfigSnapshot(createBaseServerConfig()); mounted = await render( , ); await expect.element(page.getByText("Authorized clients")).toBeInTheDocument(); await expect.element(page.getByText("Revoke others")).toBeInTheDocument(); await expect.element(page.getByText("This Mac")).toBeInTheDocument(); await page.getByRole("button", { name: "Create link", exact: true }).click(); await expect.element(page.getByText("Create pairing link")).toBeInTheDocument(); await page.getByRole("button", { name: "Create link", exact: true }).click(); authAccessHarness.emitPairingLinkUpserted(pairingLinks[0]!); authAccessHarness.emitClientUpserted(clientSessions[1]!); await expect .element(page.getByText("Client · Mobile · iOS · Safari · 192.168.1.88")) .toBeInTheDocument(); await expect .element(page.getByRole("button", { name: /^(Copy|Show link)$/ })) .toBeInTheDocument(); await expect.element(page.getByText("Revoke others")).toBeInTheDocument(); }); it("revokes all other paired clients from settings", async () => { window.desktopBridge = createDesktopBridgeStub({ serverExposureState: { mode: "network-accessible", endpointUrl: "http://192.168.1.44:3773", advertisedHost: "192.168.1.44", }, }); let clientSessions: Array = [ makeClientSession({ sessionId: "session-owner", subject: "desktop-bootstrap", role: "owner", method: "browser-session-cookie", client: { label: "This Mac", deviceType: "desktop", os: "macOS", browser: "Electron", }, issuedAt: "2036-04-05T00:00:00.000Z", expiresAt: "2036-05-05T00:00:00.000Z", connected: true, current: true, }), makeClientSession({ sessionId: "session-client", subject: "one-time-token", role: "client", method: "browser-session-cookie", client: { label: "Julius iPhone", deviceType: "mobile", os: "iOS", browser: "Safari", ipAddress: "192.168.1.88", }, issuedAt: "2036-04-05T00:01:00.000Z", expiresAt: "2036-05-05T00:01:00.000Z", connected: false, current: false, }), ]; authAccessHarness.setSnapshot({ pairingLinks: [], clientSessions, }); const fetchMock = vi.fn().mockImplementation(async (input, init) => { const url = String(input); const method = init?.method ?? "GET"; if (url.endsWith("/api/auth/clients/revoke-others") && method === "POST") { clientSessions = clientSessions.filter((session) => session.current); authAccessHarness.setSnapshot({ pairingLinks: [], clientSessions, }); authAccessHarness.emitClientRemoved("session-client"); return new Response(JSON.stringify({ revokedCount: 1 }), { status: 200, headers: { "content-type": "application/json" }, }); } throw new Error(`Unhandled fetch ${method} ${url}`); }); vi.stubGlobal("fetch", fetchMock); setServerConfigSnapshot(createBaseServerConfig()); mounted = await render( , ); await expect.element(page.getByText("Julius iPhone")).toBeInTheDocument(); await page.getByRole("button", { name: "Revoke others", exact: true }).click(); await expect.element(page.getByText("This Mac")).toBeInTheDocument(); await expect.element(page.getByText("Julius iPhone")).not.toBeInTheDocument(); expect(fetchMock).toHaveBeenCalled(); }); it("shows a disabled network access toggle with guidance in desktop builds", async () => { const desktopBridge = createDesktopBridgeStub(); window.desktopBridge = desktopBridge; setServerConfigSnapshot(createBaseServerConfig()); mounted = await render( , ); const networkAccessToggle = page.getByLabelText("Enable network access"); await expect.element(networkAccessToggle).not.toBeDisabled(); await networkAccessToggle.click(); await expect.element(page.getByText("Enable network access?")).toBeInTheDocument(); await expect .element(page.getByText("T3 Code will restart to expose this environment over the network.")) .toBeInTheDocument(); await page.getByRole("button", { name: "Restart and enable", exact: true }).click(); await vi.waitFor(() => { expect(desktopBridge.setServerExposureMode).toHaveBeenCalledWith("network-accessible"); }); await expect .element(page.getByText("Reachable at http://192.168.1.44:3773")) .toBeInTheDocument(); }); it("opens the logs folder in the preferred editor", async () => { const openInEditor = vi.fn().mockResolvedValue(undefined); window.nativeApi = { shell: { openInEditor, }, } as unknown as LocalApi; setServerConfigSnapshot(createBaseServerConfig()); mounted = await render( , ); const openLogsButton = page.getByText("Open logs folder"); await openLogsButton.click(); expect(openInEditor).toHaveBeenCalledWith("/repo/project/.t3/logs", "cursor"); }); });