Skip to content

Commit

Permalink
Replace print with serialize in HTMLElement plugin (jestjs#4215)
Browse files Browse the repository at this point in the history
* Replace print with serialize in HTMLElement plugin

* Add comment about keysMapper and propsReducer
  • Loading branch information
pedrottimark authored and cpojer committed Aug 8, 2017
1 parent b3c4338 commit 3be4327
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 97 deletions.
74 changes: 74 additions & 0 deletions packages/pretty-format/src/__tests__/html_element.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ describe('HTMLElement Plugin', () => {
expect(parent).toPrettyPrintTo('<div\n class="classy"\n id="123"\n/>');
});

it('supports an HTML element with attribute and text content', () => {
const parent = document.createElement('div');
parent.setAttribute('style', 'color: #99424F');
parent.innerHTML = 'Jest';

expect(parent).toPrettyPrintTo(
'<div\n style="color: #99424F"\n>\n Jest\n</div>',
);
});

it('supports an element with text content', () => {
const parent = document.createElement('div');
parent.innerHTML = 'texty texty';
Expand Down Expand Up @@ -170,4 +180,68 @@ describe('HTMLElement Plugin', () => {
'</div>',
].join('\n'));
});

it('supports indentation for array of elements', () => {
// For example, Array.prototype.slice.call(document.getElementsByTagName(…))
const dd1 = document.createElement('dd');
dd1.innerHTML = 'to talk in a playful manner';

const dd2 = document.createElement('dd');
dd2.innerHTML = 'painless JavaScript testing';
dd2.setAttribute('style', 'color: #99424F');

expect([dd1, dd2]).toPrettyPrintTo(
[
'Array [',
' <dd>',
' to talk in a playful manner',
' </dd>,',
' <dd',
' style="color: #99424F"',
' >',
' painless JavaScript testing',
' </dd>,',
']',
].join('\n'),
);
});

it('supports maxDepth option', () => {
const dt = document.createElement('dt');
dt.innerHTML = 'jest';

const dd1 = document.createElement('dd');
dd1.innerHTML = 'to talk in a <em>playful</em> manner';

const dd2 = document.createElement('dd');
dd2.innerHTML = '<em>painless</em> JavaScript testing';
dd2.setAttribute('style', 'color: #99424F');

const dl = document.createElement('dl');
dl.appendChild(dt);
dl.appendChild(dd1);
dl.appendChild(dd2);

expect(dl).toPrettyPrintTo(
[
'<dl>',
' <dt>',
' jest',
' </dt>',
' <dd>',
' to talk in a ',
' <em … />',
' manner', // plugin incorrectly trims preceding space
' </dd>',
' <dd',
' style="color: #99424F"',
' >',
' <em … />',
' JavaScript testing', // plugin incorrectly trims preceding space
' </dd>',
'</dl>',
].join('\n'),
{maxDepth: 2},
);
});
});
162 changes: 65 additions & 97 deletions packages/pretty-format/src/plugins/html_element.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@
* @flow
*/

import type {
Colors,
Indent,
PluginOptions,
Print,
Plugin,
} from 'types/PrettyFormat';
import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat';

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

