From 00ec76dd92401144c7daae0cffa82ee0d8c123a5 Mon Sep 17 00:00:00 2001 From: Nils Haberkamp Date: Thu, 30 Nov 2023 17:49:56 +0100 Subject: [PATCH 1/2] fix: ignore prototype methods when using setData on objects --- src/utils.ts | 26 +++++++++++++++++++++++++- tests/setData.spec.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index 7b65a7ec7..e70a673e1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -72,6 +72,25 @@ export function mergeGlobalProperties( export const isObject = (obj: unknown): obj is Record => !!obj && typeof obj === 'object' +function isClass(obj: unknown) { + if (!(obj instanceof Object)) return + + const isCtorClass = + obj.constructor && obj.constructor.toString().substring(0, 5) === 'class' + + if (!('prototype' in obj)) { + return isCtorClass + } + + const prototype = obj.prototype as any + const isPrototypeCtorClass = + prototype.constructor && + prototype.constructor.toString && + prototype.constructor.toString().substring(0, 5) === 'class' + + return isCtorClass || isPrototypeCtorClass +} + // https://stackoverflow.com/a/48218209 export const mergeDeep = ( target: Record, @@ -80,8 +99,13 @@ export const mergeDeep = ( if (!isObject(target) || !isObject(source)) { return source } + Object.keys(source) - .concat(Object.getOwnPropertyNames(Object.getPrototypeOf(source) ?? {})) + .concat( + isClass(source) + ? Object.getOwnPropertyNames(Object.getPrototypeOf(source) ?? {}) + : Object.getOwnPropertyNames(source) + ) .forEach((key) => { const targetValue = target[key] const sourceValue = source[key] diff --git a/tests/setData.spec.ts b/tests/setData.spec.ts index 6e0edb35b..2fa20430d 100644 --- a/tests/setData.spec.ts +++ b/tests/setData.spec.ts @@ -246,4 +246,33 @@ describe('setData', () => { expect(wrapper.vm.getResult()).toStrictEqual(`test2: ${expectedResult}`) }) + + it('should be possible to replace a primitive value with another', async () => { + const wrapper = mount( + defineComponent({ + template: '
', + data() { + return { + firstArray: [], + secondArray: [] + } + } + }) + ) + + await wrapper.setData({ + firstArray: [], + secondArray: [] + }) + + expect(wrapper.vm.$data).toStrictEqual({ + firstArray: [], + secondArray: [] + }) + + expect(Object.keys(wrapper.vm.$data)).toStrictEqual([ + 'firstArray', + 'secondArray' + ]) + }) }) From 30f971bd4218522671fac26891d1ae43feef327c Mon Sep 17 00:00:00 2001 From: Nils Haberkamp Date: Mon, 4 Dec 2023 14:55:36 +0100 Subject: [PATCH 2/2] test: improve tests for ignoring prototype methods when using setData --- tests/setData.spec.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/setData.spec.ts b/tests/setData.spec.ts index 2fa20430d..ba4255b6d 100644 --- a/tests/setData.spec.ts +++ b/tests/setData.spec.ts @@ -247,7 +247,8 @@ describe('setData', () => { expect(wrapper.vm.getResult()).toStrictEqual(`test2: ${expectedResult}`) }) - it('should be possible to replace a primitive value with another', async () => { + // https://github.com/vuejs/test-utils/issues/2257 + it('should ignore prototype methods when using setData on objects', async () => { const wrapper = mount( defineComponent({ template: '
', @@ -261,18 +262,13 @@ describe('setData', () => { ) await wrapper.setData({ - firstArray: [], - secondArray: [] + firstArray: [1, 2], + secondArray: [3, 4] }) expect(wrapper.vm.$data).toStrictEqual({ - firstArray: [], - secondArray: [] + firstArray: [1, 2], + secondArray: [3, 4] }) - - expect(Object.keys(wrapper.vm.$data)).toStrictEqual([ - 'firstArray', - 'secondArray' - ]) }) })