diff --git a/src/__tests__/helpers.js b/src/__tests__/helpers.js
index 1bc858374..630e9a57d 100644
--- a/src/__tests__/helpers.js
+++ b/src/__tests__/helpers.js
@@ -3,7 +3,9 @@ import {
getWindowFromNode,
checkContainerType,
runWithRealTimers,
+ isInstanceOfElement,
} from '../helpers'
+import {render} from './helpers/test-utils'
const globalObj = typeof window === 'undefined' ? global : window
@@ -53,6 +55,90 @@ describe('query container validation throws when validation fails', () => {
})
})
+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 {container} = render(``)
+
+ expect(container.firstChild.ownerDocument.defaultView).toEqual(
+ expect.objectContaining({
+ HTMLSpanElement: expect.any(Function),
+ }),
+ )
+
+ expect(isInstanceOfElement(container.firstChild, 'HTMLSpanElement')).toBe(
+ true,
+ )
+ expect(isInstanceOfElement(container.firstChild, 'HTMLDivElement')).toBe(
+ false,
+ )
+ })
+
+ test('check in detached document', () => {
+ const {container} = render(``)
+
+ Object.defineProperty(
+ Object.getPrototypeOf(container.ownerDocument),
+ 'defaultView',
+ {value: null},
+ )
+
+ expect(container.firstChild.ownerDocument.defaultView).toBe(null)
+
+ expect(isInstanceOfElement(container.firstChild, 'HTMLSpanElement')).toBe(
+ true,
+ )
+ expect(isInstanceOfElement(container.firstChild, 'HTMLDivElement')).toBe(
+ false,
+ )
+ })
+
+ test('check in environment not providing constructors on window', () => {
+ const {container} = render(``)
+
+ delete global.window.HTMLSpanElement
+
+ expect(container.firstChild.ownerDocument.defaultView.HTMLSpanElement).toBe(
+ undefined,
+ )
+
+ expect(isInstanceOfElement(container.firstChild, 'HTMLSpanElement')).toBe(
+ true,
+ )
+ expect(isInstanceOfElement(container.firstChild, '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()
+ })
+})
+
test('should always use realTimers before using callback when timers are faked with useFakeTimers', () => {
const originalSetTimeout = globalObj.setTimeout
diff --git a/src/helpers.js b/src/helpers.js
index f686d46ba..8245fd819 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -100,6 +100,36 @@ function getWindowFromNode(node) {
}
}
+/**
+ * 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 checkContainerType(container) {
if (
!container ||
@@ -131,4 +161,5 @@ export {
checkContainerType,
jestFakeTimersAreEnabled,
TEXT_NODE,
+ isInstanceOfElement,
}