Skip to content

Commit 6b73473

Browse files
authored
feat(vitest): filter stacktraces (fix #1999) (#4338)
1 parent fc51ad0 commit 6b73473

File tree

11 files changed

+115
-2
lines changed

11 files changed

+115
-2
lines changed

docs/config/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,34 @@ export default defineConfig({
17851785
})
17861786
```
17871787

1788+
### onStackTrace
1789+
1790+
- **Type**: `(error: Error, frame: ParsedStack) => boolean | void`
1791+
- **Version**: Since Vitest 1.0.0-beta.3
1792+
1793+
Apply a filtering function to each frame of each stacktrace when handling errors. The first argument, `error`, is an object with the same properties as a standard `Error`, but it is not an actual instance.
1794+
1795+
Can be useful for filtering out stacktrace frames from third-party libraries.
1796+
1797+
```ts
1798+
import type { ParsedStack } from 'vitest'
1799+
import { defineConfig } from 'vitest/config'
1800+
1801+
export default defineConfig({
1802+
test: {
1803+
onStackTrace(error: Error, { file }: ParsedStack): boolean | void {
1804+
// If we've encountered a ReferenceError, show the whole stack.
1805+
if (error.name === 'ReferenceError')
1806+
return
1807+
1808+
// Reject all frames from third party libraries.
1809+
if (file.includes('node_modules'))
1810+
return false
1811+
},
1812+
},
1813+
})
1814+
```
1815+
17881816
### diff
17891817

17901818
- **Type:** `string`

packages/utils/src/source-map.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type { SourceMapInput } from '@jridgewell/trace-mapping'
1010
export interface StackTraceParserOptions {
1111
ignoreStackEntries?: (RegExp | string)[]
1212
getSourceMap?: (file: string) => unknown
13+
frameFilter?: (error: Error, frame: ParsedStack) => boolean | void
1314
}
1415

1516
const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m
@@ -179,7 +180,10 @@ export function parseErrorStacktrace(e: ErrorWithDiff, options: StackTraceParser
179180
return e.stacks
180181

181182
const stackStr = e.stack || e.stackStr || ''
182-
const stackFrames = parseStacktrace(stackStr, options)
183+
let stackFrames = parseStacktrace(stackStr, options)
184+
185+
if (options.frameFilter)
186+
stackFrames = stackFrames.filter(f => options.frameFilter!(e, f) !== false)
183187

184188
e.stacks = stackFrames
185189
return stackFrames

packages/vitest/src/node/error.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export async function printError(error: unknown, project: WorkspaceProject | und
4949
const parserOptions: StackTraceParserOptions = {
5050
// only browser stack traces require remapping
5151
getSourceMap: file => project.getBrowserSourceMapModuleById(file),
52+
frameFilter: project.config.onStackTrace,
5253
}
5354

5455
if (fullStack)

packages/vitest/src/node/reporters/json.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ export class JsonReporter implements Reporter {
186186
const project = this.ctx.getProjectByTaskId(test.id)
187187
const stack = parseErrorStacktrace(error, {
188188
getSourceMap: file => project.getBrowserSourceMapModuleById(file),
189+
frameFilter: this.ctx.config.onStackTrace,
189190
})
190191
const frame = stack[0]
191192
if (!frame)

packages/vitest/src/node/reporters/junit.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export class JUnitReporter implements Reporter {
135135
const project = this.ctx.getProjectByTaskId(task.id)
136136
const stack = parseErrorStacktrace(error, {
137137
getSourceMap: file => project.getBrowserSourceMapModuleById(file),
138+
frameFilter: this.ctx.config.onStackTrace,
138139
})
139140

140141
// TODO: This is same as printStack but without colors. Find a way to reuse code.

packages/vitest/src/node/reporters/tap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export class TapReporter implements Reporter {
7676
task.result.errors.forEach((error) => {
7777
const stacks = parseErrorStacktrace(error, {
7878
getSourceMap: file => project.getBrowserSourceMapModuleById(file),
79+
frameFilter: this.ctx.config.onStackTrace,
7980
})
8081
const stack = stacks[0]
8182

packages/vitest/src/node/workspace.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ export class WorkspaceProject {
315315
resolveSnapshotPath: undefined,
316316
},
317317
onConsoleLog: undefined!,
318+
onStackTrace: undefined!,
318319
sequence: {
319320
...this.ctx.config.sequence,
320321
sequencer: undefined!,

packages/vitest/src/types/config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { JSDOMOptions } from './jsdom-options'
1111
import type { HappyDOMOptions } from './happy-dom-options'
1212
import type { Reporter } from './reporter'
1313
import type { SnapshotStateOptions } from './snapshot'
14-
import type { Arrayable } from './general'
14+
import type { Arrayable, ParsedStack } from './general'
1515
import type { BenchmarkUserOptions } from './benchmark'
1616
import type { BrowserConfigOptions, ResolvedBrowserOptions } from './browser'
1717
import type { Pool, PoolOptions } from './pool-options'
@@ -537,6 +537,14 @@ export interface InlineConfig {
537537
*/
538538
onConsoleLog?: (log: string, type: 'stdout' | 'stderr') => false | void
539539

540+
/**
541+
* Enable stack trace filtering. If absent, all stack trace frames
542+
* will be shown.
543+
*
544+
* Return `false` to omit the frame.
545+
*/
546+
onStackTrace?: (error: Error, frame: ParsedStack) => boolean | void
547+
540548
/**
541549
* Indicates if CSS files should be processed.
542550
*
@@ -788,6 +796,7 @@ export type ProjectConfig = Omit<
788796
| 'resolveSnapshotPath'
789797
| 'passWithNoTests'
790798
| 'onConsoleLog'
799+
| 'onStackTrace'
791800
| 'dangerouslyIgnoreUnhandledErrors'
792801
| 'slowTestThreshold'
793802
| 'inspect'
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { test } from 'vitest'
2+
3+
test('error in deps', () => {
4+
a()
5+
})
6+
7+
function a() {
8+
b()
9+
}
10+
11+
function b() {
12+
c()
13+
}
14+
15+
function c() {
16+
d()
17+
}
18+
19+
function d() {
20+
throw new Error('Something truly horrible has happened!')
21+
}

test/stacktraces/test/__snapshots__/runner.test.ts.snap

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`stacktrace filtering > filters stacktraces > stacktrace-filtering 1`] = `
4+
"⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
5+
6+
FAIL error-with-stack.test.js > error in deps
7+
Error: Something truly horrible has happened!
8+
❯ d error-with-stack.test.js:20:9
9+
18|
10+
19| function d() {
11+
20| throw new Error('Something truly horrible has happened!')
12+
| ^
13+
21| }
14+
22|
15+
❯ c error-with-stack.test.js:16:3
16+
❯ a error-with-stack.test.js:8:3
17+
❯ error-with-stack.test.js:4:3
18+
19+
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
20+
21+
"
22+
`;
23+
324
exports[`stacktrace should print error frame source file correctly > error-in-deps > error-in-deps 1`] = `
425
"⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
526
@@ -71,6 +92,17 @@ exports[`stacktraces should respect sourcemaps > error-in-deps.test.js > error-i
7192
"
7293
`;
7394
95+
exports[`stacktraces should respect sourcemaps > error-with-stack.test.js > error-with-stack.test.js 1`] = `
96+
" ❯ d error-with-stack.test.js:20:9
97+
18|
98+
19| function d() {
99+
20| throw new Error('Something truly horrible has happened!')
100+
| ^
101+
21| }
102+
22|
103+
❯ c error-with-stack.test.js:16:3"
104+
`;
105+
74106
exports[`stacktraces should respect sourcemaps > mocked-global.test.js > mocked-global.test.js 1`] = `
75107
" ❯ mocked-global.test.js:6:13
76108
4|

0 commit comments

Comments
 (0)