Files
NetMesh/components/terminal/useZmodemTransfer.test.ts
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

201 lines
5.5 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import {
reduceZmodemTransferState,
type ZmodemTransferState,
} from "./hooks/useZmodemTransfer.ts";
import { applyZmodemTransferToast, resolveZmodemTransferToast } from "./useTerminalEffects.ts";
const initialState: ZmodemTransferState = {
active: false,
transferType: null,
filename: null,
transferred: 0,
total: 0,
fileIndex: 0,
fileCount: 0,
finalizing: false,
completed: false,
startedAt: null,
updatedAt: null,
bytesPerSecond: null,
error: null,
};
test("ZMODEM state drives success toast after completion", () => {
const detected = reduceZmodemTransferState(initialState, {
type: "detect",
sessionId: "session-1",
transferType: "download",
}, 1_000);
assert.equal(detected.active, true);
assert.equal(detected.transferType, "download");
assert.equal(detected.startedAt, 1_000);
const progressed = reduceZmodemTransferState(detected, {
type: "progress",
sessionId: "session-1",
transferType: "download",
filename: "large.bin",
transferred: 1024 * 1024,
total: 2 * 1024 * 1024,
fileIndex: 0,
fileCount: -1,
}, 2_000);
assert.equal(progressed.active, true);
assert.equal(progressed.filename, "large.bin");
assert.equal(progressed.bytesPerSecond, 1024 * 1024);
const completed = reduceZmodemTransferState(progressed, {
type: "complete",
sessionId: "session-1",
}, 3_000);
assert.equal(completed.active, false);
assert.equal(completed.completed, true);
assert.equal(completed.filename, "large.bin");
assert.equal(completed.transferType, "download");
assert.equal(completed.finalizing, false);
assert.deepEqual(resolveZmodemTransferToast(completed), {
kind: "success",
message: "Downloaded: large.bin",
title: "ZMODEM",
});
});
test("ZMODEM completion without a filename still produces a success toast", () => {
const detected = reduceZmodemTransferState(initialState, {
type: "detect",
sessionId: "session-1",
transferType: "upload",
}, 1_000);
const completed = reduceZmodemTransferState(detected, {
type: "complete",
sessionId: "session-1",
}, 1_500);
assert.deepEqual(resolveZmodemTransferToast(completed), {
kind: "success",
message: "Uploaded",
title: "ZMODEM",
});
});
test("ZMODEM state drives error toast after a transfer fails", () => {
const detected = reduceZmodemTransferState(initialState, {
type: "detect",
sessionId: "session-1",
transferType: "upload",
}, 1_000);
const failed = reduceZmodemTransferState(detected, {
type: "error",
sessionId: "session-1",
error: "Transfer cancelled",
}, 1_500);
assert.equal(failed.active, false);
assert.equal(failed.transferType, "upload");
assert.equal(failed.error, "Transfer cancelled");
assert.equal(failed.finalizing, false);
assert.deepEqual(resolveZmodemTransferToast(failed), {
kind: "error",
message: "Transfer cancelled",
title: "ZMODEM",
});
});
test("ZMODEM progress clears old speed when the next file starts", () => {
const firstFile = reduceZmodemTransferState(initialState, {
type: "progress",
sessionId: "session-1",
transferType: "upload",
filename: "first.bin",
fileIndex: 0,
fileCount: 2,
transferred: 1024 * 1024,
total: 2 * 1024 * 1024,
}, 1_000);
const firstFileProgressed = reduceZmodemTransferState(firstFile, {
type: "progress",
sessionId: "session-1",
filename: "first.bin",
fileIndex: 0,
transferred: 2 * 1024 * 1024,
total: 2 * 1024 * 1024,
}, 2_000);
assert.equal(firstFileProgressed.bytesPerSecond, 1024 * 1024);
const secondFileStarted = reduceZmodemTransferState(firstFileProgressed, {
type: "progress",
sessionId: "session-1",
filename: "second.bin",
fileIndex: 1,
fileCount: 2,
transferred: 0,
total: 1024,
}, 3_000);
assert.equal(secondFileStarted.filename, "second.bin");
assert.equal(secondFileStarted.bytesPerSecond, null);
});
test("ZMODEM toast application calls success and error once per transfer", () => {
const successCalls: Array<[string, string | undefined]> = [];
const errorCalls: Array<[string, string | undefined]> = [];
const toast = {
success: (message: string, title?: string) => successCalls.push([message, title]),
error: (message: string, title?: string) => errorCalls.push([message, title]),
};
const toastedRef = { current: false };
applyZmodemTransferToast({
active: false,
completed: true,
transferType: "upload",
filename: "large.bin",
error: null,
}, toastedRef, toast);
assert.deepEqual(successCalls, [["Uploaded: large.bin", "ZMODEM"]]);
assert.deepEqual(errorCalls, []);
assert.equal(toastedRef.current, true);
applyZmodemTransferToast({
active: false,
completed: true,
transferType: "upload",
filename: "large.bin",
error: null,
}, toastedRef, toast);
assert.deepEqual(successCalls, [["Uploaded: large.bin", "ZMODEM"]]);
assert.deepEqual(errorCalls, []);
applyZmodemTransferToast({
active: true,
completed: false,
transferType: "download",
filename: null,
error: null,
}, toastedRef, toast);
assert.equal(toastedRef.current, false);
applyZmodemTransferToast({
active: false,
completed: false,
transferType: "download",
filename: "large.bin",
error: "Remote closed the transfer",
}, toastedRef, toast);
assert.deepEqual(successCalls, [["Uploaded: large.bin", "ZMODEM"]]);
assert.deepEqual(errorCalls, [["Remote closed the transfer", "ZMODEM"]]);
assert.equal(toastedRef.current, true);
});