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

Adds HtmlElement pretty-format plugin. #3230

Merged
merged 13 commits into from
Apr 17, 2017
Merged

Conversation

mute
Copy link
Contributor

@mute mute commented Mar 30, 2017

Summary

This addresses the issue noted in #2146, with jsdom HTMLElement's producing an absurdly long string when pretty formatted. It adds an HTMLElement plugin which can be used to appropriately pretty-format these elements.

Test plan

I tested this using a variety of unit tests, similar to the existing tests that were covering ReactElement.

The cases I covered were ensuring that the class / title properties would pretty-format correctly, along with attributes on single elements and nested elements.

Here is the output from running those tests:

Copy link
Collaborator

@thymikee thymikee left a comment

Choose a reason for hiding this comment

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

Nice work! Left some comments inlined. Btw, that's great usage of @jest-environment pragma 👍

module.exports = {
getPrettyPrint: plugins =>
(received, expected, opts) => {
const prettyPrintImmutable = prettyFormat(
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could use some generic naming here, e.g. prettyPrintPlugin

opts,
),
);
const pass = prettyPrintImmutable === expected;
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here


const escapeHTML = require('./lib/escapeHTML');
const HTML_ELEMENT_REGEXP = /(HTML\w*?Element)/;
const test = (maybeSet: any) => isHTMLElement;
Copy link
Collaborator

Choose a reason for hiding this comment

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

change naming, it's not Set

val.constructor &&
val.constructor.name &&
HTML_ELEMENT_REGEXP.test(val.constructor.name));
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is potentially a hot place, please keep perf in mind: https://twitter.com/bmeurer/status/846951275480711168

describe('HTMLElement Plugin', () => {
it('supports a single HTML element', () => {
expect(document.createElement('div')).toPrettyPrintTo(
'<HTMLDivElement />',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why double space?

const child = document.createElement('span');
parent.appendChild(child);
expect(parent).toPrettyPrintTo(
'<HTMLDivElement >\n <HTMLSpanElement />\n</HTMLDivElement >',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove this extra space also here: <HTMLDivElement >

child.setAttribute('class', 'classy');

expect(parent).toPrettyPrintTo(
'<HTMLDivElement >\n <HTMLSpanElement \n id=123\n class=classy\n />\n</HTMLDivElement >',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Attributes should be strings id="123" class="classy".

Reworks short circuiting logic for `isHTMLElement` function.
Copy link
Collaborator

@thymikee thymikee left a comment

Choose a reason for hiding this comment

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

Awesome!

const test = isHTMLElement;

function isHTMLElement(value: any) {
const toStringed = value.toString();
Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit worried about performance here. This will call toString on any value passed to pretty-format. toString may be expensive if it is overwritten on a class instance. Can we do Object.prototype.toString.call?

Copy link
Contributor Author

@mute mute Mar 31, 2017

Choose a reason for hiding this comment

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

That's a really good call -- I'll check how that behaves on an actual JSDOM HTMLElement to verify.

Looking at it again, I think it's possible that we could just remove this toString completely and just rely on the constructor validation, but I was deferring to the existing implementation from the previous PR.

Does that seem reasonable?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yup, I think we could make it even more performant. According to ElementImpl we could check for property nodeType. Although this prop alone could possibly result in some false positives (nodeType feels not so unique...), it's good for an early check. Only when we know there's such prop, we could perform constructor and regex check:

const test = (value: any) =>
  value !== undefined &&
  value.nodeType === 1 && 
  value.constructor !== undefined &&
  HTML_ELEMENT_REGEXP.test(value.constructor.name);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Awesome -- I've updated the PR with those changes.

I'm not sure what to do about the tests that are failing; do I need to have mercurial configured locally for those?

Copy link
Collaborator

Choose a reason for hiding this comment

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

const toPrettyPrintTo = require('./expect-util').getPrettyPrint([
ReactElementPlugin,
ReactTestComponentPlugin,
...ImmutablePlugins,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Node 4 fails because of this destructuring. You can use concat instead

@codecov-io
Copy link

codecov-io commented Apr 11, 2017

Codecov Report

Merging #3230 into master will increase coverage by 0.16%.
The diff coverage is 97.14%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3230      +/-   ##
==========================================
+ Coverage   64.24%   64.41%   +0.16%     
==========================================
  Files         175      176       +1     
  Lines        6436     6469      +33     
  Branches        4        4              
==========================================
+ Hits         4135     4167      +32     
- Misses       2300     2301       +1     
  Partials        1        1
Impacted Files Coverage Δ
packages/jest-diff/src/index.js 80% <100%> (+0.4%) ⬆️
packages/jest-matcher-utils/src/index.js 98.66% <100%> (+0.01%) ⬆️
packages/jest-snapshot/src/plugins.js 100% <100%> (ø) ⬆️
packages/pretty-format/src/plugins/HTMLElement.js 96.66% <96.66%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b93f9c6...e86a25d. Read the comment docs.

@cpojer
Copy link
Member

cpojer commented Apr 11, 2017

  • What was the decision to use "HTMLDivElement" instead of "div"? Can we instead simply use node.tagName?
  • Do you mind adding this plugin everywhere in Jest where prettyFormat is called?

@mute
Copy link
Contributor Author

mute commented Apr 13, 2017

Sure thing!

@@ -60,7 +61,7 @@ const print = (
) => {
let result = colors.tag.open + '<';
const elementName = element.constructor
Copy link
Collaborator

Choose a reason for hiding this comment

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

const elementName = element.tagName

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tagName defaults to all uppercase, which felt odd to me -- should I leave it that way or is the .toLowerCase() call okay here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

lower case is better, leave it as is :)

@cpojer cpojer merged commit adf7b2c into jestjs:master Apr 17, 2017
@cpojer
Copy link
Member

cpojer commented Apr 17, 2017

Thanks for doing this!

@mute mute deleted the 2146-pretty-jsdom branch April 18, 2017 00:01
skovhus pushed a commit to skovhus/jest that referenced this pull request Apr 29, 2017
* Adds HtmlElement pretty-format plugin.

* Adds missing copyright and 'use strict'

* Fixes variable naming / formatting.
Reworks short circuiting logic for `isHTMLElement` function.

* Improves performance for isHtmlElement.

* Updating snapshots

* Revert "Updating snapshots"

This reverts commit 55f797f.

* Fixes node 4 syntax

* - Switches to tagName rather than constructor.

* Adds HTMLElement Plugin usage.

* Removes `HTMLElement` fallback in favor of tagName
tushardhole pushed a commit to tushardhole/jest that referenced this pull request Aug 21, 2017
* Adds HtmlElement pretty-format plugin.

* Adds missing copyright and 'use strict'

* Fixes variable naming / formatting.
Reworks short circuiting logic for `isHTMLElement` function.

* Improves performance for isHtmlElement.

* Updating snapshots

* Revert "Updating snapshots"

This reverts commit 55f797f.

* Fixes node 4 syntax

* - Switches to tagName rather than constructor.

* Adds HTMLElement Plugin usage.

* Removes `HTMLElement` fallback in favor of tagName
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants