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..f5a7b2e33 --- /dev/null +++ b/docs/api/enableAutoDestroy.md @@ -0,0 +1,25 @@ +## enableAutoDestroy(hook) + +- **Arguments:** + + - `{Function} hook` + +- **Usage:** + +`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' +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/auto-destroy.js b/packages/test-utils/src/auto-destroy.js new file mode 100644 index 000000000..8f9073077 --- /dev/null +++ b/packages/test-utils/src/auto-destroy.js @@ -0,0 +1,37 @@ +// @flow + +import { throwError } from 'shared/util' +import Wrapper from './wrapper' + +let isEnabled = false +const wrapperInstances = [] + +export function resetAutoDestroyState() { + isEnabled = false + wrapperInstances.length = 0 +} + +export function enableAutoDestroy(hook: (() => void) => void) { + if (isEnabled) { + throwError('enableAutoDestroy cannot be called more than once') + } + + isEnabled = true + + hook(() => { + wrapperInstances.forEach((wrapper: Wrapper) => { + // skip child wrappers created by wrapper.find() + if (wrapper.selector) return + + wrapper.destroy() + }) + + wrapperInstances.length = 0 + }) +} + +export function trackInstance(wrapper: Wrapper) { + if (!isEnabled) 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 6e073991b..ee3d712bd 100644 --- a/packages/test-utils/src/index.js +++ b/packages/test-utils/src/index.js @@ -1,5 +1,6 @@ import shallowMount from './shallow-mount' import mount from './mount' +import { enableAutoDestroy, resetAutoDestroyState } from './auto-destroy' import createLocalVue from './create-local-vue' import RouterLinkStub from './components/RouterLinkStub' import createWrapper from './create-wrapper' @@ -20,7 +21,9 @@ export default { createLocalVue, createWrapper, config, + enableAutoDestroy, mount, + resetAutoDestroyState, shallow, shallowMount, RouterLinkStub, diff --git a/test/specs/wrapper.spec.js b/test/specs/wrapper.spec.js index 54886499a..f28e882e1 100644 --- a/test/specs/wrapper.spec.js +++ b/test/specs/wrapper.spec.js @@ -1,4 +1,5 @@ import { describeWithShallowAndMount } from '~resources/utils' +import { enableAutoDestroy, resetAutoDestroyState } 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(() => { + resetAutoDestroyState() + }) + + 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() + }) + }) })