diff --git a/CHANGELOG.md b/CHANGELOG.md index 731eb7ca0a25..0da4c29b0788 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - `[jest-config]` Add `readConfigs` function, previously in `jest-cli` ([#7096](https://github.com/facebook/jest/pull/7096)) - `[jest-snapshot]` Enable configurable snapshot paths ([#6143](https://github.com/facebook/jest/pull/6143)) - `[jest-haste-map]` [**BREAKING**] Remove support for `@providesModule` ([#6104](https://github.com/facebook/jest/pull/6104)) +- `[pretty-format]` Support HTMLCollection and NodeList in DOMCollection plugin ([#7125](https://github.com/facebook/jest/pull/7125)) ### Fixes diff --git a/packages/pretty-format/src/__tests__/dom_collection.test.js b/packages/pretty-format/src/__tests__/dom_collection.test.js index 38128a3ff8c6..cf0d498f8b18 100644 --- a/packages/pretty-format/src/__tests__/dom_collection.test.js +++ b/packages/pretty-format/src/__tests__/dom_collection.test.js @@ -12,26 +12,150 @@ 'use strict'; const prettyFormat = require('../'); -const {DOMCollection} = prettyFormat.plugins; +const {DOMCollection, DOMElement} = prettyFormat.plugins; const toPrettyPrintTo = require('./expect_util').getPrettyPrint([ DOMCollection, + DOMElement, ]); const expect: any = global.expect; expect.extend({toPrettyPrintTo}); -describe('DOMCollection Plugin', () => { - it('supports a DOMStringMap', () => { +describe('DOMCollection plugin for object properties', () => { + it('supports DOMStringMap', () => { const el = document.createElement('div'); el.dataset.foo = 'bar'; expect(el.dataset).toPrettyPrintTo('DOMStringMap {\n "foo": "bar",\n}'); }); - it('supports a NamedNodeMap', () => { + it('supports NamedNodeMap', () => { const el = document.createElement('div'); el.setAttribute('foo', 'bar'); expect(el.attributes).toPrettyPrintTo('NamedNodeMap {\n "foo": "bar",\n}'); }); + + it('supports config.min option', () => { + const el = document.createElement('div'); + el.setAttribute('name1', 'value1'); + el.setAttribute('name2', 'value2'); + + expect(el.attributes).toPrettyPrintTo( + '{"name1": "value1", "name2": "value2"}', + {min: true}, + ); + }); +}); + +describe('DOMCollection plugin for list items', () => { + const select = document.createElement('select'); + select.innerHTML = [ + '', + '', + '', + ].join(''); + + const form = document.createElement('form'); + form.appendChild(select); + + const expectedOption1 = [ + ' ', + ' one', + ' ,', // comma because item + ].join('\n'); + const expectedOption2 = [ + ' ', + ' two', + ' ,', // comma because item + ].join('\n'); + const expectedOption3 = [ + ' ', + ' three', + ' ,', // comma because item + ].join('\n'); + + const expectedHTMLCollection = [ + 'HTMLCollection [', + expectedOption1, + expectedOption2, + expectedOption3, + ']', + ].join('\n'); + + it('supports HTMLCollection for getElementsByTagName', () => { + const options = form.getElementsByTagName('option'); + expect(options).toPrettyPrintTo(expectedHTMLCollection); + }); + + it('supports HTMLCollection for children', () => { + expect(select.children).toPrettyPrintTo(expectedHTMLCollection); + }); + + it('supports config.maxDepth option', () => { + expect(select.children).toPrettyPrintTo('[HTMLCollection]', {maxDepth: 0}); + }); + + const expectedNodeList = [ + 'NodeList [', + expectedOption1, + expectedOption2, + expectedOption3, + ']', + ].join('\n'); + + it('supports NodeList for querySelectorAll', () => { + const options = form.querySelectorAll('option'); + expect(options).toPrettyPrintTo(expectedNodeList); + }); + + it('supports NodeList for childNodes', () => { + expect(select.childNodes).toPrettyPrintTo(expectedNodeList); + }); + + const expectedHTMLOptionsCollection = [ + 'HTMLOptionsCollection [', + expectedOption1, + expectedOption2, + expectedOption3, + ']', + ].join('\n'); + + it('supports HTMLOptionsCollection for select options', () => { + expect(select.options).toPrettyPrintTo(expectedHTMLOptionsCollection); + }); + + // When Jest upgrades to a version of jsdom later than 12.2.0, + // the class name might become HTMLFormControlsCollection + const expectedHTMLFormControlsCollection = [ + 'HTMLCollection [', + ' ,', // comma because item + ']', + ].join('\n'); + + it('supports HTMLCollection for form elements', () => { + expect(form.elements).toPrettyPrintTo(expectedHTMLFormControlsCollection); + }); }); diff --git a/packages/pretty-format/src/plugins/dom_collection.js b/packages/pretty-format/src/plugins/dom_collection.js index 5af357b05af5..716e83829617 100644 --- a/packages/pretty-format/src/plugins/dom_collection.js +++ b/packages/pretty-format/src/plugins/dom_collection.js @@ -9,29 +9,26 @@ import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat'; -import {printObjectProperties} from '../collections'; +import {printListItems, printObjectProperties} from '../collections'; const SPACE = ' '; -const COLLECTION_NAMES = ['DOMStringMap', 'NamedNodeMap']; +const OBJECT_NAMES = ['DOMStringMap', 'NamedNodeMap']; +const ARRAY_REGEXP = /^(HTML\w*Collection|NodeList)$/; + +const testName = (name: any) => + OBJECT_NAMES.indexOf(name) !== -1 || ARRAY_REGEXP.test(name); export const test = (val: any) => val && val.constructor && - COLLECTION_NAMES.indexOf(val.constructor.name) !== -1; - -const convertCollectionToObject = (collection: any) => { - let result = {}; - - if (collection.constructor.name === 'NamedNodeMap') { - for (let i = 0; i < collection.length; i++) { - result[collection[i].name] = collection[i].value; - } - } else { - result = Object.assign({}, collection); - } + val.constructor.name && + testName(val.constructor.name); - return result; +// Convert array of attribute objects to props object. +const propsReducer = (props, attribute) => { + props[attribute.name] = attribute.value; + return props; }; export const serialize = ( @@ -42,23 +39,36 @@ export const serialize = ( refs: Refs, printer: Printer, ): string => { + const name = collection.constructor.name; if (++depth > config.maxDepth) { - return '[' + collection.constructor.name + ']'; + return '[' + name + ']'; } return ( - collection.constructor.name + - SPACE + - '{' + - printObjectProperties( - convertCollectionToObject(collection), - config, - indentation, - depth, - refs, - printer, - ) + - '}' + (config.min ? '' : name + SPACE) + + (OBJECT_NAMES.indexOf(name) !== -1 + ? '{' + + printObjectProperties( + name === 'NamedNodeMap' + ? Array.prototype.reduce.call(collection, propsReducer, {}) + : Object.assign({}, collection), + config, + indentation, + depth, + refs, + printer, + ) + + '}' + : '[' + + printListItems( + Array.from(collection), + config, + indentation, + depth, + refs, + printer, + ) + + ']') ); };