5787 lines · 178.6 KB · TypeScript
RawDownload
// Production CSS is part of the behavior under test because row height depends on it.import "../index.css"; import {  EventId,  ORCHESTRATION_WS_METHODS,  EnvironmentId,  type EnvironmentApi,  type MessageId,  type OrchestrationReadModel,  type ProjectId,  type ServerConfig,  type ServerLifecycleWelcomePayload,  type ThreadId,  type TurnId,  WS_METHODS,  OrchestrationSessionStatus,  DEFAULT_SERVER_SETTINGS,} from "@t3tools/contracts";import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime";import { createModelCapabilities, createModelSelection } from "@t3tools/shared/model";import { RouterProvider, createMemoryHistory } from "@tanstack/react-router";import { HttpResponse, http, ws } from "msw";import { setupWorker } from "msw/browser";import { page } from "vitest/browser";import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";import { render } from "vitest-browser-react"; import { useCommandPaletteStore } from "../commandPaletteStore";import { useComposerDraftStore, DraftId } from "../composerDraftStore";import {  __resetEnvironmentApiOverridesForTests,  __setEnvironmentApiOverrideForTests,} from "../environmentApi";import {  resetSavedEnvironmentRegistryStoreForTests,  resetSavedEnvironmentRuntimeStoreForTests,  useSavedEnvironmentRegistryStore,  useSavedEnvironmentRuntimeStore,} from "../environments/runtime";import {  INLINE_TERMINAL_CONTEXT_PLACEHOLDER,  removeInlineTerminalContextPlaceholder,  type TerminalContextDraft,} from "../lib/terminalContext";import { isMacPlatform } from "../lib/utils";import { __resetLocalApiForTests } from "../localApi";import { AppAtomRegistryProvider } from "../rpc/atomRegistry";import { getServerConfig } from "../rpc/serverState";import { getRouter } from "../router";import { deriveLogicalProjectKeyFromSettings } from "../logicalProject";import { selectBootstrapCompleteForActiveEnvironment, useStore } from "../store";import { useTerminalStateStore } from "../terminalStateStore";import { useUiStateStore } from "../uiStateStore";import { createAuthenticatedSessionHandlers } from "../../test/authHttpHandlers";import { BrowserWsRpcHarness, type NormalizedWsRpcRequestBody } from "../../test/wsRpcHarness"; import { DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts/settings"; vi.mock("../lib/gitStatusState", () => ({  useGitStatus: () => ({ data: null, error: null, cause: null, isPending: false }),  useGitStatuses: () => new Map(),  refreshGitStatus: () => Promise.resolve(null),  resetGitStatusStateForTests: () => undefined,})); const THREAD_ID = "thread-browser-test" as ThreadId;const THREAD_TITLE = "Browser test thread";const ARCHIVED_SECONDARY_THREAD_ID = "thread-secondary-project-archived" as ThreadId;const PROJECT_ID = "project-1" as ProjectId;const SECOND_PROJECT_ID = "project-2" as ProjectId;const LOCAL_ENVIRONMENT_ID = EnvironmentId.make("environment-local");const REMOTE_ENVIRONMENT_ID = EnvironmentId.make("environment-remote");const THREAD_REF = scopeThreadRef(LOCAL_ENVIRONMENT_ID, THREAD_ID);const THREAD_KEY = scopedThreadKey(THREAD_REF);const UUID_ROUTE_RE = /^\/draft\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;const PROJECT_DRAFT_KEY = `${LOCAL_ENVIRONMENT_ID}:${PROJECT_ID}`;const PROJECT_LOGICAL_KEY = deriveLogicalProjectKeyFromSettings(  {    environmentId: LOCAL_ENVIRONMENT_ID,    id: PROJECT_ID,    cwd: "/repo/project",    repositoryIdentity: null,  },  {    sidebarProjectGroupingMode: DEFAULT_CLIENT_SETTINGS.sidebarProjectGroupingMode,    sidebarProjectGroupingOverrides: DEFAULT_CLIENT_SETTINGS.sidebarProjectGroupingOverrides,  },);const NOW_ISO = "2026-03-04T12:00:00.000Z";const BASE_TIME_MS = Date.parse(NOW_ISO);const ATTACHMENT_SVG = "<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'></svg>";const ADD_PROJECT_SUBMENU_PLACEHOLDER = "Enter path (e.g. ~/projects/my-app)"; interface TestFixture {  snapshot: OrchestrationReadModel;  serverConfig: ServerConfig;  welcome: ServerLifecycleWelcomePayload;} let fixture: TestFixture;const rpcHarness = new BrowserWsRpcHarness();const wsRequests = rpcHarness.requests;let customWsRpcResolver: ((body: NormalizedWsRpcRequestBody) => unknown | undefined) | null = null;const wsLink = ws.link(/ws(s)?:\/\/.*/); interface ViewportSpec {  name: string;  width: number;  height: number;  textTolerancePx: number;  attachmentTolerancePx: number;} const DEFAULT_VIEWPORT: ViewportSpec = {  name: "desktop",  width: 960,  height: 1_100,  textTolerancePx: 44,  attachmentTolerancePx: 56,};const WIDE_FOOTER_VIEWPORT: ViewportSpec = {  name: "wide-footer",  width: 1_400,  height: 1_100,  textTolerancePx: 44,  attachmentTolerancePx: 56,};const COMPACT_FOOTER_VIEWPORT: ViewportSpec = {  name: "compact-footer",  width: 430,  height: 932,  textTolerancePx: 56,  attachmentTolerancePx: 56,}; interface MountedChatView {  [Symbol.asyncDispose]: () => Promise<void>;  cleanup: () => Promise<void>;  setViewport: (viewport: ViewportSpec) => Promise<void>;  setContainerSize: (viewport: Pick<ViewportSpec, "width" | "height">) => Promise<void>;  router: ReturnType<typeof getRouter>;} function isoAt(offsetSeconds: number): string {  return new Date(BASE_TIME_MS + offsetSeconds * 1_000).toISOString();} function createBaseServerConfig(): ServerConfig {  return {    environment: {      environmentId: EnvironmentId.make("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: [      {        provider: "codex",        enabled: true,        installed: true,        version: "0.116.0",        status: "ready",        auth: { status: "authenticated" },        checkedAt: NOW_ISO,        models: [],        slashCommands: [],        skills: [],      },    ],    availableEditors: [],    observability: {      logsDirectoryPath: "/repo/project/.t3/logs",      localTracingEnabled: true,      otlpTracesEnabled: false,      otlpMetricsEnabled: false,    },    settings: {      ...DEFAULT_SERVER_SETTINGS,      ...DEFAULT_CLIENT_SETTINGS,    },  };} function createMockEnvironmentApi(input: {  browse: EnvironmentApi["filesystem"]["browse"];  dispatchCommand: EnvironmentApi["orchestration"]["dispatchCommand"];}): EnvironmentApi {  return {    terminal: {} as EnvironmentApi["terminal"],    projects: {} as EnvironmentApi["projects"],    filesystem: {      browse: input.browse,    },    git: {} as EnvironmentApi["git"],    orchestration: {      dispatchCommand: input.dispatchCommand,      getTurnDiff: (() => {        throw new Error("Not implemented in browser test.");      }) as EnvironmentApi["orchestration"]["getTurnDiff"],      getFullThreadDiff: (() => {        throw new Error("Not implemented in browser test.");      }) as EnvironmentApi["orchestration"]["getFullThreadDiff"],      subscribeShell: (() => () => undefined) as EnvironmentApi["orchestration"]["subscribeShell"],      subscribeThread: (() => () =>        undefined) as EnvironmentApi["orchestration"]["subscribeThread"],    },  };} function createUserMessage(options: {  id: MessageId;  text: string;  offsetSeconds: number;  attachments?: Array<{    type: "image";    id: string;    name: string;    mimeType: string;    sizeBytes: number;  }>;}) {  return {    id: options.id,    role: "user" as const,    text: options.text,    ...(options.attachments ? { attachments: options.attachments } : {}),    turnId: null,    streaming: false,    createdAt: isoAt(options.offsetSeconds),    updatedAt: isoAt(options.offsetSeconds + 1),  };} function createAssistantMessage(options: { id: MessageId; text: string; offsetSeconds: number }) {  return {    id: options.id,    role: "assistant" as const,    text: options.text,    turnId: null,    streaming: false,    createdAt: isoAt(options.offsetSeconds),    updatedAt: isoAt(options.offsetSeconds + 1),  };} function createTerminalContext(input: {  id: string;  terminalLabel: string;  lineStart: number;  lineEnd: number;  text: string;}): TerminalContextDraft {  return {    id: input.id,    threadId: THREAD_ID,    terminalId: `terminal-${input.id}`,    terminalLabel: input.terminalLabel,    lineStart: input.lineStart,    lineEnd: input.lineEnd,    text: input.text,    createdAt: NOW_ISO,  };} function createSnapshotForTargetUser(options: {  targetMessageId: MessageId;  targetText: string;  targetAttachmentCount?: number;  sessionStatus?: OrchestrationSessionStatus;}): OrchestrationReadModel {  const messages: Array<OrchestrationReadModel["threads"][number]["messages"][number]> = [];   for (let index = 0; index < 22; index += 1) {    const isTarget = index === 3;    const userId = `msg-user-${index}` as MessageId;    const assistantId = `msg-assistant-${index}` as MessageId;    const attachments =      isTarget && (options.targetAttachmentCount ?? 0) > 0        ? Array.from({ length: options.targetAttachmentCount ?? 0 }, (_, attachmentIndex) => ({            type: "image" as const,            id: `attachment-${attachmentIndex + 1}`,            name: `attachment-${attachmentIndex + 1}.png`,            mimeType: "image/png",            sizeBytes: 128,            previewUrl: `/attachments/attachment-${attachmentIndex + 1}`,          }))        : undefined;     messages.push(      createUserMessage({        id: isTarget ? options.targetMessageId : userId,        text: isTarget ? options.targetText : `filler user message ${index}`,        offsetSeconds: messages.length * 3,        ...(attachments ? { attachments } : {}),      }),    );    messages.push(      createAssistantMessage({        id: assistantId,        text: `assistant filler ${index}`,        offsetSeconds: messages.length * 3,      }),    );  }   return {    snapshotSequence: 1,    projects: [      {        id: PROJECT_ID,        title: "Project",        workspaceRoot: "/repo/project",        defaultModelSelection: {          provider: "codex",          model: "gpt-5",        },        scripts: [],        createdAt: NOW_ISO,        updatedAt: NOW_ISO,        deletedAt: null,      },    ],    threads: [      {        id: THREAD_ID,        projectId: PROJECT_ID,        title: THREAD_TITLE,        modelSelection: {          provider: "codex",          model: "gpt-5",        },        interactionMode: "default",        runtimeMode: "full-access",        branch: "main",        worktreePath: null,        latestTurn: null,        createdAt: NOW_ISO,        updatedAt: NOW_ISO,        archivedAt: null,        deletedAt: null,        messages,        activities: [],        proposedPlans: [],        checkpoints: [],        session: {          threadId: THREAD_ID,          status: options.sessionStatus ?? "ready",          providerName: "codex",          runtimeMode: "full-access",          activeTurnId: null,          lastError: null,          updatedAt: NOW_ISO,        },      },    ],    updatedAt: NOW_ISO,  };} function buildFixture(snapshot: OrchestrationReadModel): TestFixture {  return {    snapshot,    serverConfig: createBaseServerConfig(),    welcome: {      environment: {        environmentId: EnvironmentId.make("environment-local"),        label: "Local environment",        platform: { os: "darwin" as const, arch: "arm64" as const },        serverVersion: "0.0.0-test",        capabilities: { repositoryIdentity: true },      },      cwd: "/repo/project",      projectName: "Project",      bootstrapProjectId: PROJECT_ID,      bootstrapThreadId: THREAD_ID,    },  };} function addThreadToSnapshot(  snapshot: OrchestrationReadModel,  threadId: ThreadId,): OrchestrationReadModel {  return {    ...snapshot,    snapshotSequence: snapshot.snapshotSequence + 1,    threads: [      ...snapshot.threads,      {        id: threadId,        projectId: PROJECT_ID,        title: "New thread",        modelSelection: {          provider: "codex",          model: "gpt-5",        },        interactionMode: "default",        runtimeMode: "full-access",        branch: "main",        worktreePath: null,        latestTurn: null,        createdAt: NOW_ISO,        updatedAt: NOW_ISO,        archivedAt: null,        deletedAt: null,        messages: [],        activities: [],        proposedPlans: [],        checkpoints: [],        session: {          threadId,          status: "ready",          providerName: "codex",          runtimeMode: "full-access",          activeTurnId: null,          lastError: null,          updatedAt: NOW_ISO,        },      },    ],  };} function toShellThread(thread: OrchestrationReadModel["threads"][number]) {  return {    id: thread.id,    projectId: thread.projectId,    title: thread.title,    modelSelection: thread.modelSelection,    runtimeMode: thread.runtimeMode,    interactionMode: thread.interactionMode,    branch: thread.branch,    worktreePath: thread.worktreePath,    latestTurn: thread.latestTurn,    createdAt: thread.createdAt,    updatedAt: thread.updatedAt,    archivedAt: thread.archivedAt,    session: thread.session,    latestUserMessageAt:      thread.messages.findLast((message) => message.role === "user")?.createdAt ?? null,    hasPendingApprovals: false,    hasPendingUserInput: false,    hasActionableProposedPlan: false,  };} function toShellSnapshot(snapshot: OrchestrationReadModel) {  return {    snapshotSequence: snapshot.snapshotSequence,    projects: snapshot.projects.map((project) => ({      id: project.id,      title: project.title,      workspaceRoot: project.workspaceRoot,      repositoryIdentity: project.repositoryIdentity ?? null,      defaultModelSelection: project.defaultModelSelection,      scripts: project.scripts,      createdAt: project.createdAt,      updatedAt: project.updatedAt,    })),    threads: snapshot.threads.map(toShellThread),    updatedAt: snapshot.updatedAt,  };} function updateThreadSessionInSnapshot(  snapshot: OrchestrationReadModel,  threadId: ThreadId,  session: OrchestrationReadModel["threads"][number]["session"],): OrchestrationReadModel {  return {    ...snapshot,    snapshotSequence: snapshot.snapshotSequence + 1,    threads: snapshot.threads.map((thread) =>      thread.id === threadId        ? {            ...thread,            session,            updatedAt: NOW_ISO,          }        : thread,    ),  };} function sendShellThreadUpsert(  threadId: ThreadId,  options?: {    readonly session?: OrchestrationReadModel["threads"][number]["session"];

Lines 1–500 of 5787.