Skip to content

Commit 7bef579

Browse files
authored
chore: Cleanup structure (#209)
* Split index into focused files * Update deps * Add missing quotes
1 parent d05b77d commit 7bef579

File tree

7 files changed

+216
-210
lines changed

7 files changed

+216
-210
lines changed

jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = merge(config, {
55
testEnvironment: 'jsdom',
66
moduleFileExtensions: ['js', 'vue'],
77
moduleNameMapper: {
8-
'@testing-library/vue': '<rootDir>/src/vue-testing-library.js',
8+
'@testing-library/vue': '<rootDir>/src/index.js',
99
},
1010
coverageDirectory: './coverage',
1111
collectCoverageFrom: ['**/src/**/*.js', '!**/src/__tests__/**'],

package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@testing-library/vue",
33
"version": "0.0.0-semantically-released",
44
"description": "Simple and complete Vue DOM testing utilities that encourage good testing practices.",
5-
"main": "dist/vue-testing-library.js",
5+
"main": "dist/index.js",
66
"types": "types/index.d.ts",
77
"scripts": {
88
"format": "kcd-scripts format",
@@ -50,21 +50,21 @@
5050
"devDependencies": {
5151
"@babel/plugin-transform-runtime": "^7.11.5",
5252
"@testing-library/jest-dom": "^5.11.6",
53-
"@types/estree": "0.0.45",
5453
"@testing-library/user-event": "^12.1.10",
54+
"@types/estree": "0.0.46",
5555
"apollo-boost": "^0.4.9",
5656
"apollo-cache-inmemory": "^1.6.6",
57-
"axios": "^0.20.0",
57+
"axios": "^0.21.1",
5858
"dtslint": "^4.0.5",
5959
"eslint": "^7.13.0",
60-
"eslint-plugin-vue": "^7.1.0",
60+
"eslint-plugin-vue": "^7.6.0",
6161
"graphql": "^15.3.0",
6262
"graphql-tag": "^2.11.0",
6363
"isomorphic-unfetch": "^3.0.0",
6464
"jest-serializer-vue": "^2.0.2",
6565
"kcd-scripts": "^7.0.3",
6666
"lodash.merge": "^4.6.2",
67-
"msw": "^0.21.2",
67+
"msw": "^0.26.2",
6868
"portal-vue": "^2.1.7",
6969
"typescript": "^4.0.5",
7070
"vee-validate": "^2.2.15",

src/__tests__/fire-event.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ typingEvents.forEach(event => {
201201
expect(console.warn).toHaveBeenCalledTimes(1)
202202
expect(console.warn).toHaveBeenCalledWith(
203203
expect.stringContaining(
204-
`Using "fireEvent.${event} may lead to unexpected results. Please use fireEvent.update() instead.`,
204+
`Using "fireEvent.${event}" may lead to unexpected results. Please use fireEvent.update() instead.`,
205205
),
206206
)
207207
})

src/fire-event.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* eslint-disable testing-library/no-wait-for-empty-callback */
2+
import {waitFor, fireEvent as dtlFireEvent} from '@testing-library/dom'
3+
4+
// Vue Testing Lib's version of fireEvent will call DOM Testing Lib's
5+
// version of fireEvent. The reason is because we need to wait another
6+
// event loop tick to allow Vue to flush and update the DOM
7+
// More info: https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue
8+
9+
async function fireEvent(...args) {
10+
dtlFireEvent(...args)
11+
await waitFor(() => {})
12+
}
13+
14+
Object.keys(dtlFireEvent).forEach(key => {
15+
fireEvent[key] = async (...args) => {
16+
warnOnChangeOrInputEventCalledDirectly(args[1], key)
17+
18+
dtlFireEvent[key](...args)
19+
await waitFor(() => {})
20+
}
21+
})
22+
23+
fireEvent.touch = async elem => {
24+
await fireEvent.focus(elem)
25+
await fireEvent.blur(elem)
26+
}
27+
28+
// fireEvent.update is a small utility to provide a better experience when
29+
// working with v-model.
30+
// Related upstream issue: https://github.com/vuejs/vue-test-utils/issues/345#issuecomment-380588199
31+
// Examples: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/form.js
32+
fireEvent.update = (elem, value) => {
33+
const tagName = elem.tagName
34+
const type = elem.type
35+
36+
switch (tagName) {
37+
case 'OPTION': {
38+
elem.selected = true
39+
40+
const parentSelectElement =
41+
elem.parentElement.tagName === 'OPTGROUP'
42+
? elem.parentElement.parentElement
43+
: elem.parentElement
44+
45+
return fireEvent.change(parentSelectElement)
46+
}
47+
48+
case 'INPUT': {
49+
if (['checkbox', 'radio'].includes(type)) {
50+
elem.checked = true
51+
return fireEvent.change(elem)
52+
} else if (type === 'file') {
53+
return fireEvent.change(elem)
54+
} else {
55+
elem.value = value
56+
if (elem._vModifiers?.lazy) {
57+
return fireEvent.change(elem)
58+
}
59+
return fireEvent.input(elem)
60+
}
61+
}
62+
63+
case 'TEXTAREA': {
64+
elem.value = value
65+
if (elem._vModifiers?.lazy) {
66+
return fireEvent.change(elem)
67+
}
68+
return fireEvent.input(elem)
69+
}
70+
71+
case 'SELECT': {
72+
elem.value = value
73+
return fireEvent.change(elem)
74+
}
75+
76+
default:
77+
// do nothing
78+
}
79+
80+
return null
81+
}
82+
83+
function warnOnChangeOrInputEventCalledDirectly(eventValue, eventKey) {
84+
if (process.env.VTL_SKIP_WARN_EVENT_UPDATE) return
85+
86+
if (eventValue && (eventKey === 'change' || eventKey === 'input')) {
87+
console.warn(
88+
`Using "fireEvent.${eventKey}" may lead to unexpected results. Please use fireEvent.update() instead.`,
89+
)
90+
}
91+
}
92+
93+
export {fireEvent}

src/index.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {cleanup} from './render'
2+
3+
// If we're running in a test runner that supports afterEach then we'll
4+
// automatically run cleanup after each test.
5+
// This ensures that tests run in isolation from each other.
6+
// If you don't like this, set the VTL_SKIP_AUTO_CLEANUP variable to 'true'.
7+
if (typeof afterEach === 'function' && !process.env.VTL_SKIP_AUTO_CLEANUP) {
8+
afterEach(() => {
9+
cleanup()
10+
})
11+
}
12+
13+
export * from '@testing-library/dom'
14+
export {cleanup, render} from './render'
15+
export {fireEvent} from './fire-event'

src/render.js

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import {createLocalVue, mount} from '@vue/test-utils'
2+
3+
import {getQueriesForElement, prettyDOM} from '@testing-library/dom'
4+
5+
const mountedWrappers = new Set()
6+
7+
function render(
8+
Component,
9+
{
10+
store = null,
11+
routes = null,
12+
container: customContainer,
13+
baseElement: customBaseElement,
14+
...mountOptions
15+
} = {},
16+
configurationCb,
17+
) {
18+
const div = document.createElement('div')
19+
const baseElement = customBaseElement || customContainer || document.body
20+
const container = customContainer || baseElement.appendChild(div)
21+
22+
const attachTo = document.createElement('div')
23+
container.appendChild(attachTo)
24+
25+
const localVue = createLocalVue()
26+
let vuexStore = null
27+
let router = null
28+
let callbackOptions = {}
29+
30+
if (store) {
31+
const Vuex = require('vuex')
32+
localVue.use(Vuex)
33+
34+
vuexStore = new Vuex.Store(store)
35+
}
36+
37+
if (routes) {
38+
const requiredRouter = require('vue-router')
39+
const VueRouter = requiredRouter.default || requiredRouter
40+
localVue.use(VueRouter)
41+
42+
router = new VueRouter({routes})
43+
}
44+
45+
if (configurationCb && typeof configurationCb === 'function') {
46+
callbackOptions = configurationCb(localVue, vuexStore, router)
47+
}
48+
49+
if (!mountOptions.propsData && !!mountOptions.props) {
50+
mountOptions.propsData = mountOptions.props
51+
delete mountOptions.props
52+
}
53+
54+
const wrapper = mount(Component, {
55+
attachTo,
56+
localVue,
57+
router,
58+
store: vuexStore,
59+
...mountOptions,
60+
...callbackOptions,
61+
})
62+
63+
mountedWrappers.add(wrapper)
64+
container.appendChild(wrapper.element)
65+
66+
return {
67+
container,
68+
baseElement,
69+
debug: (el = baseElement, ...args) =>
70+
Array.isArray(el)
71+
? el.forEach(e => console.log(prettyDOM(e, ...args)))
72+
: console.log(prettyDOM(el, ...args)),
73+
unmount: () => wrapper.destroy(),
74+
isUnmounted: () => wrapper.vm._isDestroyed,
75+
html: () => wrapper.html(),
76+
emitted: () => wrapper.emitted(),
77+
updateProps: _ => wrapper.setProps(_),
78+
...getQueriesForElement(baseElement),
79+
}
80+
}
81+
82+
function cleanup() {
83+
mountedWrappers.forEach(cleanupAtWrapper)
84+
}
85+
86+
function cleanupAtWrapper(wrapper) {
87+
if (
88+
wrapper.element.parentNode &&
89+
wrapper.element.parentNode.parentNode === document.body
90+
) {
91+
document.body.removeChild(wrapper.element.parentNode)
92+
}
93+
94+
try {
95+
wrapper.destroy()
96+
} finally {
97+
mountedWrappers.delete(wrapper)
98+
}
99+
}
100+
101+
export {cleanup, render}

0 commit comments

Comments
 (0)