Skip to content

Commit

Permalink
fix: recursively call Vue.set in setData (#843)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyerburgh authored Jul 22, 2018
1 parent 2aeaee3 commit ef01abf
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 26 deletions.
2 changes: 1 addition & 1 deletion docs/api/wrapper/setData.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Sets `Wrapper` `vm` data.

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

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

Expand Down
4 changes: 4 additions & 0 deletions packages/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@ export const hyphenate = (str: string): string =>
export const vueVersion = Number(
`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`
)

export function isPlainObject (obj: any): boolean {
return Object.prototype.toString.call(obj) === '[object Object]'
}
12 changes: 12 additions & 0 deletions packages/test-utils/src/recursively-set-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { isPlainObject } from 'shared/util'

export function recursivelySetData (vm, target, obj) {
Object.keys(obj).forEach(key => {
const val = obj[key]
if (isPlainObject(val)) {
recursivelySetData(vm, target[key], val)
} else {
vm.$set(target, key, val)
}
})
}
26 changes: 3 additions & 23 deletions packages/test-utils/src/wrapper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @flow

import Vue from 'vue'
import mergeWith from 'lodash/mergeWith'
import getSelectorTypeOrThrow from './get-selector-type'
import {
REF_SELECTOR,
Expand All @@ -17,10 +16,11 @@ import {
} from './find-vue-components'
import WrapperArray from './wrapper-array'
import ErrorWrapper from './error-wrapper'
import { throwError, warn } from 'shared/util'
import { throwError, warn } from '../../shared/util'
import findAll from './find'
import createWrapper from './create-wrapper'
import { orderWatchers } from './order-watchers'
import { recursivelySetData } from './recursively-set-data'

export default class Wrapper implements BaseWrapper {
+vnode: VNode | null;
Expand Down Expand Up @@ -510,27 +510,7 @@ export default class Wrapper implements BaseWrapper {
)
}

Object.keys(data).forEach(key => {
if (
typeof data[key] === 'object' &&
data[key] !== null &&
!Array.isArray(data[key])
) {
const newObj = mergeWith(
// $FlowIgnore : Problem with possibly null this.vm
this.vm[key],
data[key],
(objValue, srcValue) => {
return Array.isArray(srcValue) ? srcValue : undefined
}
)
// $FlowIgnore : Problem with possibly null this.vm
this.vm.$set(this.vm, [key], newObj)
} else {
// $FlowIgnore : Problem with possibly null this.vm
this.vm.$set(this.vm, [key], data[key])
}
})
recursivelySetData(this.vm, this.vm, data)
}

/**
Expand Down
50 changes: 48 additions & 2 deletions test/specs/wrapper/setData.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describeWithShallowAndMount('setData', mountingMethod => {
const wrapper = mountingMethod(Component)
wrapper.setData({ show: true })
expect(wrapper.element).to.equal(wrapper.vm.$el)
expect(wrapper.hasClass('some-class')).to.be.true
expect(wrapper.classes()).to.contain('some-class')
})

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

it.skip('should not run watcher if data is null', () => {
it('should not run watcher if data is null', () => {
const TestComponent = {
template: `
<div>
Expand Down Expand Up @@ -217,4 +217,50 @@ describeWithShallowAndMount('setData', mountingMethod => {
expect(wrapper.text()).to.equal('10')
expect(wrapper.vm.nested.nested.nestedArray).to.deep.equal([10])
})

it('should append a new property to an object when the new property is referenced by a template', () => {
const TestComponent = {
data: () => ({
anObject: {
propA: 'a',
propB: 'b'
}
}),
computed: {
anObjectKeys () {
return Object.keys(this.anObject).join(',')
}
},
template: `<div>{{ anObjectKeys }}</div>`
}
const wrapper = mountingMethod(TestComponent)
wrapper.setData({
anObject: {
propC: 'c'
}
})

expect(wrapper.vm.anObject.propA).to.equal('a')
expect(wrapper.vm.anObject.propB).to.equal('b')
expect(wrapper.vm.anObject.propC).to.equal('c')
expect(wrapper.vm.anObjectKeys).to.equal('propA,propB,propC')
expect(wrapper.html()).to.equal('<div>propA,propB,propC</div>')
})

it('allows setting data of type Date synchronously', () => {
const TestComponent = {
template: `
<div>
{{selectedDate}}
</div>
`,
data: () => ({
selectedDate: undefined
})
}
const testDate = new Date()
const wrapper = mountingMethod(TestComponent)
wrapper.setData({ selectedDate: testDate })
expect(wrapper.vm.selectedDate).to.equal(testDate)
})
})

0 comments on commit ef01abf

Please sign in to comment.