-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Rewrite setInnerHTML tests to use Public API. #11385
Conversation
setInnerHTML(node, html); | ||
expect(node.innerHTML).toBe(html); | ||
const container = document.createElement('div'); | ||
const component = ReactDOM.render( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep this called node
? It's not a component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure-habit taking over from how I do this on the day-to-day. My bad!
|
||
setInnerHTML(nodeProxy, secondHtml); | ||
let component = ReactDOM.render( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a component, it's a node. I know old tests are written this way but they're wrong. Let's use correct naming in new tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, can do!
node, | ||
); | ||
ReactDOM.unmountComponentAtNode( | ||
ReactDOM.findDOMNode(component).parentNode, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to findDOMNode
because it is already a node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
@gaearon Updated. Realized the last set of tests didn't need those variable assignments, since it's checking for container calls that we're spying on. |
|
||
describe('setInnerHTML', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also rename test file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On it!
@gaearon |
Did you run |
@gaearon I believe I did, but when I get home I'll take a look and try again. Sorry for the delays. |
@gaearon Had to blow out the yarn.lock after the master merge-some things weren't updating properly. Noticed that another failure was due to prettier not picking up my file using the |
yarn.lock
Outdated
@@ -10,12 +10,12 @@ JSONStream@^1.0.3: | |||
through ">=2.2.7 <3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this change. It shouldn't be in this PR.
Conflicts: packages/react-dom/src/client/__tests__/setInnerHTML-test.js yarn.lock
@gaearon Updated. |
// Create a mock node that looks like an SVG in IE (without innerHTML) | ||
node = document.createElementNS(Namespaces.svg, 'svg'); | ||
|
||
nodeProxy = new Proxy(node, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing this causes the test to no longer verify the SVG IE code path. You can test it yourself by commenting out the related parts in setInnerHTML
: the test passes despite them being broken.
<g dangerouslySetInnerHTML={{__html: firstHtml}} />, | ||
container, | ||
); | ||
ReactDOM.unmountComponentAtNode(container); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is odd. Why do we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to defeat the purpose of the test (which is to verify setInnerHTML
itself cleans up old nodes).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is mistakingly testing that unmounting removes a child, and mounting into the same container later adds it.
Instead of testing that inside an already mounted container, operations setting dangerous HTML on SVG node, if it doesn't have innerHTML
(as in IE11), will use appendChild
/ removeChild
.
I rewrote the SVG test because it didn't test the right thing. The other ones were good. |
@gaearon I appreciate that-thank you! I should have asked you about that test, as it was a little confusing to rewrite, before taking that approach. Can you just give me a little info on how I should have been approaching that one? Thanks! |
For future readers, here is the function we're testing: react/packages/react-dom/src/client/setInnerHTML.js Lines 22 to 40 in 7432013
Here are the tests before: react/packages/react-dom/src/client/__tests__/setInnerHTML-test.js Lines 29 to 66 in 21d0c11
We need to rewrite the in terms of public API. First of all, let's look at the branch it's testing: react/packages/react-dom/src/client/setInnerHTML.js Lines 26 to 36 in 7432013
To verify that your test didn't check the right thing, I just commented out the code in that branch. The test still passed. Therefore, it wasn't being tested. I'm not sure I understand how you came to that test implementation so I can't dive into that. But I can share how I approached it. The existing test describes the problem here: react/packages/react-dom/src/client/setInnerHTML.js Lines 23 to 25 in 7432013
So our job is to:
The original tests mocked react/packages/react-dom/src/client/__tests__/setInnerHTML-test.js Lines 47 to 49 in 51c101f
But we can't pass an arbitrary element to it anymore. However, it's not even important that So how do we force React to not "see"
so seems like the only way to work around it is to delete
However, this breaks this line of code in our SVG
That's because IE is only missing support for It seemed natural that I could just put the react/packages/react-dom/src/client/__tests__/dangerouslySetInnerHTML-test.js Lines 37 to 46 in 12165eb
This way only divs would have The above is enough to crudely simulate what IE is doing. It's not exactly right but it's enough to test the behavior we need to test for, and it's unlikely we'd need to get more precise here. (Considering jsdom isn't exactly the same environment anyway, e.g. our version of jsdom doesn't even recognize SVG tags properly.) Finally, how do we test that the content gets replaced? We just assert that we see the content we expect to see, before and after the change: react/packages/react-dom/src/client/__tests__/dangerouslySetInnerHTML-test.js Lines 84 to 85 in 12165eb
react/packages/react-dom/src/client/__tests__/dangerouslySetInnerHTML-test.js Lines 90 to 91 in 12165eb
This is enough to verify we somehow set the content, which is all we needed. Finally, it is important to undo any changes to globals (even if the test fails). Since otherwise it makes the test non-deterministic, and can cause other tests to fail: react/packages/react-dom/src/client/__tests__/dangerouslySetInnerHTML-test.js Lines 49 to 56 in 12165eb
I hope this helps! |
Very much so. Thanks! |
@gaearon In regards to why I took my approach, what I ended up doing was trying to make each test pass without changing too much from the original implementation. From what I looked up and what I was using, I didn't think I had to change too much, and as I was coming up with what I was refactoring, I had situations where I would get failing tests. I never thought to comment out what I already had in my setup, therefore I never noticed that I had a false positive in this instance. |
Yeah, that makes sense. These are not often trivial changes though :-) When rewriting unit tests into acceptance/integration tests, sometimes you have to rethink the whole approach and in that case it's easier to think about what situation it was written to test rather than what the original test was trying to do. |
@gaearon Totally fair. Thanks for the thoughtful feedback! My main takeaway here though is that I definitely felt like, at the time, the test read weird to me and I was confused about whether or not I was properly testing a situation involving SVG nodes, and I could have asked a question to get a little help with it. |
I agree the existing one was pretty weird 😛 I think I remember some context on it from the past when I've merged some bugfixes to that code. IMO the most helpful thing to understand the test is to git blame it and find the PR that introduced it. That usually explains the motivation and sometimes provides a more realistic failing example (which unfortunately didn't become a unit test at the time). |
Good advice. I'll be sure to take a look at previous examples/PRs in the future when I make future contributions :) |
* Rewrite setInnerHTML tests to use Public API. * Rename variables and drop unnecessary variable assignments. * Rename testfile to dangerouslySetInnerHTML-test.js * Properly prettify test file. * Rewrite SVG tests to verify we recover from missing innerHTML
Ref #11299
This rewrites the
setInnerHTML
tests to utilizedangerouslySetInnerHTML
to ensure that nodes with and without theinnerHTML
attribute are properly set!