From c30a74fece4bac66190b4755ef881109f5406429 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Thu, 30 Apr 2020 20:22:43 +1000 Subject: [PATCH] build: 1.0.0 --- lerna.json | 2 +- .../dist/vue-server-test-utils.js | 90 ++++- .../test-utils/dist/vue-test-utils.iife.js | 358 ++++++++++++++--- packages/test-utils/dist/vue-test-utils.js | 355 ++++++++++++++--- .../test-utils/dist/vue-test-utils.umd.js | 362 +++++++++++++++--- 5 files changed, 978 insertions(+), 189 deletions(-) diff --git a/lerna.json b/lerna.json index 0756577f4..15ac47b76 100644 --- a/lerna.json +++ b/lerna.json @@ -5,5 +5,5 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "1.0.0-beta.33" + "version": "1.0.0" } diff --git a/packages/server-test-utils/dist/vue-server-test-utils.js b/packages/server-test-utils/dist/vue-server-test-utils.js index dbb7394f0..fb4047fce 100644 --- a/packages/server-test-utils/dist/vue-server-test-utils.js +++ b/packages/server-test-utils/dist/vue-server-test-utils.js @@ -6,9 +6,9 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau var Vue = _interopDefault(require('vue')); var vueTemplateCompiler = require('vue-template-compiler'); -var vueServerRenderer = require('vue-server-renderer'); var testUtils = require('@vue/test-utils'); var testUtils__default = _interopDefault(testUtils); +var vueServerRenderer = require('vue-server-renderer'); var cheerio = _interopDefault(require('cheerio')); // @@ -1701,6 +1701,18 @@ var semver_40 = semver.prerelease; var semver_41 = semver.intersects; var semver_42 = semver.coerce; +var VUE_VERSION = Number( + ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) +); + +var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') + ? 'beforeCreate' + : 'beforeMount'; + +var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') + ? '_c' + : '_h'; + // function throwError(msg) { @@ -1768,6 +1780,15 @@ var isPhantomJS = UA && UA.includes && UA.match(/phantomjs/i); var isEdge = UA && UA.indexOf('edge/') > 0; var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; +function warnDeprecated(method, fallback) { + if ( fallback === void 0 ) fallback = ''; + + if (!testUtils.config.showDeprecationWarnings) { return } + var msg = method + " is deprecated and will removed in the next major version"; + if (fallback) { msg += " " + fallback; } + warn(msg); +} + // function addMocks( @@ -1822,18 +1843,6 @@ function addEventLogger(_Vue) { }); } -var VUE_VERSION = Number( - ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) -); - -var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') - ? 'beforeCreate' - : 'beforeMount'; - -var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') - ? '_c' - : '_h'; - function addStubs(_Vue, stubComponents) { var obj; @@ -1846,6 +1855,33 @@ function addStubs(_Vue, stubComponents) { // +function isDomSelector(selector) { + if (typeof selector !== 'string') { + return false + } + + try { + if (typeof document === 'undefined') { + throwError( + "mount must be run in a browser environment like " + + "PhantomJS, jsdom or chrome" + ); + } + } catch (error) { + throwError( + "mount must be run in a browser environment like " + + "PhantomJS, jsdom or chrome" + ); + } + + try { + document.querySelector(selector); + return true + } catch (error) { + return false + } +} + function isVueComponent(c) { if (isConstructor(c)) { return true @@ -1911,6 +1947,14 @@ function isPlainObject(c) { return Object.prototype.toString.call(c) === '[object Object]' } +function isHTMLElement(c) { + if (typeof HTMLElement === 'undefined') { + return false + } + // eslint-disable-next-line no-undef + return c instanceof HTMLElement +} + function makeMap(str, expectsLowerCase) { var map = Object.create(null); var list = str.split(','); @@ -2297,7 +2341,7 @@ function createStubFromComponent( // DEPRECATED: converts string stub to template stub. function createStubFromString(templateString, name) { - warn('String stubs are deprecated and will be removed in future versions'); + warnDeprecated('Using a string for stubs'); if (templateContainsComponent(templateString, name)) { throwError('options.stub cannot contain a circular reference'); @@ -2644,6 +2688,10 @@ function mergeOptions( ) { var mocks = (getOption(options.mocks, config.mocks)); var methods = (getOption(options.methods, config.methods)); + if (methods && Object.keys(methods).length) { + warnDeprecated('overwriting methods via the `methods` property'); + } + var provide = (getOption(options.provide, config.provide)); var stubs = (getStubs(options.stubs, config.stubs)); // $FlowIgnore @@ -2702,6 +2750,20 @@ function vueExtendUnsupportedOption(option) { var UNSUPPORTED_VERSION_OPTIONS = ['mocks', 'stubs', 'localVue']; function validateOptions(options, component) { + if ( + options.attachTo && + !isHTMLElement(options.attachTo) && + !isDomSelector(options.attachTo) + ) { + throwError( + "options.attachTo should be a valid HTMLElement or CSS selector string" + ); + } + if ('attachToDocument' in options) { + warn( + "options.attachToDocument is deprecated in favor of options.attachTo and will be removed in a future release" + ); + } if (options.parentComponent && !isPlainObject(options.parentComponent)) { throwError( "options.parentComponent should be a valid Vue component options object" diff --git a/packages/test-utils/dist/vue-test-utils.iife.js b/packages/test-utils/dist/vue-test-utils.iife.js index 6364302ba..76dde5909 100644 --- a/packages/test-utils/dist/vue-test-utils.iife.js +++ b/packages/test-utils/dist/vue-test-utils.iife.js @@ -1,4 +1,4 @@ -var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { +var VueTestUtils = (function (exports, Vue, vueTemplateCompiler, testUtils) { 'use strict'; Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue; @@ -1699,6 +1699,27 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { var semver_41 = semver.intersects; var semver_42 = semver.coerce; + var NAME_SELECTOR = 'NAME_SELECTOR'; + var COMPONENT_SELECTOR = 'COMPONENT_SELECTOR'; + var REF_SELECTOR = 'REF_SELECTOR'; + var DOM_SELECTOR = 'DOM_SELECTOR'; + var INVALID_SELECTOR = 'INVALID_SELECTOR'; + + var VUE_VERSION = Number( + ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) + ); + + var FUNCTIONAL_OPTIONS = + VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions'; + + var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') + ? 'beforeCreate' + : 'beforeMount'; + + var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') + ? '_c' + : '_h'; + // function throwError(msg) { @@ -1782,6 +1803,27 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { return 'change' } + /** + * Normalize nextTick to return a promise for all Vue 2 versions. + * Vue < 2.1 does not return a Promise from nextTick + * @return {Promise} + */ + function nextTick() { + if (VUE_VERSION > 2) { return Vue.nextTick() } + return new Promise(function (resolve) { + Vue.nextTick(resolve); + }) + } + + function warnDeprecated(method, fallback) { + if ( fallback === void 0 ) fallback = ''; + + if (!testUtils.config.showDeprecationWarnings) { return } + var msg = method + " is deprecated and will removed in the next major version"; + if (fallback) { msg += " " + fallback; } + warn(msg); + } + // function addMocks( @@ -1836,27 +1878,6 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { }); } - var NAME_SELECTOR = 'NAME_SELECTOR'; - var COMPONENT_SELECTOR = 'COMPONENT_SELECTOR'; - var REF_SELECTOR = 'REF_SELECTOR'; - var DOM_SELECTOR = 'DOM_SELECTOR'; - var INVALID_SELECTOR = 'INVALID_SELECTOR'; - - var VUE_VERSION = Number( - ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) - ); - - var FUNCTIONAL_OPTIONS = - VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions'; - - var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') - ? 'beforeCreate' - : 'beforeMount'; - - var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') - ? '_c' - : '_h'; - function addStubs(_Vue, stubComponents) { var obj; @@ -1980,6 +2001,14 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { return Object.prototype.toString.call(c) === '[object Object]' } + function isHTMLElement(c) { + if (typeof HTMLElement === 'undefined') { + return false + } + // eslint-disable-next-line no-undef + return c instanceof HTMLElement + } + function makeMap(str, expectsLowerCase) { var map = Object.create(null); var list = str.split(','); @@ -2366,7 +2395,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { // DEPRECATED: converts string stub to template stub. function createStubFromString(templateString, name) { - warn('String stubs are deprecated and will be removed in future versions'); + warnDeprecated('Using a string for stubs'); if (templateContainsComponent(templateString, name)) { throwError('options.stub cannot contain a circular reference'); @@ -2961,6 +2990,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { ) { var mocks = (getOption(options.mocks, config.mocks)); var methods = (getOption(options.methods, config.methods)); + if (methods && Object.keys(methods).length) { + warnDeprecated('overwriting methods via the `methods` property'); + } + var provide = (getOption(options.provide, config.provide)); var stubs = (getStubs(options.stubs, config.stubs)); // $FlowIgnore @@ -2972,11 +3005,16 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { } var config = { - stubs: {}, + stubs: { + transition: true, + 'transition-group': true + }, mocks: {}, methods: {}, provide: {}, - silent: true + silent: true, + showDeprecationWarnings: + true }; // @@ -8558,6 +8596,15 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { ); }; + WrapperArray.prototype.overview = function overview () { + this.throwErrorIfWrappersIsEmpty('overview()'); + + throwError( + "overview() must be called on a single wrapper, use at(i) " + + "to access a wrapper" + ); + }; + WrapperArray.prototype.props = function props () { this.throwErrorIfWrappersIsEmpty('props'); @@ -8817,6 +8864,14 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { ); }; + ErrorWrapper.prototype.overview = function overview () { + throwError( + ("find did not return " + (buildSelectorString( + this.selector + )) + ", cannot call overview() on empty Wrapper") + ); + }; + ErrorWrapper.prototype.props = function props () { throwError( ("find did not return " + (buildSelectorString( @@ -10324,6 +10379,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Checks if wrapper contains provided selector. */ Wrapper.prototype.contains = function contains (rawSelector) { + warnDeprecated( + 'contains', + 'Use `wrapper.find`, `wrapper.findComponent` or `wrapper.get` instead' + ); var selector = getSelector(rawSelector, 'contains'); var nodes = find(this.rootNode, this.vm, selector); return nodes.length > 0 @@ -10370,6 +10429,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Returns an Array containing custom events emitted by the Wrapper vm */ Wrapper.prototype.emittedByOrder = function emittedByOrder () { + warnDeprecated('emittedByOrder', 'Use `wrapper.emitted` instead'); if (!this._emittedByOrder && !this.vm) { throwError( "wrapper.emittedByOrder() can only be called on a Vue instance" @@ -10405,11 +10465,17 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { }; /** - * Finds first node in tree of the current wrapper that + * Finds first DOM node in tree of the current wrapper that * matches the provided selector. */ Wrapper.prototype.find = function find$1 (rawSelector) { var selector = getSelector(rawSelector, 'find'); + if (selector.type !== DOM_SELECTOR) { + warnDeprecated( + 'finding components with `find`', + 'Use `findComponent` instead' + ); + } var node = find(this.rootNode, this.vm, selector)[0]; if (!node) { @@ -10422,13 +10488,68 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { }; /** - * Finds node in tree of the current wrapper that matches + * Finds DOM elements in tree of the current wrapper that matches * the provided selector. */ Wrapper.prototype.findAll = function findAll (rawSelector) { var this$1 = this; var selector = getSelector(rawSelector, 'findAll'); + if (selector.type !== DOM_SELECTOR) { + warnDeprecated( + 'finding components with `findAll`', + 'Use `findAllComponents` instead' + ); + } + var nodes = find(this.rootNode, this.vm, selector); + var wrappers = nodes.map(function (node) { + // Using CSS Selector, returns a VueWrapper instance if the root element + // binds a Vue instance. + var wrapper = createWrapper(node, this$1.options); + wrapper.selector = rawSelector; + return wrapper + }); + + var wrapperArray = new WrapperArray(wrappers); + wrapperArray.selector = rawSelector; + return wrapperArray + }; + + /** + * Finds first component in tree of the current wrapper that + * matches the provided selector. + */ + Wrapper.prototype.findComponent = function findComponent (rawSelector) { + var selector = getSelector(rawSelector, 'findComponent'); + if (selector.type === DOM_SELECTOR) { + throwError( + 'findComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead' + ); + } + var node = find(this.rootNode, this.vm, selector)[0]; + + if (!node) { + return new ErrorWrapper(rawSelector) + } + + var wrapper = createWrapper(node, this.options); + wrapper.selector = rawSelector; + return wrapper + }; + + /** + * Finds components in tree of the current wrapper that matches + * the provided selector. + */ + Wrapper.prototype.findAllComponents = function findAllComponents (rawSelector) { + var this$1 = this; + + var selector = getSelector(rawSelector, 'findAll'); + if (selector.type === DOM_SELECTOR) { + throwError( + 'findAllComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead' + ); + } var nodes = find(this.rootNode, this.vm, selector); var wrappers = nodes.map(function (node) { // Using CSS Selector, returns a VueWrapper instance if the root element @@ -10454,6 +10575,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Checks if node matches selector */ Wrapper.prototype.is = function is (rawSelector) { + warnDeprecated('is', 'Use element.tagName instead'); var selector = getSelector(rawSelector, 'is'); if (selector.type === REF_SELECTOR) { @@ -10467,6 +10589,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Checks if node is empty */ Wrapper.prototype.isEmpty = function isEmpty () { + warnDeprecated( + 'isEmpty', + 'Consider a custom matcher such as those provided in jest-dom: https://github.com/testing-library/jest-dom#tobeempty' + ); if (!this.vnode) { return this.element.innerHTML === '' } @@ -10491,6 +10617,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Checks if node is visible */ Wrapper.prototype.isVisible = function isVisible () { + warnDeprecated( + 'isEmpty', + "Consider a custom matcher such as those provided in jest-dom: https://github.com/testing-library/jest-dom#tobevisible" + ); var element = this.element; while (element) { if ( @@ -10511,6 +10641,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Checks if wrapper is a vue instance */ Wrapper.prototype.isVueInstance = function isVueInstance () { + warnDeprecated("isVueInstance"); return !!this.vm }; @@ -10518,6 +10649,8 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { * Returns name of component, or tag name if node is not a Vue component */ Wrapper.prototype.name = function name () { + warnDeprecated("name"); + if (this.vm) { return ( this.vm.$options.name || @@ -10533,6 +10666,86 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { return this.vnode.tag }; + /** + * Prints a simple overview of the wrapper current state + * with useful information for debugging + */ + Wrapper.prototype.overview = function overview () { + var this$1 = this; + + warnDeprecated("overview"); + + if (!this.isVueInstance()) { + throwError("wrapper.overview() can only be called on a Vue instance"); + } + + var identation = 4; + var formatJSON = function (json, replacer) { + if ( replacer === void 0 ) replacer = null; + + return JSON.stringify(json, replacer, identation).replace(/"/g, ''); + }; + + var visibility = this.isVisible() ? 'Visible' : 'Not visible'; + + var html = this.html() + ? this.html().replace(/^(?!\s*$)/gm, ' '.repeat(identation)) + '\n' + : ''; + + // $FlowIgnore + var data = formatJSON(this.vm.$data); + + /* eslint-disable operator-linebreak */ + // $FlowIgnore + var computed = this.vm._computedWatchers + ? formatJSON.apply( + // $FlowIgnore + void 0, Object.keys(this.vm._computedWatchers).map(function (computedKey) { + var obj; + + return (( obj = {}, obj[computedKey] = this$1.vm[computedKey], obj )); + }) + ) + : // $FlowIgnore + this.vm.$options.computed + ? formatJSON.apply( + // $FlowIgnore + void 0, Object.entries(this.vm.$options.computed).map(function (ref) { + var obj; + + var key = ref[0]; + var value = ref[1]; + return (( obj = {}, obj[key] = value(), obj )); + }) + ) + : '{}'; + /* eslint-enable operator-linebreak */ + + var emittedJSONReplacer = function (key, value) { return value instanceof Array + ? value.map(function (calledWith, index) { + var callParams = calledWith.map(function (param) { return typeof param === 'object' + ? JSON.stringify(param) + .replace(/"/g, '') + .replace(/,/g, ', ') + : param; } + ); + + return (index + ": [ " + (callParams.join(', ')) + " ]") + }) + : value; }; + + var emitted = formatJSON(this.emitted(), emittedJSONReplacer); + + console.log( + '\n' + + "Wrapper (" + visibility + "):\n\n" + + "Html:\n" + html + "\n" + + "Data: " + data + "\n\n" + + "Computed: " + computed + "\n\n" + + "Emitted: " + emitted + "\n" + ); + }; + /** * Returns an Object containing the prop name/value pairs on the element */ @@ -10572,6 +10785,11 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { Wrapper.prototype.setChecked = function setChecked (checked) { if ( checked === void 0 ) checked = true; + warnDeprecated( + "setChecked", + 'When you migrate to VTU 2, use setValue instead.' + ); + if (typeof checked !== 'boolean') { throwError('wrapper.setChecked() must be passed a boolean'); } @@ -10582,14 +10800,13 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { if (tagName === 'INPUT' && type === 'checkbox') { if (this.element.checked === checked) { - return + return nextTick() } if (event !== 'click' || isPhantomJS) { // $FlowIgnore this.element.checked = checked; } - this.trigger(event); - return + return this.trigger(event) } if (tagName === 'INPUT' && type === 'radio') { @@ -10601,24 +10818,29 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { } if (this.element.checked === checked) { - return + return nextTick() } if (event !== 'click' || isPhantomJS) { // $FlowIgnore this.element.selected = true; } - this.trigger(event); - return + return this.trigger(event) } throwError("wrapper.setChecked() cannot be called on this element"); + return nextTick() }; /** * Selects element */ Wrapper.prototype.setSelected = function setSelected () { + warnDeprecated( + "setSelected", + 'When you migrate to VTU 2, use setValue instead.' + ); + var tagName = this.element.tagName; if (tagName === 'SELECT') { @@ -10628,28 +10850,27 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { ); } - if (tagName === 'OPTION') { - if (this.element.selected) { - return - } + if (tagName !== 'OPTION') { + throwError("wrapper.setSelected() cannot be called on this element"); + } - // $FlowIgnore - this.element.selected = true; - // $FlowIgnore - var parentElement = this.element.parentElement; + if (this.element.selected) { + return nextTick() + } - // $FlowIgnore - if (parentElement.tagName === 'OPTGROUP') { - // $FlowIgnore - parentElement = parentElement.parentElement; - } + // $FlowIgnore + this.element.selected = true; + // $FlowIgnore + var parentElement = this.element.parentElement; + // $FlowIgnore + if (parentElement.tagName === 'OPTGROUP') { // $FlowIgnore - createWrapper(parentElement, this.options).trigger('change'); - return + parentElement = parentElement.parentElement; } - throwError("wrapper.setSelected() cannot be called on this element"); + // $FlowIgnore + return createWrapper(parentElement, this.options).trigger('change') }; /** @@ -10665,6 +10886,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { } recursivelySetData(this.vm, this.vm, data); + return nextTick() }; /** @@ -10673,6 +10895,8 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { Wrapper.prototype.setMethods = function setMethods (methods) { var this$1 = this; + warnDeprecated("setMethods"); + if (!this.isVueInstance()) { throwError("wrapper.setMethods() can only be called on a Vue instance"); } @@ -10734,7 +10958,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { if (VUE_VERSION > 2.3) { // $FlowIgnore : Problem with possibly null this.vm this$1.vm.$attrs[key] = data[key]; - return + return nextTick() } throwError( "wrapper.setProps() called with " + key + " property which " + @@ -10749,6 +10973,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { // $FlowIgnore : Problem with possibly null this.vm this.vm.$forceUpdate(); + return nextTick() } catch (err) { throw err } finally { @@ -10804,9 +11029,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { ) { this.trigger('change'); } - } else { - throwError("wrapper.setValue() cannot be called on this element"); + return nextTick() } + throwError("wrapper.setValue() cannot be called on this element"); + return nextTick() }; /** @@ -10853,11 +11079,12 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { var tagName = this.element.tagName; if (this.attributes().disabled && supportedTags.indexOf(tagName) > -1) { - return + return nextTick() } var event = createDOMEvent(type, options); this.element.dispatchEvent(event); + return nextTick() }; // @@ -12893,9 +13120,9 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { var _DataView = DataView; /* Built-in method references that are verified to be native. */ - var Promise = _getNative(_root, 'Promise'); + var Promise$1 = _getNative(_root, 'Promise'); - var _Promise = Promise; + var _Promise = Promise$1; /* Built-in method references that are verified to be native. */ var Set$1 = _getNative(_root, 'Set'); @@ -13541,6 +13768,20 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { var UNSUPPORTED_VERSION_OPTIONS = ['mocks', 'stubs', 'localVue']; function validateOptions(options, component) { + if ( + options.attachTo && + !isHTMLElement(options.attachTo) && + !isDomSelector(options.attachTo) + ) { + throwError( + "options.attachTo should be a valid HTMLElement or CSS selector string" + ); + } + if ('attachToDocument' in options) { + warn( + "options.attachToDocument is deprecated in favor of options.attachTo and will be removed in a future release" + ); + } if (options.parentComponent && !isPlainObject(options.parentComponent)) { throwError( "options.parentComponent should be a valid Vue component options object" @@ -13595,7 +13836,8 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { var parentVm = createInstance(component, mergedOptions, _Vue); - var el = options.attachToDocument ? createElement() : undefined; + var el = + options.attachTo || (options.attachToDocument ? createElement() : undefined); var vm = parentVm.$mount(el); component._Ctor = {}; @@ -13603,7 +13845,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { throwIfInstancesThrew(vm); var wrapperOptions = { - attachedToDocument: !!mergedOptions.attachToDocument + attachedToDocument: !!el }; var root = parentVm.$options._isFunctionalContainer @@ -13678,4 +13920,4 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { return exports; -}({}, Vue, VueTemplateCompiler)); +}({}, Vue, VueTemplateCompiler, testUtils)); diff --git a/packages/test-utils/dist/vue-test-utils.js b/packages/test-utils/dist/vue-test-utils.js index c35a39b7b..693f818b3 100644 --- a/packages/test-utils/dist/vue-test-utils.js +++ b/packages/test-utils/dist/vue-test-utils.js @@ -6,6 +6,7 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau var Vue = _interopDefault(require('vue')); var vueTemplateCompiler = require('vue-template-compiler'); +var testUtils = require('@vue/test-utils'); // @@ -1703,6 +1704,27 @@ var semver_40 = semver.prerelease; var semver_41 = semver.intersects; var semver_42 = semver.coerce; +var NAME_SELECTOR = 'NAME_SELECTOR'; +var COMPONENT_SELECTOR = 'COMPONENT_SELECTOR'; +var REF_SELECTOR = 'REF_SELECTOR'; +var DOM_SELECTOR = 'DOM_SELECTOR'; +var INVALID_SELECTOR = 'INVALID_SELECTOR'; + +var VUE_VERSION = Number( + ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) +); + +var FUNCTIONAL_OPTIONS = + VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions'; + +var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') + ? 'beforeCreate' + : 'beforeMount'; + +var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') + ? '_c' + : '_h'; + // function throwError(msg) { @@ -1786,6 +1808,27 @@ function getCheckedEvent() { return 'change' } +/** + * Normalize nextTick to return a promise for all Vue 2 versions. + * Vue < 2.1 does not return a Promise from nextTick + * @return {Promise} + */ +function nextTick() { + if (VUE_VERSION > 2) { return Vue.nextTick() } + return new Promise(function (resolve) { + Vue.nextTick(resolve); + }) +} + +function warnDeprecated(method, fallback) { + if ( fallback === void 0 ) fallback = ''; + + if (!testUtils.config.showDeprecationWarnings) { return } + var msg = method + " is deprecated and will removed in the next major version"; + if (fallback) { msg += " " + fallback; } + warn(msg); +} + // function addMocks( @@ -1840,27 +1883,6 @@ function addEventLogger(_Vue) { }); } -var NAME_SELECTOR = 'NAME_SELECTOR'; -var COMPONENT_SELECTOR = 'COMPONENT_SELECTOR'; -var REF_SELECTOR = 'REF_SELECTOR'; -var DOM_SELECTOR = 'DOM_SELECTOR'; -var INVALID_SELECTOR = 'INVALID_SELECTOR'; - -var VUE_VERSION = Number( - ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) -); - -var FUNCTIONAL_OPTIONS = - VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions'; - -var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') - ? 'beforeCreate' - : 'beforeMount'; - -var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') - ? '_c' - : '_h'; - function addStubs(_Vue, stubComponents) { var obj; @@ -1984,6 +2006,14 @@ function isPlainObject(c) { return Object.prototype.toString.call(c) === '[object Object]' } +function isHTMLElement(c) { + if (typeof HTMLElement === 'undefined') { + return false + } + // eslint-disable-next-line no-undef + return c instanceof HTMLElement +} + function makeMap(str, expectsLowerCase) { var map = Object.create(null); var list = str.split(','); @@ -2370,7 +2400,7 @@ function createStubFromComponent( // DEPRECATED: converts string stub to template stub. function createStubFromString(templateString, name) { - warn('String stubs are deprecated and will be removed in future versions'); + warnDeprecated('Using a string for stubs'); if (templateContainsComponent(templateString, name)) { throwError('options.stub cannot contain a circular reference'); @@ -2965,6 +2995,10 @@ function mergeOptions( ) { var mocks = (getOption(options.mocks, config.mocks)); var methods = (getOption(options.methods, config.methods)); + if (methods && Object.keys(methods).length) { + warnDeprecated('overwriting methods via the `methods` property'); + } + var provide = (getOption(options.provide, config.provide)); var stubs = (getStubs(options.stubs, config.stubs)); // $FlowIgnore @@ -2976,11 +3010,16 @@ function mergeOptions( } var config = { - stubs: {}, + stubs: { + transition: true, + 'transition-group': true + }, mocks: {}, methods: {}, provide: {}, - silent: true + silent: true, + showDeprecationWarnings: + true }; // @@ -8562,6 +8601,15 @@ WrapperArray.prototype.name = function name () { ); }; +WrapperArray.prototype.overview = function overview () { + this.throwErrorIfWrappersIsEmpty('overview()'); + + throwError( + "overview() must be called on a single wrapper, use at(i) " + + "to access a wrapper" + ); +}; + WrapperArray.prototype.props = function props () { this.throwErrorIfWrappersIsEmpty('props'); @@ -8821,6 +8869,14 @@ ErrorWrapper.prototype.name = function name () { ); }; +ErrorWrapper.prototype.overview = function overview () { + throwError( + ("find did not return " + (buildSelectorString( + this.selector + )) + ", cannot call overview() on empty Wrapper") + ); +}; + ErrorWrapper.prototype.props = function props () { throwError( ("find did not return " + (buildSelectorString( @@ -10328,6 +10384,10 @@ Wrapper.prototype.classes = function classes (className) { * Checks if wrapper contains provided selector. */ Wrapper.prototype.contains = function contains (rawSelector) { + warnDeprecated( + 'contains', + 'Use `wrapper.find`, `wrapper.findComponent` or `wrapper.get` instead' + ); var selector = getSelector(rawSelector, 'contains'); var nodes = find(this.rootNode, this.vm, selector); return nodes.length > 0 @@ -10374,6 +10434,7 @@ Wrapper.prototype.emitted = function emitted ( * Returns an Array containing custom events emitted by the Wrapper vm */ Wrapper.prototype.emittedByOrder = function emittedByOrder () { + warnDeprecated('emittedByOrder', 'Use `wrapper.emitted` instead'); if (!this._emittedByOrder && !this.vm) { throwError( "wrapper.emittedByOrder() can only be called on a Vue instance" @@ -10409,11 +10470,17 @@ Wrapper.prototype.get = function get (rawSelector) { }; /** - * Finds first node in tree of the current wrapper that + * Finds first DOM node in tree of the current wrapper that * matches the provided selector. */ Wrapper.prototype.find = function find$1 (rawSelector) { var selector = getSelector(rawSelector, 'find'); + if (selector.type !== DOM_SELECTOR) { + warnDeprecated( + 'finding components with `find`', + 'Use `findComponent` instead' + ); + } var node = find(this.rootNode, this.vm, selector)[0]; if (!node) { @@ -10426,13 +10493,68 @@ Wrapper.prototype.find = function find$1 (rawSelector) { }; /** - * Finds node in tree of the current wrapper that matches + * Finds DOM elements in tree of the current wrapper that matches * the provided selector. */ Wrapper.prototype.findAll = function findAll (rawSelector) { var this$1 = this; var selector = getSelector(rawSelector, 'findAll'); + if (selector.type !== DOM_SELECTOR) { + warnDeprecated( + 'finding components with `findAll`', + 'Use `findAllComponents` instead' + ); + } + var nodes = find(this.rootNode, this.vm, selector); + var wrappers = nodes.map(function (node) { + // Using CSS Selector, returns a VueWrapper instance if the root element + // binds a Vue instance. + var wrapper = createWrapper(node, this$1.options); + wrapper.selector = rawSelector; + return wrapper + }); + + var wrapperArray = new WrapperArray(wrappers); + wrapperArray.selector = rawSelector; + return wrapperArray +}; + +/** + * Finds first component in tree of the current wrapper that + * matches the provided selector. + */ +Wrapper.prototype.findComponent = function findComponent (rawSelector) { + var selector = getSelector(rawSelector, 'findComponent'); + if (selector.type === DOM_SELECTOR) { + throwError( + 'findComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead' + ); + } + var node = find(this.rootNode, this.vm, selector)[0]; + + if (!node) { + return new ErrorWrapper(rawSelector) + } + + var wrapper = createWrapper(node, this.options); + wrapper.selector = rawSelector; + return wrapper +}; + +/** + * Finds components in tree of the current wrapper that matches + * the provided selector. + */ +Wrapper.prototype.findAllComponents = function findAllComponents (rawSelector) { + var this$1 = this; + + var selector = getSelector(rawSelector, 'findAll'); + if (selector.type === DOM_SELECTOR) { + throwError( + 'findAllComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead' + ); + } var nodes = find(this.rootNode, this.vm, selector); var wrappers = nodes.map(function (node) { // Using CSS Selector, returns a VueWrapper instance if the root element @@ -10458,6 +10580,7 @@ Wrapper.prototype.html = function html () { * Checks if node matches selector */ Wrapper.prototype.is = function is (rawSelector) { + warnDeprecated('is', 'Use element.tagName instead'); var selector = getSelector(rawSelector, 'is'); if (selector.type === REF_SELECTOR) { @@ -10471,6 +10594,10 @@ Wrapper.prototype.is = function is (rawSelector) { * Checks if node is empty */ Wrapper.prototype.isEmpty = function isEmpty () { + warnDeprecated( + 'isEmpty', + 'Consider a custom matcher such as those provided in jest-dom: https://github.com/testing-library/jest-dom#tobeempty' + ); if (!this.vnode) { return this.element.innerHTML === '' } @@ -10495,6 +10622,10 @@ Wrapper.prototype.isEmpty = function isEmpty () { * Checks if node is visible */ Wrapper.prototype.isVisible = function isVisible () { + warnDeprecated( + 'isEmpty', + "Consider a custom matcher such as those provided in jest-dom: https://github.com/testing-library/jest-dom#tobevisible" + ); var element = this.element; while (element) { if ( @@ -10515,6 +10646,7 @@ Wrapper.prototype.isVisible = function isVisible () { * Checks if wrapper is a vue instance */ Wrapper.prototype.isVueInstance = function isVueInstance () { + warnDeprecated("isVueInstance"); return !!this.vm }; @@ -10522,6 +10654,8 @@ Wrapper.prototype.isVueInstance = function isVueInstance () { * Returns name of component, or tag name if node is not a Vue component */ Wrapper.prototype.name = function name () { + warnDeprecated("name"); + if (this.vm) { return ( this.vm.$options.name || @@ -10537,6 +10671,86 @@ Wrapper.prototype.name = function name () { return this.vnode.tag }; +/** + * Prints a simple overview of the wrapper current state + * with useful information for debugging + */ +Wrapper.prototype.overview = function overview () { + var this$1 = this; + + warnDeprecated("overview"); + + if (!this.isVueInstance()) { + throwError("wrapper.overview() can only be called on a Vue instance"); + } + + var identation = 4; + var formatJSON = function (json, replacer) { + if ( replacer === void 0 ) replacer = null; + + return JSON.stringify(json, replacer, identation).replace(/"/g, ''); + }; + + var visibility = this.isVisible() ? 'Visible' : 'Not visible'; + + var html = this.html() + ? this.html().replace(/^(?!\s*$)/gm, ' '.repeat(identation)) + '\n' + : ''; + + // $FlowIgnore + var data = formatJSON(this.vm.$data); + + /* eslint-disable operator-linebreak */ + // $FlowIgnore + var computed = this.vm._computedWatchers + ? formatJSON.apply( + // $FlowIgnore + void 0, Object.keys(this.vm._computedWatchers).map(function (computedKey) { + var obj; + + return (( obj = {}, obj[computedKey] = this$1.vm[computedKey], obj )); + }) + ) + : // $FlowIgnore + this.vm.$options.computed + ? formatJSON.apply( + // $FlowIgnore + void 0, Object.entries(this.vm.$options.computed).map(function (ref) { + var obj; + + var key = ref[0]; + var value = ref[1]; + return (( obj = {}, obj[key] = value(), obj )); + }) + ) + : '{}'; + /* eslint-enable operator-linebreak */ + + var emittedJSONReplacer = function (key, value) { return value instanceof Array + ? value.map(function (calledWith, index) { + var callParams = calledWith.map(function (param) { return typeof param === 'object' + ? JSON.stringify(param) + .replace(/"/g, '') + .replace(/,/g, ', ') + : param; } + ); + + return (index + ": [ " + (callParams.join(', ')) + " ]") + }) + : value; }; + + var emitted = formatJSON(this.emitted(), emittedJSONReplacer); + + console.log( + '\n' + + "Wrapper (" + visibility + "):\n\n" + + "Html:\n" + html + "\n" + + "Data: " + data + "\n\n" + + "Computed: " + computed + "\n\n" + + "Emitted: " + emitted + "\n" + ); +}; + /** * Returns an Object containing the prop name/value pairs on the element */ @@ -10576,6 +10790,11 @@ Wrapper.prototype.props = function props (key) { Wrapper.prototype.setChecked = function setChecked (checked) { if ( checked === void 0 ) checked = true; + warnDeprecated( + "setChecked", + 'When you migrate to VTU 2, use setValue instead.' + ); + if (typeof checked !== 'boolean') { throwError('wrapper.setChecked() must be passed a boolean'); } @@ -10586,14 +10805,13 @@ Wrapper.prototype.setChecked = function setChecked (checked) { if (tagName === 'INPUT' && type === 'checkbox') { if (this.element.checked === checked) { - return + return nextTick() } if (event !== 'click' || isPhantomJS) { // $FlowIgnore this.element.checked = checked; } - this.trigger(event); - return + return this.trigger(event) } if (tagName === 'INPUT' && type === 'radio') { @@ -10605,24 +10823,29 @@ Wrapper.prototype.setChecked = function setChecked (checked) { } if (this.element.checked === checked) { - return + return nextTick() } if (event !== 'click' || isPhantomJS) { // $FlowIgnore this.element.selected = true; } - this.trigger(event); - return + return this.trigger(event) } throwError("wrapper.setChecked() cannot be called on this element"); + return nextTick() }; /** * Selects element */ Wrapper.prototype.setSelected = function setSelected () { + warnDeprecated( + "setSelected", + 'When you migrate to VTU 2, use setValue instead.' + ); + var tagName = this.element.tagName; if (tagName === 'SELECT') { @@ -10632,28 +10855,27 @@ Wrapper.prototype.setSelected = function setSelected () { ); } - if (tagName === 'OPTION') { - if (this.element.selected) { - return - } + if (tagName !== 'OPTION') { + throwError("wrapper.setSelected() cannot be called on this element"); + } - // $FlowIgnore - this.element.selected = true; - // $FlowIgnore - var parentElement = this.element.parentElement; + if (this.element.selected) { + return nextTick() + } - // $FlowIgnore - if (parentElement.tagName === 'OPTGROUP') { - // $FlowIgnore - parentElement = parentElement.parentElement; - } + // $FlowIgnore + this.element.selected = true; + // $FlowIgnore + var parentElement = this.element.parentElement; + // $FlowIgnore + if (parentElement.tagName === 'OPTGROUP') { // $FlowIgnore - createWrapper(parentElement, this.options).trigger('change'); - return + parentElement = parentElement.parentElement; } - throwError("wrapper.setSelected() cannot be called on this element"); + // $FlowIgnore + return createWrapper(parentElement, this.options).trigger('change') }; /** @@ -10669,6 +10891,7 @@ Wrapper.prototype.setData = function setData (data) { } recursivelySetData(this.vm, this.vm, data); + return nextTick() }; /** @@ -10677,6 +10900,8 @@ Wrapper.prototype.setData = function setData (data) { Wrapper.prototype.setMethods = function setMethods (methods) { var this$1 = this; + warnDeprecated("setMethods"); + if (!this.isVueInstance()) { throwError("wrapper.setMethods() can only be called on a Vue instance"); } @@ -10738,7 +10963,7 @@ Wrapper.prototype.setProps = function setProps (data) { if (VUE_VERSION > 2.3) { // $FlowIgnore : Problem with possibly null this.vm this$1.vm.$attrs[key] = data[key]; - return + return nextTick() } throwError( "wrapper.setProps() called with " + key + " property which " + @@ -10753,6 +10978,7 @@ Wrapper.prototype.setProps = function setProps (data) { // $FlowIgnore : Problem with possibly null this.vm this.vm.$forceUpdate(); + return nextTick() } catch (err) { throw err } finally { @@ -10808,9 +11034,10 @@ Wrapper.prototype.setValue = function setValue (value) { ) { this.trigger('change'); } - } else { - throwError("wrapper.setValue() cannot be called on this element"); + return nextTick() } + throwError("wrapper.setValue() cannot be called on this element"); + return nextTick() }; /** @@ -10857,11 +11084,12 @@ Wrapper.prototype.trigger = function trigger (type, options) { var tagName = this.element.tagName; if (this.attributes().disabled && supportedTags.indexOf(tagName) > -1) { - return + return nextTick() } var event = createDOMEvent(type, options); this.element.dispatchEvent(event); + return nextTick() }; // @@ -12897,9 +13125,9 @@ var DataView = _getNative(_root, 'DataView'); var _DataView = DataView; /* Built-in method references that are verified to be native. */ -var Promise = _getNative(_root, 'Promise'); +var Promise$1 = _getNative(_root, 'Promise'); -var _Promise = Promise; +var _Promise = Promise$1; /* Built-in method references that are verified to be native. */ var Set$1 = _getNative(_root, 'Set'); @@ -13545,6 +13773,20 @@ function vueExtendUnsupportedOption(option) { var UNSUPPORTED_VERSION_OPTIONS = ['mocks', 'stubs', 'localVue']; function validateOptions(options, component) { + if ( + options.attachTo && + !isHTMLElement(options.attachTo) && + !isDomSelector(options.attachTo) + ) { + throwError( + "options.attachTo should be a valid HTMLElement or CSS selector string" + ); + } + if ('attachToDocument' in options) { + warn( + "options.attachToDocument is deprecated in favor of options.attachTo and will be removed in a future release" + ); + } if (options.parentComponent && !isPlainObject(options.parentComponent)) { throwError( "options.parentComponent should be a valid Vue component options object" @@ -13599,7 +13841,8 @@ function mount(component, options) { var parentVm = createInstance(component, mergedOptions, _Vue); - var el = options.attachToDocument ? createElement() : undefined; + var el = + options.attachTo || (options.attachToDocument ? createElement() : undefined); var vm = parentVm.$mount(el); component._Ctor = {}; @@ -13607,7 +13850,7 @@ function mount(component, options) { throwIfInstancesThrew(vm); var wrapperOptions = { - attachedToDocument: !!mergedOptions.attachToDocument + attachedToDocument: !!el }; var root = parentVm.$options._isFunctionalContainer diff --git a/packages/test-utils/dist/vue-test-utils.umd.js b/packages/test-utils/dist/vue-test-utils.umd.js index c1b688e49..844962f94 100644 --- a/packages/test-utils/dist/vue-test-utils.umd.js +++ b/packages/test-utils/dist/vue-test-utils.umd.js @@ -1,8 +1,8 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue'), require('vue-template-compiler')) : - typeof define === 'function' && define.amd ? define(['exports', 'vue', 'vue-template-compiler'], factory) : - (global = global || self, factory(global.VueTestUtils = {}, global.Vue, global.VueTemplateCompiler)); -}(this, (function (exports, Vue, vueTemplateCompiler) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue'), require('vue-template-compiler'), require('@vue/test-utils')) : + typeof define === 'function' && define.amd ? define(['exports', 'vue', 'vue-template-compiler', '@vue/test-utils'], factory) : + (global = global || self, factory(global.VueTestUtils = {}, global.Vue, global.VueTemplateCompiler, global.testUtils)); +}(this, (function (exports, Vue, vueTemplateCompiler, testUtils) { 'use strict'; Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue; @@ -1702,6 +1702,27 @@ var semver_41 = semver.intersects; var semver_42 = semver.coerce; + var NAME_SELECTOR = 'NAME_SELECTOR'; + var COMPONENT_SELECTOR = 'COMPONENT_SELECTOR'; + var REF_SELECTOR = 'REF_SELECTOR'; + var DOM_SELECTOR = 'DOM_SELECTOR'; + var INVALID_SELECTOR = 'INVALID_SELECTOR'; + + var VUE_VERSION = Number( + ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) + ); + + var FUNCTIONAL_OPTIONS = + VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions'; + + var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') + ? 'beforeCreate' + : 'beforeMount'; + + var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') + ? '_c' + : '_h'; + // function throwError(msg) { @@ -1785,6 +1806,27 @@ return 'change' } + /** + * Normalize nextTick to return a promise for all Vue 2 versions. + * Vue < 2.1 does not return a Promise from nextTick + * @return {Promise} + */ + function nextTick() { + if (VUE_VERSION > 2) { return Vue.nextTick() } + return new Promise(function (resolve) { + Vue.nextTick(resolve); + }) + } + + function warnDeprecated(method, fallback) { + if ( fallback === void 0 ) fallback = ''; + + if (!testUtils.config.showDeprecationWarnings) { return } + var msg = method + " is deprecated and will removed in the next major version"; + if (fallback) { msg += " " + fallback; } + warn(msg); + } + // function addMocks( @@ -1839,27 +1881,6 @@ }); } - var NAME_SELECTOR = 'NAME_SELECTOR'; - var COMPONENT_SELECTOR = 'COMPONENT_SELECTOR'; - var REF_SELECTOR = 'REF_SELECTOR'; - var DOM_SELECTOR = 'DOM_SELECTOR'; - var INVALID_SELECTOR = 'INVALID_SELECTOR'; - - var VUE_VERSION = Number( - ((Vue.version.split('.')[0]) + "." + (Vue.version.split('.')[1])) - ); - - var FUNCTIONAL_OPTIONS = - VUE_VERSION >= 2.5 ? 'fnOptions' : 'functionalOptions'; - - var BEFORE_RENDER_LIFECYCLE_HOOK = semver.gt(Vue.version, '2.1.8') - ? 'beforeCreate' - : 'beforeMount'; - - var CREATE_ELEMENT_ALIAS = semver.gt(Vue.version, '2.1.5') - ? '_c' - : '_h'; - function addStubs(_Vue, stubComponents) { var obj; @@ -1983,6 +2004,14 @@ return Object.prototype.toString.call(c) === '[object Object]' } + function isHTMLElement(c) { + if (typeof HTMLElement === 'undefined') { + return false + } + // eslint-disable-next-line no-undef + return c instanceof HTMLElement + } + function makeMap(str, expectsLowerCase) { var map = Object.create(null); var list = str.split(','); @@ -2369,7 +2398,7 @@ // DEPRECATED: converts string stub to template stub. function createStubFromString(templateString, name) { - warn('String stubs are deprecated and will be removed in future versions'); + warnDeprecated('Using a string for stubs'); if (templateContainsComponent(templateString, name)) { throwError('options.stub cannot contain a circular reference'); @@ -2964,6 +2993,10 @@ ) { var mocks = (getOption(options.mocks, config.mocks)); var methods = (getOption(options.methods, config.methods)); + if (methods && Object.keys(methods).length) { + warnDeprecated('overwriting methods via the `methods` property'); + } + var provide = (getOption(options.provide, config.provide)); var stubs = (getStubs(options.stubs, config.stubs)); // $FlowIgnore @@ -2975,11 +3008,16 @@ } var config = { - stubs: {}, + stubs: { + transition: true, + 'transition-group': true + }, mocks: {}, methods: {}, provide: {}, - silent: true + silent: true, + showDeprecationWarnings: + true }; // @@ -8561,6 +8599,15 @@ ); }; + WrapperArray.prototype.overview = function overview () { + this.throwErrorIfWrappersIsEmpty('overview()'); + + throwError( + "overview() must be called on a single wrapper, use at(i) " + + "to access a wrapper" + ); + }; + WrapperArray.prototype.props = function props () { this.throwErrorIfWrappersIsEmpty('props'); @@ -8820,6 +8867,14 @@ ); }; + ErrorWrapper.prototype.overview = function overview () { + throwError( + ("find did not return " + (buildSelectorString( + this.selector + )) + ", cannot call overview() on empty Wrapper") + ); + }; + ErrorWrapper.prototype.props = function props () { throwError( ("find did not return " + (buildSelectorString( @@ -10327,6 +10382,10 @@ * Checks if wrapper contains provided selector. */ Wrapper.prototype.contains = function contains (rawSelector) { + warnDeprecated( + 'contains', + 'Use `wrapper.find`, `wrapper.findComponent` or `wrapper.get` instead' + ); var selector = getSelector(rawSelector, 'contains'); var nodes = find(this.rootNode, this.vm, selector); return nodes.length > 0 @@ -10373,6 +10432,7 @@ * Returns an Array containing custom events emitted by the Wrapper vm */ Wrapper.prototype.emittedByOrder = function emittedByOrder () { + warnDeprecated('emittedByOrder', 'Use `wrapper.emitted` instead'); if (!this._emittedByOrder && !this.vm) { throwError( "wrapper.emittedByOrder() can only be called on a Vue instance" @@ -10408,11 +10468,17 @@ }; /** - * Finds first node in tree of the current wrapper that + * Finds first DOM node in tree of the current wrapper that * matches the provided selector. */ Wrapper.prototype.find = function find$1 (rawSelector) { var selector = getSelector(rawSelector, 'find'); + if (selector.type !== DOM_SELECTOR) { + warnDeprecated( + 'finding components with `find`', + 'Use `findComponent` instead' + ); + } var node = find(this.rootNode, this.vm, selector)[0]; if (!node) { @@ -10425,13 +10491,68 @@ }; /** - * Finds node in tree of the current wrapper that matches + * Finds DOM elements in tree of the current wrapper that matches * the provided selector. */ Wrapper.prototype.findAll = function findAll (rawSelector) { var this$1 = this; var selector = getSelector(rawSelector, 'findAll'); + if (selector.type !== DOM_SELECTOR) { + warnDeprecated( + 'finding components with `findAll`', + 'Use `findAllComponents` instead' + ); + } + var nodes = find(this.rootNode, this.vm, selector); + var wrappers = nodes.map(function (node) { + // Using CSS Selector, returns a VueWrapper instance if the root element + // binds a Vue instance. + var wrapper = createWrapper(node, this$1.options); + wrapper.selector = rawSelector; + return wrapper + }); + + var wrapperArray = new WrapperArray(wrappers); + wrapperArray.selector = rawSelector; + return wrapperArray + }; + + /** + * Finds first component in tree of the current wrapper that + * matches the provided selector. + */ + Wrapper.prototype.findComponent = function findComponent (rawSelector) { + var selector = getSelector(rawSelector, 'findComponent'); + if (selector.type === DOM_SELECTOR) { + throwError( + 'findComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead' + ); + } + var node = find(this.rootNode, this.vm, selector)[0]; + + if (!node) { + return new ErrorWrapper(rawSelector) + } + + var wrapper = createWrapper(node, this.options); + wrapper.selector = rawSelector; + return wrapper + }; + + /** + * Finds components in tree of the current wrapper that matches + * the provided selector. + */ + Wrapper.prototype.findAllComponents = function findAllComponents (rawSelector) { + var this$1 = this; + + var selector = getSelector(rawSelector, 'findAll'); + if (selector.type === DOM_SELECTOR) { + throwError( + 'findAllComponent requires a Vue constructor or valid find object. If you are searching for DOM nodes, use `find` instead' + ); + } var nodes = find(this.rootNode, this.vm, selector); var wrappers = nodes.map(function (node) { // Using CSS Selector, returns a VueWrapper instance if the root element @@ -10457,6 +10578,7 @@ * Checks if node matches selector */ Wrapper.prototype.is = function is (rawSelector) { + warnDeprecated('is', 'Use element.tagName instead'); var selector = getSelector(rawSelector, 'is'); if (selector.type === REF_SELECTOR) { @@ -10470,6 +10592,10 @@ * Checks if node is empty */ Wrapper.prototype.isEmpty = function isEmpty () { + warnDeprecated( + 'isEmpty', + 'Consider a custom matcher such as those provided in jest-dom: https://github.com/testing-library/jest-dom#tobeempty' + ); if (!this.vnode) { return this.element.innerHTML === '' } @@ -10494,6 +10620,10 @@ * Checks if node is visible */ Wrapper.prototype.isVisible = function isVisible () { + warnDeprecated( + 'isEmpty', + "Consider a custom matcher such as those provided in jest-dom: https://github.com/testing-library/jest-dom#tobevisible" + ); var element = this.element; while (element) { if ( @@ -10514,6 +10644,7 @@ * Checks if wrapper is a vue instance */ Wrapper.prototype.isVueInstance = function isVueInstance () { + warnDeprecated("isVueInstance"); return !!this.vm }; @@ -10521,6 +10652,8 @@ * Returns name of component, or tag name if node is not a Vue component */ Wrapper.prototype.name = function name () { + warnDeprecated("name"); + if (this.vm) { return ( this.vm.$options.name || @@ -10536,6 +10669,86 @@ return this.vnode.tag }; + /** + * Prints a simple overview of the wrapper current state + * with useful information for debugging + */ + Wrapper.prototype.overview = function overview () { + var this$1 = this; + + warnDeprecated("overview"); + + if (!this.isVueInstance()) { + throwError("wrapper.overview() can only be called on a Vue instance"); + } + + var identation = 4; + var formatJSON = function (json, replacer) { + if ( replacer === void 0 ) replacer = null; + + return JSON.stringify(json, replacer, identation).replace(/"/g, ''); + }; + + var visibility = this.isVisible() ? 'Visible' : 'Not visible'; + + var html = this.html() + ? this.html().replace(/^(?!\s*$)/gm, ' '.repeat(identation)) + '\n' + : ''; + + // $FlowIgnore + var data = formatJSON(this.vm.$data); + + /* eslint-disable operator-linebreak */ + // $FlowIgnore + var computed = this.vm._computedWatchers + ? formatJSON.apply( + // $FlowIgnore + void 0, Object.keys(this.vm._computedWatchers).map(function (computedKey) { + var obj; + + return (( obj = {}, obj[computedKey] = this$1.vm[computedKey], obj )); + }) + ) + : // $FlowIgnore + this.vm.$options.computed + ? formatJSON.apply( + // $FlowIgnore + void 0, Object.entries(this.vm.$options.computed).map(function (ref) { + var obj; + + var key = ref[0]; + var value = ref[1]; + return (( obj = {}, obj[key] = value(), obj )); + }) + ) + : '{}'; + /* eslint-enable operator-linebreak */ + + var emittedJSONReplacer = function (key, value) { return value instanceof Array + ? value.map(function (calledWith, index) { + var callParams = calledWith.map(function (param) { return typeof param === 'object' + ? JSON.stringify(param) + .replace(/"/g, '') + .replace(/,/g, ', ') + : param; } + ); + + return (index + ": [ " + (callParams.join(', ')) + " ]") + }) + : value; }; + + var emitted = formatJSON(this.emitted(), emittedJSONReplacer); + + console.log( + '\n' + + "Wrapper (" + visibility + "):\n\n" + + "Html:\n" + html + "\n" + + "Data: " + data + "\n\n" + + "Computed: " + computed + "\n\n" + + "Emitted: " + emitted + "\n" + ); + }; + /** * Returns an Object containing the prop name/value pairs on the element */ @@ -10575,6 +10788,11 @@ Wrapper.prototype.setChecked = function setChecked (checked) { if ( checked === void 0 ) checked = true; + warnDeprecated( + "setChecked", + 'When you migrate to VTU 2, use setValue instead.' + ); + if (typeof checked !== 'boolean') { throwError('wrapper.setChecked() must be passed a boolean'); } @@ -10585,14 +10803,13 @@ if (tagName === 'INPUT' && type === 'checkbox') { if (this.element.checked === checked) { - return + return nextTick() } if (event !== 'click' || isPhantomJS) { // $FlowIgnore this.element.checked = checked; } - this.trigger(event); - return + return this.trigger(event) } if (tagName === 'INPUT' && type === 'radio') { @@ -10604,24 +10821,29 @@ } if (this.element.checked === checked) { - return + return nextTick() } if (event !== 'click' || isPhantomJS) { // $FlowIgnore this.element.selected = true; } - this.trigger(event); - return + return this.trigger(event) } throwError("wrapper.setChecked() cannot be called on this element"); + return nextTick() }; /** * Selects element */ Wrapper.prototype.setSelected = function setSelected () { + warnDeprecated( + "setSelected", + 'When you migrate to VTU 2, use setValue instead.' + ); + var tagName = this.element.tagName; if (tagName === 'SELECT') { @@ -10631,28 +10853,27 @@ ); } - if (tagName === 'OPTION') { - if (this.element.selected) { - return - } + if (tagName !== 'OPTION') { + throwError("wrapper.setSelected() cannot be called on this element"); + } - // $FlowIgnore - this.element.selected = true; - // $FlowIgnore - var parentElement = this.element.parentElement; + if (this.element.selected) { + return nextTick() + } - // $FlowIgnore - if (parentElement.tagName === 'OPTGROUP') { - // $FlowIgnore - parentElement = parentElement.parentElement; - } + // $FlowIgnore + this.element.selected = true; + // $FlowIgnore + var parentElement = this.element.parentElement; + // $FlowIgnore + if (parentElement.tagName === 'OPTGROUP') { // $FlowIgnore - createWrapper(parentElement, this.options).trigger('change'); - return + parentElement = parentElement.parentElement; } - throwError("wrapper.setSelected() cannot be called on this element"); + // $FlowIgnore + return createWrapper(parentElement, this.options).trigger('change') }; /** @@ -10668,6 +10889,7 @@ } recursivelySetData(this.vm, this.vm, data); + return nextTick() }; /** @@ -10676,6 +10898,8 @@ Wrapper.prototype.setMethods = function setMethods (methods) { var this$1 = this; + warnDeprecated("setMethods"); + if (!this.isVueInstance()) { throwError("wrapper.setMethods() can only be called on a Vue instance"); } @@ -10737,7 +10961,7 @@ if (VUE_VERSION > 2.3) { // $FlowIgnore : Problem with possibly null this.vm this$1.vm.$attrs[key] = data[key]; - return + return nextTick() } throwError( "wrapper.setProps() called with " + key + " property which " + @@ -10752,6 +10976,7 @@ // $FlowIgnore : Problem with possibly null this.vm this.vm.$forceUpdate(); + return nextTick() } catch (err) { throw err } finally { @@ -10807,9 +11032,10 @@ ) { this.trigger('change'); } - } else { - throwError("wrapper.setValue() cannot be called on this element"); + return nextTick() } + throwError("wrapper.setValue() cannot be called on this element"); + return nextTick() }; /** @@ -10856,11 +11082,12 @@ var tagName = this.element.tagName; if (this.attributes().disabled && supportedTags.indexOf(tagName) > -1) { - return + return nextTick() } var event = createDOMEvent(type, options); this.element.dispatchEvent(event); + return nextTick() }; // @@ -12896,9 +13123,9 @@ var _DataView = DataView; /* Built-in method references that are verified to be native. */ - var Promise = _getNative(_root, 'Promise'); + var Promise$1 = _getNative(_root, 'Promise'); - var _Promise = Promise; + var _Promise = Promise$1; /* Built-in method references that are verified to be native. */ var Set$1 = _getNative(_root, 'Set'); @@ -13544,6 +13771,20 @@ var UNSUPPORTED_VERSION_OPTIONS = ['mocks', 'stubs', 'localVue']; function validateOptions(options, component) { + if ( + options.attachTo && + !isHTMLElement(options.attachTo) && + !isDomSelector(options.attachTo) + ) { + throwError( + "options.attachTo should be a valid HTMLElement or CSS selector string" + ); + } + if ('attachToDocument' in options) { + warn( + "options.attachToDocument is deprecated in favor of options.attachTo and will be removed in a future release" + ); + } if (options.parentComponent && !isPlainObject(options.parentComponent)) { throwError( "options.parentComponent should be a valid Vue component options object" @@ -13598,7 +13839,8 @@ var parentVm = createInstance(component, mergedOptions, _Vue); - var el = options.attachToDocument ? createElement() : undefined; + var el = + options.attachTo || (options.attachToDocument ? createElement() : undefined); var vm = parentVm.$mount(el); component._Ctor = {}; @@ -13606,7 +13848,7 @@ throwIfInstancesThrew(vm); var wrapperOptions = { - attachedToDocument: !!mergedOptions.attachToDocument + attachedToDocument: !!el }; var root = parentVm.$options._isFunctionalContainer