Skip to content

Commit fa0c8a8

Browse files
authored
feat: add enableAutoUnmount feature (#998)
- port enableAutoDestroy feature from VTU v1
1 parent 0334aab commit fa0c8a8

File tree

5 files changed

+98
-1
lines changed

5 files changed

+98
-1
lines changed

docs/api/index.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,28 @@ function shallowMount(Component, options?: MountingOptions): VueWrapper
18291829
18301830
`shallowMount` behaves exactly like `mount`, but it stubs all child components by default. Essentially, `shallowMount(Component)` is an alias of `mount(Component, { shallow: true })`.
18311831
1832+
## enableAutoUnmount
1833+
1834+
1835+
**Signature:**
1836+
```ts
1837+
enableAutoUnmount(hook: Function));
1838+
disableAutoUnmount(): void;
1839+
```
1840+
1841+
**Details:**
1842+
1843+
`enableAutoUnmount` allows to automatically destroy Vue wrappers. Destroy logic is passed as callback to `hook` Function.
1844+
Common usage is to use `enableAutoUnmount` with teardown helper functions provided by your test framework, such as `afterEach`:
1845+
1846+
```ts
1847+
import { enableAutoUnmount } from '@vue/test-utils'
1848+
1849+
enableAutoUnmount(afterEach)
1850+
```
1851+
1852+
`disableAutoUnmount` might be useful if you want this behavior only in specific subset of your test suite and you want to explicitly disable this behavior
1853+
18321854
## flushPromises
18331855
18341856
**Signature:**

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import { DOMWrapper } from './domWrapper'
66
import { createWrapperError } from './errorWrapper'
77
import { config } from './config'
88
import { flushPromises } from './utils/flushPromises'
9+
import { enableAutoUnmount, disableAutoUnmount } from './utils/autoUnmount'
910

1011
export {
1112
mount,
1213
shallowMount,
14+
enableAutoUnmount,
15+
disableAutoUnmount,
1316
RouterLinkStub,
1417
VueWrapper,
1518
DOMWrapper,

src/mount.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
isLegacyFunctionalComponent,
4646
unwrapLegacyVueExtendComponent
4747
} from './utils/vueCompatSupport'
48+
import { trackInstance } from './utils/autoUnmount'
4849

4950
// NOTE this should come from `vue`
5051
type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps
@@ -452,7 +453,9 @@ export function mount(
452453
return Reflect.has(appRef, property)
453454
}
454455
console.warn = warnSave
455-
return createWrapper(app, appRef, setProps)
456+
const wrapper = createWrapper(app, appRef, setProps)
457+
trackInstance(wrapper)
458+
return wrapper
456459
}
457460

458461
export const shallowMount: typeof mount = (component: any, options?: any) => {

src/utils/autoUnmount.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ComponentPublicInstance } from 'vue'
2+
import type { VueWrapper } from '../vueWrapper'
3+
4+
let isEnabled = false
5+
const wrapperInstances: VueWrapper<ComponentPublicInstance>[] = []
6+
7+
export function disableAutoUnmount() {
8+
isEnabled = false
9+
wrapperInstances.length = 0
10+
}
11+
12+
export function enableAutoUnmount(hook: Function) {
13+
if (isEnabled) {
14+
throw new Error('enableAutoUnmount cannot be called more than once')
15+
}
16+
17+
isEnabled = true
18+
19+
hook(() => {
20+
wrapperInstances.forEach((wrapper: VueWrapper<ComponentPublicInstance>) => {
21+
wrapper.unmount()
22+
})
23+
24+
wrapperInstances.length = 0
25+
})
26+
}
27+
28+
export function trackInstance(wrapper: VueWrapper<ComponentPublicInstance>) {
29+
if (!isEnabled) return
30+
31+
wrapperInstances.push(wrapper)
32+
}

tests/autoUnmount.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { mount, enableAutoUnmount, disableAutoUnmount } from '../src'
2+
3+
describe('enableAutoUnmount', () => {
4+
beforeEach(() => {
5+
disableAutoUnmount()
6+
})
7+
8+
it('calls the hook function', () => {
9+
const hookMock = jest.fn()
10+
11+
enableAutoUnmount(hookMock)
12+
13+
expect(hookMock).toHaveBeenCalledWith(expect.any(Function))
14+
})
15+
16+
it('uses the hook function to unmount wrappers', () => {
17+
const hookMock = jest.fn()
18+
19+
enableAutoUnmount(hookMock)
20+
const [unmountFn] = hookMock.mock.calls[0]
21+
22+
const wrapper = mount({ template: '<p>test</p>' })
23+
jest.spyOn(wrapper, 'unmount')
24+
25+
unmountFn()
26+
27+
expect(wrapper.unmount).toHaveBeenCalledTimes(1)
28+
})
29+
30+
it('cannot be called twice', () => {
31+
const noop = () => {}
32+
33+
enableAutoUnmount(noop)
34+
35+
expect(() => enableAutoUnmount(noop)).toThrow()
36+
})
37+
})

0 commit comments

Comments
 (0)