diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js
new file mode 100644
index 00000000..bdd0d0e8
--- /dev/null
+++ b/src/__tests__/utils.js
@@ -0,0 +1,73 @@
+import {isInstanceOfElement} from '../utils'
+import {setup} from './helpers/utils'
+
+// isInstanceOfElement can be removed once the peerDependency for @testing-library/dom is bumped to a version that includes https://github.com/testing-library/dom-testing-library/pull/885
+describe('check element type per isInstanceOfElement', () => {
+ let defaultViewDescriptor, spanDescriptor
+ beforeAll(() => {
+ defaultViewDescriptor = Object.getOwnPropertyDescriptor(
+ Object.getPrototypeOf(global.document),
+ 'defaultView',
+ )
+ spanDescriptor = Object.getOwnPropertyDescriptor(
+ global.window,
+ 'HTMLSpanElement',
+ )
+ })
+ afterEach(() => {
+ Object.defineProperty(
+ Object.getPrototypeOf(global.document),
+ 'defaultView',
+ defaultViewDescriptor,
+ )
+ Object.defineProperty(global.window, 'HTMLSpanElement', spanDescriptor)
+ })
+
+ test('check in regular jest environment', () => {
+ const {element} = setup(``)
+
+ expect(element.ownerDocument.defaultView).toEqual(
+ expect.objectContaining({
+ HTMLSpanElement: expect.any(Function),
+ }),
+ )
+
+ expect(isInstanceOfElement(element, 'HTMLSpanElement')).toBe(true)
+ expect(isInstanceOfElement(element, 'HTMLDivElement')).toBe(false)
+ })
+
+ test('check in detached document', () => {
+ const {element} = setup(``)
+
+ Object.defineProperty(
+ Object.getPrototypeOf(element.ownerDocument),
+ 'defaultView',
+ {value: null},
+ )
+
+ expect(element.ownerDocument.defaultView).toBe(null)
+
+ expect(isInstanceOfElement(element, 'HTMLSpanElement')).toBe(true)
+ expect(isInstanceOfElement(element, 'HTMLDivElement')).toBe(false)
+ })
+
+ test('check in environment not providing constructors on window', () => {
+ const {element} = setup(``)
+
+ delete global.window.HTMLSpanElement
+
+ expect(element.ownerDocument.defaultView.HTMLSpanElement).toBe(undefined)
+
+ expect(isInstanceOfElement(element, 'HTMLSpanElement')).toBe(true)
+ expect(isInstanceOfElement(element, 'HTMLDivElement')).toBe(false)
+ })
+
+ test('throw error if element is not created by HTML*Element constructor', () => {
+ const doc = new Document()
+
+ // constructor is global.Element
+ const element = doc.createElement('span')
+
+ expect(() => isInstanceOfElement(element, 'HTMLSpanElement')).toThrow()
+ })
+})
diff --git a/src/select-options.js b/src/select-options.js
index 1ca6777d..9620814e 100644
--- a/src/select-options.js
+++ b/src/select-options.js
@@ -1,4 +1,5 @@
import {createEvent, getConfig, fireEvent} from '@testing-library/dom'
+import {isInstanceOfElement} from './utils'
import {click} from './click'
import {focus} from './focus'
import {hover, unhover} from './hover'
@@ -36,7 +37,7 @@ function selectOptionsBase(newValue, select, values, init) {
if (select.disabled || !selectedOptions.length) return
- if (select instanceof HTMLSelectElement) {
+ if (isInstanceOfElement(select, 'HTMLSelectElement')) {
if (select.multiple) {
for (const option of selectedOptions) {
// events fired for multiple select are weird. Can't use hover...
diff --git a/src/utils.js b/src/utils.js
index 0dc0c9c9..96578020 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,4 +1,36 @@
import {getConfig} from '@testing-library/dom'
+import {getWindowFromNode} from '@testing-library/dom/dist/helpers'
+
+// isInstanceOfElement can be removed once the peerDependency for @testing-library/dom is bumped to a version that includes https://github.com/testing-library/dom-testing-library/pull/885
+/**
+ * Check if an element is of a given type.
+ *
+ * @param Element The element to test
+ * @param string Constructor name. E.g. 'HTMLSelectElement'
+ */
+function isInstanceOfElement(element, elementType) {
+ try {
+ const window = getWindowFromNode(element)
+ // Window usually has the element constructors as properties but is not required to do so per specs
+ if (typeof window[elementType] === 'function') {
+ return element instanceof window[elementType]
+ }
+ } catch (e) {
+ // The document might not be associated with a window
+ }
+
+ // Fall back to the constructor name as workaround for test environments that
+ // a) not associate the document with a window
+ // b) not provide the constructor as property of window
+ if (/^HTML(\w+)Element$/.test(element.constructor.name)) {
+ return element.constructor.name === elementType
+ }
+
+ // The user passed some node that is not created in a browser-like environment
+ throw new Error(
+ `Unable to verify if element is instance of ${elementType}. Please file an issue describing your test environment: https://github.com/testing-library/dom-testing-library/issues/new`,
+ )
+}
function isMousePressEvent(event) {
return (
@@ -256,7 +288,7 @@ const CLICKABLE_INPUT_TYPES = [
function isClickable(element) {
return (
element.tagName === 'BUTTON' ||
- (element instanceof element.ownerDocument.defaultView.HTMLInputElement &&
+ (isInstanceOfElement(element, 'HTMLInputElement') &&
CLICKABLE_INPUT_TYPES.includes(element.type))
)
}
@@ -334,4 +366,5 @@ export {
getValue,
getSelectionRange,
isContentEditable,
+ isInstanceOfElement,
}