Skip to content

Commit

Permalink
chore: Cleanup structure (#209)
Browse files Browse the repository at this point in the history
* Split index into focused files

* Update deps

* Add missing quotes
  • Loading branch information
afontcu committed Feb 17, 2021
1 parent d05b77d commit 7bef579
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 210 deletions.
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) {
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}

0 comments on commit 7bef579

Please sign in to comment.