testChangesInEditor

tcmts.cli.test.e2e.lsp.testChangesInEditor · #m3g3czad · term

The Changes view's data: this branch's history, newest first, what an entry changed (an edit with its code before and after as documents, what was re-hashed because of it, a rename), and the branches with how many names differ.

import { plainEnv, tcmBin, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness";
import { client } from "#codebase/tcmts/cli/test/e2e/lsp";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";

export const testChangesInEditor = () =>
  withChildren(async (keep) => {
    const dir = tempDir("lsp changes");
    const tcm = (...args: string[]) =>
      spawnSync(process.execPath, [tcmBin(), ...args], {
        cwd: dir,
        env: plainEnv(),
        encoding: "utf8",
      });
    tcm("init");
    fs.writeFileSync(
      path.join(dir, "scratch.ts"),
      `// tcm: namespace lib\nexport const square = (x: number): number => x * x;\nexport const area = (s: number): number => square(s);\n`,
    );
    assert.equal(tcm("add").status, 0);
    fs.writeFileSync(
      path.join(dir, "scratch.ts"),
      `// tcm: namespace lib\nexport const square = (x: number): number => x ** 2;\n`,
    );
    assert.equal(tcm("update").status, 0);
    assert.equal(tcm("mv", "lib.area", "lib.areaOf").status, 0);
    assert.equal(tcm("branch", "idea").status, 0);
    const c = client(keep);
    await c.request("initialize", {
      processId: null,
      rootUri: pathToFileURL(dir).href,
      capabilities: {},
      clientInfo: { name: "Visual Studio Code" },
    });
    c.notify("initialized", {});

    const history = await c.request("tcm/history", { limit: 10 });
    assert.deepEqual(
      history.map((h: any) => [h.command, h.back]),
      [
        ["mv lib.area lib.areaOf", 0],
        ["update lib.square", 1],
        ["add lib.square, lib.area", 2],
      ],
    );
    const update = await c.request("tcm/historyEntry", { hash: history[1].hash });
    assert.deepEqual(
      update.map((x: any) => [x.change, x.path]),
      [
        ["edited", "lib.square"],
        ["propagated", "lib.area"],
      ],
    );
    assert.deepEqual(update[1].because, ["lib.square"]);
    // the code before and after, to diff: documents rendered when the editor asks for them
    assert.match(update[0].oldUri, /^tcm:\/@\w+\.ts\?at=\w+$/);
    const text = async (uri: string) => (await c.request("tcm/source", { uri })).text as string;
    assert.match(await text(update[0].oldUri), /x \* x/);
    assert.match(await text(update[0].uri), /x \*\* 2/);
    const mv = await c.request("tcm/historyEntry", { hash: history[0].hash });
    assert.deepEqual(
      mv.map((x: any) => [x.change, x.path, x.from]),
      [["renamed", "lib.areaOf", "lib.area"]],
    );

    // a branch, created from this one: nothing differs until this one moves on
    assert.deepEqual(
      (await c.request("tcm/branches", {})).map((b: any) => [b.name, b.current, b.differs]),
      [
        ["main", true, 0],
        ["idea", false, 0],
      ],
    );
    assert.equal(tcm("rm", "lib.areaOf").status, 0);
    assert.deepEqual(
      (await c.request("tcm/branches", {})).map((b: any) => [b.name, b.differs]),
      [
        ["main", 0],
        ["idea", 1],
      ],
    );
    // what it has that this one doesn't, name by name, to open as diffs
    const toIdea = await c.request("tcm/branchChanges", { branch: "idea" });
    assert.deepEqual(
      toIdea.map((x: any) => [x.change, x.path]),
      [["added", "lib.areaOf"]],
    );
    // (its code, with that branch's names)
    assert.match(toIdea[0].uri, /\?branch=idea$/);
    assert.match(
      await text(toIdea[0].uri),
      // (a renamed function keeps its runtime name, part of its hash, until it's edited)
      /^\/\/ lib\.areaOf [\s\S]*export const area = \(s: number\): number => square\(s\)/,
    );
  })

uses