diff --git a/.all-contributorsrc b/.all-contributorsrc
index 0a043692..8c4a5552 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1637,6 +1637,15 @@
"code",
"bug"
]
+ },
+ {
+ "login": "sieem",
+ "name": "sieem",
+ "avatar_url": "https://avatars.githubusercontent.com/u/36861085?v=4",
+ "profile": "https://github.com/sieem",
+ "contributions": [
+ "code"
+ ]
}
],
"repoHost": "https://github.com",
diff --git a/README.md b/README.md
index 95795366..2011260a 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,7 @@ Thanks goes to these people ([emoji key][emojis]):
 Kent C. Dodds 💻 📖 🚇 ⚠️ |
-  Greg Bergé 🤔 |
+  Greg Bergé 🤔 |
 Ryan Castner 📖 |
 Daniel Sandiego 💻 |
 Paweł Mikołajczyk 💻 |
@@ -348,6 +348,9 @@ Thanks goes to these people ([emoji key][emojis]):
 Julien Wajsberg 💻 🐛 |
 Kevin BON 💻 🐛 |
+
+  sieem 💻 |
+
diff --git a/src/DOMElementFilter.ts b/src/DOMElementFilter.ts
index bf5ff686..743c4e3b 100644
--- a/src/DOMElementFilter.ts
+++ b/src/DOMElementFilter.ts
@@ -158,16 +158,22 @@ const FRAGMENT_NODE = 11
const ELEMENT_REGEXP = /^((HTML|SVG)\w*)?Element$/
+const isCustomElement = (val: any) => {
+ const {tagName} = val
+ return Boolean(
+ (typeof tagName === 'string' && tagName.includes('-')) ||
+ (typeof val.hasAttribute === 'function' && val.hasAttribute('is')),
+ )
+}
+
const testNode = (val: any) => {
const constructorName = val.constructor.name
- const {nodeType, tagName} = val
- const isCustomElement =
- (typeof tagName === 'string' && tagName.includes('-')) ||
- (typeof val.hasAttribute === 'function' && val.hasAttribute('is'))
+
+ const {nodeType} = val
return (
(nodeType === ELEMENT_NODE &&
- (ELEMENT_REGEXP.test(constructorName) || isCustomElement)) ||
+ (ELEMENT_REGEXP.test(constructorName) || isCustomElement(val))) ||
(nodeType === TEXT_NODE && constructorName === 'Text') ||
(nodeType === COMMENT_NODE && constructorName === 'Comment') ||
(nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment')
@@ -175,7 +181,7 @@ const testNode = (val: any) => {
}
export const test: NewPlugin['test'] = (val: any) =>
- val?.constructor?.name && testNode(val)
+ (val?.constructor?.name || isCustomElement(val)) && testNode(val)
type HandledType = Element | Text | Comment | DocumentFragment
@@ -195,7 +201,8 @@ export default function createDOMElementFilter(
filterNode: (node: Node) => boolean,
): NewPlugin {
return {
- test: (val: any) => val?.constructor?.name && testNode(val),
+ test: (val: any) =>
+ (val?.constructor?.name || isCustomElement(val)) && testNode(val),
serialize: (
node: HandledType,
config: Config,
diff --git a/src/__node_tests__/pretty-dom.js b/src/__node_tests__/pretty-dom.js
new file mode 100644
index 00000000..6aca2443
--- /dev/null
+++ b/src/__node_tests__/pretty-dom.js
@@ -0,0 +1,106 @@
+import {JSDOM} from 'jsdom'
+import {prettyDOM} from '../pretty-dom'
+
+function render(html) {
+ const {window} = new JSDOM()
+ const container = window.document.createElement('div')
+ container.innerHTML = html
+ return {container}
+}
+
+jest.mock('../get-user-code-frame')
+
+test('prettyDOM supports a COLORS environment variable', () => {
+ const {container} = render('Hello World!
')
+
+ // process.env.COLORS is a string, so make sure we test it as such
+ process.env.COLORS = 'false'
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+
+ `)
+
+ process.env.COLORS = 'true'
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+ [36m[39m
+ [36m
[39m
+ [0mHello World![0m
+ [36m
[39m
+ [36m
[39m
+ `)
+})
+
+test('prettyDOM handles a COLORS env variable of unexpected object type by colorizing for node', () => {
+ const {container} = render('Hello World!
')
+
+ const originalNodeVersion = process.versions.node
+ process.env.COLORS = '{}'
+ delete process.versions.node
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+
+ `)
+ process.versions.node = '1.2.3'
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+ [36m[39m
+ [36m
[39m
+ [0mHello World![0m
+ [36m
[39m
+ [36m
[39m
+ `)
+ process.versions.node = originalNodeVersion
+})
+
+test('prettyDOM handles a COLORS env variable of undefined by colorizing for node', () => {
+ const {container} = render('Hello World!
')
+
+ const originalNodeVersion = process.versions.node
+ process.env.COLORS = undefined
+ delete process.versions.node
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+
+ `)
+ process.versions.node = '1.2.3'
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+ [36m[39m
+ [36m
[39m
+ [0mHello World![0m
+ [36m
[39m
+ [36m
[39m
+ `)
+ process.versions.node = originalNodeVersion
+})
+
+test('prettyDOM handles a COLORS env variable of empty string by colorizing for node', () => {
+ const {container} = render('Hello World!
')
+
+ const originalNodeVersion = process.versions.node
+ process.env.COLORS = ''
+ delete process.versions.node
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+
+ `)
+ process.versions.node = '1.2.3'
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+ [36m[39m
+ [36m
[39m
+ [0mHello World![0m
+ [36m
[39m
+ [36m
[39m
+ `)
+ process.versions.node = originalNodeVersion
+})
diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js
index f3bc0ab0..a8e00011 100644
--- a/src/__tests__/element-queries.js
+++ b/src/__tests__/element-queries.js
@@ -1,6 +1,9 @@
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {configure, getConfig} from '../config'
import {render, renderIntoDocument} from './helpers/test-utils'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
// set original config
let originalConfig
beforeAll(() => {
diff --git a/src/__tests__/get-user-code-frame.js b/src/__tests__/get-user-code-frame.js
index 8d2bd058..59a1123b 100644
--- a/src/__tests__/get-user-code-frame.js
+++ b/src/__tests__/get-user-code-frame.js
@@ -1,6 +1,9 @@
import fs from 'fs'
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {getUserCodeFrame} from '../get-user-code-frame'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
jest.mock('fs', () => ({
// We setup the contents of a sample file
readFileSync: jest.fn(
diff --git a/src/__tests__/log-dom.js b/src/__tests__/log-dom.js
new file mode 100644
index 00000000..d6a52e62
--- /dev/null
+++ b/src/__tests__/log-dom.js
@@ -0,0 +1,56 @@
+import {getUserCodeFrame} from '../get-user-code-frame'
+import {logDOM} from '../pretty-dom'
+import {render} from './helpers/test-utils'
+
+jest.mock('../get-user-code-frame')
+
+beforeEach(() => {
+ jest.spyOn(console, 'log').mockImplementation(() => {})
+})
+
+afterEach(() => {
+ console.log.mockRestore()
+})
+
+test('logDOM logs highlighted prettyDOM to the console', () => {
+ const {container} = render('Hello World!
')
+ logDOM(container)
+ expect(console.log).toHaveBeenCalledTimes(1)
+ expect(console.log.mock.calls[0][0]).toMatchInlineSnapshot(`
+ [36m[39m
+ [36m
[39m
+ [0mHello World![0m
+ [36m
[39m
+ [36m
[39m
+ `)
+})
+
+test('logDOM logs highlighted prettyDOM with code frame to the console', () => {
+ getUserCodeFrame.mockImplementationOnce(
+ () => `"/home/john/projects/sample-error/error-example.js:7:14
+ 5 | document.createTextNode('Hello World!')
+ 6 | )
+ > 7 | screen.debug()
+ | ^
+ "
+ `,
+ )
+ const {container} = render('Hello World!
')
+ logDOM(container)
+ expect(console.log).toHaveBeenCalledTimes(1)
+ expect(console.log.mock.calls[0][0]).toMatchInlineSnapshot(`
+ [36m[39m
+ [36m
[39m
+ [0mHello World![0m
+ [36m
[39m
+ [36m
[39m
+
+ "/home/john/projects/sample-error/error-example.js:7:14
+ 5 | document.createTextNode('Hello World!')
+ 6 | )
+ > 7 | screen.debug()
+ | ^
+ "
+
+ `)
+})
diff --git a/src/__tests__/pretty-dom.js b/src/__tests__/pretty-dom.js
index 889d0fed..2b6ae6b4 100644
--- a/src/__tests__/pretty-dom.js
+++ b/src/__tests__/pretty-dom.js
@@ -1,16 +1,23 @@
-import {prettyDOM, logDOM} from '../pretty-dom'
-import {getUserCodeFrame} from '../get-user-code-frame'
+/* global globalThis */
+import {prettyDOM as prettyDOMImpl} from '../pretty-dom'
import {render, renderIntoDocument} from './helpers/test-utils'
-jest.mock('../get-user-code-frame')
-
-beforeEach(() => {
- jest.spyOn(console, 'log').mockImplementation(() => {})
-})
-
-afterEach(() => {
- console.log.mockRestore()
-})
+function prettyDOM(...args) {
+ let originalProcess
+ // this shouldn't be defined in this environment in the first place
+ if (typeof process === 'undefined') {
+ throw new Error('process is no longer defined. Remove this setup code.')
+ } else {
+ originalProcess = process
+ delete globalThis.process
+ }
+
+ try {
+ return prettyDOMImpl(...args)
+ } finally {
+ globalThis.process = originalProcess
+ }
+}
test('prettyDOM prints out the given DOM element tree', () => {
const {container} = render('Hello World!
')
@@ -58,49 +65,6 @@ test('prettyDOM supports receiving the document element', () => {
`)
})
-test('logDOM logs prettyDOM to the console', () => {
- const {container} = render('Hello World!
')
- logDOM(container)
- expect(console.log).toHaveBeenCalledTimes(1)
- expect(console.log.mock.calls[0][0]).toMatchInlineSnapshot(`
-
- `)
-})
-
-test('logDOM logs prettyDOM with code frame to the console', () => {
- getUserCodeFrame.mockImplementationOnce(
- () => `"/home/john/projects/sample-error/error-example.js:7:14
- 5 | document.createTextNode('Hello World!')
- 6 | )
- > 7 | screen.debug()
- | ^
- "
- `,
- )
- const {container} = render('Hello World!
')
- logDOM(container)
- expect(console.log).toHaveBeenCalledTimes(1)
- expect(console.log.mock.calls[0][0]).toMatchInlineSnapshot(`
-
-
- "/home/john/projects/sample-error/error-example.js:7:14
- 5 | document.createTextNode('Hello World!')
- 6 | )
- > 7 | screen.debug()
- | ^
- "
-
- `)
-})
-
describe('prettyDOM fails with first parameter without outerHTML field', () => {
test('with array', () => {
expect(() => prettyDOM(['outerHTML'])).toThrowErrorMatchingInlineSnapshot(
@@ -153,16 +117,33 @@ test('prettyDOM can include all elements with a custom filter', () => {
`)
})
-test('prettyDOM supports a COLORS environment variable', () => {
- const {container} = render('Hello World!
')
+test('prettyDOM supports named custom elements', () => {
+ window.customElements.define(
+ 'my-element-1',
+ class MyElement extends HTMLElement {},
+ )
- const noColors = prettyDOM(container, undefined, {highlight: false})
- const withColors = prettyDOM(container, undefined, {highlight: true})
+ const {container} = render('Hello World!')
- // process.env.COLORS is a string, so make sure we test it as such
- process.env.COLORS = 'false'
- expect(prettyDOM(container)).toEqual(noColors)
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+
+
+ Hello World!
+
+
+ `)
+})
+
+test('prettyDOM supports anonymous custom elements', () => {
+ window.customElements.define('my-element-2', class extends HTMLElement {})
- process.env.COLORS = 'true'
- expect(prettyDOM(container)).toEqual(withColors)
+ const {container} = render('Hello World!')
+
+ expect(prettyDOM(container)).toMatchInlineSnapshot(`
+
+
+ Hello World!
+
+
+ `)
})
diff --git a/src/__tests__/role-helpers.js b/src/__tests__/role-helpers.js
index 1ed32464..f47daa42 100644
--- a/src/__tests__/role-helpers.js
+++ b/src/__tests__/role-helpers.js
@@ -1,3 +1,4 @@
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {
getRoles,
logRoles,
@@ -6,6 +7,8 @@ import {
} from '../role-helpers'
import {render} from './helpers/test-utils'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
beforeEach(() => {
jest.spyOn(console, 'log').mockImplementation(() => {})
})
diff --git a/src/__tests__/role.js b/src/__tests__/role.js
index 447a6e8b..cf598583 100644
--- a/src/__tests__/role.js
+++ b/src/__tests__/role.js
@@ -1,7 +1,10 @@
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {configure, getConfig} from '../config'
import {getQueriesForElement} from '../get-queries-for-element'
import {render, renderIntoDocument} from './helpers/test-utils'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
test('by default logs accessible roles when it fails', () => {
const {getByRole} = render(`Hi
`)
expect(() => getByRole('article')).toThrowErrorMatchingInlineSnapshot(`
diff --git a/src/__tests__/screen.js b/src/__tests__/screen.js
index 7c57de4a..42356436 100644
--- a/src/__tests__/screen.js
+++ b/src/__tests__/screen.js
@@ -1,6 +1,9 @@
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {screen} from '..'
import {render, renderIntoDocument} from './helpers/test-utils'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
// Since screen.debug internally calls getUserCodeFrame, we mock it so it doesn't affect these tests
jest.mock('../get-user-code-frame', () => ({
getUserCodeFrame: () => '',
diff --git a/src/__tests__/suggestions.js b/src/__tests__/suggestions.js
index 160f7ee5..64761025 100644
--- a/src/__tests__/suggestions.js
+++ b/src/__tests__/suggestions.js
@@ -1,7 +1,10 @@
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {configure} from '../config'
import {screen, getSuggestedQuery} from '..'
import {renderIntoDocument, render} from './helpers/test-utils'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
beforeAll(() => {
configure({throwSuggestions: true})
})
diff --git a/src/__tests__/wait-for.js b/src/__tests__/wait-for.js
index 5295543c..38f5b3dd 100644
--- a/src/__tests__/wait-for.js
+++ b/src/__tests__/wait-for.js
@@ -1,7 +1,10 @@
+import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
import {waitFor} from '../'
import {configure, getConfig} from '../config'
import {renderIntoDocument} from './helpers/test-utils'
+expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
+
function deferred() {
let resolve, reject
const promise = new Promise((res, rej) => {
diff --git a/src/pretty-dom.js b/src/pretty-dom.js
index 9b1eafa2..155939d9 100644
--- a/src/pretty-dom.js
+++ b/src/pretty-dom.js
@@ -5,11 +5,19 @@ import {getDocument} from './helpers'
import {getConfig} from './config'
const shouldHighlight = () => {
+ if (typeof process === 'undefined') {
+ // Don't colorize in non-node environments (e.g. Browsers)
+ return false
+ }
let colors
+ // Try to safely parse env COLORS: We will default behavior if any step fails.
try {
- colors = JSON.parse(process?.env?.COLORS)
- } catch (e) {
- // If this throws, process?.env?.COLORS wasn't parsable. Since we only
+ const colorsJSON = process.env?.COLORS
+ if (colorsJSON) {
+ colors = JSON.parse(colorsJSON)
+ }
+ } catch {
+ // If this throws, process.env?.COLORS wasn't parsable. Since we only
// care about `true` or `false`, we can safely ignore the error.
}
@@ -18,11 +26,7 @@ const shouldHighlight = () => {
return colors
} else {
// If `colors` is not set, colorize if we're in node.
- return (
- typeof process !== 'undefined' &&
- process.versions !== undefined &&
- process.versions.node !== undefined
- )
+ return process.versions !== undefined && process.versions.node !== undefined
}
}
@@ -47,7 +51,10 @@ function prettyDOM(dom, maxLength, options = {}) {
}
if (typeof maxLength !== 'number') {
maxLength =
- (typeof process !== 'undefined' && process.env.DEBUG_PRINT_LIMIT) || 7000
+ (typeof process !== 'undefined' &&
+ typeof process.env !== 'undefined' &&
+ process.env.DEBUG_PRINT_LIMIT) ||
+ 7000
}
if (maxLength === 0) {
diff --git a/tests/setup-env.js b/tests/setup-env.js
index 1590dc53..9271f36b 100644
--- a/tests/setup-env.js
+++ b/tests/setup-env.js
@@ -1,7 +1,5 @@
import '@testing-library/jest-dom/extend-expect'
-import jestSnapshotSerializerAnsi from 'jest-snapshot-serializer-ansi'
-expect.addSnapshotSerializer(jestSnapshotSerializerAnsi)
// add serializer for MutationRecord
expect.addSnapshotSerializer({
print: (record, serialize) => {