Files
openidea/src/features/terminal/session.test.ts
T
arookieofcanda-rookie-of-C-language c5ff0de1ee fix(terminal): open the file a link names, not a same-named one (#711)
resolvePath only understood POSIX paths, so on Windows the direct lookup never matched and every click fell back to a project-wide search by bare file name, which opened any same-named file. Resolve Windows drive paths and backslashes, and make the fallback query include the link's directory and check the hit's project-relative path ends with it; a bare-name match is used only for links without a directory. Fixes #705.

Co-authored-by: a-rookie-of-C-language <[email protected]>
2026-11-02 10:03:59 +08:00

75 lines
3.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { findTerminalLinks, type TerminalLink } from "./links";
import { openFileLink } from "./session";
const ipc = vi.hoisted(() => ({
call: vi.fn(async (v: unknown) => v),
commands: { fsReadDir: vi.fn(), findFiles: vi.fn() },
}));
const workbench = vi.hoisted(() => ({
openFile: vi.fn(async () => undefined),
notify: vi.fn(),
}));
vi.mock("@/platform/ipc", () => ({ call: ipc.call, commands: ipc.commands, isTauri: () => true }));
vi.mock("@/workbench/contributions", () => ({
editorService: () => ({ openFile: workbench.openFile }),
notifications: () => ({ notify: workbench.notify }),
OWNS_FILE_DROP: { "data-owner": true },
}));
const fileLink = (text: string): Extract<TerminalLink, { kind: "file" }> => {
const [l] = findTerminalLinks(text);
if (!l || l.kind !== "file") throw new Error(`not a file link: ${text}`);
return l;
};
describe("openFileLink", () => {
beforeEach(() => vi.clearAllMocks());
it("opens the resolved path when the directory lists it", async () => {
ipc.commands.fsReadDir.mockResolvedValue([{ name: "main.php", kind: "file" }]);
await openFileLink(fileLink("a/main.php:2"), "C:\\Users\\me\\proj");
expect(ipc.commands.fsReadDir).toHaveBeenCalledWith("C:/Users/me/proj/a");
expect(workbench.openFile).toHaveBeenCalledWith("C:/Users/me/proj/a/main.php", {
selection: { line: 1, col: 0 },
focus: true,
});
expect(ipc.commands.findFiles).not.toHaveBeenCalled();
});
it("ranks the link's directory over the bare name in the project-wide fallback (#705)", async () => {
ipc.commands.fsReadDir.mockResolvedValue([]);
ipc.commands.findFiles.mockImplementation(async (q: string) =>
q === "a/main.php"
? [
{ path: "/p/b/main.php", relPath: "b/main.php" },
{ path: "/p/a/main.php", relPath: "a/main.php" },
]
: [{ path: "/p/c/main.php", relPath: "c/main.php" }],
);
await openFileLink(fileLink("a/main.php:2"), "/p");
expect(ipc.commands.findFiles).toHaveBeenCalledWith("a/main.php", 5);
expect(workbench.openFile).toHaveBeenCalledWith("/p/a/main.php", {
selection: { line: 1, col: 0 },
focus: true,
});
});
it("falls back to the bare name for a basename-only link", async () => {
ipc.commands.fsReadDir.mockResolvedValue([]);
ipc.commands.findFiles.mockResolvedValue([{ path: "/p/x/main.php", relPath: "x/main.php" }]);
await openFileLink(fileLink("main.php:1"), "/p");
expect(ipc.commands.findFiles).toHaveBeenCalledTimes(1);
expect(workbench.openFile).toHaveBeenCalledWith("/p/x/main.php", { selection: { line: 0, col: 0 }, focus: true });
});
it("warns when nothing matches", async () => {
ipc.commands.fsReadDir.mockResolvedValue([]);
ipc.commands.findFiles.mockResolvedValue([]);
await openFileLink(fileLink("gone.php:1"), "/p");
expect(workbench.openFile).not.toHaveBeenCalled();
expect(workbench.notify).toHaveBeenCalledWith({ level: "warning", title: "File not found", body: "gone.php" });
});
});