Skip to content

Commit

Permalink
fix: prototype methods being discarded when using setData (#2166)
Browse files Browse the repository at this point in the history
Currently, when calling setData on a wrapper it will not retain methods on any constructed objects passed in. These methods reside in the prototype which the current logic does not persist across. These changes will ensure that any prototype properties/methods are copied over to the proxied data object.

fixes #1851
  • Loading branch information
rory-instil committed Aug 22, 2023
1 parent 2d618e7 commit 0ad433e
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 14 deletions.
30 changes: 16 additions & 14 deletions src/utils.ts
Expand Up @@ -80,20 +80,22 @@ export const mergeDeep = (
if (!isObject(target) || !isObject(source)) {
return source
}
Object.keys(source).forEach((key) => {
const targetValue = target[key]
const sourceValue = source[key]

if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
target[key] = sourceValue
} else if (sourceValue instanceof Date) {
target[key] = sourceValue
} else if (isObject(targetValue) && isObject(sourceValue)) {
target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue)
} else {
target[key] = sourceValue
}
})
Object.keys(source)
.concat(Object.getOwnPropertyNames(Object.getPrototypeOf(source) ?? {}))
.forEach((key) => {
const targetValue = target[key]
const sourceValue = source[key]

if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
target[key] = sourceValue
} else if (sourceValue instanceof Date) {
target[key] = sourceValue
} else if (isObject(targetValue) && isObject(sourceValue)) {
target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue)
} else {
target[key] = sourceValue
}
})

return target
}
Expand Down
32 changes: 32 additions & 0 deletions tests/setData.spec.ts
Expand Up @@ -214,4 +214,36 @@ describe('setData', () => {
expect(wrapper.vm.value).toBeInstanceOf(Date)
expect(wrapper.vm.value!.toISOString()).toBe('2022-08-11T12:15:54.000Z')
})

it('should retain prototype methods for constructed objects when calling setData', async () => {
const expectedResult = 'success!'
class TestClass {
constructor(readonly name: string) {}
getResult(): string {
return expectedResult
}
}

const wrapper = mount(
defineComponent({
template: '<div/>',
data() {
return { value: new TestClass('test1') }
},
methods: {
getResult() {
return `${this.value.name}: ${this.value.getResult()}`
}
}
})
)

expect(wrapper.vm.getResult()).toStrictEqual(`test1: ${expectedResult}`)

await wrapper.setData({
value: new TestClass('test2')
})

expect(wrapper.vm.getResult()).toStrictEqual(`test2: ${expectedResult}`)
})
})

0 comments on commit 0ad433e

Please sign in to comment.