1import { EnvironmentId } from "@t3tools/contracts";
2import * as NodeAssert from "node:assert/strict";
3import { describe, expect, it } from "vite-plus/test";
4
5import { buildFileContextMenuItems, resolveFileContextMenuAbsolutePath } from "./fileContextMenu";
6
7const BASE_TARGET = {
8 environmentId: EnvironmentId.make("environment-local"),
9 filePath: "src/index.ts",
10 workspaceRoot: "/workspace/project",
11};
12
13const EMPTY_CAPABILITIES = {
14 revealLabel: undefined,
15 canOpenDefault: false,
16 editorIds: [],
17};
18
19describe("resolveFileContextMenuAbsolutePath", () => {
20 it("joins workspace-relative diff paths onto the workspace root", () => {
21 expect(resolveFileContextMenuAbsolutePath(BASE_TARGET)).toBe("/workspace/project/src/index.ts");
22 });
23
24 it("strips the repository prefix when the repo root is nested in the workspace", () => {
25 expect(
26 resolveFileContextMenuAbsolutePath({
27 ...BASE_TARGET,
28 workspaceRoot: "/workspace/project/packages/app",
29 repositoryRoot: "/workspace/project",
30 filePath: "packages/app/src/index.ts",
31 }),
32 ).toBe("/workspace/project/packages/app/src/index.ts");
33 });
34
35 it("returns null for paths outside the workspace when a repository root is set", () => {
36 expect(
37 resolveFileContextMenuAbsolutePath({
38 ...BASE_TARGET,
39 workspaceRoot: "/workspace/project/packages/app",
40 repositoryRoot: "/workspace/project",
41 filePath: "other/src/index.ts",
42 }),
43 ).toBeNull();
44 });
45
46 it("rejects absolute paths without a workspace root, matching diff path resolution", () => {
47 expect(
48 resolveFileContextMenuAbsolutePath({
49 ...BASE_TARGET,
50 workspaceRoot: undefined,
51 filePath: "/absolute/src/index.ts",
52 }),
53 ).toBeNull();
54 });
55});
56
57describe("buildFileContextMenuItems", () => {
58 it("offers open, reveal, and an open-with submenu when all are available", () => {
59 const items = buildFileContextMenuItems({
60 hasAbsolutePath: true,
61 capabilities: {
62 revealLabel: "Reveal in Finder",
63 canOpenDefault: true,
64 editorIds: ["vscode", "cursor", "file-manager"],
65 },
66 });
67
68 expect(items.map((item) => item.id)).toEqual(["open", "reveal-in-folder", "open-with"]);
69 expect(items[0]).toMatchObject({ label: "Open" });
70 expect(items[1]).toMatchObject({ label: "Reveal in Finder" });
71 const openWith = items[2];
72 NodeAssert.ok(openWith);
73 expect(openWith.children?.map((child) => child.id)).toEqual(["editor:vscode", "editor:cursor"]);
74 });
75
76 it("offers only the reveal item when just reveal is enabled", () => {
77 const items = buildFileContextMenuItems({
78 hasAbsolutePath: true,
79 capabilities: {
80 revealLabel: "Reveal in File Explorer",
81 canOpenDefault: false,
82 editorIds: [],
83 },
84 });
85
86 expect(items.map((item) => item.id)).toEqual(["reveal-in-folder"]);
87 expect(items[0]).toMatchObject({ label: "Reveal in File Explorer" });
88 });
89
90 it("offers nothing when the path cannot be resolved", () => {
91 expect(
92 buildFileContextMenuItems({
93 hasAbsolutePath: false,
94 capabilities: {
95 revealLabel: "Reveal in Finder",
96 canOpenDefault: true,
97 editorIds: ["vscode"],
98 },
99 }),
100 ).toEqual([]);
101 });
102});