testDebugging

tcmts.cli.test.e2e.lsp.testDebugging · #346gibgc · term

debugging from the editor: launch configurations for a definition, a test and an app

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 testDebugging = () => withChildren(async (keep) => {
  const dir6 = tempDir("lsp debug");
  const tcm = (...args: string[]) => spawnSync(process.execPath, [tcmBin(), ...args], { cwd: dir6, env: plainEnv(), encoding: "utf8" });
  tcm("init");
  fs.writeFileSync(
    path.join(dir6, "scratch.ts"),
    `// tcm: namespace lib\nimport { expect } from "vitest";\n\nexport const answer = (): number => 42;\n\nexport const testAnswer = () => {\n  expect(answer()).toBe(42);\n};\n\nexport function main() {\n  document.title = String(answer());\n}\n`,
  );
  assert.equal(tcm("add").status, 0);
  const c = client(keep);
  await c.request("initialize", { processId: null, rootUri: pathToFileURL(dir6).href, capabilities: {}, clientInfo: { name: "Visual Studio Code" } });
  c.notify("initialized", {});
  const launch = (config: { runtimeExecutable: string; runtimeArgs?: string[]; program: string; args?: string[]; cwd: string }) =>
    spawnSync(config.runtimeExecutable, [...(config.runtimeArgs ?? []), config.program, ...(config.args ?? [])], { cwd: config.cwd, env: plainEnv(), encoding: "utf8" });

  // a definition: its runner under Node, with source maps on
  const run = await c.request("tcm/debug", { kind: "run", name: "lib.answer" });
  assert.equal(run.type, "node");
  assert.ok(run.sourceMaps && run.skipFiles.includes("<node_internals>/**"));
  assert.match(launch(run).stdout, /^42$/m);
  // a test: through Vitest, whose processes the debugger follows
  const t = await c.request("tcm/debug", { kind: "test", name: "lib.testAnswer" });
  assert.equal(t.autoAttachChildProcesses, true);
  assert.equal(launch(t).status, 0);
  // an app: tcm dev under Node; its page in a Chrome debug session once it prints its URL
  const app = await c.request("tcm/debug", { kind: "app", name: "lib.main" });
  assert.deepEqual(app.args, ["dev", "lib.main", "--port", "0"]);
  assert.equal(app.serverReadyAction.action, "debugWithChrome");
  assert.ok(new RegExp(app.serverReadyAction.pattern).test("  url      http://127.0.0.1:5173/"));
  // view files offer Debug for what can run
  const lenses = await c.request("textDocument/codeLens", { textDocument: { uri: pathToFileURL(path.join(dir6, ".tcm", "src", "lib", "testAnswer.ts")).href } });
  assert.ok(lenses.some((l: { command: { title: string; command: string } }) => l.command.title === "Debug" && l.command.command === "tcm.debugTest"));
  c.child.kill();
})

uses