From e49099684321552530b7651ef8985f101829a765 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 27 Jan 2023 12:06:27 +1000 Subject: [PATCH 01/12] chore: publish 1.3.4 --- lerna.json | 2 +- .../dist/vue-server-test-utils.js | 32 ++++++++++++------- .../test-utils/dist/vue-test-utils.esm.js | 32 ++++++++++++------- .../test-utils/dist/vue-test-utils.iife.js | 32 ++++++++++++------- packages/test-utils/dist/vue-test-utils.js | 32 ++++++++++++------- .../test-utils/dist/vue-test-utils.umd.js | 32 ++++++++++++------- 6 files changed, 106 insertions(+), 56 deletions(-) diff --git a/lerna.json b/lerna.json index 77f280927..d65541fe6 100644 --- a/lerna.json +++ b/lerna.json @@ -5,5 +5,5 @@ ], "npmClient": "yarn", "useWorkspaces": true, - "version": "1.3.3" + "version": "1.3.4" } 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 4190a0872..6e9298823 100644 --- a/packages/server-test-utils/dist/vue-server-test-utils.js +++ b/packages/server-test-utils/dist/vue-server-test-utils.js @@ -1750,7 +1750,7 @@ function isVueComponent(c) { return true } - if (c === null || typeof c !== 'object') { + if (!isPlainObject(c)) { return false } @@ -1780,7 +1780,7 @@ function componentNeedsCompiling(component) { function isRefSelector(refOptionsObject) { if ( - typeof refOptionsObject !== 'object' || + !isPlainObject(refOptionsObject) || Object.keys(refOptionsObject || {}).length !== 1 ) { return false @@ -1790,7 +1790,7 @@ function isRefSelector(refOptionsObject) { } function isNameSelector(nameOptionsObject) { - if (typeof nameOptionsObject !== 'object' || nameOptionsObject === null) { + if (!isPlainObject(nameOptionsObject)) { return false } @@ -1806,7 +1806,7 @@ function isDynamicComponent(c) { } function isComponentOptions(c) { - return c !== null && typeof c === 'object' && (c.template || c.render) + return isPlainObject(c) && (c.template || c.render) } function isFunctionalComponent(c) { @@ -8084,7 +8084,10 @@ ErrorWrapper.prototype.destroy = function destroy () { */ function isStyleVisible(element) { - if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { + if ( + !(element instanceof window.HTMLElement) && + !(element instanceof window.SVGElement) + ) { return false } @@ -10187,8 +10190,7 @@ Wrapper.prototype.setProps = function setProps (data) { Object.keys(data).forEach(function (key) { // Don't let people set entire objects, because reactivity won't work if ( - typeof data[key] === 'object' && - data[key] !== null && + isPlainObject(data[key]) && // $FlowIgnore : Problem with possibly null this.vm data[key] === this$1.vm[key] ) { @@ -13478,10 +13480,10 @@ function resolveComponent$1(obj, component) { ) } -function getCoreProperties(componentOptions) { +function getCoreProperties(componentOptions, name) { return { attrs: componentOptions.attrs, - name: componentOptions.name, + name: componentOptions.name || name, model: componentOptions.model, props: componentOptions.props, on: componentOptions.on, @@ -13542,7 +13544,7 @@ function createStubFromComponent( Vue__default['default'].config.ignoredElements.push(tagName); } - return Object.assign({}, getCoreProperties(componentOptions), + return Object.assign({}, getCoreProperties(componentOptions, name), {$_vueTestUtils_original: originalComponent, $_doNotStubChildren: true, render: function render(h, context) { @@ -13723,8 +13725,16 @@ function patchCreateElement(_Vue, stubs, stubAllComponents) { } if (isConstructor(el) || isComponentOptions(el)) { + var componentOptions = isConstructor(el) ? el.options : el; + var elName = componentOptions.name; + + var stubbedComponent = resolveComponent(elName, stubs); + if (stubbedComponent) { + return originalCreateElement.apply(void 0, [ stubbedComponent ].concat( args )) + } + if (stubAllComponents) { - var stub = createStubFromComponent(el, el.name || 'anonymous', _Vue); + var stub = createStubFromComponent(el, elName || 'anonymous', _Vue); return originalCreateElement.apply(void 0, [ stub ].concat( args )) } var Constructor = shouldExtend(el) ? extend(el, _Vue) : el; diff --git a/packages/test-utils/dist/vue-test-utils.esm.js b/packages/test-utils/dist/vue-test-utils.esm.js index ec5ae5f13..1be3a1bb8 100644 --- a/packages/test-utils/dist/vue-test-utils.esm.js +++ b/packages/test-utils/dist/vue-test-utils.esm.js @@ -1897,7 +1897,7 @@ function isVueComponent(c) { return true } - if (c === null || typeof c !== 'object') { + if (!isPlainObject(c)) { return false } @@ -1927,7 +1927,7 @@ function componentNeedsCompiling(component) { function isRefSelector(refOptionsObject) { if ( - typeof refOptionsObject !== 'object' || + !isPlainObject(refOptionsObject) || Object.keys(refOptionsObject || {}).length !== 1 ) { return false @@ -1937,7 +1937,7 @@ function isRefSelector(refOptionsObject) { } function isNameSelector(nameOptionsObject) { - if (typeof nameOptionsObject !== 'object' || nameOptionsObject === null) { + if (!isPlainObject(nameOptionsObject)) { return false } @@ -1953,7 +1953,7 @@ function isDynamicComponent(c) { } function isComponentOptions(c) { - return c !== null && typeof c === 'object' && (c.template || c.render) + return isPlainObject(c) && (c.template || c.render) } function isFunctionalComponent(c) { @@ -2263,10 +2263,10 @@ function resolveComponent$1(obj, component) { ) } -function getCoreProperties(componentOptions) { +function getCoreProperties(componentOptions, name) { return { attrs: componentOptions.attrs, - name: componentOptions.name, + name: componentOptions.name || name, model: componentOptions.model, props: componentOptions.props, on: componentOptions.on, @@ -2327,7 +2327,7 @@ function createStubFromComponent( Vue.config.ignoredElements.push(tagName); } - return Object.assign({}, getCoreProperties(componentOptions), + return Object.assign({}, getCoreProperties(componentOptions, name), {$_vueTestUtils_original: originalComponent, $_doNotStubChildren: true, render: function render(h, context) { @@ -2508,8 +2508,16 @@ function patchCreateElement(_Vue, stubs, stubAllComponents) { } if (isConstructor(el) || isComponentOptions(el)) { + var componentOptions = isConstructor(el) ? el.options : el; + var elName = componentOptions.name; + + var stubbedComponent = resolveComponent(elName, stubs); + if (stubbedComponent) { + return originalCreateElement.apply(void 0, [ stubbedComponent ].concat( args )) + } + if (stubAllComponents) { - var stub = createStubFromComponent(el, el.name || 'anonymous', _Vue); + var stub = createStubFromComponent(el, elName || 'anonymous', _Vue); return originalCreateElement.apply(void 0, [ stub ].concat( args )) } var Constructor = shouldExtend(el) ? extend(el, _Vue) : el; @@ -9011,7 +9019,10 @@ ErrorWrapper.prototype.destroy = function destroy () { */ function isStyleVisible(element) { - if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { + if ( + !(element instanceof window.HTMLElement) && + !(element instanceof window.SVGElement) + ) { return false } @@ -11114,8 +11125,7 @@ Wrapper.prototype.setProps = function setProps (data) { Object.keys(data).forEach(function (key) { // Don't let people set entire objects, because reactivity won't work if ( - typeof data[key] === 'object' && - data[key] !== null && + isPlainObject(data[key]) && // $FlowIgnore : Problem with possibly null this.vm data[key] === this$1.vm[key] ) { diff --git a/packages/test-utils/dist/vue-test-utils.iife.js b/packages/test-utils/dist/vue-test-utils.iife.js index d2ad58734..30744f78a 100644 --- a/packages/test-utils/dist/vue-test-utils.iife.js +++ b/packages/test-utils/dist/vue-test-utils.iife.js @@ -1901,7 +1901,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { return true } - if (c === null || typeof c !== 'object') { + if (!isPlainObject(c)) { return false } @@ -1931,7 +1931,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { function isRefSelector(refOptionsObject) { if ( - typeof refOptionsObject !== 'object' || + !isPlainObject(refOptionsObject) || Object.keys(refOptionsObject || {}).length !== 1 ) { return false @@ -1941,7 +1941,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { } function isNameSelector(nameOptionsObject) { - if (typeof nameOptionsObject !== 'object' || nameOptionsObject === null) { + if (!isPlainObject(nameOptionsObject)) { return false } @@ -1957,7 +1957,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { } function isComponentOptions(c) { - return c !== null && typeof c === 'object' && (c.template || c.render) + return isPlainObject(c) && (c.template || c.render) } function isFunctionalComponent(c) { @@ -2267,10 +2267,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { ) } - function getCoreProperties(componentOptions) { + function getCoreProperties(componentOptions, name) { return { attrs: componentOptions.attrs, - name: componentOptions.name, + name: componentOptions.name || name, model: componentOptions.model, props: componentOptions.props, on: componentOptions.on, @@ -2331,7 +2331,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { Vue__default['default'].config.ignoredElements.push(tagName); } - return Object.assign({}, getCoreProperties(componentOptions), + return Object.assign({}, getCoreProperties(componentOptions, name), {$_vueTestUtils_original: originalComponent, $_doNotStubChildren: true, render: function render(h, context) { @@ -2512,8 +2512,16 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { } if (isConstructor(el) || isComponentOptions(el)) { + var componentOptions = isConstructor(el) ? el.options : el; + var elName = componentOptions.name; + + var stubbedComponent = resolveComponent(elName, stubs); + if (stubbedComponent) { + return originalCreateElement.apply(void 0, [ stubbedComponent ].concat( args )) + } + if (stubAllComponents) { - var stub = createStubFromComponent(el, el.name || 'anonymous', _Vue); + var stub = createStubFromComponent(el, elName || 'anonymous', _Vue); return originalCreateElement.apply(void 0, [ stub ].concat( args )) } var Constructor = shouldExtend(el) ? extend(el, _Vue) : el; @@ -9015,7 +9023,10 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { */ function isStyleVisible(element) { - if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { + if ( + !(element instanceof window.HTMLElement) && + !(element instanceof window.SVGElement) + ) { return false } @@ -11118,8 +11129,7 @@ var VueTestUtils = (function (exports, Vue, vueTemplateCompiler) { Object.keys(data).forEach(function (key) { // Don't let people set entire objects, because reactivity won't work if ( - typeof data[key] === 'object' && - data[key] !== null && + isPlainObject(data[key]) && // $FlowIgnore : Problem with possibly null this.vm data[key] === this$1.vm[key] ) { diff --git a/packages/test-utils/dist/vue-test-utils.js b/packages/test-utils/dist/vue-test-utils.js index c577f2e63..80ce8337c 100644 --- a/packages/test-utils/dist/vue-test-utils.js +++ b/packages/test-utils/dist/vue-test-utils.js @@ -1905,7 +1905,7 @@ function isVueComponent(c) { return true } - if (c === null || typeof c !== 'object') { + if (!isPlainObject(c)) { return false } @@ -1935,7 +1935,7 @@ function componentNeedsCompiling(component) { function isRefSelector(refOptionsObject) { if ( - typeof refOptionsObject !== 'object' || + !isPlainObject(refOptionsObject) || Object.keys(refOptionsObject || {}).length !== 1 ) { return false @@ -1945,7 +1945,7 @@ function isRefSelector(refOptionsObject) { } function isNameSelector(nameOptionsObject) { - if (typeof nameOptionsObject !== 'object' || nameOptionsObject === null) { + if (!isPlainObject(nameOptionsObject)) { return false } @@ -1961,7 +1961,7 @@ function isDynamicComponent(c) { } function isComponentOptions(c) { - return c !== null && typeof c === 'object' && (c.template || c.render) + return isPlainObject(c) && (c.template || c.render) } function isFunctionalComponent(c) { @@ -2271,10 +2271,10 @@ function resolveComponent$1(obj, component) { ) } -function getCoreProperties(componentOptions) { +function getCoreProperties(componentOptions, name) { return { attrs: componentOptions.attrs, - name: componentOptions.name, + name: componentOptions.name || name, model: componentOptions.model, props: componentOptions.props, on: componentOptions.on, @@ -2335,7 +2335,7 @@ function createStubFromComponent( Vue__default['default'].config.ignoredElements.push(tagName); } - return Object.assign({}, getCoreProperties(componentOptions), + return Object.assign({}, getCoreProperties(componentOptions, name), {$_vueTestUtils_original: originalComponent, $_doNotStubChildren: true, render: function render(h, context) { @@ -2516,8 +2516,16 @@ function patchCreateElement(_Vue, stubs, stubAllComponents) { } if (isConstructor(el) || isComponentOptions(el)) { + var componentOptions = isConstructor(el) ? el.options : el; + var elName = componentOptions.name; + + var stubbedComponent = resolveComponent(elName, stubs); + if (stubbedComponent) { + return originalCreateElement.apply(void 0, [ stubbedComponent ].concat( args )) + } + if (stubAllComponents) { - var stub = createStubFromComponent(el, el.name || 'anonymous', _Vue); + var stub = createStubFromComponent(el, elName || 'anonymous', _Vue); return originalCreateElement.apply(void 0, [ stub ].concat( args )) } var Constructor = shouldExtend(el) ? extend(el, _Vue) : el; @@ -9019,7 +9027,10 @@ ErrorWrapper.prototype.destroy = function destroy () { */ function isStyleVisible(element) { - if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { + if ( + !(element instanceof window.HTMLElement) && + !(element instanceof window.SVGElement) + ) { return false } @@ -11122,8 +11133,7 @@ Wrapper.prototype.setProps = function setProps (data) { Object.keys(data).forEach(function (key) { // Don't let people set entire objects, because reactivity won't work if ( - typeof data[key] === 'object' && - data[key] !== null && + isPlainObject(data[key]) && // $FlowIgnore : Problem with possibly null this.vm data[key] === this$1.vm[key] ) { diff --git a/packages/test-utils/dist/vue-test-utils.umd.js b/packages/test-utils/dist/vue-test-utils.umd.js index 23eae2145..a6df35715 100644 --- a/packages/test-utils/dist/vue-test-utils.umd.js +++ b/packages/test-utils/dist/vue-test-utils.umd.js @@ -1904,7 +1904,7 @@ return true } - if (c === null || typeof c !== 'object') { + if (!isPlainObject(c)) { return false } @@ -1934,7 +1934,7 @@ function isRefSelector(refOptionsObject) { if ( - typeof refOptionsObject !== 'object' || + !isPlainObject(refOptionsObject) || Object.keys(refOptionsObject || {}).length !== 1 ) { return false @@ -1944,7 +1944,7 @@ } function isNameSelector(nameOptionsObject) { - if (typeof nameOptionsObject !== 'object' || nameOptionsObject === null) { + if (!isPlainObject(nameOptionsObject)) { return false } @@ -1960,7 +1960,7 @@ } function isComponentOptions(c) { - return c !== null && typeof c === 'object' && (c.template || c.render) + return isPlainObject(c) && (c.template || c.render) } function isFunctionalComponent(c) { @@ -2270,10 +2270,10 @@ ) } - function getCoreProperties(componentOptions) { + function getCoreProperties(componentOptions, name) { return { attrs: componentOptions.attrs, - name: componentOptions.name, + name: componentOptions.name || name, model: componentOptions.model, props: componentOptions.props, on: componentOptions.on, @@ -2334,7 +2334,7 @@ Vue__default['default'].config.ignoredElements.push(tagName); } - return Object.assign({}, getCoreProperties(componentOptions), + return Object.assign({}, getCoreProperties(componentOptions, name), {$_vueTestUtils_original: originalComponent, $_doNotStubChildren: true, render: function render(h, context) { @@ -2515,8 +2515,16 @@ } if (isConstructor(el) || isComponentOptions(el)) { + var componentOptions = isConstructor(el) ? el.options : el; + var elName = componentOptions.name; + + var stubbedComponent = resolveComponent(elName, stubs); + if (stubbedComponent) { + return originalCreateElement.apply(void 0, [ stubbedComponent ].concat( args )) + } + if (stubAllComponents) { - var stub = createStubFromComponent(el, el.name || 'anonymous', _Vue); + var stub = createStubFromComponent(el, elName || 'anonymous', _Vue); return originalCreateElement.apply(void 0, [ stub ].concat( args )) } var Constructor = shouldExtend(el) ? extend(el, _Vue) : el; @@ -9018,7 +9026,10 @@ */ function isStyleVisible(element) { - if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) { + if ( + !(element instanceof window.HTMLElement) && + !(element instanceof window.SVGElement) + ) { return false } @@ -11121,8 +11132,7 @@ Object.keys(data).forEach(function (key) { // Don't let people set entire objects, because reactivity won't work if ( - typeof data[key] === 'object' && - data[key] !== null && + isPlainObject(data[key]) && // $FlowIgnore : Problem with possibly null this.vm data[key] === this$1.vm[key] ) { From 19e5e45b3f63929da6ebc877d775f75f29d4d46c Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 27 Jan 2023 12:07:10 +1000 Subject: [PATCH 02/12] v1.3.4 --- packages/server-test-utils/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server-test-utils/package.json b/packages/server-test-utils/package.json index c7d8da9f7..af5867dfd 100644 --- a/packages/server-test-utils/package.json +++ b/packages/server-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@vue/server-test-utils", - "version": "1.3.3", + "version": "1.3.4", "description": "Utilities for testing Vue components.", "main": "dist/vue-server-test-utils.js", "types": "types/index.d.ts", From 2734ec4f977b732bdb9d68eff7ad085ea1272829 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Fri, 27 Jan 2023 12:11:19 +1000 Subject: [PATCH 03/12] v1.3.4 --- packages/test-utils/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 16903686b..00023f458 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@vue/test-utils", - "version": "1.3.3", + "version": "1.3.4", "description": "Utilities for testing Vue components.", "main": "dist/vue-test-utils.js", "module": "dist/vue-test-utils.esm.js", From be11db69e55b2c781ac6394e80139da54d7a35e9 Mon Sep 17 00:00:00 2001 From: Simon He <57086651+Simon-He95@users.noreply.github.com> Date: Wed, 8 Feb 2023 13:01:08 +0800 Subject: [PATCH 04/12] chore: add lint cache (#2052) * chore: add lint cache * chore: update * chore: revert dist --- .eslintignore | 2 +- .gitignore | 1 + .prettierrc.json | 4 +++- docs/api/options.md | 2 +- docs/fr/api/options.md | 2 +- docs/fr/guides/common-tips.md | 2 +- docs/fr/guides/dom-events.md | 2 +- .../installation/using-other-test-runners.md | 2 +- docs/guides/common-tips.md | 2 +- docs/guides/dom-events.md | 2 +- docs/installation/using-other-test-runners.md | 2 +- docs/ja/api/options.md | 2 +- docs/ja/guides/dom-events.md | 2 +- docs/ja/guides/getting-started.md | 2 +- ...sting-single-file-components-with-karma.md | 4 ++-- docs/ru/api/options.md | 2 +- docs/ru/guides/dom-events.md | 2 +- docs/ru/guides/getting-started.md | 2 +- ...sting-single-file-components-with-karma.md | 4 ++-- docs/zh/api/options.md | 2 +- docs/zh/guides/common-tips.md | 2 +- docs/zh/guides/dom-events.md | 2 +- docs/zh/guides/getting-started.md | 2 +- ...sting-single-file-components-with-karma.md | 4 ++-- package.json | 6 +++--- packages/create-instance/create-instance.js | 6 +++--- .../create-instance/create-scoped-slots.js | 8 ++++---- packages/create-instance/log-events.js | 2 +- packages/shared/validators.js | 4 ++-- packages/test-utils/src/wrapper-array.js | 2 +- packages/test-utils/src/wrapper.js | 10 ++++------ .../components/component-with-computed.vue | 7 ++----- .../components/component-with-transitions.vue | 4 +--- .../component-with-watch-immediate.vue | 2 +- .../components/component-with-watch.vue | 2 +- test/resources/test-mixin.js | 2 +- test/resources/utils.js | 8 ++++---- test/specs/create-local-vue.spec.js | 4 ++-- test/specs/mount.spec.js | 6 +++--- .../mounting-options/scopedSlots.spec.js | 13 ++++++------ test/specs/shallow-mount.spec.js | 4 ++-- test/specs/wrapper-array/at.spec.js | 20 +++++++------------ test/specs/wrapper-array/attributes.spec.js | 4 +--- test/specs/wrapper-array/classes.spec.js | 4 +--- test/specs/wrapper-array/contains.spec.js | 8 ++++---- test/specs/wrapper-array/find.spec.js | 8 +++----- test/specs/wrapper-array/findAll.spec.js | 8 +++----- test/specs/wrapper-array/html.spec.js | 8 +++----- test/specs/wrapper-array/is.spec.js | 5 +---- test/specs/wrapper-array/isEmpty.spec.js | 5 +---- test/specs/wrapper-array/isVisible.spec.js | 5 +---- .../specs/wrapper-array/isVueInstance.spec.js | 5 +---- test/specs/wrapper-array/name.spec.js | 5 +---- test/specs/wrapper-array/props.spec.js | 8 +++----- test/specs/wrapper-array/setData.spec.js | 5 +---- test/specs/wrapper-array/setProps.spec.js | 5 +---- test/specs/wrapper-array/text.spec.js | 5 +---- test/specs/wrapper-array/trigger.spec.js | 5 +---- test/specs/wrapper/emittedByOrder.spec.js | 6 +++--- test/specs/wrapper/find.spec.js | 18 ++++------------- test/specs/wrapper/findAll.spec.js | 7 +------ test/specs/wrapper/overview.spec.js | 8 ++++++-- test/specs/wrapper/props.spec.js | 4 +--- test/specs/wrapper/setData.spec.js | 7 ++----- test/specs/wrapper/setProps.spec.js | 13 +++++------- yarn.lock | 7 ++++++- 66 files changed, 132 insertions(+), 196 deletions(-) diff --git a/.eslintignore b/.eslintignore index 1521c8b76..884a2323e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1 @@ -dist +**/dist/** diff --git a/.gitignore b/.gitignore index 63161737b..8161b78c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store node_modules/ +.eslintcache # Editor files /.idea diff --git a/.prettierrc.json b/.prettierrc.json index b2095be81..f79acadcb 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,4 +1,6 @@ { "semi": false, - "singleQuote": true + "singleQuote": true, + "trailingComma": "none", + "arrowParens": "avoid" } diff --git a/docs/api/options.md b/docs/api/options.md index 3fa3b0548..68efd2eeb 100644 --- a/docs/api/options.md +++ b/docs/api/options.md @@ -169,7 +169,7 @@ You can also pass a function that takes the props as an argument: ```js shallowMount(Component, { scopedSlots: { - foo: function(props) { + foo: function (props) { return this.$createElement('div', props.index) } } diff --git a/docs/fr/api/options.md b/docs/fr/api/options.md index ed2171e5f..c4a13604d 100644 --- a/docs/fr/api/options.md +++ b/docs/fr/api/options.md @@ -168,7 +168,7 @@ Vous pouvez également passer une fonction qui prend les props comme argument : ```js shallowMount(Component, { scopedSlots: { - foo: function(props) { + foo: function (props) { return this.$createElement('div', props.index) } } diff --git a/docs/fr/guides/common-tips.md b/docs/fr/guides/common-tips.md index f9dad1e0f..da6e8d863 100644 --- a/docs/fr/guides/common-tips.md +++ b/docs/fr/guides/common-tips.md @@ -221,7 +221,7 @@ En pratique, bien que nous appelions et attendions `setData` pour assurer la mis ```js const transitionStub = () => ({ - render: function(h) { + render: function (h) { return this.$options._renderChildren } }) diff --git a/docs/fr/guides/dom-events.md b/docs/fr/guides/dom-events.md index b90f33ea2..0d8bce39e 100644 --- a/docs/fr/guides/dom-events.md +++ b/docs/fr/guides/dom-events.md @@ -144,7 +144,7 @@ Ce composant permet d'incrémenter/décrémenter la quantité à l'aide de diff }, watch: { - quantity: function(newValue) { + quantity: function (newValue) { this.$emit('input', newValue) } } diff --git a/docs/fr/installation/using-other-test-runners.md b/docs/fr/installation/using-other-test-runners.md index d019ec8a9..2ad784d22 100644 --- a/docs/fr/installation/using-other-test-runners.md +++ b/docs/fr/installation/using-other-test-runners.md @@ -12,7 +12,7 @@ Vous trouverez ci-dessous une configuration de base de Karma pour Vue Test Utils // karma.conf.js var webpackConfig = require('./webpack.config.js') -module.exports = function(config) { +module.exports = function (config) { config.set({ frameworks: ['mocha'], files: ['test/**/*.spec.js'], diff --git a/docs/guides/common-tips.md b/docs/guides/common-tips.md index e394786a3..240119ebf 100644 --- a/docs/guides/common-tips.md +++ b/docs/guides/common-tips.md @@ -223,7 +223,7 @@ In practice, although we are calling and awaiting `setData` to ensure the DOM is ```js const transitionStub = () => ({ - render: function(h) { + render: function (h) { return this.$options._renderChildren } }) diff --git a/docs/guides/dom-events.md b/docs/guides/dom-events.md index df0988d45..d203a740c 100644 --- a/docs/guides/dom-events.md +++ b/docs/guides/dom-events.md @@ -142,7 +142,7 @@ This component allows to increment/decrement the quantity using various keys. }, watch: { - quantity: function(newValue) { + quantity: function (newValue) { this.$emit('input', newValue) } } diff --git a/docs/installation/using-other-test-runners.md b/docs/installation/using-other-test-runners.md index 0b49972e0..48349f267 100644 --- a/docs/installation/using-other-test-runners.md +++ b/docs/installation/using-other-test-runners.md @@ -12,7 +12,7 @@ Following is a basic Karma config for Vue Test Utils: // karma.conf.js var webpackConfig = require('./webpack.config.js') -module.exports = function(config) { +module.exports = function (config) { config.set({ frameworks: ['mocha'], files: ['test/**/*.spec.js'], diff --git a/docs/ja/api/options.md b/docs/ja/api/options.md index 7f6a550bd..8d4e8e7cc 100644 --- a/docs/ja/api/options.md +++ b/docs/ja/api/options.md @@ -94,7 +94,7 @@ props を引数に取る関数を渡すことができます。 ```js shallowMount(Component, { scopedSlots: { - foo: function(props) { + foo: function (props) { return this.$createElement('div', props.index) } } diff --git a/docs/ja/guides/dom-events.md b/docs/ja/guides/dom-events.md index 3a3f35aaa..562117299 100644 --- a/docs/ja/guides/dom-events.md +++ b/docs/ja/guides/dom-events.md @@ -134,7 +134,7 @@ it('Click on yes button calls our method with argument "yes"', async () => { } }, watch: { - quantity: function(newValue) { + quantity: function (newValue) { this.$emit('input', newValue) } } diff --git a/docs/ja/guides/getting-started.md b/docs/ja/guides/getting-started.md index d085eb9c7..cdcf63728 100644 --- a/docs/ja/guides/getting-started.md +++ b/docs/ja/guides/getting-started.md @@ -131,7 +131,7 @@ it('will catch the error using done', done => { }) it('will catch the error using a promise', () => { - return Vue.nextTick().then(function() { + return Vue.nextTick().then(function () { expect(true).toBe(false) }) }) diff --git a/docs/ja/installation/testing-single-file-components-with-karma.md b/docs/ja/installation/testing-single-file-components-with-karma.md index 26ef15c6b..9e2c91f2a 100644 --- a/docs/ja/installation/testing-single-file-components-with-karma.md +++ b/docs/ja/installation/testing-single-file-components-with-karma.md @@ -36,7 +36,7 @@ npm install --save-dev @vue/test-utils karma karma-chrome-launcher karma-mocha k var webpackConfig = require('./webpack.config.js') -module.exports = function(config) { +module.exports = function (config) { config.set({ frameworks: ['mocha'], @@ -160,7 +160,7 @@ npm install --save-dev babel-plugin-istanbul ```js // karma.conf.js -module.exports = function(config) { +module.exports = function (config) { config.set({ // ... diff --git a/docs/ru/api/options.md b/docs/ru/api/options.md index 40968a0bc..afeb619f3 100644 --- a/docs/ru/api/options.md +++ b/docs/ru/api/options.md @@ -107,7 +107,7 @@ shallowMount(Component, { ```js shallowMount(Component, { scopedSlots: { - foo: function(props) { + foo: function (props) { return this.$createElement('div', props.index) } } diff --git a/docs/ru/guides/dom-events.md b/docs/ru/guides/dom-events.md index b3ce272cb..5f472fc95 100644 --- a/docs/ru/guides/dom-events.md +++ b/docs/ru/guides/dom-events.md @@ -136,7 +136,7 @@ describe('Click event', () => { }, watch: { - quantity: function(newValue) { + quantity: function (newValue) { this.$emit('input', newValue) } } diff --git a/docs/ru/guides/getting-started.md b/docs/ru/guides/getting-started.md index 77b324fa2..a4cc198d7 100644 --- a/docs/ru/guides/getting-started.md +++ b/docs/ru/guides/getting-started.md @@ -131,7 +131,7 @@ it('должен отлавливать ошибку с использовани }) it('должен отлавливать ошибку с использованием promise', () => { - return Vue.nextTick().then(function() { + return Vue.nextTick().then(function () { expect(true).toBe(false) }) }) diff --git a/docs/ru/installation/testing-single-file-components-with-karma.md b/docs/ru/installation/testing-single-file-components-with-karma.md index 0105006e2..fe2cacf8a 100644 --- a/docs/ru/installation/testing-single-file-components-with-karma.md +++ b/docs/ru/installation/testing-single-file-components-with-karma.md @@ -36,7 +36,7 @@ npm install --save-dev @vue/test-utils karma karma-chrome-launcher karma-mocha k var webpackConfig = require('./webpack.config.js') -module.exports = function(config) { +module.exports = function (config) { config.set({ frameworks: ['mocha'], @@ -160,7 +160,7 @@ npm install --save-dev babel-plugin-istanbul ```js // karma.conf.js -module.exports = function(config) { +module.exports = function (config) { config.set({ // ... diff --git a/docs/zh/api/options.md b/docs/zh/api/options.md index 076090228..40b86c2ad 100644 --- a/docs/zh/api/options.md +++ b/docs/zh/api/options.md @@ -167,7 +167,7 @@ shallowMount(Component, { ```js shallowMount(Component, { scopedSlots: { - foo: function(props) { + foo: function (props) { return this.$createElement('div', props.index) } } diff --git a/docs/zh/guides/common-tips.md b/docs/zh/guides/common-tips.md index 110fc0a96..780c980c9 100644 --- a/docs/zh/guides/common-tips.md +++ b/docs/zh/guides/common-tips.md @@ -222,7 +222,7 @@ test('should render Foo, then hide it', async () => { ```js const transitionStub = () => ({ - render: function(h) { + render: function (h) { return this.$options._renderChildren } }) diff --git a/docs/zh/guides/dom-events.md b/docs/zh/guides/dom-events.md index a3c858807..1af79fc2a 100644 --- a/docs/zh/guides/dom-events.md +++ b/docs/zh/guides/dom-events.md @@ -142,7 +142,7 @@ it('Click on yes button calls our method with argument "yes"', async () => { }, watch: { - quantity: function(newValue) { + quantity: function (newValue) { this.$emit('input', newValue) } } diff --git a/docs/zh/guides/getting-started.md b/docs/zh/guides/getting-started.md index 70f35f93b..7b10a0336 100644 --- a/docs/zh/guides/getting-started.md +++ b/docs/zh/guides/getting-started.md @@ -164,7 +164,7 @@ it('will catch the error using done', done => { }) it('will catch the error using a promise', () => { - return Vue.nextTick().then(function() { + return Vue.nextTick().then(function () { expect(true).toBe(false) }) }) diff --git a/docs/zh/installation/testing-single-file-components-with-karma.md b/docs/zh/installation/testing-single-file-components-with-karma.md index 544ae4521..a1a5dc19c 100644 --- a/docs/zh/installation/testing-single-file-components-with-karma.md +++ b/docs/zh/installation/testing-single-file-components-with-karma.md @@ -36,7 +36,7 @@ npm install --save-dev @vue/test-utils karma karma-chrome-launcher karma-mocha k var webpackConfig = require('./webpack.config.js') -module.exports = function(config) { +module.exports = function (config) { config.set({ frameworks: ['mocha'], @@ -160,7 +160,7 @@ npm install --save-dev babel-plugin-istanbul ```js // karma.conf.js -module.exports = function(config) { +module.exports = function (config) { config.set({ // ... diff --git a/package.json b/package.json index 4ada8c2e2..d75249253 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,10 @@ "docs": "vuepress dev docs", "docs:build": "vuepress build docs", "flow": "flow check", - "lint": "eslint --ext js,vue .", + "lint": "eslint --ext js,vue . --cache", "lint:docs": "eslint --ext js,vue,md docs --ignore-path .gitignore", "lint:fix": "yarn lint -- --fix", - "format": "prettier --write \"**/*.{js,json,vue,md}\"", + "format": "prettier --write \"**/*.{js,json,vue,md}\" --cache", "format:check": "prettier --check \"**/*.{js,json,vue,md}\"", "release": "yarn build && yarn test:unit && lerna publish --conventional-commits -m \"chore(release): publish %s\"", "test": "yarn format:check && yarn lint && yarn lint:docs && yarn flow && yarn test:types && yarn test:unit -w 1 && yarn test:unit:browser", @@ -91,7 +91,7 @@ "karma-spec-reporter": "^0.0.32", "karma-webpack": "^4.0.2", "lint-staged": "^9.5.0", - "prettier": "^1.16.0", + "prettier": "^2.8.1", "puppeteer": "^5.2.1", "rollup-plugin-delete": "^2.0.0", "@rollup/plugin-replace": "^2.3.3", diff --git a/packages/create-instance/create-instance.js b/packages/create-instance/create-instance.js index 3c0ba7d2e..7aaafb962 100644 --- a/packages/create-instance/create-instance.js +++ b/packages/create-instance/create-instance.js @@ -107,7 +107,7 @@ export default function createInstance( const parentComponentOptions = options.parentComponent || {} const originalParentComponentProvide = parentComponentOptions.provide - parentComponentOptions.provide = function() { + parentComponentOptions.provide = function () { return { ...getValuesFromCallableOption.call(this, originalParentComponentProvide), // $FlowIgnore @@ -116,7 +116,7 @@ export default function createInstance( } const originalParentComponentData = parentComponentOptions.data - parentComponentOptions.data = function() { + parentComponentOptions.data = function () { return { ...getValuesFromCallableOption.call(this, originalParentComponentData), vueTestUtils_childProps: { ...options.propsData } @@ -126,7 +126,7 @@ export default function createInstance( parentComponentOptions.$_doNotStubChildren = true parentComponentOptions.$_isWrapperParent = true parentComponentOptions._isFunctionalContainer = componentOptions.functional - parentComponentOptions.render = function(h) { + parentComponentOptions.render = function (h) { return h( Constructor, createContext(options, scopedSlots, this.vueTestUtils_childProps), diff --git a/packages/create-instance/create-scoped-slots.js b/packages/create-instance/create-scoped-slots.js index 50464d9ee..daa4f3cb1 100644 --- a/packages/create-instance/create-scoped-slots.js +++ b/packages/create-instance/create-scoped-slots.js @@ -8,9 +8,9 @@ function isDestructuringSlotScope(slotScope: string): boolean { return /^{.*}$/.test(slotScope) } -function getVueTemplateCompilerHelpers( - _Vue: Component -): { [name: string]: Function } { +function getVueTemplateCompilerHelpers(_Vue: Component): { + [name: string]: Function +} { // $FlowIgnore const vue = new _Vue() const helpers = {} @@ -111,7 +111,7 @@ export default function createScopedSlots( const slotScope = scopedSlotMatches.match && scopedSlotMatches.match[1] - scopedSlots[scopedSlotName] = function(props) { + scopedSlots[scopedSlotName] = function (props) { let res if (isFn) { res = renderFn.call({ ...helpers }, props) diff --git a/packages/create-instance/log-events.js b/packages/create-instance/log-events.js index 9de6abf20..c3fd97f0d 100644 --- a/packages/create-instance/log-events.js +++ b/packages/create-instance/log-events.js @@ -15,7 +15,7 @@ export function logEvents( export function addEventLogger(_Vue: Component): void { _Vue.mixin({ - beforeCreate: function() { + beforeCreate: function () { this.__emitted = Object.create(null) this.__emittedByOrder = [] logEvents(this, this.__emitted, this.__emittedByOrder) diff --git a/packages/shared/validators.js b/packages/shared/validators.js index 986049d33..c72591d22 100644 --- a/packages/shared/validators.js +++ b/packages/shared/validators.js @@ -137,10 +137,10 @@ function makeMap(str: string, expectsLowerCase?: boolean) { map[list[i]] = true } return expectsLowerCase - ? function(val: string) { + ? function (val: string) { return map[val.toLowerCase()] } - : function(val: string) { + : function (val: string) { return map[val] } } diff --git a/packages/test-utils/src/wrapper-array.js b/packages/test-utils/src/wrapper-array.js index f5bd64bf7..e29cb2bed 100644 --- a/packages/test-utils/src/wrapper-array.js +++ b/packages/test-utils/src/wrapper-array.js @@ -5,7 +5,7 @@ import type VueWrapper from './vue-wrapper' import { throwError } from 'shared/util' export default class WrapperArray implements BaseWrapper { - +wrappers: Array + +wrappers: Array; +length: number selector: Selector | void diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index bbaae815a..bcf7985ef 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -29,11 +29,11 @@ import createDOMEvent from './create-dom-event' import { throwIfInstancesThrew } from './error' export default class Wrapper implements BaseWrapper { - +vnode: VNode | null + +vnode: VNode | null; +vm: Component | void _emitted: { [name: string]: Array> } - _emittedByOrder: Array<{ name: string, args: Array }> - +element: Element + _emittedByOrder: Array<{ name: string, args: Array }>; + +element: Element; +options: WrapperOptions isFunctionalComponent: boolean rootNode: VNode | Element @@ -502,9 +502,7 @@ export default class Wrapper implements BaseWrapper { ? value.map((calledWith, index) => { const callParams = calledWith.map(param => typeof param === 'object' - ? JSON.stringify(param) - .replace(/"/g, '') - .replace(/,/g, ', ') + ? JSON.stringify(param).replace(/"/g, '').replace(/,/g, ', ') : param ) diff --git a/test/resources/components/component-with-computed.vue b/test/resources/components/component-with-computed.vue index 02445747a..d608e4885 100644 --- a/test/resources/components/component-with-computed.vue +++ b/test/resources/components/component-with-computed.vue @@ -11,11 +11,8 @@ export default { message: 'egassem' }), computed: { - reversedMessage: function() { - return this.message - .split('') - .reverse() - .join('') + reversedMessage: function () { + return this.message.split('').reverse().join('') } } } diff --git a/test/resources/components/component-with-transitions.vue b/test/resources/components/component-with-transitions.vue index 9af326ce4..e8965da44 100644 --- a/test/resources/components/component-with-transitions.vue +++ b/test/resources/components/component-with-transitions.vue @@ -1,9 +1,7 @@ diff --git a/test/resources/components/component-with-watch-immediate.vue b/test/resources/components/component-with-watch-immediate.vue index f8abce285..736be6939 100644 --- a/test/resources/components/component-with-watch-immediate.vue +++ b/test/resources/components/component-with-watch-immediate.vue @@ -6,7 +6,7 @@ +``` + +**Teste** + +```js +import { mount } from '@vue/test-utils' +import ParentComponent from '@/components/ParentComponent' +import ChildComponent from '@/components/ChildComponent' + +describe('ParentComponent', () => { + it("displays 'Emitted!' when custom event is emitted", () => { + const wrapper = mount(ParentComponent) + wrapper.findComponent(ChildComponent).vm.$emit('custom') + expect(wrapper.html()).toContain('Emitted!') + }) +}) +``` + +### Manipulando o Estado de Componente + +Você pode manipular o estado do componente diretamente usando o método `setData` ou o método `setProps` no envolvedor (wrapper): + +```js +it('manipulates state', async () => { + await wrapper.setData({ count: 10 }) + + await wrapper.setProps({ foo: 'bar' }) +}) +``` + +### Imitando Propriedades + +Você pode passar as propriedades para o componente usando opção `propsData` embutida da Vue: + +```js +import { mount } from '@vue/test-utils' + +mount(Component, { + propsData: { + aProp: 'some value' + } +}) +``` + +Você pode também atualizar as propriedades de um componente já montado com o método `wrapper.setProps({})`. + +_Para uma lista completa das opções, consulte a [seção opções de montagem](../api/options.md) da documentação._ + +### Imitando Transições + +Apesar das chamadas de `await Vue.nextTick()` funcionarem bem para a maioria dos casos de uso, existem algumas situações onde soluções adicionais são necessárias. Estes problemas serão resolvidos antes da biblioteca `vue-test-utils` sair da beta. Um destes exemplos é o teste unitário de componentes com o envolvedor `` fornecido pela Vue. + +```vue + + + +``` + +Você pode querer escrever um teste que verifica que se Foo está exibido, então quando o `show` é definido para `false`, o Foo não é mais renderizado. Tal teste poderia ser escrito como o seguinte: + +```js +test('should render Foo, then hide it', async () => { + const wrapper = mount(Foo) + expect(wrapper.text()).toMatch(/Foo/) + + await wrapper.setData({ + show: false + }) + + expect(wrapper.text()).not.toMatch(/Foo/) +}) +``` + +Na prática, apesar de nós estarmos chamando e chamando o `setData` para garantir que o DOM é atualizado, este teste falha. Isto é um problema em andamento relacionado a como a Vue implementa o componente ``, isto nós gostaríamos de resolver antes da versão 1.0. Por agora, existem algumas soluções: + +#### Usando o auxiliar `transitionStub` + +```js +const transitionStub = () => ({ + render: function(h) { + return this.$options._renderChildren + } +}) + +test('should render Foo, then hide it', async () => { + const wrapper = mount(Foo, { + stubs: { + transition: transitionStub() + } + }) + expect(wrapper.text()).toMatch(/Foo/) + + await wrapper.setData({ + show: false + }) + + expect(wrapper.text()).not.toMatch(/Foo/) +}) +``` + +Isto sobrescreve o comportamento padrão do componente `` e renderiza os filhos assim que condição booleana relevante mudar, visto que é o oposto de aplicar classes de CSS, que é como componente `` da Vue funciona. + +#### Evitar `setData` + +Uma outra alternativa é simplesmente evitar usar o `setData` ao escrever dois testes, com a configuração necessária realizada usando as opções `mount` ou `shallowMount`: + +```js +test('should render Foo', async () => { + const wrapper = mount(Foo, { + data() { + return { + show: true + } + } + }) + + expect(wrapper.text()).toMatch(/Foo/) +}) + +test('should not render Foo', async () => { + const wrapper = mount(Foo, { + data() { + return { + show: false + } + } + }) + + expect(wrapper.text()).not.toMatch(/Foo/) +}) +``` + +### Aplicando Plugins Globais e Mixins + +Alguns componentes podem depender de funcionalidades injetadas por um plugin global ou mixin, por exemplo a `vuex` e a `vue-router`. + +Se você estiver escrevendo testes para componentes em uma aplicação especifica, você pode configurar os mesmos plugins globais e mixins de uma vez na entrada de seus testes. Mas em alguns casos, por exemplo testando um conjunto de componente genérico que podem ser partilhados entre aplicações diferentes, é melhor testar seus componentes em uma configuração mais isolada, sem poluir o construtor global da `Vue`. Nós podemos usar o método [`createLocalVue`](../api/createLocalVue.md) para alcançar isso: + +```js +import { createLocalVue, mount } from '@vue/test-utils' + +// cria construtor de `Vue` estendido +const localVue = createLocalVue() + +// instala os plugins como normais +localVue.use(MyPlugin) + +// passa o `localVue` para as opções do `mount` +mount(Component, { + localVue +}) +``` + +**Repare que alguns plugins, como a Vue Router, adicionam propriedades de somente leitura ao construtor global da Vue. Isto torna impossível reinstalar o plugin em construtor `localVue`, ou adicionar imitações para estas propriedades de somente leitura.** + +### Imitando Injeções + +Uma outra estratégia para propriedades injetadas é simplesmente imitá-las. Você fazer isso com a opção `mocks`: + +```js +import { mount } from '@vue/test-utils' + +const $route = { + path: '/', + hash: '', + params: { id: '123' }, + query: { q: 'hello' } +} + +mount(Component, { + mocks: { + // adiciona objeto `$route` imitado para a instância da Vue + // antes da montagem do componente + $route + } +}) +``` + +### Forjando componentes + +Você pode sobrescrever componentes que são registados globalmente ou localmente usando a opção `stubs`: + +```js +import { mount } from '@vue/test-utils' + +mount(Component, { + // Resolverá o `globally-registered-component` com o + // forjado vazio + stubs: ['globally-registered-component'] +}) +``` + +### Lidando com o Roteamento + +Visto que o roteamento por definição tem haver com toda estrutura da aplicação e envolve vários componentes, ele é melhor testado através de integração ou testes fim-à-fim (end-to-end). Para componentes individuais que dependem de funcionalidades da `vue-router`, você pode imitá-las usando as técnicas mencionadas acima. + +### Detetando estilos + +O seu teste apenas pode detetar estilos em linha quando estiver executando no `jsdom`. diff --git a/docs/pt/guides/dom-events.md b/docs/pt/guides/dom-events.md new file mode 100644 index 000000000..a8daea085 --- /dev/null +++ b/docs/pt/guides/dom-events.md @@ -0,0 +1,215 @@ +## Testando eventos de Teclado, Rato e outros do DOM + + + +### Acionar eventos + +O `Wrapper` expõe um método `trigger` assíncrono. Ele pode ser usado para acionar eventos do DOM. + +```js +test('triggers a click', async () => { + const wrapper = mount(MyComponent) + + await wrapper.trigger('click') +}) +``` + +Você deve estar ciente de que o método `find` retorna também um `Wrapper`. Assumindo que o `MyComponent` contém um botão, o seguinte código clica neste botão. + +```js +test('triggers a click', async () => { + const wrapper = mount(MyComponent) + + await wrapper.find('button').trigger('click') +}) +``` + +### Opções + +O método `trigger` recebe um objeto `options` opcional. As propriedades dentro do objeto `options` são adicionadas ao Event. + +Repare que o alvo não pode ser adicionado dentro do objeto `options`. + +```js +test('triggers a click', async () => { + const wrapper = mount(MyComponent) + + await wrapper.trigger('click', { button: 0 }) +}) +``` + +### Exemplo de Clique do Rato + +**Componente sob teste** + +```html + + + +``` + +**Testar** + +```js +import YesNoComponent from '@/components/YesNoComponent' +import { mount } from '@vue/test-utils' +import sinon from 'sinon' + +it('Click on yes button calls our method with argument "yes"', async () => { + const spy = sinon.spy() + const wrapper = mount(YesNoComponent, { + propsData: { + callMe: spy + } + }) + await wrapper.find('button.yes').trigger('click') + + spy.should.have.been.calledWith('yes') +}) +``` + +### Exemplo de Teclado + +**Componente sob teste** + +Este componente permite incrementar/decrementar a quantidade usando várias teclas. + +```html + + + +``` + +**Testar** + +```js +import QuantityComponent from '@/components/QuantityComponent' +import { mount } from '@vue/test-utils' + +describe('Key event tests', () => { + it('Quantity is zero by default', () => { + const wrapper = mount(QuantityComponent) + expect(wrapper.vm.quantity).toBe(0) + }) + + it('Up arrow key increments quantity by 1', async () => { + const wrapper = mount(QuantityComponent) + await wrapper.trigger('keydown.up') + expect(wrapper.vm.quantity).toBe(1) + }) + + it('Down arrow key decrements quantity by 1', async () => { + const wrapper = mount(QuantityComponent) + wrapper.vm.quantity = 5 + await wrapper.trigger('keydown.down') + expect(wrapper.vm.quantity).toBe(4) + }) + + it('Escape sets quantity to 0', async () => { + const wrapper = mount(QuantityComponent) + wrapper.vm.quantity = 5 + await wrapper.trigger('keydown.esc') + expect(wrapper.vm.quantity).toBe(0) + }) + + it('Magic character "a" sets quantity to 13', async () => { + const wrapper = mount(QuantityComponent) + await wrapper.trigger('keydown', { + key: 'a' + }) + expect(wrapper.vm.quantity).toBe(13) + }) +}) +``` + +**Limitações** + +O nome da tecla depois do ponto `keydown.up` é traduzido para um `KeyCode` (código da tecla). Isto é suportado para os seguintes nomes: + +| nome da tecla | código da tecla | +| ------------- | --------------- | +| enter | 13 | +| esc | 27 | +| tab | 9 | +| space | 32 | +| delete | 46 | +| backspace | 8 | +| insert | 45 | +| up | 38 | +| down | 40 | +| left | 37 | +| right | 39 | +| end | 35 | +| home | 36 | +| pageup | 33 | +| pagedown | 34 | diff --git a/docs/pt/guides/getting-started.md b/docs/pt/guides/getting-started.md new file mode 100644 index 000000000..a80ea34f7 --- /dev/null +++ b/docs/pt/guides/getting-started.md @@ -0,0 +1,76 @@ +## Começar agora + + + +### O que é o Vue Test Utils? + +A Vue Test Utils (VTU) é um conjunto de funções utilitárias com o fim de simplificar os testes de componentes de Vue.js. Ele fornece alguns métodos para **montar** e **interagir** com componentes de Vue.js em um modo isolado. + +Vamos ver um exemplo: + +```js +// Importa o método `mount()` do Vue Test Utils +import { mount } from '@vue/test-utils' + +// O componente para testar +const MessageComponent = { + template: '

{{ msg }}

', + props: ['msg'] +} + +test('displays message', () => { + // mount() retorna um componente de Vue envolvido com qual podemos interagir + const wrapper = mount(MessageComponent, { + propsData: { + msg: 'Hello world' + } + }) + + // Afirma o texto renderizado do componente + expect(wrapper.text()).toContain('Hello world') +}) +``` + +Os componentes montados são retornados dentro de um [Wrapper (envolvedor)](../api/wrapper/), o qual expõe métodos para consulta e interação com o componente sob teste. + +### Simulando a Interação do Usuário + +Vamos imaginar um componente contador que incrementa quando o usuário clica no botão: + +```js +const Counter = { + template: ` +
+ +

Total clicks: {{ count }}

+
+ `, + data() { + return { count: 0 } + } +} +``` + +Para simular o comportamento, nós precisamos primeiro localizar o botão com o `wrapper.find()`, o qual retorna um **envolvedor para o elemento `button`**. Nós podemos então simular o clique ao chamar `.trigger()` no envolvedor do botão: + +```js +test('increments counter value on click', async () => { + const wrapper = mount(Counter) + const button = wrapper.find('button') + const text = wrapper.find('p') + + expect(text.text()).toContain('Total clicks: 0') + + await button.trigger('click') + + expect(text.text()).toContain('Total clicks: 1') +}) +``` + +Repare como o teste deve ser `async` e que o `trigger` precisa ser esperado. Consulte o guia [Testando Comportamento Assíncronos](./README.md#testing-asynchronous-behavior) para entender porquê isto é necessário e outras coisas a considerar quando estiver testando cenários assíncronos. + +### O que se segue + +Consulte as nossas [dicas comuns para quando estiver escrevendo testes](./README.md#knowing-what-to-test). + +Por outro lado, você pode explorar a [API completa](../api/). diff --git a/docs/pt/guides/testing-async-components.md b/docs/pt/guides/testing-async-components.md new file mode 100644 index 000000000..8a55f9af0 --- /dev/null +++ b/docs/pt/guides/testing-async-components.md @@ -0,0 +1,143 @@ +## Testando Comportamento Assíncrono + +Existem dois tipos de comportamentos assíncronos que você encontrará em seus testes: + +1. Atualizações aplicadas pelo Vue +2. Comportamento assíncrono fora do Vue + +### Atualizações aplicadas pela Vue + +O Vue agrupa atualizações pendentes da DOM e aplica elas assincronamente para prevenir re-renderizações desnecessárias causadas por várias mutações de dados. + +_Você pode ler mais sobre atualizações assíncronas na [documentação do Vue](https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue)_ + +Na prática, isto significa que depois da mutação de uma propriedade reativa, para confirmar aquela mudança o seu teste tem que aguardar enquanto o Vue estiver desempenhando atualizações. +Uma maneira é usar o `await Vue.nextTick()`, mas uma maneira mais fácil e clara é apenas esperar (`await`) o método que com qual você mudou o estado, tipo `trigger`. + +```js +// dentro do conjunto de teste, adicione este caso de teste +it('button click should increment the count text', async () => { + expect(wrapper.text()).toContain('0') + const button = wrapper.find('button') + await button.trigger('click') + expect(wrapper.text()).toContain('1') +}) +``` + +Esperar o acionador acima é o mesmo que fazer: + +```js +it('button click should increment the count text', async () => { + expect(wrapper.text()).toContain('0') + const button = wrapper.find('button') + button.trigger('click') + await Vue.nextTick() + expect(wrapper.text()).toContain('1') +}) +``` + +Os métodos que podem ser esperados são: + +- [setData](../api/wrapper/README.md#o-método-setdata) +- [setValue](../api/wrapper/README.md#o-método-setvalue) +- [setChecked](../api/wrapper/README.md#o-método-setchecked) +- [setSelected](../api/wrapper/README.md#o-método-setselected) +- [setProps](../api/wrapper/README.md#o-método-setprops) +- [trigger](../api/wrapper/README.md#o-método-trigger) + +### Comportamento assíncrono fora do Vue + +Um dos comportamentos assíncrono mais comuns fora de Vue é a chamada de API dentro de ações de Vuex. Os seguintes exemplos mostram como testar um método que faz uma chama de API. Este exemplo usa o `Jest` para executar o teste e imitar a biblioteca de HTTP `axios`. Mais sobre as imitações manuais de `Jest` podem ser encontradas [aqui](https://jestjs.io/docs/en/manual-mocks.html#content). + +A implementação da imitação do `axios` parece com isto: + +```js +export default { + get: () => Promise.resolve({ data: 'value' }) +} +``` + +O componente abaixo faz uma chamada de API quando um botão é clicado, depois atribui a resposta ao `value`. + +```html + + + +``` + +Um teste pode ser escrito da seguinte maneira: + +```js +import { shallowMount } from '@vue/test-utils' +import Foo from './Foo' +jest.mock('axios', () => ({ + get: Promise.resolve('value') +})) + +it('fetches async when a button is clicked', () => { + const wrapper = shallowMount(Foo) + wrapper.find('button').trigger('click') + expect(wrapper.text()).toBe('value') +}) +``` + +Este teste atualmente falha porque a afirmação é chamada antes de resolver a promessa em `fetchResults`. A maioria das bibliotecas de testes unitários fornecem uma _callback (função de resposta)_ para permitir que o executor saiba quando o teste está completo ou terminado. Ambos `Jest` e `Mocha` usam o `done`. Nós podemos usar o `done` em combinação com o `$nextTick` ou `setTimeout` para garantir que quaisquer promessas estão resolvidas antes da afirmação ser feita. + +```js +it('fetches async when a button is clicked', done => { + const wrapper = shallowMount(Foo) + wrapper.find('button').trigger('click') + wrapper.vm.$nextTick(() => { + expect(wrapper.text()).toBe('value') + done() + }) +}) +``` + +A razão pela qual o `setTimeout` permite o teste passar é porque a fila de micro-tarefa onde as funções de resposta de promessa são processadas executam antes da fila de tarefa, onde as funções de respostas de `setTimeout` são processadas. Isto significa que no momento que a função de resposta de `setTimeout` executa, quaisquer funções de resposta de promessa na fila de micro-tarefa terão que ser executadas. O `$nextTick` por outro lado agenda uma micro-tarefa, mas visto que a fila de micro-tarefa é processada no sentido de que o primeiro a entrar é o primeiro a sair isso também garante que a função de resposta de promessa tem sido executada no momento que a afirmação é feita. Para uma explicação mais detalhada consulte a seguinte [ligação](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/). + +Uma outra solução é usar uma função `async` e um pacote como o [flush-promises](https://www.npmjs.com/package/flush-promises). O `flush-promises` libera todos manipuladores de promessa pendentes resolvidas. Você pode `await` a chamada de `flushPromises` para liberar promessas pendentes e melhorar a legibilidade do seu teste. + +O teste atualizado parece com isto: + +```js +import { shallowMount } from '@vue/test-utils' +import flushPromises from 'flush-promises' +import Foo from './Foo' +jest.mock('axios') + +it('fetches async when a button is clicked', async () => { + const wrapper = shallowMount(Foo) + wrapper.find('button').trigger('click') + await flushPromises() + expect(wrapper.text()).toBe('value') +}) +``` + +Esta mesma técnica pode ser aplicada às ações de Vuex, as quais retornam uma promessa por padrão. + +#### Porquê não apenas `await button.trigger()` ? + +Como explicado acima, existe uma diferença entre o tempo que leva para o Vue atualizar seus componentes, e o tempo que leva para uma promessa, como aquela de `axios` resolver. + +Um ótima regra para seguir é sempre esperar (`await`) em mutações como o `trigger` ou o `setProps`. +Se o seu código depende de algo assíncrono, como a chamada de `axios`, adicione uma espera (`await`) para a chamada `flushPromises` também. diff --git a/docs/pt/guides/useful-libraries-for-testing.md b/docs/pt/guides/useful-libraries-for-testing.md new file mode 100644 index 000000000..a20d29cc2 --- /dev/null +++ b/docs/pt/guides/useful-libraries-for-testing.md @@ -0,0 +1,21 @@ +## Bibliotecas Úteis para Testes + +A Vue Test Utils fornece métodos úteis para testes de componentes de Vue.js. Os membros da comunidade tem também escrito algumas bibliotecas adicionais as quais ou estendem a `vue-test-utils` com métodos extras úteis, ou fornecem ferramentas para testes de outras coisas encontradas dentro de aplicações de Vue.js. + +### A Biblioteca de Testes de Vue + +A [Vue Testing Library ou Biblioteca de Testes de Vue](https://github.com/testing-library/vue-testing-library) é um conjunto de ferramentas focadas em testes de componentes sem depender de detalhes de implementação. Construida com a acessibilidade em mente, sua abordagem também torna a refatoração uma brisa. + +É construida sobre a Vue Test Utils. + +### `vuex-mock-store` + +A [`vuex-mock-store`](https://github.com/posva/vuex-mock-store) fornece uma simples e direta imitação da memória para simplificar os testes de componentes consumindo uma memória de Vuex. + +### `jest-matcher-vue-test-utils` + +A [`jest-matcher-vue-test-utils`](https://github.com/hmsk/jest-matcher-vue-test-utils) adiciona correspondentes adicionais para o executor de teste Jest com o propósito de tornar as afirmações mais expressivas. + +### `jest-mock-axios` + +A [`jest-mock-axios`](https://github.com/knee-cola/jest-mock-axios) permite você imitar facilmente o `axios`, um cliente de HTTP comum, dentro de seus testes. Ele funciona fora da caixa com Jest, e o autor fornece um guia de como suportar outros executores de teste dentro da documentação. diff --git a/docs/pt/guides/using-with-typescript.md b/docs/pt/guides/using-with-typescript.md new file mode 100644 index 000000000..9e0f5b94d --- /dev/null +++ b/docs/pt/guides/using-with-typescript.md @@ -0,0 +1,157 @@ +## Usando com o TypeScript + +> Um exemplo de projeto para este configuração está disponível no [GitHub](https://github.com/vuejs/vue-test-utils-typescript-example). + +O TypeScript é um superconjunto popular de JavaScript que adiciona tipos e classes sobre o JavaScript habitual. A Vue Test Utils inclui tipos dentro do pacote distribuído, assim ela funciona bem com o TypeScript. + +Neste guia, nós iremos caminhar através de como definir uma configuração de estes para um projeto de TypeScript usando Jest e a Vue Test Utils a partir de uma configuração básica de TypeScript da Vue CLI. + +### Adicionado o TypeScript + +Primeiro, você precisa criar um projeto. Se você não tiver a Vue CLI instalada, instale ela globalmente: + +```shell +$ npm install -g @vue/cli +``` + +E crie um projeto ao executar: + +```shell +$ vue create hello-world +``` + +No pronto da CLI, escolha a opção `Manually select features`, selecione TypeScript, e pressione Enter. Isto criará um projeto com o TypeScript já configurado. + +::: tip NOTA +Se você quiser mais um guia mais detalhado sobre a configuração de Vue com o TypeScript, consulte o [guia de iniciação de Vue com TypeScript](https://github.com/Microsoft/TypeScript-Vue-Starter). +::: + +O próximo passo é adicionar o Jest ao projeto. + +### Configurando o Jest + +O Jest é um executor de teste desenvolvido pelo Facebook, com o propósito de entregar uma solução de testes unitários com baterias incluídas. Você pode aprender mais sobre o Jest na sua [documentação oficial](https://jestjs.io/). + +Instale o Jest e a Vue Test Utils: + +```bash +$ npm install --save-dev jest @vue/test-utils +``` + +Depois defina um roteiro `test:unit` dentro de `package.json`. + +```json +// package.json +{ + // .. + "scripts": { + // .. + "test:unit": "jest" + } + // .. +} +``` + +### Processando Componentes de Único Ficheiro dentro do Jest + +Para ensinar o Jest a como processar ficheiros `*.vue`, nós precisamos instalar e configurar o pré-processador `vue-jest`: + +```bash +npm install --save-dev vue-jest +``` + +Depois, criar um bloco de `jest` dentro de `package.json`: + +```json +{ + // ... + "jest": { + "moduleFileExtensions": [ + "js", + "ts", + "json", + // diz ao Jest para manipular ficheiros `*.vue` + "vue" + ], + "transform": { + // processa ficheiros `*.vue` com o `vue-jest` + ".*\\.(vue)$": "vue-jest" + }, + "testURL": "http://localhost/" + } +} +``` + +### Configurando o TypeScript para o Jest + +No sentido de usar ficheiros de TypeScript dentro de testes, nós precisamos configurar o Jest para compilar o TypeScript. Para isso nós precisamos instalar o `ts-jest`: + +```bash +$ npm install --save-dev ts-jest +``` + +Depois, nós precisamos dizer ao Jest para processar os ficheiros de teste em TypeScript com o `ts-jest` ao adicionar um entrada sob `jest.transform` dentro de `package.json`: + +```json +{ + // ... + "jest": { + // ... + "transform": { + // ... + // processar os ficheiros `*.ts` com o `ts-jest` + "^.+\\.tsx?$": "ts-jest" + } + // ... + } +} +``` + +### Colocação de Ficheiros de Teste + +Por padrão, o Jest selecionará recursivamente todos ficheiros que têm uma extensão `.spec.js` ou `.test.js` dentro do projeto inteiro. + +Para executar o teste de ficheiros com uma extensão `.ts`, nós precisamos mudar a `testRegex` na secção de configuração dentro do ficheiro `package.json`. + +Adicione o seguinte ao campo `jest` dentro de `package.json`: + +```json +{ + // ... + "jest": { + // ... + "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$" + } +} +``` + +O Jest recomenda a criação de um diretório `__tests__` exatamente próximo ao código a ser testado, mas esteja à vontade para estruturar os seus testes como você desejar. Apenas saiba que o Jest criaria um diretório `__snapshots__` próximo aos ficheiros de teste que executam testes instantâneos. + +### Escrevendo um Teste Unitário + +Agora que nós temos o projeto configurado, é hora de escrever um teste unitário. + +Crie um ficheiro `src/components/__tests__/HelloWorld.spec.ts`, e adicione o seguinte código: + +```js +// src/components/__tests__/HelloWorld.spec.ts +import { shallowMount } from '@vue/test-utils' +import HelloWorld from '../HelloWorld.vue' + +describe('HelloWorld.vue', () => { + test('renders props.msg when passed', () => { + const msg = 'new message' + const wrapper = shallowMount(HelloWorld, { + propsData: { msg } + }) + expect(wrapper.text()).toMatch(msg) + }) +}) +``` + +É tudo o que nós precisamos fazer para ter o TypeScript e a Vue Test Utils trabalhando juntos! + +### Recursos + +- [Exemplo de projeto para esta configuração](https://github.com/vuejs/vue-test-utils-typescript-example) +- [Jest](https://jestjs.io/) diff --git a/docs/pt/guides/using-with-vue-router.md b/docs/pt/guides/using-with-vue-router.md new file mode 100644 index 000000000..03d81f26e --- /dev/null +++ b/docs/pt/guides/using-with-vue-router.md @@ -0,0 +1,86 @@ +## Usando com o Vue Router + +### Instalando o Vue Router dentro de testes + +Você nunca deve instalar o Vue Router sobre o construtor base de Vue dentro de testes. A instalação de Vue Router adiciona `$route` e `$router` como propriedades de apenas leitura sobre o protótipo de Vue. + +Para evitar isso, nós podemos criar um `localVue`, e instalar o Vue Router sobre ele. + +```js +import { shallowMount, createLocalVue } from '@vue/test-utils' +import VueRouter from 'vue-router' + +const localVue = createLocalVue() +localVue.use(VueRouter) +const router = new VueRouter() + +shallowMount(Component, { + localVue, + router +}) +``` + +> **Nota:** A instalação de Vue Router sobre um `localVue` também adiciona o `$route` e `$router` como propriedades de apenas leitura ao `localVue`. Isto significa que você não pode usar a opção `mocks` para sobrescrever o `$route` e o `$router` quando estiver montando um componente usando um `localVue` com o Vue Router instalado. + +### Testando componentes que usam o `router-link` ou `router-view` + +Quando você instalar o Vue Router, os componentes `router-link` e `router-view` são registados. Isto significa que nós podemos usar eles em qualquer lugar dentro da aplicação sem a necessidade de importar eles. + +Quando nós executamos os testes, nós precisamos tornar estes componentes de Vue Router disponíveis para o componente que estamos montando. Há dois métodos de fazer isso. + +### Usando os stubs + +```js +import { shallowMount } from '@vue/test-utils' + +shallowMount(Component, { + stubs: ['router-link', 'router-view'] +}) +``` + +### Instalando o Vue Router com o `localVue` + +```js +import { mount, createLocalVue } from '@vue/test-utils' +import VueRouter from 'vue-router' + +const localVue = createLocalVue() +localVue.use(VueRouter) + +mount(Component, { + localVue, + router +}) +``` + +A instância do roteador está disponível para todos componentes filhos, isto é útil para testes de nível de integração. + +### Imitando o `#route` e o `$router` + +Algumas vezes você deseja testar aquele componente que faz alguma coisa com parâmetros dos objetos `$route` e `$router`. Para fazer isso, você pode passar imitações personalizadas para a instância de Vue. + +```js +import { shallowMount } from '@vue/test-utils' + +const $route = { + path: '/some/path' +} + +const wrapper = shallowMount(Component, { + mocks: { + $route + } +}) + +wrapper.vm.$route.path // /some/path +``` + +> **Nota:** os valores imitados de `$route` e `$router` não estão disponíveis aos componentes filhos, ou forje estes componentes ou use o método `localVue`. + +### Conclusão + +A instalação de Vue Router adiciona o `$route` e o `$router` como propriedades de apenas leitura sobre o protótipo de Vue. + +Isso significa que quaisquer testes futuros que tentar imitar o `$route` ou o `$router` falhará. + +Para evitar isso, nunca instale o Vue Router globalmente quando você estiver executando os testes; use um `localVue` como detalhado acima. diff --git a/docs/pt/guides/using-with-vuex.md b/docs/pt/guides/using-with-vuex.md new file mode 100644 index 000000000..311c0cb17 --- /dev/null +++ b/docs/pt/guides/using-with-vuex.md @@ -0,0 +1,393 @@ +# Usando com a Vuex + +Neste guia, iremos ver como testar a Vuex em componentes com a Vue Test Utils, e como testar uma memória da Vuex. + + + +## Testando a Vuex em componentes + +### Imitando Ações + +Vamos ver um pouco de código. + +Isto é o componente que nós queremos testar. Ele chama as ações da Vuex. + +```html + + + +``` + +Para o propósito deste teste, nós não temos interesse no que as ações fazem, ou em como a memória se parece. Nós apenas precisamos saber que estas ações estão sendo disparadas quando elas deveriam, e que elas são disparadas com o valor esperado. + +Para testar isto, nós precisamos passar uma imitação da memória para a Vue quando nós montamos superficialmente (shallowMount) o nosso componente. + +Ao invés de passar a memória para o construtor base da Vue, nós podemos passar ela para um - [localVue](../api/options.md#localvue). Um localVue é um construtor isolado da Vue que nós podemos realizar mudanças sem afetar o construtor global da Vue. + +Veremos que isto se parece: + +```js +import { shallowMount, createLocalVue } from '@vue/test-utils' +import Vuex from 'vuex' +import Actions from '../../../src/components/Actions' + +const localVue = createLocalVue() + +localVue.use(Vuex) + +describe('Actions.vue', () => { + let actions + let store + + beforeEach(() => { + actions = { + actionClick: jest.fn(), + actionInput: jest.fn() + } + store = new Vuex.Store({ + actions + }) + }) + + it('dispatches "actionInput" when input event value is "input"', () => { + const wrapper = shallowMount(Actions, { store, localVue }) + const input = wrapper.find('input') + input.element.value = 'input' + input.trigger('input') + expect(actions.actionInput).toHaveBeenCalled() + }) + + it('does not dispatch "actionInput" when event value is not "input"', () => { + const wrapper = shallowMount(Actions, { store, localVue }) + const input = wrapper.find('input') + input.element.value = 'not input' + input.trigger('input') + expect(actions.actionInput).not.toHaveBeenCalled() + }) + + it('calls store action "actionClick" when button is clicked', () => { + const wrapper = shallowMount(Actions, { store, localVue }) + wrapper.find('button').trigger('click') + expect(actions.actionClick).toHaveBeenCalled() + }) +}) +``` + +O que está acontecendo aqui? Primeiro nós dizemos a Vue para usar a Vuex com o método `localVue`. Isto é apenas um envolvedor em volta da `Vue.use`. + +Nós depois fazemos uma imitação da memória ao chamar `new Vuex.Store` com as nossas imitações de valores. Nós apenas passamos ela para as ações, visto que é tudo com o que nós nos preocupamos. + +As ações são [funções de imitação da jest](https://jestjs.io/docs/en/mock-functions.html). Estas funções de imitação nos dão métodos para afirmar se as ações foram chamadas ou não. + +Nós podemos então afirmar em nossos testes que a ação forjada foi chamada quando esperada. + +Agora a maneira que nós definimos a memória pode parecer um pouco estranha para você. + +Estamos usando `beforeEach` para garantir que nós temos uma memória limpa antes de cada teste. O `beforeEach` é um gatilho da mocha que é chamada antes de cada teste. No nosso teste, nós estamos re-atribuindo os valores das variáveis da memória. Se nós não fizéssemos isto, as funções de imitação precisariam ser automaticamente re-definidas. Ela também nos deixa mudar o estado em nossos testes, sem isto afetar os testes futuros. + +A coisa mais importante a anotar neste teste é que **nós criamos uma imitação da memória da Vuex e depois passamos ela para a Vue Test Utils**. + +Excelente, agora nós podemos imitar as ações, veremos em imitando os getters. + +### Imitando os Getters + +```html + + + +``` + +Este é um componente razoavelmente simples. Ele renderiza o resultado dos getters `clicks` e `inputValue`. Novamente, nós não nos preocupamos com o que os getters retornam – apenas que seus resultados estão sendo renderizado corretamente. + +Vejamos o teste: + +```js +import { shallowMount, createLocalVue } from '@vue/test-utils' +import Vuex from 'vuex' +import Getters from '../../../src/components/Getters' + +const localVue = createLocalVue() + +localVue.use(Vuex) + +describe('Getters.vue', () => { + let getters + let store + + beforeEach(() => { + getters = { + clicks: () => 2, + inputValue: () => 'input' + } + + store = new Vuex.Store({ + getters + }) + }) + + it('Renders "store.getters.inputValue" in first p tag', () => { + const wrapper = shallowMount(Getters, { store, localVue }) + const p = wrapper.find('p') + expect(p.text()).toBe(getters.inputValue()) + }) + + it('Renders "store.getters.clicks" in second p tag', () => { + const wrapper = shallowMount(Getters, { store, localVue }) + const p = wrapper.findAll('p').at(1) + expect(p.text()).toBe(getters.clicks().toString()) + }) +}) +``` + +O teste é similar aos nossas ações de teste. Nós criamos uma imitação de memória antes de cada teste, passamos ele como uma opção quando nós chamamos `shallowMount`, e afirmar que o valor renderizado pela nossa imitação de getters está sendo renderizada. + +Isto é genial, mas se nós quiséssemos verificar se nossos getters estão retornando a parte correta do nosso estado? + +### Imitando com Módulos + +Os [módulos](https://vuex.vuejs.org/guide/modules.html) são úteis para separação da nossa memória em pedaços gerenciáveis. Eles também exportam os getters. Nós podemos usar estes nos nossos testes. + +Vejamos o nosso componente: + +```html + + + +``` + +Componente simples que inclui uma ação e o getter. + +E o teste: + +```js +import { shallowMount, createLocalVue } from '@vue/test-utils' +import Vuex from 'vuex' +import MyComponent from '../../../src/components/MyComponent' +import myModule from '../../../src/store/myModule' + +const localVue = createLocalVue() + +localVue.use(Vuex) + +describe('MyComponent.vue', () => { + let actions + let state + let store + + beforeEach(() => { + state = { + clicks: 2 + } + + actions = { + moduleActionClick: jest.fn() + } + + store = new Vuex.Store({ + modules: { + myModule: { + state, + actions, + getters: myModule.getters, + namespaced: true + } + } + }) + }) + + it('calls store action "moduleActionClick" when button is clicked', () => { + const wrapper = shallowMount(MyComponent, { store, localVue }) + const button = wrapper.find('button') + button.trigger('click') + expect(actions.moduleActionClick).toHaveBeenCalled() + }) + + it('renders "state.clicks" in first p tag', () => { + const wrapper = shallowMount(MyComponent, { store, localVue }) + const p = wrapper.find('p') + expect(p.text()).toBe(state.clicks.toString()) + }) +}) +``` + +### Testando uma Memória da Vuex + +Existem duas abordagens para testes de uma memória da Vuex. A primeira abordagem é fazer testes unitários de getters, mutações, e ações separadamente. A segunda abordagem é criar uma memória e testar ela. Veremos ambas abordagens. + +Para ver como testar uma memória da Vuex, iremos criar um simples memória de contador (counter). A memória terá uma mutação `increment` e um getter `evenOrOdd`. + +```js +// mutations.js +export default { + increment(state) { + state.count++ + } +} +``` + +```js +// getters.js +export default { + evenOrOdd: state => (state.count % 2 === 0 ? 'even' : 'odd') +} +``` + +### Testando getters, mutações, e ações separadamente + +Os getters, mutações, e ações são todas funções de JavaScript, então nós podemos testar elas sem usar Vue Test Utils e Vuex. + +O benefício de testar os getters, mutações, e ações separadamente é que seus testes unitários são detalhados. Quando eles falham, você sabe exatamente o que está errado com o seu código. A desvantagem é que você precisará imitar as funções de Vuex, como `commit` e o `dispatch`. Isto pode levar para uma situação em que seus testes unitários passam, mas seu código de produção falha porque suas imitações estão incorretas. + +Criaremos dois ficheiros de teste, o `mutations.spec.js` e o `getters.spec.js`: + +Primeiro, vamos testar as mutações de `increment`: + +```js +// mutations.spec.js + +import mutations from './mutations' + +test('"increment" increments "state.count" by 1', () => { + const state = { + count: 0 + } + mutations.increment(state) + expect(state.count).toBe(1) +}) +``` + +Agora vamos testar o getter `evenOrOdd`. Nós podemos testar ele ao criar uma imitação `state`, chamando o getter com o `state` e verificar se ele retorna o valor correto. + +```js +// getters.spec.js + +import getters from './getters' + +test('"evenOrOdd" returns even if "state.count" is even', () => { + const state = { + count: 2 + } + expect(getters.evenOrOdd(state)).toBe('even') +}) + +test('"evenOrOdd" returns odd if "state.count" is odd', () => { + const state = { + count: 1 + } + expect(getters.evenOrOdd(state)).toBe('odd') +}) +``` + +### Testando uma execução da memória + +Um outra abordagem para testar uma memória da Vuex é criar uma execução da memória usando a configuração da memória. + +O benefício da criação de uma instância de execução da memória é que nós não temos que imitar nenhuma função da Vuex. + +A desvantagem é que quando um teste quebrar, pode ser difícil encontrar onde o problema está. + +Vamos escrever um teste. Quando nós criamos uma memória, usaremos o `localVue` para evitar a poluição da base do construtor da Vue. O teste cria uma memória usando a exportação de `store-config.js`: + +```js +// store-config.js + +import mutations from './mutations' +import getters from './getters' + +export default { + state: { + count: 0 + }, + mutations, + getters +} +``` + +```js +// store-config.spec.js + +import { createLocalVue } from '@vue/test-utils' +import Vuex from 'vuex' +import storeConfig from './store-config' +import { cloneDeep } from 'lodash' + +test('increments "count" value when "increment" is committed', () => { + const localVue = createLocalVue() + localVue.use(Vuex) + const store = new Vuex.Store(cloneDeep(storeConfig)) + expect(store.state.count).toBe(0) + store.commit('increment') + expect(store.state.count).toBe(1) +}) + +test('updates "evenOrOdd" getter when "increment" is committed', () => { + const localVue = createLocalVue() + localVue.use(Vuex) + const store = new Vuex.Store(cloneDeep(storeConfig)) + expect(store.getters.evenOrOdd).toBe('even') + store.commit('increment') + expect(store.getters.evenOrOdd).toBe('odd') +}) +``` + +Repare que nós usamos o `cloneDeep` para clonar a configuração da memória antes da criação uma memória com ela. Isto porque a Vuex realiza mutações nas opções do objeto usado para criar a memória. Para ter a certeza que nós temos uma memória limpa em cada teste, nós precisamos clonar o objeto `storeConfig`. + +No entanto, o `cloneDeep` não é "profunda (deep)" o suficiente para também clonar os módulos da memória. Se a sua `storeConfig` incluírem os módulos, você precisará passar um objeto para `new Vuex.Store()`, deste jeito: + +```js +import myModule from './myModule' +// ... +const store = new Vuex.Store({ modules: { myModule: cloneDeep(myModule) } }) +``` + +### Recursos + +- [Projeto de exemplo para testes de componentes](https://github.com/eddyerburgh/vue-test-utils-vuex-example) +- [Projeto de exemplo para testes da memória](https://github.com/eddyerburgh/testing-vuex-store-example) +- [`localVue`](../api/options.md#localvue) +- [`createLocalVue`](../api/createLocalVue.md) diff --git a/docs/pt/installation/README.md b/docs/pt/installation/README.md new file mode 100644 index 000000000..6b31dbce9 --- /dev/null +++ b/docs/pt/installation/README.md @@ -0,0 +1,5 @@ +# Instalação + +!!!include(docs/pt/installation/semantic-versioning.md)!!! +!!!include(docs/pt/installation/using-with-jest.md)!!! +!!!include(docs/pt/installation/using-other-test-runners.md)!!! diff --git a/docs/pt/installation/semantic-versioning.md b/docs/pt/installation/semantic-versioning.md new file mode 100644 index 000000000..b05837c2d --- /dev/null +++ b/docs/pt/installation/semantic-versioning.md @@ -0,0 +1,3 @@ +## Versionamento Semântico + +O Vue Test Utils segue o [Versionamento Semântico](https://semver.org/) dentro de todos os seus projetos oficiais para funcionalidades documentadas e comportamento. Para comportamento não documentado ou interior exposto, as mudanças são descritas dentro das [notas de lançamento](https://github.com/vuejs/vue-test-utils/releases). diff --git a/docs/pt/installation/using-other-test-runners.md b/docs/pt/installation/using-other-test-runners.md new file mode 100644 index 000000000..de70fb5f4 --- /dev/null +++ b/docs/pt/installation/using-other-test-runners.md @@ -0,0 +1,60 @@ +## Usando outros executores de Teste + +### Executando o Vue Test Utils com o Karma + +O [Karma](http://karma-runner.github.io/) é um executor de teste que lança o navegador, executa os testes, e reporta eles de volta para nós. + +Adicionalmente ao Karma, você pode desejar usar o framework [Mocha](https://mochajs.org/) para escrever os testes e a biblioteca [Chai](http://chaijs.com/) para afirmação de teste. Também, você talvez também deseja verificar o [Sinon](http://sinonjs.org/) para criação de espiões e forjados. + +A seguir é uma configuração básica do Karma para o Vue Test Utils: + +```js +// karma.conf.js +var webpackConfig = require('./webpack.config.js') + +module.exports = function(config) { + config.set({ + frameworks: ['mocha'], + files: ['test/**/*.spec.js'], + webpack: webpackConfig, + reporters: ['spec'], + browsers: ['Chrome'], + preprocessors: { + '**/*.spec.js': ['webpack', 'sourcemap'] + } + }) +} +``` + +### Executando o Vue Test Utils com o mocha webpack + +Uma outra estratégia para testar Componentes de Ficheiro Único (SFCs em Inglês) é compilar todos os nossos testes via webpack e depois executar ele dentro de um executor de teste. A vantagem desta abordagem é que ela dá para nós suporte completo para todas as funcionalidades do webpack e do `vue-loader`, assim nós não temos que fazer acordos dentro do nosso código-fonte. + +Nós selecionamos o [`mochapack`](https://github.com/sysgears/mochapack) para fornecer uma experiência toda otimizada para esta tarefa em particular. + +A primeira coisa a fazer é a instalação das dependências de teste: + +```bash +npm install --save-dev @vue/test-utils mocha mochapack +``` + +Depois da instalação do Vue Test Utils e o `mochapack`, você precisará definir um roteiro de teste dentro do seu `package.json`: + +```json +// package.json +{ + "scripts": { + "test": "mochapack --webpack-config webpack.config.js --require test/setup.js test/**/*.spec.js" + } +} +``` + +### Executando o Vue Test Utils sem uma etapa de construção + +Enquanto é comum construir aplicações em Vue usando ferramentas taís como o [webpack](https://webpack.js.org/) para empacotar a aplicação, `vue-loader` para entregar Componentes de Ficheiro Único (SFC em Inglês), é possível usar o Vue Test Utils com muito menos. Os requisitos mínimos para a Vue Test Utils, além dela mesma são: + +- Vue +- [vue-template-compiler](https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme) +- um DOM (seja ele [jsdom](https://github.com/jsdom/jsdom) dentro de um ambiente Node, ou o DOM dentro de um navegador real) + +Repare que o `jsdom` (ou qualquer outra implementação do DOM) deve ser exigido e instalado antes da Vue Test Utils, porque ele espera um DOM (DOM real, ou JSDOM) existir. diff --git a/docs/pt/installation/using-with-jest.md b/docs/pt/installation/using-with-jest.md new file mode 100644 index 000000000..aa3fd3056 --- /dev/null +++ b/docs/pt/installation/using-with-jest.md @@ -0,0 +1,119 @@ +## Usando o Vue Test Utils com o Jest (recomendado) + +O Jest é um executor de teste desenvolvido pelo Facebook (agora Meta), com o objetivo de entregar uma solução de testes unitários com baterias inclusas. Você pode aprender mais sobre o Jest em sua [documentação oficial](https://jestjs.io/). + + + +### Instalação com a Vue CLI (recomendado) + +Se você estiver usando a Vue CLI para construir o seu projeto, você pode usar o plugin [cli-plugin-unit-jest](https://cli.vuejs.org/core-plugins/unit-jest.html) para executar testes do Jest. + +```bash +$ vue add unit-jest +``` + +O plugin empurra todas dependências obrigatórias (incluindo o jest), cria um ficheiro `jest.config.js` com padrões sensíveis, e gera um exemplo de conjunto de teste. + +Depois disso, tudo o que você precisa fazer é instalar a Vue Test Utils. + +```bash +$ npm install --save-dev @vue/test-utils +``` + +### Instalação Manual + +Depois de configurar o Jest, a primeira coisa que você precisa fazer é instalar a Vue Test Utils e a [`vue-jest`](https://github.com/vuejs/vue-jest) para processar os Componentes de Ficheiro Único: + +```bash +$ npm install --save-dev @vue/test-utils vue-jest +``` + +A seguir, você precisa dizer ao Jest para transformar ficheiros `.vue` usando `vue-jest`. Você pode fazer isso ao adicionar a seguinte configuração dentro do `package.json` ou dentro de um [ficheiro dedicado de configuração do Jest](https://jestjs.io/docs/en/configuration): + +```json +{ + "jest": { + "moduleFileExtensions": [ + "js", + "json", + // dizer ao Jest para manipular ficheiros `*.vue` + "vue" + ], + "transform": { + // processar ficheiros `*.vue` com o `vue-jest` + ".*\\.(vue)$": "vue-jest" + } + } +} +``` + +#### Usando com o Babel + +Se você for usar o `babel` e importar componentes de vue de ficheiro único com a extensão `.vue` dentro dos seus testes, você precisará instalar o babel e transformar os ficheiros com o `babel-jest`. + +```bash +npm install --save-dev babel-jest @babel/core @babel/preset-env babel-core@^7.0.0-bridge.0 +``` + +Depois, você precisa dizer ao Jest para transformar ficheiros os `.js` usando o `babel-jest`. Você pode fazer isso adicionando a seguinte configuração dentro do `package.json` ou dentro de um [ficheiro dedicado de configuração do Jest](https://jestjs.io/docs/en/configuration): + +```json +{ + "jest": { + "transform": { + // processa ficheiros `*.js` com o `babel-jest` + ".*\\.(js)$": "babel-jest" + } + } +} +``` + +A seguir você precisa criar a configuração de babel usando os ficheiros de configuração [babel.config.json](https://babeljs.io/docs/en/configuration#babelconfigjson) ou o [.babelrc.json](https://babeljs.io/docs/en/configuration#babelrcjson): + +```json +{ + "presets": ["@babel/preset-env"] +} +``` + +Você pode também adicionar estas opções ao `package.json`: + +```json +{ + "babel": { + "presets": ["@babel/preset-env"] + } +} +``` + +#### Manipulando os apelidos (aliases) do webpack + +Se você usar um resolvedor de apelidos dentro da configuração do webpack, por exemplo apelidando o `@` para o `/src`, você precisa adicionar uma configuração de correspondência para o Jest também, usando a opção `moduleNameMapper`: + +```json +{ + "jest": { + // suportar o mesmo mapeamento de apelido de `@ -> src` dentro do código fonte. + "moduleNameMapper": { + "^@/(.*)$": "/src/$1" + } + } +} +``` + +### Cobertura de Código + +O Jest pode ser usado para gerar relatórios de cobertura em vários formatos. Isto está desativado por padrão (para ambas instalação via vue-cli e para uma manual). A seguir está um exemplo simples para começar com: + +Estenda a sua configuração `jest` com a opção [`collectCoverage`](https://jestjs.io/docs/en/configuration#collectcoverage-boolean), e depois adicione o arranjo [`collectCoverageFrom`](https://jestjs.io/docs/en/configuration#collectcoveragefrom-array) para definir os ficheiros para os quais a informações de cobertura devem ser coletadas. + +```json +{ + "jest": { + "collectCoverage": true, + "collectCoverageFrom": ["**/*.{js,vue}", "!**/node_modules/**"] + } +} +``` + +Isto ativará os relatórios de cobertura com os [relatórios de cobertura padrão](https://jestjs.io/docs/en/configuration#coveragereporters-array-string). A documentação mais avançada pode ser encontrada dentro da [documentação da configuração do Jest](https://jestjs.io/docs/en/configuration#collectcoverage-boolean), onde você pode encontrar opções para os limites de cobertura, alvo de diretórios de saída, etc. diff --git a/docs/pt/upgrading-to-v1/README.md b/docs/pt/upgrading-to-v1/README.md new file mode 100644 index 000000000..90a3cfb5f --- /dev/null +++ b/docs/pt/upgrading-to-v1/README.md @@ -0,0 +1,84 @@ +### Atualizando para V1.0 + +Depois de um longo período em Beta, a Vue Test Utils finalmente lançou a versão 1.0! Nós depreciamos alguns métodos que não eram úteis, então você poderá ver vários avisos de depreciação depois da atualização. Este guia ajudará você a migrar para longe deles. + +Você pode ler as notas de lançamentos para versão 1 [aqui](https://github.com/vuejs/vue-test-utils/releases) ou as discussões sobre as depreciações [aqui](https://github.com/vuejs/rfcs/pull/161). + +### O método `find` + +Na beta, o método `find` poderia ser usado para buscar ambos nós do DOM (usando a sintaxe de `querySelector`) ou um componente (através de uma referência de componente, uma opção `ref` ou uma opção `name`). Este comportamento agora está dividido em dois métodos: `find` e `findComponent`. + +Se você estava usando esta sintaxe: + +- `find(Foo)` +- `find({ name: 'foo' })` +- `find({ ref: 'my-ref' })` + +Mude-os para serem `findComponent`. + +Você pode continuar usando o `find` em nós do DOM usando a sintaxe de `querySelector`. + +### O método `isVueInstance` + +Este método foi depreciado porque ele tende a encorajar detalhes de implementação de testes, o que é uma má prática. As afirmações usando isto podem simplesmente ser removidas; se você realmente precisar de um substituto, você pode usar o `expect((...).vm).toBeTruthy()`, o que é basicamente o que o método `isVueInstance` está fazendo. + +### O método `contains` + +Os testes usando o método `contains` podem ser substituídos por `find` ou `findComponent` ou `get`. Por exemplo, `expect(wrapper.contains('#el')).toBe(true)` pode ser escrito como `wrapper.get('#el')`, o que lançará um erro se o seletor não for correspondido. Um outra maneira de escrever isto usando o `find` é `expect(wrapper.find('#el').element).toBeTruthy()`. + +### O método `is` + +Você pode sobrescrever os testes usando o `is` para usar `element.tagName` no lugar. Por exemplo, `wrapper.find('div').is('div')` pode ser escrito como `wrapper.find('div').element.tagName`. + +### O método `isEmpty` + +Saber se um nó do DOM está vazio não é uma funcionalidade especifica da Vue, e não é algo que é difícil de saber. No lugar de reinventar a roda, nós decidimos que é melhor delegar para uma solução bem testa existente por padrão. Considere o excelente correspondente `toBeEmpty` da [jest-dom](https://github.com/testing-library/jest-dom#tobeempty), por exemplo, se você estiver usando o Jest. + +### O método `name` + +Afirmações contra o método `name` encorajam detalhes de implementação de testes, o que é uma má prática. Se você precisar desta funcionalidade, você pode usar o `vm.$options.name` para componentes de Vue ou `element.tagName` para nós do DOM. Novamente, considere se você realmente precisa deste teste - é provável que você não precisa. + +### As opções `setMethods` e `mountingOptions.methods` + +Ao usar `setMethods`, você está fazendo mutação da instância da Vue - isto não é algo que a Vue suporte, e pode levar para testes escamosos e acoplados. + +Não existe uma substituição direta para isto. Se você tiver um método complexo você gostaria de apagar, considere mover ele para um outro ficheiro e usando a funcionalidade de imitar ou forjar do seu executor de teste. + +Por exemplo, você pode querer evitar uma chamada de API: + +```js +const Foo = { + created() { + this.getData() + }, + methods: { + getData() { + axios.get('...') + } + } +} +``` + +Em lugar de fazer: + +```js +mount(Foo, { + methods: { + getData: () => {} + } +}) +``` + +Imite a dependência `axios`. No Jest, por exemplo, você pode fazer isso com `jest.mock('axios')`. Isto evitará a chamada da API, sem mutação da seu componente de Vue. + +Se você precisar de mais ajuda para a migração, você pode juntar-se ao [servidor VueLand](https://chat.vuejs.org/) no Discord. + +### Avisos de Depreciação + +Os avisos de depreciação podem ser silenciados. + +```js +import { config } from '@vue/test-utils' + +config.showDeprecationWarnings = false +```