From 1920537e6097af097bf842480df723b28b9ae645 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sun, 5 Aug 2018 17:52:37 +0100 Subject: [PATCH 01/15] refactor: extract shared logic from create-stubs --- packages/create-instance/add-stubs.js | 4 +- packages/create-instance/create-instance.js | 19 +-- packages/shared/compile-template.js | 24 ++++ ...omponents.js => create-component-stubs.js} | 127 ++++++++---------- packages/shared/index.js | 2 +- packages/test-utils/src/shallow-mount.js | 23 ++-- test/specs/shallow-mount.spec.js | 10 -- 7 files changed, 102 insertions(+), 107 deletions(-) rename packages/shared/{stub-components.js => create-component-stubs.js} (71%) diff --git a/packages/create-instance/add-stubs.js b/packages/create-instance/add-stubs.js index 07c67057d..e34a6a8cf 100644 --- a/packages/create-instance/add-stubs.js +++ b/packages/create-instance/add-stubs.js @@ -1,7 +1,7 @@ -import { createComponentStubs } from 'shared/stub-components' +import { createStubsFromStubsObject } from 'shared/create-component-stubs' export function addStubs (component, stubs, _Vue) { - const stubComponents = createComponentStubs( + const stubComponents = createStubsFromStubsObject( component.components, stubs ) diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 52d7c361f..fce5add25 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -5,7 +5,10 @@ import addMocks from './add-mocks' import { addEventLogger } from './log-events' import { addStubs } from './add-stubs' import { throwError, vueVersion } from 'shared/util' -import { compileTemplate } from 'shared/compile-template' +import { + compileTemplate, + compileTemplateForSlots +} from 'shared/compile-template' import { isRequiredComponent } from 'shared/validators' import extractInstanceOptions from './extract-instance-options' import createFunctionalComponent from './create-functional-component' @@ -14,17 +17,6 @@ import { validateSlots } from './validate-slots' import createScopedSlots from './create-scoped-slots' import { extendExtendedComponents } from './extend-extended-components' -function compileTemplateForSlots (slots: Object): void { - Object.keys(slots).forEach(key => { - const slot = Array.isArray(slots[key]) ? slots[key] : [slots[key]] - slot.forEach(slotValue => { - if (componentNeedsCompiling(slotValue)) { - compileTemplate(slotValue) - } - }) - }) -} - export default function createInstance ( component: Component, options: Options, @@ -92,6 +84,9 @@ export default function createInstance ( if (options.slots) { compileTemplateForSlots(options.slots) + // validate slots outside of the createSlots function so + // that we can throw an error without it being caught by + // the Vue error handler // $FlowIgnore validateSlots(options.slots) } diff --git a/packages/shared/compile-template.js b/packages/shared/compile-template.js index c19078238..9effafbe0 100644 --- a/packages/shared/compile-template.js +++ b/packages/shared/compile-template.js @@ -1,6 +1,19 @@ // @flow import { compileToFunctions } from 'vue-template-compiler' +import { componentNeedsCompiling } from './validators' +import { throwError } from './util' + +export function compileFromString (str: string) { + if (!compileToFunctions) { + throwError( + `vueTemplateCompiler is undefined, you must pass ` + + `precompiled components if vue-template-compiler is ` + + `undefined` + ) + } + return compileToFunctions(str) +} export function compileTemplate (component: Component): void { if (component.template) { @@ -24,3 +37,14 @@ export function compileTemplate (component: Component): void { compileTemplate(component.options) } } + +export function compileTemplateForSlots (slots: Object): void { + Object.keys(slots).forEach(key => { + const slot = Array.isArray(slots[key]) ? slots[key] : [slots[key]] + slot.forEach(slotValue => { + if (componentNeedsCompiling(slotValue)) { + compileTemplate(slotValue) + } + }) + }) +} diff --git a/packages/shared/stub-components.js b/packages/shared/create-component-stubs.js similarity index 71% rename from packages/shared/stub-components.js rename to packages/shared/create-component-stubs.js index b203971b3..e1fe7cbb5 100644 --- a/packages/shared/stub-components.js +++ b/packages/shared/create-component-stubs.js @@ -1,7 +1,6 @@ // @flow import Vue from 'vue' -import { compileToFunctions } from 'vue-template-compiler' import { throwError, camelize, @@ -14,18 +13,10 @@ import { isVueComponent, isRequiredComponent } from './validators' -import { compileTemplate } from './compile-template' - -function compileFromString (str) { - if (!compileToFunctions) { - throwError( - `vueTemplateCompiler is undefined, you must pass ` + - `precompiled components if vue-template-compiler is ` + - `undefined` - ) - } - return compileToFunctions(str) -} +import { + compileTemplate, + compileFromString +} from './compile-template' function isVueComponentStub (comp): boolean { return comp && comp.template || isVueComponent(comp) @@ -33,8 +24,8 @@ function isVueComponentStub (comp): boolean { function isValidStub (stub: any): boolean { return ( + typeof stub === 'boolean' || (!!stub && typeof stub === 'string') || - stub === true || isVueComponentStub(stub) ) } @@ -69,7 +60,7 @@ function getCoreProperties (componentOptions: Component): Object { function createStubFromString ( templateString: string, - originalComponent: Component, + originalComponent: Component = {}, name: string ): Component { if (templateContainsComponent(templateString, name)) { @@ -93,7 +84,7 @@ function createClassString (staticClass, dynamicClass) { return staticClass || dynamicClass } -export function createBlankStub ( +export function createStubFromComponent ( originalComponent: Component, name: string ): Component { @@ -129,81 +120,76 @@ export function createBlankStub ( } } } - -export function createComponentStubs ( +function validateStub (stub) { + if (!isValidStub(stub)) { + throwError( + `options.stub values must be passed a string or ` + + `component` + ) + } +} +// Takes an originalComponents object and a stubs options +// returns stubs +export function createStubsFromStubsObject ( originalComponents: Object = {}, stubs: Object ): Components { - const components = {} - if (!stubs) { - return components - } - Object.keys(stubs).forEach(stubName => { + return Object.keys(stubs || {}).reduce((acc, stubName) => { const stub = stubs[stubName] - if (stub === false) { - return - } - if (!isValidStub(stub)) { - throwError( - `options.stub values must be passed a string or ` + `component` - ) + validateStub(stub) + + if (stub === false) { + return acc } if (stub === true) { const component = resolveComponent(originalComponents, stubName) - components[stubName] = createBlankStub(component, stubName) - return - } - - if (typeof stub !== 'string' && componentNeedsCompiling(stub)) { - compileTemplate(stub) + acc[stubName] = createStubFromComponent(component, stubName) + return acc } if (originalComponents[stubName]) { // Remove cached constructor delete originalComponents[stubName]._Ctor - if (typeof stub === 'string') { - components[stubName] = createStubFromString( - stub, - originalComponents[stubName], - stubName - ) - } else { - const stubObject = (stub: Object) - components[stubName] = { - ...stubObject, - name: originalComponents[stubName].name - } - } - } else { - if (typeof stub === 'string') { - components[stubName] = { - ...compileFromString(stub) - } - } else { - const stubObject = (stub: Object) - components[stubName] = { - ...stubObject - } - } } - }) - return components + + if (typeof stub === 'string') { + acc[stubName] = createStubFromString( + stub, + originalComponents[stubName], + stubName + ) + return acc + } + + if (componentNeedsCompiling(stub)) { + compileTemplate(stub) + } + const name = originalComponents[stubName] && + originalComponents[stubName].name + + acc[stubName] = { + name, + ...stub + } + + return acc + }, {}) } function stubComponents ( components: Components, stubbedComponents: Components ): void { - Object.keys(components).forEach(component => { + for (const component in components) { const cmp = components[component] const componentOptions = typeof cmp === 'function' ? cmp.extendOptions : cmp if (!componentOptions) { - stubbedComponents[component] = createBlankStub({}, component) + stubbedComponents[component] = createStubFromComponent({}, component) return } // Remove cached constructor @@ -211,11 +197,16 @@ function stubComponents ( if (!componentOptions.name) { componentOptions.name = component } - stubbedComponents[component] = createBlankStub(componentOptions, component) - }) + stubbedComponents[component] = createStubFromComponent( + componentOptions, + component + ) + } } -export function createComponentStubsForAll (component: Component): Components { +export function createStubsForComponent ( + component: Component +): Components { const stubbedComponents = {} if (component.components) { @@ -251,7 +242,7 @@ export function createComponentStubsForGlobals ( if (isRequiredComponent(c)) { continue } - components[c] = createBlankStub(instance.options.components[c], c) + components[c] = createStubFromComponent(instance.options.components[c], c) delete instance.options.components[c]._Ctor delete components[c]._Ctor } diff --git a/packages/shared/index.js b/packages/shared/index.js index 43f7c5e21..4e2443b21 100644 --- a/packages/shared/index.js +++ b/packages/shared/index.js @@ -1,4 +1,4 @@ export * from './compile-template' -export * from './stub-components' +export * from './create-component-stubs' export * from './util' export * from './validators' diff --git a/packages/test-utils/src/shallow-mount.js b/packages/test-utils/src/shallow-mount.js index c78fb832f..866b5b818 100644 --- a/packages/test-utils/src/shallow-mount.js +++ b/packages/test-utils/src/shallow-mount.js @@ -5,12 +5,11 @@ import Vue from 'vue' import mount from './mount' import type VueWrapper from './vue-wrapper' import { - createComponentStubsForAll, + createStubsForComponent, createComponentStubsForGlobals, - createBlankStub -} from 'shared/stub-components' -import { camelize, capitalize, hyphenate } from 'shared/util' -import { normalizeStubs } from 'shared/normalize' + createStubFromComponent +} from '../../shared/create-component-stubs' +import { normalizeStubs } from '../../shared/normalize' export default function shallowMount ( component: Component, @@ -18,13 +17,6 @@ export default function shallowMount ( ): VueWrapper { const _Vue = options.localVue || Vue - // remove any recursive components added to the constructor - // in vm._init from previous tests - if (component.name && component.components) { - delete component.components[capitalize(camelize(component.name))] - delete component.components[hyphenate(component.name)] - } - options.stubs = normalizeStubs(options.stubs) // In Vue.extends, Vue adds a recursive component to the options @@ -32,14 +24,17 @@ export default function shallowMount ( // $FlowIgnore if (!options.stubs[component.name]) { // $FlowIgnore - options.stubs[component.name] = createBlankStub(component, component.name) + options.stubs[component.name] = createStubFromComponent( + component, + component.name + ) } return mount(component, { ...options, components: { ...createComponentStubsForGlobals(_Vue), - ...createComponentStubsForAll(component) + ...createStubsForComponent(component) } }) } diff --git a/test/specs/shallow-mount.spec.js b/test/specs/shallow-mount.spec.js index 4f6ca15c9..8157f7260 100644 --- a/test/specs/shallow-mount.spec.js +++ b/test/specs/shallow-mount.spec.js @@ -351,16 +351,6 @@ describeRunIf(process.env.TEST_ENV !== 'node', 'shallowMount', () => { } }).findAll(RecursiveComponent).length ).to.equal(3) - RecursiveComponent.components = { - 'recursive-component': { render: h => h('div') } - } - expect( - shallowMount(RecursiveComponent, { - propsData: { - items: ['', ''] - } - }).findAll(RecursiveComponent).length - ).to.equal(3) }) it('handles extended stubs', () => { From fe0941a1dbfc2f20c9bfc80c8f1ecf6b5d670317 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sun, 5 Aug 2018 18:07:43 +0100 Subject: [PATCH 02/15] refactor: remove createStubsForGlobals function --- packages/shared/create-component-stubs.js | 68 ++++++++++------------- packages/test-utils/src/shallow-mount.js | 5 +- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/packages/shared/create-component-stubs.js b/packages/shared/create-component-stubs.js index e1fe7cbb5..966c9b15e 100644 --- a/packages/shared/create-component-stubs.js +++ b/packages/shared/create-component-stubs.js @@ -58,25 +58,6 @@ function getCoreProperties (componentOptions: Component): Object { } } -function createStubFromString ( - templateString: string, - originalComponent: Component = {}, - name: string -): Component { - if (templateContainsComponent(templateString, name)) { - throwError('options.stub cannot contain a circular reference') - } - - const componentOptions = typeof originalComponent === 'function' - ? originalComponent.extendOptions - : originalComponent - - return { - ...getCoreProperties(componentOptions), - ...compileFromString(templateString) - } -} - function createClassString (staticClass, dynamicClass) { if (staticClass && dynamicClass) { return staticClass + ' ' + dynamicClass @@ -120,6 +101,26 @@ export function createStubFromComponent ( } } } + +function createStubFromString ( + templateString: string, + originalComponent: Component = {}, + name: string +): Component { + if (templateContainsComponent(templateString, name)) { + throwError('options.stub cannot contain a circular reference') + } + + const componentOptions = typeof originalComponent === 'function' + ? originalComponent.extendOptions + : originalComponent + + return { + ...getCoreProperties(componentOptions), + ...compileFromString(templateString) + } +} + function validateStub (stub) { if (!isValidStub(stub)) { throwError( @@ -128,8 +129,7 @@ function validateStub (stub) { ) } } -// Takes an originalComponents object and a stubs options -// returns stubs + export function createStubsFromStubsObject ( originalComponents: Object = {}, stubs: Object @@ -189,7 +189,10 @@ function stubComponents ( : cmp if (!componentOptions) { - stubbedComponents[component] = createStubFromComponent({}, component) + stubbedComponents[component] = createStubFromComponent( + {}, + component + ) return } // Remove cached constructor @@ -209,13 +212,15 @@ export function createStubsForComponent ( ): Components { const stubbedComponents = {} + if (component.options) { + stubComponents(component.options.components, stubbedComponents) + } + if (component.components) { stubComponents(component.components, stubbedComponents) } let extended = component.extends - - // Loop through extended component chains to stub all child components while (extended) { if (extended.components) { stubComponents(extended.components, stubbedComponents) @@ -233,18 +238,3 @@ export function createStubsForComponent ( return stubbedComponents } - -export function createComponentStubsForGlobals ( - instance: Component -): Components { - const components = {} - for (const c in instance.options.components) { - if (isRequiredComponent(c)) { - continue - } - components[c] = createStubFromComponent(instance.options.components[c], c) - delete instance.options.components[c]._Ctor - delete components[c]._Ctor - } - return components -} diff --git a/packages/test-utils/src/shallow-mount.js b/packages/test-utils/src/shallow-mount.js index 866b5b818..c96c7600c 100644 --- a/packages/test-utils/src/shallow-mount.js +++ b/packages/test-utils/src/shallow-mount.js @@ -6,7 +6,6 @@ import mount from './mount' import type VueWrapper from './vue-wrapper' import { createStubsForComponent, - createComponentStubsForGlobals, createStubFromComponent } from '../../shared/create-component-stubs' import { normalizeStubs } from '../../shared/normalize' @@ -19,7 +18,7 @@ export default function shallowMount ( options.stubs = normalizeStubs(options.stubs) - // In Vue.extends, Vue adds a recursive component to the options + // Vue registers a recursive component on the original options // This stub will override the component added by Vue // $FlowIgnore if (!options.stubs[component.name]) { @@ -33,7 +32,7 @@ export default function shallowMount ( return mount(component, { ...options, components: { - ...createComponentStubsForGlobals(_Vue), + ...createStubsForComponent(_Vue), ...createStubsForComponent(component) } }) From 531a357ca2e8992f65a2ef7f19043aa07a1480d9 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sun, 5 Aug 2018 18:46:07 +0100 Subject: [PATCH 03/15] refactor: clean formatting --- packages/shared/create-component-stubs.js | 3 +-- packages/test-utils/src/index.js | 2 +- packages/test-utils/src/warn-if-no-window.js | 12 ++++-------- packages/test-utils/src/wrapper.js | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/shared/create-component-stubs.js b/packages/shared/create-component-stubs.js index 966c9b15e..48513627a 100644 --- a/packages/shared/create-component-stubs.js +++ b/packages/shared/create-component-stubs.js @@ -10,8 +10,7 @@ import { import { componentNeedsCompiling, templateContainsComponent, - isVueComponent, - isRequiredComponent + isVueComponent } from './validators' import { compileTemplate, diff --git a/packages/test-utils/src/index.js b/packages/test-utils/src/index.js index fabf0cbd7..086677cf8 100644 --- a/packages/test-utils/src/index.js +++ b/packages/test-utils/src/index.js @@ -13,7 +13,7 @@ import { warn } from 'shared/util' function shallow (component, options) { warn( `shallow has been renamed to shallowMount. shallow ` + - `will be removed in 1.0.0, use shallowMount instead` + `will be removed in 1.0.0, use shallowMount instead` ) return shallowMount(component, options) } diff --git a/packages/test-utils/src/warn-if-no-window.js b/packages/test-utils/src/warn-if-no-window.js index d325b4601..efea8002d 100644 --- a/packages/test-utils/src/warn-if-no-window.js +++ b/packages/test-utils/src/warn-if-no-window.js @@ -6,14 +6,10 @@ export default function warnIfNoWindow (): void { if (typeof window === 'undefined') { throwError( `window is undefined, vue-test-utils needs to be ` + - `run in a browser environment. -` + - (`You can run the tests in node using jsdom + ` + - `jsdom-global. -`) + - (`See ` + - `https://vue-test-utils.vuejs.org/guides/common-tips.html ` + - `for more details.`) + `run in a browser environment. \n` + + `You can run the tests in node using jsdom \n` + + `See https://vue-test-utils.vuejs.org/guides/common-tips.html ` + + `for more details.` ) } } diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 093d6195c..f09b2e000 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -905,7 +905,7 @@ export default class Wrapper implements BaseWrapper { update (): void { warn( `update has been removed from vue-test-utils. All ` + - `updates are now synchronous by default` + `updates are now synchronous by default` ) } } From c8e174061d269dfccc0b83bb85a4746b3b7ddc97 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sun, 5 Aug 2018 20:04:47 +0100 Subject: [PATCH 04/15] refactor: add matches function --- packages/test-utils/src/find.js | 4 +- packages/test-utils/src/matches.js | 69 ++++++++++++++++++++++++++++++ packages/test-utils/src/wrapper.js | 64 ++++++++------------------- 3 files changed, 89 insertions(+), 48 deletions(-) create mode 100644 packages/test-utils/src/matches.js diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 1217343d4..0a11ec225 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -19,8 +19,8 @@ export default function find ( if (!vnode && !vm && selectorType !== DOM_SELECTOR) { throwError( `cannot find a Vue instance on a DOM node. The node ` + - `you are calling find on does not exist in the ` + - `VDom. Are you adding the node as innerHTML?` + `you are calling find on does not exist in the ` + + `VDom. Are you adding the node as innerHTML?` ) } diff --git a/packages/test-utils/src/matches.js b/packages/test-utils/src/matches.js new file mode 100644 index 000000000..23cb44016 --- /dev/null +++ b/packages/test-utils/src/matches.js @@ -0,0 +1,69 @@ +// @flow + +import { + DOM_SELECTOR, + NAME_SELECTOR, + COMPONENT_SELECTOR, + FUNCTIONAL_OPTIONS +} from './consts' +import { vmCtorMatchesSelector } from './find-vue-components'; + +export function vmMatchesName (vm: Component, name: string): boolean { + return !!( + name && ( + (vm._vnode && + vm._vnode.functionalOptions && + vm._vnode.functionalOptions.name === name) || + (vm.$options && vm.$options.name === name) || + (vm.options && vm.options.name === name) + )) +} + +function vmCtorMatches(vm, component) { + const componentOptions = typeof component === 'function' + ? component.options + : component + const isFunctional = componentOptions.functional + const Ctor = componentOptions._Ctor + + if (!Ctor) { + return false + } + debugger + const constructor = vm.constructor + return Object.keys(Ctor).some(c => { + return isFunctional + ? Ctor[c] === vm._Ctor[c] + : Ctor[c] === constructor || Ctor[c] === constructor.super + }) +} + +export function matches(node, selectorType, selector) { + debugger + if(selectorType === DOM_SELECTOR) { + const element = node instanceof Element + ? node + : node.elm + return element && element.matches(selector) + } + + const nameSelector = + typeof selector === 'function' ? selector.extendOptions.name : selector.name + + if(selectorType === NAME_SELECTOR) { + const componentInstance = node && node.parent && node.parent.componentInstance + if(!componentInstance) { + return false + } + return vmMatchesName(componentInstance, nameSelector) + } + + if(selectorType === COMPONENT_SELECTOR) { + debugger + const componentInstance = selector.functional + ? node && node[FUNCTIONAL_OPTIONS] + : node && node.parent && node.parent.componentInstance + return vmCtorMatches(componentInstance, selector) || + vmMatchesName(componentInstance, nameSelector) + } +} diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index f09b2e000..5c7b45511 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -21,6 +21,7 @@ import findAll from './find' import createWrapper from './create-wrapper' import { orderWatchers } from './order-watchers' import { recursivelySetData } from './recursively-set-data' +import { matches } from './matches' export default class Wrapper implements BaseWrapper { +vnode: VNode | null; @@ -100,17 +101,15 @@ export default class Wrapper implements BaseWrapper { let classes = className ? className.split(' ') : [] // Handle converting cssmodules identifiers back to the original class name if (this.vm && this.vm.$style) { - const cssModuleIdentifiers = {} - let moduleIdent - Object.keys(this.vm.$style).forEach(key => { - moduleIdent = this.vm && this.vm.$style[key] - // CSS Modules may be multi-class if they extend others. - // Extended classes should be already present in $style. - if (moduleIdent) { - moduleIdent = moduleIdent.split(' ')[0] - cssModuleIdentifiers[moduleIdent] = key - } - }) + const cssModuleIdentifiers = Object.keys(this.vm.$style) + .reduce((acc, key) => { + // $FlowIgnore + const moduleIdent = this.vm.$style[key] + if (moduleIdent) { + acc[moduleIdent.split(' ')[0]] = key + } + return acc + }, {}) classes = classes.map( className => cssModuleIdentifiers[className] || className ) @@ -122,10 +121,8 @@ export default class Wrapper implements BaseWrapper { * Checks if wrapper contains provided selector. */ contains (selector: Selector): boolean { - const selectorType = getSelectorTypeOrThrow(selector, 'contains') const nodes = findAll(this.vm, this.vnode, this.element, selector) - const is = selectorType === REF_SELECTOR ? false : this.is(selector) - return nodes.length > 0 || is + return nodes.length > 0 } /** @@ -176,7 +173,7 @@ export default class Wrapper implements BaseWrapper { visible (): boolean { warn( `visible has been deprecated and will be removed in ` + - `version 1, use isVisible instead` + `version 1, use isVisible instead` ) let element = this.element while (element) { @@ -331,8 +328,8 @@ export default class Wrapper implements BaseWrapper { * matches the provided selector. */ find (selector: Selector): Wrapper | ErrorWrapper { - const nodes = findAll(this.vm, this.vnode, this.element, selector) - if (nodes.length === 0) { + const node = findAll(this.vm, this.vnode, this.element, selector)[0] + if (!node) { if (selector.ref) { return new ErrorWrapper(`ref="${selector.ref}"`) } @@ -342,10 +339,10 @@ export default class Wrapper implements BaseWrapper { } // Using CSS Selector, returns a VueWrapper instance if the root element // binds a Vue instance. - if (nodes[0].elm === this.element) { + if (node.elm === this.element) { return this } - return createWrapper(nodes[0], this.options) + return createWrapper(node, this.options) } /** @@ -353,7 +350,6 @@ export default class Wrapper implements BaseWrapper { * the provided selector. */ findAll (selector: Selector): WrapperArray { - getSelectorTypeOrThrow(selector, 'findAll') const nodes = findAll(this.vm, this.vnode, this.element, selector) const wrappers = nodes.map(node => { // Using CSS Selector, returns a VueWrapper instance if the root element @@ -378,35 +374,11 @@ export default class Wrapper implements BaseWrapper { is (selector: Selector): boolean { const selectorType = getSelectorTypeOrThrow(selector, 'is') - if (selectorType === NAME_SELECTOR) { - if (!this.vm) { - return false - } - return vmCtorMatchesName(this.vm, selector.name) - } - - if (selectorType === COMPONENT_SELECTOR) { - if (!this.vm) { - return false - } - if (selector.functional) { - return vmFunctionalCtorMatchesSelector(this.vm._vnode, selector._Ctor) - } - return vmCtorMatchesSelector(this.vm, selector) - } - if (selectorType === REF_SELECTOR) { throwError('$ref selectors can not be used with wrapper.is()') } - if (typeof selector === 'object') { - return false - } - - return !!( - this.element.getAttribute && - this.element.matches(selector) - ) + return matches(this.vnode || this.element, selectorType, selector) } /** @@ -483,7 +455,7 @@ export default class Wrapper implements BaseWrapper { const keys = this.vm && this.vm.$options._propKeys if (keys) { - keys.forEach(key => { + (keys || {}).forEach(key => { if (this.vm) { props[key] = this.vm[key] } From 696d6fbd9f9d95e4275cb9e276dfad658d706b40 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Mon, 6 Aug 2018 21:47:33 +0100 Subject: [PATCH 05/15] refactor: use one find function --- packages/test-utils/src/consts.js | 1 + packages/test-utils/src/create-wrapper.js | 2 +- packages/test-utils/src/find-vnodes.js | 73 ---- .../test-utils/src/find-vue-components.js | 123 ------ packages/test-utils/src/find.js | 86 ++-- .../{get-selector-type.js => get-selector.js} | 35 +- packages/test-utils/src/matches.js | 60 ++- packages/test-utils/src/wrapper.js | 378 +++++++++--------- 8 files changed, 305 insertions(+), 453 deletions(-) delete mode 100644 packages/test-utils/src/find-vnodes.js delete mode 100644 packages/test-utils/src/find-vue-components.js rename packages/test-utils/src/{get-selector-type.js => get-selector.js} (51%) diff --git a/packages/test-utils/src/consts.js b/packages/test-utils/src/consts.js index 276c020f8..fa4c1e01a 100644 --- a/packages/test-utils/src/consts.js +++ b/packages/test-utils/src/consts.js @@ -4,6 +4,7 @@ export const NAME_SELECTOR = 'NAME_SELECTOR' export const COMPONENT_SELECTOR = 'COMPONENT_SELECTOR' export const REF_SELECTOR = 'REF_SELECTOR' export const DOM_SELECTOR = 'DOM_SELECTOR' +export const INVALID_SELECTOR = 'INVALID_SELECTOR' export const VUE_VERSION = Number( `${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}` ) diff --git a/packages/test-utils/src/create-wrapper.js b/packages/test-utils/src/create-wrapper.js index 9a420fedc..6ae1b9b81 100644 --- a/packages/test-utils/src/create-wrapper.js +++ b/packages/test-utils/src/create-wrapper.js @@ -8,7 +8,7 @@ export default function createWrapper ( node: VNode | Component, options: WrapperOptions = {} ): VueWrapper | Wrapper { - const componentInstance = node.componentInstance || node.child + const componentInstance = node.componentInstance if (componentInstance) { return new VueWrapper(componentInstance, options) } diff --git a/packages/test-utils/src/find-vnodes.js b/packages/test-utils/src/find-vnodes.js deleted file mode 100644 index 58da14867..000000000 --- a/packages/test-utils/src/find-vnodes.js +++ /dev/null @@ -1,73 +0,0 @@ -// @flow - -import { REF_SELECTOR } from './consts' -import { throwError } from 'shared/util' - -function findAllVNodes (vnode: VNode, nodes: Array = []): Array { - nodes.push(vnode) - - if (Array.isArray(vnode.children)) { - vnode.children.forEach(childVNode => { - findAllVNodes(childVNode, nodes) - }) - } - - if (vnode.child) { - findAllVNodes(vnode.child._vnode, nodes) - } - - return nodes -} - -function removeDuplicateNodes (vNodes: Array): Array { - const vNodeElms = vNodes.map(vNode => vNode.elm) - return vNodes.filter( - (vNode, index) => index === vNodeElms.indexOf(vNode.elm) - ) -} - -function nodeMatchesRef (node: VNode, refName: string): boolean { - return node.data && node.data.ref === refName -} - -function findVNodesByRef (vNode: VNode, refName: string): Array { - const nodes = findAllVNodes(vNode) - const refFilteredNodes = nodes.filter(node => nodeMatchesRef(node, refName)) - // Only return refs defined on top-level VNode to provide the same - // behavior as selecting via vm.$ref.{someRefName} - const mainVNodeFilteredNodes = refFilteredNodes.filter( - node => !!vNode.context.$refs[node.data.ref] - ) - return removeDuplicateNodes(mainVNodeFilteredNodes) -} - -function nodeMatchesSelector (node: VNode, selector: string): boolean { - return node.elm && node.elm.getAttribute && node.elm.matches(selector) -} - -function findVNodesBySelector (vNode: VNode, selector: string): Array { - const nodes = findAllVNodes(vNode) - const filteredNodes = nodes.filter(node => - nodeMatchesSelector(node, selector) - ) - return removeDuplicateNodes(filteredNodes) -} - -export default function findVnodes ( - vnode: VNode, - vm: Component | void, - selectorType: ?string, - selector: Object | string -): Array { - if (selectorType === REF_SELECTOR) { - if (!vm) { - throwError( - `$ref selectors can only be used on Vue component ` + `wrappers` - ) - } - // $FlowIgnore - return findVNodesByRef(vnode, selector.ref) - } - // $FlowIgnore - return findVNodesBySelector(vnode, selector) -} diff --git a/packages/test-utils/src/find-vue-components.js b/packages/test-utils/src/find-vue-components.js deleted file mode 100644 index d9858c49b..000000000 --- a/packages/test-utils/src/find-vue-components.js +++ /dev/null @@ -1,123 +0,0 @@ -// @flow -import { FUNCTIONAL_OPTIONS, VUE_VERSION } from './consts' -import { throwError } from 'shared/util' - -export function findAllVueComponentsFromVm ( - vm: Component, - components: Array = [] -): Array { - components.push(vm) - vm.$children.forEach(child => { - findAllVueComponentsFromVm(child, components) - }) - - return components -} - -function findAllVueComponentsFromVnode ( - vnode: Component, - components: Array = [] -): Array { - if (vnode.child) { - components.push(vnode.child) - } - if (vnode.children) { - vnode.children.forEach(child => { - findAllVueComponentsFromVnode(child, components) - }) - } - - return components -} - -function findAllFunctionalComponentsFromVnode ( - vnode: Component, - components: Array = [] -): Array { - if (vnode[FUNCTIONAL_OPTIONS] || vnode.functionalContext) { - components.push(vnode) - } - if (vnode.children) { - vnode.children.forEach(child => { - findAllFunctionalComponentsFromVnode(child, components) - }) - } - return components -} - -export function vmCtorMatchesName (vm: Component, name: string): boolean { - return !!( - name && ( - (vm._vnode && - vm._vnode.functionalOptions && - vm._vnode.functionalOptions.name === name) || - (vm.$options && vm.$options.name === name) || - (vm.options && vm.options.name === name) - )) -} - -export function vmCtorMatchesSelector ( - component: Component, - selector: Object -): boolean { - const Ctor = selector._Ctor || (selector.options && selector.options._Ctor) - if (!Ctor) { - return false - } - const constructor = component.__proto__.constructor - return Object.keys(Ctor || {}).some(c => { - return Ctor[c] === constructor || Ctor[c] === constructor.super - }) -} - -export function vmFunctionalCtorMatchesSelector ( - component: VNode, - Ctor: Object -): boolean { - if (VUE_VERSION < 2.3) { - throwError( - `find for functional components is not supported in ` + `Vue < 2.3` - ) - } - - if (!Ctor) { - return false - } - - if (!component[FUNCTIONAL_OPTIONS]) { - return false - } - const Ctors = Object.keys(component[FUNCTIONAL_OPTIONS]._Ctor) - return Ctors.some(c => Ctor[c] === component[FUNCTIONAL_OPTIONS]._Ctor[c]) -} - -export default function findVueComponents ( - root: Component, - selectorType: ?string, - selector: Object -): Array { - if (selector.functional) { - const nodes = root._vnode - ? findAllFunctionalComponentsFromVnode(root._vnode) - : findAllFunctionalComponentsFromVnode(root) - return nodes.filter( - node => - vmFunctionalCtorMatchesSelector(node, selector._Ctor) || - node[FUNCTIONAL_OPTIONS].name === selector.name - ) - } - const nameSelector = - typeof selector === 'function' ? selector.extendOptions.name : selector.name - const components = root._isVue - ? findAllVueComponentsFromVm(root) - : findAllVueComponentsFromVnode(root) - return components.filter(component => { - if (!component.$vnode && !component.$options.extends) { - return false - } - return ( - vmCtorMatchesSelector(component, selector) || - vmCtorMatchesName(component, nameSelector) - ) - }) -} diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 0a11ec225..1e74545f7 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -1,22 +1,60 @@ // @flow -import findVnodes from './find-vnodes' -import findVueComponents from './find-vue-components' import findDOMNodes from './find-dom-nodes' -import { COMPONENT_SELECTOR, NAME_SELECTOR, DOM_SELECTOR } from './consts' +import { + DOM_SELECTOR, + REF_SELECTOR +} from './consts' import Vue from 'vue' -import getSelectorTypeOrThrow from './get-selector-type' import { throwError } from 'shared/util' +import { matches } from './matches' -export default function find ( - vm: Component | void, +function findAllVNodes ( + vnode: VNode, + nodes: Array = [], + selector: any +): Array { + if (matches(vnode, selector)) { + nodes.push(vnode) + } + + if (Array.isArray(vnode.children)) { + vnode.children.forEach(v => { + findAllVNodes(v, nodes, selector) + }) + } + if (vnode.child) { + findAllVNodes(vnode.child._vnode, nodes, selector) + } + return nodes +} + +function removeDuplicateNodes (vNodes: Array): Array { + const vNodeElms = vNodes.map(vNode => vNode.elm) + return vNodes.filter( + (vNode, index) => index === vNodeElms.indexOf(vNode.elm) + ) +} + +export default function findAll ( vnode: VNode | null, element: Element, + vm: Component, selector: Selector ): Array { - const selectorType = getSelectorTypeOrThrow(selector, 'find') + if (!vnode && selector.type !== DOM_SELECTOR) { + throwError( + `cannot find a Vue instance on a DOM node. The node ` + + `you are calling find on does not exist in the ` + + `VDom. Are you adding the node as innerHTML?` + ) + } - if (!vnode && !vm && selectorType !== DOM_SELECTOR) { + if (!vnode) { + return findDOMNodes(element, selector.value) + } + + if (!vnode && selector.type !== DOM_SELECTOR) { throwError( `cannot find a Vue instance on a DOM node. The node ` + `you are calling find on does not exist in the ` + @@ -24,30 +62,28 @@ export default function find ( ) } - if (selectorType === COMPONENT_SELECTOR || selectorType === NAME_SELECTOR) { - const root = vm || vnode - if (!root) { - return [] - } - return findVueComponents(root, selectorType, selector) + if (!vm && selector.type === REF_SELECTOR) { + throwError( + `$ref selectors can only be used on Vue component ` + `wrappers` + ) } if ( vm && vm.$refs && - selector.ref in vm.$refs && - vm.$refs[selector.ref] instanceof Vue + selector.value.ref in vm.$refs ) { - return [vm.$refs[selector.ref]] + const refs = vm.$refs[selector.value.ref] + return Array.isArray(refs) ? refs : [refs] } + const rootVnode = vm && vm.$vnode + ? vm.$vnode + : vm && vm._vnode + const nodes = findAllVNodes(vm ? rootVnode : vnode, [], selector) + const dedupedNodes = removeDuplicateNodes(nodes) - if (vnode) { - const nodes = findVnodes(vnode, vm, selectorType, selector) - if (selectorType !== DOM_SELECTOR) { - return nodes - } - return nodes.length > 0 ? nodes : findDOMNodes(element, selector) + if (nodes.length > 0 || selector.type !== DOM_SELECTOR) { + return dedupedNodes } - - return findDOMNodes(element, selector) + return findDOMNodes(element, selector.value) } diff --git a/packages/test-utils/src/get-selector-type.js b/packages/test-utils/src/get-selector.js similarity index 51% rename from packages/test-utils/src/get-selector-type.js rename to packages/test-utils/src/get-selector.js index a4b25473c..bb41a7dd9 100644 --- a/packages/test-utils/src/get-selector-type.js +++ b/packages/test-utils/src/get-selector.js @@ -11,20 +11,37 @@ import { REF_SELECTOR, COMPONENT_SELECTOR, NAME_SELECTOR, - DOM_SELECTOR + DOM_SELECTOR, + INVALID_SELECTOR } from './consts' -export default function getSelectorTypeOrThrow ( - selector: Selector, - methodName: string -): string | void { +function getSelectorType ( + selector: Selector +): string { + const selectorObject = { + + } if (isDomSelector(selector)) return DOM_SELECTOR if (isNameSelector(selector)) return NAME_SELECTOR if (isVueComponent(selector)) return COMPONENT_SELECTOR if (isRefSelector(selector)) return REF_SELECTOR - throwError( - `wrapper.${methodName}() must be passed a valid CSS selector, ` + - `Vue constructor, or valid find option object` - ) + return INVALID_SELECTOR +} + +export default function getSelector ( + selector: Selector, + methodName: string +): Object { + const type = getSelectorType(selector) + if (type === INVALID_SELECTOR) { + throwError( + `wrapper.${methodName}() must be passed a valid CSS selector, Vue ` + + `constructor, or valid find option object` + ) + } + return { + type, + value: selector + } } diff --git a/packages/test-utils/src/matches.js b/packages/test-utils/src/matches.js index 23cb44016..821103ea6 100644 --- a/packages/test-utils/src/matches.js +++ b/packages/test-utils/src/matches.js @@ -6,64 +6,62 @@ import { COMPONENT_SELECTOR, FUNCTIONAL_OPTIONS } from './consts' -import { vmCtorMatchesSelector } from './find-vue-components'; export function vmMatchesName (vm: Component, name: string): boolean { return !!( name && ( - (vm._vnode && - vm._vnode.functionalOptions && - vm._vnode.functionalOptions.name === name) || + (vm && + vm.name === name) || (vm.$options && vm.$options.name === name) || (vm.options && vm.options.name === name) )) } -function vmCtorMatches(vm, component) { - const componentOptions = typeof component === 'function' - ? component.options - : component - const isFunctional = componentOptions.functional - const Ctor = componentOptions._Ctor +function vmCtorMatches (vm, component) { + // const componentOptions = typeof component === 'function' + // ? component.options + // : component + const isFunctional = component.functional + const Ctor = component._Ctor if (!Ctor) { return false } - debugger + const constructor = vm.constructor return Object.keys(Ctor).some(c => { return isFunctional - ? Ctor[c] === vm._Ctor[c] - : Ctor[c] === constructor || Ctor[c] === constructor.super + ? Ctor[c] === vm._Ctor[c] + : Ctor[c] === constructor || Ctor[c] === constructor.super }) } -export function matches(node, selectorType, selector) { - debugger - if(selectorType === DOM_SELECTOR) { +export function matches (node, selector) { + if (selector.type === DOM_SELECTOR) { const element = node instanceof Element - ? node - : node.elm - return element && element.matches(selector) + ? node + : node.elm + return element && element.matches && element.matches(selector.value) } const nameSelector = - typeof selector === 'function' ? selector.extendOptions.name : selector.name + typeof selector.value === 'function' + ? selector.value.extendOptions.name + : selector.value.name + const componentInstance = selector.value.functional + ? node && node[FUNCTIONAL_OPTIONS] + : node.componentInstance || node + + if (!componentInstance) { + return false + } - if(selectorType === NAME_SELECTOR) { - const componentInstance = node && node.parent && node.parent.componentInstance - if(!componentInstance) { - return false - } + if (selector.type === NAME_SELECTOR) { return vmMatchesName(componentInstance, nameSelector) } - if(selectorType === COMPONENT_SELECTOR) { - debugger - const componentInstance = selector.functional - ? node && node[FUNCTIONAL_OPTIONS] - : node && node.parent && node.parent.componentInstance - return vmCtorMatches(componentInstance, selector) || + if (selector.type === COMPONENT_SELECTOR) { + return vmCtorMatches(componentInstance, selector.value) || vmMatchesName(componentInstance, nameSelector) } } diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 5c7b45511..e9f7f6f6d 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -1,19 +1,12 @@ // @flow import Vue from 'vue' -import getSelectorTypeOrThrow from './get-selector-type' +import getSelector from './get-selector' import { REF_SELECTOR, - COMPONENT_SELECTOR, - NAME_SELECTOR, FUNCTIONAL_OPTIONS } from './consts' import config from './config' -import { - vmCtorMatchesName, - vmCtorMatchesSelector, - vmFunctionalCtorMatchesSelector -} from './find-vue-components' import WrapperArray from './wrapper-array' import ErrorWrapper from './error-wrapper' import { throwError, warn } from '../../shared/util' @@ -120,11 +113,27 @@ export default class Wrapper implements BaseWrapper { /** * Checks if wrapper contains provided selector. */ - contains (selector: Selector): boolean { - const nodes = findAll(this.vm, this.vnode, this.element, selector) + contains (rawSelector: Selector): boolean { + const selector = getSelector(rawSelector, 'contains') + const nodes = findAll(this.vnode, this.element, this.vm, selector) return nodes.length > 0 } + /** + * Calls destroy on vm + */ + destroy (): void { + if (!this.isVueInstance()) { + throwError(`wrapper.destroy() can only be called on a Vue instance`) + } + + if (this.element.parentNode) { + this.element.parentNode.removeChild(this.element) + } + // $FlowIgnore + this.vm.$destroy() + } + /** * Returns an object containing custom events emitted by the Wrapper vm */ @@ -167,27 +176,40 @@ export default class Wrapper implements BaseWrapper { } /** - * Utility to check wrapper is visible. Returns false if a parent - * element has display: none or visibility: hidden style. + * Finds first node in tree of the current wrapper that + * matches the provided selector. */ - visible (): boolean { - warn( - `visible has been deprecated and will be removed in ` + - `version 1, use isVisible instead` - ) - let element = this.element - while (element) { - if ( - element.style && - (element.style.visibility === 'hidden' || - element.style.display === 'none') - ) { - return false + find (rawSelector: Selector): Wrapper | ErrorWrapper { + const selector = getSelector(rawSelector, 'find') + const node = findAll(this.vnode, this.element, this.vm, selector)[0] + + if (!node) { + if (selector.type === REF_SELECTOR) { + return new ErrorWrapper(`ref="${selector.value.ref}"`) } - element = element.parentElement + return new ErrorWrapper( + typeof selector.value === 'string' + ? selector.value + : 'Component' + ) } - return true + return createWrapper(node, this.options) + } + + /** + * Finds node in tree of the current wrapper that matches + * the provided selector. + */ + findAll (rawSelector: Selector): WrapperArray { + const selector = getSelector(rawSelector, 'findAll') + const nodes = findAll(this.vnode, this.element, this.vm, selector) + const wrappers = nodes.map(node => { + // Using CSS Selector, returns a VueWrapper instance if the root element + // binds a Vue instance. + return createWrapper(node, this.options) + }) + return new WrapperArray(wrappers) } /** @@ -323,44 +345,6 @@ export default class Wrapper implements BaseWrapper { return !!(elStyle && mockNodeStyle && elStyle === mockNodeStyle) } - /** - * Finds first node in tree of the current wrapper that - * matches the provided selector. - */ - find (selector: Selector): Wrapper | ErrorWrapper { - const node = findAll(this.vm, this.vnode, this.element, selector)[0] - if (!node) { - if (selector.ref) { - return new ErrorWrapper(`ref="${selector.ref}"`) - } - return new ErrorWrapper( - typeof selector === 'string' ? selector : 'Component' - ) - } - // Using CSS Selector, returns a VueWrapper instance if the root element - // binds a Vue instance. - if (node.elm === this.element) { - return this - } - return createWrapper(node, this.options) - } - - /** - * Finds node in tree of the current wrapper that matches - * the provided selector. - */ - findAll (selector: Selector): WrapperArray { - const nodes = findAll(this.vm, this.vnode, this.element, selector) - const wrappers = nodes.map(node => { - // Using CSS Selector, returns a VueWrapper instance if the root element - // binds a Vue instance. - return node.elm === this.element - ? this - : createWrapper(node, this.options) - }) - return new WrapperArray(wrappers) - } - /** * Returns HTML of element as a string */ @@ -371,14 +355,17 @@ export default class Wrapper implements BaseWrapper { /** * Checks if node matches selector */ - is (selector: Selector): boolean { - const selectorType = getSelectorTypeOrThrow(selector, 'is') + is (rawSelector: Selector): boolean { + const selector = getSelector(rawSelector, 'is') - if (selectorType === REF_SELECTOR) { + if (selector.type === REF_SELECTOR) { throwError('$ref selectors can not be used with wrapper.is()') } - return matches(this.vnode || this.element, selectorType, selector) + const root = this.vm && !this.isFunctionalComponent + ? (this.vm.$vnode || this.vm) + : this.vnode + return matches(root || this.element, selector) } /** @@ -465,24 +452,100 @@ export default class Wrapper implements BaseWrapper { } /** - * Sets vm data + * Checks radio button or checkbox element */ - setData (data: Object): void { - if (this.isFunctionalComponent) { + setChecked (checked: boolean = true): void { + if (typeof checked !== 'boolean') { + throwError('wrapper.setChecked() must be passed a boolean') + } + const tagName = this.element.tagName + const type = this.attributes().type + + if (tagName === 'SELECT') { throwError( - `wrapper.setData() cannot be called on a functional ` + - `component` + `wrapper.setChecked() cannot be called on a ` + + ` ` + + `element.` + ) + } else { + // $FlowIgnore + if (!this.element.checked) { + this.trigger('click') + this.trigger('change') + } + } + } else if (tagName === 'INPUT' || tagName === 'TEXTAREA') { + throwError( + `wrapper.setChecked() cannot be called on "text" ` + + `inputs. Use wrapper.setValue() instead` + ) + } else { + throwError(`wrapper.setChecked() cannot be called on this element`) } + } - if (!this.vm) { + /** + * Selects element + */ + setSelected (): void { + const tagName = this.element.tagName + const type = this.attributes().type + + if (tagName === 'OPTION') { + // $FlowIgnore + this.element.selected = true + // $FlowIgnore + if (this.element.parentElement.tagName === 'OPTGROUP') { + // $FlowIgnore + createWrapper(this.element.parentElement.parentElement, this.options) + .trigger('change') + } else { + // $FlowIgnore + createWrapper(this.element.parentElement, this.options) + .trigger('change') + } + } else if (tagName === 'SELECT') { throwError( - `wrapper.setData() can only be called on a Vue ` + - `instance` + `wrapper.setSelected() cannot be called on select. ` + + `Call it on one of its options` + ) + } else if (tagName === 'INPUT' && type === 'checkbox') { + throwError( + `wrapper.setSelected() cannot be called on a element. Use ` + + `wrapper.setChecked() instead` + ) + } else if (tagName === 'INPUT' && type === 'radio') { + throwError( + `wrapper.setSelected() cannot be called on a element. Use wrapper.setChecked() ` + + `instead` + ) + } else if (tagName === 'INPUT' || tagName === 'TEXTAREA') { + throwError( + `wrapper.setSelected() cannot be called on "text" ` + + `inputs. Use wrapper.setValue() instead` ) + } else { + throwError(`wrapper.setSelected() cannot be called on this element`) } - - recursivelySetData(this.vm, this.vm, data) } /** @@ -558,6 +621,27 @@ export default class Wrapper implements BaseWrapper { }) } + /** + * Sets vm data + */ + setData (data: Object): void { + if (this.isFunctionalComponent) { + throwError( + `wrapper.setData() cannot be called on a functional ` + + `component` + ) + } + + if (!this.vm) { + throwError( + `wrapper.setData() can only be called on a Vue ` + + `instance` + ) + } + + recursivelySetData(this.vm, this.vm, data) + } + /** * Sets vm methods */ @@ -683,103 +767,6 @@ export default class Wrapper implements BaseWrapper { } } - /** - * Checks radio button or checkbox element - */ - setChecked (checked: boolean = true): void { - if (typeof checked !== 'boolean') { - throwError('wrapper.setChecked() must be passed a boolean') - } - const tagName = this.element.tagName - const type = this.attributes().type - - if (tagName === 'SELECT') { - throwError( - `wrapper.setChecked() cannot be called on a ` + - ` ` + - `element.` - ) - } else { - // $FlowIgnore - if (!this.element.checked) { - this.trigger('click') - this.trigger('change') - } - } - } else if (tagName === 'INPUT' || tagName === 'TEXTAREA') { - throwError( - `wrapper.setChecked() cannot be called on "text" ` + - `inputs. Use wrapper.setValue() instead` - ) - } else { - throwError(`wrapper.setChecked() cannot be called on this element`) - } - } - - /** - * Selects element - */ - setSelected (): void { - const tagName = this.element.tagName - const type = this.attributes().type - - if (tagName === 'OPTION') { - // $FlowIgnore - this.element.selected = true - // $FlowIgnore - if (this.element.parentElement.tagName === 'OPTGROUP') { - // $FlowIgnore - createWrapper(this.element.parentElement.parentElement, this.options) - .trigger('change') - } else { - // $FlowIgnore - createWrapper(this.element.parentElement, this.options) - .trigger('change') - } - } else if (tagName === 'SELECT') { - throwError( - `wrapper.setSelected() cannot be called on select. ` + - `Call it on one of its options` - ) - } else if (tagName === 'INPUT' && type === 'checkbox') { - throwError( - `wrapper.setSelected() cannot be called on a element. Use ` + - `wrapper.setChecked() instead` - ) - } else if (tagName === 'INPUT' && type === 'radio') { - throwError( - `wrapper.setSelected() cannot be called on a element. Use wrapper.setChecked() ` + - `instead` - ) - } else if (tagName === 'INPUT' || tagName === 'TEXTAREA') { - throwError( - `wrapper.setSelected() cannot be called on "text" ` + - `inputs. Use wrapper.setValue() instead` - ) - } else { - throwError(`wrapper.setSelected() cannot be called on this element`) - } - } - /** * Return text of wrapper element */ @@ -787,21 +774,6 @@ export default class Wrapper implements BaseWrapper { return this.element.textContent.trim() } - /** - * Calls destroy on vm - */ - destroy (): void { - if (!this.isVueInstance()) { - throwError(`wrapper.destroy() can only be called on a Vue instance`) - } - - if (this.element.parentNode) { - this.element.parentNode.removeChild(this.element) - } - // $FlowIgnore - this.vm.$destroy() - } - /** * Dispatches a DOM event on wrapper */ @@ -880,4 +852,28 @@ export default class Wrapper implements BaseWrapper { `updates are now synchronous by default` ) } + + /** + * Utility to check wrapper is visible. Returns false if a parent + * element has display: none or visibility: hidden style. + */ + visible (): boolean { + warn( + `visible has been deprecated and will be removed in ` + + `version 1, use isVisible instead` + ) + let element = this.element + while (element) { + if ( + element.style && + (element.style.visibility === 'hidden' || + element.style.display === 'none') + ) { + return false + } + element = element.parentElement + } + + return true + } } From d33ed354aae76480c8f8a052f450ede871950bf7 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Mon, 6 Aug 2018 22:00:01 +0100 Subject: [PATCH 06/15] refactor: remove unused code --- packages/test-utils/src/find.js | 10 ++++++++++ packages/test-utils/src/matches.js | 3 --- packages/test-utils/src/mount.js | 4 ++-- test/specs/wrapper/find.spec.js | 6 ++++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 1e74545f7..cc08bf439 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -9,6 +9,16 @@ import Vue from 'vue' import { throwError } from 'shared/util' import { matches } from './matches' +export function findAllInstances (vm, instances = []) { + instances.push(vm) + + ;(vm.$children || []).forEach(child => { + findAllInstances(child, instances) + }) + + return instances +} + function findAllVNodes ( vnode: VNode, nodes: Array = [], diff --git a/packages/test-utils/src/matches.js b/packages/test-utils/src/matches.js index 821103ea6..3a2650083 100644 --- a/packages/test-utils/src/matches.js +++ b/packages/test-utils/src/matches.js @@ -18,9 +18,6 @@ export function vmMatchesName (vm: Component, name: string): boolean { } function vmCtorMatches (vm, component) { - // const componentOptions = typeof component === 'function' - // ? component.options - // : component const isFunctional = component.functional const Ctor = component._Ctor diff --git a/packages/test-utils/src/mount.js b/packages/test-utils/src/mount.js index 4608a9e44..16ae619e1 100644 --- a/packages/test-utils/src/mount.js +++ b/packages/test-utils/src/mount.js @@ -8,7 +8,7 @@ import createInstance from 'create-instance' import createElement from './create-element' import createLocalVue from './create-local-vue' import errorHandler from './error-handler' -import { findAllVueComponentsFromVm } from './find-vue-components' +import { findAllInstances } from './find' import { mergeOptions } from 'shared/merge-options' import config from './config' import warnIfNoWindow from './warn-if-no-window' @@ -42,7 +42,7 @@ export default function mount ( const vm = parentVm.$mount(elm).$refs.vm - const componentsWithError = findAllVueComponentsFromVm(vm).filter( + const componentsWithError = findAllInstances(vm).filter( c => c._error ) diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index a45e8fc53..c57f4490f 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -83,14 +83,16 @@ describeWithShallowAndMount('find', mountingMethod => { it('returns matching extended component', () => { const ChildComponent = Vue.extend({ template: '
', - props: ['propA'] + props: ['propA'], + name: 'child-component' }) const TestComponent = { template: '', components: { ChildComponent } } const wrapper = mountingMethod(TestComponent) - expect(wrapper.find(ChildComponent).vnode).to.be.an('object') + + expect(wrapper.find(ChildComponent).name()).to.equal('child-component') }) it('returns Wrapper of elements matching attribute selector passed', () => { From becbe7ba5460fba7900584d98e9a26524fd84ed1 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 18 Aug 2018 09:20:14 +0100 Subject: [PATCH 07/15] refactor: update isEmpty --- packages/test-utils/src/find.js | 10 +++--- packages/test-utils/src/mount.js | 7 +++-- packages/test-utils/src/vue-wrapper.js | 4 +-- packages/test-utils/src/wrapper.js | 30 ++++++++++++------ test/specs/wrapper/find.spec.js | 4 +-- test/specs/wrapper/isEmpty.spec.js | 42 +++++++++++++++++++++++++- 6 files changed, 75 insertions(+), 22 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index cc08bf439..20dce0d16 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -33,8 +33,8 @@ function findAllVNodes ( findAllVNodes(v, nodes, selector) }) } - if (vnode.child) { - findAllVNodes(vnode.child._vnode, nodes, selector) + if (vnode.componentInstance) { + findAllVNodes(vnode.componentInstance._vnode, nodes, selector) } return nodes } @@ -86,10 +86,8 @@ export default function findAll ( const refs = vm.$refs[selector.value.ref] return Array.isArray(refs) ? refs : [refs] } - const rootVnode = vm && vm.$vnode - ? vm.$vnode - : vm && vm._vnode - const nodes = findAllVNodes(vm ? rootVnode : vnode, [], selector) + + const nodes = findAllVNodes(vnode, [], selector) const dedupedNodes = removeDuplicateNodes(nodes) if (nodes.length > 0 || selector.type !== DOM_SELECTOR) { diff --git a/packages/test-utils/src/mount.js b/packages/test-utils/src/mount.js index 16ae619e1..3cdecc251 100644 --- a/packages/test-utils/src/mount.js +++ b/packages/test-utils/src/mount.js @@ -12,6 +12,7 @@ import { findAllInstances } from './find' import { mergeOptions } from 'shared/merge-options' import config from './config' import warnIfNoWindow from './warn-if-no-window' +import createWrapper from './create-wrapper' Vue.config.productionTip = false Vue.config.devtools = false @@ -56,6 +57,8 @@ export default function mount ( attachedToDocument: !!mergedOptions.attachToDocument, sync: mergedOptions.sync } - - return new VueWrapper(vm, wrapperOptions) + const root = vm.$options._isFunctionalContainer + ? vm._vnode + : vm + return createWrapper(root, wrapperOptions) } diff --git a/packages/test-utils/src/vue-wrapper.js b/packages/test-utils/src/vue-wrapper.js index 9ec11280f..428422005 100644 --- a/packages/test-utils/src/vue-wrapper.js +++ b/packages/test-utils/src/vue-wrapper.js @@ -7,11 +7,11 @@ import { orderWatchers } from './order-watchers' export default class VueWrapper extends Wrapper implements BaseWrapper { constructor (vm: Component, options: WrapperOptions) { - super(vm._vnode, options, true) + super(vm.$vnode, options, true) // $FlowIgnore : issue with defineProperty Object.defineProperty(this, 'vnode', { - get: () => vm._vnode, + get: () => vm.$vnode, set: () => throwError('wrapper.vnode is read-only') }) // $FlowIgnore diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index e9f7f6f6d..141b1c3eb 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -362,10 +362,7 @@ export default class Wrapper implements BaseWrapper { throwError('$ref selectors can not be used with wrapper.is()') } - const root = this.vm && !this.isFunctionalComponent - ? (this.vm.$vnode || this.vm) - : this.vnode - return matches(root || this.element, selector) + return matches(this.vnode || this.element, selector) } /** @@ -375,12 +372,27 @@ export default class Wrapper implements BaseWrapper { if (!this.vnode) { return this.element.innerHTML === '' } - if (this.vnode.children) { - return this.vnode.children.every(vnode => vnode.isComment) + const nodes = [] + const rootVnodes = [] + let node = this.vm + ? this.vm._vnode + : this.vnode + while (node) { + if (node.componentInstance) { + rootVnodes.push(node.componentInstance._vnode) + } else { + nodes.push(node) + } + node.children && node.children.forEach(n => { + if (n.componentInstance) { + rootVnodes.push(n) + } else { + nodes.push(n) + } + }) + node = rootVnodes.shift() } - return ( - this.vnode.children === undefined || this.vnode.children.length === 0 - ) + return nodes.every(n => n.isComment) } /** diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index c57f4490f..f60bce6b0 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -51,7 +51,7 @@ describeWithShallowAndMount('find', mountingMethod => { ) it('returns Wrapper matching class selector passed if they are declared inside a functional component', () => { - const Component = { + const TestComponent = { functional: true, render (h, { props }) { return h('div', {}, [ @@ -68,7 +68,7 @@ describeWithShallowAndMount('find', mountingMethod => { const context = { data: { hello: true } } - const wrapper = mountingMethod(Component, { + const wrapper = mountingMethod(TestComponent, { context }) expect(wrapper.find('.foo').vnode).to.be.an('object') diff --git a/test/specs/wrapper/isEmpty.spec.js b/test/specs/wrapper/isEmpty.spec.js index d3d56901e..8e88a45fb 100644 --- a/test/specs/wrapper/isEmpty.spec.js +++ b/test/specs/wrapper/isEmpty.spec.js @@ -5,7 +5,7 @@ import { } from '~resources/utils' import { itSkipIf } from 'conditional-specs' -describeWithShallowAndMount('isEmpty', mountingMethod => { +describeWithShallowAndMount.only('isEmpty', mountingMethod => { it('returns true if node is empty', () => { const compiled = compileToFunctions('
') const wrapper = mountingMethod(compiled) @@ -19,6 +19,46 @@ describeWithShallowAndMount('isEmpty', mountingMethod => { expect(wrapper.isEmpty()).to.equal(true) }) + it('returns true if node contains empty components', () => { + const GrandChildComponent = { + render () {} + } + const ChildComponent = { + template: '', + components: { + GrandChildComponent + } + } + const TestComponent = { + template: ``, + components: { + ChildComponent + } + } + const wrapper = mountingMethod(TestComponent) + expect(wrapper.isEmpty()).to.equal(true) + }) + + it('returns false if nest child component renders element', () => { + const GrandChildComponent = { + template: '
' + } + const ChildComponent = { + template: '', + components: { + GrandChildComponent + } + } + const TestComponent = { + template: ``, + components: { + ChildComponent + } + } + const wrapper = mountingMethod(TestComponent) + expect(wrapper.isEmpty()).to.equal(false) + }) + itSkipIf(isRunningPhantomJS, 'returns true if innerHTML is empty', () => { const TestComponent = { render (createElement) { From 1d3c29d62a97afe922149821cc7918a59d15b191 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 12:59:24 +0100 Subject: [PATCH 08/15] refactor: use root vnode in search --- packages/test-utils/src/find.js | 15 ++++++------ packages/test-utils/src/vue-wrapper.js | 9 ++++--- packages/test-utils/src/wrapper.js | 34 ++++++++++++-------------- test/specs/wrapper/isEmpty.spec.js | 12 ++++++--- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 20dce0d16..4d75b0f40 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -47,12 +47,11 @@ function removeDuplicateNodes (vNodes: Array): Array { } export default function findAll ( - vnode: VNode | null, - element: Element, + root: VNode | Element, vm: Component, selector: Selector ): Array { - if (!vnode && selector.type !== DOM_SELECTOR) { + if ((root instanceof Element) && selector.type !== DOM_SELECTOR) { throwError( `cannot find a Vue instance on a DOM node. The node ` + `you are calling find on does not exist in the ` + @@ -60,11 +59,11 @@ export default function findAll ( ) } - if (!vnode) { - return findDOMNodes(element, selector.value) + if (root instanceof Element) { + return findDOMNodes(root, selector.value) } - if (!vnode && selector.type !== DOM_SELECTOR) { + if (!root && selector.type !== DOM_SELECTOR) { throwError( `cannot find a Vue instance on a DOM node. The node ` + `you are calling find on does not exist in the ` + @@ -87,11 +86,11 @@ export default function findAll ( return Array.isArray(refs) ? refs : [refs] } - const nodes = findAllVNodes(vnode, [], selector) + const nodes = findAllVNodes(root, [], selector) const dedupedNodes = removeDuplicateNodes(nodes) if (nodes.length > 0 || selector.type !== DOM_SELECTOR) { return dedupedNodes } - return findDOMNodes(element, selector.value) + return findDOMNodes(root.elm, selector.value) } diff --git a/packages/test-utils/src/vue-wrapper.js b/packages/test-utils/src/vue-wrapper.js index 428422005..1dc301dd9 100644 --- a/packages/test-utils/src/vue-wrapper.js +++ b/packages/test-utils/src/vue-wrapper.js @@ -7,11 +7,14 @@ import { orderWatchers } from './order-watchers' export default class VueWrapper extends Wrapper implements BaseWrapper { constructor (vm: Component, options: WrapperOptions) { - super(vm.$vnode, options, true) - + super(vm._vnode, options, true) + Object.defineProperty(this, 'rootNode', { + get: () => vm.$vnode || { componentInstance: this.vm }, + set: () => throwError('wrapper.vnode is read-only') + }) // $FlowIgnore : issue with defineProperty Object.defineProperty(this, 'vnode', { - get: () => vm.$vnode, + get: () => vm._vnode, set: () => throwError('wrapper.vnode is read-only') }) // $FlowIgnore diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 141b1c3eb..ce7a323ff 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -36,6 +36,10 @@ export default class Wrapper implements BaseWrapper { const element = node instanceof Element ? node : node.elm // Prevent redefine by VueWrapper if (!isVueWrapper) { + Object.defineProperty(this, 'rootNode', { + get: () => vnode || element, + set: () => throwError('wrapper.vnode is read-only') + }) // $FlowIgnore Object.defineProperty(this, 'vnode', { get: () => vnode, @@ -115,7 +119,7 @@ export default class Wrapper implements BaseWrapper { */ contains (rawSelector: Selector): boolean { const selector = getSelector(rawSelector, 'contains') - const nodes = findAll(this.vnode, this.element, this.vm, selector) + const nodes = findAll(this.rootNode, this.vm, selector) return nodes.length > 0 } @@ -181,7 +185,7 @@ export default class Wrapper implements BaseWrapper { */ find (rawSelector: Selector): Wrapper | ErrorWrapper { const selector = getSelector(rawSelector, 'find') - const node = findAll(this.vnode, this.element, this.vm, selector)[0] + const node = findAll(this.rootNode, this.vm, selector)[0] if (!node) { if (selector.type === REF_SELECTOR) { @@ -203,7 +207,7 @@ export default class Wrapper implements BaseWrapper { */ findAll (rawSelector: Selector): WrapperArray { const selector = getSelector(rawSelector, 'findAll') - const nodes = findAll(this.vnode, this.element, this.vm, selector) + const nodes = findAll(this.rootNode, this.vm, selector) const wrappers = nodes.map(node => { // Using CSS Selector, returns a VueWrapper instance if the root element // binds a Vue instance. @@ -362,7 +366,7 @@ export default class Wrapper implements BaseWrapper { throwError('$ref selectors can not be used with wrapper.is()') } - return matches(this.vnode || this.element, selector) + return matches(this.rootNode, selector) } /** @@ -373,26 +377,20 @@ export default class Wrapper implements BaseWrapper { return this.element.innerHTML === '' } const nodes = [] - const rootVnodes = [] - let node = this.vm - ? this.vm._vnode - : this.vnode + let node = this.vnode + let i = 0 + debugger + // if(this.root.componentInstance) while (node) { if (node.componentInstance) { - rootVnodes.push(node.componentInstance._vnode) - } else { - nodes.push(node) + nodes.push(node.componentInstance._vnode) } node.children && node.children.forEach(n => { - if (n.componentInstance) { - rootVnodes.push(n) - } else { - nodes.push(n) - } + nodes.push(n) }) - node = rootVnodes.shift() + node = nodes[i++] } - return nodes.every(n => n.isComment) + return nodes.every(n => n.isComment || n.componentInstance) } /** diff --git a/test/specs/wrapper/isEmpty.spec.js b/test/specs/wrapper/isEmpty.spec.js index 8e88a45fb..0db5bb242 100644 --- a/test/specs/wrapper/isEmpty.spec.js +++ b/test/specs/wrapper/isEmpty.spec.js @@ -3,9 +3,9 @@ import { describeWithShallowAndMount, isRunningPhantomJS } from '~resources/utils' -import { itSkipIf } from 'conditional-specs' +import { itSkipIf, itDoNotRunIf } from 'conditional-specs' -describeWithShallowAndMount.only('isEmpty', mountingMethod => { +describeWithShallowAndMount('isEmpty', mountingMethod => { it('returns true if node is empty', () => { const compiled = compileToFunctions('
') const wrapper = mountingMethod(compiled) @@ -19,7 +19,9 @@ describeWithShallowAndMount.only('isEmpty', mountingMethod => { expect(wrapper.isEmpty()).to.equal(true) }) - it('returns true if node contains empty components', () => { + itDoNotRunIf( + mountingMethod.name === 'shallowMount', + 'returns true if node contains empty components', () => { const GrandChildComponent = { render () {} } @@ -39,7 +41,9 @@ describeWithShallowAndMount.only('isEmpty', mountingMethod => { expect(wrapper.isEmpty()).to.equal(true) }) - it('returns false if nest child component renders element', () => { + itDoNotRunIf( + mountingMethod.name === 'shallowMount', + 'returns false if nested child component renders element', () => { const GrandChildComponent = { template: '
' } From b66261b45a1333dd9591f21fd7899f54bf5a5931 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 13:58:11 +0100 Subject: [PATCH 09/15] fix: remove name mutation from create-stubs --- packages/shared/create-component-stubs.js | 5 +- packages/test-utils/src/find.js | 1 - packages/test-utils/src/get-selector.js | 3 -- packages/test-utils/src/matches.js | 4 ++ packages/test-utils/src/wrapper.js | 2 +- test/specs/wrapper/find.spec.js | 1 + test/specs/wrapper/isEmpty.spec.js | 64 +++++++++++------------ 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/shared/create-component-stubs.js b/packages/shared/create-component-stubs.js index 48513627a..0cf071835 100644 --- a/packages/shared/create-component-stubs.js +++ b/packages/shared/create-component-stubs.js @@ -80,6 +80,7 @@ export function createStubFromComponent ( return { ...getCoreProperties(componentOptions), + $_vueTestUtils_original: originalComponent, render (h, context) { return h( tagName, @@ -196,9 +197,7 @@ function stubComponents ( } // Remove cached constructor delete componentOptions._Ctor - if (!componentOptions.name) { - componentOptions.name = component - } + stubbedComponents[component] = createStubFromComponent( componentOptions, component diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 4d75b0f40..0a1a138dd 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -5,7 +5,6 @@ import { DOM_SELECTOR, REF_SELECTOR } from './consts' -import Vue from 'vue' import { throwError } from 'shared/util' import { matches } from './matches' diff --git a/packages/test-utils/src/get-selector.js b/packages/test-utils/src/get-selector.js index bb41a7dd9..59d4af590 100644 --- a/packages/test-utils/src/get-selector.js +++ b/packages/test-utils/src/get-selector.js @@ -18,9 +18,6 @@ import { function getSelectorType ( selector: Selector ): string { - const selectorObject = { - - } if (isDomSelector(selector)) return DOM_SELECTOR if (isNameSelector(selector)) return NAME_SELECTOR if (isVueComponent(selector)) return COMPONENT_SELECTOR diff --git a/packages/test-utils/src/matches.js b/packages/test-utils/src/matches.js index 3a2650083..c2517c747 100644 --- a/packages/test-utils/src/matches.js +++ b/packages/test-utils/src/matches.js @@ -20,6 +20,10 @@ export function vmMatchesName (vm: Component, name: string): boolean { function vmCtorMatches (vm, component) { const isFunctional = component.functional const Ctor = component._Ctor + if (vm.$options && vm.$options.$_vueTestUtils_original === component || + vm.$_vueTestUtils_original === component) { + return true + } if (!Ctor) { return false diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index ce7a323ff..edc96b7d7 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -379,7 +379,7 @@ export default class Wrapper implements BaseWrapper { const nodes = [] let node = this.vnode let i = 0 - debugger + // if(this.root.componentInstance) while (node) { if (node.componentInstance) { diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index f60bce6b0..73debf72a 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -169,6 +169,7 @@ describeWithShallowAndMount('find', mountingMethod => { } const wrapper = mountingMethod(TestComponent) + expect(wrapper.find(FunctionalComponent).vnode).to.be.an('object') expect(wrapper.find(FunctionalComponent).vm).to.equal(undefined) }) diff --git a/test/specs/wrapper/isEmpty.spec.js b/test/specs/wrapper/isEmpty.spec.js index 0db5bb242..714a62beb 100644 --- a/test/specs/wrapper/isEmpty.spec.js +++ b/test/specs/wrapper/isEmpty.spec.js @@ -22,46 +22,46 @@ describeWithShallowAndMount('isEmpty', mountingMethod => { itDoNotRunIf( mountingMethod.name === 'shallowMount', 'returns true if node contains empty components', () => { - const GrandChildComponent = { - render () {} - } - const ChildComponent = { - template: '', - components: { - GrandChildComponent + const GrandChildComponent = { + render () {} } - } - const TestComponent = { - template: ``, - components: { - ChildComponent + const ChildComponent = { + template: '', + components: { + GrandChildComponent + } } - } - const wrapper = mountingMethod(TestComponent) - expect(wrapper.isEmpty()).to.equal(true) - }) + const TestComponent = { + template: ``, + components: { + ChildComponent + } + } + const wrapper = mountingMethod(TestComponent) + expect(wrapper.isEmpty()).to.equal(true) + }) itDoNotRunIf( mountingMethod.name === 'shallowMount', 'returns false if nested child component renders element', () => { - const GrandChildComponent = { - template: '
' - } - const ChildComponent = { - template: '', - components: { - GrandChildComponent + const GrandChildComponent = { + template: '
' } - } - const TestComponent = { - template: ``, - components: { - ChildComponent + const ChildComponent = { + template: '', + components: { + GrandChildComponent + } } - } - const wrapper = mountingMethod(TestComponent) - expect(wrapper.isEmpty()).to.equal(false) - }) + const TestComponent = { + template: ``, + components: { + ChildComponent + } + } + const wrapper = mountingMethod(TestComponent) + expect(wrapper.isEmpty()).to.equal(false) + }) itSkipIf(isRunningPhantomJS, 'returns true if innerHTML is empty', () => { const TestComponent = { From c26fa98c7b24e2a0e6e0d94f1905c96ac62916ef Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 14:13:37 +0100 Subject: [PATCH 10/15] fix: handle extended components in shallow mount --- packages/shared/create-component-stubs.js | 2 +- test/specs/wrapper/find.spec.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/shared/create-component-stubs.js b/packages/shared/create-component-stubs.js index 0cf071835..0cb12eee5 100644 --- a/packages/shared/create-component-stubs.js +++ b/packages/shared/create-component-stubs.js @@ -199,7 +199,7 @@ function stubComponents ( delete componentOptions._Ctor stubbedComponents[component] = createStubFromComponent( - componentOptions, + cmp, component ) } diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index 73debf72a..adc8f9141 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -335,6 +335,20 @@ describeWithShallowAndMount('find', mountingMethod => { expect(wrapper.find(TestComponent).isVueInstance()).to.equal(true) }) + it('works for extended child components', () => { + const ChildComponent = Vue.extend({ + template: '
' + }) + const TestComponent = { + template: '', + components: { + ChildComponent + } + } + const wrapper = mountingMethod(TestComponent) + expect(wrapper.find(ChildComponent).exists()).to.equal(true) + }) + it('returns a Wrapper matching a component name in options object', () => { const wrapper = mountingMethod(ComponentWithChild) expect(wrapper.find({ name: 'test-component' }).name()).to.equal( From 99c37f94898464156d5622b69128e49f12b33cf5 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 14:50:27 +0100 Subject: [PATCH 11/15] fix: search tree in dom tree order --- packages/test-utils/src/find.js | 48 +++++++++++++++++++-------------- test/specs/wrapper/find.spec.js | 17 ++++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 0a1a138dd..6a2d55baf 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -8,34 +8,42 @@ import { import { throwError } from 'shared/util' import { matches } from './matches' -export function findAllInstances (vm, instances = []) { - instances.push(vm) - - ;(vm.$children || []).forEach(child => { - findAllInstances(child, instances) - }) - +export function findAllInstances (rootVm) { + const instances = [rootVm] + let i = 0 + while (i < instances.length) { + const vm = instances[i] + ;(vm.$children || []).forEach(child => { + instances.push(child) + }) + i++ + } return instances } function findAllVNodes ( vnode: VNode, - nodes: Array = [], selector: any ): Array { - if (matches(vnode, selector)) { - nodes.push(vnode) + const matchingNodes = [] + const nodes = [vnode] + while (nodes.length) { + const node = nodes.shift() + if (node.children) { + const children = [...node.children].reverse() + children.forEach((n) => { + nodes.unshift(n) + }) + } + if (node.componentInstance) { + nodes.push(node.componentInstance._vnode) + } + if (matches(node, selector)) { + matchingNodes.push(node) + } } - if (Array.isArray(vnode.children)) { - vnode.children.forEach(v => { - findAllVNodes(v, nodes, selector) - }) - } - if (vnode.componentInstance) { - findAllVNodes(vnode.componentInstance._vnode, nodes, selector) - } - return nodes + return matchingNodes } function removeDuplicateNodes (vNodes: Array): Array { @@ -85,7 +93,7 @@ export default function findAll ( return Array.isArray(refs) ? refs : [refs] } - const nodes = findAllVNodes(root, [], selector) + const nodes = findAllVNodes(root, selector) const dedupedNodes = removeDuplicateNodes(nodes) if (nodes.length > 0 || selector.type !== DOM_SELECTOR) { diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index adc8f9141..df8831dee 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -153,6 +153,23 @@ describeWithShallowAndMount('find', mountingMethod => { expect(wrapper.find(ComponentAsAClass).vnode).to.be.an('object') }) + it('follows DOM tree order', () => { + const TestComponent = { + template: ` +
+
+
+
+
+
+ ` + } + const wrapper = mountingMethod(TestComponent) + const wrappers = wrapper.findAll('div').wrappers + const expectedClasses = ['1', '1a', '1aa', '1b', '2'] + wrappers.forEach((w, i) => expect(w.classes()).to.contain(expectedClasses[i])) + }) + it('returns Wrapper of Vue Component matching functional component', () => { if (!functionalSFCsSupported) { return From 3f59bac6ba5196709e0484b7c4a0aa81a6e358a7 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 15:22:13 +0100 Subject: [PATCH 12/15] fix: traverse vnode in dom tree order --- packages/test-utils/src/find.js | 2 +- test/specs/wrapper/find.spec.js | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 6a2d55baf..e81cf418d 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -36,7 +36,7 @@ function findAllVNodes ( }) } if (node.componentInstance) { - nodes.push(node.componentInstance._vnode) + nodes.unshift(node.componentInstance._vnode) } if (matches(node, selector)) { matchingNodes.push(node) diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index df8831dee..5471173f9 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -153,20 +153,28 @@ describeWithShallowAndMount('find', mountingMethod => { expect(wrapper.find(ComponentAsAClass).vnode).to.be.an('object') }) - it('follows DOM tree order', () => { + itDoNotRunIf( + mountingMethod.name === 'shallowMount', + 'follows DOM tree order', () => { const TestComponent = { template: `
-
+ +
- ` + `, + components: { + 'component-2' : { + template: '
' + } + } } const wrapper = mountingMethod(TestComponent) const wrappers = wrapper.findAll('div').wrappers - const expectedClasses = ['1', '1a', '1aa', '1b', '2'] + const expectedClasses = ['1', '1a', '1aa', '1b', '2', '3'] wrappers.forEach((w, i) => expect(w.classes()).to.contain(expectedClasses[i])) }) From d2c4ab5ac0eac25c6399d6215bed6106dc85294d Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 19:52:08 +0100 Subject: [PATCH 13/15] fix: Vue 2.3 compat --- packages/create-instance/create-instance.js | 2 + .../extend-extended-components.js | 8 +++- packages/test-utils/src/create-wrapper.js | 2 +- packages/test-utils/src/find.js | 24 +++++++--- packages/test-utils/src/matches.js | 44 +++++++++---------- packages/test-utils/src/mount.js | 2 +- packages/test-utils/src/vue-wrapper.js | 1 + packages/test-utils/src/wrapper.js | 13 +++--- test/specs/wrapper/find.spec.js | 24 +++++----- 9 files changed, 72 insertions(+), 48 deletions(-) diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index fce5add25..5f087dd03 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -82,6 +82,8 @@ export default function createInstance ( // Keep reference to component mount was called with Constructor._vueTestUtilsRoot = component + // used to identify extended component using constructor + Constructor.options.$_vueTestUtils_original = component if (options.slots) { compileTemplateForSlots(options.slots) // validate slots outside of the createSlots function so diff --git a/packages/create-instance/extend-extended-components.js b/packages/create-instance/extend-extended-components.js index 73a597ed6..ff8935a64 100644 --- a/packages/create-instance/extend-extended-components.js +++ b/packages/create-instance/extend-extended-components.js @@ -75,7 +75,13 @@ export function extendExtendedComponents ( `config.logModifiedComponents option to false.` ) } - extendedComponents[c] = _Vue.extend(comp) + const extendedComp = _Vue.extend(comp) + // used to identify instance when calling find with component selector + if (extendedComp.extendOptions.options) { + extendedComp.extendOptions.options.$_vueTestUtils_original = comp + } + extendedComp.extendOptions.$_vueTestUtils_original = comp + extendedComponents[c] = extendedComp } // If a component has been replaced with an extended component // all its child components must also be replaced. diff --git a/packages/test-utils/src/create-wrapper.js b/packages/test-utils/src/create-wrapper.js index 6ae1b9b81..5d19d8541 100644 --- a/packages/test-utils/src/create-wrapper.js +++ b/packages/test-utils/src/create-wrapper.js @@ -8,7 +8,7 @@ export default function createWrapper ( node: VNode | Component, options: WrapperOptions = {} ): VueWrapper | Wrapper { - const componentInstance = node.componentInstance + const componentInstance = node.child if (componentInstance) { return new VueWrapper(componentInstance, options) } diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index e81cf418d..76b102762 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -3,12 +3,13 @@ import findDOMNodes from './find-dom-nodes' import { DOM_SELECTOR, - REF_SELECTOR + REF_SELECTOR, + COMPONENT_SELECTOR } from './consts' -import { throwError } from 'shared/util' +import { throwError, vueVersion } from 'shared/util' import { matches } from './matches' -export function findAllInstances (rootVm) { +export function findAllInstances (rootVm: any) { const instances = [rootVm] let i = 0 while (i < instances.length) { @@ -35,8 +36,8 @@ function findAllVNodes ( nodes.unshift(n) }) } - if (node.componentInstance) { - nodes.unshift(node.componentInstance._vnode) + if (node.child) { + nodes.unshift(node.child._vnode) } if (matches(node, selector)) { matchingNodes.push(node) @@ -55,7 +56,7 @@ function removeDuplicateNodes (vNodes: Array): Array { export default function findAll ( root: VNode | Element, - vm: Component, + vm?: Component, selector: Selector ): Array { if ((root instanceof Element) && selector.type !== DOM_SELECTOR) { @@ -65,6 +66,17 @@ export default function findAll ( `VDom. Are you adding the node as innerHTML?` ) } + console.log(selector.type) + if ( + selector.type === COMPONENT_SELECTOR && + selector.value.functional && + vueVersion < 2.3 + ) { + throwError( + `find for functional components is not supported ` + + `in Vue < 2.3` + ) + } if (root instanceof Element) { return findDOMNodes(root, selector.value) diff --git a/packages/test-utils/src/matches.js b/packages/test-utils/src/matches.js index c2517c747..9d8f8b21e 100644 --- a/packages/test-utils/src/matches.js +++ b/packages/test-utils/src/matches.js @@ -1,5 +1,3 @@ -// @flow - import { DOM_SELECTOR, NAME_SELECTOR, @@ -7,21 +5,22 @@ import { FUNCTIONAL_OPTIONS } from './consts' -export function vmMatchesName (vm: Component, name: string): boolean { +export function vmMatchesName (vm, name) { return !!( - name && ( - (vm && - vm.name === name) || - (vm.$options && vm.$options.name === name) || - (vm.options && vm.options.name === name) - )) + (vm.name === name) || + (vm.$options && vm.$options.name === name) + ) } function vmCtorMatches (vm, component) { - const isFunctional = component.functional - const Ctor = component._Ctor - if (vm.$options && vm.$options.$_vueTestUtils_original === component || - vm.$_vueTestUtils_original === component) { + const Ctor = typeof component === 'function' + ? component.options._Ctor + : component._Ctor + + if ( + vm.$options && vm.$options.$_vueTestUtils_original === component || + vm.$_vueTestUtils_original === component + ) { return true } @@ -31,9 +30,9 @@ function vmCtorMatches (vm, component) { const constructor = vm.constructor return Object.keys(Ctor).some(c => { - return isFunctional + return component.functional ? Ctor[c] === vm._Ctor[c] - : Ctor[c] === constructor || Ctor[c] === constructor.super + : Ctor[c] === constructor }) } @@ -45,24 +44,25 @@ export function matches (node, selector) { return element && element.matches && element.matches(selector.value) } - const nameSelector = - typeof selector.value === 'function' - ? selector.value.extendOptions.name - : selector.value.name const componentInstance = selector.value.functional ? node && node[FUNCTIONAL_OPTIONS] - : node.componentInstance || node + : node.child if (!componentInstance) { return false } if (selector.type === NAME_SELECTOR) { + const nameSelector = + typeof selector.value === 'function' + ? selector.value.extendOptions.name + : selector.value.name return vmMatchesName(componentInstance, nameSelector) } if (selector.type === COMPONENT_SELECTOR) { - return vmCtorMatches(componentInstance, selector.value) || - vmMatchesName(componentInstance, nameSelector) + return vmCtorMatches(componentInstance, selector.value) } + + return false } diff --git a/packages/test-utils/src/mount.js b/packages/test-utils/src/mount.js index 3cdecc251..67c36f4c8 100644 --- a/packages/test-utils/src/mount.js +++ b/packages/test-utils/src/mount.js @@ -20,7 +20,7 @@ Vue.config.devtools = false export default function mount ( component: Component, options: Options = {} -): VueWrapper { +): VueWrapper | Wrapper { const existingErrorHandler = Vue.config.errorHandler Vue.config.errorHandler = errorHandler diff --git a/packages/test-utils/src/vue-wrapper.js b/packages/test-utils/src/vue-wrapper.js index 1dc301dd9..5c245f378 100644 --- a/packages/test-utils/src/vue-wrapper.js +++ b/packages/test-utils/src/vue-wrapper.js @@ -8,6 +8,7 @@ import { orderWatchers } from './order-watchers' export default class VueWrapper extends Wrapper implements BaseWrapper { constructor (vm: Component, options: WrapperOptions) { super(vm._vnode, options, true) + // $FlowIgnore : issue with defineProperty Object.defineProperty(this, 'rootNode', { get: () => vm.$vnode || { componentInstance: this.vm }, set: () => throwError('wrapper.vnode is read-only') diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index edc96b7d7..132378460 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -26,6 +26,7 @@ export default class Wrapper implements BaseWrapper { +options: WrapperOptions; version: number; isFunctionalComponent: boolean; + rootNode: VNode | Element constructor ( node: VNode | Element, @@ -36,6 +37,7 @@ export default class Wrapper implements BaseWrapper { const element = node instanceof Element ? node : node.elm // Prevent redefine by VueWrapper if (!isVueWrapper) { + // $FlowIgnore : issue with defineProperty Object.defineProperty(this, 'rootNode', { get: () => vnode || element, set: () => throwError('wrapper.vnode is read-only') @@ -380,17 +382,16 @@ export default class Wrapper implements BaseWrapper { let node = this.vnode let i = 0 - // if(this.root.componentInstance) while (node) { - if (node.componentInstance) { - nodes.push(node.componentInstance._vnode) + if (node.child) { + nodes.push(node.child._vnode) } node.children && node.children.forEach(n => { nodes.push(n) }) node = nodes[i++] } - return nodes.every(n => n.isComment || n.componentInstance) + return nodes.every(n => n.isComment || n.child) } /** @@ -424,7 +425,9 @@ export default class Wrapper implements BaseWrapper { */ name (): string { if (this.vm) { - return this.vm.$options.name + return this.vm.$options.name || + // compat for Vue < 2.3 + (this.vm.$options.extendOptions && this.vm.$options.extendOptions.name) } if (!this.vnode) { diff --git a/test/specs/wrapper/find.spec.js b/test/specs/wrapper/find.spec.js index 5471173f9..f9f088fb0 100644 --- a/test/specs/wrapper/find.spec.js +++ b/test/specs/wrapper/find.spec.js @@ -148,16 +148,16 @@ describeWithShallowAndMount('find', mountingMethod => { ComponentAsAClass } } - const wrapper = mountingMethod(TestComponent) + expect(wrapper.find(ComponentAsAClass).vnode).to.be.an('object') }) itDoNotRunIf( mountingMethod.name === 'shallowMount', 'follows DOM tree order', () => { - const TestComponent = { - template: ` + const TestComponent = { + template: `
@@ -166,17 +166,17 @@ describeWithShallowAndMount('find', mountingMethod => {
`, - components: { - 'component-2' : { - template: '
' + components: { + 'component-2': { + template: '
' + } } } - } - const wrapper = mountingMethod(TestComponent) - const wrappers = wrapper.findAll('div').wrappers - const expectedClasses = ['1', '1a', '1aa', '1b', '2', '3'] - wrappers.forEach((w, i) => expect(w.classes()).to.contain(expectedClasses[i])) - }) + const wrapper = mountingMethod(TestComponent) + const wrappers = wrapper.findAll('div').wrappers + const expectedClasses = ['1', '1a', '1aa', '1b', '2', '3'] + wrappers.forEach((w, i) => expect(w.classes()).to.contain(expectedClasses[i])) + }) it('returns Wrapper of Vue Component matching functional component', () => { if (!functionalSFCsSupported) { From 8059b8908d4a24168a12d033e8d946c583427284 Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 21:53:53 +0100 Subject: [PATCH 14/15] fix: fall back to name selector --- packages/test-utils/src/find.js | 2 +- packages/test-utils/src/matches.js | 22 ++++++++++------------ test/specs/config.spec.js | 6 +++--- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 76b102762..9a963eff2 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -66,7 +66,7 @@ export default function findAll ( `VDom. Are you adding the node as innerHTML?` ) } - console.log(selector.type) + if ( selector.type === COMPONENT_SELECTOR && selector.value.functional && diff --git a/packages/test-utils/src/matches.js b/packages/test-utils/src/matches.js index 9d8f8b21e..24f160f1a 100644 --- a/packages/test-utils/src/matches.js +++ b/packages/test-utils/src/matches.js @@ -1,12 +1,11 @@ import { DOM_SELECTOR, - NAME_SELECTOR, COMPONENT_SELECTOR, FUNCTIONAL_OPTIONS } from './consts' export function vmMatchesName (vm, name) { - return !!( + return !!name && ( (vm.name === name) || (vm.$options && vm.$options.name === name) ) @@ -52,17 +51,16 @@ export function matches (node, selector) { return false } - if (selector.type === NAME_SELECTOR) { - const nameSelector = - typeof selector.value === 'function' - ? selector.value.extendOptions.name - : selector.value.name - return vmMatchesName(componentInstance, nameSelector) - } - if (selector.type === COMPONENT_SELECTOR) { - return vmCtorMatches(componentInstance, selector.value) + if (vmCtorMatches(componentInstance, selector.value)) { + return true + } } - return false + // Fallback to name selector for COMPONENT_SELECTOR for Vue < 2.1 + const nameSelector = + typeof selector.value === 'function' + ? selector.value.extendOptions.name + : selector.value.name + return vmMatchesName(componentInstance, nameSelector) } diff --git a/test/specs/config.spec.js b/test/specs/config.spec.js index 951a1c6fc..5dacf4adb 100644 --- a/test/specs/config.spec.js +++ b/test/specs/config.spec.js @@ -34,13 +34,13 @@ describeWithShallowAndMount('config', mountingMethod => { template: `

-

+

` } const wrapper = mountingMethod(testComponent) - expect(wrapper.contains(TransitionStub)).to.equal(true) - expect(wrapper.contains(TransitionGroupStub)).to.equal(true) + expect(wrapper.contains('p')).to.equal(true) + expect(wrapper.contains('span')).to.equal(true) } ) From 83f5228a9ad611a0d553aa3a047e8fe3815b454e Mon Sep 17 00:00:00 2001 From: eddyerburgh Date: Sat, 25 Aug 2018 22:14:56 +0100 Subject: [PATCH 15/15] refactor: rename findAll to find --- packages/test-utils/src/find.js | 5 ++++- packages/test-utils/src/wrapper.js | 16 ++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/test-utils/src/find.js b/packages/test-utils/src/find.js index 9a963eff2..dd5c22a8b 100644 --- a/packages/test-utils/src/find.js +++ b/packages/test-utils/src/find.js @@ -54,7 +54,7 @@ function removeDuplicateNodes (vNodes: Array): Array { ) } -export default function findAll ( +export default function find ( root: VNode | Element, vm?: Component, selector: Selector @@ -111,5 +111,8 @@ export default function findAll ( if (nodes.length > 0 || selector.type !== DOM_SELECTOR) { return dedupedNodes } + + // Fallback in case element exists in HTML, but not in vnode tree + // (e.g. if innerHTML is set as a domProp) return findDOMNodes(root.elm, selector.value) } diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 132378460..8b786a046 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -9,8 +9,8 @@ import { import config from './config' import WrapperArray from './wrapper-array' import ErrorWrapper from './error-wrapper' -import { throwError, warn } from '../../shared/util' -import findAll from './find' +import { throwError, warn, vueVersion } from 'shared/util' +import find from './find' import createWrapper from './create-wrapper' import { orderWatchers } from './order-watchers' import { recursivelySetData } from './recursively-set-data' @@ -24,7 +24,6 @@ export default class Wrapper implements BaseWrapper { +element: Element; update: Function; +options: WrapperOptions; - version: number; isFunctionalComponent: boolean; rootNode: VNode | Element @@ -70,9 +69,6 @@ export default class Wrapper implements BaseWrapper { ) { this.isFunctionalComponent = true } - this.version = Number( - `${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}` - ) } at (): void { @@ -121,7 +117,7 @@ export default class Wrapper implements BaseWrapper { */ contains (rawSelector: Selector): boolean { const selector = getSelector(rawSelector, 'contains') - const nodes = findAll(this.rootNode, this.vm, selector) + const nodes = find(this.rootNode, this.vm, selector) return nodes.length > 0 } @@ -187,7 +183,7 @@ export default class Wrapper implements BaseWrapper { */ find (rawSelector: Selector): Wrapper | ErrorWrapper { const selector = getSelector(rawSelector, 'find') - const node = findAll(this.rootNode, this.vm, selector)[0] + const node = find(this.rootNode, this.vm, selector)[0] if (!node) { if (selector.type === REF_SELECTOR) { @@ -209,7 +205,7 @@ export default class Wrapper implements BaseWrapper { */ findAll (rawSelector: Selector): WrapperArray { const selector = getSelector(rawSelector, 'findAll') - const nodes = findAll(this.rootNode, this.vm, selector) + const nodes = find(this.rootNode, this.vm, selector) const wrappers = nodes.map(node => { // Using CSS Selector, returns a VueWrapper instance if the root element // binds a Vue instance. @@ -580,7 +576,7 @@ export default class Wrapper implements BaseWrapper { ) Object.keys(computed).forEach(key => { - if (this.version > 2.1) { + if (vueVersion > 2.1) { // $FlowIgnore : Problem with possibly null this.vm if (!this.vm._computedWatchers[key]) { throwError(