-
Notifications
You must be signed in to change notification settings - Fork 47.5k
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 SyntheticEvent tests using public APIs only #11525
Rewrite SyntheticEvent tests using public APIs only #11525
Conversation
* rewritten createEvent to use public APIs * removed all references SyntheticEvent.release In order to test under realistic circumstances I had to move the expectations into a callback in mosts tests to overcome the effects of event pooling.
createEvent = function(nativeEvent) { | ||
var target = require('../getEventTarget').default(nativeEvent); | ||
return SyntheticEvent.getPooled({}, '', nativeEvent, target); | ||
createEvent = (nativeEvent, callback) => { |
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.
Can we get rid of this helper and just inline calls in each test? I don't mind some repetition.
return SyntheticEvent.getPooled({}, '', nativeEvent, target); | ||
createEvent = (nativeEvent, callback) => { | ||
var instance; | ||
var container = document.createElement('div'); |
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 would like to use the same beforeEach/afterEach structure as in #11365. Set up a container and add it to the document, then remove it.
<div ref={ref => (instance = ref)} onClick={callback} />, | ||
container, | ||
); | ||
ReactTestUtils.SimulateNative.click(instance, nativeEvent); |
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.
Unfortunately Simulate* APIs are not very useful for testing because they are sort of like private APIs too. If you look at #11365 you'll see we're trying to avoid them and dispatch native browser events on DOM nodes instead. This PR should too.
}; | ||
}); | ||
|
||
it('should normalize `target` from the nativeEvent', () => { | ||
// TODO: This is not testable with the public APIs, write a test for getEventTarget |
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.
Why is it not testable? Pretty sure #11365 tests this case.
Thanks for the comments. It's all understood and I'm working on an update. |
}); | ||
instance.dispatchEvent(event); | ||
|
||
// TODO: figure out why this fails |
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'm currently investigating this.
expect(syntheticEvent.isPropagationStopped()).toBe(false); | ||
syntheticEvent.stopPropagation(); | ||
expect(syntheticEvent.isPropagationStopped()).toBe(true); | ||
// TODO: Figure out why this is undefined when switching to public API |
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'm currently investigating this.
expect(syntheticEvent.isPersistent()).toBe(false); | ||
syntheticEvent.persist(); | ||
expect(syntheticEvent.isPersistent()).toBe(true); | ||
// TODO: Figure out why this is undefined when switching to public API |
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'm currently investigating this.
The removed `expect` calls verified the correct behaviour based on missing `preventDefault` and `stopPropagation` methods. The was correct as we used plain objects to simulate events. Since we switched to the public API we're using native events which do have these methods.
This was missed when the test was first migrated. When emulating IE8 not only has returnValue to be false. In addition defaultPrevented must not be defined.
All TODOs resolved. Ready to review again 🙂 |
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.
Thanks! Got more comments.
|
||
expectedCount++; | ||
}; | ||
instance = ReactDOM.render(<div onClick={eventHandler} />, 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.
Let's rename this to node. It's not a React instance.
|
||
expect(syntheticEvent.defaultPrevented).toBe(true); | ||
expectedCount++; | ||
}; |
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.
Same.
instance = ReactDOM.render(<div onClick={eventHandler} />, container); | ||
|
||
var event; | ||
event = document.createEvent('Event'); |
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.
Style nit: let's include assignment into definition?
|
||
event = document.createEvent('Event'); | ||
event.initEvent('click', true, true); | ||
// Emulate IE8 |
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.
What does this test have to do with IE8?
The test title says "should be prevented if nativeEvent is prevented". To test this, remove these properties and just call e.nativeEvent.preventDefault()
. Then check e.isDefaultPrevented()
.
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.
The previous version of this test had two expectations. On where defaultPrevented
was true
and one where returnValue
was false
. I checked why and it turned out IE uses returnValue
instead of defaultPrevented
. Happy to use e.nativeEvent.preventDefault()
but it will have less parity with the previous version of the test.
var event; | ||
event = document.createEvent('Event'); | ||
event.initEvent('click', true, true); | ||
Object.defineProperty(event, 'srcElement', { |
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.
Again, this is specific to IE8 and there is just a single test needed for this. There's no need to add srcElement
testing to every test.
var event; | ||
event = document.createEvent('Event'); | ||
event.initEvent('click', true, true); | ||
Object.defineProperty(event, 'srcElement', { |
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.
Same.
|
||
it('should be able to `persist`', () => { |
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.
Is this test missing?
Totally agree with your comments. Just should have focused this tiny bit more ;-) |
var event; | ||
event = document.createEvent('Event'); | ||
event.initEvent('click', true, true); | ||
Object.defineProperty(event, 'defaultPrevented', { |
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.
Can this be event.preventDefault()
call instead? Since jsdom should implement it by providing .defaultPrevented
.
event = document.createEvent('Event'); | ||
event.initEvent('click', true, true); | ||
node.dispatchEvent(event); | ||
|
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 would be nice to test that we can still access syntheticEvent
fields here. Since that's the thing that persist
allows.
Let's save syntheticEvent
in a variable and asset its target
and type
here.
var expectedCount = 0; | ||
|
||
var eventHandler = syntheticEvent => { | ||
syntheticEvent.destructor(); |
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 a private API. Instead of triggering it explicitly we should save the synthetic event to a variable. Then we should assert at the end of the test (after dispatching events) that accessing them does not work.
var expectedCount = 0; | ||
|
||
var eventHandler = syntheticEvent => { | ||
syntheticEvent.destructor(); |
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.
Same
|
||
var eventHandler = syntheticEvent => { | ||
syntheticEvent.destructor(); | ||
expect((syntheticEvent.type = 'MouseEvent')).toBe('MouseEvent'); |
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.
What is this asserting? I don't understand this line.
event = document.createEvent('Event'); | ||
event.initEvent('click', true, true); | ||
// Emulate IE8 | ||
Object.defineProperty(event, 'defaultPrevented', { |
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 makes sense to me now, thanks for explaining.
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 just realized that we have dropped support for IE8 and IE9 doesn't use srcElement
anymore so we could simplify this like you have suggested initially. Your call.
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 want to keep the test for srcElement
. You're right we don't support IE8 anymore but the code is there so the test should be there too. We can later remove them together but that is unrelated to the task at hand (converting the test to use public API).
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.
In other words I'd like to leave this part tested.
I think this is good. Thank you! |
No worries an thank you for holding out during my first PR here :-) |
* generate synthetics events using public API * rewritten createEvent to use public APIs * removed all references SyntheticEvent.release In order to test under realistic circumstances I had to move the expectations into a callback in mosts tests to overcome the effects of event pooling. * run prettier * remove empty line * don't use ReactTestUtils * run prettier and fix linter issues * remove duplicate test * remove invalid calls to expect The removed `expect` calls verified the correct behaviour based on missing `preventDefault` and `stopPropagation` methods. The was correct as we used plain objects to simulate events. Since we switched to the public API we're using native events which do have these methods. * set event.defaultPrevented to undefined This was missed when the test was first migrated. When emulating IE8 not only has returnValue to be false. In addition defaultPrevented must not be defined. * run all tests and format code * rename instance variable to node * remove backtick * only simulate IE in normalisation test * include assignment in definition * add missing `persist` test * use method instead of field to prevent default * expect properties to be unchanged on persisted event * optimise tests that deal with event persitence * declare and assign `event` on the same line if not reassigned later
Related to #11299
createEvent
to use public APIsSyntheticEvent.release
In order to test under realistic circumstances I had to move the expectations into a callback in mosts tests to overcome the effects of event pooling.