diff --git a/packages/vitest/src/utils/base.ts b/packages/vitest/src/utils/base.ts index 3874d9684815..b82d6fd9edd3 100644 --- a/packages/vitest/src/utils/base.ts +++ b/packages/vitest/src/utils/base.ts @@ -1,18 +1,25 @@ import type { Arrayable, DeepMerge, Nullable } from '../types' +function isFinalObj(obj: any) { + return obj === Object.prototype || obj === Function.prototype || obj === RegExp.prototype +} + +function collectOwnProperties(obj: any, collector: Set) { + const props = Object.getOwnPropertyNames(obj) + const symbs = Object.getOwnPropertySymbols(obj) + + props.forEach(prop => collector.add(prop)) + symbs.forEach(symb => collector.add(symb)) +} + export function getAllProperties(obj: any) { const allProps = new Set() let curr = obj do { // we don't need propterties from these - if (curr === Object.prototype || curr === Function.prototype || curr === RegExp.prototype) + if (isFinalObj(curr)) break - const props = Object.getOwnPropertyNames(curr) - const symbs = Object.getOwnPropertySymbols(curr) - - props.forEach(prop => allProps.add(prop)) - symbs.forEach(symb => allProps.add(symb)) - + collectOwnProperties(curr, allProps) // eslint-disable-next-line no-cond-assign } while (curr = Object.getPrototypeOf(curr)) return Array.from(allProps) @@ -36,6 +43,14 @@ export function getType(value: unknown): string { return Object.prototype.toString.apply(value).slice(8, -1) } +function getOwnProperties(obj: any) { + const ownProps = new Set() + if (isFinalObj(obj)) + return [] + collectOwnProperties(obj, ownProps) + return Array.from(ownProps) +} + export function clone(val: T): T { let k: any, out: any, tmp: any @@ -49,7 +64,8 @@ export function clone(val: T): T { if (Object.prototype.toString.call(val) === '[object Object]') { out = Object.create(Object.getPrototypeOf(val)) - const props = getAllProperties(val) + // we don't need properties from prototype + const props = getOwnProperties(val) for (const k of props) { // eslint-disable-next-line no-cond-assign out[k] = (tmp = (val as any)[k]) && typeof tmp === 'object' ? clone(tmp) : tmp diff --git a/test/core/test/utils.spec.ts b/test/core/test/utils.spec.ts index 7cb4e725c079..6135e1a024f3 100644 --- a/test/core/test/utils.spec.ts +++ b/test/core/test/utils.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'vitest' -import { assertTypes, deepMerge, toArray } from '../../../packages/vitest/src/utils' +import { assertTypes, clone, deepMerge, toArray } from '../../../packages/vitest/src/utils' import { deepMergeSnapshot } from '../../../packages/vitest/src/integrations/snapshot/port/utils' describe('assertTypes', () => { @@ -121,3 +121,26 @@ describe('toArray', () => { expect(toArray({ a: 1, b: 1, expected: 2 })).toEqual([{ a: 1, b: 1, expected: 2 }]) }) }) + +describe('clone', () => { + test('various types should be cloned correctly', () => { + expect(clone(1)).toBe(1) + expect(clone(true)).toBe(true) + expect(clone(undefined)).toBe(undefined) + expect(clone(null)).toBe(null) + expect(clone({ a: 1 })).toEqual({ a: 1 }) + expect(clone([1, 2])).toEqual([1, 2]) + const symbolA = Symbol('a') + expect(clone(symbolA)).toBe(symbolA) + const objB: any = {} + Object.defineProperty(objB, 'value', { + configurable: false, + enumerable: false, + value: 1, + writable: false, + }) + expect(clone(objB).value).toEqual(objB.value) + const objC = Object.create(objB) + expect(clone(objC).value).toEqual(objC.value) + }) +})