Skip to content

Commit ef01abf

Browse files
authored
fix: recursively call Vue.set in setData (#843)
1 parent 2aeaee3 commit ef01abf

File tree

5 files changed

+68
-26
lines changed

5 files changed

+68
-26
lines changed

Diff for: docs/api/wrapper/setData.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Sets `Wrapper` `vm` data.
44

5-
setData works by merging existing properties, except for arrays which are overwritten.
5+
setData works by recursively calling Vue.set.
66

77
**Note the Wrapper must contain a Vue instance.**
88

Diff for: packages/shared/util.js

+4
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,7 @@ export const hyphenate = (str: string): string =>
3434
export const vueVersion = Number(
3535
`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`
3636
)
37+
38+
export function isPlainObject (obj: any): boolean {
39+
return Object.prototype.toString.call(obj) === '[object Object]'
40+
}

Diff for: packages/test-utils/src/recursively-set-data.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { isPlainObject } from 'shared/util'
2+
3+
export function recursivelySetData (vm, target, obj) {
4+
Object.keys(obj).forEach(key => {
5+
const val = obj[key]
6+
if (isPlainObject(val)) {
7+
recursivelySetData(vm, target[key], val)
8+
} else {
9+
vm.$set(target, key, val)
10+
}
11+
})
12+
}

Diff for: packages/test-utils/src/wrapper.js

+3-23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// @flow
22

33
import Vue from 'vue'
4-
import mergeWith from 'lodash/mergeWith'
54
import getSelectorTypeOrThrow from './get-selector-type'
65
import {
76
REF_SELECTOR,
@@ -17,10 +16,11 @@ import {
1716
} from './find-vue-components'
1817
import WrapperArray from './wrapper-array'
1918
import ErrorWrapper from './error-wrapper'
20-
import { throwError, warn } from 'shared/util'
19+
import { throwError, warn } from '../../shared/util'
2120
import findAll from './find'
2221
import createWrapper from './create-wrapper'
2322
import { orderWatchers } from './order-watchers'
23+
import { recursivelySetData } from './recursively-set-data'
2424

2525
export default class Wrapper implements BaseWrapper {
2626
+vnode: VNode | null;
@@ -510,27 +510,7 @@ export default class Wrapper implements BaseWrapper {
510510
)
511511
}
512512

513-
Object.keys(data).forEach(key => {
514-
if (
515-
typeof data[key] === 'object' &&
516-
data[key] !== null &&
517-
!Array.isArray(data[key])
518-
) {
519-
const newObj = mergeWith(
520-
// $FlowIgnore : Problem with possibly null this.vm
521-
this.vm[key],
522-
data[key],
523-
(objValue, srcValue) => {
524-
return Array.isArray(srcValue) ? srcValue : undefined
525-
}
526-
)
527-
// $FlowIgnore : Problem with possibly null this.vm
528-
this.vm.$set(this.vm, [key], newObj)
529-
} else {
530-
// $FlowIgnore : Problem with possibly null this.vm
531-
this.vm.$set(this.vm, [key], data[key])
532-
}
533-
})
513+
recursivelySetData(this.vm, this.vm, data)
534514
}
535515

536516
/**

Diff for: test/specs/wrapper/setData.spec.js

+48-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ describeWithShallowAndMount('setData', mountingMethod => {
3333
const wrapper = mountingMethod(Component)
3434
wrapper.setData({ show: true })
3535
expect(wrapper.element).to.equal(wrapper.vm.$el)
36-
expect(wrapper.hasClass('some-class')).to.be.true
36+
expect(wrapper.classes()).to.contain('some-class')
3737
})
3838

3939
it('runs watch function when data is updated', () => {
@@ -117,7 +117,7 @@ describeWithShallowAndMount('setData', mountingMethod => {
117117
expect(wrapper.vm.basket[0]).to.equal('hello')
118118
})
119119

120-
it.skip('should not run watcher if data is null', () => {
120+
it('should not run watcher if data is null', () => {
121121
const TestComponent = {
122122
template: `
123123
<div>
@@ -217,4 +217,50 @@ describeWithShallowAndMount('setData', mountingMethod => {
217217
expect(wrapper.text()).to.equal('10')
218218
expect(wrapper.vm.nested.nested.nestedArray).to.deep.equal([10])
219219
})
220+
221+
it('should append a new property to an object when the new property is referenced by a template', () => {
222+
const TestComponent = {
223+
data: () => ({
224+
anObject: {
225+
propA: 'a',
226+
propB: 'b'
227+
}
228+
}),
229+
computed: {
230+
anObjectKeys () {
231+
return Object.keys(this.anObject).join(',')
232+
}
233+
},
234+
template: `<div>{{ anObjectKeys }}</div>`
235+
}
236+
const wrapper = mountingMethod(TestComponent)
237+
wrapper.setData({
238+
anObject: {
239+
propC: 'c'
240+
}
241+
})
242+
243+
expect(wrapper.vm.anObject.propA).to.equal('a')
244+
expect(wrapper.vm.anObject.propB).to.equal('b')
245+
expect(wrapper.vm.anObject.propC).to.equal('c')
246+
expect(wrapper.vm.anObjectKeys).to.equal('propA,propB,propC')
247+
expect(wrapper.html()).to.equal('<div>propA,propB,propC</div>')
248+
})
249+
250+
it('allows setting data of type Date synchronously', () => {
251+
const TestComponent = {
252+
template: `
253+
<div>
254+
{{selectedDate}}
255+
</div>
256+
`,
257+
data: () => ({
258+
selectedDate: undefined
259+
})
260+
}
261+
const testDate = new Date()
262+
const wrapper = mountingMethod(TestComponent)
263+
wrapper.setData({ selectedDate: testDate })
264+
expect(wrapper.vm.selectedDate).to.equal(testDate)
265+
})
220266
})

0 commit comments

Comments
 (0)