[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

perf(pretty-format): combine DOMElement plugins (#9581)

authored by

Vladimir and committed by
GitHub
(Feb 4, 2026, 10:16 AM +0100) da85a326 92ab06d6

+64 -174
+1 -1
packages/pretty-format/src/index.ts
··· 30 30 import ReactElement from './plugins/ReactElement' 31 31 import ReactTestComponent from './plugins/ReactTestComponent' 32 32 33 - export { createDOMElementFilter } from './plugins/DOMElementFilter' 33 + export { createDOMElementFilter } from './plugins/DOMElement' 34 34 35 35 const toString = Object.prototype.toString 36 36 const toISOString = Date.prototype.toISOString
+63 -6
packages/pretty-format/src/plugins/DOMElement.ts
··· 65 65 return node.nodeType === FRAGMENT_NODE 66 66 } 67 67 68 - export const serialize: NewPlugin['serialize'] = ( 68 + export interface FilterConfig extends Config { 69 + filterNode?: (node: any) => boolean 70 + } 71 + 72 + function filterChildren(children: any[], filterNode?: (node: any) => boolean): any[] { 73 + // Filter out text nodes that only contain whitespace to prevent empty lines 74 + // This is done regardless of whether a filterNode is provided 75 + let filtered = children.filter((node) => { 76 + // Filter out text nodes that are only whitespace 77 + if (node.nodeType === TEXT_NODE) { 78 + const text = node.data || '' 79 + // Keep text nodes that have non-whitespace content 80 + return text.trim().length > 0 81 + } 82 + return true 83 + }) 84 + 85 + // Apply additional user-provided filter if specified 86 + if (filterNode) { 87 + filtered = filtered.filter(filterNode) 88 + } 89 + 90 + return filtered 91 + } 92 + 93 + function serializeDOM( 69 94 node: HandledType, 70 95 config: Config, 71 96 indentation: string, 72 97 depth: number, 73 98 refs: Refs, 74 99 printer: Printer, 75 - ) => { 100 + filterNode?: (node: any) => boolean, 101 + ) { 76 102 if (nodeIsText(node)) { 77 103 return printText(node.data, config) 78 104 } ··· 88 114 if (++depth > config.maxDepth) { 89 115 return printElementAsLeaf(type, config) 90 116 } 117 + 118 + const children = Array.prototype.slice.call(node.childNodes || node.children) 119 + const shadowChildren = (nodeIsFragment(node) || !node.shadowRoot) 120 + ? [] 121 + : Array.prototype.slice.call(node.shadowRoot.children) 122 + 123 + const resolvedChildren = filterNode ? filterChildren(children, filterNode) : children 124 + const resolvedShadowChildren = filterNode ? filterChildren(shadowChildren, filterNode) : shadowChildren 91 125 92 126 return printElement( 93 127 type, ··· 110 144 refs, 111 145 printer, 112 146 ), 113 - ((nodeIsFragment(node) || !node.shadowRoot) 114 - ? '' 115 - : printShadowRoot(Array.prototype.slice.call(node.shadowRoot.children), config, indentation + config.indent, depth, refs, printer)) 147 + (resolvedShadowChildren.length > 0 148 + ? printShadowRoot(resolvedShadowChildren, config, indentation + config.indent, depth, refs, printer) 149 + : '') 116 150 + printChildren( 117 - Array.prototype.slice.call(node.childNodes || node.children), 151 + resolvedChildren, 118 152 config, 119 153 indentation + config.indent, 120 154 depth, ··· 124 158 config, 125 159 indentation, 126 160 ) 161 + } 162 + 163 + export const serialize: NewPlugin['serialize'] = ( 164 + node: HandledType, 165 + config: Config, 166 + indentation: string, 167 + depth: number, 168 + refs: Refs, 169 + printer: Printer, 170 + ) => serializeDOM(node, config, indentation, depth, refs, printer) 171 + 172 + export function createDOMElementFilter(filterNode?: (node: any) => boolean): NewPlugin { 173 + return { 174 + test, 175 + serialize: ( 176 + node: HandledType, 177 + config: Config, 178 + indentation: string, 179 + depth: number, 180 + refs: Refs, 181 + printer: Printer, 182 + ) => serializeDOM(node, config, indentation, depth, refs, printer, filterNode), 183 + } 127 184 } 128 185 129 186 const plugin: NewPlugin = { serialize, test }
-167
packages/pretty-format/src/plugins/DOMElementFilter.ts
··· 1 - /** 2 - * Copyright (c) Meta Platforms, Inc. and affiliates. 3 - * 4 - * This source code is licensed under the MIT license found in the 5 - * LICENSE file in the root directory of this source tree. 6 - */ 7 - 8 - import type { Config, NewPlugin, Printer, Refs } from '../types' 9 - import { 10 - printChildren, 11 - printComment, 12 - printElement, 13 - printElementAsLeaf, 14 - printProps, 15 - printShadowRoot, 16 - printText, 17 - } from './lib/markup' 18 - 19 - const ELEMENT_NODE = 1 20 - const TEXT_NODE = 3 21 - const COMMENT_NODE = 8 22 - const FRAGMENT_NODE = 11 23 - 24 - const ELEMENT_REGEXP = /^(?:(?:HTML|SVG)\w*)?Element$/ 25 - 26 - function testHasAttribute(val: any) { 27 - try { 28 - return typeof val.hasAttribute === 'function' && val.hasAttribute('is') 29 - } 30 - catch { 31 - return false 32 - } 33 - } 34 - 35 - function testNode(val: any) { 36 - const constructorName = val.constructor.name 37 - const { nodeType, tagName } = val 38 - const isCustomElement 39 - = (typeof tagName === 'string' && tagName.includes('-')) 40 - || testHasAttribute(val) 41 - 42 - return ( 43 - (nodeType === ELEMENT_NODE 44 - && (ELEMENT_REGEXP.test(constructorName) || isCustomElement)) 45 - || (nodeType === TEXT_NODE && constructorName === 'Text') 46 - || (nodeType === COMMENT_NODE && constructorName === 'Comment') 47 - || (nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment') 48 - ) 49 - } 50 - 51 - export const test: NewPlugin['test'] = (val: any) => 52 - val?.constructor?.name && testNode(val) 53 - 54 - type HandledType = Element | Text | Comment | DocumentFragment 55 - 56 - function nodeIsText(node: HandledType): node is Text { 57 - return node.nodeType === TEXT_NODE 58 - } 59 - 60 - function nodeIsComment(node: HandledType): node is Comment { 61 - return node.nodeType === COMMENT_NODE 62 - } 63 - 64 - function nodeIsFragment(node: HandledType): node is DocumentFragment { 65 - return node.nodeType === FRAGMENT_NODE 66 - } 67 - 68 - export interface FilterConfig extends Config { 69 - filterNode?: (node: any) => boolean 70 - } 71 - 72 - function filterChildren(children: any[], filterNode?: (node: any) => boolean): any[] { 73 - // Filter out text nodes that only contain whitespace to prevent empty lines 74 - // This is done regardless of whether a filterNode is provided 75 - let filtered = children.filter((node) => { 76 - // Filter out text nodes that are only whitespace 77 - if (node.nodeType === TEXT_NODE) { 78 - const text = node.data || '' 79 - // Keep text nodes that have non-whitespace content 80 - return text.trim().length > 0 81 - } 82 - return true 83 - }) 84 - 85 - // Apply additional user-provided filter if specified 86 - if (filterNode) { 87 - filtered = filtered.filter(filterNode) 88 - } 89 - 90 - return filtered 91 - } 92 - 93 - export function createDOMElementFilter(filterNode?: (node: any) => boolean): NewPlugin { 94 - return { 95 - test, 96 - serialize: ( 97 - node: HandledType, 98 - config: Config, 99 - indentation: string, 100 - depth: number, 101 - refs: Refs, 102 - printer: Printer, 103 - ) => { 104 - if (nodeIsText(node)) { 105 - return printText(node.data, config) 106 - } 107 - 108 - if (nodeIsComment(node)) { 109 - return printComment(node.data, config) 110 - } 111 - 112 - const type = nodeIsFragment(node) 113 - ? 'DocumentFragment' 114 - : node.tagName.toLowerCase() 115 - 116 - if (++depth > config.maxDepth) { 117 - return printElementAsLeaf(type, config) 118 - } 119 - 120 - const children = Array.prototype.slice.call(node.childNodes || node.children) 121 - const filteredChildren = filterChildren(children, filterNode) 122 - 123 - const shadowChildren = (nodeIsFragment(node) || !node.shadowRoot) 124 - ? [] 125 - : Array.prototype.slice.call(node.shadowRoot.children) 126 - const filteredShadowChildren = filterChildren(shadowChildren, filterNode) 127 - 128 - return printElement( 129 - type, 130 - printProps( 131 - nodeIsFragment(node) 132 - ? [] 133 - : Array.from(node.attributes, attr => attr.name).sort(), 134 - nodeIsFragment(node) 135 - ? {} 136 - : [...node.attributes].reduce<Record<string, string>>( 137 - (props, attribute) => { 138 - props[attribute.name] = attribute.value 139 - return props 140 - }, 141 - {}, 142 - ), 143 - config, 144 - indentation + config.indent, 145 - depth, 146 - refs, 147 - printer, 148 - ), 149 - (filteredShadowChildren.length > 0 150 - ? printShadowRoot(filteredShadowChildren, config, indentation + config.indent, depth, refs, printer) 151 - : '') 152 - + printChildren( 153 - filteredChildren, 154 - config, 155 - indentation + config.indent, 156 - depth, 157 - refs, 158 - printer, 159 - ), 160 - config, 161 - indentation, 162 - ) 163 - }, 164 - } 165 - } 166 - 167 - export default createDOMElementFilter