Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/terminal/lib/terminal.test.ts
Views: 687
import { Terminal } from "./terminal";1import {2getOpts,3getPrimusMock,4isPidRunning,5getCommandLine,6waitForPidToChange,7} from "./support";8import { getChannelName } from "./util";910describe("very basic test of creating a terminal and changing shell", () => {11let terminal;12const { path, options } = getOpts();1314beforeAll(() => {15const primus = getPrimusMock();16terminal = new Terminal(primus, path, options);17});1819afterAll(() => {20terminal.close();21});2223it("checks conditions of terminal before it is initialized", () => {24expect(terminal.getPid()).toBe(undefined);25expect(terminal.getPath()).toBe(options.path);26expect(terminal.getCommand()).toBe("/bin/bash");27});2829it("initializes the terminal and checks conditions", async () => {30await terminal.init();31expect(typeof terminal.getPid()).toBe("number");32});3334it("changes the shell to /bin/sh and sees that the pid changes", async () => {35const pid = terminal.getPid();36terminal.setCommand("/bin/sh", []);37const newPid = await waitForPidToChange(terminal, pid);38expect(pid).not.toBe(newPid);39// check that original process is no longer running.40expect(await isPidRunning(pid)).toBe(false);41});42});4344describe("create a shell, connect a client, and communicate with it", () => {45let terminal;46const { path, options } = getOpts();47const primus = getPrimusMock();48const channel = primus.channel(getChannelName(path));4950beforeAll(() => {51terminal = new Terminal(primus, path, options);52});5354afterAll(() => {55terminal.close();56});5758it("initialize the terminal", async () => {59await terminal.init();60expect(typeof terminal.getPid()).toBe("number");61});6263let spark;64it("create a client connection to the terminal", () => {65spark = channel.createSpark("192.168.2.1");66});6768it("waits to receive no-ignore command", async () => {69expect(await spark.waitForMessage()).toEqual({70cmd: "computeServerId",71id: 0,72});73expect(await spark.waitForMessage()).toEqual({ cmd: "no-ignore" });74});7576it("sets the terminal size and confirm it was set", async () => {77const rows = 10,78cols = 100;79expect(terminal.client_sizes[spark.id]).toEqual(undefined);80spark.emit("data", { cmd: "size", rows, cols });81expect(terminal.client_sizes[spark.id]).toEqual({ rows, cols });82// also confirm receipt of size message83const mesg = await spark.waitForMessage();84expect(mesg).toEqual({ cmd: "size", rows, cols });85});8687it("gets the current working directory via a command", async () => {88spark.emit("data", { cmd: "cwd" });89const mesg = await spark.waitForMessage();90expect(mesg.cmd).toBe("cwd");91expect(process.cwd().endsWith(mesg.payload)).toBe(true);92});9394it("write pwd to terminal and get back the current working directory", async () => {95spark.emit("data", "pwd\n");96spark.data = "";97const resp = await spark.waitForData(process.cwd());98expect(resp).toContain(process.cwd());99});100101// this test is very flaky and I'm not even sure it makes sense given that the terminal is supposed102// to not autorestart unless you explicilty do the right thing. Disabling it.103// it("send kill command, send some input (to start new shell), and see that pid changes", async () => {104// const pid = terminal.getPid();105// spark.emit("data", { cmd: "kill" });106// spark.emit("data", "pwd\n");107// spark.data = "";108// const newPid = await waitForPidToChange(terminal, pid);109// expect(pid).not.toBe(newPid);110// expect(await isPidRunning(pid)).toBe(false);111// });112113it("set shell with set_command see that pid changes", async () => {114const pid = terminal.getPid();115spark.emit("data", {116cmd: "set_command",117command: "/usr/bin/sleep",118args: ["1000"],119});120const newPid = await waitForPidToChange(terminal, pid);121expect(pid).not.toBe(newPid);122expect(await isPidRunning(pid)).toBe(false);123expect(await getCommandLine(newPid)).toContain("sleep");124});125126it("send some data, then disconnect and reconnect, and verify that history contains that data, and also that terminal continues to work", async () => {127spark.emit("data", "echo 'hello cocalc'\n");128const resp = await spark.waitForData("hello cocalc");129expect(resp).toContain("hello cocalc");130spark.end();131const id = spark.id;132spark = channel.createSpark("192.168.2.1");133expect(id).not.toEqual(spark.id);134expect(await spark.waitForMessage()).toEqual({135cmd: "size",136cols: 100,137rows: 10,138});139expect(await spark.waitForMessage()).toEqual({140cmd: "computeServerId",141id: 0,142});143expect(await spark.waitForMessage()).toEqual({ cmd: "no-ignore" });144expect(spark.data).toContain("hello cocalc");145spark.data = "";146});147});148149describe("collaboration -- two clients connected to the same terminal session", () => {150let terminal;151const { path, options } = getOpts();152const primus = getPrimusMock();153const channel = primus.channel(getChannelName(path));154155beforeAll(() => {156terminal = new Terminal(primus, path, options);157});158159afterAll(() => {160terminal.close();161});162163let spark1, spark2;164it("create two clients connection to the terminal", async () => {165await terminal.init();166spark1 = channel.createSpark("192.168.2.1");167spark2 = channel.createSpark("192.168.2.2");168for (const s of [spark1, spark2]) {169expect(await s.waitForMessage()).toEqual({170cmd: "computeServerId",171id: 0,172});173const mesg = await s.waitForMessage();174expect(mesg).toEqual({ cmd: "no-ignore" });175}176});177178it("have one client send something, and both clients see the input and result", async () => {179const input = "expr 5077 \\* 389\n";180const output = `${5077 * 389}`;181spark1.emit("data", input);182const out1 = await spark1.waitForData(output);183expect(out1).toContain("5077");184expect(out1).toContain(output);185const out2 = await spark2.waitForData(output);186expect(out2).toContain("5077");187expect(out2).toContain(output);188// also check that output only appears once:189let i = out2.indexOf(output);190expect(out2.indexOf(output, i + 1)).toBe(-1);191});192193it("set the sizes of the two clients; verify that the min size is returned", async () => {194const rows1 = 15,195cols1 = 90;196const rows2 = 20,197cols2 = 70;198spark1.emit("data", { cmd: "size", rows: rows1, cols: cols1 });199const mesg1 = await spark1.waitForMessage();200expect(mesg1).toEqual({ cmd: "size", rows: rows1, cols: cols1 });201const mesg1a = await spark2.waitForMessage();202expect(mesg1a).toEqual({ cmd: "size", rows: rows1, cols: cols1 });203spark2.emit("data", { cmd: "size", rows: rows2, cols: cols2 });204const mesg2 = await spark2.waitForMessage();205expect(mesg2).toEqual({206cmd: "size",207rows: Math.min(rows1, rows2),208cols: Math.min(cols1, cols2),209});210});211});212213214