Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pretty-format: Support HTMLCollection and NodeList in DOMCollection plugin #7125

Merged
merged 4 commits into from Oct 11, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@
- `[jest-worker]` [**BREAKING**] Add functionality to call a `setup` method in the worker before the first call and a `teardown` method when ending the farm ([#7014](https://github.com/facebook/jest/pull/7014)).
- `[jest-config]` [**BREAKING**] Set default `notifyMode` to `failure-change` ([#7024](https://github.com/facebook/jest/pull/7024))
- `[jest-snapshot]` Enable configurable snapshot paths ([#6143](https://github.com/facebook/jest/pull/6143))
- `[pretty-format]` Support HTMLCollection and NodeList in DOMCollection plugin ([#7125](https://github.com/facebook/jest/pull/7125))

### Fixes

Expand Down
93 changes: 89 additions & 4 deletions packages/pretty-format/src/__tests__/dom_collection.test.js
Expand Up @@ -12,26 +12,111 @@
'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}');
});
});

describe('DOMCollection plugin for list items', () => {
const select = document.createElement('select');
select.innerHTML = [
'<option value="1">one</option>',
'<option value="2">two</option>',
'<option value="3">three</option>',
].join('');

const form = document.createElement('form');
form.appendChild(select);

const expectedOption1 = [
' <option',
' value="1"',
' >',
' one',
' </option>,', // comma because item
].join('\n');
const expectedOption2 = [
' <option',
' value="2"',
' >',
' two',
' </option>,', // comma because item
].join('\n');
const expectedOption3 = [
' <option',
' value="3"',
' >',
' three',
' </option>,', // 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);
});

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 options', () => {
expect(select.options).toPrettyPrintTo(expectedHTMLOptionsCollection);
});

// Omitted a test for form.elements because constructor.name
// is HTMLCollection in jsdom version 11
// might become HTMLFormControlsCollectio in jsdom 12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdom 12 is released, and while we won't upgrade for some time, maybe we can normalize the name to HTMLFormControlsCollection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified outside Jest that form.elements still HTMLCollection class in jsdom@12.2.0 so added the test. Wrote a comment so that we will know to update the criterion if it ever changes.

});
66 changes: 38 additions & 28 deletions packages/pretty-format/src/plugins/dom_collection.js
Expand Up @@ -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 = (
Expand All @@ -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.prototype.slice.call(collection),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I will leave the compiling to Babel :)

config,
indentation,
depth,
refs,
printer,
) +
']')
);
};

Expand Down