Skip to content

feat(pretty-format): Enable filtering of children in DOMElement #11130

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

Closed

Conversation

eps1lon
Copy link
Contributor

@eps1lon eps1lon commented Feb 28, 2021

Summary

Allow ignoring "noise" in the DOM in pretty-format

Given

<body>
  <script
    src="context.js"
  />
  <!-- Some comment -->
  <p>
    Hello, Dave
  </p>
</body>

I want to be able to pretty-format only the "relevant" parts:

<body>
  <p>
    Hello, Dave
  </p>
</body>

"relevant" means the parts that the tested code controls. Some testing frameworks (e.g. mocha or karma) insert script tags and comments which take up considerable space.

Test plan

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

changelog entry? 🙂

This makes sense to me, but possibly something that should be mentioned in the readme? Dropping empty strings might be unexpected, dunno?

@SimenB SimenB requested a review from jeysal February 28, 2021 21:55
@eps1lon
Copy link
Contributor Author

eps1lon commented Mar 1, 2021

changelog entry? slightly_smiling_face

Will do.

Dropping empty strings might be unexpected, dunno?

This would only change existing behavior if people had custom plugins that handle an Element or CommentNode.

eps1lon added a commit to eps1lon/dom-testing-library that referenced this pull request Mar 1, 2021
@SimenB
Copy link
Member

SimenB commented Mar 1, 2021

Dropping empty strings might be unexpected, dunno?

This would only change existing behavior if people had custom plugins that handle an Element or CommentNode.

right! that seems somewhat unlikely

child !== null &&
(child as Node).nodeType !== NodeTypeTextNode
) {
// A plugin serialized this Node to '' meaning we should ignore it.
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I'm not so excited about taking away the ability of plugins to output empty text for a node, esp. in a breaking way. Would it be an option to allow returning null instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I'm not so excited about taking away the ability of plugins to output empty text for a node, esp. in a breaking way.

My question would be: Why is that desired? Why write a plugin that takes a Node, serializes it to an empty string but also expect that empty string to be included in the snapshot.

Would it be an option to allow returning null instead?

I've tried this route but it propagated the possibly null return all the way up so that other packages started to break.
I'll check again if we can change the boundaries but so far it seemed there are too many places that are accidentally coupled.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not saying I have a concrete use case, but I know how many usages are out there pretty-printing element trees that are abstractions over HTML (if you need a horrifying example, take a look at 'ExtReact'). If in just one of them one renders plain text not with string children, but with createElem('Label', {text: ''}), and their pretty printer converts the custom component language to the actual HTML, it will now no longer print the right representation of the element tree. With something used in as many wildly different tech stacks as Jest is, it is bound to break someone.
I would follow principle of least surprise — if an actual '' text child is printed as an empty (except indented) line, you would expect a child that is mapped to '' by the printer to behave the same. I don't know the empty string to typically signalize omission in other contexts either.

Regarding an explicit omission return value, we would want that limited specifically to printChildren, because while printing children you can omit one, not in all other contexts.
I would expect that changing the type of printChildrens printer argument to something like the following would work?

type NullablePrinter = (...args: Parameters<Printer>) => ReturnType<Printer> | null

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, I've just looked at the way you implemented your plugin, and this would not really help you because you are not in control of the printChildren call, instead you rely on the standard DOMElement or other plugin to make that call, passing the standard printer along which will then delegate to your plugin. :/

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we have both been thinking about this the wrong way.

Printing

<div>
  ${print('asdf')}
  ${print('')}
</div>

and expecting the print('') to be able to remove the whole third line is like printing

Object {
  a: ${print(42)}
  b: ${print(null)}
}

and expecting the print(null) to be able to remove the whole third line for a toEqualIgnoringNulls() matcher.

print('') at top level should have the same result as print('') in an element child, and print('') at top level does not return a zero-line string either (because a zero-line string is not a thing, it would have -1 line breaks in it).

I think what should really be done to omit children from the output is not pass them into a printer in the first place. Replace the DOMElement plugin with a plugin under your control, that clones the node, throws out any children you don't want, then pass it to the DOMElement plugin to reuse its code. Let me know if this works for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, I've just looked at the way you implemented your plugin, and this would not really help you because you are not in control of the

Did you see the test added?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, the sentence you quoted referred to the previous comment, not to the implementation in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand why you can say the current implementation doesn't help me when the test that uses the new implementation is exactly my desired outcome.

Copy link
Contributor

Choose a reason for hiding this comment

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

Again, I am not saying what you implemented here does not work for you, I am aware it does. The second of the three comments says that what I suggested in the second half of the first comment actually would not work for you after I reconsidered it.
For why this change will lead to surprising behavior, see the first half of my first comment.
For an alternative solution that I believe would work without introducing this surprising behavior, see my third comment.


it('filters children', () => {
const div = document.createElement('div');

Copy link
Contributor

Choose a reason for hiding this comment

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

If possible I think it would be easier to compare this to the expected if this was being constructed via

div.innerHTML = `...`

@eps1lon
Copy link
Contributor Author

eps1lon commented Apr 25, 2021

We're just going to fork the pretty-format dom plugin. Cloning DOM trees is not viable and it seems like you intend to keep printing empty strings on a new line.

@eps1lon eps1lon closed this Apr 25, 2021
@eps1lon eps1lon deleted the feat/pretty-form/FilterDOMElement branch April 25, 2021 10:01
@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 26, 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.

4 participants