import { IPty } from "node-pty"; import { createClient } from "@coder/protocol/test"; const client = createClient(); jest.mock("../../ide/src/fill/client", () => ({ client })); const pty = require("../src/fill/node-pty") as typeof import("node-pty"); describe("node-pty", () => { /** * Returns a function that when called returns a promise that resolves with * the next chunk of data from the process. */ const promisifyData = (proc: IPty): (() => Promise) => { // Use a persistent callback instead of creating it in the promise since // otherwise we could lose data that comes in while no promise is listening. let onData: (() => void) | undefined; let buffer: string | undefined; proc.on("data", (data) => { // Remove everything that isn't a letter, number, or $ to avoid issues // with ANSI escape codes printing inside the test output. buffer = (buffer || "") + data.toString().replace(/[^a-zA-Z0-9$]/g, ""); if (onData) { onData(); } }); return (): Promise => new Promise((resolve): void => { onData = (): void => { if (typeof buffer !== "undefined") { const data = buffer; buffer = undefined; onData = undefined; resolve(data); } }; onData(); }); }; it("should create shell", async () => { // Setting the config file to something that shouldn't exist so the test // isn't affected by custom configuration. const proc = pty.spawn("/bin/bash", ["--rcfile", "/tmp/test/nope/should/not/exist"], { cols: 100, rows: 10, }); const getData = promisifyData(proc); // First it outputs @hostname:cwd expect((await getData()).length).toBeGreaterThan(1); // Then it seems to overwrite that with a shorter prompt in the format of // [hostname@user]$ expect((await getData())).toContain("$"); proc.kill(); await new Promise((resolve): void => { proc.on("exit", resolve); }); }); it("should resize", async () => { // Requires the `tput lines` cmd to be available. // Setting the config file to something that shouldn't exist so the test // isn't affected by custom configuration. const proc = pty.spawn("/bin/bash", ["--rcfile", "/tmp/test/nope/should/not/exist"], { cols: 10, rows: 10, }); const getData = promisifyData(proc); // We've already tested these first two bits of output; see shell test. await getData(); await getData(); proc.write("tput lines\n"); expect(await getData()).toContain("tput"); expect((await getData()).trim()).toContain("10"); proc.resize(10, 50); // The prompt again. await getData(); await getData(); proc.write("tput lines\n"); expect(await getData()).toContain("tput"); expect((await getData())).toContain("50"); proc.kill(); await new Promise((resolve): void => { proc.on("exit", resolve); }); }); });