Repository navigation
Conversation
When a test or hook timed out before its done callback was called, the
hint said to remove the last parameter from the callback function. The
done callback is not always the last parameter:
- test(name, (a, b) => {}) and beforeEach((a, b) => {}) receive it in a.
- test.each([[1, 2]])(name, (a, b, c, d) => {}) receives it in c.
Removing the last parameter left those with the same timeout. The hint
now says to remove the parameters that the callback function gets no
value for. The text before the hint is unchanged.
|
No actionable comments were generated in the recent review. WalkthroughTimeout guidance for tests and hooks that fail before invoking a done callback now advises removing callback parameters that receive no value. Tests cover Bun callbacks, ChangesTest timeout guidance
Possibly related issues
Suggested reviewers: Merge Risk:
|
|
Status: ready for review. How I reproduced it on a release build of main (98cda0d), with import { test } from "bun:test";
test("x", (a, b) => {
console.log(typeof a, typeof b);
});Without PR: #48536 |
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Beyond the inline finding, I also swept the repo for the old "remove the last parameter" wording — no stale assertions, docs, or comments remain — and checked the --timeout 1 run: every fixture callback never calls done, so the timeout is the intended outcome rather than a speed-dependent race. I did not run the test suite; the inline snapshots were checked by reading.
Extended reasoning...
The change rewords two hint literals in src/runtime/cli/test_command.rs with no logic change, updates two existing inline snapshots, and adds one subprocess snapshot test covering test, test.each (array and object rows), beforeEach, and a node:test before hook. It touches no security-sensitive surface. The inline finding about the node:test lines in the new snapshot is the reason a human look is still worthwhile.
The start of that line names the wrong hook kind for a node:test before() hook and prints (unnamed) as the test name. The test of the done-timeout hint no longer pins those parts. It runs the node:test file in its own process and compares the hint alone.
Related to #24347
Problem
doneis called printsremove the last parameter from the test callback function(orhook).doneis often another parameter.test("x", (a, b) => {})getsdoneina.test.each([[1, 2]])("x", (a, b, c, d) => {})gets it inc. Without the last parameter, the line prints again.print_test_line(src/runtime/cli/test_command.rs:2032,:2040).Fix
remove the parameters that the test callback function gets no value for(hookin the hook line).done, after the values that.eachbinds (src/runtime/test_runner/bun_test.rs:1379).test/js/bun/test/bun_test.test.ts(2 of 8 tests fail on main). Also ranjest-each.test.ts,jest-hooks.test.tsandnode-test.test.ts.Background
bun:testcallback with more parameters than values getsdone, and Bun waits for its call.test.eachpasses a row's values first.doneis: no command, and wrong for anode:testhook(context, done)..each(new state, wrong after bun:test: support optional trailing tuple elements in test.each #31618 or bun:test: spreadtest.eachrows only if every row is an array, as Jest does #48440).Downsides
.eachrow shorter than another row (test.eachdoes not support tuples with optional elements at the end #24347), the command removes a parameter that other rows use.--lto=off):text+64 bytes, stripped file size equal.Notes
Other open PRs that touch these two lines
This PR changes only the command at the end of both lines.
beforeEach. That line changes with it. For thenode:testhook, the test compares only the hint.test/js/bun/test/test-test.test.ts. bun test: identify failed hooks and their files #43843 has one intest/cli/test/test-timeout-behavior.test.ts. Neither file is in this PR, so git reports no conflict. The PR that merges second must update those lines. Neither PR has a note about this.git grep -n "remove the last parameter" -- src testmust print nothing.Not changed
.eachtable receives.test.eachdoes not support tuples with optional elements at the end #24347 and bun:test: support optional trailing tuple elements in test.each #31618 own that..todoand.failinghints in the samematch.before its done callback was calledis also printed if the callback callsdone()after the deadline (bun test: time out a test or hook whose callback completes after its deadline #39440).node:test. Bun registers eachnode:testtest and hook through a wrapper that takesdone(src/js/node/test.ts:2564,:2840). If bun:test's timer ends such a test or hook, these lines are printed, even for a callback with no parameter (node:testasync tests exceeding 5s timeout fail in Bun but pass in Node #27422,node:testasync tests and hooks time out after 5s instead of matching Node.js no-timeout default #28190). In that case the new command has nothing to remove. For a hookbefore((context, done) => {})the command is right:contextgets a value.What each callback receives
On a debug build of this branch. "After the command" is the same callback without the parameters that get no value.
test("x", (a, b) => {})function, undefined() => {}passestest.each([[1, 2]])("x", (a, b, c, d) => {})number, number, function, undefined(a, b) => {}passestest.each([{ a: 1 }])("x", (row, extra) => {})object, functionrow => {}passesbeforeEach((a, b) => {})function, undefined() => {}passesbefore((a, b) => {})fromnode:testobject, functiona => {}passesWith the old command (remove the last parameter), rows 1, 2 and 4 print the same line again.
Run by hand, not in the test:
test.each([[1, 2], [3]])("x", (a, b) => {}): the row[3]receivesnumber, function.test.each([[1, 2], [1, 2, undefined]])("x", (a, b, c, d) => {}):number, number, function, undefinedandnumber, number, undefined, function.test.each([[]])("x", value => {}):function.(row, extra):object, function.The new test has no table with rows of different lengths. No test on main pins what such a row receives, and #24347 is open.
An option
The hint can end with
, or give them a default value. A parameter with a default value is not counted, sotest.each([[1, 2, 3], [4, 5]])("x", (a, b, c = undefined) => {})passes and still reads the third column. This PR does not add it, because it is the way out for a short row, and #24347 decides what a short row receives.Jest
Jest 30.5.2 passes
donein the same positions. It printsExceeded timeout of 300 ms for a test while waiting for `done()` to be called.with a code frame at thetest.eachcall, and no advice about parameters. The command is Bun's own (#22534).Test runs
bun bd test test/js/bun/test/bun_test.test.tswithsrc/of main: 6 pass, 2 fail. Only the hint text differs: 2 lines indescribe/test, and each hint in the new test. With this branch: 8 pass. Release builds give the same counts.jest-each.test.ts(35 pass),jest-hooks.test.ts(25 pass, 1 todo),test-failing.test.ts(8 pass),test-error-code-done-callback.test.ts(3 pass),done-async.test.ts(1 pass),test/js/node/test_runner/node-test.test.ts(45 pass).expect-deferred.test.ts(1 of 56 tests) andtest/cli/test/bun-test.test.ts(3 of 235 tests, then 7) had 5 s timeouts on the debug build. The tests that timed out do not print this hint, and they changed from run to run. The test ofexpect-deferred.test.tspasses alone.Cost
--lto=offof 98cda0d and of 98cda0d with this change,sizeofbun:text138,833,326 and 138,833,390,data722,228 andbss1,862,416 in both, stripped file 139,622,760 bytes in both. Debug builds:text+40 bytes. The branch was rebased after the measurement.