diff --git a/docs/api/wrapper/setData.md b/docs/api/wrapper/setData.md index 8a90cb6d6..40bf823c7 100644 --- a/docs/api/wrapper/setData.md +++ b/docs/api/wrapper/setData.md @@ -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.** diff --git a/packages/shared/util.js b/packages/shared/util.js index 1b78c8506..730cf8b27 100644 --- a/packages/shared/util.js +++ b/packages/shared/util.js @@ -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]' +} diff --git a/packages/test-utils/src/recursively-set-data.js b/packages/test-utils/src/recursively-set-data.js new file mode 100644 index 000000000..bd75f35ae --- /dev/null +++ b/packages/test-utils/src/recursively-set-data.js @@ -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) + } + }) +} diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 7c453c7dc..093d6195c 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -1,7 +1,6 @@ // @flow import Vue from 'vue' -import mergeWith from 'lodash/mergeWith' import getSelectorTypeOrThrow from './get-selector-type' import { REF_SELECTOR, @@ -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; @@ -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) } /** diff --git a/test/specs/wrapper/setData.spec.js b/test/specs/wrapper/setData.spec.js index feba76ac9..41e3f97ac 100644 --- a/test/specs/wrapper/setData.spec.js +++ b/test/specs/wrapper/setData.spec.js @@ -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', () => { @@ -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: `
@@ -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: `
{{ anObjectKeys }}
` + } + 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('
propA,propB,propC
') + }) + + it('allows setting data of type Date synchronously', () => { + const TestComponent = { + template: ` +
+ {{selectedDate}} +
+ `, + data: () => ({ + selectedDate: undefined + }) + } + const testDate = new Date() + const wrapper = mountingMethod(TestComponent) + wrapper.setData({ selectedDate: testDate }) + expect(wrapper.vm.selectedDate).to.equal(testDate) + }) })