@samitouri / QOS-React-2 / commits / 56d1b0fb59

[react-events] DOM event testing library (#16433)

This patch formalizes the mock native events and event sequences used in unit tests. The `createEventTarget` function returns an object that can be used to dispatch native event sequences on the target without having to manually do so across all the scenarios we need to account for. Unit tests can be written as if we were only working with PointerEvent, but they will dispatch realistic native event sequences based on the execution environment (e.g., is PointerEvent supported?) and pointer type. ``` describe.each(environments)('Suite', (hasPointerEvents) => { beforeEach(() => { // setup }); test.each(pointerTypes)('Test', (pointerType) => { const target = createEventTarget(node); target.pointerdown({pointerType}); expect(callback).toBeCalled(); }); }); ``` Every native event that is dispatched now includes a complete object by default. The properties of the events can be customized. Properties that shouldn't be relied on in responder implementations are excluded from the mock native events to ensure tests will fail. Equivalent properties are normalized across different event types, e.g., 'pointerId' is converted to 'identifier' before a TouchEvent is dispatched.

Nicolas Gallagher committed Aug 19, 2019 at 09:21 UTC 56d1b0fb5991e954c21609dce80d9849eda115fe
14 files changed +1243 -1224
packages/react-events/src/dom/Press.js
+2
@@ -520,6 +520,7 @@ const pressResponderImpl = {
520 const isPressed = state.isPressed;
521
522 handleStopPropagation(props, context, nativeEvent);
523 +
524 switch (type) {
525 // START
526 case 'pointerdown':
@@ -632,6 +633,7 @@ const pressResponderImpl = {
633 const previousPointerType = state.pointerType;
634
635 handleStopPropagation(props, context, nativeEvent);
636 +
637 switch (type) {
638 // MOVE
639 case 'pointermove':
packages/react-events/src/dom/Scroll.js
+18 -8
@@ -62,14 +62,16 @@ type ScrollEvent = {|
62 y: null | number,
63 |};
64
65 -const targetEventTypes = [
66 - 'scroll',
67 - 'pointerdown',
68 - 'touchstart',
69 - 'keyup',
70 - 'wheel',
71 -];
72 -const rootEventTypes = ['touchcancel', 'touchend'];
65 +const hasPointerEvents =
66 + typeof window !== 'undefined' && window.PointerEvent !== undefined;
67 +
68 +const targetEventTypes = hasPointerEvents
69 + ? ['scroll', 'pointerdown', 'keyup', 'wheel']
70 + : ['scroll', 'mousedown', 'touchstart', 'keyup', 'wheel'];
71 +
72 +const rootEventTypes = hasPointerEvents
73 + ? ['pointercancel', 'pointerup']
74 + : ['touchcancel', 'touchend'];
75
76 function isFunction(obj): boolean {
77 return typeof obj === 'function';
@@ -237,17 +239,23 @@ const scrollResponderImpl = {
239 state.pointerType = pointerType;
240 break;
241 }
242 + case 'mousedown':
243 case 'wheel': {
244 state.pointerType = 'mouse';
245 break;
246 }
247 case 'pointerdown': {
248 state.pointerType = pointerType;
249 + if (pointerType === 'touch' && !state.isTouching) {
250 + state.isTouching = true;
251 + context.addRootEventTypes(rootEventTypes);
252 + }
253 break;
254 }
255 case 'touchstart': {
256 if (!state.isTouching) {
257 state.isTouching = true;
258 + state.pointerType = 'touch';
259 context.addRootEventTypes(rootEventTypes);
260 }
261 }
@@ -262,6 +270,8 @@ const scrollResponderImpl = {
270 const {type} = event;
271
272 switch (type) {
273 + case 'pointercancel':
274 + case 'pointerup':
275 case 'touchcancel':
276 case 'touchend': {
277 if (state.isTouching) {
packages/react-events/src/dom/__tests__/ContextMenu-test.internal.js
+13 -13
@@ -9,13 +9,7 @@
9
10 'use strict';
11
12 -import {
13 - dispatchLongPressContextMenu,
14 - dispatchRightClickContextMenu,
15 - dispatchModifiedClickContextMenu,
16 - platform,
17 - setPointerEvent,
18 -} from '../test-utils';
12 +import {createEventTarget, platform, setPointerEvent} from '../testing-library';
13
14 let React;
15 let ReactFeatureFlags;
@@ -62,7 +56,8 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
56 };
57 ReactDOM.render(<Component />, container);
58
65 - dispatchRightClickContextMenu(ref.current, {preventDefault});
59 + const target = createEventTarget(ref.current);
60 + target.contextmenu({preventDefault});
61 expect(preventDefault).toHaveBeenCalledTimes(1);
62 expect(onContextMenu).toHaveBeenCalledTimes(1);
63 expect(onContextMenu).toHaveBeenCalledWith(
@@ -80,7 +75,8 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
75 };
76 ReactDOM.render(<Component />, container);
77
83 - dispatchLongPressContextMenu(ref.current, {preventDefault});
78 + const target = createEventTarget(ref.current);
79 + target.contextmenu({preventDefault}, {pointerType: 'touch'});
80 expect(preventDefault).toHaveBeenCalledTimes(1);
81 expect(onContextMenu).toHaveBeenCalledTimes(1);
82 expect(onContextMenu).toHaveBeenCalledWith(
@@ -100,7 +96,8 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
96 };
97 ReactDOM.render(<Component />, container);
98
103 - dispatchRightClickContextMenu(ref.current);
99 + const target = createEventTarget(ref.current);
100 + target.contextmenu();
101 expect(onContextMenu).toHaveBeenCalledTimes(0);
102 });
103
@@ -117,7 +114,8 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
114 };
115 ReactDOM.render(<Component />, container);
116
120 - dispatchRightClickContextMenu(ref.current, {preventDefault});
117 + const target = createEventTarget(ref.current);
118 + target.contextmenu({preventDefault});
119 expect(preventDefault).toHaveBeenCalledTimes(0);
120 expect(onContextMenu).toHaveBeenCalledTimes(1);
121 });
@@ -142,7 +140,8 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
140 };
141 ReactDOM.render(<Component />, container);
142
145 - dispatchModifiedClickContextMenu(ref.current);
143 + const target = createEventTarget(ref.current);
144 + target.contextmenu({}, {modified: true});
145 expect(onContextMenu).toHaveBeenCalledTimes(1);
146 expect(onContextMenu).toHaveBeenCalledWith(
147 expect.objectContaining({pointerType: 'mouse', type: 'contextmenu'}),
@@ -169,7 +168,8 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
168 };
169 ReactDOM.render(<Component />, container);
170
172 - dispatchModifiedClickContextMenu(ref.current);
171 + const target = createEventTarget(ref.current);
172 + target.contextmenu({}, {modified: true});
173 expect(onContextMenu).toHaveBeenCalledTimes(0);
174 });
175 });
packages/react-events/src/dom/__tests__/Focus-test.internal.js
+64 -61
@@ -9,15 +9,7 @@
9
10 'use strict';
11
12 -import {
13 - blur,
14 - focus,
15 - keydown,
16 - setPointerEvent,
17 - platform,
18 - dispatchPointerDown,
19 - dispatchPointerUp,
20 -} from '../test-utils';
12 +import {createEventTarget, setPointerEvent, platform} from '../testing-library';
13
14 let React;
15 let ReactFeatureFlags;
@@ -73,9 +65,9 @@ describe.each(table)('Focus responder', hasPointerEvents => {
65 });
66
67 it('does not call callbacks', () => {
76 - const dispatch = arg => ref.current.dispatchEvent(arg);
77 - dispatch(focus());
78 - dispatch(blur());
68 + const target = createEventTarget(ref.current);
69 + target.focus();
70 + target.blur();
71 expect(onFocus).not.toBeCalled();
72 expect(onBlur).not.toBeCalled();
73 });
@@ -97,9 +89,9 @@ describe.each(table)('Focus responder', hasPointerEvents => {
89 });
90
91 it('is called after "blur" event', () => {
100 - const dispatch = arg => ref.current.dispatchEvent(arg);
101 - dispatch(focus());
102 - dispatch(blur());
92 + const target = createEventTarget(ref.current);
93 + target.focus();
94 + target.blur();
95 expect(onBlur).toHaveBeenCalledTimes(1);
96 });
97 });
@@ -127,19 +119,21 @@ describe.each(table)('Focus responder', hasPointerEvents => {
119 beforeEach(componentInit);
120
121 it('is called after "focus" event', () => {
130 - ref.current.dispatchEvent(focus());
122 + const target = createEventTarget(ref.current);
123 + target.focus();
124 expect(onFocus).toHaveBeenCalledTimes(1);
125 });
126
127 it('is not called if descendants of target receive focus', () => {
135 - innerRef.current.dispatchEvent(focus());
128 + const target = createEventTarget(innerRef.current);
129 + target.focus();
130 expect(onFocus).not.toBeCalled();
131 });
132
133 it('is called with the correct pointerType: mouse', () => {
140 - const target = ref.current;
141 - dispatchPointerDown(target, {pointerType: 'mouse'});
142 - dispatchPointerUp(target, {pointerType: 'mouse'});
134 + const target = createEventTarget(ref.current);
135 + target.pointerdown();
136 + target.pointerup();
137 expect(onFocus).toHaveBeenCalledTimes(1);
138 expect(onFocus).toHaveBeenCalledWith(
139 expect.objectContaining({pointerType: 'mouse'}),
@@ -147,9 +141,10 @@ describe.each(table)('Focus responder', hasPointerEvents => {
141 });
142
143 it('is called with the correct pointerType: touch', () => {
150 - const target = ref.current;
151 - dispatchPointerDown(target, {pointerType: 'touch'});
152 - dispatchPointerUp(target, {pointerType: 'touch'});
144 + const target = createEventTarget(ref.current);
145 + const pointerType = 'touch';
146 + target.pointerdown({pointerType});
147 + target.pointerup({pointerType});
148 expect(onFocus).toHaveBeenCalledTimes(1);
149 expect(onFocus).toHaveBeenCalledWith(
150 expect.objectContaining({pointerType: 'touch'}),
@@ -158,9 +153,10 @@ describe.each(table)('Focus responder', hasPointerEvents => {
153
154 if (hasPointerEvents) {
155 it('is called with the correct pointerType: pen', () => {
161 - const target = ref.current;
162 - dispatchPointerDown(target, {pointerType: 'pen'});
163 - dispatchPointerUp(target, {pointerType: 'pen'});
156 + const target = createEventTarget(ref.current);
157 + const pointerType = 'pen';
158 + target.pointerdown({pointerType});
159 + target.pointerup({pointerType});
160 expect(onFocus).toHaveBeenCalledTimes(1);
161 expect(onFocus).toHaveBeenCalledWith(
162 expect.objectContaining({pointerType: 'pen'}),
@@ -169,10 +165,9 @@ describe.each(table)('Focus responder', hasPointerEvents => {
165 }
166
167 it('is called with the correct pointerType using a keyboard', () => {
172 - const target = ref.current;
173 - // Keyboard tab
174 - target.dispatchEvent(keydown({key: 'Tab'}));
175 - target.dispatchEvent(focus());
168 + const target = createEventTarget(ref.current);
169 + target.keydown({key: 'Tab'});
170 + target.focus();
171 expect(onFocus).toHaveBeenCalledTimes(1);
172 expect(onFocus).toHaveBeenCalledWith(
173 expect.objectContaining({pointerType: 'keyboard'}),
@@ -184,10 +179,11 @@ describe.each(table)('Focus responder', hasPointerEvents => {
179 jest.resetModules();
180 initializeModules();
181 componentInit();
187 - const target = ref.current;
182
189 - target.dispatchEvent(keydown({key: 'Tab', altKey: true}));
190 - target.dispatchEvent(focus());
183 + const target = createEventTarget(ref.current);
184 + target.keydown({key: 'Tab', altKey: true});
185 + target.focus();
186 +
187 expect(onFocus).toHaveBeenCalledTimes(1);
188 expect(onFocus).toHaveBeenCalledWith(
189 expect.objectContaining({
@@ -220,20 +216,20 @@ describe.each(table)('Focus responder', hasPointerEvents => {
216 });
217
218 it('is called after "blur" and "focus" events', () => {
223 - const target = ref.current;
224 - target.dispatchEvent(focus());
219 + const target = createEventTarget(ref.current);
220 + target.focus();
221 expect(onFocusChange).toHaveBeenCalledTimes(1);
222 expect(onFocusChange).toHaveBeenCalledWith(true);
227 - target.dispatchEvent(blur());
223 + target.blur();
224 expect(onFocusChange).toHaveBeenCalledTimes(2);
225 expect(onFocusChange).toHaveBeenCalledWith(false);
226 });
227
228 it('is not called after "blur" and "focus" events on descendants', () => {
233 - const target = innerRef.current;
234 - target.dispatchEvent(focus());
229 + const target = createEventTarget(innerRef.current);
230 + target.focus();
231 expect(onFocusChange).toHaveBeenCalledTimes(0);
236 - target.dispatchEvent(blur());
232 + target.blur();
233 expect(onFocusChange).toHaveBeenCalledTimes(0);
234 });
235 });
@@ -259,48 +255,52 @@ describe.each(table)('Focus responder', hasPointerEvents => {
255 });
256
257 it('is called after "focus" and "blur" if keyboard navigation is active', () => {
262 - const target = ref.current;
258 + const target = createEventTarget(ref.current);
259 + const containerTarget = createEventTarget(container);
260 // use keyboard first
264 - container.dispatchEvent(keydown({key: 'Tab'}));
265 - target.dispatchEvent(focus());
261 + containerTarget.keydown({key: 'Tab'});
262 + target.focus();
263 expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
264 expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
268 - target.dispatchEvent(blur({relatedTarget: container}));
265 + target.blur({relatedTarget: container});
266 expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
267 expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
268 });
269
270 it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
274 - const target = ref.current;
271 + const target = createEventTarget(ref.current);
272 + const containerTarget = createEventTarget(container);
273 // use keyboard first
276 - container.dispatchEvent(keydown({key: 'Tab'}));
277 - target.dispatchEvent(focus());
274 + containerTarget.keydown({key: 'Tab'});
275 + target.focus();
276 expect(onFocusVisibleChange).toHaveBeenCalledTimes(1);
277 expect(onFocusVisibleChange).toHaveBeenCalledWith(true);
278 // then use pointer on the target, focus should no longer be visible
281 - dispatchPointerDown(target);
279 + target.pointerdown();
280 expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
281 expect(onFocusVisibleChange).toHaveBeenCalledWith(false);
282 // onFocusVisibleChange should not be called again
285 - target.dispatchEvent(blur({relatedTarget: container}));
283 + target.blur({relatedTarget: container});
284 expect(onFocusVisibleChange).toHaveBeenCalledTimes(2);
285 });
286
287 it('is not called after "focus" and "blur" events without keyboard', () => {
290 - const target = ref.current;
291 - dispatchPointerDown(target);
292 - dispatchPointerUp(target);
293 - dispatchPointerDown(container);
294 - target.dispatchEvent(blur({relatedTarget: container}));
288 + const target = createEventTarget(ref.current);
289 + const containerTarget = createEventTarget(container);
290 + target.pointerdown();
291 + target.pointerup();
292 + containerTarget.pointerdown();
293 + target.blur({relatedTarget: container});
294 expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
295 });
296
297 it('is not called after "blur" and "focus" events on descendants', () => {
299 - const target = innerRef.current;
300 - container.dispatchEvent(keydown({key: 'Tab'}));
301 - target.dispatchEvent(focus());
298 + const innerTarget = createEventTarget(innerRef.current);
299 + const containerTarget = createEventTarget(container);
300 + containerTarget.keydown({key: 'Tab'});
301 + innerTarget.focus();
302 expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
303 - target.dispatchEvent(blur({relatedTarget: container}));
303 + innerTarget.blur({relatedTarget: container});
304 expect(onFocusVisibleChange).toHaveBeenCalledTimes(0);
305 });
306 });
@@ -338,10 +338,13 @@ describe.each(table)('Focus responder', hasPointerEvents => {
338
339 ReactDOM.render(<Outer />, container);
340
341 - outerRef.current.dispatchEvent(focus());
342 - outerRef.current.dispatchEvent(blur());
343 - innerRef.current.dispatchEvent(focus());
344 - innerRef.current.dispatchEvent(blur());
341 + const innerTarget = createEventTarget(innerRef.current);
342 + const outerTarget = createEventTarget(outerRef.current);
343 +
344 + outerTarget.focus();
345 + outerTarget.blur();
346 + innerTarget.focus();
347 + innerTarget.blur();
348 expect(events).toEqual([
349 'outer: onFocus',
350 'outer: onFocusChange',
packages/react-events/src/dom/__tests__/FocusWithin-test.internal.js
+70 -64
@@ -9,14 +9,7 @@
9
10 'use strict';
11
12 -import {
13 - blur,
14 - focus,
15 - keydown,
16 - setPointerEvent,
17 - dispatchPointerDown,
18 - dispatchPointerUp,
19 -} from '../test-utils';
12 +import {createEventTarget, setPointerEvent} from '../testing-library';
13
14 let React;
15 let ReactFeatureFlags;
@@ -73,9 +66,9 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
66 });
67
68 it('prevents custom events being dispatched', () => {
76 - const target = ref.current;
77 - target.dispatchEvent(focus());
78 - target.dispatchEvent(blur());
69 + const target = createEventTarget(ref.current);
70 + target.focus();
71 + target.blur();
72 expect(onFocusWithinChange).not.toBeCalled();
73 expect(onFocusWithinVisibleChange).not.toBeCalled();
74 });
@@ -104,42 +97,46 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
97 });
98
99 it('is called after "blur" and "focus" events on focus target', () => {
107 - const target = ref.current;
108 - target.dispatchEvent(focus());
100 + const target = createEventTarget(ref.current);
101 + target.focus();
102 expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
103 expect(onFocusWithinChange).toHaveBeenCalledWith(true);
111 - target.dispatchEvent(blur({relatedTarget: container}));
104 + target.blur({relatedTarget: container});
105 expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
106 expect(onFocusWithinChange).toHaveBeenCalledWith(false);
107 });
108
109 it('is called after "blur" and "focus" events on descendants', () => {
117 - const target = innerRef.current;
118 - target.dispatchEvent(focus());
110 + const target = createEventTarget(innerRef.current);
111 + target.focus();
112 expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
113 expect(onFocusWithinChange).toHaveBeenCalledWith(true);
121 - target.dispatchEvent(blur({relatedTarget: container}));
114 + target.blur({relatedTarget: container});
115 expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
116 expect(onFocusWithinChange).toHaveBeenCalledWith(false);
117 });
118
119 it('is only called once when focus moves within and outside the subtree', () => {
127 - const target = ref.current;
128 - const innerTarget1 = innerRef.current;
129 - const innerTarget2 = innerRef2.current;
120 + const node = ref.current;
121 + const innerNode1 = innerRef.current;
122 + const innerNode2 = innerRef.current;
123 + const target = createEventTarget(node);
124 + const innerTarget1 = createEventTarget(innerNode1);
125 + const innerTarget2 = createEventTarget(innerNode2);
126 +
127 // focus shifts into subtree
131 - innerTarget1.dispatchEvent(focus());
128 + innerTarget1.focus();
129 expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
130 expect(onFocusWithinChange).toHaveBeenCalledWith(true);
131 // focus moves around subtree
135 - innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
136 - innerTarget2.dispatchEvent(focus());
137 - innerTarget2.dispatchEvent(blur({relatedTarget: target}));
138 - target.dispatchEvent(focus());
139 - target.dispatchEvent(blur({relatedTarget: innerTarget1}));
132 + innerTarget1.blur({relatedTarget: innerNode2});
133 + innerTarget2.focus();
134 + innerTarget2.blur({relatedTarget: node});
135 + target.focus();
136 + target.blur({relatedTarget: innerNode1});
137 expect(onFocusWithinChange).toHaveBeenCalledTimes(1);
138 // focus shifts outside subtree
142 - innerTarget1.dispatchEvent(blur({relatedTarget: container}));
139 + innerTarget1.blur({relatedTarget: container});
140 expect(onFocusWithinChange).toHaveBeenCalledTimes(2);
141 expect(onFocusWithinChange).toHaveBeenCalledWith(false);
142 });
@@ -168,87 +165,96 @@ describe.each(table)('FocusWithin responder', hasPointerEvents => {
165 });
166
167 it('is called after "focus" and "blur" on focus target if keyboard was used', () => {
171 - const target = ref.current;
168 + const target = createEventTarget(ref.current);
169 + const containerTarget = createEventTarget(container);
170 // use keyboard first
173 - container.dispatchEvent(keydown({key: 'Tab'}));
174 - target.dispatchEvent(focus());
171 + containerTarget.keydown({key: 'Tab'});
172 + target.focus();
173 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
174 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
177 - target.dispatchEvent(blur({relatedTarget: container}));
175 + target.blur({relatedTarget: container});
176 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
177 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
178 });
179
180 it('is called after "focus" and "blur" on descendants if keyboard was used', () => {
183 - const innerTarget = innerRef.current;
181 + const innerTarget = createEventTarget(innerRef.current);
182 + const containerTarget = createEventTarget(container);
183 // use keyboard first
185 - container.dispatchEvent(keydown({key: 'Tab'}));
186 - innerTarget.dispatchEvent(focus());
184 + containerTarget.keydown({key: 'Tab'});
185 + innerTarget.focus();
186 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
187 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
189 - innerTarget.dispatchEvent(blur({relatedTarget: container}));
188 + innerTarget.blur({relatedTarget: container});
189 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
190 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
191 });
192
193 it('is called if non-keyboard event is dispatched on target previously focused with keyboard', () => {
195 - const target = ref.current;
196 - const innerTarget1 = innerRef.current;
197 - const innerTarget2 = innerRef2.current;
194 + const node = ref.current;
195 + const innerNode1 = innerRef.current;
196 + const innerNode2 = innerRef2.current;
197 +
198 + const target = createEventTarget(node);
199 + const innerTarget1 = createEventTarget(innerNode1);
200 + const innerTarget2 = createEventTarget(innerNode2);
201 // use keyboard first
199 - target.dispatchEvent(focus());
200 - target.dispatchEvent(keydown({key: 'Tab'}));
201 - target.dispatchEvent(blur({relatedTarget: innerTarget1}));
202 - innerTarget1.dispatchEvent(focus());
202 + target.focus();
203 + target.keydown({key: 'Tab'});
204 + target.blur({relatedTarget: innerNode1});
205 + innerTarget1.focus();
206 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
207 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
208 // then use pointer on the next target, focus should no longer be visible
206 - dispatchPointerDown(innerTarget2);
207 - innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
208 - innerTarget2.dispatchEvent(focus());
209 + innerTarget2.pointerdown();
210 + innerTarget1.blur({relatedTarget: innerNode2});
211 + innerTarget2.focus();
212 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
213 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
214 // then use keyboard again
212 - innerTarget2.dispatchEvent(keydown({key: 'Tab', shiftKey: true}));
213 - innerTarget2.dispatchEvent(blur({relatedTarget: innerTarget1}));
214 - innerTarget1.dispatchEvent(focus());
215 + innerTarget2.keydown({key: 'Tab', shiftKey: true});
216 + innerTarget2.blur({relatedTarget: innerNode1});
217 + innerTarget1.focus();
218 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(3);
219 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
220 // then use pointer on the target, focus should no longer be visible
218 - dispatchPointerDown(innerTarget1);
221 + innerTarget1.pointerdown();
222 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
223 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
224 // onFocusVisibleChange should not be called again
222 - innerTarget1.dispatchEvent(blur({relatedTarget: container}));
225 + innerTarget1.blur({relatedTarget: container});
226 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(4);
227 });
228
229 it('is not called after "focus" and "blur" events without keyboard', () => {
227 - const innerTarget = innerRef.current;
228 - dispatchPointerDown(innerTarget);
229 - dispatchPointerUp(innerTarget);
230 - innerTarget.dispatchEvent(blur({relatedTarget: container}));
230 + const innerTarget = createEventTarget(innerRef.current);
231 + innerTarget.pointerdown();
232 + innerTarget.pointerup();
233 + innerTarget.blur({relatedTarget: container});
234 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(0);
235 });
236
237 it('is only called once when focus moves within and outside the subtree', () => {
235 - const target = ref.current;
236 - const innerTarget1 = innerRef.current;
237 - const innerTarget2 = innerRef2.current;
238 + const node = ref.current;
239 + const innerNode1 = innerRef.current;
240 + const innerNode2 = innerRef2.current;
241 + const target = createEventTarget(node);
242 + const innerTarget1 = createEventTarget(innerNode1);
243 + const innerTarget2 = createEventTarget(innerNode2);
244
245 // focus shifts into subtree
240 - innerTarget1.dispatchEvent(focus());
246 + innerTarget1.focus();
247 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
248 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(true);
249 // focus moves around subtree
244 - innerTarget1.dispatchEvent(blur({relatedTarget: innerTarget2}));
245 - innerTarget2.dispatchEvent(focus());
246 - innerTarget2.dispatchEvent(blur({relatedTarget: target}));
247 - target.dispatchEvent(focus());
248 - target.dispatchEvent(blur({relatedTarget: innerTarget1}));
250 + innerTarget1.blur({relatedTarget: innerNode2});
251 + innerTarget2.focus();
252 + innerTarget2.blur({relatedTarget: node});
253 + target.focus();
254 + target.blur({relatedTarget: innerNode1});
255 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(1);
256 // focus shifts outside subtree
251 - innerTarget1.dispatchEvent(blur({relatedTarget: container}));
257 + innerTarget1.blur({relatedTarget: container});
258 expect(onFocusWithinVisibleChange).toHaveBeenCalledTimes(2);
259 expect(onFocusWithinVisibleChange).toHaveBeenCalledWith(false);
260 });
packages/react-events/src/dom/__tests__/Hover-test.internal.js
+55 -55
@@ -9,14 +9,7 @@
9
10 'use strict';
11
12 -import {
13 - dispatchPointerCancel,
14 - dispatchPointerHoverEnter,
15 - dispatchPointerHoverExit,
16 - dispatchPointerHoverMove,
17 - dispatchTouchTap,
18 - setPointerEvent,
19 -} from '../test-utils';
12 +import {createEventTarget, setPointerEvent} from '../testing-library';
13
14 let React;
15 let ReactFeatureFlags;
@@ -77,9 +70,9 @@ describe.each(table)('Hover responder', hasPointerEvents => {
70 });
71
72 it('does not call callbacks', () => {
80 - const target = ref.current;
81 - dispatchPointerHoverEnter(target);
82 - dispatchPointerHoverExit(target);
73 + const target = createEventTarget(ref.current);
74 + target.pointerenter();
75 + target.pointerexit();
76 expect(onHoverChange).not.toBeCalled();
77 expect(onHoverStart).not.toBeCalled();
78 expect(onHoverMove).not.toBeCalled();
@@ -103,21 +96,23 @@ describe.each(table)('Hover responder', hasPointerEvents => {
96 });
97
98 it('is called for mouse pointers', () => {
106 - const target = ref.current;
107 - dispatchPointerHoverEnter(target);
99 + const target = createEventTarget(ref.current);
100 + target.pointerenter();
101 expect(onHoverStart).toHaveBeenCalledTimes(1);
102 });
103
104 it('is not called for touch pointers', () => {
112 - const target = ref.current;
113 - dispatchTouchTap(target);
105 + const target = createEventTarget(ref.current);
106 + target.pointerdown({pointerType: 'touch'});
107 + target.pointerup({pointerType: 'touch'});
108 expect(onHoverStart).not.toBeCalled();
109 });
110
111 it('is called if a mouse pointer is used after a touch pointer', () => {
118 - const target = ref.current;
119 - dispatchTouchTap(target);
120 - dispatchPointerHoverEnter(target);
112 + const target = createEventTarget(ref.current);
113 + target.pointerdown({pointerType: 'touch'});
114 + target.pointerup({pointerType: 'touch'});
115 + target.pointerenter();
116 expect(onHoverStart).toHaveBeenCalledTimes(1);
117 });
118 });
@@ -138,18 +133,19 @@ describe.each(table)('Hover responder', hasPointerEvents => {
133 });
134
135 it('is called for mouse pointers', () => {
141 - const target = ref.current;
142 - dispatchPointerHoverEnter(target);
136 + const target = createEventTarget(ref.current);
137 + target.pointerenter();
138 expect(onHoverChange).toHaveBeenCalledTimes(1);
139 expect(onHoverChange).toHaveBeenCalledWith(true);
145 - dispatchPointerHoverExit(target);
140 + target.pointerexit();
141 expect(onHoverChange).toHaveBeenCalledTimes(2);
142 expect(onHoverChange).toHaveBeenCalledWith(false);
143 });
144
145 it('is not called for touch pointers', () => {
151 - const target = ref.current;
152 - dispatchTouchTap(target);
146 + const target = createEventTarget(ref.current);
147 + target.pointerdown({pointerType: 'touch'});
148 + target.pointerup({pointerType: 'touch'});
149 expect(onHoverChange).not.toBeCalled();
150 });
151 });
@@ -170,31 +166,32 @@ describe.each(table)('Hover responder', hasPointerEvents => {
166 });
167
168 it('is called for mouse pointers', () => {
173 - const target = ref.current;
174 - dispatchPointerHoverEnter(target);
175 - dispatchPointerHoverExit(target);
169 + const target = createEventTarget(ref.current);
170 + target.pointerenter();
171 + target.pointerexit();
172 expect(onHoverEnd).toHaveBeenCalledTimes(1);
173 });
174
175 if (hasPointerEvents) {
176 it('is called once for cancelled mouse pointers', () => {
181 - const target = ref.current;
182 - dispatchPointerHoverEnter(target);
183 - dispatchPointerCancel(target);
177 + const target = createEventTarget(ref.current);
178 + target.pointerenter();
179 + target.pointercancel();
180 expect(onHoverEnd).toHaveBeenCalledTimes(1);
181
182 // only called once if cancel follows exit
183 onHoverEnd.mockReset();
188 - dispatchPointerHoverEnter(target);
189 - dispatchPointerHoverExit(target);
190 - dispatchPointerCancel(target);
184 + target.pointerenter();
185 + target.pointerexit();
186 + target.pointercancel();
187 expect(onHoverEnd).toHaveBeenCalledTimes(1);
188 });
189 }
190
191 it('is not called for touch pointers', () => {
196 - const target = ref.current;
197 - dispatchTouchTap(target);
192 + const target = createEventTarget(ref.current);
193 + target.pointerdown({pointerType: 'touch'});
194 + target.pointerup({pointerType: 'touch'});
195 expect(onHoverEnd).not.toBeCalled();
196 });
197 });
@@ -211,10 +208,10 @@ describe.each(table)('Hover responder', hasPointerEvents => {
208 };
209 ReactDOM.render(<Component />, container);
210
214 - const target = ref.current;
215 - dispatchPointerHoverEnter(target);
216 - dispatchPointerHoverMove(target, {x: 0, y: 0});
217 - dispatchPointerHoverMove(target, {x: 1, y: 1});
211 + const target = createEventTarget(ref.current);
212 + target.pointerenter();
213 + target.pointerhover({x: 0, y: 0});
214 + target.pointerhover({x: 1, y: 1});
215 expect(onHoverMove).toHaveBeenCalledTimes(2);
216 expect(onHoverMove).toHaveBeenCalledWith(
217 expect.objectContaining({type: 'hovermove'}),
@@ -254,15 +251,17 @@ describe.each(table)('Hover responder', hasPointerEvents => {
251 };
252 ReactDOM.render(<Outer />, container);
253
257 - const innerTarget = innerRef.current;
258 - const outerTarget = outerRef.current;
254 + const innerNode = innerRef.current;
255 + const outerNode = outerRef.current;
256 + const innerTarget = createEventTarget(innerNode);
257 + const outerTarget = createEventTarget(outerNode);
258
260 - dispatchPointerHoverEnter(outerTarget, {relatedTarget: container});
261 - dispatchPointerHoverExit(outerTarget, {relatedTarget: innerTarget});
262 - dispatchPointerHoverEnter(innerTarget, {relatedTarget: outerTarget});
263 - dispatchPointerHoverExit(innerTarget, {relatedTarget: outerTarget});
264 - dispatchPointerHoverEnter(outerTarget, {relatedTarget: innerTarget});
265 - dispatchPointerHoverExit(outerTarget, {relatedTarget: container});
259 + outerTarget.pointerenter({relatedTarget: container});
260 + outerTarget.pointerexit({relatedTarget: innerNode});
261 + innerTarget.pointerenter({relatedTarget: outerNode});
262 + innerTarget.pointerexit({relatedTarget: outerNode});
263 + outerTarget.pointerenter({relatedTarget: innerNode});
264 + outerTarget.pointerexit({relatedTarget: container});
265
266 expect(events).toEqual([
267 'outer: onHoverStart',
@@ -315,12 +314,13 @@ describe.each(table)('Hover responder', hasPointerEvents => {
314 };
315 ReactDOM.render(<Component />, container);
316
318 - const target = ref.current;
317 + const node = ref.current;
318 + const target = createEventTarget(node);
319
320 - dispatchPointerHoverEnter(target, {x: 10, y: 10});
321 - dispatchPointerHoverMove(target, {x: 10, y: 10});
322 - dispatchPointerHoverMove(target, {x: 20, y: 20});
323 - dispatchPointerHoverExit(target, {x: 20, y: 20});
320 + target.pointerenter({x: 10, y: 10});
321 + target.pointerhover({x: 10, y: 10});
322 + target.pointerhover({x: 20, y: 20});
323 + target.pointerexit({x: 20, y: 20});
324
325 expect(eventLog).toEqual([
326 {
@@ -330,7 +330,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
330 pageY: 10,
331 clientX: 10,
332 clientY: 10,
333 - target,
333 + target: node,
334 timeStamp: timeStamps[0],
335 type: 'hoverstart',
336 pointerType: 'mouse',
@@ -342,7 +342,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
342 pageY: 10,
343 clientX: 10,
344 clientY: 10,
345 - target,
345 + target: node,
346 timeStamp: timeStamps[1],
347 type: 'hovermove',
348 pointerType: 'mouse',
@@ -354,7 +354,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
354 pageY: 20,
355 clientX: 20,
356 clientY: 20,
357 - target,
357 + target: node,
358 timeStamp: timeStamps[2],
359 type: 'hovermove',
360 pointerType: 'mouse',
@@ -366,7 +366,7 @@ describe.each(table)('Hover responder', hasPointerEvents => {
366 pageY: 20,
367 clientX: 20,
368 clientY: 20,
369 - target,
369 + target: node,
370 timeStamp: timeStamps[3],
371 type: 'hoverend',
372 pointerType: 'mouse',
packages/react-events/src/dom/__tests__/Keyboard-test.internal.js
+9 -8
@@ -14,7 +14,7 @@ let ReactFeatureFlags;
14 let ReactDOM;
15 let useKeyboardResponder;
16
17 -import {keydown, keyup} from '../test-utils';
17 +import {createEventTarget} from '../testing-library';
18
19 function initializeModules(hasPointerEvents) {
20 jest.resetModules();
@@ -59,9 +59,9 @@ describe('Keyboard event responder', () => {
59 });
60
61 it('prevents custom events being dispatched', () => {
62 - const target = ref.current;
63 - target.dispatchEvent(keydown());
64 - target.dispatchEvent(keyup());
62 + const target = createEventTarget(ref.current);
63 + target.keydown();
64 + target.keyup();
65 expect(onKeyDown).not.toBeCalled();
66 expect(onKeyUp).not.toBeCalled();
67 });
@@ -83,7 +83,8 @@ describe('Keyboard event responder', () => {
83 });
84
85 it('is called after "keydown" event', () => {
86 - ref.current.dispatchEvent(keydown({key: 'Q'}));
86 + const target = createEventTarget(ref.current);
87 + target.keydown({key: 'Q'});
88 expect(onKeyDown).toHaveBeenCalledTimes(1);
89 expect(onKeyDown).toHaveBeenCalledWith(
90 expect.objectContaining({key: 'Q', type: 'keydown'}),
@@ -109,9 +110,9 @@ describe('Keyboard event responder', () => {
110 });
111
112 it('is called after "keydown" event', () => {
112 - const target = ref.current;
113 - target.dispatchEvent(keydown({key: 'Q'}));
114 - target.dispatchEvent(keyup({key: 'Q'}));
113 + const target = createEventTarget(ref.current);
114 + target.keydown({key: 'Q'});
115 + target.keyup({key: 'Q'});
116 expect(onKeyDown).toHaveBeenCalledTimes(1);
117 expect(onKeyDown).toHaveBeenCalledWith(
118 expect.objectContaining({key: 'Q', type: 'keydown'}),
packages/react-events/src/dom/__tests__/Press-test.internal.js
+268 -401
@@ -9,27 +9,13 @@
9
10 'use strict';
11
12 -import {
13 - click,
14 - dispatchPointerCancel,
15 - dispatchPointerDown,
16 - dispatchPointerUp,
17 - dispatchPointerHoverMove,
18 - dispatchPointerMove,
19 - keydown,
20 - keyup,
21 - scroll,
22 - pointerdown,
23 - pointerup,
24 - setPointerEvent,
25 -} from '../test-utils';
12 +import {createEventTarget, setPointerEvent} from '../testing-library';
13
14 let React;
15 let ReactFeatureFlags;
16 let ReactDOM;
17 let PressResponder;
18 let usePressResponder;
32 -let Scheduler;
19
20 function initializeModules(hasPointerEvents) {
21 jest.resetModules();
@@ -40,7 +26,6 @@ function initializeModules(hasPointerEvents) {
26 ReactDOM = require('react-dom');
27 PressResponder = require('react-events/press').PressResponder;
28 usePressResponder = require('react-events/press').usePressResponder;
43 - Scheduler = require('scheduler');
29 }
30
31 function removePressMoveStrings(eventString) {
@@ -92,9 +77,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
77 });
78
79 it('does not call callbacks', () => {
95 - const target = ref.current;
96 - dispatchPointerDown(target);
97 - dispatchPointerUp(target);
80 + const target = createEventTarget(ref.current);
81 + target.pointerdown();
82 + target.pointerup();
83 expect(onPressStart).not.toBeCalled();
84 expect(onPress).not.toBeCalled();
85 expect(onPressEnd).not.toBeCalled();
@@ -120,7 +105,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
105 it.each(pointerTypesTable)(
106 'is called after pointer down: %s',
107 pointerType => {
123 - dispatchPointerDown(ref.current, {pointerType});
108 + const target = createEventTarget(ref.current);
109 + target.pointerdown({pointerType});
110 expect(onPressStart).toHaveBeenCalledTimes(1);
111 expect(onPressStart).toHaveBeenCalledWith(
112 expect.objectContaining({pointerType, type: 'pressstart'}),
@@ -129,7 +115,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
115 );
116
117 it('is called after auxillary-button pointer down', () => {
132 - dispatchPointerDown(ref.current, {button: 1, pointerType: 'mouse'});
118 + const target = createEventTarget(ref.current);
119 + target.pointerdown({button: 1, pointerType: 'mouse'});
120 expect(onPressStart).toHaveBeenCalledTimes(1);
121 expect(onPressStart).toHaveBeenCalledWith(
122 expect.objectContaining({
@@ -140,39 +127,31 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
127 );
128 });
129
143 - it('is not called after "pointermove" following auxillary-button press', () => {
144 - const target = ref.current;
145 - target.getBoundingClientRect = () => ({
146 - top: 0,
147 - left: 0,
148 - bottom: 100,
149 - right: 100,
150 - });
151 - dispatchPointerDown(target, {
152 - button: 1,
153 - pointerType: 'mouse',
154 - });
155 - dispatchPointerUp(target, {
156 - button: 1,
157 - pointerType: 'mouse',
158 - });
159 - dispatchPointerHoverMove(target, {x: 110, y: 110});
160 - dispatchPointerHoverMove(target, {x: 50, y: 50});
130 + it('is not called after pointer move following auxillary-button press', () => {
131 + const node = ref.current;
132 + const target = createEventTarget(node);
133 + target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
134 + target.pointerdown({button: 1, pointerType: 'mouse'});
135 + target.pointerup({button: 1, pointerType: 'mouse'});
136 + target.pointerhover({x: 110, y: 110});
137 + target.pointerhover({x: 50, y: 50});
138 expect(onPressStart).toHaveBeenCalledTimes(1);
139 });
140
141 it('ignores any events not caused by primary/auxillary-click or touch/pen contact', () => {
165 - const target = ref.current;
166 - dispatchPointerDown(target, {button: 2});
167 - dispatchPointerDown(target, {button: 5});
142 + const target = createEventTarget(ref.current);
143 + target.pointerdown({button: 2});
144 + target.pointerup({button: 2});
145 + target.pointerdown({button: 5});
146 + target.pointerup({button: 5});
147 expect(onPressStart).toHaveBeenCalledTimes(0);
148 });
149
150 it('is called once after "keydown" events for Enter', () => {
172 - const target = ref.current;
173 - target.dispatchEvent(keydown({key: 'Enter'}));
174 - target.dispatchEvent(keydown({key: 'Enter'}));
175 - target.dispatchEvent(keydown({key: 'Enter'}));
151 + const target = createEventTarget(ref.current);
152 + target.keydown({key: 'Enter'});
153 + target.keydown({key: 'Enter'});
154 + target.keydown({key: 'Enter'});
155 expect(onPressStart).toHaveBeenCalledTimes(1);
156 expect(onPressStart).toHaveBeenCalledWith(
157 expect.objectContaining({pointerType: 'keyboard', type: 'pressstart'}),
@@ -180,11 +159,11 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
159 });
160
161 it('is called once after "keydown" events for Spacebar', () => {
183 - const target = ref.current;
162 + const target = createEventTarget(ref.current);
163 const preventDefault = jest.fn();
185 - target.dispatchEvent(keydown({key: ' ', preventDefault}));
164 + target.keydown({key: ' ', preventDefault});
165 expect(preventDefault).toBeCalled();
187 - target.dispatchEvent(keydown({key: ' ', preventDefault}));
166 + target.keydown({key: ' ', preventDefault});
167 expect(onPressStart).toHaveBeenCalledTimes(1);
168 expect(onPressStart).toHaveBeenCalledWith(
169 expect.objectContaining({
@@ -195,7 +174,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
174 });
175
176 it('is not called after "keydown" for other keys', () => {
198 - ref.current.dispatchEvent(keydown({key: 'a'}));
177 + const target = createEventTarget(ref.current);
178 + target.keydown({key: 'a'});
179 expect(onPressStart).not.toBeCalled();
180 });
181 });
@@ -219,9 +199,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
199 it.each(pointerTypesTable)(
200 'is called after pointer up: %s',
201 pointerType => {
222 - const target = ref.current;
223 - dispatchPointerDown(target, {pointerType});
224 - dispatchPointerUp(target, {pointerType});
202 + const target = createEventTarget(ref.current);
203 + target.pointerdown({pointerType});
204 + target.pointerup({pointerType});
205 expect(onPressEnd).toHaveBeenCalledTimes(1);
206 expect(onPressEnd).toHaveBeenCalledWith(
207 expect.objectContaining({pointerType, type: 'pressend'}),
@@ -230,9 +210,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
210 );
211
212 it('is called after auxillary-button pointer up', () => {
233 - const target = ref.current;
234 - dispatchPointerDown(target, {button: 1, pointerType: 'mouse'});
235 - dispatchPointerUp(target, {button: 1, pointerType: 'mouse'});
213 + const target = createEventTarget(ref.current);
214 + target.pointerdown({button: 1, pointerType: 'mouse'});
215 + target.pointerup({button: 1, pointerType: 'mouse'});
216 expect(onPressEnd).toHaveBeenCalledTimes(1);
217 expect(onPressEnd).toHaveBeenCalledWith(
218 expect.objectContaining({
@@ -244,11 +224,11 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
224 });
225
226 it('is called after "keyup" event for Enter', () => {
247 - const target = ref.current;
248 - target.dispatchEvent(keydown({key: 'Enter'}));
227 + const target = createEventTarget(ref.current);
228 + target.keydown({key: 'Enter'});
229 // click occurs before keyup
250 - target.dispatchEvent(click());
251 - target.dispatchEvent(keyup({key: 'Enter'}));
230 + target.click();
231 + target.keyup({key: 'Enter'});
232 expect(onPressEnd).toHaveBeenCalledTimes(1);
233 expect(onPressEnd).toHaveBeenCalledWith(
234 expect.objectContaining({pointerType: 'keyboard', type: 'pressend'}),
@@ -256,9 +236,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
236 });
237
238 it('is called after "keyup" event for Spacebar', () => {
259 - const target = ref.current;
260 - target.dispatchEvent(keydown({key: ' '}));
261 - target.dispatchEvent(keyup({key: ' '}));
239 + const target = createEventTarget(ref.current);
240 + target.keydown({key: ' '});
241 + target.keyup({key: ' '});
242 expect(onPressEnd).toHaveBeenCalledTimes(1);
243 expect(onPressEnd).toHaveBeenCalledWith(
244 expect.objectContaining({pointerType: 'keyboard', type: 'pressend'}),
@@ -266,24 +246,22 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
246 });
247
248 it('is not called after "keyup" event for other keys', () => {
269 - const target = ref.current;
270 - target.dispatchEvent(keydown({key: 'Enter'}));
271 - target.dispatchEvent(keyup({key: 'a'}));
249 + const target = createEventTarget(ref.current);
250 + target.keydown({key: 'Enter'});
251 + target.keyup({key: 'a'});
252 expect(onPressEnd).not.toBeCalled();
253 });
254
255 it('is called with keyboard modifiers', () => {
276 - const target = ref.current;
277 - target.dispatchEvent(keydown({key: 'Enter'}));
278 - target.dispatchEvent(
279 - keyup({
280 - key: 'Enter',
281 - metaKey: true,
282 - ctrlKey: true,
283 - altKey: true,
284 - shiftKey: true,
285 - }),
286 - );
256 + const target = createEventTarget(ref.current);
257 + target.keydown({key: 'Enter'});
258 + target.keyup({
259 + key: 'Enter',
260 + metaKey: true,
261 + ctrlKey: true,
262 + altKey: true,
263 + shiftKey: true,
264 + });
265 expect(onPressEnd).toHaveBeenCalledWith(
266 expect.objectContaining({
267 pointerType: 'keyboard',
@@ -316,21 +294,22 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
294 it.each(pointerTypesTable)(
295 'is called after pointer down and up: %s',
296 pointerType => {
319 - const target = ref.current;
320 - dispatchPointerDown(target, {pointerType});
297 + const target = createEventTarget(ref.current);
298 + target.pointerdown({pointerType});
299 expect(onPressChange).toHaveBeenCalledTimes(1);
300 expect(onPressChange).toHaveBeenCalledWith(true);
323 - dispatchPointerUp(target, {pointerType});
301 + target.pointerup({pointerType});
302 expect(onPressChange).toHaveBeenCalledTimes(2);
303 expect(onPressChange).toHaveBeenCalledWith(false);
304 },
305 );
306
307 it('is called after valid "keydown" and "keyup" events', () => {
330 - ref.current.dispatchEvent(keydown({key: 'Enter'}));
308 + const target = createEventTarget(ref.current);
309 + target.keydown({key: 'Enter'});
310 expect(onPressChange).toHaveBeenCalledTimes(1);
311 expect(onPressChange).toHaveBeenCalledWith(true);
333 - ref.current.dispatchEvent(keyup({key: 'Enter'}));
312 + target.keyup({key: 'Enter'});
313 expect(onPressChange).toHaveBeenCalledTimes(2);
314 expect(onPressChange).toHaveBeenCalledWith(false);
315 });
@@ -361,9 +340,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
340 it.each(pointerTypesTable)(
341 'is called after pointer up: %s',
342 pointerType => {
364 - const target = ref.current;
365 - dispatchPointerDown(target, {pointerType});
366 - dispatchPointerUp(target, {pointerType, x: 10, y: 10});
343 + const target = createEventTarget(ref.current);
344 + target.pointerdown({pointerType});
345 + target.pointerup({pointerType, x: 10, y: 10});
346 expect(onPress).toHaveBeenCalledTimes(1);
347 expect(onPress).toHaveBeenCalledWith(
348 expect.objectContaining({pointerType, type: 'press'}),
@@ -372,16 +351,16 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
351 );
352
353 it('is not called after auxillary-button press', () => {
375 - const target = ref.current;
376 - dispatchPointerDown(target, {button: 1, pointerType: 'mouse'});
377 - dispatchPointerUp(target, {button: 1, pointerType: 'mouse'});
354 + const target = createEventTarget(ref.current);
355 + target.pointerdown({button: 1, pointerType: 'mouse'});
356 + target.pointerup({button: 1, pointerType: 'mouse'});
357 expect(onPress).not.toHaveBeenCalled();
358 });
359
360 it('is called after valid "keyup" event', () => {
382 - const target = ref.current;
383 - target.dispatchEvent(keydown({key: 'Enter'}));
384 - target.dispatchEvent(keyup({key: 'Enter'}));
361 + const target = createEventTarget(ref.current);
362 + target.keydown({key: 'Enter'});
363 + target.keyup({key: 'Enter'});
364 expect(onPress).toHaveBeenCalledTimes(1);
365 expect(onPress).toHaveBeenCalledWith(
366 expect.objectContaining({pointerType: 'keyboard', type: 'press'}),
@@ -391,27 +370,22 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
370 it('is not called after invalid "keyup" event', () => {
371 const inputRef = React.createRef();
372 const Component = () => {
394 - const listener = usePressResponder({
395 - onPress,
396 - });
373 + const listener = usePressResponder({onPress});
374 return <input ref={inputRef} listeners={listener} />;
375 };
376 ReactDOM.render(<Component />, container);
400 - const target = inputRef.current;
401 - target.dispatchEvent(keydown({key: 'Enter'}));
402 - target.dispatchEvent(keyup({key: 'Enter'}));
403 - target.dispatchEvent(keydown({key: ' '}));
404 - target.dispatchEvent(keyup({key: ' '}));
377 + const target = createEventTarget(inputRef.current);
378 + target.keydown({key: 'Enter'});
379 + target.keyup({key: 'Enter'});
380 + target.keydown({key: ' '});
381 + target.keyup({key: ' '});
382 expect(onPress).not.toBeCalled();
383 });
384
385 it('is called with modifier keys', () => {
409 - const target = ref.current;
410 - dispatchPointerDown(target, {metaKey: true, pointerType: 'mouse'});
411 - dispatchPointerUp(target, {
412 - metaKey: true,
413 - pointerType: 'mouse',
414 - });
386 + const target = createEventTarget(ref.current);
387 + target.pointerdown({metaKey: true, pointerType: 'mouse'});
388 + target.pointerup({metaKey: true, pointerType: 'mouse'});
389 expect(onPress).toHaveBeenCalledWith(
390 expect.objectContaining({
391 pointerType: 'mouse',
@@ -426,9 +400,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
400 const divRef = React.createRef();
401
402 const Component = () => {
429 - const listener = usePressResponder({
430 - onPress,
431 - });
403 + const listener = usePressResponder({onPress});
404 return (
405 <div ref={divRef} listeners={listener}>
406 <button ref={buttonRef} />
@@ -437,15 +409,11 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
409 };
410 ReactDOM.render(<Component />, container);
411
440 - divRef.current.getBoundingClientRect = () => ({
441 - left: 0,
442 - right: 0,
443 - bottom: 0,
444 - top: 0,
445 - });
446 - const target = buttonRef.current;
447 - dispatchPointerDown(target, {pointerType: 'mouse'});
448 - dispatchPointerUp(target, {pointerType: 'mouse'});
412 + const target = createEventTarget(divRef.current);
413 + target.setBoundingClientRect({x: 0, y: 0, width: 0, height: 0});
414 + const innerTarget = createEventTarget(buttonRef.current);
415 + innerTarget.pointerdown({pointerType: 'mouse'});
416 + innerTarget.pointerup({pointerType: 'mouse'});
417 expect(onPress).toBeCalled();
418 });
419 });
@@ -475,24 +443,12 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
443 it.each(pointerTypesTable)(
444 'is called after pointer move: %s',
445 pointerType => {
478 - const target = ref.current;
479 - target.getBoundingClientRect = () => ({
480 - top: 0,
481 - left: 0,
482 - bottom: 100,
483 - right: 100,
484 - });
485 - dispatchPointerDown(target, {pointerType});
486 - dispatchPointerMove(target, {
487 - pointerType,
488 - x: 10,
489 - y: 10,
490 - });
491 - dispatchPointerMove(target, {
492 - pointerType,
493 - x: 20,
494 - y: 20,
495 - });
446 + const node = ref.current;
447 + const target = createEventTarget(node);
448 + target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
449 + target.pointerdown({pointerType});
450 + target.pointermove({pointerType, x: 10, y: 10});
451 + target.pointermove({pointerType, x: 20, y: 20});
452 expect(onPressMove).toHaveBeenCalledTimes(2);
453 expect(onPressMove).toHaveBeenCalledWith(
454 expect.objectContaining({pointerType, type: 'pressmove'}),
@@ -501,20 +457,10 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
457 );
458
459 it('is not called if pointer move occurs during keyboard press', () => {
504 - const target = ref.current;
505 - target.getBoundingClientRect = () => ({
506 - top: 0,
507 - left: 0,
508 - bottom: 100,
509 - right: 100,
510 - });
511 - target.dispatchEvent(keydown({key: 'Enter'}));
512 - dispatchPointerMove(target, {
513 - button: -1,
514 - pointerType: 'mouse',
515 - x: 10,
516 - y: 10,
517 - });
460 + const target = createEventTarget(ref.current);
461 + target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
462 + target.keydown({key: 'Enter'});
463 + target.pointermove({button: -1, pointerType: 'mouse', x: 10, y: 10});
464 expect(onPressMove).not.toBeCalled();
465 });
466 });
@@ -547,23 +493,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
493 document.elementFromPoint = () => ref.current;
494 });
495
550 - const rectMock = {
551 - width: 100,
552 - height: 100,
553 - top: 50,
554 - left: 50,
555 - right: 150,
556 - bottom: 150,
557 - };
496 + const rectMock = {width: 100, height: 100, x: 50, y: 50};
497 const pressRectOffset = 20;
559 - const getBoundingClientRectMock = () => rectMock;
498 const coordinatesInside = {
561 - x: rectMock.left - pressRectOffset,
562 - y: rectMock.top - pressRectOffset,
499 + x: rectMock.x - pressRectOffset,
500 + y: rectMock.y - pressRectOffset,
501 };
502 const coordinatesOutside = {
565 - x: rectMock.left - pressRectOffset - 1,
566 - y: rectMock.top - pressRectOffset - 1,
503 + x: rectMock.x - pressRectOffset - 1,
504 + y: rectMock.y - pressRectOffset - 1,
505 };
506
507 describe('within bounds of hit rect', () => {
@@ -575,12 +513,11 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
513 * └──────────────────┘
514 */
515 it('"onPress*" events are called immediately', () => {
578 - const target = ref.current;
579 - target.getBoundingClientRect = getBoundingClientRectMock;
580 - dispatchPointerDown(target, {pointerType});
581 - dispatchPointerMove(target, {pointerType, ...coordinatesInside});
582 - dispatchPointerUp(target, {pointerType, ...coordinatesInside});
583 - jest.runAllTimers();
516 + const target = createEventTarget(ref.current);
517 + target.setBoundingClientRect(rectMock);
518 + target.pointerdown({pointerType});
519 + target.pointermove({pointerType, ...coordinatesInside});
520 + target.pointerup({pointerType, ...coordinatesInside});
521 expect(events).toEqual([
522 'onPressStart',
523 'onPressChange',
@@ -592,20 +529,20 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
529 });
530
531 it('"onPress*" events are correctly called with target change', () => {
595 - const target = ref.current;
596 - const outer = outerRef.current;
597 - target.getBoundingClientRect = getBoundingClientRectMock;
598 - dispatchPointerDown(target, {pointerType});
599 - dispatchPointerMove(target, {pointerType, ...coordinatesInside});
532 + const target = createEventTarget(ref.current);
533 + const outerTarget = createEventTarget(outerRef.current);
534 + target.setBoundingClientRect(rectMock);
535 + target.pointerdown({pointerType});
536 + target.pointermove({pointerType, ...coordinatesInside});
537 // TODO: this sequence may differ in the future between PointerEvent and mouse fallback when
538 // use 'setPointerCapture'.
539 if (pointerType === 'touch') {
603 - dispatchPointerMove(target, {pointerType, ...coordinatesOutside});
540 + target.pointermove({pointerType, ...coordinatesOutside});
541 } else {
605 - dispatchPointerMove(outer, {pointerType, ...coordinatesOutside});
542 + outerTarget.pointermove({pointerType, ...coordinatesOutside});
543 }
607 - dispatchPointerMove(target, {pointerType, ...coordinatesInside});
608 - dispatchPointerUp(target, {pointerType, ...coordinatesInside});
544 + target.pointermove({pointerType, ...coordinatesInside});
545 + target.pointerup({pointerType, ...coordinatesInside});
546
547 expect(events.filter(removePressMoveStrings)).toEqual([
548 'onPressStart',
@@ -641,15 +578,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
578 };
579 ReactDOM.render(<Component />, container);
580
644 - const target = localRef.current;
645 - target.getBoundingClientRect = getBoundingClientRectMock;
646 - dispatchPointerDown(target, {pointerType});
647 - dispatchPointerMove(target, {
581 + const target = createEventTarget(localRef.current);
582 + target.setBoundingClientRect(rectMock);
583 + target.pointerdown({pointerType});
584 + target.pointermove({
585 pointerType,
649 - x: rectMock.left,
650 - y: rectMock.top,
586 + x: rectMock.x,
587 + y: rectMock.y,
588 });
652 - dispatchPointerUp(target, {pointerType, ...coordinatesInside});
589 + target.pointerup({pointerType, ...coordinatesInside});
590 expect(localEvents).toEqual([
591 'onPressStart',
592 'onPressChange',
@@ -661,25 +598,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
598 });
599
600 it('responder region accounts for decrease in element dimensions', () => {
664 - const target = ref.current;
665 - target.getBoundingClientRect = getBoundingClientRectMock;
666 - dispatchPointerDown(target, {pointerType});
601 + const target = createEventTarget(ref.current);
602 + target.setBoundingClientRect(rectMock);
603 + target.pointerdown({pointerType});
604 // emulate smaller dimensions change on activation
668 - target.getBoundingClientRect = () => ({
669 - width: 80,
670 - height: 80,
671 - top: 60,
672 - left: 60,
673 - right: 490,
674 - bottom: 490,
675 - });
676 - const coordinates = {
677 - x: rectMock.left,
678 - y: rectMock.top,
679 - };
605 + target.setBoundingClientRect({width: 80, height: 80, y: 60, x: 60});
606 + const coordinates = {x: rectMock.x, y: rectMock.y};
607 // move to an area within the pre-activation region
681 - dispatchPointerMove(target, {pointerType, ...coordinates});
682 - dispatchPointerUp(target, {pointerType, ...coordinates});
608 + target.pointermove({pointerType, ...coordinates});
609 + target.pointerup({pointerType, ...coordinates});
610 expect(events).toEqual([
611 'onPressStart',
612 'onPressChange',
@@ -691,25 +618,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
618 });
619
620 it('responder region accounts for increase in element dimensions', () => {
694 - const target = ref.current;
695 - target.getBoundingClientRect = getBoundingClientRectMock;
696 - dispatchPointerDown(target, {pointerType});
621 + const target = createEventTarget(ref.current);
622 + target.setBoundingClientRect(rectMock);
623 + target.pointerdown({pointerType});
624 // emulate larger dimensions change on activation
698 - target.getBoundingClientRect = () => ({
699 - width: 200,
700 - height: 200,
701 - top: 0,
702 - left: 0,
703 - right: 550,
704 - bottom: 550,
705 - });
706 - const coordinates = {
707 - x: rectMock.left - 50,
708 - y: rectMock.top - 50,
709 - };
625 + target.setBoundingClientRect({width: 200, height: 200, y: 0, x: 0});
626 + const coordinates = {x: rectMock.x - 50, y: rectMock.y - 50};
627 // move to an area within the post-activation region
711 - dispatchPointerMove(target, {pointerType, ...coordinates});
712 - dispatchPointerUp(target, {pointerType, ...coordinates});
628 + target.pointermove({pointerType, ...coordinates});
629 + target.pointerup({pointerType, ...coordinates});
630 expect(events).toEqual([
631 'onPressStart',
632 'onPressChange',
@@ -731,17 +648,19 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
648 * X <= Move to X and release
649 */
650 it('"onPress" is not called on release', () => {
734 - const target = ref.current;
735 - target.getBoundingClientRect = getBoundingClientRectMock;
736 - dispatchPointerDown(target, {pointerType});
737 - dispatchPointerMove(target, {pointerType, ...coordinatesInside});
651 + const target = createEventTarget(ref.current);
652 + const targetContainer = createEventTarget(container);
653 + target.setBoundingClientRect(rectMock);
654 + target.pointerdown({pointerType});
655 + target.pointermove({pointerType, ...coordinatesInside});
656 if (pointerType === 'mouse') {
657 // TODO: use setPointerCapture so this is only true for fallback mouse events.
740 - dispatchPointerMove(container, {pointerType, ...coordinatesOutside});
658 + targetContainer.pointermove({pointerType, ...coordinatesOutside});
659 + targetContainer.pointerup({pointerType, ...coordinatesOutside});
660 } else {
742 - dispatchPointerMove(target, {pointerType, ...coordinatesOutside});
661 + target.pointermove({pointerType, ...coordinatesOutside});
662 + target.pointerup({pointerType, ...coordinatesOutside});
663 }
744 - dispatchPointerUp(container, {pointerType, ...coordinatesOutside});
664 expect(events.filter(removePressMoveStrings)).toEqual([
665 'onPressStart',
666 'onPressChange',
@@ -752,18 +671,19 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
671 });
672
673 it('"onPress" is called on re-entry to hit rect', () => {
755 - const target = ref.current;
756 - target.getBoundingClientRect = getBoundingClientRectMock;
757 - dispatchPointerDown(target, {pointerType});
758 - dispatchPointerMove(target, {pointerType, ...coordinatesInside});
674 + const target = createEventTarget(ref.current);
675 + const targetContainer = createEventTarget(container);
676 + target.setBoundingClientRect(rectMock);
677 + target.pointerdown({pointerType});
678 + target.pointermove({pointerType, ...coordinatesInside});
679 if (pointerType === 'mouse') {
680 // TODO: use setPointerCapture so this is only true for fallback mouse events.
761 - dispatchPointerMove(container, {pointerType, ...coordinatesOutside});
681 + targetContainer.pointermove({pointerType, ...coordinatesOutside});
682 } else {
763 - dispatchPointerMove(target, {pointerType, ...coordinatesOutside});
683 + target.pointermove({pointerType, ...coordinatesOutside});
684 }
765 - dispatchPointerMove(target, {pointerType, ...coordinatesInside});
766 - dispatchPointerUp(target, {pointerType, ...coordinatesInside});
685 + target.pointermove({pointerType, ...coordinatesInside});
686 + target.pointerup({pointerType, ...coordinatesInside});
687
688 expect(events).toEqual([
689 'onPressStart',
@@ -826,15 +746,10 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
746 };
747 ReactDOM.render(<Outer />, container);
748
829 - const target = ref.current;
830 - target.getBoundingClientRect = () => ({
831 - top: 0,
832 - left: 0,
833 - bottom: 100,
834 - right: 100,
835 - });
836 - dispatchPointerDown(target);
837 - dispatchPointerUp(target);
749 + const target = createEventTarget(ref.current);
750 + target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
751 + target.pointerdown();
752 + target.pointerup();
753 expect(events).toEqual([
754 'inner: onPressStart',
755 'inner: onPressChange',
@@ -850,19 +765,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
765 describe('correctly not propagate', () => {
766 it('for onPress', () => {
767 const ref = React.createRef();
853 - const fn = jest.fn();
768 + const onPress = jest.fn();
769
770 const Inner = () => {
856 - const listener = usePressResponder({
857 - onPress: fn,
858 - });
771 + const listener = usePressResponder({onPress});
772 return <div ref={ref} listeners={listener} />;
773 };
774
775 const Outer = () => {
863 - const listener = usePressResponder({
864 - onPress: fn,
865 - });
776 + const listener = usePressResponder({onPress});
777 return (
778 <div listeners={listener}>
779 <Inner />
@@ -871,36 +782,25 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
782 };
783 ReactDOM.render(<Outer />, container);
784
874 - const target = ref.current;
875 - target.getBoundingClientRect = () => ({
876 - top: 0,
877 - left: 0,
878 - bottom: 100,
879 - right: 100,
880 - });
881 - dispatchPointerDown(target);
882 - dispatchPointerUp(target);
883 - expect(fn).toHaveBeenCalledTimes(1);
785 + const target = createEventTarget(ref.current);
786 + target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
787 + target.pointerdown();
788 + target.pointerup();
789 + expect(onPress).toHaveBeenCalledTimes(1);
790 });
791
792 it('for onPressStart/onPressEnd', () => {
793 const ref = React.createRef();
888 - const fn = jest.fn();
889 - const fn2 = jest.fn();
794 + const onPressStart = jest.fn();
795 + const onPressEnd = jest.fn();
796
797 const Inner = () => {
892 - const listener = usePressResponder({
893 - onPressStart: fn,
894 - onPressEnd: fn2,
895 - });
798 + const listener = usePressResponder({onPressStart, onPressEnd});
799 return <div ref={ref} listeners={listener} />;
800 };
801
802 const Outer = () => {
900 - const listener = usePressResponder({
901 - onPressStart: fn,
902 - onPressEnd: fn2,
903 - });
803 + const listener = usePressResponder({onPressStart, onPressEnd});
804 return (
805 <div listeners={listener}>
806 <Inner />
@@ -909,30 +809,26 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
809 };
810 ReactDOM.render(<Outer />, container);
811
912 - const target = ref.current;
913 - dispatchPointerDown(target);
914 - expect(fn).toHaveBeenCalledTimes(1);
915 - expect(fn2).toHaveBeenCalledTimes(0);
916 - dispatchPointerUp(target);
917 - expect(fn).toHaveBeenCalledTimes(1);
918 - expect(fn2).toHaveBeenCalledTimes(1);
812 + const target = createEventTarget(ref.current);
813 + target.pointerdown();
814 + expect(onPressStart).toHaveBeenCalledTimes(1);
815 + expect(onPressEnd).toHaveBeenCalledTimes(0);
816 + target.pointerup();
817 + expect(onPressStart).toHaveBeenCalledTimes(1);
818 + expect(onPressEnd).toHaveBeenCalledTimes(1);
819 });
820
821 it('for onPressChange', () => {
822 const ref = React.createRef();
923 - const fn = jest.fn();
823 + const onPressChange = jest.fn();
824
825 const Inner = () => {
926 - const listener = usePressResponder({
927 - onPressChange: fn,
928 - });
826 + const listener = usePressResponder({onPressChange});
827 return <div ref={ref} listeners={listener} />;
828 };
829
830 const Outer = () => {
933 - const listener = usePressResponder({
934 - onPressChange: fn,
935 - });
831 + const listener = usePressResponder({onPressChange});
832 return (
833 <div listeners={listener}>
834 <Inner />
@@ -941,11 +837,11 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
837 };
838 ReactDOM.render(<Outer />, container);
839
944 - const target = ref.current;
945 - dispatchPointerDown(target);
946 - expect(fn).toHaveBeenCalledTimes(1);
947 - dispatchPointerUp(target);
948 - expect(fn).toHaveBeenCalledTimes(2);
840 + const target = createEventTarget(ref.current);
841 + target.pointerdown();
842 + expect(onPressChange).toHaveBeenCalledTimes(1);
843 + target.pointerup();
844 + expect(onPressChange).toHaveBeenCalledTimes(2);
845 });
846 });
847 });
@@ -957,16 +853,14 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
853 const ref = React.createRef();
854
855 const Component = () => {
960 - const listener = usePressResponder({
961 - onPress,
962 - });
856 + const listener = usePressResponder({onPress});
857 return <a href="#" ref={ref} listeners={listener} />;
858 };
859 ReactDOM.render(<Component />, container);
860
967 - const target = ref.current;
968 - dispatchPointerDown(target);
969 - dispatchPointerUp(target, {preventDefault});
861 + const target = createEventTarget(ref.current);
862 + target.pointerdown();
863 + target.pointerup({preventDefault});
864 expect(preventDefault).toBeCalled();
865 expect(onPress).toHaveBeenCalledWith(
866 expect.objectContaining({defaultPrevented: true}),
@@ -979,17 +873,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
873 const ref = React.createRef();
874
875 const Component = () => {
982 - const listener = usePressResponder({
983 - onPress,
984 - });
876 + const listener = usePressResponder({onPress});
877 return <a href="#" ref={ref} listeners={listener} />;
878 };
879 ReactDOM.render(<Component />, container);
880
989 - const target = ref.current;
990 - target.dispatchEvent(keydown({key: 'Enter'}));
991 - target.dispatchEvent(click({preventDefault}));
992 - target.dispatchEvent(keyup({key: 'Enter'}));
881 + const target = createEventTarget(ref.current);
882 + target.keydown({key: 'Enter'});
883 + target.click({preventDefault});
884 + target.keyup({key: 'Enter'});
885 expect(preventDefault).toBeCalled();
886 expect(onPress).toHaveBeenCalledWith(
887 expect.objectContaining({defaultPrevented: true}),
@@ -1002,9 +894,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
894 const buttonRef = React.createRef();
895
896 const Component = () => {
1005 - const listener = usePressResponder({
1006 - onPress,
1007 - });
897 + const listener = usePressResponder({onPress});
898 return (
899 <a href="#">
900 <button ref={buttonRef} listeners={listener} />
@@ -1013,9 +903,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
903 };
904 ReactDOM.render(<Component />, container);
905
1016 - const target = buttonRef.current;
1017 - dispatchPointerDown(target);
1018 - dispatchPointerUp(target, {preventDefault});
906 + const target = createEventTarget(buttonRef.current);
907 + target.pointerdown();
908 + target.pointerup({preventDefault});
909 expect(preventDefault).toBeCalled();
910 });
911
@@ -1025,9 +915,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
915 const ref = React.createRef();
916
917 const Component = () => {
1028 - const listener = usePressResponder({
1029 - onPress,
1030 - });
918 + const listener = usePressResponder({onPress});
919 return (
920 <a href="#" listeners={listener}>
921 <div ref={ref} />
@@ -1036,9 +924,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
924 };
925 ReactDOM.render(<Component />, container);
926
1039 - const target = ref.current;
1040 - dispatchPointerDown(target);
1041 - dispatchPointerUp(target, {preventDefault});
927 + const target = createEventTarget(ref.current);
928 + target.pointerdown();
929 + target.pointerup({preventDefault});
930 expect(preventDefault).toBeCalled();
931 expect(onPress).toHaveBeenCalledWith(
932 expect.objectContaining({defaultPrevented: true}),
@@ -1051,17 +939,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
939 const ref = React.createRef();
940
941 const Component = () => {
1054 - const listener = usePressResponder({
1055 - onPress,
1056 - });
942 + const listener = usePressResponder({onPress});
943 return <a href="#" ref={ref} listeners={listener} />;
944 };
945 ReactDOM.render(<Component />, container);
946
947 ['metaKey', 'ctrlKey', 'shiftKey'].forEach(modifierKey => {
1062 - const target = ref.current;
1063 - dispatchPointerDown(target, {[modifierKey]: true});
1064 - dispatchPointerUp(target, {[modifierKey]: true, preventDefault});
948 + const target = createEventTarget(ref.current);
949 + target.pointerdown({[modifierKey]: true});
950 + target.pointerup({[modifierKey]: true, preventDefault});
951 expect(preventDefault).not.toBeCalled();
952 expect(onPress).toHaveBeenCalledWith(
953 expect.objectContaining({defaultPrevented: false}),
@@ -1075,17 +961,14 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
961 const ref = React.createRef();
962
963 const Component = () => {
1078 - const listener = usePressResponder({
1079 - onPress,
1080 - preventDefault: false,
1081 - });
964 + const listener = usePressResponder({onPress, preventDefault: false});
965 return <a href="#" ref={ref} listeners={listener} />;
966 };
967 ReactDOM.render(<Component />, container);
968
1086 - const target = ref.current;
1087 - dispatchPointerDown(target);
1088 - dispatchPointerUp(target, {preventDefault});
969 + const target = createEventTarget(ref.current);
970 + target.pointerdown();
971 + target.pointerup({preventDefault});
972 expect(preventDefault).not.toBeCalled();
973 expect(onPress).toHaveBeenCalledWith(
974 expect.objectContaining({defaultPrevented: false}),
@@ -1098,18 +981,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
981 const ref = React.createRef();
982
983 const Component = () => {
1101 - const listener = usePressResponder({
1102 - onPress,
1103 - preventDefault: false,
1104 - });
984 + const listener = usePressResponder({onPress, preventDefault: false});
985 return <a href="#" ref={ref} listeners={listener} />;
986 };
987 ReactDOM.render(<Component />, container);
988
1109 - const target = ref.current;
1110 - target.dispatchEvent(keydown({key: 'Enter'}));
1111 - target.dispatchEvent(click({preventDefault}));
1112 - target.dispatchEvent(keyup({key: 'Enter'}));
989 + const target = createEventTarget(ref.current);
990 + target.keydown({key: 'Enter'});
991 + target.click({preventDefault});
992 + target.keyup({key: 'Enter'});
993 expect(preventDefault).not.toBeCalled();
994 expect(onPress).toHaveBeenCalledWith(
995 expect.objectContaining({defaultPrevented: false}),
@@ -1123,16 +1003,14 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1003 const ref = React.createRef();
1004
1005 const Component = () => {
1126 - const listener = usePressResponder({
1127 - onPressEnd,
1128 - });
1006 + const listener = usePressResponder({onPressEnd});
1007 return <a href="#" ref={ref} listeners={listener} />;
1008 };
1009 ReactDOM.render(<Component />, container);
1010
1133 - const target = ref.current;
1134 - dispatchPointerDown(target, {pointerType});
1135 - dispatchPointerCancel(target, {pointerType});
1011 + const target = createEventTarget(ref.current);
1012 + target.pointerdown({pointerType});
1013 + target.pointercancel({pointerType});
1014 expect(onPressEnd).toHaveBeenCalledTimes(1);
1015 });
1016 });
@@ -1142,16 +1020,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1020 const ref = React.createRef();
1021
1022 const Component = () => {
1145 - const listener = usePressResponder({
1146 - onPressEnd,
1147 - });
1023 + const listener = usePressResponder({onPressEnd});
1024 return <a href="#" ref={ref} listeners={listener} />;
1025 };
1026 ReactDOM.render(<Component />, container);
1027
1152 - const target = ref.current;
1153 - dispatchPointerDown(target, {pointerType: 'touch'});
1154 - document.dispatchEvent(scroll());
1028 + const target = createEventTarget(ref.current);
1029 + const targetDocument = createEventTarget(document);
1030 + target.pointerdown({pointerType: 'touch'});
1031 + targetDocument.scroll();
1032 expect(onPressEnd).toHaveBeenCalledTimes(1);
1033 });
1034
@@ -1161,9 +1038,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1038 const containerRef = React.createRef();
1039
1040 const Component = () => {
1164 - const listener = usePressResponder({
1165 - onPressEnd,
1166 - });
1041 + const listener = usePressResponder({onPressEnd});
1042 return (
1043 <div ref={containerRef}>
1044 <a ref={ref} listeners={listener} />
@@ -1172,8 +1047,10 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1047 };
1048 ReactDOM.render(<Component />, container);
1049
1175 - dispatchPointerDown(ref.current, {pointerType: 'touch'});
1176 - containerRef.current.dispatchEvent(scroll());
1050 + const target = createEventTarget(ref.current);
1051 + const targetContainer = createEventTarget(containerRef.current);
1052 + target.pointerdown({pointerType: 'touch'});
1053 + targetContainer.scroll();
1054 expect(onPressEnd).toHaveBeenCalledTimes(1);
1055 });
1056
@@ -1183,9 +1060,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1060 const outsideRef = React.createRef();
1061
1062 const Component = () => {
1186 - const listener = usePressResponder({
1187 - onPressEnd,
1188 - });
1063 + const listener = usePressResponder({onPressEnd});
1064 return (
1065 <div>
1066 <a ref={ref} listeners={listener} />
@@ -1195,8 +1070,10 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1070 };
1071 ReactDOM.render(<Component />, container);
1072
1198 - dispatchPointerDown(ref.current);
1199 - outsideRef.current.dispatchEvent(scroll());
1073 + const target = createEventTarget(ref.current);
1074 + const targetOutside = createEventTarget(outsideRef.current);
1075 + target.pointerdown();
1076 + targetOutside.scroll();
1077 expect(onPressEnd).not.toBeCalled();
1078 });
1079
@@ -1208,15 +1085,16 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1085 const ref = React.createRef();
1086
1087 const Component = () => {
1211 - return <button ref={ref} responders={<PressResponder />} />;
1088 + const listener = usePressResponder();
1089 + return <button ref={ref} listeners={listener} />;
1090 };
1091 ReactDOM.render(<Component />, container);
1092
1215 - const target = ref.current;
1216 - dispatchPointerDown(target);
1217 - dispatchPointerMove(target);
1218 - dispatchPointerUp(target);
1219 - dispatchPointerDown(target);
1093 + const target = createEventTarget(ref.current);
1094 + target.pointerdown();
1095 + target.pointermove();
1096 + target.pointerup();
1097 + target.pointerdown();
1098 });
1099
1100 it('should correctly pass through event properties', () => {
@@ -1251,48 +1129,35 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1129 };
1130 ReactDOM.render(<Component />, container);
1131
1254 - const target = ref.current;
1255 - target.getBoundingClientRect = () => ({
1256 - top: 10,
1257 - left: 10,
1258 - bottom: 110,
1259 - right: 110,
1260 - });
1261 - dispatchPointerDown(target, {
1132 + const target = createEventTarget(ref.current);
1133 + target.setBoundingClientRect({x: 10, y: 10, width: 100, height: 100});
1134 + target.pointerdown({
1135 pointerType: 'mouse',
1136 pageX: 15,
1137 pageY: 16,
1265 - screenX: 20,
1266 - screenY: 21,
1267 - clientX: 30,
1268 - clientY: 31,
1138 + x: 30,
1139 + y: 31,
1140 });
1270 - dispatchPointerMove(target, {
1141 + target.pointermove({
1142 pointerType: 'mouse',
1143 pageX: 16,
1144 pageY: 17,
1274 - screenX: 21,
1275 - screenY: 22,
1276 - clientX: 31,
1277 - clientY: 32,
1145 + x: 31,
1146 + y: 32,
1147 });
1279 - dispatchPointerUp(target, {
1148 + target.pointerup({
1149 pointerType: 'mouse',
1150 pageX: 17,
1151 pageY: 18,
1283 - screenX: 22,
1284 - screenY: 23,
1285 - clientX: 32,
1286 - clientY: 33,
1152 + x: 32,
1153 + y: 33,
1154 });
1288 - dispatchPointerDown(target, {
1155 + target.pointerdown({
1156 pointerType: 'mouse',
1157 pageX: 18,
1158 pageY: 19,
1292 - screenX: 23,
1293 - screenY: 24,
1294 - clientX: 33,
1295 - clientY: 34,
1159 + x: 33,
1160 + y: 34,
1161 });
1162 expect(typeof timeStamps[0] === 'number').toBe(true);
1163 expect(eventLog).toEqual([
@@ -1300,8 +1165,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1165 pointerType: 'mouse',
1166 pageX: 15,
1167 pageY: 16,
1303 - screenX: 20,
1304 - screenY: 21,
1168 + screenX: 30,
1169 + screenY: 81,
1170 clientX: 30,
1171 clientY: 31,
1172 target: ref.current,
@@ -1312,8 +1177,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1177 pointerType: 'mouse',
1178 pageX: 16,
1179 pageY: 17,
1315 - screenX: 21,
1316 - screenY: 22,
1180 + screenX: 31,
1181 + screenY: 82,
1182 clientX: 31,
1183 clientY: 32,
1184 target: ref.current,
@@ -1324,8 +1189,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1189 pointerType: 'mouse',
1190 pageX: 17,
1191 pageY: 18,
1327 - screenX: 22,
1328 - screenY: 23,
1192 + screenX: 32,
1193 + screenY: 83,
1194 clientX: 32,
1195 clientY: 33,
1196 target: ref.current,
@@ -1336,8 +1201,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1201 pointerType: 'mouse',
1202 pageX: 17,
1203 pageY: 18,
1339 - screenX: 22,
1340 - screenY: 23,
1204 + screenX: 32,
1205 + screenY: 83,
1206 clientX: 32,
1207 clientY: 33,
1208 target: ref.current,
@@ -1348,8 +1213,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1213 pointerType: 'mouse',
1214 pageX: 18,
1215 pageY: 19,
1351 - screenX: 23,
1352 - screenY: 24,
1216 + screenX: 33,
1217 + screenY: 84,
1218 clientX: 33,
1219 clientY: 34,
1220 target: ref.current,
@@ -1360,6 +1225,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1225 });
1226
1227 if (hasPointerEvents) {
1228 + // TODO(T46067442): move these tests to core
1229 + /*
1230 it('should properly only flush sync once when the event systems are mixed', () => {
1231 const ref = React.createRef();
1232 let renderCounts = 0;
@@ -1557,7 +1424,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1424 expect(ops).toEqual(['Presses: 0, Clicks: 0']);
1425 },
1426 );
1560 -
1427 + */
1428 it('should work correctly with stopPropagation set to true', () => {
1429 const ref = React.createRef();
1430 const pointerDownEvent = jest.fn();
@@ -1569,7 +1436,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1436
1437 container.addEventListener('pointerdown', pointerDownEvent);
1438 ReactDOM.render(<Component />, container);
1572 - dispatchPointerDown(ref.current);
1439 + createEventTarget(ref.current).pointerdown();
1440 container.removeEventListener('pointerdown', pointerDownEvent);
1441 expect(pointerDownEvent).toHaveBeenCalledTimes(0);
1442 });
packages/react-events/src/dom/__tests__/Scroll-test.internal.js
+44 -133
@@ -9,33 +9,31 @@
9
10 'use strict';
11
12 +import {createEventTarget, setPointerEvent} from '../testing-library';
13 +
14 let React;
15 let ReactFeatureFlags;
16 let ReactDOM;
17 let useScrollResponder;
18
17 -const createEvent = (type, data) => {
18 - const event = document.createEvent('CustomEvent');
19 - event.initCustomEvent(type, true, true);
20 - if (data != null) {
21 - Object.entries(data).forEach(([key, value]) => {
22 - event[key] = value;
23 - });
24 - }
25 - return event;
19 +const forcePointerEvents = true;
20 +const table = [[forcePointerEvents], [!forcePointerEvents]];
21 +
22 +const initializeModules = hasPointerEvents => {
23 + setPointerEvent(hasPointerEvents);
24 + jest.resetModules();
25 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
26 + ReactFeatureFlags.enableFlareAPI = true;
27 + React = require('react');
28 + ReactDOM = require('react-dom');
29 + useScrollResponder = require('react-events/scroll').useScrollResponder;
30 };
31
28 -describe('Scroll event responder', () => {
32 +describe.each(table)('Scroll responder', hasPointerEvents => {
33 let container;
34
35 beforeEach(() => {
32 - jest.resetModules();
33 - ReactFeatureFlags = require('shared/ReactFeatureFlags');
34 - ReactFeatureFlags.enableFlareAPI = true;
35 - React = require('react');
36 - ReactDOM = require('react-dom');
37 - useScrollResponder = require('react-events/scroll').useScrollResponder;
38 -
36 + initializeModules(hasPointerEvents);
37 container = document.createElement('div');
38 document.body.appendChild(container);
39 });
@@ -63,7 +61,8 @@ describe('Scroll event responder', () => {
61 });
62
63 it('prevents custom events being dispatched', () => {
66 - ref.current.dispatchEvent(createEvent('scroll'));
64 + const target = createEventTarget(ref.current);
65 + target.scroll();
66 expect(onScroll).not.toBeCalled();
67 });
68 });
@@ -84,129 +83,49 @@ describe('Scroll event responder', () => {
83 });
84
85 describe('is called after "scroll" event', () => {
87 - it('with a mouse pointerType', () => {
88 - ref.current.dispatchEvent(
89 - createEvent('pointerdown', {
90 - pointerType: 'mouse',
91 - }),
92 - );
93 - ref.current.dispatchEvent(createEvent('scroll'));
86 + const pointerTypesTable = hasPointerEvents
87 + ? [['mouse'], ['touch'], ['pen']]
88 + : [['mouse'], ['touch']];
89 + it.each(pointerTypesTable)('with pointerType: %s', pointerType => {
90 + const node = ref.current;
91 + const target = createEventTarget(node);
92 + target.pointerdown({pointerType});
93 + target.scroll();
94 expect(onScroll).toHaveBeenCalledTimes(1);
95 expect(onScroll).toHaveBeenCalledWith(
96 expect.objectContaining({
97 - pointerType: 'mouse',
97 + pointerType,
98 type: 'scroll',
99 direction: '',
100 }),
101 );
102 onScroll.mockReset();
103 - ref.current.scrollTop = -1;
104 - ref.current.dispatchEvent(createEvent('scroll'));
103 + node.scrollTop = -1;
104 + target.scroll();
105 expect(onScroll).toHaveBeenCalledWith(
106 expect.objectContaining({
107 - pointerType: 'mouse',
107 + pointerType,
108 type: 'scroll',
109 direction: 'up',
110 }),
111 );
112 onScroll.mockReset();
113 - ref.current.scrollTop = 1;
114 - ref.current.dispatchEvent(createEvent('scroll'));
113 + node.scrollTop = 1;
114 + target.scroll();
115 expect(onScroll).toHaveBeenCalledWith(
116 expect.objectContaining({
117 - pointerType: 'mouse',
117 + pointerType,
118 type: 'scroll',
119 direction: 'down',
120 }),
121 );
122 });
123
124 - it('with a touch pointerType', () => {
125 - ref.current.dispatchEvent(
126 - createEvent('pointerdown', {
127 - pointerType: 'touch',
128 - }),
129 - );
130 - ref.current.dispatchEvent(createEvent('scroll'));
131 - expect(onScroll).toHaveBeenCalledTimes(1);
132 - expect(onScroll).toHaveBeenCalledWith(
133 - expect.objectContaining({
134 - pointerType: 'touch',
135 - type: 'scroll',
136 - direction: '',
137 - }),
138 - );
139 - onScroll.mockReset();
140 - ref.current.scrollTop = -1;
141 - ref.current.dispatchEvent(createEvent('scroll'));
142 - expect(onScroll).toHaveBeenCalledWith(
143 - expect.objectContaining({
144 - pointerType: 'touch',
145 - type: 'scroll',
146 - direction: 'up',
147 - }),
148 - );
149 - onScroll.mockReset();
150 - ref.current.scrollTop = 1;
151 - ref.current.dispatchEvent(createEvent('scroll'));
152 - expect(onScroll).toHaveBeenCalledWith(
153 - expect.objectContaining({
154 - pointerType: 'touch',
155 - type: 'scroll',
156 - direction: 'down',
157 - }),
158 - );
159 - });
160 -
161 - it('with a pen pointerType', () => {
162 - ref.current.dispatchEvent(
163 - createEvent('pointerdown', {
164 - pointerType: 'pen',
165 - }),
166 - );
167 - ref.current.dispatchEvent(createEvent('scroll'));
168 - expect(onScroll).toHaveBeenCalledTimes(1);
169 - expect(onScroll).toHaveBeenCalledWith(
170 - expect.objectContaining({
171 - pointerType: 'pen',
172 - type: 'scroll',
173 - direction: '',
174 - }),
175 - );
176 - onScroll.mockReset();
177 - ref.current.scrollTop = -1;
178 - ref.current.dispatchEvent(createEvent('scroll'));
179 - expect(onScroll).toHaveBeenCalledWith(
180 - expect.objectContaining({
181 - pointerType: 'pen',
182 - type: 'scroll',
183 - direction: 'up',
184 - }),
185 - );
186 - onScroll.mockReset();
187 - ref.current.scrollTop = 1;
188 - ref.current.dispatchEvent(createEvent('scroll'));
189 - expect(onScroll).toHaveBeenCalledWith(
190 - expect.objectContaining({
191 - pointerType: 'pen',
192 - type: 'scroll',
193 - direction: 'down',
194 - }),
195 - );
196 - });
197 -
198 - it('with a keyboard pointerType', () => {
199 - ref.current.dispatchEvent(
200 - createEvent('keydown', {
201 - key: 'A',
202 - }),
203 - );
204 - ref.current.dispatchEvent(
205 - createEvent('keyup', {
206 - key: 'A',
207 - }),
208 - );
209 - ref.current.dispatchEvent(createEvent('scroll'));
124 + it('with pointerType: keyboard', () => {
125 + const target = createEventTarget(ref.current);
126 + target.keydown({key: 'A'});
127 + target.keyup({key: 'A'});
128 + target.scroll();
129 expect(onScroll).toHaveBeenCalledTimes(1);
130 expect(onScroll).toHaveBeenCalledWith(
131 expect.objectContaining({
@@ -235,13 +154,9 @@ describe('Scroll event responder', () => {
154 });
155
156 it('works as expected with touch events', () => {
238 - ref.current.dispatchEvent(
239 - createEvent('pointerdown', {
240 - pointerType: 'touch',
241 - }),
242 - );
243 - ref.current.dispatchEvent(createEvent('touchstart'));
244 - ref.current.dispatchEvent(createEvent('scroll'));
157 + const target = createEventTarget(ref.current);
158 + target.pointerdown({pointerType: 'touch'});
159 + target.scroll();
160 expect(onScrollDragStart).toHaveBeenCalledTimes(1);
161 expect(onScrollDragStart).toHaveBeenCalledWith(
162 expect.objectContaining({
@@ -269,14 +184,10 @@ describe('Scroll event responder', () => {
184 });
185
186 it('works as expected with touch events', () => {
272 - ref.current.dispatchEvent(
273 - createEvent('pointerdown', {
274 - pointerType: 'touch',
275 - }),
276 - );
277 - ref.current.dispatchEvent(createEvent('touchstart'));
278 - ref.current.dispatchEvent(createEvent('scroll'));
279 - ref.current.dispatchEvent(createEvent('touchend'));
187 + const target = createEventTarget(ref.current);
188 + target.pointerdown({pointerType: 'touch'});
189 + target.scroll();
190 + target.pointerup({pointerType: 'touch'});
191 expect(onScrollDragEnd).toHaveBeenCalledTimes(1);
192 expect(onScrollDragEnd).toHaveBeenCalledWith(
193 expect.objectContaining({
packages/react-events/src/dom/test-utils.js deleted
-481
@@ -1,481 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @emails react-core
8 - */
9 -
10 -'use strict';
11 -
12 -/* eslint-disable no-unused-vars */
13 -
14 -/**
15 - * Change environment support for PointerEvent.
16 - */
17 -
18 -function hasPointerEvent() {
19 - return global != null && global.PointerEvent != null;
20 -}
21 -
22 -function setPointerEvent(bool) {
23 - global.PointerEvent = bool ? function() {} : undefined;
24 -}
25 -
26 -/**
27 - * Change environment host platform.
28 - */
29 -
30 -const platformGetter = jest.spyOn(global.navigator, 'platform', 'get');
31 -
32 -const platform = {
33 - clear() {
34 - platformGetter.mockClear();
35 - },
36 - get() {
37 - return global.navigator.platform === 'MacIntel' ? 'mac' : 'windows';
38 - },
39 - set(name: 'mac' | 'windows') {
40 - switch (name) {
41 - case 'mac': {
42 - platformGetter.mockReturnValue('MacIntel');
43 - break;
44 - }
45 - case 'windows': {
46 - platformGetter.mockReturnValue('Win32');
47 - break;
48 - }
49 - default: {
50 - break;
51 - }
52 - }
53 - },
54 -};
55 -
56 -/**
57 - * Mock native events
58 - */
59 -
60 -function createEvent(type, data = {}) {
61 - const event = document.createEvent('CustomEvent');
62 - event.initCustomEvent(type, true, true);
63 - event.clientX = data.x || 0;
64 - event.clientY = data.y || 0;
65 - event.x = data.x || 0;
66 - event.y = data.y || 0;
67 - if (data != null) {
68 - Object.keys(data).forEach(key => {
69 - const value = data[key];
70 - Object.defineProperty(event, key, {value});
71 - });
72 - }
73 - return event;
74 -}
75 -
76 -function createTouchEvent(type, data = {}, id) {
77 - return createEvent(type, {
78 - changedTouches: [
79 - {
80 - identifier: id,
81 - clientX: data.x || 0,
82 - clientY: data.y || 0,
83 - ...data,
84 - },
85 - ],
86 - targetTouches: [
87 - {
88 - identifier: id,
89 - clientX: 0 || data.x,
90 - clientY: 0 || data.y,
91 - ...data,
92 - },
93 - ],
94 - });
95 -}
96 -
97 -const createKeyboardEvent = (type, data) => {
98 - return new KeyboardEvent(type, {
99 - bubbles: true,
100 - cancelable: true,
101 - ...data,
102 - });
103 -};
104 -
105 -function blur(data) {
106 - return createEvent('blur', data);
107 -}
108 -
109 -function click(data) {
110 - return createEvent('click', data);
111 -}
112 -
113 -function contextmenu(data) {
114 - return createEvent('contextmenu', data);
115 -}
116 -
117 -function dragstart(data) {
118 - return createEvent('dragstart', data);
119 -}
120 -
121 -function focus(data) {
122 - return createEvent('focus', data);
123 -}
124 -
125 -function gotpointercapture(data) {
126 - return createEvent('gotpointercapture', data);
127 -}
128 -
129 -function keydown(data) {
130 - return createEvent('keydown', data);
131 -}
132 -
133 -function keyup(data) {
134 - return createEvent('keyup', data);
135 -}
136 -
137 -function lostpointercapture(data) {
138 - return createEvent('lostpointercapture', data);
139 -}
140 -
141 -function mousedown(data) {
142 - return createEvent('mousedown', data);
143 -}
144 -
145 -function mouseenter(data) {
146 - return createEvent('mouseenter', data);
147 -}
148 -
149 -function mouseleave(data) {
150 - return createEvent('mouseleave', data);
151 -}
152 -
153 -function mousemove(data) {
154 - return createEvent('mousemove', data);
155 -}
156 -
157 -function mouseout(data) {
158 - return createEvent('mouseout', data);
159 -}
160 -
161 -function mouseover(data) {
162 - return createEvent('mouseover', data);
163 -}
164 -
165 -function mouseup(data) {
166 - return createEvent('mouseup', data);
167 -}
168 -
169 -function pointercancel(data) {
170 - return createEvent('pointercancel', data);
171 -}
172 -
173 -function pointerdown(data) {
174 - return createEvent('pointerdown', data);
175 -}
176 -
177 -function pointerenter(data) {
178 - return createEvent('pointerenter', data);
179 -}
180 -
181 -function pointerleave(data) {
182 - return createEvent('pointerleave', data);
183 -}
184 -
185 -function pointermove(data) {
186 - return createEvent('pointermove', data);
187 -}
188 -
189 -function pointerout(data) {
190 - return createEvent('pointerout', data);
191 -}
192 -
193 -function pointerover(data) {
194 - return createEvent('pointerover', data);
195 -}
196 -
197 -function pointerup(data) {
198 - return createEvent('pointerup', data);
199 -}
200 -
201 -function scroll(data) {
202 - return createEvent('scroll', data);
203 -}
204 -
205 -function touchcancel(data, id) {
206 - return createTouchEvent('touchcancel', data, id);
207 -}
208 -
209 -function touchend(data, id) {
210 - return createTouchEvent('touchend', data, id);
211 -}
212 -
213 -function touchmove(data, id) {
214 - return createTouchEvent('touchmove', data, id);
215 -}
216 -
217 -function touchstart(data, id) {
218 - return createTouchEvent('touchstart', data, id);
219 -}
220 -
221 -/**
222 - * Dispatch high-level event sequences
223 - */
224 -
225 -function emptyFunction() {}
226 -
227 -function dispatchLongPressContextMenu(
228 - target,
229 - {preventDefault = emptyFunction} = {},
230 -) {
231 - const dispatch = arg => target.dispatchEvent(arg);
232 - const button = 0;
233 - if (hasPointerEvent()) {
234 - dispatch(pointerdown({button, pointerType: 'touch'}));
235 - }
236 - dispatch(touchstart());
237 - dispatch(contextmenu({button, preventDefault}));
238 -}
239 -
240 -function dispatchRightClickContextMenu(
241 - target,
242 - {preventDefault = emptyFunction} = {},
243 -) {
244 - const dispatch = arg => target.dispatchEvent(arg);
245 - const button = 2;
246 - if (hasPointerEvent()) {
247 - dispatch(pointerdown({button, pointerType: 'mouse'}));
248 - }
249 - dispatch(mousedown({button}));
250 - dispatch(contextmenu({button, preventDefault}));
251 -}
252 -
253 -function dispatchModifiedClickContextMenu(
254 - target,
255 - {preventDefault = emptyFunction} = {},
256 -) {
257 - const dispatch = arg => target.dispatchEvent(arg);
258 - const button = 0;
259 - const ctrlKey = true;
260 - if (hasPointerEvent()) {
261 - dispatch(pointerdown({button, ctrlKey, pointerType: 'mouse'}));
262 - }
263 - dispatch(mousedown({button, ctrlKey}));
264 - if (platform.get() === 'mac') {
265 - dispatch(contextmenu({button, ctrlKey, preventDefault}));
266 - }
267 -}
268 -
269 -function dispatchPointerHoverEnter(target, {relatedTarget, x, y} = {}) {
270 - const dispatch = arg => target.dispatchEvent(arg);
271 - const button = -1;
272 - const pointerType = 'mouse';
273 - const event = {
274 - button,
275 - relatedTarget,
276 - clientX: x,
277 - clientY: y,
278 - pageX: x,
279 - pageY: y,
280 - };
281 - if (hasPointerEvent()) {
282 - dispatch(pointerover({pointerType, ...event}));
283 - dispatch(pointerenter({pointerType, ...event}));
284 - }
285 - dispatch(mouseover(event));
286 - dispatch(mouseenter(event));
287 -}
288 -
289 -function dispatchPointerHoverMove(target, {x, y} = {}) {
290 - const dispatch = arg => target.dispatchEvent(arg);
291 - const button = -1;
292 - const pointerId = 1;
293 - const pointerType = 'mouse';
294 - function dispatchMove() {
295 - const event = {
296 - button,
297 - clientX: x,
298 - clientY: y,
299 - pageX: x,
300 - pageY: y,
301 - };
302 - if (hasPointerEvent()) {
303 - dispatch(pointermove({pointerId, pointerType, ...event}));
304 - }
305 - dispatch(mousemove(event));
306 - }
307 - dispatchMove();
308 -}
309 -
310 -function dispatchPointerHoverExit(target, {relatedTarget, x, y} = {}) {
311 - const dispatch = arg => target.dispatchEvent(arg);
312 - const button = -1;
313 - const pointerType = 'mouse';
314 - const event = {
315 - button,
316 - relatedTarget,
317 - clientX: x,
318 - clientY: y,
319 - pageX: x,
320 - pageY: y,
321 - };
322 - if (hasPointerEvent()) {
323 - dispatch(pointerout({pointerType, ...event}));
324 - dispatch(pointerleave({pointerType, ...event}));
325 - }
326 - dispatch(mouseout(event));
327 - dispatch(mouseleave(event));
328 -}
329 -
330 -function dispatchPointerCancel(target, {pointerType = 'mouse', ...rest} = {}) {
331 - const dispatchEvent = arg => target.dispatchEvent(arg);
332 - if (hasPointerEvent()) {
333 - dispatchEvent(pointercancel({pointerType, ...rest}));
334 - } else {
335 - if (pointerType === 'mouse') {
336 - dispatchEvent(dragstart({...rest}));
337 - } else {
338 - dispatchEvent(touchcancel({...rest}));
339 - }
340 - }
341 -}
342 -
343 -function dispatchPointerDown(
344 - target,
345 - {button = 0, pointerType = 'mouse', ...rest} = {},
346 -) {
347 - const dispatch = arg => target.dispatchEvent(arg);
348 - const pointerId = 1;
349 - const pointerEvent = {button, pointerId, pointerType, ...rest};
350 - const mouseEvent = {button, ...rest};
351 - const touch = {...rest};
352 -
353 - if (pointerType === 'mouse') {
354 - if (hasPointerEvent()) {
355 - dispatch(pointerover(pointerEvent));
356 - dispatch(pointerenter(pointerEvent));
357 - }
358 - dispatch(mouseover(mouseEvent));
359 - dispatch(mouseenter(mouseEvent));
360 - if (hasPointerEvent()) {
361 - dispatch(pointerdown(pointerEvent));
362 - }
363 - dispatch(mousedown(mouseEvent));
364 - if (document.activeElement !== target) {
365 - dispatch(focus());
366 - }
367 - } else {
368 - if (hasPointerEvent()) {
369 - dispatch(pointerover(pointerEvent));
370 - dispatch(pointerenter(pointerEvent));
371 - dispatch(pointerdown(pointerEvent));
372 - }
373 - dispatch(touchstart(touch, pointerId));
374 - if (hasPointerEvent()) {
375 - dispatch(gotpointercapture(pointerEvent));
376 - }
377 - }
378 -}
379 -
380 -function dispatchPointerUp(
381 - target,
382 - {button = 0, pointerType = 'mouse', ...rest} = {},
383 -) {
384 - const dispatch = arg => target.dispatchEvent(arg);
385 - const pointerId = 1;
386 - const pointerEvent = {button, pointerId, pointerType, ...rest};
387 - const mouseEvent = {button, ...rest};
388 - const touch = {...rest};
389 -
390 - if (pointerType === 'mouse') {
391 - if (hasPointerEvent()) {
392 - dispatch(pointerup(pointerEvent));
393 - }
394 - dispatch(mouseup(mouseEvent));
395 - dispatch(click(mouseEvent));
396 - } else {
397 - if (hasPointerEvent()) {
398 - dispatch(pointerup(pointerEvent));
399 - dispatch(lostpointercapture(pointerEvent));
400 - dispatch(pointerout(pointerEvent));
401 - dispatch(pointerleave(pointerEvent));
402 - }
403 - dispatch(touchend(touch, pointerId));
404 - dispatch(mouseover(mouseEvent));
405 - dispatch(mousemove(mouseEvent));
406 - dispatch(mousedown(mouseEvent));
407 - if (document.activeElement !== target) {
408 - dispatch(focus());
409 - }
410 - dispatch(mouseup(mouseEvent));
411 - dispatch(click(mouseEvent));
412 - }
413 -}
414 -
415 -function dispatchPointerMove(
416 - target,
417 - {button = 0, pointerType = 'mouse', ...rest} = {},
418 -) {
419 - const dispatch = arg => target.dispatchEvent(arg);
420 - const pointerId = 1;
421 - const pointerEvent = {
422 - button,
423 - pointerId,
424 - pointerType,
425 - ...rest,
426 - };
427 - const mouseEvent = {
428 - button,
429 - ...rest,
430 - };
431 - const touch = {
432 - ...rest,
433 - };
434 -
435 - if (hasPointerEvent()) {
436 - dispatch(pointermove(pointerEvent));
437 - }
438 - if (pointerType === 'mouse') {
439 - dispatch(mousemove(mouseEvent));
440 - }
441 - if (pointerType === 'touch') {
442 - dispatch(touchmove(touch, pointerId));
443 - }
444 -}
445 -
446 -function dispatchTouchTap(target) {
447 - dispatchPointerDown(target, {pointerType: 'touch'});
448 - dispatchPointerUp(target, {pointerType: 'touch'});
449 -}
450 -
451 -function dispatchMouseTap(target) {
452 - dispatchPointerDown(target, {pointerType: 'mouse'});
453 - dispatchPointerUp(target, {pointerType: 'mouse'});
454 -}
455 -
456 -module.exports = {
457 - blur,
458 - click,
459 - focus,
460 - createEvent,
461 - dispatchLongPressContextMenu,
462 - dispatchRightClickContextMenu,
463 - dispatchModifiedClickContextMenu,
464 - dispatchPointerCancel,
465 - dispatchPointerHoverEnter,
466 - dispatchPointerHoverExit,
467 - dispatchPointerHoverMove,
468 - dispatchPointerMove,
469 - dispatchPointerDown,
470 - dispatchPointerUp,
471 - dispatchTouchTap,
472 - dispatchMouseTap,
473 - keydown,
474 - keyup,
475 - scroll,
476 - pointerdown,
477 - pointerup,
478 - platform,
479 - hasPointerEvent,
480 - setPointerEvent,
481 -};
packages/react-events/src/dom/testing-library/domEnvironment.js new
+51
@@ -0,0 +1,51 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +/**
12 + * Change environment support for PointerEvent.
13 + */
14 +
15 +export function hasPointerEvent() {
16 + return global != null && global.PointerEvent != null;
17 +}
18 +
19 +export function setPointerEvent(bool) {
20 + global.PointerEvent = bool ? function() {} : undefined;
21 +}
22 +
23 +/**
24 + * Change environment host platform.
25 + */
26 +
27 +const platformGetter = jest.spyOn(global.navigator, 'platform', 'get');
28 +
29 +export const platform = {
30 + clear() {
31 + platformGetter.mockClear();
32 + },
33 + get() {
34 + return global.navigator.platform === 'MacIntel' ? 'mac' : 'windows';
35 + },
36 + set(name: 'mac' | 'windows') {
37 + switch (name) {
38 + case 'mac': {
39 + platformGetter.mockReturnValue('MacIntel');
40 + break;
41 + }
42 + case 'windows': {
43 + platformGetter.mockReturnValue('Win32');
44 + break;
45 + }
46 + default: {
47 + break;
48 + }
49 + }
50 + },
51 +};
packages/react-events/src/dom/testing-library/domEventSequences.js new
+175
@@ -0,0 +1,175 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +import * as domEvents from './domEvents';
13 +import {hasPointerEvent, platform} from './domEnvironment';
14 +
15 +function emptyFunction() {}
16 +
17 +function getPointerType(payload) {
18 + let pointerType = 'mouse';
19 + if (payload != null && payload.pointerType != null) {
20 + pointerType = payload.pointerType;
21 + }
22 + return pointerType;
23 +}
24 +
25 +export function contextmenu(
26 + target,
27 + {preventDefault = emptyFunction} = {},
28 + {pointerType = 'mouse', modified} = {},
29 +) {
30 + const dispatch = arg => target.dispatchEvent(arg);
31 + if (pointerType === 'touch') {
32 + const button = 0;
33 + if (hasPointerEvent()) {
34 + dispatch(domEvents.pointerdown({button, pointerType}));
35 + }
36 + dispatch(domEvents.touchstart());
37 + dispatch(domEvents.contextmenu({button, preventDefault}));
38 + } else if (pointerType === 'mouse') {
39 + if (modified === true) {
40 + const button = 0;
41 + const ctrlKey = true;
42 + if (hasPointerEvent()) {
43 + dispatch(domEvents.pointerdown({button, ctrlKey, pointerType}));
44 + }
45 + dispatch(domEvents.mousedown({button, ctrlKey}));
46 + if (platform.get() === 'mac') {
47 + dispatch(domEvents.contextmenu({button, ctrlKey, preventDefault}));
48 + }
49 + } else {
50 + const button = 2;
51 + if (hasPointerEvent()) {
52 + dispatch(domEvents.pointerdown({button, pointerType}));
53 + }
54 + dispatch(domEvents.mousedown({button}));
55 + dispatch(domEvents.contextmenu({button, preventDefault}));
56 + }
57 + }
58 +}
59 +
60 +export function pointercancel(target, payload) {
61 + const dispatchEvent = arg => target.dispatchEvent(arg);
62 + const pointerType = getPointerType(payload);
63 + if (hasPointerEvent()) {
64 + dispatchEvent(domEvents.pointercancel(payload));
65 + } else {
66 + if (pointerType === 'mouse') {
67 + dispatchEvent(domEvents.dragstart(payload));
68 + } else {
69 + dispatchEvent(domEvents.touchcancel(payload));
70 + }
71 + }
72 +}
73 +
74 +export function pointerdown(target, defaultPayload) {
75 + const dispatch = arg => target.dispatchEvent(arg);
76 + const pointerType = getPointerType(defaultPayload);
77 + const payload = {button: 0, buttons: 2, ...defaultPayload};
78 +
79 + if (pointerType === 'mouse') {
80 + if (hasPointerEvent()) {
81 + dispatch(domEvents.pointerover(payload));
82 + dispatch(domEvents.pointerenter(payload));
83 + }
84 + dispatch(domEvents.mouseover(payload));
85 + dispatch(domEvents.mouseenter(payload));
86 + if (hasPointerEvent()) {
87 + dispatch(domEvents.pointerdown(payload));
88 + }
89 + dispatch(domEvents.mousedown(payload));
90 + if (document.activeElement !== target) {
91 + dispatch(domEvents.focus());
92 + }
93 + } else {
94 + if (hasPointerEvent()) {
95 + dispatch(domEvents.pointerover(payload));
96 + dispatch(domEvents.pointerenter(payload));
97 + dispatch(domEvents.pointerdown(payload));
98 + }
99 + dispatch(domEvents.touchstart(payload));
100 + if (hasPointerEvent()) {
101 + dispatch(domEvents.gotpointercapture(payload));
102 + }
103 + }
104 +}
105 +
106 +export function pointerenter(target, payload) {
107 + const dispatch = arg => target.dispatchEvent(arg);
108 + if (hasPointerEvent()) {
109 + dispatch(domEvents.pointerover(payload));
110 + dispatch(domEvents.pointerenter(payload));
111 + }
112 + dispatch(domEvents.mouseover(payload));
113 + dispatch(domEvents.mouseenter(payload));
114 +}
115 +
116 +export function pointerexit(target, payload) {
117 + const dispatch = arg => target.dispatchEvent(arg);
118 + if (hasPointerEvent()) {
119 + dispatch(domEvents.pointerout(payload));
120 + dispatch(domEvents.pointerleave(payload));
121 + }
122 + dispatch(domEvents.mouseout(payload));
123 + dispatch(domEvents.mouseleave(payload));
124 +}
125 +
126 +export function pointerhover(target, payload) {
127 + const dispatch = arg => target.dispatchEvent(arg);
128 + if (hasPointerEvent()) {
129 + dispatch(domEvents.pointermove(payload));
130 + }
131 + dispatch(domEvents.mousemove(payload));
132 +}
133 +
134 +export function pointermove(target, payload) {
135 + const dispatch = arg => target.dispatchEvent(arg);
136 + const pointerType = getPointerType(payload);
137 + if (hasPointerEvent()) {
138 + dispatch(domEvents.pointermove(payload));
139 + }
140 + if (pointerType === 'mouse') {
141 + dispatch(domEvents.mousemove(payload));
142 + } else {
143 + dispatch(domEvents.touchmove(payload));
144 + }
145 +}
146 +
147 +export function pointerup(target, defaultPayload) {
148 + const dispatch = arg => target.dispatchEvent(arg);
149 + const pointerType = getPointerType(defaultPayload);
150 + const payload = {button: 0, buttons: 2, ...defaultPayload};
151 +
152 + if (pointerType === 'mouse') {
153 + if (hasPointerEvent()) {
154 + dispatch(domEvents.pointerup(payload));
155 + }
156 + dispatch(domEvents.mouseup(payload));
157 + dispatch(domEvents.click(payload));
158 + } else {
159 + if (hasPointerEvent()) {
160 + dispatch(domEvents.pointerup(payload));
161 + dispatch(domEvents.lostpointercapture(payload));
162 + dispatch(domEvents.pointerout(payload));
163 + dispatch(domEvents.pointerleave(payload));
164 + }
165 + dispatch(domEvents.touchend(payload));
166 + dispatch(domEvents.mouseover(payload));
167 + dispatch(domEvents.mousemove(payload));
168 + dispatch(domEvents.mousedown(payload));
169 + if (document.activeElement !== target) {
170 + dispatch(domEvents.focus());
171 + }
172 + dispatch(domEvents.mouseup(payload));
173 + dispatch(domEvents.click(payload));
174 + }
175 +}
packages/react-events/src/dom/testing-library/domEvents.js new
+370
@@ -0,0 +1,370 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +/**
13 + * Native event object mocks for higher-level events.
14 + *
15 + * 1. Each event type defines the exact object that it accepts. This ensures
16 + * that no arbitrary properties can be assigned to events, and the properties
17 + * that don't exist on specific event types (e.g., 'pointerType') are not added
18 + * to the respective native event.
19 + *
20 + * 2. Properties that cannot be relied on due to inconsistent browser support (e.g., 'x' and 'y') are not
21 + * added to the native event. Others that shouldn't be arbitrarily customized (e.g., 'screenX')
22 + * are automatically inferred from associated values.
23 + *
24 + * 3. PointerEvent and TouchEvent fields are normalized (e.g., 'rotationAngle' -> 'twist')
25 + */
26 +
27 +function emptyFunction() {}
28 +
29 +function createEvent(type, data = {}) {
30 + const event = document.createEvent('CustomEvent');
31 + event.initCustomEvent(type, true, true);
32 + if (data != null) {
33 + Object.keys(data).forEach(key => {
34 + const value = data[key];
35 + Object.defineProperty(event, key, {value});
36 + });
37 + }
38 + return event;
39 +}
40 +
41 +function createGetModifierState(keyArg, data) {
42 + if (keyArg === 'Alt') {
43 + return data.altKey || false;
44 + }
45 + if (keyArg === 'Control') {
46 + return data.ctrlKey || false;
47 + }
48 + if (keyArg === 'Meta') {
49 + return data.metaKey || false;
50 + }
51 + if (keyArg === 'Shift') {
52 + return data.shiftKey || false;
53 + }
54 +}
55 +
56 +function createPointerEvent(
57 + type,
58 + {
59 + altKey = false,
60 + button = -1,
61 + buttons = 0,
62 + ctrlKey = false,
63 + height,
64 + metaKey = false,
65 + movementX = 0,
66 + movementY = 0,
67 + offsetX = 0,
68 + offsetY = 0,
69 + pageX,
70 + pageY,
71 + pointerId = 1,
72 + pressure = 0,
73 + preventDefault = emptyFunction,
74 + pointerType = 'mouse',
75 + shiftKey = false,
76 + tangentialPressure = 0,
77 + tiltX = 0,
78 + tiltY = 0,
79 + twist = 0,
80 + width,
81 + x = 0,
82 + y = 0,
83 + } = {},
84 +) {
85 + const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
86 +
87 + return createEvent(type, {
88 + altKey,
89 + button,
90 + buttons,
91 + clientX: x,
92 + clientY: y,
93 + ctrlKey,
94 + getModifierState(keyArg) {
95 + createGetModifierState(keyArg, modifierState);
96 + },
97 + height: pointerType === 'mouse' ? 1 : height != null ? height : 11.5,
98 + metaKey,
99 + movementX,
100 + movementY,
101 + offsetX,
102 + offsetY,
103 + pageX: pageX || x,
104 + pageY: pageY || y,
105 + pointerId,
106 + pointerType,
107 + pressure,
108 + preventDefault,
109 + screenX: x,
110 + screenY: y + 50, // arbitrary value to emulate browser chrome, etc
111 + shiftKey,
112 + tangentialPressure,
113 + tiltX,
114 + tiltY,
115 + twist,
116 + width: pointerType === 'mouse' ? 1 : width != null ? width : 11.5,
117 + });
118 +}
119 +
120 +function createKeyboardEvent(
121 + type,
122 + {
123 + altKey = false,
124 + ctrlKey = false,
125 + key = '',
126 + metaKey = false,
127 + preventDefault = emptyFunction,
128 + shiftKey = false,
129 + } = {},
130 +) {
131 + const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
132 +
133 + return createEvent(type, {
134 + altKey,
135 + ctrlKey,
136 + getModifierState(keyArg) {
137 + createGetModifierState(keyArg, modifierState);
138 + },
139 + key,
140 + metaKey,
141 + preventDefault,
142 + shiftKey,
143 + });
144 +}
145 +
146 +function createMouseEvent(
147 + type,
148 + {
149 + altKey = false,
150 + button = -1,
151 + buttons = 0,
152 + ctrlKey = false,
153 + metaKey = false,
154 + movementX = 0,
155 + movementY = 0,
156 + offsetX = 0,
157 + offsetY = 0,
158 + pageX,
159 + pageY,
160 + preventDefault = emptyFunction,
161 + shiftKey = false,
162 + x = 0,
163 + y = 0,
164 + } = {},
165 +) {
166 + const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
167 +
168 + return createEvent(type, {
169 + altKey,
170 + button,
171 + buttons,
172 + clientX: x,
173 + clientY: y,
174 + ctrlKey,
175 + getModifierState(keyArg) {
176 + createGetModifierState(keyArg, modifierState);
177 + },
178 + metaKey,
179 + movementX,
180 + movementY,
181 + offsetX,
182 + offsetY,
183 + pageX: pageX || x,
184 + pageY: pageY || y,
185 + preventDefault,
186 + screenX: x,
187 + screenY: y + 50, // arbitrary value to emulate browser chrome, etc
188 + shiftKey,
189 + });
190 +}
191 +
192 +function createTouchEvent(
193 + type,
194 + {
195 + altKey = false,
196 + ctrlKey = false,
197 + height = 11.5,
198 + metaKey = false,
199 + pageX,
200 + pageY,
201 + pointerId = 1,
202 + preventDefault = emptyFunction,
203 + shiftKey = false,
204 + twist = 0,
205 + width = 11.5,
206 + x = 0,
207 + y = 0,
208 + } = {},
209 +) {
210 + const touch = {
211 + clientX: x,
212 + clientY: y,
213 + force: 1,
214 + identifier: pointerId,
215 + pageX: pageX || x,
216 + pageY: pageY || y,
217 + radiusX: width,
218 + radiusY: height,
219 + rotationAngle: twist,
220 + screenX: x,
221 + screenY: y + 50, // arbitrary value to emulate browser chrome, etc
222 + };
223 +
224 + return createEvent(type, {
225 + altKey,
226 + changedTouches: [touch],
227 + ctrlKey,
228 + metaKey,
229 + preventDefault,
230 + shiftKey,
231 + targetTouches: type !== 'touchend' ? [touch] : null,
232 + touches: [touch],
233 + });
234 +}
235 +
236 +/**
237 + * Mock event objects
238 + */
239 +
240 +export function blur({relatedTarget} = {}) {
241 + return createEvent('blur', {relatedTarget});
242 +}
243 +
244 +export function click(payload) {
245 + return createMouseEvent('click', payload);
246 +}
247 +
248 +export function contextmenu(payload) {
249 + return createMouseEvent('contextmenu', payload);
250 +}
251 +
252 +export function dragstart(payload) {
253 + return createMouseEvent('dragstart', payload);
254 +}
255 +
256 +export function focus({relatedTarget} = {}) {
257 + return createEvent('focus', {relatedTarget});
258 +}
259 +
260 +export function scroll() {
261 + return createEvent('scroll');
262 +}
263 +
264 +/**
265 + * Key events
266 + */
267 +
268 +export function keydown(payload) {
269 + return createKeyboardEvent('keydown', payload);
270 +}
271 +
272 +export function keyup(payload) {
273 + return createKeyboardEvent('keyup', payload);
274 +}
275 +
276 +/**
277 + * Pointer events
278 + */
279 +
280 +export function gotpointercapture(payload) {
281 + return createPointerEvent('gotpointercapture', payload);
282 +}
283 +
284 +export function lostpointercapture(payload) {
285 + return createPointerEvent('lostpointercapture', payload);
286 +}
287 +
288 +export function pointercancel(payload) {
289 + return createPointerEvent('pointercancel', payload);
290 +}
291 +
292 +export function pointerdown(payload) {
293 + return createPointerEvent('pointerdown', {button: 0, buttons: 2, ...payload});
294 +}
295 +
296 +export function pointerenter(payload) {
297 + return createPointerEvent('pointerenter', payload);
298 +}
299 +
300 +export function pointerleave(payload) {
301 + return createPointerEvent('pointerleave', payload);
302 +}
303 +
304 +export function pointermove(payload) {
305 + return createPointerEvent('pointermove', payload);
306 +}
307 +
308 +export function pointerout(payload) {
309 + return createPointerEvent('pointerout', payload);
310 +}
311 +
312 +export function pointerover(payload) {
313 + return createPointerEvent('pointerover', payload);
314 +}
315 +
316 +export function pointerup(payload) {
317 + return createPointerEvent('pointerup', {button: 0, buttons: 2, ...payload});
318 +}
319 +
320 +/**
321 + * Mouse events
322 + */
323 +
324 +export function mousedown(payload) {
325 + return createMouseEvent('mousedown', {button: 0, buttons: 2, ...payload});
326 +}
327 +
328 +export function mouseenter(payload) {
329 + return createMouseEvent('mouseenter', payload);
330 +}
331 +
332 +export function mouseleave(payload) {
333 + return createMouseEvent('mouseleave', payload);
334 +}
335 +
336 +export function mousemove(payload) {
337 + return createMouseEvent('mousemove', payload);
338 +}
339 +
340 +export function mouseout(payload) {
341 + return createMouseEvent('mouseout', payload);
342 +}
343 +
344 +export function mouseover(payload) {
345 + return createMouseEvent('mouseover', payload);
346 +}
347 +
348 +export function mouseup(payload) {
349 + return createMouseEvent('mouseup', {button: 0, buttons: 2, ...payload});
350 +}
351 +
352 +/**
353 + * Touch events
354 + */
355 +
356 +export function touchcancel(payload) {
357 + return createTouchEvent('touchcancel', payload);
358 +}
359 +
360 +export function touchend(payload) {
361 + return createTouchEvent('touchend', payload);
362 +}
363 +
364 +export function touchmove(payload) {
365 + return createTouchEvent('touchmove', payload);
366 +}
367 +
368 +export function touchstart(payload) {
369 + return createTouchEvent('touchstart', payload);
370 +}
packages/react-events/src/dom/testing-library/index.js new
+104
@@ -0,0 +1,104 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +import * as domEvents from './domEvents';
13 +import * as domEventSequences from './domEventSequences';
14 +import {hasPointerEvent, setPointerEvent, platform} from './domEnvironment';
15 +
16 +const createEventTarget = node => ({
17 + node,
18 + /**
19 + * General events abstraction.
20 + */
21 + blur(payload) {
22 + node.dispatchEvent(domEvents.blur(payload));
23 + },
24 + click(payload) {
25 + node.dispatchEvent(domEvents.click(payload));
26 + },
27 + contextmenu(payload, options) {
28 + domEventSequences.contextmenu(node, payload, options);
29 + },
30 + focus(payload) {
31 + node.dispatchEvent(domEvents.focus(payload));
32 + },
33 + /**
34 + * KeyboardEvent abstraction.
35 + */
36 + keydown(payload) {
37 + node.dispatchEvent(domEvents.keydown(payload));
38 + },
39 + keyup(payload) {
40 + node.dispatchEvent(domEvents.keyup(payload));
41 + },
42 + scroll(payload) {
43 + node.dispatchEvent(domEvents.scroll(payload));
44 + },
45 + /**
46 + * PointerEvent abstraction.
47 + * Dispatches the expected sequence of PointerEvents, MouseEvents, and
48 + * TouchEvents for a given environment.
49 + */
50 + // node no longer receives events for the pointer
51 + pointercancel(payload) {
52 + domEventSequences.pointercancel(node, payload);
53 + },
54 + // node dispatches down events
55 + pointerdown(payload) {
56 + domEventSequences.pointerdown(node, payload);
57 + },
58 + // node dispatches move events (pointer is not down)
59 + pointerhover(payload) {
60 + domEventSequences.pointerhover(node, payload);
61 + },
62 + // node dispatches move events (pointer is down)
63 + pointermove(payload) {
64 + domEventSequences.pointermove(node, payload);
65 + },
66 + // node dispatches enter & over events
67 + pointerenter(payload) {
68 + domEventSequences.pointerenter(node, payload);
69 + },
70 + // node dispatches exit & out events
71 + pointerexit(payload) {
72 + domEventSequences.pointerexit(node, payload);
73 + },
74 + // node dispatches up events
75 + pointerup(payload) {
76 + domEventSequences.pointerup(node, payload);
77 + },
78 + /**
79 + * Gesture abstractions.
80 + * Helpers for event sequences expected in a gesture.
81 + * target.tap({ pointerType: 'touch' })
82 + */
83 + tap(payload) {
84 + domEventSequences.pointerdown(payload);
85 + domEventSequences.pointerup(payload);
86 + },
87 + /**
88 + * Utilities
89 + */
90 + setBoundingClientRect({x, y, width, height}) {
91 + node.getBoundingClientRect = function() {
92 + return {
93 + width,
94 + height,
95 + left: x,
96 + right: x + width,
97 + top: y,
98 + bottom: y + height,
99 + };
100 + };
101 + },
102 +});
103 +
104 +export {createEventTarget, platform, hasPointerEvent, setPointerEvent};