Skip to content

Commit 47a840b

Browse files
pedrottimarkcpojer
authored andcommitted
Replace print with serialize in HTMLElement plugin (#4215)
* Replace print with serialize in HTMLElement plugin * Add comment about keysMapper and propsReducer
1 parent 4523826 commit 47a840b

File tree

2 files changed

+139
-97
lines changed

2 files changed

+139
-97
lines changed

packages/pretty-format/src/__tests__/html_element.test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ describe('HTMLElement Plugin', () => {
6161
expect(parent).toPrettyPrintTo('<div\n class="classy"\n id="123"\n/>');
6262
});
6363

64+
it('supports an HTML element with attribute and text content', () => {
65+
const parent = document.createElement('div');
66+
parent.setAttribute('style', 'color: #99424F');
67+
parent.innerHTML = 'Jest';
68+
69+
expect(parent).toPrettyPrintTo(
70+
'<div\n style="color: #99424F"\n>\n Jest\n</div>',
71+
);
72+
});
73+
6474
it('supports an element with text content', () => {
6575
const parent = document.createElement('div');
6676
parent.innerHTML = 'texty texty';
@@ -170,4 +180,68 @@ describe('HTMLElement Plugin', () => {
170180
'</div>',
171181
].join('\n'));
172182
});
183+
184+
it('supports indentation for array of elements', () => {
185+
// For example, Array.prototype.slice.call(document.getElementsByTagName(…))
186+
const dd1 = document.createElement('dd');
187+
dd1.innerHTML = 'to talk in a playful manner';
188+
189+
const dd2 = document.createElement('dd');
190+
dd2.innerHTML = 'painless JavaScript testing';
191+
dd2.setAttribute('style', 'color: #99424F');
192+
193+
expect([dd1, dd2]).toPrettyPrintTo(
194+
[
195+
'Array [',
196+
' <dd>',
197+
' to talk in a playful manner',
198+
' </dd>,',
199+
' <dd',
200+
' style="color: #99424F"',
201+
' >',
202+
' painless JavaScript testing',
203+
' </dd>,',
204+
']',
205+
].join('\n'),
206+
);
207+
});
208+
209+
it('supports maxDepth option', () => {
210+
const dt = document.createElement('dt');
211+
dt.innerHTML = 'jest';
212+
213+
const dd1 = document.createElement('dd');
214+
dd1.innerHTML = 'to talk in a <em>playful</em> manner';
215+
216+
const dd2 = document.createElement('dd');
217+
dd2.innerHTML = '<em>painless</em> JavaScript testing';
218+
dd2.setAttribute('style', 'color: #99424F');
219+
220+
const dl = document.createElement('dl');
221+
dl.appendChild(dt);
222+
dl.appendChild(dd1);
223+
dl.appendChild(dd2);
224+
225+
expect(dl).toPrettyPrintTo(
226+
[
227+
'<dl>',
228+
' <dt>',
229+
' jest',
230+
' </dt>',
231+
' <dd>',
232+
' to talk in a ',
233+
' <em … />',
234+
' manner', // plugin incorrectly trims preceding space
235+
' </dd>',
236+
' <dd',
237+
' style="color: #99424F"',
238+
' >',
239+
' <em … />',
240+
' JavaScript testing', // plugin incorrectly trims preceding space
241+
' </dd>',
242+
'</dl>',
243+
].join('\n'),
244+
{maxDepth: 2},
245+
);
246+
});
173247
});

packages/pretty-format/src/plugins/html_element.js

Lines changed: 65 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,10 @@
88
* @flow
99
*/
1010

11-
import type {
12-
Colors,
13-
Indent,
14-
PluginOptions,
15-
Print,
16-
Plugin,
17-
} from 'types/PrettyFormat';
11+
import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat';
1812

1913
import escapeHTML from './lib/escape_html';
14+
import {printElement, printElementAsLeaf, printProps} from './lib/markup';
2015

