mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-12 22:59:21 +00:00
* fix(file-manager): accept OS file drops over existing rows * test(file-manager): target the file label in drop tests * test(css): exercise the configured PostCSS plugin chain * test(identity): preserve full Ed25519 public keys in fixtures * test(remote-desktop): always mutate ciphertext in tamper test
142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
import "@testing-library/jest-dom/vitest";
|
|
import { FileManagerGrid } from "../../src/frontend/FileManagerGrid";
|
|
import type { FileItem } from "../../src/frontend/host-types";
|
|
|
|
vi.mock("react-i18next", () => ({
|
|
useTranslation: () => ({ t: (key: string) => key }),
|
|
initReactI18next: { type: "3rdParty", init: () => {} },
|
|
}));
|
|
|
|
vi.mock("@tanstack/react-virtual", () => ({
|
|
useVirtualizer: ({ count }: { count: number }) => ({
|
|
getTotalSize: () => count * 29,
|
|
getVirtualItems: () =>
|
|
Array.from({ length: count }, (_, index) => ({
|
|
index,
|
|
key: index,
|
|
start: index * 29,
|
|
size: 29,
|
|
})),
|
|
measureElement: () => {},
|
|
measure: () => {},
|
|
}),
|
|
}));
|
|
|
|
const file: FileItem = {
|
|
name: "notes.txt",
|
|
path: "/notes.txt",
|
|
type: "file",
|
|
modified: "2026-09-01",
|
|
owner: "owner",
|
|
permissions: "-rw-r--r--",
|
|
};
|
|
const props = {
|
|
files: [file],
|
|
selectedFiles: [file],
|
|
viewMode: "list" as const,
|
|
onFileOpen: vi.fn(),
|
|
onSelectionChange: vi.fn(),
|
|
onRefresh: vi.fn(),
|
|
};
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.restoreAllMocks();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe("remote file rename", () => {
|
|
it.each(["F2", "F6"])("starts rename with %s in the visible pane", (key) => {
|
|
vi.spyOn(HTMLElement.prototype, "offsetParent", "get").mockReturnValue(
|
|
document.body,
|
|
);
|
|
const onStartEdit = vi.fn();
|
|
render(<FileManagerGrid {...props} onStartEdit={onStartEdit} />);
|
|
fireEvent.keyDown(document, { key });
|
|
expect(onStartEdit).toHaveBeenCalledExactlyOnceWith(file);
|
|
});
|
|
|
|
it("does not rename files in a hidden pane", () => {
|
|
vi.spyOn(HTMLElement.prototype, "offsetParent", "get").mockReturnValue(
|
|
null,
|
|
);
|
|
const onStartEdit = vi.fn();
|
|
render(<FileManagerGrid {...props} onStartEdit={onStartEdit} />);
|
|
fireEvent.keyDown(document, { key: "F2" });
|
|
expect(onStartEdit).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("hides metadata while editing and commits the new name", () => {
|
|
const onRename = vi.fn();
|
|
const { rerender } = render(<FileManagerGrid {...props} />);
|
|
expect(screen.getByText(file.modified!)).toBeInTheDocument();
|
|
rerender(
|
|
<FileManagerGrid
|
|
{...props}
|
|
density="compact"
|
|
editingFile={file}
|
|
onRename={onRename}
|
|
/>,
|
|
);
|
|
expect(screen.queryByText(file.modified!)).not.toBeInTheDocument();
|
|
expect(screen.queryByText(file.permissions!)).not.toBeInTheDocument();
|
|
const input = screen.getByRole("textbox");
|
|
fireEvent.change(input, { target: { value: "renamed.txt" } });
|
|
fireEvent.keyDown(input, { key: "Enter" });
|
|
expect(onRename).toHaveBeenCalledWith(file, "renamed.txt");
|
|
rerender(<FileManagerGrid {...props} />);
|
|
expect(screen.getByText(file.modified!)).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe("external file drops", () => {
|
|
beforeEach(() => {
|
|
vi.stubGlobal(
|
|
"ResizeObserver",
|
|
class {
|
|
observe() {}
|
|
disconnect() {}
|
|
},
|
|
);
|
|
});
|
|
it.each(["list", "grid"] as const)(
|
|
"uploads dropped folders over a file in %s view",
|
|
(viewMode) => {
|
|
const onUploadItems = vi.fn();
|
|
render(
|
|
<FileManagerGrid
|
|
{...props}
|
|
viewMode={viewMode}
|
|
onUploadItems={onUploadItems}
|
|
/>,
|
|
);
|
|
const folder = {
|
|
name: "logs",
|
|
isDirectory: true,
|
|
isFile: false,
|
|
} as FileSystemEntry;
|
|
fireEvent.drop(screen.getByText(file.name), {
|
|
dataTransfer: {
|
|
types: ["Files"],
|
|
files: [],
|
|
getData: () => "",
|
|
items: [{ webkitGetAsEntry: () => folder }],
|
|
},
|
|
});
|
|
expect(onUploadItems).toHaveBeenCalledExactlyOnceWith([folder]);
|
|
},
|
|
);
|
|
|
|
it("uploads a plain OS file dropped over a row", () => {
|
|
const onUpload = vi.fn();
|
|
render(<FileManagerGrid {...props} onUpload={onUpload} />);
|
|
const files = [new File(["log"], "app.log")];
|
|
fireEvent.drop(screen.getByText(file.name), {
|
|
dataTransfer: { types: ["Files"], files, items: [], getData: () => "" },
|
|
});
|
|
expect(onUpload).toHaveBeenCalledExactlyOnceWith(files);
|
|
});
|
|
});
|