Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit de9f78a

Browse files
authored
chore: Cleanup structure (#211)
* Split index into focused files * Add test to increase coverage
1 parent 9937f01 commit de9f78a

File tree

5 files changed

+191
-174
lines changed

5 files changed

+191
-174
lines changed

‎src/__tests__/fire-event.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,23 @@ test.each(['input', 'change'])(
201201

202202
expect(console.warn).toHaveBeenCalledTimes(1)
203203
expect(console.warn).toHaveBeenCalledWith(
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
)
208208

209+
test('does not warn when disabled via env var', async () => {
210+
process.env.VTL_SKIP_WARN_EVENT_UPDATE = 'true'
211+
212+
const {getByTestId} = render({
213+
template: `<input type="text" data-testid="test-update" />`,
214+
})
215+
216+
await fireEvent.input(getByTestId('test-update'), 'hello')
217+
218+
expect(console.warn).not.toHaveBeenCalled()
219+
})
220+
209221
test('fireEvent.update does not trigger warning messages', async () => {
210222
const {getByTestId} = render({
211223
template: `<input type="text" data-testid="test-update" />`,

‎src/__tests__/vue-apollo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import '@testing-library/jest-dom'
22
import fetch from 'isomorphic-unfetch'
3-
import {render, fireEvent, screen} from '..'
43
import {DefaultApolloClient} from '@vue/apollo-composable'
54
import ApolloClient from 'apollo-boost'
65
import {setupServer} from 'msw/node'
76
import {graphql} from 'msw'
7+
import {render, fireEvent, screen} from '..'
88
import Component from './components/VueApollo.vue'
99

1010
// Since vue-apollo doesn't provide a MockProvider for Vue,

‎src/fire-event.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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 fireEvent.update = (elem, value) => {
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+
return fireEvent.input(elem)
57+
}
58+
}
59+
60+
case 'TEXTAREA': {
61+
elem.value = value
62+
return fireEvent.input(elem)
63+
}
64+
65+
case 'SELECT': {
66+
elem.value = value
67+
return fireEvent.change(elem)
68+
}
69+
70+
default:
71+
// do nothing
72+
}
73+
74+
return null
75+
}
76+
77+
function warnOnChangeOrInputEventCalledDirectly(eventValue, eventKey) {
78+
if (process.env.VTL_SKIP_WARN_EVENT_UPDATE) return
79+
80+
if (eventValue && (eventKey === 'change' || eventKey === 'input')) {
81+
console.warn(
82+
`Using "fireEvent.${eventKey}" may lead to unexpected results. Please use fireEvent.update() instead.`,
83+
)
84+
}
85+
}
86+
87+
export {fireEvent}

‎src/index.js

Lines changed: 5 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,8 @@
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'.
1757
if (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

18113
export * from '@testing-library/dom'
182-
export {cleanup, render, fireEvent}
14+
export {cleanup, render} from './render'
15+
export {fireEvent} from './fire-event'

‎src/render.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* eslint-disable testing-library/no-wait-for-empty-callback */
2+
import {mount} from '@vue/test-utils'
3+
4+
import {getQueriesForElement, prettyDOM} from '@testing-library/dom'
5+
6+
const mountedWrappers = new Set()
7+
8+
function render(
9+
Component,
10+
{
11+
store = null,
12+
routes = null,
13+
container: customContainer,
14+
baseElement: customBaseElement,
15+
...mountOptions
16+
} = {},
17+
) {
18+
const div = document.createElement('div')
19+
const baseElement = customBaseElement || customContainer || document.body
20+
const container = customContainer || baseElement.appendChild(div)
21+
22+
const plugins = mountOptions.global?.plugins || []
23+
24+
if (store) {
25+
const {createStore} = require('vuex')
26+
plugins.push(createStore(store))
27+
}
28+
29+
if (routes) {
30+
const requiredRouter = require('vue-router')
31+
const {createRouter, createWebHistory} =
32+
requiredRouter.default || requiredRouter
33+
34+
const routerPlugin = createRouter({history: createWebHistory(), routes})
35+
plugins.push(routerPlugin)
36+
}
37+
38+
const wrapper = mount(Component, {
39+
...mountOptions,
40+
attachTo: container,
41+
global: {...mountOptions.global, plugins},
42+
})
43+
44+
// this removes the additional "data-v-app" div node from VTU:
45+
// https://github.com/vuejs/vue-test-utils-next/blob/master/src/mount.ts#L196-L213
46+
unwrapNode(wrapper.parentElement)
47+
48+
mountedWrappers.add(wrapper)
49+
50+
return {
51+
container,
52+
baseElement,
53+
debug: (el = baseElement, maxLength, options) =>
54+
Array.isArray(el)
55+
? el.forEach(e => console.log(prettyDOM(e, maxLength, options)))
56+
: console.log(prettyDOM(el, maxLength, options)),
57+
unmount: () => wrapper.unmount(),
58+
html: () => wrapper.html(),
59+
emitted: () => wrapper.emitted(),
60+
rerender: props => wrapper.setProps(props),
61+
...getQueriesForElement(baseElement),
62+
}
63+
}
64+
65+
function unwrapNode(node) {
66+
node.replaceWith(...node.childNodes)
67+
}
68+
69+
function cleanup() {
70+
mountedWrappers.forEach(cleanupAtWrapper)
71+
}
72+
73+
function cleanupAtWrapper(wrapper) {
74+
if (
75+
wrapper.element.parentNode &&
76+
wrapper.element.parentNode.parentNode === document.body
77+
) {
78+
document.body.removeChild(wrapper.element.parentNode)
79+
}
80+
81+
wrapper.unmount()
82+
mountedWrappers.delete(wrapper)
83+
}
84+
85+
export {render, cleanup}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /