Rewrite SyntheticEvent tests using public APIs only (#11525)

* 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
This commit is contained in:
Tim Jacobi authored and Dan Abramov committed 2017-11-20 14:29:27 +00:00
1 parent bd9dbd5d60
commit 669a70dab7
1 file changed
+199 -53
+199 -53
View File
@@ -9,79 +9,170 @@
'use strict';
var SyntheticEvent;
var React;
var ReactDOM;
var ReactTestUtils;
describe('SyntheticEvent', () => {
var createEvent;
var container;
beforeEach(() => {
// TODO: can we express this test with only public API?
SyntheticEvent = require('events/SyntheticEvent').default;
React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
createEvent = function(nativeEvent) {
var target = require('../getEventTarget').default(nativeEvent);
return SyntheticEvent.getPooled({}, '', nativeEvent, target);
};
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('should normalize `target` from the nativeEvent', () => {
var target = document.createElement('div');
var syntheticEvent = createEvent({srcElement: target});
var node;
var expectedCount = 0;
expect(syntheticEvent.target).toBe(target);
expect(syntheticEvent.type).toBe(undefined);
var eventHandler = syntheticEvent => {
expect(syntheticEvent.target).toBe(node);
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
// Emulate IE8
Object.defineProperty(event, 'target', {
get() {},
});
Object.defineProperty(event, 'srcElement', {
get() {
return node;
},
});
node.dispatchEvent(event);
expect(expectedCount).toBe(1);
});
it('should be able to `preventDefault`', () => {
var nativeEvent = {};
var syntheticEvent = createEvent(nativeEvent);
var node;
var expectedCount = 0;
expect(syntheticEvent.isDefaultPrevented()).toBe(false);
syntheticEvent.preventDefault();
expect(syntheticEvent.isDefaultPrevented()).toBe(true);
var eventHandler = syntheticEvent => {
expect(syntheticEvent.isDefaultPrevented()).toBe(false);
syntheticEvent.preventDefault();
expect(syntheticEvent.isDefaultPrevented()).toBe(true);
expect(syntheticEvent.defaultPrevented).toBe(true);
expect(syntheticEvent.defaultPrevented).toBe(true);
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
expect(nativeEvent.returnValue).toBe(false);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
expect(expectedCount).toBe(1);
});
it('should be prevented if nativeEvent is prevented', () => {
expect(createEvent({defaultPrevented: true}).isDefaultPrevented()).toBe(
true,
);
expect(createEvent({returnValue: false}).isDefaultPrevented()).toBe(true);
var node;
var expectedCount = 0;
var eventHandler = syntheticEvent => {
expect(syntheticEvent.isDefaultPrevented()).toBe(true);
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event;
event = document.createEvent('Event');
event.initEvent('click', true, true);
event.preventDefault();
node.dispatchEvent(event);
event = document.createEvent('Event');
event.initEvent('click', true, true);
// Emulate IE8
Object.defineProperty(event, 'defaultPrevented', {
get() {},
});
Object.defineProperty(event, 'returnValue', {
get() {
return false;
},
});
node.dispatchEvent(event);
expect(expectedCount).toBe(2);
});
it('should be able to `stopPropagation`', () => {
var nativeEvent = {};
var syntheticEvent = createEvent(nativeEvent);
var node;
var expectedCount = 0;
expect(syntheticEvent.isPropagationStopped()).toBe(false);
syntheticEvent.stopPropagation();
expect(syntheticEvent.isPropagationStopped()).toBe(true);
var eventHandler = syntheticEvent => {
expect(syntheticEvent.isPropagationStopped()).toBe(false);
syntheticEvent.stopPropagation();
expect(syntheticEvent.isPropagationStopped()).toBe(true);
expect(nativeEvent.cancelBubble).toBe(true);
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
expect(expectedCount).toBe(1);
});
it('should be able to `persist`', () => {
var syntheticEvent = createEvent({});
var node;
var expectedCount = 0;
var syntheticEvent;
expect(syntheticEvent.isPersistent()).toBe(false);
syntheticEvent.persist();
expect(syntheticEvent.isPersistent()).toBe(true);
var eventHandler = e => {
expect(e.isPersistent()).toBe(false);
e.persist();
syntheticEvent = e;
expect(e.isPersistent()).toBe(true);
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
expect(syntheticEvent.type).toBe('click');
expect(syntheticEvent.bubbles).toBe(true);
expect(syntheticEvent.cancelable).toBe(true);
expect(expectedCount).toBe(1);
});
it('should be nullified if the synthetic event has called destructor and log warnings', () => {
it('should be nullified and log warnings if the synthetic event has not been persisted', () => {
spyOn(console, 'error');
var target = document.createElement('div');
var syntheticEvent = createEvent({srcElement: target});
syntheticEvent.destructor();
var node;
var expectedCount = 0;
var syntheticEvent;
var eventHandler = e => {
syntheticEvent = e;
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
expect(syntheticEvent.type).toBe(null);
expect(syntheticEvent.nativeEvent).toBe(null);
expect(syntheticEvent.target).toBe(null);
@@ -95,14 +186,27 @@ describe('SyntheticEvent', () => {
'keep the original synthetic event around, use event.persist(). ' +
'See https://fb.me/react-event-pooling for more information.',
);
expect(expectedCount).toBe(1);
});
it('should warn when setting properties of a destructored synthetic event', () => {
it('should warn when setting properties of a synthetic event that has not been persisted', () => {
spyOn(console, 'error');
var target = document.createElement('div');
var syntheticEvent = createEvent({srcElement: target});
syntheticEvent.destructor();
expect((syntheticEvent.type = 'MouseEvent')).toBe('MouseEvent');
var node;
var expectedCount = 0;
var syntheticEvent;
var eventHandler = e => {
syntheticEvent = e;
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
syntheticEvent.type = 'MouseEvent';
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toBe(
'Warning: This synthetic event is reused for performance reasons. If ' +
@@ -111,12 +215,25 @@ describe('SyntheticEvent', () => {
'keep the original synthetic event around, use event.persist(). ' +
'See https://fb.me/react-event-pooling for more information.',
);
expect(expectedCount).toBe(1);
});
it('should warn if the synthetic event has been released when calling `preventDefault`', () => {
it('should warn when calling `preventDefault` if the synthetic event has not been persisted', () => {
spyOn(console, 'error');
var syntheticEvent = createEvent({});
SyntheticEvent.release(syntheticEvent);
var node;
var expectedCount = 0;
var syntheticEvent;
var eventHandler = e => {
syntheticEvent = e;
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
syntheticEvent.preventDefault();
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toBe(
@@ -126,12 +243,26 @@ describe('SyntheticEvent', () => {
'keep the original synthetic event around, use event.persist(). ' +
'See https://fb.me/react-event-pooling for more information.',
);
expect(expectedCount).toBe(1);
});
it('should warn if the synthetic event has been released when calling `stopPropagation`', () => {
it('should warn when calling `stopPropagation` if the synthetic event has not been persisted', () => {
spyOn(console, 'error');
var syntheticEvent = createEvent({});
SyntheticEvent.release(syntheticEvent);
var node;
var expectedCount = 0;
var syntheticEvent;
var eventHandler = e => {
syntheticEvent = e;
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
syntheticEvent.stopPropagation();
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toBe(
@@ -141,6 +272,7 @@ describe('SyntheticEvent', () => {
'keep the original synthetic event around, use event.persist(). ' +
'See https://fb.me/react-event-pooling for more information.',
);
expect(expectedCount).toBe(1);
});
// TODO: reenable this test. We are currently silencing these warnings when
@@ -153,8 +285,8 @@ describe('SyntheticEvent', () => {
function assignEvent(e) {
event = e;
}
var instance = ReactDOM.render(<div onClick={assignEvent} />, element);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance));
var node = ReactDOM.render(<div onClick={assignEvent} />, element);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(node));
expectDev(console.error.calls.count()).toBe(0);
// access a property to cause the warning
@@ -172,9 +304,22 @@ describe('SyntheticEvent', () => {
it('should warn if Proxy is supported and the synthetic event is added a property', () => {
spyOn(console, 'error');
var syntheticEvent = createEvent({});
syntheticEvent.foo = 'bar';
SyntheticEvent.release(syntheticEvent);
var node;
var expectedCount = 0;
var syntheticEvent;
var eventHandler = e => {
e.foo = 'bar';
syntheticEvent = e;
expectedCount++;
};
node = ReactDOM.render(<div onClick={eventHandler} />, container);
var event = document.createEvent('Event');
event.initEvent('click', true, true);
node.dispatchEvent(event);
expect(syntheticEvent.foo).toBe('bar');
if (typeof Proxy === 'function') {
expectDev(console.error.calls.count()).toBe(1);
@@ -187,5 +332,6 @@ describe('SyntheticEvent', () => {
} else {
expectDev(console.error.calls.count()).toBe(0);
}
expect(expectedCount).toBe(1);
});
});