Skip to content

chore: Cleanup structure #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = merge(config, {
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'vue'],
moduleNameMapper: {
'@testing-library/vue': '<rootDir>/src/vue-testing-library.js',
'@testing-library/vue': '<rootDir>/src/index.js',
},
coverageDirectory: './coverage',
collectCoverageFrom: ['**/src/**/*.js', '!**/src/__tests__/**'],
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@testing-library/vue",
"version": "0.0.0-semantically-released",
"description": "Simple and complete Vue DOM testing utilities that encourage good testing practices.",
"main": "dist/vue-testing-library.js",
"main": "dist/index.js",
"types": "types/index.d.ts",
"scripts": {
"format": "kcd-scripts format",
Expand Down Expand Up @@ -50,21 +50,21 @@
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.11.5",
"@testing-library/jest-dom": "^5.11.6",
"@types/estree": "0.0.45",
"@testing-library/user-event": "^12.1.10",
"@types/estree": "0.0.46",
"apollo-boost": "^0.4.9",
"apollo-cache-inmemory": "^1.6.6",
"axios": "^0.20.0",
"axios": "^0.21.1",
"dtslint": "^4.0.5",
"eslint": "^7.13.0",
"eslint-plugin-vue": "^7.1.0",
"eslint-plugin-vue": "^7.6.0",
"graphql": "^15.3.0",
"graphql-tag": "^2.11.0",
"isomorphic-unfetch": "^3.0.0",
"jest-serializer-vue": "^2.0.2",
"kcd-scripts": "^7.0.3",
"lodash.merge": "^4.6.2",
"msw": "^0.21.2",
"msw": "^0.26.2",
"portal-vue": "^2.1.7",
"typescript": "^4.0.5",
"vee-validate": "^2.2.15",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/fire-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ typingEvents.forEach(event => {
expect(console.warn).toHaveBeenCalledTimes(1)
expect(console.warn).toHaveBeenCalledWith(
expect.stringContaining(
`Using "fireEvent.${event} may lead to unexpected results. Please use fireEvent.update() instead.`,
`Using "fireEvent.${event}" may lead to unexpected results. Please use fireEvent.update() instead.`,
),
)
})
Expand Down
93 changes: 93 additions & 0 deletions src/fire-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* eslint-disable testing-library/no-wait-for-empty-callback */
import {waitFor, fireEvent as dtlFireEvent} from '@testing-library/dom'

// Vue Testing Lib's version of fireEvent will call DOM Testing Lib's
// version of fireEvent. The reason is because we need to wait another
// event loop tick to allow Vue to flush and update the DOM
// More info: https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue

async function fireEvent(...args) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question out of curiosity. when using VTL, should we always use the fireEvent exported by this library or instead should we use user-event? with RTL, user-event is suggested https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#not-using-testing-libraryuser-event

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi! Using user-event is advisable, as it is closer to the user. We even showcase some examples: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/user-event.js

dtlFireEvent(...args)
await waitFor(() => {})
}

Object.keys(dtlFireEvent).forEach(key => {
fireEvent[key] = async (...args) => {
warnOnChangeOrInputEventCalledDirectly(args[1], key)

dtlFireEvent[key](...args)
await waitFor(() => {})
}
})

fireEvent.touch = async elem => {
await fireEvent.focus(elem)
await fireEvent.blur(elem)
}

// fireEvent.update is a small utility to provide a better experience when
// working with v-model.
// Related upstream issue: https://github.com/vuejs/vue-test-utils/issues/345#issuecomment-380588199
// Examples: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/form.js
fireEvent.update = (elem, value) => {
const tagName = elem.tagName
const type = elem.type

switch (tagName) {
case 'OPTION': {
elem.selected = true

const parentSelectElement =
elem.parentElement.tagName === 'OPTGROUP'
? elem.parentElement.parentElement
: elem.parentElement

return fireEvent.change(parentSelectElement)
}

case 'INPUT': {
if (['checkbox', 'radio'].includes(type)) {
elem.checked = true
return fireEvent.change(elem)
} else if (type === 'file') {
return fireEvent.change(elem)
} else {
elem.value = value
if (elem._vModifiers?.lazy) {
return fireEvent.change(elem)
}
return fireEvent.input(elem)
}
}

case 'TEXTAREA': {
elem.value = value
if (elem._vModifiers?.lazy) {
return fireEvent.change(elem)
}
return fireEvent.input(elem)
}

case 'SELECT': {
elem.value = value
return fireEvent.change(elem)
}

default:
// do nothing
}

return null
}

