1import { describe, expect, it } from "vite-plus/test";
2import type { ProjectEntry } from "@t3tools/contracts";
3
4import { buildFileTree, defaultExpandedTreePaths, flattenFileTree } from "./fileTree";
5
6const entries = [
7 { kind: "file", path: "README.md" },
8 { kind: "directory", path: "src" },
9 { kind: "file", path: "src/index.ts" },
10 { kind: "file", path: "src/components/App.tsx" },
11 { kind: "file", path: "package.json" },
12] satisfies ReadonlyArray<ProjectEntry>;
13
14describe("mobile file tree helpers", () => {
15 it("builds a deterministic hierarchy with directories before files", () => {
16 const tree = buildFileTree(entries);
17
18 expect(tree.map((node) => `${node.kind}:${node.path}`)).toEqual([
19 "directory:src",
20 "file:package.json",
21 "file:README.md",
22 ]);
23 expect(tree[0]?.children.map((node) => `${node.kind}:${node.path}`)).toEqual([
24 "directory:src/components",
25 "file:src/index.ts",
26 ]);
27 });
28
29 it("flattens expanded directories and hides collapsed descendants", () => {
30 const tree = buildFileTree(entries);
31
32 expect(
33 flattenFileTree({
34 nodes: tree,
35 expanded: new Set(["src"]),
36 }).map((item) => `${item.depth}:${item.node.path}`),
37 ).toEqual(["0:src", "1:src/components", "1:src/index.ts", "0:package.json", "0:README.md"]);
38
39 expect(
40 flattenFileTree({
41 nodes: tree,
42 expanded: new Set(),
43 }).map((item) => item.node.path),
44 ).toEqual(["src", "package.json", "README.md"]);
45 });
46
47 it("includes matching descendants and their ancestors during search", () => {
48 const tree = buildFileTree(entries);
49
50 expect(
51 flattenFileTree({
52 nodes: tree,
53 expanded: new Set(),
54 searchQuery: "app",
55 }).map((item) => item.node.path),
56 ).toEqual(["src", "src/components", "src/components/App.tsx"]);
57 });
58
59 it("supports fuzzy, whitespace-separated path queries", () => {
60 const tree = buildFileTree([
61 {
62 kind: "file",
63 path: "docs/internals/workspace-layout.md",
64 },
65 {
66 kind: "file",
67 path: ".repos/alchemy-effect/examples/aws-lambda/src/JobNotifications.ts",
68 },
69 { kind: "directory", path: "apps/web/src/components/chat" },
70 { kind: "file", path: "apps/web/src/components/chat/ChatHeader.test.ts" },
71 { kind: "file", path: "apps/web/src/components/chat/ChatHeader.tsx" },
72 { kind: "file", path: "apps/web/src/components/chat/Composer.tsx" },
73 ]);
74
75 const expectedPaths = [
76 "apps",
77 "apps/web",
78 "apps/web/src",
79 "apps/web/src/components",
80 "apps/web/src/components/chat",
81 "apps/web/src/components/chat/ChatHeader.test.ts",
82 "apps/web/src/components/chat/ChatHeader.tsx",
83 ];
84
85 for (const searchQuery of ["chat hea", "cht hdr"]) {
86 expect(
87 flattenFileTree({
88 nodes: tree,
89 expanded: new Set(),
90 searchQuery,
91 }).map((item) => item.node.path),
92 ).toEqual(expectedPaths);
93 }
94 });
95
96 it("expands top-level directories by default", () => {
97 const tree = buildFileTree(entries);
98
99 expect([...defaultExpandedTreePaths(tree)]).toEqual(["src"]);
100 });
101});