дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
150 рядки
4.9 KiB
TypeScript
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…");
|
|
});
|
|
});
|