Skip to content

Commit 84a3062

Browse files
fix(browser): string formatting bug when including placeholders in console.log (fix #9030) (#9131)
Co-authored-by: Hiroshi Ogawa <hi.ogawa.zz@gmail.com>
1 parent 6a024c5 commit 84a3062

File tree

6 files changed

+42
-19
lines changed

6 files changed

+42
-19
lines changed

packages/browser/src/client/tester/logger.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { format, stringify } from 'vitest/internal/browser'
1+
import { browserFormat } from 'vitest/internal/browser'
22
import { getConfig } from '../utils'
33
import { rpc } from './rpc'
44
import { getBrowserRunner } from './runner'
@@ -30,7 +30,7 @@ export function setupConsoleLogSpy(): void {
3030

3131
console.dir = (item, options) => {
3232
dir(item, options)
33-
sendLog('stdout', formatInput(item))
33+
sendLog('stdout', browserFormat(item))
3434
}
3535

3636
console.dirxml = (...args) => {
@@ -113,18 +113,8 @@ function stderr(base: (...args: unknown[]) => void) {
113113
}
114114
}
115115

116-
function formatInput(input: unknown) {
117-
if (typeof input === 'object') {
118-
return stringify(input, undefined, {
119-
printBasicPrototype: false,
120-
escapeString: false,
121-
})
122-
}
123-
return format(input)
124-
}
125-
126116
function processLog(args: unknown[]) {
127-
return args.map(formatInput).join(' ')
117+
return browserFormat(...args)
128118
}
129119

130120
function sendLog(

packages/utils/src/display.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,25 @@ export function stringify(
8080

8181
export const formatRegExp: RegExp = /%[sdjifoOc%]/g
8282

83-
export function format(...args: unknown[]): string {
83+
interface FormatOptions {
84+
prettifyObject?: boolean
85+
}
86+
87+
function baseFormat(args: unknown[], options: FormatOptions = {}): string {
88+
const formatArg = (item: unknown, inspecOptions?: LoupeOptions) => {
89+
if (options.prettifyObject) {
90+
return stringify(item, undefined, {
91+
printBasicPrototype: false,
92+
escapeString: false,
93+
})
94+
}
95+
return inspect(item, inspecOptions)
96+
}
97+
8498
if (typeof args[0] !== 'string') {
8599
const objects = []
86100
for (let i = 0; i < args.length; i++) {
87-
objects.push(inspect(args[i], { depth: 0, colors: false }))
101+
objects.push(formatArg(args[i], { depth: 0, colors: false }))
88102
}
89103
return objects.join(' ')
90104
}
@@ -112,7 +126,7 @@ export function format(...args: unknown[]): string {
112126
if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
113127
return value.toString()
114128
}
115-
return inspect(value, { depth: 0, colors: false })
129+
return formatArg(value, { depth: 0, colors: false })
116130
}
117131
return String(value)
118132
}
@@ -133,9 +147,9 @@ export function format(...args: unknown[]): string {
133147
case '%f':
134148
return Number.parseFloat(String(args[i++])).toString()
135149
case '%o':
136-
return inspect(args[i++], { showHidden: true, showProxy: true })
150+
return formatArg(args[i++], { showHidden: true, showProxy: true })
137151
case '%O':
138-
return inspect(args[i++])
152+
return formatArg(args[i++])
139153
case '%c': {
140154
i++
141155
return ''
@@ -168,12 +182,20 @@ export function format(...args: unknown[]): string {
168182
str += ` ${x}`
169183
}
170184
else {
171-
str += ` ${inspect(x)}`
185+
str += ` ${formatArg(x)}`
172186
}
173187
}
174188
return str
175189
}
176190

191+
export function format(...args: unknown[]): string {
192+
return baseFormat(args)
193+
}
194+
195+
export function browserFormat(...args: unknown[]): string {
196+
return baseFormat(args, { prettifyObject: true })
197+
}
198+
177199
export function inspect(obj: unknown, options: LoupeOptions = {}): string {
178200
if (options.truncate === 0) {
179201
options.truncate = Number.POSITIVE_INFINITY

packages/vitest/src/public/browser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export { collectTests, startTests } from '@vitest/runner'
1212
export * as SpyModule from '@vitest/spy'
1313
export type { LoupeOptions, ParsedStack, StringifyOptions } from '@vitest/utils'
1414
export {
15+
browserFormat,
1516
format,
1617
inspect,
1718
stringify,

test/browser/fixtures/print-logs/test/logs.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ test('console.time', async () => {
6868
await new Promise(r => setTimeout(r, 500))
6969
console.timeEnd('[console-time-fake]')
7070
})
71+
72+
test('log with placeholders', () => {
73+
console.log('hello from %s', "one")
74+
console.log('hello from %s', "two", { hello: 'object' })
75+
})

test/browser/specs/runner.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ describe('console logging tests', async () => {
128128
expect(stdout).toMatch(/time: [\d.]+ ms/)
129129
expect(stdout).toMatch(/\[console-time-fake\]: [\d.]+ ms/)
130130
expect(stdout).not.toContain('[console-time-fake]: 0 ms')
131+
expect(stdout).toContain('hello from one')
132+
expect(stdout).toContain(`hello from two {
133+
"hello": "object",
134+
}`)
131135
})
132136

133137
test('logs are redirected to stderr', () => {

test/core/test/exports.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ it('exports snapshot', async ({ skip, task }) => {
231231
"DecodedMap": "function",
232232
"SpyModule": "object",
233233
"__INTERNAL": "object",
234+
"browserFormat": "function",
234235
"collectTests": "function",
235236
"format": "function",
236237
"getOriginalPosition": "function",

0 commit comments

Comments
 (0)