Files
Netcatty/components/terminal/runtime/middleClickBehavior.test.ts
T
2026-07-04 19:04:45 +08:00

260 lines
6.9 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import {
isMiddleClickContextMenuEvent,
isShiftSelectionReplayMouseEvent,
markMiddleClickContextMenuEvent,
markShiftSelectionReplayMouseEvent,
captureMiddleClickTerminalMouseEvent,
resolveMiddleClickBehavior,
shouldInterceptMouseTrackingContextMenu,
shouldReplayShiftMouseSelectionAsMacOption,
shouldStopShiftRightClickMouseTrackingMouseDown,
} from "./middleClickBehavior";
test("resolveMiddleClickBehavior uses the explicit middle-click behavior", () => {
assert.equal(resolveMiddleClickBehavior({ middleClickBehavior: "context-menu" }), "context-menu");
assert.equal(resolveMiddleClickBehavior({ middleClickBehavior: "disabled" }), "disabled");
});
test("resolveMiddleClickBehavior ignores unsupported middle-click behavior values", () => {
assert.equal(
resolveMiddleClickBehavior({ middleClickBehavior: "select-word" as never }),
"paste",
);
});
test("resolveMiddleClickBehavior falls back to the legacy middle-click paste flag", () => {
assert.equal(resolveMiddleClickBehavior({ middleClickPaste: true }), "paste");
assert.equal(resolveMiddleClickBehavior({ middleClickPaste: false }), "disabled");
assert.equal(resolveMiddleClickBehavior(undefined), "paste");
});
test("middle-click context menu events are identifiable", () => {
const event = {} as MouseEvent;
assert.equal(isMiddleClickContextMenuEvent(event), false);
assert.equal(isMiddleClickContextMenuEvent(markMiddleClickContextMenuEvent(event)), true);
});
test("mouse-tracking context menu capture lets middle-click menu events pass through", () => {
assert.equal(
shouldInterceptMouseTrackingContextMenu({
event: markMiddleClickContextMenuEvent({} as MouseEvent),
mouseTracking: true,
status: "connected",
}),
false,
);
assert.equal(
shouldInterceptMouseTrackingContextMenu({
event: {} as MouseEvent,
mouseTracking: true,
status: "connected",
}),
true,
);
});
test("mouse-tracking context menu capture lets Shift-modified mouse events pass through", () => {
assert.equal(
shouldInterceptMouseTrackingContextMenu({
event: { shiftKey: true } as MouseEvent,
mouseTracking: true,
status: "connected",
}),
false,
);
});
test("Shift selection replay events are identifiable", () => {
const event = {} as MouseEvent;
assert.equal(isShiftSelectionReplayMouseEvent(event), false);
assert.equal(isShiftSelectionReplayMouseEvent(markShiftSelectionReplayMouseEvent(event)), true);
});
test("macOS mouse tracking replays plain Shift left-click as xterm option selection", () => {
const event = {
button: 0,
shiftKey: true,
altKey: false,
ctrlKey: false,
metaKey: false,
} as MouseEvent;
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event,
mouseTracking: true,
status: "connected",
isMacPlatform: true,
}),
true,
);
});
test("Shift selection replay is limited to the macOS connected mouse-tracking case", () => {
const baseEvent = {
button: 0,
shiftKey: true,
altKey: false,
ctrlKey: false,
metaKey: false,
} as MouseEvent;
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event: baseEvent,
mouseTracking: true,
status: "connected",
isMacPlatform: false,
}),
false,
);
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event: baseEvent,
mouseTracking: false,
status: "connected",
isMacPlatform: true,
}),
false,
);
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event: baseEvent,
mouseTracking: true,
status: "disconnected",
isMacPlatform: true,
}),
false,
);
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event: { ...baseEvent, button: 2 } as MouseEvent,
mouseTracking: true,
status: "connected",
isMacPlatform: true,
}),
false,
);
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event: { ...baseEvent, shiftKey: false } as MouseEvent,
mouseTracking: true,
status: "connected",
isMacPlatform: true,
}),
false,
);
});
test("Shift selection replay ignores modified and already replayed mouse events", () => {
const baseEvent = {
button: 0,
shiftKey: true,
altKey: false,
ctrlKey: false,
metaKey: false,
} as MouseEvent;
for (const event of [
{ ...baseEvent, altKey: true },
{ ...baseEvent, ctrlKey: true },
{ ...baseEvent, metaKey: true },
markShiftSelectionReplayMouseEvent({ ...baseEvent } as MouseEvent),
]) {
assert.equal(
shouldReplayShiftMouseSelectionAsMacOption({
event: event as MouseEvent,
mouseTracking: true,
status: "connected",
isMacPlatform: true,
}),
false,
);
}
});
test("Shift right-click mousedown is stopped while connected mouse tracking is active", () => {
assert.equal(
shouldStopShiftRightClickMouseTrackingMouseDown({
event: {
button: 2,
shiftKey: true,
} as MouseEvent,
mouseTracking: true,
status: "connected",
}),
true,
);
});
test("Shift right-click mousedown capture is limited to connected mouse tracking", () => {
const baseEvent = {
button: 2,
shiftKey: true,
} as MouseEvent;
assert.equal(
shouldStopShiftRightClickMouseTrackingMouseDown({
event: baseEvent,
mouseTracking: false,
status: "connected",
}),
false,
);
assert.equal(
shouldStopShiftRightClickMouseTrackingMouseDown({
event: baseEvent,
mouseTracking: true,
status: "disconnected",
}),
false,
);
assert.equal(
shouldStopShiftRightClickMouseTrackingMouseDown({
event: {
button: 2,
shiftKey: false,
} as MouseEvent,
mouseTracking: true,
status: "connected",
}),
false,
);
assert.equal(
shouldStopShiftRightClickMouseTrackingMouseDown({
event: {
button: 0,
shiftKey: true,
} as MouseEvent,
mouseTracking: true,
status: "connected",
}),
false,
);
});
test("middle-click terminal mouse down/up events are captured before xterm sees them", () => {
const calls: string[] = [];
const middleClickEvent = {
button: 1,
preventDefault: () => calls.push("preventDefault"),
stopImmediatePropagation: () => calls.push("stopImmediatePropagation"),
} as unknown as MouseEvent;
assert.equal(captureMiddleClickTerminalMouseEvent(middleClickEvent), true);
assert.deepEqual(calls, ["preventDefault", "stopImmediatePropagation"]);
calls.length = 0;
assert.equal(captureMiddleClickTerminalMouseEvent({
button: 0,
preventDefault: () => calls.push("preventDefault"),
stopImmediatePropagation: () => calls.push("stopImmediatePropagation"),
} as unknown as MouseEvent), false);
assert.deepEqual(calls, []);
});