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
552 lines
16 KiB
TypeScript
552 lines
16 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
isFollowOriginStillCurrent,
|
|
mergeLatestFollowTerminalCwdHostSetting,
|
|
resolveHostFollowTerminalCwd,
|
|
resolveSftpFollowTerminalCwdTargetHost,
|
|
shouldApplyFollowTerminalCwdSyncResult,
|
|
shouldClearBlockedFollowOnReach,
|
|
shouldFollowTerminalCwdNavigate,
|
|
shouldInvalidateFollowBookkeepingOnCwdChange,
|
|
shouldLatchInitialFollowInterruption,
|
|
shouldReleaseInitialFollowSyncAttempt,
|
|
shouldResetInitialFollowTerminalCwdSync,
|
|
} from "./sftpFollowTerminalCwd";
|
|
|
|
const base = {
|
|
followEnabled: true,
|
|
isVisible: true,
|
|
terminalCwd: "/home/user/project",
|
|
currentPath: "/home/user",
|
|
connectionId: "conn-1",
|
|
hasActiveWork: false,
|
|
isConnected: true,
|
|
};
|
|
|
|
test("shouldFollowTerminalCwdNavigate returns true when follow is on and paths differ", () => {
|
|
assert.equal(shouldFollowTerminalCwdNavigate(base), true);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate returns false when paths already match", () => {
|
|
assert.equal(
|
|
shouldFollowTerminalCwdNavigate({ ...base, currentPath: "/home/user/project" }),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate returns false when follow is disabled", () => {
|
|
assert.equal(shouldFollowTerminalCwdNavigate({ ...base, followEnabled: false }), false);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate returns false while interactive work is active", () => {
|
|
assert.equal(shouldFollowTerminalCwdNavigate({ ...base, hasActiveWork: true }), false);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate returns false without a known terminal cwd", () => {
|
|
assert.equal(shouldFollowTerminalCwdNavigate({ ...base, terminalCwd: null }), false);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate returns false when cwd is blocked after a failed follow", () => {
|
|
assert.equal(
|
|
shouldFollowTerminalCwdNavigate({
|
|
...base,
|
|
blockedFollow: { connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate ignores blocked cwd for a different connection", () => {
|
|
assert.equal(
|
|
shouldFollowTerminalCwdNavigate({
|
|
...base,
|
|
connectionId: "conn-2",
|
|
blockedFollow: { connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate ignores blocked cwd when terminal cwd changed", () => {
|
|
assert.equal(
|
|
shouldFollowTerminalCwdNavigate({
|
|
...base,
|
|
terminalCwd: "/home/user/other",
|
|
blockedFollow: { connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate does not recapture manual navigation after the cwd was handled", () => {
|
|
assert.equal(
|
|
shouldFollowTerminalCwdNavigate({
|
|
...base,
|
|
currentPath: "/srv/bookmark",
|
|
handledFollow: { connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldFollowTerminalCwdNavigate resumes when the terminal cwd changes", () => {
|
|
assert.equal(
|
|
shouldFollowTerminalCwdNavigate({
|
|
...base,
|
|
terminalCwd: "/home/user/other",
|
|
currentPath: "/srv/bookmark",
|
|
handledFollow: { connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("resolveHostFollowTerminalCwd inherits the global setting until the host overrides it", () => {
|
|
assert.equal(resolveHostFollowTerminalCwd(undefined, true), true);
|
|
assert.equal(resolveHostFollowTerminalCwd(undefined, false), false);
|
|
assert.equal(resolveHostFollowTerminalCwd(true, false), true);
|
|
assert.equal(resolveHostFollowTerminalCwd(false, true), false);
|
|
});
|
|
|
|
test("resolveSftpFollowTerminalCwdTargetHost prefers the visible SFTP host", () => {
|
|
const terminalHost = { id: "terminal-host" };
|
|
const visibleHost = { id: "visible-sftp-host" };
|
|
|
|
assert.equal(
|
|
resolveSftpFollowTerminalCwdTargetHost(visibleHost, terminalHost),
|
|
visibleHost,
|
|
);
|
|
assert.equal(
|
|
resolveSftpFollowTerminalCwdTargetHost(null, terminalHost),
|
|
terminalHost,
|
|
);
|
|
});
|
|
|
|
test("visible SFTP host override can enable follow when terminal host inherits global off", () => {
|
|
const terminalHost = { id: "terminal-host", sftpFollowTerminalCwd: undefined };
|
|
const visibleHost = { id: "visible-sftp-host", sftpFollowTerminalCwd: true };
|
|
const followHost = resolveSftpFollowTerminalCwdTargetHost(visibleHost, terminalHost);
|
|
|
|
assert.equal(resolveHostFollowTerminalCwd(followHost?.sftpFollowTerminalCwd, false), true);
|
|
});
|
|
|
|
test("mergeLatestFollowTerminalCwdHostSetting refreshes the follow flag without losing display overrides", () => {
|
|
const connectedHost = {
|
|
id: "host-1",
|
|
hostname: "session.example.com",
|
|
sftpFollowTerminalCwd: false,
|
|
};
|
|
const latestHost = {
|
|
id: "host-1",
|
|
hostname: "vault.example.com",
|
|
sftpFollowTerminalCwd: true,
|
|
};
|
|
|
|
assert.deepEqual(
|
|
mergeLatestFollowTerminalCwdHostSetting(connectedHost, latestHost),
|
|
{
|
|
id: "host-1",
|
|
hostname: "session.example.com",
|
|
sftpFollowTerminalCwd: true,
|
|
},
|
|
);
|
|
});
|
|
|
|
test("mergeLatestFollowTerminalCwdHostSetting keeps optimistic session override until vault updates", () => {
|
|
const connectedHost = {
|
|
id: "host-1",
|
|
hostname: "session.example.com",
|
|
sftpFollowTerminalCwd: false,
|
|
};
|
|
const latestHost = {
|
|
id: "host-1",
|
|
hostname: "vault.example.com",
|
|
};
|
|
|
|
assert.deepEqual(
|
|
mergeLatestFollowTerminalCwdHostSetting(connectedHost, latestHost, false),
|
|
{
|
|
id: "host-1",
|
|
hostname: "session.example.com",
|
|
sftpFollowTerminalCwd: false,
|
|
},
|
|
);
|
|
});
|
|
|
|
test("mergeLatestFollowTerminalCwdHostSetting drops stale session override when vault clears the follow flag", () => {
|
|
const connectedHost = {
|
|
id: "host-1",
|
|
hostname: "session.example.com",
|
|
sftpFollowTerminalCwd: true,
|
|
};
|
|
const latestHost = {
|
|
id: "host-1",
|
|
hostname: "vault.example.com",
|
|
};
|
|
|
|
assert.deepEqual(
|
|
mergeLatestFollowTerminalCwdHostSetting(connectedHost, latestHost),
|
|
{
|
|
id: "host-1",
|
|
hostname: "session.example.com",
|
|
sftpFollowTerminalCwd: undefined,
|
|
},
|
|
);
|
|
});
|
|
|
|
test("shouldClearBlockedFollowOnReach clears when the active connection reaches the blocked cwd", () => {
|
|
assert.equal(
|
|
shouldClearBlockedFollowOnReach(
|
|
{ connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
"conn-1",
|
|
"/home/user/project",
|
|
false,
|
|
),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("shouldClearBlockedFollowOnReach keeps block while navigation is still loading", () => {
|
|
assert.equal(
|
|
shouldClearBlockedFollowOnReach(
|
|
{ connectionId: "conn-1", terminalCwd: "/home/user/project" },
|
|
"conn-1",
|
|
"/home/user/project",
|
|
true,
|
|
),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects stale follow results after cwd changes", () => {
|
|
let generation = 0;
|
|
const followA = generation;
|
|
|
|
generation += 1;
|
|
const followB = generation;
|
|
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: followB,
|
|
currentGeneration: generation,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: followA,
|
|
currentGeneration: generation,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects results after follow is unavailable", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: false,
|
|
canFollow: true,
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: false,
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("isFollowOriginStillCurrent treats a missing origin as bound to the live focused terminal", () => {
|
|
assert.equal(isFollowOriginStillCurrent({
|
|
expectedOriginId: null,
|
|
liveOriginId: null,
|
|
}), true);
|
|
assert.equal(isFollowOriginStillCurrent({
|
|
expectedOriginId: null,
|
|
liveOriginId: "mosh-b",
|
|
}), false);
|
|
assert.equal(isFollowOriginStillCurrent({
|
|
expectedOriginId: "mosh-a",
|
|
liveOriginId: "mosh-b",
|
|
}), false);
|
|
assert.equal(isFollowOriginStillCurrent({
|
|
expectedOriginId: "mosh-a",
|
|
liveOriginId: "mosh-a",
|
|
}), true);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects a null origin after focus appears", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedSessionId: null,
|
|
liveSessionId: null,
|
|
}),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedSessionId: null,
|
|
liveSessionId: "mosh-b",
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects results after the focused session changes", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedSessionId: "session-a",
|
|
liveSessionId: "session-b",
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedSessionId: "session-a",
|
|
liveSessionId: "session-a",
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects results for an old connection", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedConnectionId: "conn-1",
|
|
liveConnectionId: "conn-2",
|
|
paneConnectionId: "conn-2",
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects results for an old terminal cwd", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedConnectionId: "conn-1",
|
|
liveConnectionId: "conn-1",
|
|
paneConnectionId: "conn-1",
|
|
expectedTerminalCwd: "/srv/old",
|
|
liveTerminalCwd: "/srv/new",
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult rejects missing live cwd when required", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedConnectionId: "conn-1",
|
|
liveConnectionId: "conn-1",
|
|
paneConnectionId: "conn-1",
|
|
expectedTerminalCwd: "/srv/project",
|
|
liveTerminalCwd: null,
|
|
requireLiveTerminalCwd: true,
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("shouldApplyFollowTerminalCwdSyncResult allows missing live cwd when target was fetched fresh", () => {
|
|
assert.equal(
|
|
shouldApplyFollowTerminalCwdSyncResult({
|
|
syncGeneration: 2,
|
|
currentGeneration: 2,
|
|
followEnabled: true,
|
|
canFollow: true,
|
|
expectedConnectionId: "conn-1",
|
|
liveConnectionId: "conn-1",
|
|
paneConnectionId: "conn-1",
|
|
expectedTerminalCwd: "/srv/project",
|
|
liveTerminalCwd: null,
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("follow bookkeeping keeps handled state across hidden-panel null cwd transitions", () => {
|
|
// Hidden panels receive activeTerminalCwd={null} and get the last live value
|
|
// back on reshow: the visibility transitions may not drop handled follow
|
|
// bookkeeping when the cwd did not actually change.
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: null,
|
|
lastCwd: "/home/user/project",
|
|
isVisible: false,
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: "/home/user/project",
|
|
lastCwd: "/home/user/project",
|
|
isVisible: false,
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: "/home/user/project",
|
|
lastCwd: "/home/user/project",
|
|
isVisible: true,
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: null,
|
|
lastCwd: null,
|
|
isVisible: false,
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("follow bookkeeping invalidates on a real terminal cwd change", () => {
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: "/home/user/other",
|
|
lastCwd: "/home/user/project",
|
|
isVisible: true,
|
|
}),
|
|
true,
|
|
);
|
|
// A cwd that changed while the panel was hidden still invalidates on reshow.
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: "/home/user/other",
|
|
lastCwd: null,
|
|
isVisible: true,
|
|
}),
|
|
true,
|
|
);
|
|
// A `null` while the surface is visible means the linked terminal session
|
|
// changed (or closed) and its cwd cache is empty: in-flight follow results
|
|
// of the previous session must be invalidated.
|
|
assert.equal(
|
|
shouldInvalidateFollowBookkeepingOnCwdChange({
|
|
nextCwd: null,
|
|
lastCwd: "/home/user/project",
|
|
isVisible: true,
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("first-open sync reset re-arms on a replaced connection", () => {
|
|
assert.equal(
|
|
shouldResetInitialFollowTerminalCwdSync({
|
|
isVisible: true,
|
|
ownerPanelOpen: true,
|
|
connectionId: "conn-2",
|
|
trackedConnectionId: "conn-1",
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("first-open sync reset re-arms after a fresh open on the same connection", () => {
|
|
assert.equal(
|
|
shouldResetInitialFollowTerminalCwdSync({
|
|
isVisible: false,
|
|
ownerPanelOpen: false,
|
|
connectionId: "conn-1",
|
|
trackedConnectionId: "conn-1",
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test("first-open sync reset survives hiding the surface while the owner panel stays open", () => {
|
|
// Terminal tab switches / side-panel tool switches keep the panel mounted
|
|
// and open: the user's browsed directory and filename filter must survive.
|
|
assert.equal(
|
|
shouldResetInitialFollowTerminalCwdSync({
|
|
isVisible: false,
|
|
ownerPanelOpen: true,
|
|
connectionId: "conn-1",
|
|
trackedConnectionId: "conn-1",
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
shouldResetInitialFollowTerminalCwdSync({
|
|
isVisible: true,
|
|
ownerPanelOpen: true,
|
|
connectionId: "conn-1",
|
|
trackedConnectionId: "conn-1",
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("first-open sync attempt is released while visible or after the owner panel closed", () => {
|
|
assert.equal(shouldReleaseInitialFollowSyncAttempt({ isVisible: true, ownerPanelOpen: true }), true);
|
|
assert.equal(shouldReleaseInitialFollowSyncAttempt({ isVisible: true, ownerPanelOpen: false }), true);
|
|
assert.equal(shouldReleaseInitialFollowSyncAttempt({ isVisible: false, ownerPanelOpen: false }), true);
|
|
});
|
|
|
|
test("first-open sync attempt stays consumed while hidden with the owner panel open", () => {
|
|
// Terminal tab switch while the fresh-CWD probe is still pending: the
|
|
// interrupted attempt must not re-arm, or returning to the tab re-runs the
|
|
// first-open sync and navigates away from the browsed directory.
|
|
assert.equal(shouldReleaseInitialFollowSyncAttempt({ isVisible: false, ownerPanelOpen: true }), false);
|
|
});
|
|
|
|
test("first-open probe is latched as interrupted on a hide with the owner panel open", () => {
|
|
// Terminal-tab switch while the fresh-CWD probe is pending: the probe must be
|
|
// latched as interrupted so it cannot navigate once the tab is reshown.
|
|
assert.equal(
|
|
shouldLatchInitialFollowInterruption({ isVisible: false, ownerPanelOpen: true }),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
shouldLatchInitialFollowInterruption({ isVisible: true, ownerPanelOpen: true }),
|
|
false,
|
|
);
|
|
// A panel close is not an interruption latch: the reset guard re-arms there.
|
|
assert.equal(
|
|
shouldLatchInitialFollowInterruption({ isVisible: false, ownerPanelOpen: false }),
|
|
false,
|
|
);
|
|
});
|