From 7dd4d1d1938748aa47a2e28a2c149eef60d71bff Mon Sep 17 00:00:00 2001 From: winniehell Date: Mon, 27 May 2019 21:24:34 +0200 Subject: [PATCH 1/6] feat: Introduce enableAutoDestroy() helper function --- docs/api/README.md | 1 + docs/api/enableAutoDestroy.md | 25 ++++++++++++++++++++ packages/test-utils/src/index.js | 3 ++- packages/test-utils/src/wrapper.js | 25 ++++++++++++++++++++ test/specs/wrapper.spec.js | 38 ++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 docs/api/enableAutoDestroy.md diff --git a/docs/api/README.md b/docs/api/README.md index e14cacb49..76699876e 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -8,3 +8,4 @@ !!!include(docs/api/createLocalVue.md)!!! !!!include(docs/api/createWrapper.md)!!! !!!include(docs/api/config.md)!!! +!!!include(docs/api/enableAutoDestroy.md)!!! diff --git a/docs/api/enableAutoDestroy.md b/docs/api/enableAutoDestroy.md new file mode 100644 index 000000000..ab33b9b43 --- /dev/null +++ b/docs/api/enableAutoDestroy.md @@ -0,0 +1,25 @@ +## enableAutoDestroy(hook) + +- **Arguments:** + + - `{Function} hook` + +- **Usage:** + +`enableAutoDestroy` allows to destroy all created `Wrapper` instances using the passed hook function (for example [`afterEach`](https://jestjs.io/docs/en/api#aftereachfn-timeout)). + +```js +import { enableAutoDestroy, mount } from '@vue/test-utils' +import Foo from './Foo.vue' + +// calls wrapper.destroy() after each test +enableAutoDestroy(afterEach) + +describe('Foo', () => { + it('renders a div', () => { + const wrapper = mount(Foo) + expect(wrapper.contains('div')).toBe(true) + // no need to call wrapper.destroy() here + }) +}) +``` diff --git a/packages/test-utils/src/index.js b/packages/test-utils/src/index.js index 6e073991b..c784a5bc9 100644 --- a/packages/test-utils/src/index.js +++ b/packages/test-utils/src/index.js @@ -3,7 +3,7 @@ import mount from './mount' import createLocalVue from './create-local-vue' import RouterLinkStub from './components/RouterLinkStub' import createWrapper from './create-wrapper' -import Wrapper from './wrapper' +import Wrapper, { enableAutoDestroy } from './wrapper' import WrapperArray from './wrapper-array' import config from './config' import { warn } from 'shared/util' @@ -20,6 +20,7 @@ export default { createLocalVue, createWrapper, config, + enableAutoDestroy, mount, shallow, shallowMount, diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 4810e75da..736eb3734 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -25,6 +25,7 @@ export default class Wrapper implements BaseWrapper { isFunctionalComponent: boolean rootNode: VNode | Element selector: Selector | void + static _instances: ?Array constructor( node: VNode | Element, @@ -68,6 +69,10 @@ export default class Wrapper implements BaseWrapper { ) { this.isFunctionalComponent = true } + + if (Wrapper._instances) { + Wrapper._instances.push(this) + } } at(): void { @@ -603,3 +608,23 @@ export default class Wrapper implements BaseWrapper { this.element.dispatchEvent(event) } } + +export const enableAutoDestroy = (hook: Function) => { + if (Wrapper._instances) { + throwError('enableAutoDestroy cannot be called more than once') + } + + Wrapper._instances = [] + + hook(() => { + const { _instances } = Wrapper + if (!_instances) { + return + } + + while (_instances.length > 0) { + const wrapper = _instances.pop() + wrapper.destroy() + } + }) +} diff --git a/test/specs/wrapper.spec.js b/test/specs/wrapper.spec.js index 54886499a..566873c14 100644 --- a/test/specs/wrapper.spec.js +++ b/test/specs/wrapper.spec.js @@ -1,4 +1,5 @@ import { describeWithShallowAndMount } from '~resources/utils' +import { Wrapper, enableAutoDestroy } from '~vue/test-utils' describeWithShallowAndMount('Wrapper', mountingMethod => { ;['vnode', 'element', 'vm', 'options'].forEach(property => { @@ -15,4 +16,41 @@ describeWithShallowAndMount('Wrapper', mountingMethod => { .with.property('message', message) }) }) + + describe('enableAutoDestroy', () => { + const sandbox = sinon.createSandbox() + + beforeEach(() => { + Wrapper._instances = undefined + }) + + it('calls the hook function', () => { + const hookSpy = sandbox.spy() + + enableAutoDestroy(hookSpy) + + expect(hookSpy).calledOnce + }) + + it('uses the hook function to destroy wrappers', () => { + let hookCallback + enableAutoDestroy(callback => { + hookCallback = callback + }) + const wrapper = mountingMethod({ template: '

