Skip to content

Commit

Permalink
fix: don't serialize symbols and immutables (#2266)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Nov 3, 2022
1 parent c3d8950 commit c46c302
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 3 deletions.
6 changes: 3 additions & 3 deletions packages/vitest/src/node/error.ts
Expand Up @@ -90,7 +90,7 @@ function printErrorType(type: string, ctx: Vitest) {
ctx.logger.error(`\n${c.red(divider(c.bold(c.inverse(` ${type} `))))}`)
}

const skipErrorProperties = [
const skipErrorProperties = new Set([
'nameStr',
'stack',
'cause',
Expand All @@ -102,15 +102,15 @@ const skipErrorProperties = [
'expected',
...Object.getOwnPropertyNames(Error.prototype),
...Object.getOwnPropertyNames(Object.prototype),
]
])

function getErrorProperties(e: ErrorWithDiff) {
const errorObject = Object.create(null)
if (e.name === 'AssertionError')
return errorObject

for (const key of Object.getOwnPropertyNames(e)) {
if (!skipErrorProperties.includes(key))
if (!skipErrorProperties.has(key))
errorObject[key] = e[key as keyof ErrorWithDiff]
}

Expand Down
10 changes: 10 additions & 0 deletions packages/vitest/src/runtime/error.ts
Expand Up @@ -3,6 +3,11 @@ import { util as ChaiUtil } from 'chai'
import { stringify } from '../integrations/chai/jest-matcher-utils'
import { deepClone, getType } from '../utils'

const IS_RECORD_SYMBOL = '@@__IMMUTABLE_RECORD__@@'
const IS_COLLECTION_SYMBOL = '@@__IMMUTABLE_ITERABLE__@@'

const isImmutable = (v: any) => v && (v[IS_COLLECTION_SYMBOL] || v[IS_RECORD_SYMBOL])

const OBJECT_PROTO = Object.getPrototypeOf({})

function getUnserializableMessage(err: unknown) {
Expand All @@ -19,8 +24,13 @@ export function serializeError(val: any, seen = new WeakMap()): any {
return val
if (typeof val === 'function')
return `Function<${val.name}>`
if (typeof val === 'symbol')
return val.toString()
if (typeof val !== 'object')
return val
// cannot serialize immutables as immutables
if (isImmutable(val))
return serializeError(val.toJSON(), seen)
if (val instanceof Promise || (val.constructor && val.constructor.prototype === 'AsyncFunction'))
return 'Promise'
if (typeof Element !== 'undefined' && val instanceof Element)
Expand Down
1 change: 1 addition & 0 deletions test/core/test/__snapshots__/serialize.test.ts.snap
Expand Up @@ -30,5 +30,6 @@ exports[`error serialize > works 1`] = `
},
"null": null,
"promise": "Promise",
"symbol": "Symbol(hi)",
}
`;
31 changes: 31 additions & 0 deletions test/core/test/serialize.test.ts
Expand Up @@ -14,6 +14,7 @@ describe('error serialize', () => {
promise: new Promise(() => {}),
fn: () => {},
null: null,
symbol: Symbol('hi'),
nested: {
false: false,
class: class {},
Expand Down Expand Up @@ -137,4 +138,34 @@ describe('error serialize', () => {
stack: expect.stringContaining('InvalidStateError: You failed'),
})
})

it('correctly serialized immutables', () => {
const immutableList = {
'@@__IMMUTABLE_ITERABLE__@@': true,
toJSON() {
return ['foo']
},
}

const immutableRecord = {
'@@__IMMUTABLE_RECORD__@@': true,
toJSON() {
return { foo: 'bar' }
},
}

const error = new Error('test')
Object.assign(error, {
immutableList,
immutableRecord,
})

expect(serializeError(error)).toMatchObject({
stack: expect.stringContaining('Error: test'),
immutableList: ['foo'],
immutableRecord: { foo: 'bar' },
name: 'Error',
message: 'test',
})
})
})

0 comments on commit c46c302

Please sign in to comment.