bun:test: render null/undefined in test.each $key titles and stop eating the next character
Repro
import { it } from "bun:test";
it.each([{ v: null }])("v is $v here", () => {});
| before | after | Jest | |
|---|---|---|---|
{ v: null } / "v is $v here" | v is $vhere | v is null here | v is null here |
{ v: undefined } / "u is $v end" | u is $vend | u is undefined end | u is undefined end |
{ v: 1 } / "x $nope end" | x $nopeend | x $nope end | x $nope end |
{ a: { b: null } } / "n $a.b tail" | n $a.btail | n null tail | n null tail |
{ v: 1 } / "$5 USD" | $5USD | $5 USD | $5 USD |
Cause
format_label's $key fallback wrote the literal $key text and set idx = var_end, then fell through to the loop's trailing idx += 1, which skipped the character immediately after the identifier. Both arms of the is_identifier_start check land on that fallback (property missing after a valid identifier, and $ followed by a digit or other non-identifier-start), so both swallowed a character.
Separately, is_empty_or_undefined_or_null() sent existing-but-null/undefined properties down the same literal path. Jest renders those values through pretty-format.
Fix
continueafter emitting the literal$keyso the next character is not swallowed.- Guard only on
is_empty().get_if_property_exists_from_pathreturns an emptyJSValuefor a missing property and the real null/undefined value otherwise, so null/undefined now go through the same formatter as every other non-string value and printnull/undefined. A missing property still leaves the literal placeholder, which is also what Jest does.
Tests
test/cli/test/bun-test.test.ts in the existing $variable syntax block:
- New
renders null and undefined property valuescovers{ v: null },{ v: undefined }, nested$a.bresolving to null, and a missing property keeping its literal with the following space intact. handles edge cases with underscores and invalid identifiersnow asserts the full rendered title, which pins the space after$123invalid(the non-identifier-start arm) as well as$has-dash/$has space.handles missing properties gracefullypreviously encoded the swallowed-character output ($missing|) and now expects the full title.
All three fail on the released binary and pass with this change.
no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/test/bun-test.test.ts