diff --git a/CHANGELOG.md b/CHANGELOG.md index e54f28c38b77..b7cbb4b0ca45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[pretty-format]` Added support for serializing custom elements (web components) ([#10217](https://github.com/facebook/jest/pull/10237)) + ### Fixes - `[expect]` Match symbols and bigints in `any()` ([#10223](https://github.com/facebook/jest/pull/10223)) diff --git a/packages/pretty-format/src/__tests__/DOMElement.test.ts b/packages/pretty-format/src/__tests__/DOMElement.test.ts index b5e22f549c40..6fb25f764095 100644 --- a/packages/pretty-format/src/__tests__/DOMElement.test.ts +++ b/packages/pretty-format/src/__tests__/DOMElement.test.ts @@ -344,6 +344,37 @@ Testing.`; ); }); + it('supports custom elements', () => { + class CustomElement extends HTMLElement {} + class CustomParagraphElement extends HTMLParagraphElement {} + class CustomExtendedElement extends CustomElement {} + + customElements.define('custom-element', CustomElement); + customElements.define('custom-extended-element', CustomExtendedElement); + customElements.define('custom-paragraph', CustomParagraphElement, { + extends: 'p', + }); + + const parent = document.createElement('div'); + parent.innerHTML = [ + '', + '', + '

', + ].join(''); + + expect(parent).toPrettyPrintTo( + [ + '
', + ' ', + ' ', + ' ', + '
', + ].join('\n'), + ); + }); + describe('matches constructor name of SVG elements', () => { // Too bad, so sad, element.constructor.name of SVG elements // is HTMLUnknownElement in jsdom v9 and v10 diff --git a/packages/pretty-format/src/plugins/DOMElement.ts b/packages/pretty-format/src/plugins/DOMElement.ts index daf8f9be721f..75c47737af21 100644 --- a/packages/pretty-format/src/plugins/DOMElement.ts +++ b/packages/pretty-format/src/plugins/DOMElement.ts @@ -23,17 +23,22 @@ const FRAGMENT_NODE = 11; const ELEMENT_REGEXP = /^((HTML|SVG)\w*)?Element$/; -const testNode = (nodeType: number, name: string) => - (nodeType === ELEMENT_NODE && ELEMENT_REGEXP.test(name)) || - (nodeType === TEXT_NODE && name === 'Text') || - (nodeType === COMMENT_NODE && name === 'Comment') || - (nodeType === FRAGMENT_NODE && name === 'DocumentFragment'); +const testNode = (val: any) => { + const constructorName = val.constructor.name; + const {nodeType, tagName = ''} = val; + const isCustomElement = tagName.includes('-') || val.hasAttribute?.('is'); + + return ( + (nodeType === ELEMENT_NODE && + (ELEMENT_REGEXP.test(constructorName) || isCustomElement)) || + (nodeType === TEXT_NODE && constructorName === 'Text') || + (nodeType === COMMENT_NODE && constructorName === 'Comment') || + (nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment') + ); +}; export const test: NewPlugin['test'] = (val: any) => - val && - val.constructor && - val.constructor.name && - testNode(val.nodeType, val.constructor.name); + val?.constructor?.name && testNode(val); type HandledType = Element | Text | Comment | DocumentFragment;