Skip to content

Commit ca059fc

Browse files
committed
Use a fork of DOMElement plugin
1 parent 4295727 commit ca059fc

File tree

8 files changed

+287
-64
lines changed

8 files changed

+287
-64
lines changed

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
"scripts": {
2727
"build": "kcd-scripts build --no-ts-defs --ignore \"**/__tests__/**,**/__node_tests__/**,**/__mocks__/**\" && kcd-scripts build --no-ts-defs --bundle --no-clean",
2828
"lint": "kcd-scripts lint",
29-
"postinstall": "patch-package",
3029
"setup": "npm install && npm run validate -s",
3130
"test": "kcd-scripts test",
3231
"test:debug": "node --inspect-brk ./node_modules/.bin/jest --watch --runInBand",
@@ -55,7 +54,6 @@
5554
"jest-watch-select-projects": "^2.0.0",
5655
"jsdom": "^16.4.0",
5756
"kcd-scripts": "^7.5.3",
58-
"patch-package": "^6.4.1",
5957
"typescript": "^4.1.2"
6058
},
6159
"eslintConfig": {

patches/pretty-format+26.6.2.patch

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/DOMElementFilter.ts

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/**
2+
* Source: https://github.com/facebook/jest/blob/master/packages/pretty-format/src/plugins/DOMElement.ts
3+
*/
4+
import type {Config, NewPlugin, Printer, Refs} from 'pretty-format'
5+
6+
function escapeHTML(str: string): string {
7+
return str.replace(/</g, '&lt;').replace(/>/g, '&gt;')
8+
}
9+
// Return empty string if keys is empty.
10+
const printProps = (
11+
keys: Array<string>,
12+
props: Record<string, unknown>,
13+
config: Config,
14+
indentation: string,
15+
depth: number,
16+
refs: Refs,
17+
printer: Printer,
18+
): string => {
19+
const indentationNext = indentation + config.indent
20+
const colors = config.colors
21+
return keys
22+
.map(key => {
23+
const value = props[key]
24+
let printed = printer(value, config, indentationNext, depth, refs)
25+
26+
if (typeof value !== 'string') {
27+
if (printed.indexOf('\n') !== -1) {
28+
printed =
29+
config.spacingOuter +
30+
indentationNext +
31+
printed +
32+
config.spacingOuter +
33+
indentation
34+
}
35+
printed = '{' + printed + '}'
36+
}
37+
38+
return (
39+
config.spacingInner +
40+
indentation +
41+
colors.prop.open +
42+
key +
43+
colors.prop.close +
44+
'=' +
45+
colors.value.open +
46+
printed +
47+
colors.value.close
48+
)
49+
})
50+
.join('')
51+
}
52+
53+
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node_type_constants
54+
const NodeTypeTextNode = 3
55+
56+
// Return empty string if children is empty.
57+
const printChildren = (
58+
children: Array<unknown>,
59+
config: Config,
60+
indentation: string,
61+
depth: number,
62+
refs: Refs,
63+
printer: Printer,
64+
): string =>
65+
children
66+
.map(child => {
67+
const printedChild =
68+
typeof child === 'string'
69+
? printText(child, config)
70+
: printer(child, config, indentation, depth, refs)
71+
72+
if (
73+
printedChild === '' &&
74+
typeof child === 'object' &&
75+
child !== null &&
76+
(child as Node).nodeType !== NodeTypeTextNode
77+
) {
78+
// A plugin serialized this Node to '' meaning we should ignore it.
79+
return ''
80+
}
81+
return config.spacingOuter + indentation + printedChild
82+
})
83+
.join('')
84+
85+
const printText = (text: string, config: Config): string => {
86+
const contentColor = config.colors.content
87+
return contentColor.open + escapeHTML(text) + contentColor.close
88+
}
89+
90+
const printComment = (comment: string, config: Config): string => {
91+
const commentColor = config.colors.comment
92+
return (
93+
commentColor.open +
94+
'<!--' +
95+
escapeHTML(comment) +
96+
'-->' +
97+
commentColor.close
98+
)
99+
}
100+
101+
// Separate the functions to format props, children, and element,
102+
// so a plugin could override a particular function, if needed.
103+
// Too bad, so sad: the traditional (but unnecessary) space
104+
// in a self-closing tagColor requires a second test of printedProps.
105+
const printElement = (
106+
type: string,
107+
printedProps: string,
108+
printedChildren: string,
109+
config: Config,
110+
indentation: string,
111+
): string => {
112+
const tagColor = config.colors.tag
113+
return (
114+
tagColor.open +
115+
'<' +
116+
type +
117+
(printedProps &&
118+
tagColor.close +
119+
printedProps +
120+
config.spacingOuter +
121+
indentation +
122+
tagColor.open) +
123+
(printedChildren
124+
? '>' +
125+
tagColor.close +
126+
printedChildren +
127+
config.spacingOuter +
128+
indentation +
129+
tagColor.open +
130+
'</' +
131+
type
132+
: (printedProps && !config.min ? '' : ' ') + '/') +
133+
'>' +
134+
tagColor.close
135+
)
136+
}
137+
138+
const printElementAsLeaf = (type: string, config: Config): string => {
139+
const tagColor = config.colors.tag
140+
return (
141+
tagColor.open +
142+
'<' +
143+
type +
144+
tagColor.close +
145+
' …' +
146+
tagColor.open +
147+
' />' +
148+
tagColor.close
149+
)
150+
}
151+
152+
const ELEMENT_NODE = 1
153+
const TEXT_NODE = 3
154+
const COMMENT_NODE = 8
155+
const FRAGMENT_NODE = 11
156+
157+
const ELEMENT_REGEXP = /^((HTML|SVG)\w*)?Element$/
158+
159+
const testNode = (val: any) => {
160+
const constructorName = val.constructor.name
161+
const {nodeType, tagName} = val
162+
const isCustomElement =
163+
(typeof tagName === 'string' && tagName.includes('-')) ||
164+
(typeof val.hasAttribute === 'function' && val.hasAttribute('is'))
165+
166+
return (
167+
(nodeType === ELEMENT_NODE &&
168+
(ELEMENT_REGEXP.test(constructorName) || isCustomElement)) ||
169+
(nodeType === TEXT_NODE && constructorName === 'Text') ||
170+
(nodeType === COMMENT_NODE && constructorName === 'Comment') ||
171+
(nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment')
172+
)
173+
}
174+
175+
export const test: NewPlugin['test'] = (val: any) =>
176+
val?.constructor?.name && testNode(val)
177+
178+
type HandledType = Element | Text | Comment | DocumentFragment
179+
180+
function nodeIsText(node: HandledType): node is Text {
181+
return node.nodeType === TEXT_NODE
182+
}
183+
184+
function nodeIsComment(node: HandledType): node is Comment {
185+
return node.nodeType === COMMENT_NODE
186+
}
187+
188+
function nodeIsFragment(node: HandledType): node is DocumentFragment {
189+
return node.nodeType === FRAGMENT_NODE
190+
}
191+
192+
export default function createDOMElementFilter(
193+
filterNode: (node: Node) => boolean,
194+
): NewPlugin {
195+
return {
196+
test: (val: any) => val?.constructor?.name && testNode(val),
197+
serialize: (
198+
node: HandledType,
199+
config: Config,
200+
indentation: string,
201+
depth: number,
202+
refs: Refs,
203+
printer: Printer,
204+
) => {
205+
if (nodeIsText(node)) {
206+
return printText(node.data, config)
207+
}
208+
209+
if (nodeIsComment(node)) {
210+
return printComment(node.data, config)
211+
}
212+
213+
const type = nodeIsFragment(node)
214+
? `DocumentFragment`
215+
: node.tagName.toLowerCase()
216+
217+
if (++depth > config.maxDepth) {
218+
return printElementAsLeaf(type, config)
219+
}
220+
221+
return printElement(
222+
type,
223+
printProps(
224+
nodeIsFragment(node)
225+
? []
226+
: Array.from(node.attributes)
227+
.map(attr => attr.name)
228+
.sort(),
229+
nodeIsFragment(node)
230+
? {}
231+
: Array.from(node.attributes).reduce<Record<string, string>>(
232+
(props, attribute) => {
233+
props[attribute.name] = attribute.value
234+
return props
235+
},
236+
{},
237+
),
238+
config,
239+
indentation + config.indent,
240+
depth,
241+
refs,
242+
printer,
243+
),
244+
printChildren(
245+
Array.prototype.slice
246+
.call(node.childNodes || node.children)
247+
.filter(filterNode),
248+
config,
249+
indentation + config.indent,
250+
depth,
251+
refs,
252+
printer,
253+
),
254+
config,
255+
indentation,
256+
)
257+
},
258+
}
259+
}

src/config.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,18 @@ let config: InternalConfig = {
3131

3232
// called when getBy* queries fail. (message, container) => Error
3333
getElementError(message, container) {
34+
const prettifiedDOM = prettyDOM(container)
3435
const error = new Error(
35-
[message, prettyDOM(container)].filter(Boolean).join('\n\n'),
36+
[
37+
message,
38+
prettifiedDOM.length > 0
39+
? `Ignored nodes: comments, <script />, <style />\n${prettyDOM(
40+
container,
41+
)}`
42+
: null,
43+
]
44+
.filter(Boolean)
45+
.join('\n\n'),
3646
)
3747
error.name = 'TestingLibraryElementError'
3848
return error
@@ -41,7 +51,6 @@ let config: InternalConfig = {
4151
computedStyleSupportsPseudoElements: false,
4252
}
4353

44-
export const DEFAULT_IGNORE_TAGS = 'script, style'
4554
export function runWithExpensiveErrorDiagnosticsDisabled<T>(
4655
callback: Callback<T>,
4756
) {

0 commit comments

Comments
 (0)