[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
145
lib/sftpFileUtils.test.ts
Normal file
145
lib/sftpFileUtils.test.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
captureDropPayload,
|
||||
formatDropScanLabel,
|
||||
getFileExtension,
|
||||
hasFileExtension,
|
||||
localTreeToDropEntries,
|
||||
materializeDropEntries,
|
||||
type LocalTreeListEntry,
|
||||
} from "./sftpFileUtils.ts";
|
||||
|
||||
test("hasFileExtension identifies extensionless and dotted filenames", () => {
|
||||
assert.equal(hasFileExtension("nginx"), false);
|
||||
assert.equal(hasFileExtension("my-binary"), false);
|
||||
assert.equal(hasFileExtension(".git"), false);
|
||||
assert.equal(getFileExtension("nginx"), "file");
|
||||
|
||||
assert.equal(hasFileExtension("readme.txt"), true);
|
||||
assert.equal(hasFileExtension(".bashrc"), false);
|
||||
assert.equal(hasFileExtension("archive.tar.gz"), true);
|
||||
});
|
||||
|
||||
test("formatDropScanLabel summarizes dropped root names", () => {
|
||||
assert.equal(formatDropScanLabel([]), "Scanning files...");
|
||||
assert.equal(formatDropScanLabel([{ name: "docs", isDirectory: true }]), "docs");
|
||||
assert.equal(
|
||||
formatDropScanLabel([
|
||||
{ name: "a", isDirectory: true },
|
||||
{ name: "b", isDirectory: true },
|
||||
]),
|
||||
"a, b",
|
||||
);
|
||||
assert.equal(
|
||||
formatDropScanLabel([
|
||||
{ name: "a", isDirectory: true },
|
||||
{ name: "b", isDirectory: true },
|
||||
{ name: "c", isDirectory: true },
|
||||
]),
|
||||
"a, b +1",
|
||||
);
|
||||
});
|
||||
|
||||
test("captureDropPayload reads webkit entries synchronously", () => {
|
||||
const file = new File(["x"], "note.txt");
|
||||
Object.defineProperty(file, "path", { value: "/tmp/note.txt" });
|
||||
const dataTransfer = {
|
||||
items: [{
|
||||
kind: "file",
|
||||
getAsFile: () => file,
|
||||
webkitGetAsEntry: () => ({
|
||||
name: "note.txt",
|
||||
isFile: true,
|
||||
isDirectory: false,
|
||||
}),
|
||||
}],
|
||||
files: [file],
|
||||
} as unknown as DataTransfer;
|
||||
|
||||
const payload = captureDropPayload(dataTransfer);
|
||||
assert.equal(payload.roots.length, 1);
|
||||
assert.equal(payload.roots[0].name, "note.txt");
|
||||
assert.equal(payload.roots[0].isDirectory, false);
|
||||
assert.equal(payload.roots[0].localPath, "/tmp/note.txt");
|
||||
});
|
||||
|
||||
test("materializeDropEntries prefers listLocalTree for directory roots with paths", async () => {
|
||||
const progress: Array<{ fileCount: number; directoryCount: number }> = [];
|
||||
const tree: LocalTreeListEntry[] = [
|
||||
{
|
||||
localPath: "/tmp/project",
|
||||
relativePath: "project",
|
||||
type: "directory",
|
||||
size: 0,
|
||||
lastModified: 1,
|
||||
},
|
||||
{
|
||||
localPath: "/tmp/project/src",
|
||||
relativePath: "project/src",
|
||||
type: "directory",
|
||||
size: 0,
|
||||
lastModified: 1,
|
||||
},
|
||||
{
|
||||
localPath: "/tmp/project/src/main.ts",
|
||||
relativePath: "project/src/main.ts",
|
||||
type: "file",
|
||||
size: 12,
|
||||
lastModified: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const entries = await materializeDropEntries(
|
||||
{
|
||||
roots: [{
|
||||
name: "project",
|
||||
isDirectory: true,
|
||||
localPath: "/tmp/project",
|
||||
}],
|
||||
filesFallback: [],
|
||||
},
|
||||
{
|
||||
listLocalTree: async (path, options) => {
|
||||
assert.equal(path, "/tmp/project");
|
||||
options?.onProgress?.({ fileCount: 1, directoryCount: 2, entryCount: 3 });
|
||||
return tree;
|
||||
},
|
||||
onProgress: (p) => progress.push({ fileCount: p.fileCount, directoryCount: p.directoryCount }),
|
||||
},
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
entries.map((entry) => ({
|
||||
relativePath: entry.relativePath,
|
||||
isDirectory: entry.isDirectory,
|
||||
localPath: entry.localPath,
|
||||
size: entry.size,
|
||||
})),
|
||||
[
|
||||
{ relativePath: "project", isDirectory: true, localPath: "/tmp/project", size: undefined },
|
||||
{ relativePath: "project/src", isDirectory: true, localPath: "/tmp/project/src", size: undefined },
|
||||
{
|
||||
relativePath: "project/src/main.ts",
|
||||
isDirectory: false,
|
||||
localPath: "/tmp/project/src/main.ts",
|
||||
size: 12,
|
||||
},
|
||||
],
|
||||
);
|
||||
assert.ok(progress.some((p) => p.fileCount === 1 && p.directoryCount === 2));
|
||||
});
|
||||
|
||||
test("localTreeToDropEntries preserves file sizes without File handles", () => {
|
||||
const entries = localTreeToDropEntries([{
|
||||
localPath: "/tmp/a.txt",
|
||||
relativePath: "a.txt",
|
||||
type: "file",
|
||||
size: 42,
|
||||
lastModified: 9,
|
||||
}]);
|
||||
assert.equal(entries[0].file, null);
|
||||
assert.equal(entries[0].localPath, "/tmp/a.txt");
|
||||
assert.equal(entries[0].size, 42);
|
||||
});
|
||||
Reference in New Issue
Block a user