1- /* eslint-disable testing-library/no-wait-for-empty-callback */
2- import { mount } from '@vue/test-utils'
3- 4- import {
5- getQueriesForElement ,
6- prettyDOM ,
7- waitFor ,
8- fireEvent as dtlFireEvent ,
9- } from '@testing-library/dom'
10- 11- const mountedWrappers = new Set ( )
12- 13- function render (
14- Component ,
15- {
16- store = null ,
17- routes = null ,
18- container : customContainer ,
19- baseElement : customBaseElement ,
20- ...mountOptions
21- } = { } ,
22- ) {
23- const div = document . createElement ( 'div' )
24- const baseElement = customBaseElement || customContainer || document . body
25- const container = customContainer || baseElement . appendChild ( div )
26- 27- const plugins = mountOptions . global ?. plugins || [ ]
28- 29- if ( store ) {
30- const { createStore} = require ( 'vuex' )
31- plugins . push ( createStore ( store ) )
32- }
33- 34- if ( routes ) {
35- const requiredRouter = require ( 'vue-router' )
36- const { createRouter, createWebHistory} =
37- requiredRouter . default || requiredRouter
38- 39- const routerPlugin = createRouter ( { history : createWebHistory ( ) , routes} )
40- plugins . push ( routerPlugin )
41- }
42- 43- const wrapper = mount ( Component , {
44- ...mountOptions ,
45- attachTo : container ,
46- global : { ...mountOptions . global , plugins} ,
47- } )
48- 49- // this removes the additional "data-v-app" div node from VTU:
50- // https://github.com/vuejs/vue-test-utils-next/blob/master/src/mount.ts#L196-L213
51- unwrapNode ( wrapper . parentElement )
52- 53- mountedWrappers . add ( wrapper )
54- 55- return {
56- container,
57- baseElement,
58- debug : ( el = baseElement , maxLength , options ) =>
59- Array . isArray ( el )
60- ? el . forEach ( e => console . log ( prettyDOM ( e , maxLength , options ) ) )
61- : console . log ( prettyDOM ( el , maxLength , options ) ) ,
62- unmount : ( ) => wrapper . unmount ( ) ,
63- html : ( ) => wrapper . html ( ) ,
64- emitted : ( ) => wrapper . emitted ( ) ,
65- rerender : props => wrapper . setProps ( props ) ,
66- ...getQueriesForElement ( baseElement ) ,
67- }
68- }
69- 70- function unwrapNode ( node ) {
71- node . replaceWith ( ...node . childNodes )
72- }
73- 74- function cleanup ( ) {
75- mountedWrappers . forEach ( cleanupAtWrapper )
76- }
77- 78- function cleanupAtWrapper ( wrapper ) {
79- if (
80- wrapper . element . parentNode &&
81- wrapper . element . parentNode . parentNode === document . body
82- ) {
83- document . body . removeChild ( wrapper . element . parentNode )
84- }
85- 86- wrapper . unmount ( )
87- mountedWrappers . delete ( wrapper )
88- }
89- 90- // Vue Testing Library's version of fireEvent will call DOM Testing Library's
91- // version of fireEvent plus wait for one tick of the event loop to allow Vue
92- // to asynchronously handle the event.
93- // More info: https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue
94- async function fireEvent ( ...args ) {
95- dtlFireEvent ( ...args )
96- await waitFor ( ( ) => { } )
97- }
98- 99- function suggestUpdateIfNecessary ( eventValue , eventKey ) {
100- const changeOrInputEventCalledDirectly =
101- eventValue && ( eventKey === 'change' || eventKey === 'input' )
102- 103- if ( changeOrInputEventCalledDirectly ) {
104- console . warn (
105- `Using fireEvent.${ eventKey } () may lead to unexpected results. Please use fireEvent.update() instead.` ,
106- )
107- }
108- }
109- 110- Object . keys ( dtlFireEvent ) . forEach ( key => {
111- fireEvent [ key ] = async ( ...args ) => {
112- suggestUpdateIfNecessary ( args [ 1 ] , key )
113- dtlFireEvent [ key ] ( ...args )
114- await waitFor ( ( ) => { } )
115- }
116- } )
117- 118- fireEvent . touch = async elem => {
119- await fireEvent . focus ( elem )
120- await fireEvent . blur ( elem )
121- }
122- 123- // Small utility to provide a better experience when working with v-model.
124- // Related upstream issue: https://github.com/vuejs/vue-test-utils/issues/345#issuecomment-380588199
125- // Examples: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/form.js
126- fireEvent . update = ( elem , value ) => {
127- const tagName = elem . tagName
128- const type = elem . type
129- 130- switch ( tagName ) {
131- case 'OPTION' : {
132- elem . selected = true
133- 134- const parentSelectElement =
135- elem . parentElement . tagName === 'OPTGROUP'
136- ? elem . parentElement . parentElement
137- : elem . parentElement
138- 139- return fireEvent . change ( parentSelectElement )
140- }
141- 142- case 'INPUT' : {
143- if ( [ 'checkbox' , 'radio' ] . includes ( type ) ) {
144- elem . checked = true
145- return fireEvent . change ( elem )
146- } else if ( type === 'file' ) {
147- return fireEvent . change ( elem )
148- } else {
149- elem . value = value
150- return fireEvent . input ( elem )
151- }
152- }
153- 154- case 'TEXTAREA' : {
155- elem . value = value
156- return fireEvent . input ( elem )
157- }
158- 159- case 'SELECT' : {
160- elem . value = value
161- return fireEvent . change ( elem )
162- }
163- 164- default :
165- // do nothing
166- }
167- 168- return null
169- }
1+ import { cleanup } from './render'
1702
1713// If we're running in a test runner that supports afterEach then we'll
172- // automatically run cleanup after each test. This ensures that tests run in
173- // isolation from each other.
4+ // automatically run cleanup after each test.
5+ // This ensures that tests run in isolation from each other.
1746// If you don't like this, set the VTL_SKIP_AUTO_CLEANUP variable to 'true'.
1757if ( typeof afterEach === 'function' && ! process . env . VTL_SKIP_AUTO_CLEANUP ) {
1768 afterEach ( ( ) => {
@@ -179,4 +11,5 @@ if (typeof afterEach === 'function' && !process.env.VTL_SKIP_AUTO_CLEANUP) {
17911}
18012
18113export * from '@testing-library/dom'
182- export { cleanup , render , fireEvent }
14+ export { cleanup , render } from './render'
15+ export { fireEvent } from './fire-event'
0 commit comments