From 99b1f77332b29e0110de1364647818723e3bd445 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 3 Nov 2022 10:52:12 +0100 Subject: [PATCH] fix: don't serialize symbols and immutables --- packages/vitest/src/node/error.ts | 6 ++-- packages/vitest/src/runtime/error.ts | 10 ++++++ .../test/__snapshots__/serialize.test.ts.snap | 1 + test/core/test/serialize.test.ts | 31 +++++++++++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/packages/vitest/src/node/error.ts b/packages/vitest/src/node/error.ts index 5ab9ff874b97..0746af6f2b2f 100644 --- a/packages/vitest/src/node/error.ts +++ b/packages/vitest/src/node/error.ts @@ -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', @@ -102,7 +102,7 @@ const skipErrorProperties = [ 'expected', ...Object.getOwnPropertyNames(Error.prototype), ...Object.getOwnPropertyNames(Object.prototype), -] +]) function getErrorProperties(e: ErrorWithDiff) { const errorObject = Object.create(null) @@ -110,7 +110,7 @@ function getErrorProperties(e: ErrorWithDiff) { return errorObject for (const key of Object.getOwnPropertyNames(e)) { - if (!skipErrorProperties.includes(key)) + if (!skipErrorProperties.has(key)) errorObject[key] = e[key as keyof ErrorWithDiff] } diff --git a/packages/vitest/src/runtime/error.ts b/packages/vitest/src/runtime/error.ts index ca4e4cb26543..6417ca86c7cf 100644 --- a/packages/vitest/src/runtime/error.ts +++ b/packages/vitest/src/runtime/error.ts @@ -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) { @@ -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) diff --git a/test/core/test/__snapshots__/serialize.test.ts.snap b/test/core/test/__snapshots__/serialize.test.ts.snap index 58791b8351f6..5ba7947a9f00 100644 --- a/test/core/test/__snapshots__/serialize.test.ts.snap +++ b/test/core/test/__snapshots__/serialize.test.ts.snap @@ -30,5 +30,6 @@ exports[`error serialize > works 1`] = ` }, "null": null, "promise": "Promise", + "symbol": "Symbol(hi)", } `; diff --git a/test/core/test/serialize.test.ts b/test/core/test/serialize.test.ts index b4e79c4dfbe3..6179c8cddb8d 100644 --- a/test/core/test/serialize.test.ts +++ b/test/core/test/serialize.test.ts @@ -14,6 +14,7 @@ describe('error serialize', () => { promise: new Promise(() => {}), fn: () => {}, null: null, + symbol: Symbol('hi'), nested: { false: false, class: class {}, @@ -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', + }) + }) })