diff --git a/.size-snapshot.json b/.size-snapshot.json
index 094d20cb..8b346d5f 100644
--- a/.size-snapshot.json
+++ b/.size-snapshot.json
@@ -1,7 +1,7 @@
{
"dist/dom-testing-library.umd.js": {
- "bundled": 115443,
- "minified": 50960,
- "gzipped": 15294
+ "bundled": 144335,
+ "minified": 52790,
+ "gzipped": 15639
}
}
diff --git a/jest.config.js b/jest.config.js
index 4160d66d..feca704e 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,5 +1,5 @@
const jestConfig = require('kcd-scripts/jest')
module.exports = Object.assign(jestConfig, {
- testEnvironment: 'jest-environment-jsdom',
+ testEnvironment: 'jest-environment-node',
})
diff --git a/package.json b/package.json
index 27221312..b47bc1ee 100644
--- a/package.json
+++ b/package.json
@@ -27,8 +27,9 @@
"build": "kcd-scripts build && kcd-scripts build --bundle umd --no-clean",
"lint": "kcd-scripts lint",
"test": "kcd-scripts test",
+ "test:all": "npm test && npm test -- --env jsdom",
"test:update": "npm test -- --updateSnapshot --coverage",
- "validate": "kcd-scripts validate",
+ "validate": "kcd-scripts validate build,lint,test:all",
"setup": "npm install && npm run validate -s",
"precommit": "kcd-scripts precommit",
"dtslint": "dtslint typings"
@@ -38,14 +39,15 @@
"typings"
],
"dependencies": {
- "mutationobserver-shim": "^0.3.2",
+ "@sheerun/mutationobserver-shim": "^0.3.2",
"pretty-format": "^23.6.0",
"wait-for-expect": "^1.0.0"
},
"devDependencies": {
"dtslint": "^0.3.0",
- "jest-dom": "^1.7.0",
+ "jest-dom": "^2.0.4",
"jest-in-case": "^1.0.2",
+ "jsdom": "^12.2.0",
"kcd-scripts": "^0.41.0",
"microbundle": "^0.4.4"
},
diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js
index f0e0f6db..5374ec6b 100644
--- a/src/__tests__/element-queries.js
+++ b/src/__tests__/element-queries.js
@@ -1,8 +1,9 @@
import 'jest-dom/extend-expect'
import {render} from './helpers/test-utils'
+import document from './helpers/document'
beforeEach(() => {
- window.Cypress = null
+ document.defaultView.Cypress = null
})
test('query can return null', () => {
@@ -148,9 +149,7 @@ test('get element by its alt text', () => {
,
`)
- expect(getByAltText(/fin.*nem.*poster$/i).src).toBe(
- 'http://localhost/finding-nemo.png',
- )
+ expect(getByAltText(/fin.*nem.*poster$/i).src).toContain('/finding-nemo.png')
})
test('query/get element by its title', () => {
@@ -489,7 +488,7 @@ test('test the debug helper prints the dom state here', () => {
})
test('get throws a useful error message without DOM in Cypress', () => {
- window.Cypress = {}
+ document.defaultView.Cypress = {}
const {
getByLabelText,
getBySelectText,
diff --git a/src/__tests__/events.js b/src/__tests__/events.js
index cb3e7483..10b15f69 100644
--- a/src/__tests__/events.js
+++ b/src/__tests__/events.js
@@ -1,4 +1,5 @@
import {fireEvent} from '..'
+import document from './helpers/document'
const eventTypes = [
{
@@ -171,7 +172,7 @@ test('assigning a value to a target that cannot have a value throws an error', (
test('assigning the files property on an input', () => {
const node = document.createElement('input')
- const file = new File(['(⌐□_□)'], 'chucknorris.png', {
+ const file = new document.defaultView.File(['(⌐□_□)'], 'chucknorris.png', {
type: 'image/png',
})
fireEvent.change(node, {target: {files: [file]}})
diff --git a/src/__tests__/example.js b/src/__tests__/example.js
index da0bb8ba..f6a96dd6 100644
--- a/src/__tests__/example.js
+++ b/src/__tests__/example.js
@@ -2,6 +2,7 @@
import {getByLabelText, getByText, getByTestId, queryByTestId, wait} from '../'
// adds special assertions like toHaveTextContent
import 'jest-dom/extend-expect'
+import document from './helpers/document'
function getExampleDOM() {
// This is just a raw example of setting up some DOM
diff --git a/src/__tests__/get-queries-for-element.js b/src/__tests__/get-queries-for-element.js
index 1a6afba8..44d2e43d 100644
--- a/src/__tests__/get-queries-for-element.js
+++ b/src/__tests__/get-queries-for-element.js
@@ -1,5 +1,6 @@
import {getQueriesForElement} from '../get-queries-for-element'
import {queries} from '..'
+import document from './helpers/document'
test('uses default queries', () => {
const container = document.createElement('div')
diff --git a/src/__tests__/helpers/document.js b/src/__tests__/helpers/document.js
new file mode 100644
index 00000000..1df46c07
--- /dev/null
+++ b/src/__tests__/helpers/document.js
@@ -0,0 +1,9 @@
+let testWindow = typeof window === 'undefined' ? undefined : window
+
+if (typeof window === 'undefined') {
+ const {JSDOM} = require('jsdom')
+ const dom = new JSDOM()
+ testWindow = dom.window
+}
+
+module.exports = testWindow.document
diff --git a/src/__tests__/helpers/test-utils.js b/src/__tests__/helpers/test-utils.js
index a21a316b..46d270d9 100644
--- a/src/__tests__/helpers/test-utils.js
+++ b/src/__tests__/helpers/test-utils.js
@@ -1,4 +1,5 @@
import {getQueriesForElement} from '../../get-queries-for-element'
+import document from './document'
function render(html) {
const container = document.createElement('div')
diff --git a/src/__tests__/pretty-dom.js b/src/__tests__/pretty-dom.js
index 312cf78b..b2fe3d02 100644
--- a/src/__tests__/pretty-dom.js
+++ b/src/__tests__/pretty-dom.js
@@ -1,5 +1,6 @@
import {prettyDOM} from '../pretty-dom'
import {render} from './helpers/test-utils'
+import document from './helpers/document'
test('it prints out the given DOM element tree', () => {
const {container} = render('
Hello World!
')
diff --git a/src/__tests__/wait-for-element.js b/src/__tests__/wait-for-element.js
index 1feaa619..247309e4 100644
--- a/src/__tests__/wait-for-element.js
+++ b/src/__tests__/wait-for-element.js
@@ -2,6 +2,7 @@ import {waitForElement, wait} from '../'
// adds special assertions like toBeTruthy
import 'jest-dom/extend-expect'
import {render} from './helpers/test-utils'
+import document from './helpers/document'
async function skipSomeTime(delayMs) {
await new Promise(resolve => setTimeout(resolve, delayMs))
@@ -104,7 +105,16 @@ test('it waits for the next DOM mutation with default callback', async () => {
const successHandler = jest.fn().mockName('successHandler')
const errorHandler = jest.fn().mockName('errorHandler')
- const promise = waitForElement().then(successHandler, errorHandler)
+ let promise
+
+ if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
+ promise = waitForElement().then(successHandler, errorHandler)
+ } else {
+ promise = waitForElement(undefined, {container: document}).then(
+ successHandler,
+ errorHandler,
+ )
+ }
// Promise callbacks are always asynchronous.
expect(successHandler).toHaveBeenCalledTimes(0)
diff --git a/src/events.js b/src/events.js
index 51073c5b..c50fb59a 100644
--- a/src/events.js
+++ b/src/events.js
@@ -1,218 +1,201 @@
-const {
- AnimationEvent,
- ClipboardEvent,
- CompositionEvent,
- DragEvent,
- Event,
- FocusEvent,
- InputEvent,
- KeyboardEvent,
- MouseEvent,
- ProgressEvent,
- TouchEvent,
- TransitionEvent,
- UIEvent,
- WheelEvent,
-} = typeof window === 'undefined' ? /* istanbul ignore next */ global : window
-
const eventMap = {
// Clipboard Events
copy: {
- EventType: ClipboardEvent,
+ EventType: 'ClipboardEvent',
defaultInit: {bubbles: true, cancelable: true},
},
cut: {
- EventType: ClipboardEvent,
+ EventType: 'ClipboardEvent',
defaultInit: {bubbles: true, cancelable: true},
},
paste: {
- EventType: ClipboardEvent,
+ EventType: 'ClipboardEvent',
defaultInit: {bubbles: true, cancelable: true},
},
// Composition Events
compositionEnd: {
- EventType: CompositionEvent,
+ EventType: 'CompositionEvent',
defaultInit: {bubbles: true, cancelable: true},
},
compositionStart: {
- EventType: CompositionEvent,
+ EventType: 'CompositionEvent',
defaultInit: {bubbles: true, cancelable: true},
},
compositionUpdate: {
- EventType: CompositionEvent,
+ EventType: 'CompositionEvent',
defaultInit: {bubbles: true, cancelable: false},
},
// Keyboard Events
keyDown: {
- EventType: KeyboardEvent,
+ EventType: 'KeyboardEvent',
defaultInit: {bubbles: true, cancelable: true},
},
keyPress: {
- EventType: KeyboardEvent,
+ EventType: 'KeyboardEvent',
defaultInit: {bubbles: true, cancelable: true},
},
keyUp: {
- EventType: KeyboardEvent,
+ EventType: 'KeyboardEvent',
defaultInit: {bubbles: true, cancelable: true},
},
// Focus Events
focus: {
- EventType: FocusEvent,
+ EventType: 'FocusEvent',
defaultInit: {bubbles: false, cancelable: false},
},
blur: {
- EventType: FocusEvent,
+ EventType: 'FocusEvent',
defaultInit: {bubbles: false, cancelable: false},
},
// Form Events
change: {
- EventType: InputEvent,
+ EventType: 'InputEvent',
defaultInit: {bubbles: true, cancelable: true},
},
input: {
- EventType: InputEvent,
+ EventType: 'InputEvent',
defaultInit: {bubbles: true, cancelable: true},
},
invalid: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: true},
},
submit: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: true, cancelable: true},
},
// Mouse Events
click: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true, button: 0},
},
contextMenu: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
dblClick: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
drag: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: true},
},
dragEnd: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: false},
},
dragEnter: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: true},
},
dragExit: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: false},
},
dragLeave: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: false},
},
dragOver: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: true},
},
dragStart: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: true},
},
drop: {
- EventType: DragEvent,
+ EventType: 'DragEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseDown: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseEnter: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseLeave: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseMove: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseOut: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseOver: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
mouseUp: {
- EventType: MouseEvent,
+ EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true},
},
// Selection Events
select: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: true, cancelable: false},
},
// Touch Events
touchCancel: {
- EventType: TouchEvent,
+ EventType: 'TouchEvent',
defaultInit: {bubbles: true, cancelable: false},
},
touchEnd: {
- EventType: TouchEvent,
+ EventType: 'TouchEvent',
defaultInit: {bubbles: true, cancelable: true},
},
touchMove: {
- EventType: TouchEvent,
+ EventType: 'TouchEvent',
defaultInit: {bubbles: true, cancelable: true},
},
touchStart: {
- EventType: TouchEvent,
+ EventType: 'TouchEvent',
defaultInit: {bubbles: true, cancelable: true},
},
// UI Events
scroll: {
- EventType: UIEvent,
+ EventType: 'UIEvent',
defaultInit: {bubbles: false, cancelable: false},
},
// Wheel Events
wheel: {
- EventType: WheelEvent,
+ EventType: 'WheelEvent',
defaultInit: {bubbles: true, cancelable: true},
},
// Media Events
abort: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
canPlay: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
canPlayThrough: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
durationChange: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
emptied: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
encrypted: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
ended: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
// error: {
@@ -220,90 +203,90 @@ const eventMap = {
// defaultInit: {bubbles: false, cancelable: false},
// },
loadedData: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
loadedMetadata: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
loadStart: {
- EventType: ProgressEvent,
+ EventType: 'ProgressEvent',
defaultInit: {bubbles: false, cancelable: false},
},
pause: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
play: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
playing: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
progress: {
- EventType: ProgressEvent,
+ EventType: 'ProgressEvent',
defaultInit: {bubbles: false, cancelable: false},
},
rateChange: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
seeked: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
seeking: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
stalled: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
suspend: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
timeUpdate: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
volumeChange: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
waiting: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
// Image Events
load: {
- EventType: UIEvent,
+ EventType: 'UIEvent',
defaultInit: {bubbles: false, cancelable: false},
},
error: {
- EventType: Event,
+ EventType: 'Event',
defaultInit: {bubbles: false, cancelable: false},
},
// Animation Events
animationStart: {
- EventType: AnimationEvent,
+ EventType: 'AnimationEvent',
defaultInit: {bubbles: true, cancelable: false},
},
animationEnd: {
- EventType: AnimationEvent,
+ EventType: 'AnimationEvent',
defaultInit: {bubbles: true, cancelable: false},
},
animationIteration: {
- EventType: AnimationEvent,
+ EventType: 'AnimationEvent',
defaultInit: {bubbles: true, cancelable: false},
},
// Transition Events
transitionEnd: {
- EventType: TransitionEvent,
+ EventType: 'TransitionEvent',
defaultInit: {bubbles: true, cancelable: true},
},
}
@@ -316,28 +299,32 @@ function fireEvent(element, event) {
return element.dispatchEvent(event)
}
-Object.entries(eventMap).forEach(([key, {EventType = Event, defaultInit}]) => {
- const eventName = key.toLowerCase()
+Object.entries(eventMap).forEach(
+ ([key, {EventType = 'Event', defaultInit}]) => {
+ const eventName = key.toLowerCase()
- fireEvent[key] = (node, init) => {
- const eventInit = {...defaultInit, ...init}
- const {target: {value, files, ...targetProperties} = {}} = eventInit
- Object.assign(node, targetProperties)
- if (value !== undefined) {
- setNativeValue(node, value)
- }
- if (files !== undefined) {
- // input.files is a read-only property so this is not allowed:
- // input.files = [file]
- // so we have to use this workaround to set the property
- Object.defineProperty(node, 'files', {
- value: files,
- })
+ fireEvent[key] = (node, init) => {
+ const eventInit = {...defaultInit, ...init}
+ const {target: {value, files, ...targetProperties} = {}} = eventInit
+ Object.assign(node, targetProperties)
+ if (value !== undefined) {
+ setNativeValue(node, value)
+ }
+ if (files !== undefined) {
+ // input.files is a read-only property so this is not allowed:
+ // input.files = [file]
+ // so we have to use this workaround to set the property
+ Object.defineProperty(node, 'files', {
+ value: files,
+ })
+ }
+ const window = node.ownerDocument.defaultView
+ const EventConstructor = window[EventType] || window.Event
+ const event = new EventConstructor(eventName, eventInit)
+ return fireEvent(node, event)
}
- const event = new EventType(eventName, eventInit)
- return fireEvent(node, event)
- }
-})
+ },
+)
// function written after some investigation here:
// https://github.com/facebook/react/issues/10135#issuecomment-401496776
diff --git a/src/get-node-text.js b/src/get-node-text.js
index cace4734..1f7c3ecd 100644
--- a/src/get-node-text.js
+++ b/src/get-node-text.js
@@ -1,4 +1,6 @@
function getNodeText(node) {
+ const window = node.ownerDocument.defaultView
+
return Array.from(node.childNodes)
.filter(
child =>
diff --git a/src/query-helpers.js b/src/query-helpers.js
index 8a8e7751..3f3e7e56 100644
--- a/src/query-helpers.js
+++ b/src/query-helpers.js
@@ -8,6 +8,7 @@ function debugDOM(htmlElement) {
typeof process !== 'undefined' &&
process.versions !== undefined &&
process.versions.node !== undefined
+ const window = htmlElement.ownerDocument.defaultView
const inCypress = typeof window !== 'undefined' && window.Cypress
/* istanbul ignore else */
if (inCypress) {
diff --git a/src/wait-for-element.js b/src/wait-for-element.js
index 1bed5511..e63d0e7c 100644
--- a/src/wait-for-element.js
+++ b/src/wait-for-element.js
@@ -1,9 +1,21 @@
-import 'mutationobserver-shim'
+import MutationObserver from '@sheerun/mutationobserver-shim'
+
+function windowFor(container) {
+ if (container.defaultView) {
+ return container.defaultView
+ }
+
+ if (container.ownerDocument) {
+ return container.ownerDocument.defaultView
+ }
+
+ throw new Error('No window for container')
+}
function waitForElement(
callback = undefined,
{
- container = document,
+ container = getDocument(),
timeout = 4500,
mutationObserverOptions = {
subtree: true,
@@ -46,7 +58,10 @@ function waitForElement(
onDone(lastError || new Error('Timed out in waitForElement.'), null)
}
timer = setTimeout(onTimeout, timeout)
- observer = new window.MutationObserver(onMutation)
+ const window = windowFor(container)
+ const MutationObserverConstructor =
+ window.MutationObserver || MutationObserver
+ observer = new MutationObserverConstructor(onMutation)
observer.observe(container, mutationObserverOptions)
if (callback !== undefined) {
onMutation()
@@ -54,4 +69,11 @@ function waitForElement(
})
}
+function getDocument() {
+ if (typeof document === 'undefined') {
+ throw new Error('Could not find default container')
+ }
+ return document
+}
+
export {waitForElement}