193 lines · 5.4 KB · TypeScript
RawDownload
import { EnvironmentId, type ProjectReadFileResult } from "@t3tools/contracts";import * as Effect from "effect/Effect";import { Atom, AtomRegistry } from "effect/unstable/reactivity";import { beforeEach, describe, expect, it, vi } from "vite-plus/test"; const projectMocks = vi.hoisted(() => ({  listEntries: vi.fn(),  optimisticFile: vi.fn(),  readFile: vi.fn(),})); const atomHooks = vi.hoisted(() => ({  registry: null as {    get(atom: object): unknown;    refresh(atom: object): void;  } | null,})); const reactHooks = vi.hoisted(() => {  let cursor = 0;  let refs: Array<{ current: unknown }> = [];  const nextIndex = () => cursor++;   return {    beginRender() {      cursor = 0;    },    reset() {      cursor = 0;      refs = [];    },    useCallback<A>(callback: A): A {      nextIndex();      return callback;    },    useEffect(effect: () => void): void {      nextIndex();      effect();    },    useRef<A>(initialValue: A): { current: A } {      const index = nextIndex();      refs[index] ??= { current: initialValue };      return refs[index] as { current: A };    },  };}); vi.mock("@effect/atom-react", () => ({  useAtomRefresh: (atom: object) => () => {    atomHooks.registry?.refresh(atom);  },  useAtomValue: (atom: object) => atomHooks.registry?.get(atom),})); vi.mock("react", async (importOriginal) => {  const actual = await importOriginal<typeof import("react")>();  return {    ...actual,    useCallback: reactHooks.useCallback,    useEffect: reactHooks.useEffect,    useRef: reactHooks.useRef,  };}); vi.mock("~/state/projects", () => ({  projectEnvironment: projectMocks,})); vi.mock("~/state/queries", () => ({  useProjectPathSearch: vi.fn(),})); import { useWorkspaceMutationRefresh } from "~/hooks/useWorkspaceMutationRefresh";import { useProjectFileQuery } from "./projectFilesQueryState"; const environmentId = EnvironmentId.make("environment-1"); function deferred<A>() {  let resolve!: (value: A) => void;  const promise = new Promise<A>((complete) => {    resolve = complete;  });  return { promise, resolve };} function file(contents: string): ProjectReadFileResult {  return {    relativePath: "src/preview.ts",    contents,    byteLength: contents.length,    truncated: false,  };} async function flushEffects(): Promise<void> {  await Promise.resolve();  await Promise.resolve();  await Promise.resolve();} describe("project file query refresh", () => {  beforeEach(() => {    projectMocks.listEntries.mockReset();    projectMocks.optimisticFile.mockReset();    projectMocks.readFile.mockReset();    reactHooks.reset();  });   it("replaces an in-flight initial read when a workspace mutation arrives", async () => {    const requests: Array<ReturnType<typeof deferred<ProjectReadFileResult>>> = [];    const readAtom = Atom.make(      Effect.promise(() => {        const request = deferred<ProjectReadFileResult>();        requests.push(request);        return request.promise;      }),    ).pipe(Atom.swr({ staleTime: 30_000, revalidateOnMount: true }));    const registry = AtomRegistry.make();    const unmount = registry.mount(readAtom);    projectMocks.readFile.mockReturnValue(readAtom);    projectMocks.optimisticFile.mockReturnValue(Atom.make(null));    atomHooks.registry = registry;    let renderedContents: string | null = null;     const render = (mutationId: string | null) => {      reactHooks.beginRender();      const query = useProjectFileQuery(environmentId, "/repo", "src/preview.ts");      renderedContents = query.data?.contents ?? null;      useWorkspaceMutationRefresh({        mutationId,        refresh: query.refresh,        resourceKey: "file:environment-1:/repo:src/preview.ts",      });    };     try {      render(null);      await flushEffects();      expect(requests).toHaveLength(1);       render("mutation-1");      await flushEffects();      expect(requests).toHaveLength(2);       requests[1]!.resolve(file("fresh"));      await flushEffects();      render("mutation-1");      expect(renderedContents).toBe("fresh");       requests[0]!.resolve(file("stale"));      await flushEffects();      render("mutation-1");      expect(renderedContents).toBe("fresh");    } finally {      unmount();      registry.dispose();      atomHooks.registry = null;    }  });   it("does not issue a file read for a disabled image preview", async () => {    const requests: Array<ReturnType<typeof deferred<ProjectReadFileResult>>> = [];    const readAtom = Atom.make(      Effect.promise(() => {        const request = deferred<ProjectReadFileResult>();        requests.push(request);        return request.promise;      }),    );    const registry = AtomRegistry.make();    projectMocks.readFile.mockReturnValue(readAtom);    projectMocks.optimisticFile.mockReturnValue(Atom.make(null));    atomHooks.registry = registry;     try {      reactHooks.beginRender();      const query = useProjectFileQuery(environmentId, "/repo", "preview.png", false);      useWorkspaceMutationRefresh({        enabled: false,        mutationId: "mutation-1",        refresh: query.refresh,        resourceKey: "file:environment-1:/repo:preview.png",      });      await flushEffects();       expect(projectMocks.readFile).not.toHaveBeenCalled();      expect(requests).toHaveLength(0);    } finally {      registry.dispose();      atomHooks.registry = null;    }  });});