UNPKG

5.24 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = createDOMElementFilter;
7exports.test = void 0;
8/**
9 * Source: https://github.com/facebook/jest/blob/e7bb6a1e26ffab90611b2593912df15b69315611/packages/pretty-format/src/plugins/DOMElement.ts
10 */
11/* eslint-disable -- trying to stay as close to the original as possible */
12/* istanbul ignore file */
13
14function escapeHTML(str) {
15 return str.replace(/</g, '&lt;').replace(/>/g, '&gt;');
16}
17// Return empty string if keys is empty.
18const printProps = (keys, props, config, indentation, depth, refs, printer) => {
19 const indentationNext = indentation + config.indent;
20 const colors = config.colors;
21 return keys.map(key => {
22 const value = props[key];
23 let printed = printer(value, config, indentationNext, depth, refs);
24 if (typeof value !== 'string') {
25 if (printed.indexOf('\n') !== -1) {
26 printed = config.spacingOuter + indentationNext + printed + config.spacingOuter + indentation;
27 }
28 printed = '{' + printed + '}';
29 }
30 return config.spacingInner + indentation + colors.prop.open + key + colors.prop.close + '=' + colors.value.open + printed + colors.value.close;
31 }).join('');
32};
33
34// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node_type_constants
35const NodeTypeTextNode = 3;
36
37// Return empty string if children is empty.
38const printChildren = (children, config, indentation, depth, refs, printer) => children.map(child => {
39 const printedChild = typeof child === 'string' ? printText(child, config) : printer(child, config, indentation, depth, refs);
40 if (printedChild === '' && typeof child === 'object' && child !== null && child.nodeType !== NodeTypeTextNode) {
41 // A plugin serialized this Node to '' meaning we should ignore it.
42 return '';
43 }
44 return config.spacingOuter + indentation + printedChild;
45}).join('');
46const printText = (text, config) => {
47 const contentColor = config.colors.content;
48 return contentColor.open + escapeHTML(text) + contentColor.close;
49};
50const printComment = (comment, config) => {
51 const commentColor = config.colors.comment;
52 return commentColor.open + '<!--' + escapeHTML(comment) + '-->' + commentColor.close;
53};
54
55// Separate the functions to format props, children, and element,
56// so a plugin could override a particular function, if needed.
57// Too bad, so sad: the traditional (but unnecessary) space
58// in a self-closing tagColor requires a second test of printedProps.
59const printElement = (type, printedProps, printedChildren, config, indentation) => {
60 const tagColor = config.colors.tag;
61 return tagColor.open + '<' + type + (printedProps && tagColor.close + printedProps + config.spacingOuter + indentation + tagColor.open) + (printedChildren ? '>' + tagColor.close + printedChildren + config.spacingOuter + indentation + tagColor.open + '</' + type : (printedProps && !config.min ? '' : ' ') + '/') + '>' + tagColor.close;
62};
63const printElementAsLeaf = (type, config) => {
64 const tagColor = config.colors.tag;
65 return tagColor.open + '<' + type + tagColor.close + ' …' + tagColor.open + ' />' + tagColor.close;
66};
67const ELEMENT_NODE = 1;
68const TEXT_NODE = 3;
69const COMMENT_NODE = 8;
70const FRAGMENT_NODE = 11;
71const ELEMENT_REGEXP = /^((HTML|SVG)\w*)?Element$/;
72const testNode = val => {
73 const constructorName = val.constructor.name;
74 const {
75 nodeType,
76 tagName
77 } = val;
78 const isCustomElement = typeof tagName === 'string' && tagName.includes('-') || typeof val.hasAttribute === 'function' && val.hasAttribute('is');
79 return nodeType === ELEMENT_NODE && (ELEMENT_REGEXP.test(constructorName) || isCustomElement) || nodeType === TEXT_NODE && constructorName === 'Text' || nodeType === COMMENT_NODE && constructorName === 'Comment' || nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment';
80};
81const test = val => val?.constructor?.name && testNode(val);
82exports.test = test;
83function nodeIsText(node) {
84 return node.nodeType === TEXT_NODE;
85}
86function nodeIsComment(node) {
87 return node.nodeType === COMMENT_NODE;
88}
89function nodeIsFragment(node) {
90 return node.nodeType === FRAGMENT_NODE;
91}
92function createDOMElementFilter(filterNode) {
93 return {
94 test: val => val?.constructor?.name && testNode(val),
95 serialize: (node, config, indentation, depth, refs, printer) => {
96 if (nodeIsText(node)) {
97 return printText(node.data, config);
98 }
99 if (nodeIsComment(node)) {
100 return printComment(node.data, config);
101 }
102 const type = nodeIsFragment(node) ? `DocumentFragment` : node.tagName.toLowerCase();
103 if (++depth > config.maxDepth) {
104 return printElementAsLeaf(type, config);
105 }
106 return printElement(type, printProps(nodeIsFragment(node) ? [] : Array.from(node.attributes).map(attr => attr.name).sort(), nodeIsFragment(node) ? {} : Array.from(node.attributes).reduce((props, attribute) => {
107 props[attribute.name] = attribute.value;
108 return props;
109 }, {}), config, indentation + config.indent, depth, refs, printer), printChildren(Array.prototype.slice.call(node.childNodes || node.children).filter(filterNode), config, indentation + config.indent, depth, refs, printer), config, indentation);
110 }
111 };
112}
\No newline at end of file