[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:
657
components/terminalHelpers.test.ts
Normal file
657
components/terminalHelpers.test.ts
Normal file
@@ -0,0 +1,657 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import type { Host } from "../domain/models";
|
||||
import {
|
||||
AUTO_RUN_SNIPPET_LINE_DELAY_MS,
|
||||
resolveDisconnectedDialogTerminalRoot,
|
||||
restoreTerminalFocusFromDisconnectedDialog,
|
||||
shouldClaimDisconnectedDialogFocus,
|
||||
shouldHideConnectingDialogForConnectionReuse,
|
||||
shouldDelayAutoRunSnippetInput,
|
||||
shouldReconnectDisconnectedDialogOnEnterKey,
|
||||
shouldRestoreDisconnectedDialogTerminalFocus,
|
||||
shouldShowTerminalDisconnectedNotice,
|
||||
shouldShowTerminalConnectionDialog,
|
||||
} from "./terminal/terminalHelpers";
|
||||
|
||||
const host = (overrides: Partial<Host> = {}): Host => ({
|
||||
id: "host-1",
|
||||
label: "Host",
|
||||
hostname: "example.com",
|
||||
username: "alice",
|
||||
authMethod: "password",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
test("disconnected dialog reconnects on plain Enter when no control owns focus", () => {
|
||||
assert.equal(
|
||||
shouldReconnectDisconnectedDialogOnEnterKey({
|
||||
key: "Enter",
|
||||
enabled: true,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldReconnectDisconnectedDialogOnEnterKey({
|
||||
key: "Enter",
|
||||
enabled: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldReconnectDisconnectedDialogOnEnterKey({
|
||||
key: "a",
|
||||
enabled: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldReconnectDisconnectedDialogOnEnterKey({
|
||||
key: "Enter",
|
||||
enabled: true,
|
||||
shiftKey: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("disconnected dialog leaves Enter to focused buttons and inputs", () => {
|
||||
class FakeElement {
|
||||
constructor(private readonly selectorMatch: boolean) {}
|
||||
closest(selector: string) {
|
||||
void selector;
|
||||
return this.selectorMatch ? this : null;
|
||||
}
|
||||
}
|
||||
const previousHTMLElement = globalThis.HTMLElement;
|
||||
// @ts-expect-error test double for DOM instanceof checks
|
||||
globalThis.HTMLElement = FakeElement;
|
||||
try {
|
||||
const buttonLike = new FakeElement(true);
|
||||
const plain = new FakeElement(false);
|
||||
assert.equal(
|
||||
shouldReconnectDisconnectedDialogOnEnterKey({
|
||||
key: "Enter",
|
||||
enabled: true,
|
||||
target: buttonLike as unknown as EventTarget,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldReconnectDisconnectedDialogOnEnterKey({
|
||||
key: "Enter",
|
||||
enabled: true,
|
||||
target: plain as unknown as EventTarget,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
} finally {
|
||||
globalThis.HTMLElement = previousHTMLElement;
|
||||
}
|
||||
});
|
||||
|
||||
test("disconnected dialog focus claim never steals from other panes", () => {
|
||||
class FakeNode {
|
||||
constructor(
|
||||
private readonly opts: {
|
||||
id: string;
|
||||
parent?: FakeNode | null;
|
||||
interactive?: boolean;
|
||||
children?: FakeNode[];
|
||||
},
|
||||
) {
|
||||
for (const child of opts.children ?? []) {
|
||||
child.opts.parent = this;
|
||||
}
|
||||
}
|
||||
get parentElement() {
|
||||
return this.opts.parent ?? null;
|
||||
}
|
||||
contains(other: FakeNode | null) {
|
||||
if (!other) return false;
|
||||
let cur: FakeNode | null | undefined = other;
|
||||
while (cur) {
|
||||
if (cur === this) return true;
|
||||
cur = cur.opts.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
closest(selector: string) {
|
||||
if (selector.includes("button") && this.opts.interactive) return this;
|
||||
return null;
|
||||
}
|
||||
querySelector(selector: string) {
|
||||
if (!selector.includes("xterm-helper-textarea")) return null;
|
||||
return (this.opts.children ?? []).find((c) => c.opts.id === "textarea") ?? null;
|
||||
}
|
||||
focusCalls = 0;
|
||||
focus() {
|
||||
this.focusCalls += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const previousHTMLElement = globalThis.HTMLElement;
|
||||
// @ts-expect-error test double for DOM instanceof checks
|
||||
globalThis.HTMLElement = FakeNode;
|
||||
try {
|
||||
const body = new FakeNode({ id: "body" });
|
||||
const otherPane = new FakeNode({ id: "other" });
|
||||
const textarea = new FakeNode({ id: "textarea" });
|
||||
const dialogButton = new FakeNode({ id: "btn", interactive: true });
|
||||
const dialog = new FakeNode({ id: "dialog", children: [dialogButton] });
|
||||
const session = new FakeNode({ id: "session", children: [textarea, dialog] });
|
||||
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: null,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
// Unfocused split sibling must not claim when focus is on body/null.
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: null,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
isFocusedPane: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
isFocusedPane: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
isFocusedPane: true,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: textarea as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: otherPane as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: dialogButton as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: dialog as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: dialog as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(textarea.focusCalls, 1);
|
||||
// After React removes the focused dialog, the browser parks focus on body
|
||||
// before passive-effect cleanup runs — still restore to this session's xterm.
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(textarea.focusCalls, 2);
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: otherPane as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(textarea.focusCalls, 2);
|
||||
|
||||
// Popup terminals have no data-session-id ancestor — restore via local tree walk.
|
||||
const popupTextarea = new FakeNode({ id: "textarea" });
|
||||
const popupDialog = new FakeNode({ id: "dialog" });
|
||||
const popupRoot = new FakeNode({ id: "popup-root", children: [popupTextarea, popupDialog] });
|
||||
assert.equal(
|
||||
resolveDisconnectedDialogTerminalRoot(popupDialog as unknown as Element, null),
|
||||
popupRoot as unknown as Element,
|
||||
);
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: popupDialog as unknown as Element,
|
||||
dialogNode: popupDialog as unknown as HTMLElement,
|
||||
sessionRoot: null,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(popupTextarea.focusCalls, 1);
|
||||
assert.equal(
|
||||
shouldClaimDisconnectedDialogFocus({
|
||||
activeElement: popupTextarea as unknown as Element,
|
||||
dialogNode: popupDialog as unknown as HTMLElement,
|
||||
sessionRoot: null,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
// Detached popup dialog (parent cleared) + body focus: use captured terminal root.
|
||||
popupDialog.opts.parent = null;
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: popupDialog as unknown as HTMLElement,
|
||||
sessionRoot: popupRoot as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(popupTextarea.focusCalls, 2);
|
||||
|
||||
// Unfocused split sibling must not restore when browser parked focus on body.
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
isFocusedPane: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(textarea.focusCalls, 2);
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: body as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
isFocusedPane: true,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(textarea.focusCalls, 3);
|
||||
// Dialog actually owned focus — restore even if pane focus flag flipped.
|
||||
assert.equal(
|
||||
restoreTerminalFocusFromDisconnectedDialog({
|
||||
activeElement: dialog as unknown as Element,
|
||||
dialogNode: dialog as unknown as HTMLElement,
|
||||
sessionRoot: session as unknown as Element,
|
||||
documentBody: body as unknown as Element,
|
||||
isFocusedPane: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(textarea.focusCalls, 4);
|
||||
} finally {
|
||||
globalThis.HTMLElement = previousHTMLElement;
|
||||
}
|
||||
});
|
||||
|
||||
test("xterm focus restore is skipped while the connection overlay stays mounted", () => {
|
||||
assert.equal(
|
||||
shouldRestoreDisconnectedDialogTerminalFocus({ isConnected: true } as HTMLElement),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldRestoreDisconnectedDialogTerminalFocus({ isConnected: false } as HTMLElement),
|
||||
true,
|
||||
);
|
||||
assert.equal(shouldRestoreDisconnectedDialogTerminalFocus(null), false);
|
||||
});
|
||||
|
||||
test("connection dialog is hidden while a reused SSH channel is opening", () => {
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
status: "connecting",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
hideConnectingDialogForConnectionReuse: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("connection dialog remains visible when reuse is not actually supported", () => {
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
status: "connecting",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
hideConnectingDialogForConnectionReuse: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("connection dialog still appears for fresh remote connections", () => {
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
status: "connecting",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("connection dialog keeps existing local and disconnected behavior", () => {
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
status: "connecting",
|
||||
isLocalConnection: true,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
status: "connected",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
status: "disconnected",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("established sessions use the selected disconnected presentation", () => {
|
||||
const base = {
|
||||
status: "disconnected" as const,
|
||||
hasEverConnected: true,
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({
|
||||
...base,
|
||||
disconnectedNoticeMode: "terminal",
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...base,
|
||||
disconnectedNoticeMode: "terminal",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({
|
||||
...base,
|
||||
disconnectedNoticeMode: "dialog",
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...base,
|
||||
disconnectedNoticeMode: "dialog",
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("terminal notice never hides first-connect failures or restored session guidance", () => {
|
||||
const dialogArgs = {
|
||||
status: "disconnected" as const,
|
||||
disconnectedNoticeMode: "terminal" as const,
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({
|
||||
status: "disconnected",
|
||||
disconnectedNoticeMode: "terminal",
|
||||
hasEverConnected: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({ ...dialogArgs, hasEverConnected: false }),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({
|
||||
status: "disconnected",
|
||||
disconnectedNoticeMode: "terminal",
|
||||
hasEverConnected: true,
|
||||
restoreState: "restored-disconnected",
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...dialogArgs,
|
||||
hasEverConnected: true,
|
||||
restoreState: "restored-disconnected",
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("terminal notice is absent while connected", () => {
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({
|
||||
status: "connected",
|
||||
disconnectedNoticeMode: "terminal",
|
||||
hasEverConnected: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("established manual reconnect stays non-blocking until input is required", () => {
|
||||
const base = {
|
||||
status: "connecting" as const,
|
||||
disconnectedNoticeMode: "terminal" as const,
|
||||
hasEverConnected: true,
|
||||
isReconnectActive: true,
|
||||
};
|
||||
|
||||
assert.equal(shouldShowTerminalDisconnectedNotice(base), true);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...base,
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({ ...base, requiresUserInput: true }),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...base,
|
||||
requiresUserInput: true,
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("established automatic reconnect stays non-blocking until input is required", () => {
|
||||
const base = {
|
||||
status: "connecting" as const,
|
||||
disconnectedNoticeMode: "terminal" as const,
|
||||
hasEverConnected: true,
|
||||
isReconnectActive: true,
|
||||
};
|
||||
|
||||
assert.equal(shouldShowTerminalDisconnectedNotice(base), true);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...base,
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
shouldShowTerminalDisconnectedNotice({ ...base, requiresUserInput: true }),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldShowTerminalConnectionDialog({
|
||||
...base,
|
||||
requiresUserInput: true,
|
||||
isLocalConnection: false,
|
||||
isSerialConnection: false,
|
||||
isDisconnectedDialogDismissed: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("connection reuse hides connecting dialog only while reuse is still possible", () => {
|
||||
assert.equal(
|
||||
shouldHideConnectingDialogForConnectionReuse({
|
||||
reuseConnectionFromSessionId: undefined,
|
||||
host: host(),
|
||||
connectionReuseFellBack: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldHideConnectingDialogForConnectionReuse({
|
||||
reuseConnectionFromSessionId: "source-session",
|
||||
host: host(),
|
||||
connectionReuseFellBack: false,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shouldHideConnectingDialogForConnectionReuse({
|
||||
reuseConnectionFromSessionId: "source-session",
|
||||
host: host({ x11Forwarding: true }),
|
||||
connectionReuseFellBack: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldHideConnectingDialogForConnectionReuse({
|
||||
reuseConnectionFromSessionId: "source-session",
|
||||
host: host({ moshEnabled: true }),
|
||||
connectionReuseFellBack: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldHideConnectingDialogForConnectionReuse({
|
||||
reuseConnectionFromSessionId: "source-session",
|
||||
host: host({ etEnabled: true }),
|
||||
connectionReuseFellBack: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldHideConnectingDialogForConnectionReuse({
|
||||
reuseConnectionFromSessionId: "source-session",
|
||||
host: host(),
|
||||
connectionReuseFellBack: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("auto-run snippets only delay multi-line input in line-by-line mode", () => {
|
||||
assert.equal(AUTO_RUN_SNIPPET_LINE_DELAY_MS > 0, true);
|
||||
assert.equal(shouldDelayAutoRunSnippetInput("tthdf 0 2323\nadmin\ntest123", { noAutoRun: false }), false);
|
||||
assert.equal(
|
||||
shouldDelayAutoRunSnippetInput("sudo apt install gconf2-common -y\necho \"123456\"", {
|
||||
noAutoRun: false,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldDelayAutoRunSnippetInput("tthdf 0 2323\nadmin\ntest123", {
|
||||
noAutoRun: false,
|
||||
multiLineRunMode: "lineDelay",
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(shouldDelayAutoRunSnippetInput("tthdf 0 2323\nadmin\ntest123", { noAutoRun: true }), false);
|
||||
assert.equal(shouldDelayAutoRunSnippetInput("show version", { noAutoRun: false }), false);
|
||||
assert.equal(shouldDelayAutoRunSnippetInput("show version\r", { noAutoRun: false }), false);
|
||||
});
|
||||
Reference in New Issue
Block a user