1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 claimWorkspaceBasenameLookup,
5 needsWorkspaceBasenameLookup,
6 pickWorkspaceBasenameMatch,
7} from "./workspaceBasenameLookup";
8
9describe("needsWorkspaceBasenameLookup", () => {
10 it("flags bare filenames", () => {
11 expect(needsWorkspaceBasenameLookup("ChatView.tsx")).toBe(true);
12 expect(needsWorkspaceBasenameLookup("Makefile")).toBe(true);
13 });
14
15 it("leaves anything with a directory alone", () => {
16 expect(needsWorkspaceBasenameLookup("apps/web/src/components/ChatView.tsx")).toBe(false);
17 expect(needsWorkspaceBasenameLookup("apps\\web\\ChatView.tsx")).toBe(false);
18 expect(needsWorkspaceBasenameLookup(".")).toBe(false);
19 expect(needsWorkspaceBasenameLookup("..")).toBe(false);
20 expect(needsWorkspaceBasenameLookup(" ")).toBe(false);
21 });
22});
23
24describe("pickWorkspaceBasenameMatch", () => {
25 const entries = [
26 { path: "apps/web/src/components/ChatView.test.tsx", kind: "file" as const },
27 { path: "apps/web/src/components/ChatView.tsx", kind: "file" as const },
28 ];
29
30 it("takes the first exact filename match, not the closest fuzzy one", () => {
31 expect(pickWorkspaceBasenameMatch("ChatView.tsx", entries)).toBe(
32 "apps/web/src/components/ChatView.tsx",
33 );
34 });
35
36 it("ignores directories", () => {
37 expect(
38 pickWorkspaceBasenameMatch("components", [
39 { path: "apps/web/src/components", kind: "directory" },
40 { path: "apps/web/src/components/components", kind: "file" },
41 ]),
42 ).toBe("apps/web/src/components/components");
43 });
44
45 it("prefers the exactly-cased file over a case-only twin", () => {
46 expect(
47 pickWorkspaceBasenameMatch("foo.ts", [
48 { path: "src/Foo.ts", kind: "file" },
49 { path: "src/foo.ts", kind: "file" },
50 ]),
51 ).toBe("src/foo.ts");
52 });
53
54 it("falls back to case-insensitive when only the casing differs", () => {
55 expect(pickWorkspaceBasenameMatch("chatview.tsx", entries)).toBe(
56 "apps/web/src/components/ChatView.tsx",
57 );
58 });
59
60 it("returns null when the case-insensitive fallback is ambiguous", () => {
61 expect(
62 pickWorkspaceBasenameMatch("FOO.ts", [
63 { path: "src/Foo.ts", kind: "file" },
64 { path: "src/foo.ts", kind: "file" },
65 ]),
66 ).toBeNull();
67 });
68
69 it("returns null when nothing matches the name", () => {
70 expect(pickWorkspaceBasenameMatch("ChatView.tsx", [])).toBeNull();
71 expect(
72 pickWorkspaceBasenameMatch("ChatView.tsx", [
73 { path: "apps/web/src/components/ChatHeader.tsx", kind: "file" },
74 ]),
75 ).toBeNull();
76 });
77});
78
79describe("claimWorkspaceBasenameLookup", () => {
80 it("keeps only the newest claim, whatever order the lookups settle in", () => {
81 const first = claimWorkspaceBasenameLookup();
82 const second = claimWorkspaceBasenameLookup();
83
84 // The older lookup answering last must not reopen the panel behind the
85 // newer one.
86 expect(second()).toBe(true);
87 expect(first()).toBe(false);
88 });
89
90 it("stays valid while it is the only claim", () => {
91 const only = claimWorkspaceBasenameLookup();
92 expect(only()).toBe(true);
93 });
94});