testPatchSaysPlacesAsTheViewNumbersThem

tcmts.cli.test.e2e.editing.testPatchSaysPlacesAsTheViewNumbersThem · #i7zyyn4g · term

Where tcm patch says a problem is (the checker's error, the line nearest an edit it can't find), it's the line as tcm view numbers the definition, the edits made: with imports above it, a doc, its first line edited, an import it added, or (--patches) written with definitions from another namespace, which puts a // tcm: path line among its own.

import { okIn, tcmIn, tempDir } from "#codebase/tcmts/cli/test/e2e/harness";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";

export const testPatchSaysPlacesAsTheViewNumbersThem = () => {
  const dir = tempDir("patch places");
  const ok = (...args: string[]) => okIn(dir, ...args);
  const fails = (...args: string[]) => {
    const r = tcmIn(dir, ...args);
    assert.notEqual(r.status, 0, r.out);
    return r.out;
  };
  ok("init");
  fs.writeFileSync(
    path.join(dir, "m.ts"),
    `// tcm: namespace m\nimport { basename, join } from "node:path";\n\n/** doc */\nexport const f = (x: string): string => join(x, "a");\n` +
      `export const tools = [\n  { name: "a", run: (x: string): string => join(x, "a"), other: (x: string): string => basename(x) },\n];\n` +
      `export const h = (x: number): number => {\n  return x + 1;\n};\n` +
      `export const listed = [\n  {\n    name: "a",\n  },\n] as const;\n`,
  );
  fs.writeFileSync(path.join(dir, "n.ts"), `// tcm: namespace n\nexport const z = 1;\n`);
  ok("add", "m.ts", "n.ts");
  for (const f of ["m.ts", "n.ts"]) fs.rmSync(path.join(dir, f));
  // (as viewed: f's code is its line 6, tools' `run` its line 6, h's `return` its line 2, listed's `name` its line 3)
  assert.match(ok("view", "m.f", "--lines", "6"), /6 export const f/);
  assert.match(ok("view", "m.tools", "--lines", "6"), /6 +run: /);
  // the first line edited, with a doc and imports above it
  assert.match(
    fails("patch", "m.f", "--old", 'join(x, "a")', "--new", "join(x, 1)"),
    /m\.f:6:\d+ - error TS2345/,
  );
  // an import it adds is a line above the code
  const added = fails("patch", "m.h", "--old", "x + 1", "--new", 'x + basename("s")');
  assert.match(added, /import \{ basename \} from "node:path"/);
  assert.match(added, /m\.h:2:\d+ - error TS\d+/);
  // written with another namespace's definition: its lines aren't the note's
  assert.match(
    fails(
      "patch",
      "--patches",
      JSON.stringify([
        { name: "m.listed", edits: [{ old: 'name: "a"', new: "name: y" }] },
        { name: "n.z", edits: [{ old: "1", new: "2" }] },
      ]),
    ),
    /m\.listed:3:\d+ - error TS2304/,
  );
  // what's nearest an edit that isn't there, either way
  for (const out of [
    fails("patch", "m.tools", "--old", "nope(x, a)", "--new", "1"),
    fails(
      "patch",
      "--patches",
      JSON.stringify([{ name: "m.tools", edits: [{ old: "nope(x, a)", new: "1" }] }]),
    ),
  ])
    assert.match(out, /the nearest line is 6: "run: /);
  assert.match(ok("view", "n.z"), /z = 1/);
}

uses