From 8f4ca5c73eade3457325b887c16858c1029d395e Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 1 Nov 2026 23:21:46 +0000 Subject: [PATCH] test: make test fails when jest force-stops a worker (closes #711) When a test leaves a timer running, jest force-stops the worker that ran the test, prints a warning, and the run still passes. Jest has no option that fails the run instead. Both test scripts in package.json now run jest through script/lib/jest.js. Jest runs inside that file's process, so its terminal output stays the same. The file exits 1 when jest printed that warning. Model: opus-5-5 --- eslint.config.js | 3 +-- package.json | 4 ++-- script/lib/jest.js | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 script/lib/jest.js diff --git a/eslint.config.js b/eslint.config.js index f834c06..1c420d5 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -153,8 +153,7 @@ module.exports = [ }, }, - // The helpers the script/ entrypoints call: plain node programs too, run - // from a shell script rather than from yarn, and never bundled. + // The files in script/lib/ run on plain node too, and are never bundled. { files: ["script/lib/**/*.js"], languageOptions: { diff --git a/package.json b/package.json index b61275e..2080619 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "license": "GPL-3.0", "private": true, "scripts": { - "test": "jest --maxWorkers=3", - "test:verbose": "jest --maxWorkers=3 --verbose", + "test": "node script/lib/jest.js --maxWorkers=3", + "test:verbose": "node script/lib/jest.js --maxWorkers=3 --verbose", "build": "node build.js", "lint": "eslint . && prettier --check .", "fmt": "prettier --write .", diff --git a/script/lib/jest.js b/script/lib/jest.js new file mode 100644 index 0000000..96ba5dc --- /dev/null +++ b/script/lib/jest.js @@ -0,0 +1,40 @@ +// Runs jest with the arguments it is given, and fails the run when jest had to +// force-stop the worker process that ran a test file. Jest itself only prints a +// warning when that happens, and its run still passes. The warning means a test +// left a timer or another open handle running. The test scripts in package.json +// run jest through this file. +// +// Jest runs inside this process and writes to the terminal directly, so its +// output looks the same as a plain jest run. This file only checks each write +// to stderr for the warning below. + +"use strict"; + +// The start of the warning that jest prints on stderr after it force-stops a +// worker process. +const WORKER_WARNING = "A worker process has failed to exit gracefully"; + +let forceStopped = false; + +const writeStderr = process.stderr.write.bind(process.stderr); +process.stderr.write = (chunk, ...rest) => { + if (String(chunk).includes(WORKER_WARNING)) { + forceStopped = true; + } + return writeStderr(chunk, ...rest); +}; + +// Runs after jest has finished, as the process exits. +process.on("exit", () => { + if (forceStopped) { + console.error( + "script/lib/jest.js: failing the run, because jest force-stopped " + + "a worker process. A test left a timer or another open " + + "handle running.", + ); + process.exitCode = 1; + } +}); + +// Jest's own command-line program. It reads its arguments from process.argv. +require("jest/bin/jest");