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

Rewrite SyntheticEvent tests using public APIs only #11525

Merged
merged 18 commits into from
Nov 20, 2017

Conversation

timjacobi
Copy link
Member

@timjacobi timjacobi commented Nov 11, 2017

Related to #11299

  • 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.

* 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) => {
Copy link
Collaborator

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');
Copy link
Collaborator

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);
Copy link
Collaborator

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
Copy link
Collaborator

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.

@timjacobi
Copy link
Member Author

Thanks for the comments. It's all understood and I'm working on an update.

});
instance.dispatchEvent(event);

// TODO: figure out why this fails
Copy link
Member Author

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
Copy link
Member Author

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
Copy link
Member Author

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.
@timjacobi
Copy link
Member Author

All TODOs resolved. Ready to review again 🙂

Copy link
Collaborator

@gaearon gaearon left a 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);
Copy link
Collaborator

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++;
};
Copy link
Collaborator

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');
Copy link
Collaborator

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
Copy link
Collaborator

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().

Copy link
Member Author

@timjacobi timjacobi Nov 20, 2017

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', {
Copy link
Collaborator

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', {
Copy link
Collaborator

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`', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this test missing?

@timjacobi
Copy link
Member Author

Totally agree with your comments. Just should have focused this tiny bit more ;-)
I added one question regarding the approach for should be prevented if nativeEvent is prevented otherwise this is up to date with your requests.

var event;
event = document.createEvent('Event');
event.initEvent('click', true, true);
Object.defineProperty(event, 'defaultPrevented', {
Copy link
Collaborator

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);

Copy link
Collaborator

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();
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 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();
Copy link
Collaborator

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');
Copy link
Collaborator

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', {
Copy link
Collaborator

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.

Copy link
Member Author

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.

Copy link
Collaborator

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).

Copy link
Collaborator

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.

@gaearon gaearon merged commit 669a70d into facebook:master Nov 20, 2017
@gaearon
Copy link
Collaborator

gaearon commented Nov 20, 2017

I think this is good. Thank you!

@timjacobi timjacobi deleted the synthetic-event-public-api branch November 20, 2017 15:06
@timjacobi
Copy link
Member Author

No worries an thank you for holding out during my first PR here :-)

@gaearon gaearon mentioned this pull request Nov 20, 2017
26 tasks
Ethan-Arrowood pushed a commit to Ethan-Arrowood/react that referenced this pull request Dec 8, 2017
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants