174 lines · 5.5 KB · TypeScript
RawDownload
import { describe, expect, it, vi } from "vite-plus/test"; const testState = vi.hoisted(() => {  let completeProjectFileRead: (value: null) => void = () => undefined;  let projectFileRead = Promise.resolve<null>(null);  let storedDraft: {    readonly draftId: string;    readonly environmentId: string;    readonly promotedTo: null;    readonly threadId: string;  } | null = null;  const router = {    state: {      location: { href: "/" },      matches: [{ params: {} }],    },    navigate: vi.fn(async (request: { readonly params: { readonly draftId: string } }) => {      router.state.location.href = `/draft/${request.params.draftId}`;    }),  };  const draftStore = {    getComposerDraft: vi.fn(() => ({})),    getDraftSessionByLogicalProjectKey: vi.fn(() => storedDraft),    getDraftSession: vi.fn(() => null),    getDraftThread: vi.fn(() => null),    applyStickyState: vi.fn(),    setDraftThreadContext: vi.fn(),    setLogicalProjectDraftThreadId: vi.fn(),    setModelSelection: vi.fn(),  };   return {    completeProjectFileRead: (value: null) => completeProjectFileRead(value),    draftStore,    get projectFileRead() {      return projectFileRead;    },    reset(nextStoredDraft: typeof storedDraft) {      storedDraft = nextStoredDraft;      router.state.location.href = "/";      router.navigate.mockClear();      draftStore.setLogicalProjectDraftThreadId.mockClear();      projectFileRead = new Promise<null>((resolve) => {        completeProjectFileRead = resolve;      });    },    router,  };}); vi.mock("@effect/atom-react", () => ({  useAtomValue: (atom: unknown) =>    atom === "primary-settings"      ? { newWorktreesStartFromOrigin: false }      : new Map([          [            "environment-ssh",            {              settings: {                defaultThreadEnvMode: "local",                newWorktreesStartFromOrigin: false,                defaultModelSelection: null,              },            },          ],        ]),}));vi.mock("@t3tools/client-runtime/environment", () => ({  scopedProjectKey: () => "remote-project",  scopeProjectRef: (environmentId: string, projectId: string) => ({ environmentId, projectId }),  scopeThreadRef: (environmentId: string, threadId: string) => ({ environmentId, threadId }),}));vi.mock("@t3tools/contracts", () => ({  DEFAULT_RUNTIME_MODE: "default",  DEFAULT_SERVER_SETTINGS: {},}));vi.mock("@t3tools/shared/threadEnvMode", () => ({  resolveDefaultThreadEnvMode: (input: {    readonly projectFile: "local" | "worktree" | null;    readonly globalDefault: "local" | "worktree";  }) => input.projectFile ?? input.globalDefault,}));vi.mock("@tanstack/react-router", () => ({  useParams: () => null,  useRouter: () => testState.router,}));vi.mock("react", () => ({  useCallback: <T>(callback: T) => callback,  useMemo: <T>(factory: () => T) => factory(),}));vi.mock("../components/Sidebar.logic", () => ({ orderItemsByPreferredIds: () => [] }));vi.mock("../composerDraftStore", () => {  const useComposerDraftStore = Object.assign(() => null, {    getState: () => testState.draftStore,  });  return {    composerDraftHasUserContent: () => false,    markPromotedDraftThreadByRef: vi.fn(),    useComposerDraftStore,  };});vi.mock("../lib/chatThreadActions", () => ({  hasExplicitComposerModelSelection: () => false,  resolveNewDraftStartFromOrigin: () => false,  resolveNewThreadModelSelectionOverride: () => null,}));vi.mock("../lib/t3ProjectFileDefaults", () => ({  readT3ProjectFileDefaultThreadEnvMode: () => testState.projectFileRead,}));vi.mock("../lib/utils", () => ({  newDraftId: () => "draft-delayed",  newThreadId: () => "thread-delayed",}));vi.mock("../logicalProject", () => ({  deriveLogicalProjectKeyFromSettings: () => "remote-project",  getProjectOrderKey: () => "remote-project",  selectProjectGroupingSettings: () => ({}),}));vi.mock("../state/entities", () => ({  readProjects: () => [    {      id: "project-remote",      environmentId: "environment-ssh",      workspaceRoot: "/remote/project",      defaultThreadEnvMode: null,      defaultModelSelection: null,    },  ],  readThreadShell: () => null,  useProjects: () => [],  useThread: () => null,}));vi.mock("../state/server", () => ({  environmentServerConfigsAtom: {},  primaryServerSettingsAtom: "primary-settings",}));vi.mock("../threadRoutes", () => ({ resolveThreadRouteTarget: () => null }));vi.mock("../uiStateStore", () => ({  legacyProjectCwdPreferenceKey: () => "remote-project",  useUiStateStore: () => [],}));vi.mock("./useSettings", () => ({ useClientSettings: () => ({}) })); import { useNewThreadHandler } from "./useHandleNewThread"; describe("useNewThreadHandler", () => {  it.each([    ["new", null],    [      "reusable",      {        draftId: "draft-existing",        environmentId: "environment-ssh",        promotedTo: null,        threadId: "thread-existing",      },    ],  ])("abandons a delayed %s draft open when the user navigates elsewhere", async (_, draft) => {    testState.reset(draft);    const openThread = useNewThreadHandler();    const pendingOpen = openThread(      { environmentId: "environment-ssh", projectId: "project-remote" } as never,      { replace: true },    );     testState.router.state.location.href = "/usage";    testState.completeProjectFileRead(null);    await pendingOpen;     expect(testState.router.state.location.href).toBe("/usage");    expect(testState.router.navigate).not.toHaveBeenCalled();    expect(testState.draftStore.setLogicalProjectDraftThreadId).not.toHaveBeenCalled();  });});