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 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
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
132 changes: 128 additions & 4 deletions packages/pretty-format/src/__tests__/dom_collection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
'<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);
});

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

it('supports HTMLCollection for form elements', () => {
expect(form.elements).toPrettyPrintTo(expectedHTMLFormControlsCollection);
});
});
66 changes: 38 additions & 28 deletions packages/pretty-format/src/plugins/dom_collection.js
Original file line number Diff line number Diff line change
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.from(collection),
config,
indentation,
depth,
refs,
printer,
) +
']')
);
};

Expand Down