startTests

tcmts.studio.startTests · #aylb6kth · term · runs on the server

uses node:child_process

Run tests as tcm test <names> does (all of them, without names; the tests under a namespace, or one): it's run, on the branch the studio serves, what it says kept as it comes (testRuns). One at a time. The run's id.

import { HttpError } from "#codebase/tcmts/server";
import { editableCodebase, runsOfTests, tcmEnv } from "#codebase/tcmts/studio";
import type { TestRun } from "#codebase/tcmts/studio";
import { spawn } from "node:child_process";
import { stripVTControlCharacters } from "node:util";

export async function startTests(names: string[]): Promise<string> {
  const cb = editableCodebase();
  if (runsOfTests.some((r) => r.ended === undefined))
    throw new HttpError(409, "Tests are running already: one run at a time.");
  for (const n of names) if (!/^[\w$.-]+$/.test(n)) throw new HttpError(400, `${n} isn't a name.`);
  const env: NodeJS.ProcessEnv = { ...tcmEnv(), NO_COLOR: "1" };
  const run: TestRun = {
    id: Math.random().toString(36).slice(2, 10),
    names,
    started: Date.now(),
    lines: [],
  };
  runsOfTests.push(run);
  while (runsOfTests.length > 5) runsOfTests.shift();
  const child = spawn(process.execPath, [process.argv[1]!, "test", ...names], {
    cwd: cb.lay.root,
    env,
  });
  let rest = "";
  const said = (chunk: Buffer) => {
    rest += stripVTControlCharacters(chunk.toString("utf8"));
    const lines = rest.split("\n");
    rest = lines.pop()!;
    run.lines.push(...lines);
    if (run.lines.length > 2000) run.lines.splice(0, run.lines.length - 2000);
  };
  child.stdout.on("data", said);
  child.stderr.on("data", said);
  child.on("close", (code) => {
    if (rest) run.lines.push(rest);
    run.code = code ?? 1;
    run.ended = Date.now();
  });
  return run.id;
}

uses

used by