Skip to content

Commit 9ac8ff9

Browse files
authored
fix(runner): Ensure test.each print -0 and -NaN properly (#5806)
1 parent dd754c1 commit 9ac8ff9

File tree

4 files changed

+45
-3
lines changed

4 files changed

+45
-3
lines changed

packages/runner/src/suite.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { format, isObject, objDisplay, objectAttr } from '@vitest/utils'
1+
import { format, isNegativeNaN, isObject, objDisplay, objectAttr } from '@vitest/utils'
22
import { parseSingleStack } from '@vitest/utils/source-map'
33
import type { Custom, CustomAPI, File, Fixtures, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, Task, TaskCustomOptions, Test, TestAPI, TestFunction, TestOptions } from './types'
44
import type { VitestRunner } from './types/runner'
@@ -433,6 +433,21 @@ function formatTitle(template: string, items: any[], idx: number) {
433433
.replace(/__vitest_escaped_%__/g, '%%')
434434
}
435435
const count = template.split('%').length - 1
436+
437+
if (template.includes('%f')) {
438+
const placeholders = template.match(/%f/g) || []
439+
placeholders.forEach((_, i) => {
440+
if (isNegativeNaN(items[i]) || Object.is(items[i], -0)) {
441+
// Replace the i-th occurrence of '%f' with '-%f'
442+
let occurrence = 0
443+
template = template.replace(/%f/g, (match) => {
444+
occurrence++
445+
return occurrence === i + 1 ? '-%f' : match
446+
})
447+
}
448+
})
449+
}
450+
436451
let formatted = format(template, ...items.slice(0, count))
437452
if (isObject(items[0])) {
438453
formatted = formatted.replace(

packages/utils/src/helpers.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,14 @@ export function getCallLastIndex(code: string) {
216216
}
217217
return null
218218
}
219+
220+
export function isNegativeNaN(val: number) {
221+
if (!Number.isNaN(val))
222+
return false
223+
const f64 = new Float64Array(1)
224+
f64[0] = val
225+
const u32 = new Uint32Array(f64.buffer)
226+
const isNegative = u32[1] >>> 31 === 1
227+
228+
return isNegative
229+
}

test/core/test/utils-display.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ describe('format', () => {
6060
expect(format(formatString, ...args), `failed ${formatString}`).toBe(util.format(formatString, ...args))
6161
})
6262

63-
test('cannont serialize some values', () => {
63+
test('cannot serialize some values', () => {
6464
expect(() => format('%j', 100n)).toThrowErrorMatchingInlineSnapshot(`[TypeError: Do not know how to serialize a BigInt]`)
6565
})
6666

test/core/test/utils.spec.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { beforeAll, describe, expect, test } from 'vitest'
2-
import { assertTypes, createColors, deepClone, objDisplay, objectAttr, toArray } from '@vitest/utils'
2+
import { assertTypes, createColors, deepClone, isNegativeNaN, objDisplay, objectAttr, toArray } from '@vitest/utils'
33
import { deepMerge, resetModules } from '../../../packages/vitest/src/utils'
44
import { deepMergeSnapshot } from '../../../packages/snapshot/src/port/utils'
55
import type { EncodedSourceMap } from '../../../packages/vite-node/src/types'
@@ -295,3 +295,19 @@ describe(createColors, () => {
295295
expect(c.blue(c.blue('x').repeat(10000))).toBeTruthy()
296296
})
297297
})
298+
299+
describe('isNegativeNaN', () => {
300+
test.each`
301+
value | expected
302+
${Number.NaN} | ${false}
303+
${-Number.NaN} | ${true}
304+
${0} | ${false}
305+
${-0} | ${false}
306+
${1} | ${false}
307+
${-1} | ${false}
308+
${Number.POSITIVE_INFINITY} | ${false}
309+
${Number.NEGATIVE_INFINITY} | ${false}
310+
`('isNegativeNaN($value) -> $expected', ({ value, expected }) => {
311+
expect(isNegativeNaN(value)).toBe(expected)
312+
})
313+
})

0 commit comments

Comments
 (0)