con tent

' }) + sandbox.spy(wrapper, 'destroy') + + hookCallback() + + expect(wrapper.destroy).calledOnce + }) + + it('cannot be called twice', () => { + const noop = () => {} + + enableAutoDestroy(noop) + + expect(() => enableAutoDestroy(noop)).to.throw() + }) + }) }) From 7064ab0074890649be2979d556ed71c2e50377c1 Mon Sep 17 00:00:00 2001 From: winniehell Date: Thu, 19 Dec 2019 09:30:57 +0100 Subject: [PATCH 2/6] chore: Move enableAutoDestroy() to separate module --- packages/test-utils/src/auto-destroy.js | 27 +++++++++++++++++++++++ packages/test-utils/src/create-wrapper.js | 14 ++++++++---- packages/test-utils/src/index.js | 3 ++- packages/test-utils/src/wrapper.js | 25 --------------------- test/specs/wrapper.spec.js | 6 +---- 5 files changed, 40 insertions(+), 35 deletions(-) create mode 100644 packages/test-utils/src/auto-destroy.js diff --git a/packages/test-utils/src/auto-destroy.js b/packages/test-utils/src/auto-destroy.js new file mode 100644 index 000000000..b139393f8 --- /dev/null +++ b/packages/test-utils/src/auto-destroy.js @@ -0,0 +1,27 @@ +// @flow + +import { throwError } from 'shared/util' +import Wrapper from './wrapper' + +let wrapperInstances: Array + +export function enableAutoDestroy(hook: (() => void) => void) { + if (wrapperInstances) { + throwError('enableAutoDestroy cannot be called more than once') + } + + wrapperInstances = [] + + hook(() => { + wrapperInstances.forEach((wrapper: Wrapper) => { + wrapper.destroy() + }) + wrapperInstances = [] + }) +} + +export function trackInstance(wrapper: Wrapper) { + if (!wrapperInstances) return + + wrapperInstances.push(wrapper) +} diff --git a/packages/test-utils/src/create-wrapper.js b/packages/test-utils/src/create-wrapper.js index dff8f4346..3cdd84d22 100644 --- a/packages/test-utils/src/create-wrapper.js +++ b/packages/test-utils/src/create-wrapper.js @@ -3,6 +3,7 @@ import Vue from 'vue' import Wrapper from './wrapper' import VueWrapper from './vue-wrapper' +import { trackInstance } from './auto-destroy' export default function createWrapper( node: VNode | Component, @@ -10,9 +11,14 @@ export default function createWrapper( ): VueWrapper | Wrapper { const componentInstance = node.child if (componentInstance) { - return new VueWrapper(componentInstance, options) + const wrapper = new VueWrapper(componentInstance, options) + trackInstance(wrapper) + return wrapper } - return node instanceof Vue - ? new VueWrapper(node, options) - : new Wrapper(node, options) + const wrapper = + node instanceof Vue + ? new VueWrapper(node, options) + : new Wrapper(node, options) + trackInstance(wrapper) + return wrapper } diff --git a/packages/test-utils/src/index.js b/packages/test-utils/src/index.js index c784a5bc9..a61cdf021 100644 --- a/packages/test-utils/src/index.js +++ b/packages/test-utils/src/index.js @@ -1,9 +1,10 @@ import shallowMount from './shallow-mount' import mount from './mount' +import { enableAutoDestroy } from './auto-destroy' import createLocalVue from './create-local-vue' import RouterLinkStub from './components/RouterLinkStub' import createWrapper from './create-wrapper' -import Wrapper, { enableAutoDestroy } from './wrapper' +import Wrapper from './wrapper' import WrapperArray from './wrapper-array' import config from './config' import { warn } from 'shared/util' diff --git a/packages/test-utils/src/wrapper.js b/packages/test-utils/src/wrapper.js index 736eb3734..4810e75da 100644 --- a/packages/test-utils/src/wrapper.js +++ b/packages/test-utils/src/wrapper.js @@ -25,7 +25,6 @@ export default class Wrapper implements BaseWrapper { isFunctionalComponent: boolean rootNode: VNode | Element selector: Selector | void - static _instances: ?Array constructor( node: VNode | Element, @@ -69,10 +68,6 @@ export default class Wrapper implements BaseWrapper { ) { this.isFunctionalComponent = true } - - if (Wrapper._instances) { - Wrapper._instances.push(this) - } } at(): void { @@ -608,23 +603,3 @@ export default class Wrapper implements BaseWrapper { this.element.dispatchEvent(event) } } - -export const enableAutoDestroy = (hook: Function) => { - if (Wrapper._instances) { - throwError('enableAutoDestroy cannot be called more than once') - } - - Wrapper._instances = [] - - hook(() => { - const { _instances } = Wrapper - if (!_instances) { - return - } - - while (_instances.length > 0) { - const wrapper = _instances.pop() - wrapper.destroy() - } - }) -} diff --git a/test/specs/wrapper.spec.js b/test/specs/wrapper.spec.js index 566873c14..34604a051 100644 --- a/test/specs/wrapper.spec.js +++ b/test/specs/wrapper.spec.js @@ -1,5 +1,5 @@ import { describeWithShallowAndMount } from '~resources/utils' -import { Wrapper, enableAutoDestroy } from '~vue/test-utils' +import { enableAutoDestroy } from '~vue/test-utils' describeWithShallowAndMount('Wrapper', mountingMethod => { ;['vnode', 'element', 'vm', 'options'].forEach(property => { @@ -20,10 +20,6 @@ describeWithShallowAndMount('Wrapper', mountingMethod => { describe('enableAutoDestroy', () => { const sandbox = sinon.createSandbox() - beforeEach(() => { - Wrapper._instances = undefined - }) - it('calls the hook function', () => { const hookSpy = sandbox.spy() From 2d0a6dfceb39f987db650dd3c77f4c0a94571696 Mon Sep 17 00:00:00 2001 From: winniehell Date: Thu, 19 Dec 2019 09:37:31 +0100 Subject: [PATCH 3/6] fix: Avoid destroying child wrappers --- packages/test-utils/src/auto-destroy.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/test-utils/src/auto-destroy.js b/packages/test-utils/src/auto-destroy.js index b139393f8..f50b7006b 100644 --- a/packages/test-utils/src/auto-destroy.js +++ b/packages/test-utils/src/auto-destroy.js @@ -14,6 +14,9 @@ export function enableAutoDestroy(hook: (() => void) => void) { hook(() => { wrapperInstances.forEach((wrapper: Wrapper) => { + // skip child wrappers created by wrapper.find() + if (wrapper.selector) return + wrapper.destroy() }) wrapperInstances = [] From 560b6ec663e085275af5d1f8373c6c61bfb8b122 Mon Sep 17 00:00:00 2001 From: Winnie Date: Thu, 19 Dec 2019 12:36:05 +0100 Subject: [PATCH 4/6] Apply suggestions from code review Co-Authored-By: souldzin <4908127+souldzin@users.noreply.github.com> --- docs/api/enableAutoDestroy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/enableAutoDestroy.md b/docs/api/enableAutoDestroy.md index ab33b9b43..ca96f54a7 100644 --- a/docs/api/enableAutoDestroy.md +++ b/docs/api/enableAutoDestroy.md @@ -6,7 +6,7 @@ - **Usage:** -`enableAutoDestroy` allows to destroy all created `Wrapper` instances using the passed hook function (for example [`afterEach`](https://jestjs.io/docs/en/api#aftereachfn-timeout)). +`enableAutoDestroy` will destroy all created `Wrapper` instances using the passed hook function (for example [`afterEach`](https://jestjs.io/docs/en/api#aftereachfn-timeout)). ```js import { enableAutoDestroy, mount } from '@vue/test-utils' From 69dd59420719756c9dedeca41d1f41195b8557b4 Mon Sep 17 00:00:00 2001 From: winniehell Date: Thu, 19 Dec 2019 13:43:47 +0100 Subject: [PATCH 5/6] chore: Expose resetAutoDestroyState() --- packages/test-utils/src/auto-destroy.js | 17 ++++++++++++----- packages/test-utils/src/index.js | 3 ++- test/specs/wrapper.spec.js | 6 +++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/test-utils/src/auto-destroy.js b/packages/test-utils/src/auto-destroy.js index f50b7006b..8f9073077 100644 --- a/packages/test-utils/src/auto-destroy.js +++ b/packages/test-utils/src/auto-destroy.js @@ -3,14 +3,20 @@ import { throwError } from 'shared/util' import Wrapper from './wrapper' -let wrapperInstances: Array +let isEnabled = false +const wrapperInstances = [] + +export function resetAutoDestroyState() { + isEnabled = false + wrapperInstances.length = 0 +} export function enableAutoDestroy(hook: (() => void) => void) { - if (wrapperInstances) { + if (isEnabled) { throwError('enableAutoDestroy cannot be called more than once') } - wrapperInstances = [] + isEnabled = true hook(() => { wrapperInstances.forEach((wrapper: Wrapper) => { @@ -19,12 +25,13 @@ export function enableAutoDestroy(hook: (() => void) => void) { wrapper.destroy() }) - wrapperInstances = [] + + wrapperInstances.length = 0 }) } export function trackInstance(wrapper: Wrapper) { - if (!wrapperInstances) return + if (!isEnabled) return wrapperInstances.push(wrapper) } diff --git a/packages/test-utils/src/index.js b/packages/test-utils/src/index.js index a61cdf021..ee3d712bd 100644 --- a/packages/test-utils/src/index.js +++ b/packages/test-utils/src/index.js @@ -1,6 +1,6 @@ import shallowMount from './shallow-mount' import mount from './mount' -import { enableAutoDestroy } from './auto-destroy' +import { enableAutoDestroy, resetAutoDestroyState } from './auto-destroy' import createLocalVue from './create-local-vue' import RouterLinkStub from './components/RouterLinkStub' import createWrapper from './create-wrapper' @@ -23,6 +23,7 @@ export default { config, enableAutoDestroy, mount, + resetAutoDestroyState, shallow, shallowMount, RouterLinkStub, diff --git a/test/specs/wrapper.spec.js b/test/specs/wrapper.spec.js index 34604a051..f28e882e1 100644 --- a/test/specs/wrapper.spec.js +++ b/test/specs/wrapper.spec.js @@ -1,5 +1,5 @@ import { describeWithShallowAndMount } from '~resources/utils' -import { enableAutoDestroy } from '~vue/test-utils' +import { enableAutoDestroy, resetAutoDestroyState } from '~vue/test-utils' describeWithShallowAndMount('Wrapper', mountingMethod => { ;['vnode', 'element', 'vm', 'options'].forEach(property => { @@ -20,6 +20,10 @@ describeWithShallowAndMount('Wrapper', mountingMethod => { describe('enableAutoDestroy', () => { const sandbox = sinon.createSandbox() + beforeEach(() => { + resetAutoDestroyState() + }) + it('calls the hook function', () => { const hookSpy = sandbox.spy() From d17fc1ff8e9ec9f99b186afc56e106ef6257fecf Mon Sep 17 00:00:00 2001 From: Lachlan Date: Mon, 13 Jan 2020 21:02:09 +1000 Subject: [PATCH 6/6] docs: add info on resetAutoDestroyState --- docs/api/enableAutoDestroy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/enableAutoDestroy.md b/docs/api/enableAutoDestroy.md index ca96f54a7..f5a7b2e33 100644 --- a/docs/api/enableAutoDestroy.md +++ b/docs/api/enableAutoDestroy.md @@ -6,7 +6,7 @@ - **Usage:** -`enableAutoDestroy` will destroy all created `Wrapper` instances using the passed hook function (for example [`afterEach`](https://jestjs.io/docs/en/api#aftereachfn-timeout)). +`enableAutoDestroy` will destroy all created `Wrapper` instances using the passed hook function (for example [`afterEach`](https://jestjs.io/docs/en/api#aftereachfn-timeout)). After calling the method, you can revert to the default behavior by calling the `resetAutoDestroyState` method. ```js import { enableAutoDestroy, mount } from '@vue/test-utils'