2116
type Attribute = {
2217
name: string,
@@ -34,79 +29,63 @@ type HTMLText = {
3429
data: string,
3530
nodeType: 3,
3631
};
37-
3832
type HTMLComment = {
3933
data: string,
4034
nodeType: 8,
4135
};
4236

4337
const HTML_ELEMENT_REGEXP = /(HTML\w*?Element)|Text|Comment/;
44-
export const test = isHTMLElement;
45-
46-
function isHTMLElement(value: any) {
47-
return (
48-
value !== undefined &&
49-
value !== null &&
50-
(value.nodeType === 1 || value.nodeType === 3 || value.nodeType === 8) &&
51-
value.constructor !== undefined &&
52-
value.constructor.name !== undefined &&
53-
HTML_ELEMENT_REGEXP.test(value.constructor.name)
54-
);
55-
}
56-
57-
function printChildren(flatChildren, print, indent, colors, opts) {
58-
return flatChildren
59-
.map(node => {
60-
if (typeof node === 'string') {
61-
return colors.content.open + escapeHTML(node) + colors.content.close;
62-
} else {
63-
return print(node);
64-
}
65-
})
66-
.filter(value => value.trim().length)
67-
.join(opts.edgeSpacing);
68-
}
6938

70-
function printAttributes(
71-
attributes: Array<Attribute>,
72-
print,
73-
indent,
74-
colors,
75-
opts,
76-
) {
77-
return attributes
78-
.sort(
79-
(attributeA, attributeB) =>
80-
attributeA.name === attributeB.name
81-
? 0
82-
: attributeA.name < attributeB.name ? -1 : 1,
39+
export const test = (val: any) =>
40+
val !== undefined &&
41+
val !== null &&
42+
(val.nodeType === 1 || val.nodeType === 3 || val.nodeType === 8) &&
43+
val.constructor !== undefined &&
44+
val.constructor.name !== undefined &&
45+
HTML_ELEMENT_REGEXP.test(val.constructor.name);
46+
47+
// Return empty string if children is empty.
48+
function printChildren(children, config, indentation, depth, refs, printer) {
49+
const colors = config.colors;
50+
return children
51+
.map(
52+
node =>
53+
typeof node === 'string'
54+
? colors.content.open + escapeHTML(node) + colors.content.close
55+
: printer(node, config, indentation, depth, refs),
8356
)
84-
.map(attribute => {
85-
return (
86-
opts.spacing +
87-
indent(colors.prop.open + attribute.name + colors.prop.close + '=') +
88-
colors.value.open +
89-
print(attribute.value) +
90-
colors.value.close
91-
);
92-
})
57+
.filter(value => value.trim().length)
58+
.map(value => config.spacingOuter + indentation + value)
9359
.join('');
9460
}
9561

96-
export const print = (
62+
const getType = element => element.tagName.toLowerCase();
63+
64+
// Convert array of attribute objects to keys array and props object.
65+
const keysMapper = attribute => attribute.name;
66+
const propsReducer = (props, attribute) => {
67+
props[attribute.name] = attribute.value;
68+
return props;
69+
};
70+
71+
export const serialize = (
9772
element: HTMLElement | HTMLText | HTMLComment,
98-
print: Print,
99-
indent: Indent,
100-
opts: PluginOptions,
101-
colors: Colors,
73+
config: Config,
74+
indentation: string,
75+
depth: number,
76+
refs: Refs,
77+
printer: Printer,
10278
): string => {
10379
if (element.nodeType === 3) {
10480
return element.data
10581
.split('\n')
10682
.map(text => text.trimLeft())
10783
.filter(text => text.length)
10884
.join(' ');
109-
} else if (element.nodeType === 8) {
85+
}
86+
87+
const colors = config.colors;
88+
if (element.nodeType === 8) {
11089
return (
11190
colors.comment.open +
11291
'<!-- ' +
@@ -116,43 +95,32 @@ export const print = (
11695
);
11796
}
11897

119-
let result = colors.tag.open + '<';
120-
const elementName = element.tagName.toLowerCase();
121-
result += elementName + colors.tag.close;
122-
123-
const hasAttributes = element.attributes && element.attributes.length;
124-
if (hasAttributes) {
125-
const attributes = Array.prototype.slice.call(element.attributes);
126-
result += printAttributes(attributes, print, indent, colors, opts);
127-
}
128-
129-
const flatChildren = Array.prototype.slice.call(element.childNodes);
130-
if (!flatChildren.length && element.textContent) {
131-
flatChildren.push(element.textContent);
98+
if (++depth > config.maxDepth) {
99+
return printElementAsLeaf(getType(element), config);
132100
}
133101

134-
const closeInNewLine = hasAttributes && !opts.min;
135-
if (flatChildren.length) {
136-
const children = printChildren(flatChildren, print, indent, colors, opts);
137-
result +=
138-
colors.tag.open +
139-
(closeInNewLine ? '\n' : '') +
140-
'>' +
141-
colors.tag.close +
142-
opts.edgeSpacing +
143-
indent(children) +
144-
opts.edgeSpacing +
145-
colors.tag.open +
146-
'</' +
147-
elementName +
148-
'>' +
149-
colors.tag.close;
150-
} else {
151-
result +=
152-
colors.tag.open + (closeInNewLine ? '\n' : ' ') + '/>' + colors.tag.close;
153-
}
154-
155-
return result;
102+
return printElement(
103+
getType(element),
104+
printProps(
105+
Array.prototype.map.call(element.attributes, keysMapper).sort(),
106+
Array.prototype.reduce.call(element.attributes, propsReducer, {}),
107+
config,
108+
indentation + config.indent,
109+
depth,
110+
refs,
111+
printer,
112+
),
113+
printChildren(
114+
Array.prototype.slice.call(element.childNodes),
115+
config,
116+
indentation + config.indent,
117+
depth,
118+
refs,
119+
printer,
120+
),
121+
config,
122+
indentation,
123+
);
156124
};
157125

158-
export default ({print, test}: Plugin);
126+
export default ({serialize, test}: NewPlugin);

0 commit comments

Comments
 (0)