eigent/test/unit/electron/main/localPathActions.test.ts
Douglas Lai 1edaab9819
feat: make the Files page a usable project workspace (#1859)
Co-authored-by: 4pmtong <web_chentong@163.com>
2026-08-21 20:09:02 +08:00

256 lines
9.2 KiB
TypeScript

// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
import fsp from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
authorizeWorkspaceLocalNode,
openWorkspaceLocalFile,
rememberBoundedPathGrant,
revealUserVisibleLocalNode,
revealWorkspaceLocalNode,
type LocalPathShell,
} from '../../../../electron/main/localPathActions';
const temporaryDirectories: string[] = [];
async function temporaryWorkspace(): Promise<string> {
const directory = await fsp.mkdtemp(
path.join(os.tmpdir(), 'eigent-path-actions-')
);
temporaryDirectories.push(directory);
return directory;
}
function shellMock() {
return {
openPath: vi.fn(async () => ''),
showItemInFolder: vi.fn(),
} satisfies LocalPathShell;
}
afterEach(async () => {
await Promise.all(
temporaryDirectories
.splice(0)
.map((directory) => fsp.rm(directory, { recursive: true, force: true }))
);
});
describe('local path actions', () => {
it('evicts the least-recently-used exact grant at the configured limit', () => {
const grants = new Set<string>();
rememberBoundedPathGrant(grants, '/picked/first', 2);
rememberBoundedPathGrant(grants, '/picked/second', 2);
rememberBoundedPathGrant(grants, '/picked/first', 2);
rememberBoundedPathGrant(grants, '/picked/third', 2);
expect([...grants]).toEqual(['/picked/first', '/picked/third']);
});
it('opens an exact selected folder instead of its parent', async () => {
const workspace = await temporaryWorkspace();
const selectedFolder = path.join(workspace, 'reports', 'final');
await fsp.mkdir(selectedFolder, { recursive: true });
const targetShell = shellMock();
await expect(
revealWorkspaceLocalNode(selectedFolder, [workspace], targetShell)
).resolves.toEqual({ success: true });
expect(targetShell.openPath).toHaveBeenCalledWith(
await fsp.realpath(selectedFolder)
);
expect(targetShell.showItemInFolder).not.toHaveBeenCalled();
});
it('reveals and highlights an exact selected file', async () => {
const workspace = await temporaryWorkspace();
const selectedFile = path.join(workspace, 'reports', 'summary.md');
await fsp.mkdir(path.dirname(selectedFile), { recursive: true });
await fsp.writeFile(selectedFile, '# Summary');
const targetShell = shellMock();
await expect(
revealWorkspaceLocalNode(selectedFile, [workspace], targetShell)
).resolves.toEqual({ success: true });
expect(targetShell.showItemInFolder).toHaveBeenCalledWith(
await fsp.realpath(selectedFile)
);
expect(targetShell.openPath).not.toHaveBeenCalled();
});
it('opens an exact selected file with its default app', async () => {
const workspace = await temporaryWorkspace();
const selectedFile = path.join(workspace, 'report.pdf');
await fsp.writeFile(selectedFile, 'pdf');
const targetShell = shellMock();
await expect(
openWorkspaceLocalFile(selectedFile, [workspace], targetShell)
).resolves.toEqual({ success: true });
expect(targetShell.openPath).toHaveBeenCalledWith(
await fsp.realpath(selectedFile)
);
});
it('authorizes both file and folder editor targets as exact real paths', async () => {
const workspace = await temporaryWorkspace();
const selectedFolder = path.join(workspace, 'src');
const selectedFile = path.join(selectedFolder, 'index.ts');
await fsp.mkdir(selectedFolder);
await fsp.writeFile(selectedFile, 'export {};');
await expect(
authorizeWorkspaceLocalNode(selectedFolder, [workspace])
).resolves.toMatchObject({
success: true,
path: await fsp.realpath(selectedFolder),
kind: 'directory',
});
await expect(
authorizeWorkspaceLocalNode(selectedFile, [workspace])
).resolves.toMatchObject({
success: true,
path: await fsp.realpath(selectedFile),
kind: 'file',
});
});
it('rejects paths outside the active workspace before opening them', async () => {
const root = await temporaryWorkspace();
const workspace = path.join(root, 'workspace');
const outsideFile = path.join(root, 'secret.txt');
await fsp.mkdir(workspace);
await fsp.writeFile(outsideFile, 'secret');
const targetShell = shellMock();
await expect(
revealWorkspaceLocalNode(outsideFile, [workspace], targetShell)
).resolves.toEqual({
success: false,
error: 'Path is outside the active workspace',
});
await expect(
openWorkspaceLocalFile(outsideFile, [workspace], targetShell)
).resolves.toEqual({
success: false,
error: 'Path is outside the active workspace',
});
expect(targetShell.openPath).not.toHaveBeenCalled();
expect(targetShell.showItemInFolder).not.toHaveBeenCalled();
});
it('blocks executable default-app opens and reveals the file instead', async () => {
const workspace = await temporaryWorkspace();
const executableFile = path.join(workspace, 'install.command');
await fsp.writeFile(executableFile, '#!/bin/sh');
const targetShell = shellMock();
await expect(
openWorkspaceLocalFile(executableFile, [workspace], targetShell)
).resolves.toEqual({
success: false,
error: 'Executable files cannot be opened from an agent result',
});
expect(targetShell.openPath).not.toHaveBeenCalled();
expect(targetShell.showItemInFolder).toHaveBeenCalledWith(
await fsp.realpath(executableFile)
);
});
it('reveals an executable bundle directory instead of launching it', async () => {
const workspace = await temporaryWorkspace();
const bundle = path.join(workspace, 'Payload.app');
await fsp.mkdir(path.join(bundle, 'Contents', 'MacOS'), {
recursive: true,
});
const targetShell = shellMock();
await expect(
revealWorkspaceLocalNode(bundle, [workspace], targetShell)
).resolves.toEqual({ success: true });
// shell.openPath would execute the bundle rather than browse it.
expect(targetShell.openPath).not.toHaveBeenCalled();
expect(targetShell.showItemInFolder).toHaveBeenCalledWith(
await fsp.realpath(bundle)
);
});
it('still browses an ordinary workspace folder', async () => {
const workspace = await temporaryWorkspace();
const plainFolder = path.join(workspace, 'reports.output');
await fsp.mkdir(plainFolder, { recursive: true });
const targetShell = shellMock();
await expect(
revealWorkspaceLocalNode(plainFolder, [workspace], targetShell)
).resolves.toEqual({ success: true });
expect(targetShell.openPath).toHaveBeenCalledWith(
await fsp.realpath(plainFolder)
);
expect(targetShell.showItemInFolder).not.toHaveBeenCalled();
});
it('reveals a user-picked attachment stored outside every Space root', async () => {
const workspace = await temporaryWorkspace();
const elsewhere = await temporaryWorkspace();
const attachment = path.join(elsewhere, 'contract.pdf');
await fsp.writeFile(attachment, 'pdf');
const realAttachment = await fsp.realpath(attachment);
const targetShell = shellMock();
await expect(
revealUserVisibleLocalNode(
attachment,
[workspace],
new Set([realAttachment]),
targetShell
)
).resolves.toEqual({ success: true });
expect(targetShell.showItemInFolder).toHaveBeenCalledWith(realAttachment);
});
it('rejects an outside path the user never picked', async () => {
const workspace = await temporaryWorkspace();
const elsewhere = await temporaryWorkspace();
const secret = path.join(elsewhere, 'id_rsa');
await fsp.writeFile(secret, 'key');
const targetShell = shellMock();
await expect(
revealUserVisibleLocalNode(secret, [workspace], new Set(), targetShell)
).resolves.toEqual({
success: false,
error: 'Path is outside the active workspace',
});
expect(targetShell.openPath).not.toHaveBeenCalled();
expect(targetShell.showItemInFolder).not.toHaveBeenCalled();
});
it('does not launch a bundle that the user picked in the file dialog', async () => {
const elsewhere = await temporaryWorkspace();
const bundle = path.join(elsewhere, 'Installer.app');
await fsp.mkdir(bundle, { recursive: true });
const realBundle = await fsp.realpath(bundle);
const targetShell = shellMock();
await expect(
revealUserVisibleLocalNode(bundle, [], new Set([realBundle]), targetShell)
).resolves.toEqual({ success: true });
expect(targetShell.openPath).not.toHaveBeenCalled();
expect(targetShell.showItemInFolder).toHaveBeenCalledWith(realBundle);
});
});