Skip to content

Commit 91e1650

Browse files
authoredJun 28, 2023
fix(utils): respect all flags in format function (#3695)
1 parent 726a57e commit 91e1650

File tree

4 files changed

+278
-119
lines changed

4 files changed

+278
-119
lines changed
 

‎packages/utils/src/display.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ interface LoupeOptions {
1818
truncate?: number
1919
}
2020

21-
const formatRegExp = /%[sdj%]/g
21+
const formatRegExp = /%[sdjifoOcj%]/g
2222

2323
export function format(...args: unknown[]) {
2424
if (typeof args[0] !== 'string') {
2525
const objects = []
2626
for (let i = 0; i < args.length; i++)
27-
objects.push(inspect(args[i]))
27+
objects.push(inspect(args[i], { depth: 0, colors: false, compact: 3 }))
2828
return objects.join(' ')
2929
}
3030

@@ -62,13 +62,26 @@ export function format(...args: unknown[]) {
6262
case '%f': return Number.parseFloat(String(args[i++])).toString()
6363
case '%o': return inspect(args[i++], { showHidden: true, showProxy: true })
6464
case '%O': return inspect(args[i++])
65-
case '%c': return ''
65+
case '%c': {
66+
i++
67+
return ''
68+
}
6669
case '%j':
6770
try {
6871
return JSON.stringify(args[i++])
6972
}
70-
catch (_) {
71-
return '[Circular]'
73+
catch (err: any) {
74+
const m = err.message
75+
if (
76+
// chromium
77+
m.includes('circular structure')
78+
// safari
79+
|| m.includes('cyclic structures')
80+
// firefox
81+
|| m.includes('cyclic object')
82+
)
83+
return '[Circular]'
84+
throw err
7285
}
7386
default:
7487
return x

‎pnpm-lock.yaml

+155-114
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/utils/package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "@vitest/test-utils",
3+
"private": true,
4+
"scripts": {
5+
"test": "vitest",
6+
"coverage": "vitest run --coverage"
7+
},
8+
"devDependencies": {
9+
"@vitest/utils": "workspace:*",
10+
"vitest": "workspace:*"
11+
}
12+
}

‎test/utils/test/display.spec.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import util from 'node:util'
2+
import { describe, expect, test } from 'vitest'
3+
import { format } from '@vitest/utils'
4+
5+
describe('format', () => {
6+
const obj = {} as any
7+
obj.obj = obj
8+
9+
test.each([
10+
[''],
11+
['test'],
12+
[{ obj: { nested: true }, value: 1 }],
13+
['test %s', 'test'],
14+
['test %s %s', 'test', 'test'],
15+
['test %s %s', 'test', 'test', 'test'],
16+
['%s', 100],
17+
['%s', 100n],
18+
['%s', -0],
19+
['%s', null],
20+
['%s', null, 'next'],
21+
['%d', 100],
22+
['%d', 100n],
23+
['%d', null],
24+
['%d', {}],
25+
['%d', {}, 'next'],
26+
['%i', 100],
27+
['%i', 100n],
28+
['%i', null],
29+
['%i', {}],
30+
['%i', {}, 'next'],
31+
['%f', 100],
32+
['%f', 100n],
33+
['%f', null],
34+
['%f', {}],
35+
['%f', {}, 'next'],
36+
['%o', 'string'],
37+
['%o', 100],
38+
['%o', 100n],
39+
['%o', null],
40+
['%o', {}],
41+
['%o', {}, 'next'],
42+
['%O', 'string'],
43+
['%O', 100],
44+
['%O', 100n],
45+
['%O', null],
46+
['%O', {}],
47+
['%O', {}, 'next'],
48+
['%c', 'css value'],
49+
['%c', 'css value', 'some other value'],
50+
['%c %f', 'css value', '100.00'],
51+
['%j', 'string'],
52+
['%j', 100],
53+
['%j', null],
54+
['%j', {}],
55+
['%j', {}, 'next'],
56+
['%j', { obj }],
57+
['%j', { fn: () => {} }],
58+
['%%', 'string'],
59+
])('format(%s)', (formatString, ...args) => {
60+
expect(format(formatString, ...args), `failed ${formatString}`).toBe(util.format(formatString, ...args))
61+
})
62+
63+
test('cannont serialize some values', () => {
64+
expect(() => format('%j', 100n)).toThrowErrorMatchingInlineSnapshot('"Do not know how to serialize a BigInt"')
65+
})
66+
67+
test.each(
68+
[
69+
{
70+
name: 'without format',
71+
args: [{ n: { a: { b: { c: { d: { e: '3' } } } } } }],
72+
result: '{ n: { a: { b: { c: { d: { e: \'3\' } } } } } }',
73+
},
74+
{
75+
name: 'as an object',
76+
args: ['%o', {}, { n: { a: { b: { c: '3' } } } }],
77+
result: '{} { n: { a: { b: { c: \'3\' } } } }',
78+
},
79+
{
80+
name: 'as a full object',
81+
args: ['%O', {}, { n: { a: { b: { c: '3' } } } }],
82+
result: '{} { n: { a: { b: { c: \'3\' } } } }',
83+
},
84+
{
85+
name: 'as a json',
86+
args: ['%j', {}, { n: { a: { b: { c: '3' } } } }],
87+
result: '{} { n: { a: { b: { c: \'3\' } } } }',
88+
},
89+
],
90+
)('formats objects $name (loupe doesn\'t respect depth)', ({ args, result }) => {
91+
expect(format(...args)).toBe(result)
92+
})
93+
})

0 commit comments

Comments
 (0)
Please sign in to comment.