Files
NetMesh/electron/bridges/clipboardFiles.test.cjs
zhaolei 3c72efcb7f
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
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

308 lines
8.4 KiB
JavaScript

"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const {
createClipboardImageFileName,
decodeWindowsHDrop,
decodeWindowsFileNameW,
hasClipboardImage,
parseClipboardTextFilePaths,
readClipboardFiles,
readClipboardImage,
} = require("./clipboardFiles.cjs");
const createFs = (entries) => ({
existsSync: (filePath) => filePath in entries,
statSync: (filePath) => ({
isDirectory: () => entries[filePath] === "directory",
isFile: () => entries[filePath] === "file",
size: entries[filePath] === "file" ? 42 : 0,
}),
});
test("decodes Windows FileNameW clipboard buffers", () => {
const buffer = Buffer.from("C:\\Users\\me\\a.txt\0\0", "utf16le");
assert.deepEqual(decodeWindowsFileNameW(buffer), [
"C:\\Users\\me\\a.txt",
]);
});
test("decodes Windows CF_HDROP clipboard buffers", () => {
const paths = "C:\\Users\\me\\a.txt\0D:\\b.txt\0\0";
const filesOffset = 20;
const header = Buffer.alloc(filesOffset);
header.writeUInt32LE(filesOffset, 0);
header.writeUInt32LE(1, 16);
const buffer = Buffer.concat([header, Buffer.from(paths, "utf16le")]);
assert.deepEqual(decodeWindowsHDrop(buffer), [
"C:\\Users\\me\\a.txt",
"D:\\b.txt",
]);
});
test("parses text and uri-list clipboard file urls", () => {
const fsImpl = createFs({
"/Users/me/a.txt": "file",
"/Users/me/folder": "directory",
});
const files = parseClipboardTextFilePaths(
"file:///Users/me/a.txt\nfile:///Users/me/folder\n/Users/me/missing.txt",
{ fsImpl, pathImpl: require("node:path") },
);
assert.deepEqual(files, [
{ path: "/Users/me/a.txt", name: "a.txt", isDirectory: false, size: 42 },
{ path: "/Users/me/folder", name: "folder", isDirectory: true, size: 0 },
]);
});
test("parses Windows file urls with drive letters and UNC paths", () => {
const fsImpl = createFs({
"C:\\Users\\me\\a file.txt": "file",
"\\\\server\\share\\b.txt": "file",
});
const files = parseClipboardTextFilePaths(
"file:///C:/Users/me/a%20file.txt\nfile://server/share/b.txt",
{ fsImpl, pathImpl: require("node:path"), windows: true },
);
assert.deepEqual(files, [
{ path: "C:\\Users\\me\\a file.txt", name: "a file.txt", isDirectory: false, size: 42 },
{ path: "\\\\server\\share\\b.txt", name: "b.txt", isDirectory: false, size: 42 },
]);
});
test("ignores plain text paths without file uri formats", () => {
const fsImpl = createFs({ "/Users/me/a.txt": "file" });
assert.deepEqual(parseClipboardTextFilePaths("/Users/me/a.txt", { fsImpl, pathImpl: require("node:path") }), []);
});
test("reads macOS public.file-url clipboard paths", () => {
const fsImpl = createFs({
"/Users/me/folder": "directory",
});
const clipboard = {
availableFormats: () => ["public.file-url"],
read: (format) => {
if (format === "public.file-url") {
return "file:///Users/me/folder";
}
return "";
},
};
assert.deepEqual(readClipboardFiles({ clipboard, fsImpl, pathImpl: require("node:path") }), [
{ path: "/Users/me/folder", name: "folder", isDirectory: true, size: 0 },
]);
});
test("reads macOS NSFilenamesPboardType clipboard paths", () => {
const fsImpl = createFs({
"/Users/me/folder": "directory",
"/Users/me/a.txt": "file",
});
const clipboard = {
availableFormats: () => ["NSFilenamesPboardType"],
read: (format) => {
if (format === "NSFilenamesPboardType") {
return "/Users/me/folder\n/Users/me/a.txt";
}
return "";
},
};
assert.deepEqual(readClipboardFiles({ clipboard, fsImpl, pathImpl: require("node:path") }), [
{ path: "/Users/me/folder", name: "folder", isDirectory: true, size: 0 },
{ path: "/Users/me/a.txt", name: "a.txt", isDirectory: false, size: 42 },
]);
});
test("reads CF_HDROP before falling back to FileNameW", () => {
const filesOffset = 20;
const header = Buffer.alloc(filesOffset);
header.writeUInt32LE(filesOffset, 0);
header.writeUInt32LE(1, 16);
const hdropBuffer = Buffer.concat([
header,
Buffer.from("C:\\Users\\me\\a.txt\0D:\\b.txt\0\0", "utf16le"),
]);
const buffer = Buffer.from("C:\\Users\\me\\a.txt\0\0", "utf16le");
const fsImpl = createFs({ "C:\\Users\\me\\a.txt": "file", "D:\\b.txt": "file" });
const clipboard = {
availableFormats: () => ["CF_HDROP", "FileNameW", "text/plain"],
readBuffer: (format) => {
if (format === "CF_HDROP") return hdropBuffer;
if (format === "FileNameW") return buffer;
return Buffer.alloc(0);
},
readText: () => "file:///fallback.txt",
};
assert.deepEqual(readClipboardFiles({ clipboard, fsImpl, pathImpl: require("node:path") }), [
{ path: "C:\\Users\\me\\a.txt", name: "a.txt", isDirectory: false, size: 42 },
{ path: "D:\\b.txt", name: "b.txt", isDirectory: false, size: 42 },
]);
});
test("creates stable clipboard image file names", () => {
assert.equal(
createClipboardImageFileName(new Date(2026, 5, 11, 3, 4, 5, 6)),
"netcatty-paste-20260611-030405-006.png",
);
});
test("writes clipboard image to Netcatty temp directory", async () => {
const writes = [];
const content = Buffer.from([1, 2, 3]);
const result = await readClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => false,
toPNG: () => content,
}),
},
fsImpl: {
promises: {
writeFile: async (filePath, data) => writes.push({ filePath, data }),
},
},
tempDirBridge: {
getTempFilePath: (name) => `/netcatty-temp/${name}`,
},
now: () => new Date(2026, 5, 11, 3, 4, 5, 6),
});
assert.deepEqual(result, {
path: "/netcatty-temp/netcatty-paste-20260611-030405-006.png",
name: "netcatty-paste-20260611-030405-006.png",
mediaType: "image/png",
size: 3,
});
assert.deepEqual(writes, [
{ filePath: "/netcatty-temp/netcatty-paste-20260611-030405-006.png", data: content },
]);
});
test("returns null when clipboard image is empty", async () => {
const result = await readClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => true,
toPNG: () => Buffer.from([1]),
}),
},
tempDirBridge: {
getTempFilePath: () => "/netcatty-temp/unused.png",
},
});
assert.equal(result, null);
});
test("hasClipboardImage is true when native clipboard holds an image", () => {
assert.equal(
hasClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => false,
getSize: () => ({ width: 10, height: 8 }),
}),
},
}),
true,
);
});
test("hasClipboardImage is true when availableFormats lists an image type", () => {
assert.equal(
hasClipboardImage({
clipboard: {
availableFormats: () => ["text/plain", "image/png"],
readImage: () => assert.fail("formats probe should short-circuit"),
},
}),
true,
);
});
test("hasClipboardImage falls back to readImage when availableFormats throws", () => {
assert.equal(
hasClipboardImage({
clipboard: {
availableFormats: () => {
throw new Error("formats unavailable");
},
readImage: () => ({
isEmpty: () => false,
getSize: () => ({ width: 1, height: 1 }),
}),
},
}),
true,
);
});
test("hasClipboardImage is false when native clipboard image is empty", () => {
assert.equal(
hasClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => true,
getSize: () => ({ width: 10, height: 8 }),
}),
},
}),
false,
);
});
test("hasClipboardImage is false when clipboard image has no measurable size", () => {
assert.equal(
hasClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => false,
getSize: () => ({ width: 0, height: 0 }),
}),
},
}),
false,
);
});
test("hasClipboardImage falls back to toPNG when getSize is unavailable", () => {
assert.equal(
hasClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => false,
toPNG: () => Buffer.from("png"),
}),
},
}),
true,
);
assert.equal(
hasClipboardImage({
clipboard: {
readImage: () => ({
isEmpty: () => false,
}),
},
}),
false,
);
});
test("hasClipboardImage is false when clipboard API is unavailable", () => {
assert.equal(hasClipboardImage({ clipboard: null }), false);
assert.equal(hasClipboardImage({ clipboard: {} }), false);
});