File size: 3,953 Bytes
6aa76ed | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | import { afterEach, describe, expect, it, vi } from "vitest";
import { createPtyCompositionForwarder } from "./pty-composition";
describe("createPtyCompositionForwarder", () => {
afterEach(() => vi.useRealTimers());
it("forwards committed dead-key text when xterm emits no onData", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ä");
vi.runAllTimers();
expect(send).toHaveBeenCalledExactlyOnceWith("ä");
});
it("leaves xterm's committed input alone when it arrives before the fallback", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ä");
forwarder.noteTerminalData("äx");
vi.runAllTimers();
expect(send).not.toHaveBeenCalled();
});
it("forwards a pending composition after unrelated terminal data", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ä");
forwarder.noteTerminalData("x");
vi.advanceTimersByTime(15);
expect(send).not.toHaveBeenCalled();
vi.advanceTimersByTime(1);
expect(send).toHaveBeenCalledExactlyOnceWith("ä");
});
it("forwards a pending composition when unrelated data precedes matching chunks", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ab");
forwarder.noteTerminalData("x");
forwarder.noteTerminalData("a");
forwarder.noteTerminalData("b");
vi.runAllTimers();
expect(send).toHaveBeenCalledExactlyOnceWith("ab");
});
it("cancels a pending composition when matching text arrives in clean chunks", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ab");
forwarder.noteTerminalData("a");
forwarder.noteTerminalData("b");
vi.runAllTimers();
expect(send).not.toHaveBeenCalled();
});
it("ignores ESC/SGR data while matching composition chunks", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ab");
forwarder.noteTerminalData("a");
forwarder.noteTerminalData("\x1b[<0;10;10M");
forwarder.noteTerminalData("b");
vi.runAllTimers();
expect(send).not.toHaveBeenCalled();
});
it("forwards a second composition after the first fallback completes", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ä");
vi.runAllTimers();
forwarder.onCompositionEnd("ö");
vi.runAllTimers();
expect(send).toHaveBeenNthCalledWith(1, "ä");
expect(send).toHaveBeenNthCalledWith(2, "ö");
});
it("preserves an earlier rapid composition before scheduling the next", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("a");
forwarder.onCompositionEnd("ä");
vi.runAllTimers();
expect(send).toHaveBeenNthCalledWith(1, "a");
expect(send).toHaveBeenNthCalledWith(2, "ä");
});
it("cancels a pending composition on disposal", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("ä");
forwarder.dispose();
vi.runAllTimers();
expect(send).not.toHaveBeenCalled();
});
it("does not send an empty cancelled composition", () => {
vi.useFakeTimers();
const send = vi.fn();
const forwarder = createPtyCompositionForwarder(send);
forwarder.onCompositionEnd("");
vi.runAllTimers();
expect(send).not.toHaveBeenCalled();
});
});
|