Vitest doesn't snapshot `Error.cause` like Jest does
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- testing-qa
Research direction
Start by running the linked minimal reproduction and comparing Vitest 4 with Jest 30 for toThrowErrorMatchingInlineSnapshot. Read the @vitest/pretty-format Error plugin and the snapshot serializer entry points mentioned in the issue; done means Error.cause is captured by default without unnecessarily changing unrelated error snapshots.
Written by the indexing model from the issue text.
Description
Describe the bug
Error.cause is now a standard ES feature.
Jest 30 implemented snapshot serialization of Error.cause: https://github.com/jestjs/jest/pull/13965
Vitest 4 doesn't serialize Error.cause by default, which IMHO should be considered as a bug and makes the migration from Jest to Vitest harder for me (https://github.com/facebook/docusaurus/pull/12010).
All modern tools and browser consoles now integrate with that new feature by default, so it would be great if Vitest did too.
When asserting errors and when we care about our project/framework DX, we often care about the full error chain, not just the top-level parent error.
Follow-up of discussion with @hi-ogawa here: https://github.com/vitest-dev/vitest/pull/10052#pullrequestreview-4274451482
Related request: https://github.com/vitest-dev/vitest/issues/5697#issuecomment-2453085114
Reproduction
Here's a minimal repro using Vitest 4 and Jest 30:
https://github.com/slorber/jest-vitest-snapshot-error-cause
Jest 30:
test('Hello World', () => {
expect(() => throwError()).toThrowErrorMatchingInlineSnapshot(`
"my parent error
Cause: x is not defined"
`)
});
Vitest 4:
test('Hello World', () => {
expect(() => throwError()).toThrowErrorMatchingInlineSnapshot(`[Error: my parent error]`)
});
Doing some research, I found out about a pretty-format Error plugin that could help:
import { plugins } from '@vitest/pretty-format'
expect.addSnapshotSerializer(plugins.Error)
test('Hello World', () => {
expect(() => throwError()).toThrowErrorMatchingInlineSnapshot(`
Error {
"message": "my parent error",
"cause": ReferenceError {
"message": "x is not defined",
},
}
`)
});
However, it was not so easy to find. It makes sense to do like Jest and serialize error causes by default, or at least provide clear documentation on how to officially achieve that, and eventually provide a top-level config option to make it easier.
Also note that this plugin significantly changes the snapshot format for errors, also serializing more things like error attributes, which may be a bit too verbose in some cases, and may reduce DX compared to what Jest currently offers natively.
In our case, we can't apply this plugin globally easily without increasing the size of many snapshots. For example, MDX parser and Joi validation errors contain a lot of attributes that we do not really need to snapshot.
Our best option is likely to create our own snapshot serializer, while I believe it should be something provided out of the box by Vitest.
Possible alternatives
It's possible to use matchers instead of snapshots: https://github.com/vitest-dev/vitest/issues/5697#issuecomment-2395624275
test('timeout', async () => {
await expect(async () => {
await expect.poll(() => false, { timeout: 100, interval: 10 }).toBe(true)
}).rejects.toThrowError(expect.objectContaining({
message: 'Matcher did not succeed in 100ms',
stack: expect.stringContaining('expect-poll.test.ts:38:68'),
cause: expect.objectContaining({
message: 'expected false to be true // Object.is equality',
}),
}))
})
However, I find this less ergonomic. We do want to use snapshots on purpose, we just want to capture regressions in our error messages and freeze them, and do not want to maintain the error messages on both impl/test sides.
System Info
Vitest 4 / Jest 30
Used Package Manager
npm
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- The provided reproduction is a minimal reproducible example of the bug.
- Dominant language
- TypeScript
- Stars
- 17.1k
- Forks
- 2k
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 92
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from vitest-dev/vitest
-
pending triage
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
vitest-dev/vitest#11276 · 3 reactions ·
-
p3-minor-bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
vitest-dev/vitest#11144 · 1 comment ·
-
pending triage
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
vitest-dev/vitest#11019 · 5 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
vitest-dev/vitest#10940 ·
-
p2-nice-to-have
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
vitest-dev/vitest#10793 · 1 comment ·
All issues in vitest-dev/vitest
Similar issues
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster no-stale P3
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 76/100
-
code-quality refactoring
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
github/gh-aw-firewall#8816 ·
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
langchain-ai/deepagents#6450 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
vercel/react-tweet#225 ·