IKHSAN
EN Read in EnglishID

homeio

Publik

Fork dari doctor-io/homeio

Branch
main
Commit terakhir
958d74616 Oktober 2026

useFiles.test.tsx

Buka raw
/* @vitest-environment jsdom */

import { act, renderHook, waitFor } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import {
  buildAssetUrl,
  toFilePath,
  useCreateFile,
  useCreateFolder,
  useFileContent,
  useFilesDirectory,
  usePasteFileEntry,
  useSaveFileContent,
} from "@/modules/files/hooks/useFiles";
import { queryKeys } from "@/lib/shared/query-keys";
import { createTestQueryClient, createWrapper } from "@/test/query-client-wrapper";

describe("useFiles hooks", () => {
  it("loads directory entries from files API", async () => {
    const fetchMock = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        data: {
          root: "/",
          cwd: "Documents",
          entries: [],
        },
        meta: {
          count: 0,
        },
      }),
    });
    vi.stubGlobal("fetch", fetchMock);

    const client = createTestQueryClient();
    const { result } = renderHook(() => useFilesDirectory(["Documents"]), {
      wrapper: createWrapper(client),
    });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(fetchMock).toHaveBeenCalledWith("/api/v1/files?path=Documents", {
      cache: "no-store",
    });
  });

  it("loads file content by path", async () => {
    const fetchMock = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        data: {
          root: "/",
          path: "Documents/notes.txt",
          name: "notes.txt",
          ext: "txt",
          mode: "text",
          mimeType: "text/plain; charset=utf-8",
          sizeBytes: 6,
          modifiedAt: "2026-02-26T12:00:00.000Z",
          mtimeMs: 11,
          content: "hello!",
        },
      }),
    });
    vi.stubGlobal("fetch", fetchMock);

    const client = createTestQueryClient();
    const { result } = renderHook(
      () => useFileContent("Documents/notes.txt"),
      {
        wrapper: createWrapper(client),
      },
    );

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(fetchMock).toHaveBeenCalledWith(
      "/api/v1/files/content?path=Documents%2Fnotes.txt",
      {
        cache: "no-store",
      },
    );
  });

  it("saves file content and invalidates related queries", async () => {
    const fetchMock = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        data: {
          root: "/",
          path: "Documents/notes.txt",
          sizeBytes: 7,
          modifiedAt: "2026-02-26T12:00:05.000Z",
          mtimeMs: 22,
        },
      }),
    });
    vi.stubGlobal("fetch", fetchMock);

    const client = createTestQueryClient();
    const invalidateSpy = vi.spyOn(client, "invalidateQueries");

    const { result } = renderHook(() => useSaveFileContent(), {
      wrapper: createWrapper(client),
    });

    await act(async () => {
      await result.current.mutateAsync({
        path: "Documents/notes.txt",
        content: "updated",
        expectedMtimeMs: 11,
      });
    });

    expect(fetchMock).toHaveBeenCalledWith("/api/v1/files/content", {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        path: "Documents/notes.txt",
        content: "updated",
        expectedMtimeMs: 11,
      }),
    });
    expect(invalidateSpy).toHaveBeenCalledWith({
      queryKey: queryKeys.fileContent("Documents/notes.txt"),
    });
    expect(invalidateSpy).toHaveBeenCalledWith({
      queryKey: ["files", "list", "Documents"],
    });
  });

  it("exports path helpers", () => {
    expect(toFilePath(["Documents", "notes.txt"])).toBe("Documents/notes.txt");
    expect(toFilePath(["Trash", "notes.txt"])).toBe("Trash/notes.txt");
    expect(buildAssetUrl("Media/photo.png")).toBe(
      "/api/v1/files/asset?path=Media%2Fphoto.png",
    );
  });

  it("creates a folder via files ops endpoint", async () => {
    const fetchMock = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        data: {
          root: "/",
          path: "Documents/New Folder",
          type: "folder",
        },
      }),
    });
    vi.stubGlobal("fetch", fetchMock);

    const client = createTestQueryClient();
    const invalidateSpy = vi.spyOn(client, "invalidateQueries");
    const { result } = renderHook(() => useCreateFolder(), {
      wrapper: createWrapper(client),
    });

    await act(async () => {
      await result.current.mutateAsync({
        parentPath: "Documents",
        name: "New Folder",
      });
    });

    expect(fetchMock).toHaveBeenCalledWith("/api/v1/files/ops", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        action: "create_folder",
        parentPath: "Documents",
        name: "New Folder",
      }),
    });
    expect(invalidateSpy).toHaveBeenCalledWith({
      queryKey: ["files", "list", "Documents"],
    });
  });

  it("creates a file via files ops endpoint", async () => {
    const fetchMock = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        data: {
          root: "/",
          path: "Documents/notes.txt",
          type: "file",
        },
      }),
    });
    vi.stubGlobal("fetch", fetchMock);

    const client = createTestQueryClient();
    const { result } = renderHook(() => useCreateFile(), {
      wrapper: createWrapper(client),
    });

    await act(async () => {
      await result.current.mutateAsync({
        parentPath: "Documents",
        name: "notes.txt",
      });
    });

    expect(fetchMock).toHaveBeenCalledWith("/api/v1/files/ops", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        action: "create_file",
        parentPath: "Documents",
        name: "notes.txt",
      }),
    });
  });

  it("pastes copied entries via files ops endpoint", async () => {
    const fetchMock = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => ({
        data: {
          root: "/",
          path: "Documents/photo.jpg",
          type: "file",
        },
      }),
    });
    vi.stubGlobal("fetch", fetchMock);

    const client = createTestQueryClient();
    const invalidateSpy = vi.spyOn(client, "invalidateQueries");
    const { result } = renderHook(() => usePasteFileEntry(), {
      wrapper: createWrapper(client),
    });

    await act(async () => {
      await result.current.mutateAsync({
        sourcePath: "Media/photo.jpg",
        destinationPath: "Documents",
        operation: "copy",
      });
    });

    expect(fetchMock).toHaveBeenCalledWith("/api/v1/files/ops", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        action: "paste",
        sourcePath: "Media/photo.jpg",
        destinationPath: "Documents",
        operation: "copy",
      }),
    });
    expect(invalidateSpy).toHaveBeenCalledWith({
      queryKey: ["files", "list", "Documents"],
    });
    expect(invalidateSpy).toHaveBeenCalledWith({
      queryKey: ["files", "list", "Media"],
    });
  });
});
byte
7.220
baris
273
Jenis
tsx
Commit terakhir
16 Oktober 2026