type Attribute = {
name: string,
Expand All @@ -34,79 +29,63 @@ type HTMLText = {
data: string,
nodeType: 3,
};

type HTMLComment = {
data: string,
nodeType: 8,
};

const HTML_ELEMENT_REGEXP = /(HTML\w*?Element)|Text|Comment/;
export const test = isHTMLElement;

function isHTMLElement(value: any) {
return (
value !== undefined &&
value !== null &&
(value.nodeType === 1 || value.nodeType === 3 || value.nodeType === 8) &&
value.constructor !== undefined &&
value.constructor.name !== undefined &&
HTML_ELEMENT_REGEXP.test(value.constructor.name)
);
}

function printChildren(flatChildren, print, indent, colors, opts) {
return flatChildren
.map(node => {
if (typeof node === 'string') {
return colors.content.open + escapeHTML(node) + colors.content.close;
} else {
return print(node);
}
})
.filter(value => value.trim().length)
.join(opts.edgeSpacing);
}

function printAttributes(
attributes: Array<Attribute>,
print,
indent,
colors,
opts,
) {
return attributes
.sort(
(attributeA, attributeB) =>
attributeA.name === attributeB.name
? 0
: attributeA.name < attributeB.name ? -1 : 1,
export const test = (val: any) =>
val !== undefined &&
val !== null &&
(val.nodeType === 1 || val.nodeType === 3 || val.nodeType === 8) &&
val.constructor !== undefined &&
val.constructor.name !== undefined &&
HTML_ELEMENT_REGEXP.test(val.constructor.name);

// Return empty string if children is empty.
function printChildren(children, config, indentation, depth, refs, printer) {
const colors = config.colors;
return children
.map(
node =>
typeof node === 'string'
? colors.content.open + escapeHTML(node) + colors.content.close
: printer(node, config, indentation, depth, refs),
)
.map(attribute => {
return (
opts.spacing +
indent(colors.prop.open + attribute.name + colors.prop.close + '=') +
colors.value.open +
print(attribute.value) +
colors.value.close
);
})
.filter(value => value.trim().length)
.map(value => config.spacingOuter + indentation + value)
.join('');
}

export const print = (
const getType = element => element.tagName.toLowerCase();

// Convert array of attribute objects to keys array and props object.
const keysMapper = attribute => attribute.name;
const propsReducer = (props, attribute) => {
props[attribute.name] = attribute.value;
return props;
};

export const serialize = (
element: HTMLElement | HTMLText | HTMLComment,
print: Print,
indent: Indent,
opts: PluginOptions,
colors: Colors,
config: Config,
indentation: string,
depth: number,
refs: Refs,
printer: Printer,
): string => {
if (element.nodeType === 3) {
return element.data
.split('\n')
.map(text => text.trimLeft())
.filter(text => text.length)
.join(' ');
} else if (element.nodeType === 8) {
}

const colors = config.colors;
if (element.nodeType === 8) {
return (
colors.comment.open +
'<!-- ' +
Expand All @@ -116,43 +95,32 @@ export const print = (
);
}

let result = colors.tag.open + '<';
const elementName = element.tagName.toLowerCase();
result += elementName + colors.tag.close;

const hasAttributes = element.attributes && element.attributes.length;
if (hasAttributes) {
const attributes = Array.prototype.slice.call(element.attributes);
result += printAttributes(attributes, print, indent, colors, opts);
}

const flatChildren = Array.prototype.slice.call(element.childNodes);
if (!flatChildren.length && element.textContent) {
flatChildren.push(element.textContent);
if (++depth > config.maxDepth) {
return printElementAsLeaf(getType(element), config);
}

const closeInNewLine = hasAttributes && !opts.min;
if (flatChildren.length) {
const children = printChildren(flatChildren, print, indent, colors, opts);
result +=
colors.tag.open +
(closeInNewLine ? '\n' : '') +
'>' +
colors.tag.close +
opts.edgeSpacing +
indent(children) +
opts.edgeSpacing +
colors.tag.open +
'</' +
elementName +
'>' +
colors.tag.close;
} else {
result +=
colors.tag.open + (closeInNewLine ? '\n' : ' ') + '/>' + colors.tag.close;
}

return result;
return printElement(
getType(element),
printProps(
Array.prototype.map.call(element.attributes, keysMapper).sort(),
Array.prototype.reduce.call(element.attributes, propsReducer, {}),
config,
indentation + config.indent,
depth,
refs,
printer,
),
printChildren(
Array.prototype.slice.call(element.childNodes),
config,
indentation + config.indent,
depth,
refs,
printer,
),
config,
indentation,
);
};

export default ({print, test}: Plugin);
export default ({serialize, test}: NewPlugin);

0 comments on commit 3be4327

Please sign in to comment.