withChildren

tcmts.cli.test.e2e.harness.withChildren · #omsmo3yp · term · runs on the server

uses tcmts.cli.test.e2e.harness.stopWhenThisEnds, which uses node:child_process

Run fn with a way to keep the processes it starts (servers, watchers), and stop them when it's done, waiting until they have: a tcm stopped so writes what code it ran as it exits (coverage, which the suite reads once this test's process ends), and one still writing would be missed, its suite's result cached as though that code hadn't run. One that doesn't stop in 10 seconds is killed. And if this process ends first, however it ends (a test that hangs is killed by its suite's time limit, and its finally never runs), they're stopped all the same (stopWhenThisEnds).

import { stopWhenThisEnds } from "#codebase/tcmts/cli/test/e2e/harness";
import type { ChildProcess } from "node:child_process";

export async function withChildren<T>(
  fn: (keep: <C extends ChildProcess>(c: C) => C) => Promise<T> | T,
): Promise<T> {
  const kept: ChildProcess[] = [];
  try {
    return await fn((c) => (kept.push(c), stopWhenThisEnds(c), c));
  } finally {
    await Promise.all(
      kept.map(
        (c) =>
          new Promise<void>((resolve) => {
            // (something stopped as a process is, a server in this one: stopped, nothing to wait for)
            if (typeof c.once !== "function") return (c.kill(), resolve());
            if (c.exitCode !== null || c.signalCode !== null) return resolve();
            const late = setTimeout(() => c.kill("SIGKILL"), 10_000);
            c.once("exit", () => (clearTimeout(late), resolve()));
            c.kill();
          }),
      ),
    );
  }
}

uses

used by