nandi/oripublic Fork 0
db7446cc6b52b6bd9196294c1e66e5eb56754d5e
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

FileTree.test.tsx · 110 lines · 3.4 KBTypeScript Blame HistoryRaw
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday1import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2import { beforeEach, describe, expect, it, vi } from "vitest";
3import type { FileEntry } from "../api";
4import { resetWorkspace, useWorkspace } from "../workspace";
5import { FileTree } from "./FileTree";
6
7const { listFilesMock } = vi.hoisted(() => ({ listFilesMock: vi.fn() }));
8
9vi.mock("../api", () => ({
10 listFiles: listFilesMock,
11}));
12
13const rootEntries: FileEntry[] = [
14 { name: "src", path: "/w/src", isDir: true, size: 0 },
15 { name: "README.md", path: "/w/README.md", isDir: false, size: 7 },
16];
17
18describe("FileTree", () => {
19 beforeEach(() => {
20 resetWorkspace();
21 listFilesMock.mockReset();
22 });
23
24 it("lists the workspace root", async () => {
25 listFilesMock.mockResolvedValue({ path: "/w", entries: rootEntries });
26 render(<FileTree />);
27
28 await waitFor(() => expect(screen.getByText("README.md")).toBeDefined());
29 expect(screen.getByText(/src/)).toBeDefined();
30 expect(listFilesMock.mock.calls[0]).toEqual([]);
31 });
32
33 it("expands a directory lazily", async () => {
34 listFilesMock.mockImplementation((path?: string) =>
35 Promise.resolve(
36 path === "/w/src"
37 ? {
38 path: "/w/src",
39 entries: [
40 {
41 name: "main.go",
42 path: "/w/src/main.go",
43 isDir: false,
44 size: 3,
45 },
46 ],
47 }
48 : { path: "/w", entries: rootEntries },
49 ),
50 );
51 render(<FileTree />);
52 await waitFor(() => expect(screen.getByText(/src/)).toBeDefined());
53
54 fireEvent.click(screen.getByText(/src/));
55 await waitFor(() => expect(screen.getByText("main.go")).toBeDefined());
56 expect(listFilesMock).toHaveBeenCalledWith("/w/src");
57 });
58
59 it("opens a file in preview on click and in editor on double-click", async () => {
60 listFilesMock.mockResolvedValue({ path: "/w", entries: rootEntries });
61 render(<FileTree />);
62 await waitFor(() => expect(screen.getByText("README.md")).toBeDefined());
63
64 fireEvent.click(screen.getByText("README.md"));
65 expect(useWorkspace.getState()).toMatchObject({
66 tab: "preview",
67 previewPath: "/w/README.md",
68 });
69
70 fireEvent.dblClick(screen.getByText("README.md"));
71 expect(useWorkspace.getState()).toMatchObject({
72 tab: "editor",
73 editorPath: "/w/README.md",
74 });
75 });
76
77 it("shows Material icons for files and directories", async () => {
78 listFilesMock.mockResolvedValue({ path: "/w", entries: rootEntries });
79 const { container } = render(<FileTree />);
80 await waitFor(() => expect(screen.getByText("README.md")).toBeDefined());
81
82 const icons = Array.from(
83 container.querySelectorAll("img.filetree-icon"),
84 ).map((img) => img.getAttribute("src"));
85 expect(icons).toHaveLength(2);
86 for (const src of icons) {
87 expect(src).toMatch(/^\/material-icons\/.+\.svg$/);
88 }
89 });
90
91 it("highlights the file open in the preview", async () => {
92 listFilesMock.mockResolvedValue({ path: "/w", entries: rootEntries });
93 const { container } = render(<FileTree />);
94 await waitFor(() => expect(screen.getByText("README.md")).toBeDefined());
95
96 expect(container.querySelector(".filetree-active")).toBeNull();
97 fireEvent.click(screen.getByText("README.md"));
98 await waitFor(() =>
99 expect(container.querySelector(".filetree-active")).not.toBeNull(),
100 );
101 });
102
103 it("surfaces API errors", async () => {
104 listFilesMock.mockRejectedValue(new Error("permission denied"));
105 render(<FileTree />);
106 await waitFor(() =>
107 expect(screen.getByText("permission denied")).toBeDefined(),
108 );
109 });
110});