Skip to content

Commit 1d026d4

Browse files
committed
Improve messaging
1 parent e39281f commit 1d026d4

File tree

4 files changed

+167
-6
lines changed

4 files changed

+167
-6
lines changed

src/__tests__/deprecated-options.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test('warns on deprecated store option', () => {
1818
expect(console.warn).toHaveBeenCalledTimes(1)
1919
expect(console.warn).toHaveBeenCalledWith(
2020
expect.stringContaining(
21-
`Providing 'store' or 'routes' options is now deprecated`,
21+
`Providing 'store' or 'routes' options is no longer available`,
2222
),
2323
)
2424
})
@@ -33,7 +33,7 @@ test('warns on deprecated routes option', () => {
3333
expect(console.warn).toHaveBeenCalledTimes(1)
3434
expect(console.warn).toHaveBeenCalledWith(
3535
expect.stringContaining(
36-
`Providing 'store' or 'routes' options is now deprecated`,
36+
`Providing 'store' or 'routes' options is no longer available`,
3737
),
3838
)
3939
})

src/index.js

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

3164
// If we're running in a test runner that supports afterEach then we'll
4165
// automatically run cleanup after each test.

src/render.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ function render(
2020
const container = customContainer || baseElement.appendChild(div)
2121

2222
if (store || routes) {
23-
console.warn(`Providing 'store' or 'routes' options is now deprecated.
24-
You need to create a router/vuex plugin and provide it through 'global.plugins'.
23+
console.warn(`Providing 'store' or 'routes' options is no longer available.
24+
You need to create a router/vuex instance and provide it through 'global.plugins'.
2525
Check out the test examples on GitHub for further details.`)
2626
}
2727

types/index.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ type VueTestUtilsRenderOptions = Omit<
3232
>
3333
type VueTestingLibraryRenderOptions = {
3434
/**
35-
* @deprecated Use `global.plugins` array instead.
35+
* @deprecated Add a Vuex instance through `global.plugins` array instead.
3636
*/
3737
store: any
3838
/**
39-
* @deprecated Use `global.plugins` array instead.
39+
* @deprecated Add a Router instance through `global.plugins` array instead.
4040
*/
4141
routes?: any
4242
container?: Element

0 commit comments

Comments
 (0)