function warnOnChangeOrInputEventCalledDirectly(eventValue, eventKey) {
if (process.env.VTL_SKIP_WARN_EVENT_UPDATE) return

if (eventValue && (eventKey === 'change' || eventKey === 'input')) {
console.warn(
`Using "fireEvent.${eventKey}" may lead to unexpected results. Please use fireEvent.update() instead.`,
)
}
}

export {fireEvent}
15 changes: 15 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {cleanup} from './render'

// If we're running in a test runner that supports afterEach then we'll
// automatically run cleanup after each test.
// This ensures that tests run in isolation from each other.
// If you don't like this, set the VTL_SKIP_AUTO_CLEANUP variable to 'true'.
if (typeof afterEach === 'function' && !process.env.VTL_SKIP_AUTO_CLEANUP) {
afterEach(() => {
cleanup()
})
}

export * from '@testing-library/dom'
export {cleanup, render} from './render'
export {fireEvent} from './fire-event'
101 changes: 101 additions & 0 deletions src/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {createLocalVue, mount} from '@vue/test-utils'

import {getQueriesForElement, prettyDOM} from '@testing-library/dom'

const mountedWrappers = new Set()

function render(
Component,
{
store = null,
routes = null,
container: customContainer,
baseElement: customBaseElement,
...mountOptions
} = {},
configurationCb,
) {
const div = document.createElement('div')
const baseElement = customBaseElement || customContainer || document.body
const container = customContainer || baseElement.appendChild(div)

const attachTo = document.createElement('div')
container.appendChild(attachTo)

const localVue = createLocalVue()
let vuexStore = null
let router = null
let callbackOptions = {}

if (store) {
const Vuex = require('vuex')
localVue.use(Vuex)

vuexStore = new Vuex.Store(store)
}

if (routes) {
const requiredRouter = require('vue-router')
const VueRouter = requiredRouter.default || requiredRouter
localVue.use(VueRouter)

router = new VueRouter({routes})
}

if (configurationCb && typeof configurationCb === 'function') {
callbackOptions = configurationCb(localVue, vuexStore, router)
}

if (!mountOptions.propsData && !!mountOptions.props) {
mountOptions.propsData = mountOptions.props
delete mountOptions.props
}

const wrapper = mount(Component, {
attachTo,
localVue,
router,
store: vuexStore,
...mountOptions,
...callbackOptions,
})

mountedWrappers.add(wrapper)
container.appendChild(wrapper.element)

return {
container,
baseElement,
debug: (el = baseElement, ...args) =>
Array.isArray(el)
? el.forEach(e => console.log(prettyDOM(e, ...args)))
: console.log(prettyDOM(el, ...args)),
unmount: () => wrapper.destroy(),
isUnmounted: () => wrapper.vm._isDestroyed,
html: () => wrapper.html(),
emitted: () => wrapper.emitted(),
updateProps: _ => wrapper.setProps(_),
...getQueriesForElement(baseElement),
}
}

function cleanup() {
mountedWrappers.forEach(cleanupAtWrapper)
}

function cleanupAtWrapper(wrapper) {
if (
wrapper.element.parentNode &&
wrapper.element.parentNode.parentNode === document.body
) {
document.body.removeChild(wrapper.element.parentNode)
}

try {
wrapper.destroy()
} finally {
mountedWrappers.delete(wrapper)
}
}

export {cleanup, render}
Loading