From 689d76bafd45d9d2b03157be10d8dd5f221e20fe Mon Sep 17 00:00:00 2001 From: Illya Klymov Date: Mon, 2 Sep 2019 18:42:01 +0300 Subject: [PATCH 1/2] chore: Add async test with immediate watcher --- .../component-with-watch-immediate.vue | 22 +++++++++++++++++++ test/specs/config.spec.js | 4 +--- test/specs/wrapper/setProps.spec.js | 12 ++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 test/resources/components/component-with-watch-immediate.vue diff --git a/test/resources/components/component-with-watch-immediate.vue b/test/resources/components/component-with-watch-immediate.vue new file mode 100644 index 000000000..a660b9865 --- /dev/null +++ b/test/resources/components/component-with-watch-immediate.vue @@ -0,0 +1,22 @@ + + diff --git a/test/specs/config.spec.js b/test/specs/config.spec.js index 660aafabc..c66ab40a6 100644 --- a/test/specs/config.spec.js +++ b/test/specs/config.spec.js @@ -85,9 +85,7 @@ describeWithShallowAndMount('config', mountingMethod => { localVue }) expect(wrapper.vm.prop1).to.equal('example') - wrapper.setProps({ - prop1: 'new value' - }) + wrapper.vm.prop1 = 'new value' expect(console.error).calledWith(sandbox.match('[Vue warn]')) }) }) diff --git a/test/specs/wrapper/setProps.spec.js b/test/specs/wrapper/setProps.spec.js index de74807dd..29b38c905 100644 --- a/test/specs/wrapper/setProps.spec.js +++ b/test/specs/wrapper/setProps.spec.js @@ -1,6 +1,7 @@ import { compileToFunctions } from 'vue-template-compiler' import ComponentWithProps from '~resources/components/component-with-props.vue' import ComponentWithWatch from '~resources/components/component-with-watch.vue' +import ComponentWithWatchImmediate from '~resources/components/component-with-watch-immediate.vue' import { describeWithShallowAndMount, vueVersion } from '~resources/utils' import { itDoNotRunIf } from 'conditional-specs' import Vue from 'vue' @@ -244,6 +245,17 @@ describeWithShallowAndMount('setProps', mountingMethod => { expect(wrapper.vm.propA).to.equal('value') }) + it('correctly sets props in async mode when component has immediate watchers', async () => { + const wrapper = mountingMethod(ComponentWithWatchImmediate, { + sync: false + }) + + const prop1 = 'testest' + wrapper.setProps({ prop1 }) + await Vue.nextTick() + expect(wrapper.vm.prop1).to.equal(prop1) + }) + it('throws an error if node is not a Vue instance', () => { const message = 'wrapper.setProps() can only be called on a Vue instance' const compiled = compileToFunctions('

') From cbf1bd41c1a55962dd7988b80539fdb1a86d44ec Mon Sep 17 00:00:00 2001 From: Illya Klymov Date: Mon, 2 Sep 2019 18:53:40 +0300 Subject: [PATCH 2/2] fix: add support for proper props update in async mode Since we're already wrapping a component into a parent one we can get rid of explicitly setting props on component, instead modifying them being passed from parent component instead --- packages/create-instance/create-instance.js | 12 +++++-- packages/test-utils/src/wrapper.js | 35 ++++++--------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index fb7cfc292..d1bc01657 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -11,7 +11,7 @@ import createScopedSlots from './create-scoped-slots' import { createStubsFromStubsObject } from './create-component-stubs' import { patchCreateElement } from './patch-create-element' -function createContext(options, scopedSlots) { +function createContext(options, scopedSlots, currentProps) { const on = { ...(options.context && options.context.on), ...options.listeners @@ -21,7 +21,7 @@ function createContext(options, scopedSlots) { ...options.attrs, // pass as attrs so that inheritAttrs works correctly // propsData should take precedence over attrs - ...options.propsData + ...currentProps }, ...(options.context || {}), on, @@ -98,10 +98,16 @@ export default function createInstance( parentComponentOptions._provided = options.provide parentComponentOptions.$_doNotStubChildren = true parentComponentOptions._isFunctionalContainer = componentOptions.functional + const originalDataFn = parentComponentOptions.data + parentComponentOptions.data = function() { + const originalData = originalDataFn ? originalDataFn() : {} + originalData.vueTestUtils_childProps = { ...options.propsData } + return originalData + } parentComponentOptions.render = function(h) { return h( Constructor, - createContext(options, scopedSlots), + createContext(options, scopedSlots, this.vueTestUtils_childProps), createChildren(this, h, options) ) } diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index b8beeca16..bc2a5e6a9 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -1,10 +1,7 @@ // @flow - -import Vue from 'vue' import pretty from 'pretty' import getSelector from './get-selector' import { REF_SELECTOR, FUNCTIONAL_OPTIONS, VUE_VERSION } from 'shared/consts' -import config from './config' import WrapperArray from './wrapper-array' import ErrorWrapper from './error-wrapper' import { throwError, getCheckedEvent, isPhantomJS } from 'shared/util' @@ -478,8 +475,6 @@ export default class Wrapper implements BaseWrapper { * Sets vm props */ setProps(data: Object): void { - const originalConfig = Vue.config.silent - Vue.config.silent = config.silent if (this.isFunctionalComponent) { throwError( `wrapper.setProps() cannot be called on a functional component` @@ -503,38 +498,26 @@ export default class Wrapper implements BaseWrapper { ) } if ( - !this.vm || - !this.vm.$options._propKeys || - !this.vm.$options._propKeys.some(prop => prop === key) + VUE_VERSION <= 2.3 && + // $FlowIgnore : Problem with possibly null this.vm + (!this.vm.$options._propKeys || + !this.vm.$options._propKeys.some(prop => prop === key)) ) { - if (VUE_VERSION > 2.3) { - // $FlowIgnore : Problem with possibly null this.vm - this.vm.$attrs[key] = data[key] - return - } throwError( `wrapper.setProps() called with ${key} property which ` + `is not defined on the component` ) } - if (this.vm && this.vm._props) { - // Set actual props value - this.vm._props[key] = data[key] - // $FlowIgnore : Problem with possibly null this.vm - this.vm[key] = data[key] - } else { - // $FlowIgnore : Problem with possibly null this.vm.$options - this.vm.$options.propsData[key] = data[key] - // $FlowIgnore : Problem with possibly null this.vm - this.vm[key] = data[key] - // $FlowIgnore : Need to call this twice to fix watcher bug in 2.0.x - this.vm[key] = data[key] + if (this.vm && this.vm.$parent) { + this.vm.$parent.vueTestUtils_childProps[key] = data[key] } }) // $FlowIgnore : Problem with possibly null this.vm this.vm.$forceUpdate() - Vue.config.silent = originalConfig + // We need to explicitly trigger parent watcher to support sync scenarios + // $FlowIgnore : Problem with possibly null this.vm + this.vm.$parent._watcher.run() } /**