Файли
openidea/src/features/project-tree/contextMenu.test.ts
T

150 рядки
4.9 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { registerActions } from "@/platform/actions";
import { useProject } from "@/platform/project";
import { resolveMenuItems, type MenuItem } from "@/ui";
import { registerMenuGroup } from "@/workbench/contributions";
import { contextMenuItems, revealLabel } from "./contextMenu";
import type { ProjectNode } from "./model";
import { useProjectTree } from "./store";
const ROOT = "/p";
const SCRATCHES = "/config/scratches";
const node = (path: string, dir = false, root = false): ProjectNode => ({
key: path,
path,
chain: [path],
label: path.slice(path.lastIndexOf("/") + 1),
dir,
root,
entry: null,
ignored: false,
symlink: false,
});
/** Labels as the menu shows them ("-" for separators). */
const labels = (items: MenuItem[]) => resolveMenuItems(items).map((r) => (r.kind === "separator" ? "-" : r.label));
const sub = (items: MenuItem[], label: string) => {
const item = resolveMenuItems(items).find((r) => r.label === label);
return item?.submenu ? labels(item.submenu) : undefined;
};
const OTHER_ACTIONS = [
"find.inPath",
"find.replaceInPath",
"code.reformatFile",
"code.optimizeImportsFile",
"terminal.openAt",
"vcs.localHistory",
"localHistory.putLabel",
"diff.compareWith",
"diff.compareFiles",
"vcs.commit",
"vcs.addToVcs",
"vcs.annotate",
"vcs.showDiff",
"vcs.rollback",
"vcs.push",
];
let disposers: Array<() => void> = [];
beforeEach(() => {
useProjectTree.setState({ root: ROOT });
useProject.setState({ project: { name: "p", root: ROOT, isGit: true, settings: {} } as never, settings: {} as never });
disposers = [
registerActions(OTHER_ACTIONS.map((id) => ({ id, title: id, run: () => undefined }))),
registerMenuGroup({
id: "test.run",
menus: ["projectView"],
items: (_menu, ctx) =>
ctx.paths.length === 1 && !ctx.dirs?.[0] ? [{ label: "Run 'a'", icon: "run", run: () => undefined }] : [],
}),
];
});
afterEach(() => {
disposers.forEach((d) => d());
useProject.setState({ project: null, settings: null });
useProjectTree.setState({ root: null });
});
describe("project view context menu", () => {
it("follows IDEA's order for a file", () => {
const items = contextMenuItems([node(`${ROOT}/src/a.py`)]);
expect(labels(items)).toEqual([
"New",
"-",
"Cut",
"Copy",
"Copy Path/Reference…",
"Paste",
"-",
"Find Usages",
"Find in Files…",
"Replace in Files…",
"-",
"Refactor",
"-",
"Reformat Code",
"Optimize Imports",
"Delete…",
"-",
"Run 'a'",
"-",
"Open In",
"-",
"Local History",
"Reload from Disk",
"-",
"Git",
"-",
"Compare With…",
]);
expect(sub(items, "Refactor")).toEqual(["Rename…", "Move File…", "-", "Safe Delete…"]);
expect(sub(items, "Open In")).toEqual([revealLabel(), "Terminal"]);
expect(sub(items, "Git")?.slice(0, 4)).toEqual(["Commit File…", "Add", "-", "Annotate with Git Blame"]);
});
it("shows Mark Directory as for folders, without file-only items", () => {
const l = labels(contextMenuItems([node(`${ROOT}/src`, true)]));
expect(l).toContain("Mark Directory as");
expect(sub(contextMenuItems([node(`${ROOT}/src`, true)]), "Refactor")).toContain("Move Directory…");
for (const fileOnly of ["Find Usages", "Reformat Code", "Optimize Imports", "Compare With…", "Run 'a'"])
expect(l).not.toContain(fileOnly);
});
it("keeps the project root menu short and disables what cannot apply", () => {
const items = contextMenuItems([node(ROOT, true, true)]);
const l = labels(items);
expect(l).not.toContain("Refactor");
expect(l).not.toContain("Mark Directory as");
const resolved = resolveMenuItems(items);
for (const label of ["Cut", "Copy", "Delete…"]) expect(resolved.find((r) => r.label === label)?.disabled).toBe(true);
});
it("drops project-only groups for scratch files", () => {
const l = labels(contextMenuItems([node(`${SCRATCHES}/s.py`)]));
expect(l).not.toContain("Git");
expect(l).not.toContain("Find Usages");
expect(l).toContain("Refactor");
expect(l).toContain("Reload from Disk");
});
it("offers Compare Files for two files", () => {
const l = labels(contextMenuItems([node(`${ROOT}/a.py`), node(`${ROOT}/b.py`)]));
expect(l).toContain("Compare Files");
expect(l).not.toContain("Compare With…");
});
it("never shows an empty submenu", () => {
disposers.forEach((d) => d());
disposers = [];
useProject.setState({ project: { name: "p", root: ROOT, isGit: true, settings: {} } as never, settings: {} as never });
const l = labels(contextMenuItems([node(`${ROOT}/a.py`)]));
expect(l).not.toContain("Git");
expect(l).not.toContain("Local History");
expect(l).not.toContain("Find in Files…");
});
});