[react-events] Improve mock event object accuracy (#16590)
* Better simulation for pointercancel * Fix pressure values for different pointers * Add describe/test helpers for pointer events
Nicolas Gallagher committed
Aug 27, 2019 at 15:45 UTC
cb15f18dc1a3a28e67c8fe1101e55b5033d12e96
4 files changed
+158
-67
packages/react-events/src/dom/testing-library/domEnvironment.js
+6
-1
@@ -13,12 +13,17 @@
13
* Change environment support for PointerEvent.
14
*/
15
16
+const emptyFunction = function() {};
17
+
18
export function hasPointerEvent() {
19
return global != null && global.PointerEvent != null;
20
}
21
22
export function setPointerEvent(bool) {
21
- global.PointerEvent = bool ? function() {} : undefined;
23
+ const mock = bool ? emptyFunction : undefined;
24
+ global.PointerEvent = mock;
25
+ global.HTMLElement.prototype.setPointerCapture = mock;
26
+ global.HTMLElement.prototype.releasePointerCapture = mock;
27
}
28
29
/**
packages/react-events/src/dom/testing-library/domEventSequences.js
+11
-5
@@ -138,12 +138,18 @@ export function pointermove(target, payload) {
138
const dispatch = arg => target.dispatchEvent(arg);
139
const pointerType = getPointerType(payload);
140
if (hasPointerEvent()) {
141
- dispatch(domEvents.pointermove(payload));
142
- }
143
- if (pointerType === 'mouse') {
144
- dispatch(domEvents.mousemove(payload));
141
+ dispatch(
142
+ domEvents.pointermove({
143
+ pressure: pointerType === 'touch' ? 1 : 0.5,
144
+ ...payload,
145
+ }),
146
+ );
147
} else {
146
- dispatch(domEvents.touchmove(payload));
148
+ if (pointerType === 'mouse') {
149
+ dispatch(domEvents.mousemove(payload));
150
+ } else {
151
+ dispatch(domEvents.touchmove(payload));
152
+ }
153
}
154
}
155
packages/react-events/src/dom/testing-library/domEvents.js
+116
-61
@@ -64,6 +64,7 @@ function createPointerEvent(
64
altKey = false,
65
buttons = buttonsType.none,
66
ctrlKey = false,
67
+ detail = 1,
68
height,
69
metaKey = false,
70
movementX = 0,
@@ -76,6 +77,8 @@ function createPointerEvent(
77
pressure = 0,
78
preventDefault = emptyFunction,
79
pointerType = 'mouse',
80
+ screenX,
81
+ screenY,
82
shiftKey = false,
83
tangentialPressure = 0,
84
tiltX = 0,
@@ -87,6 +90,7 @@ function createPointerEvent(
90
} = {},
91
) {
92
const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
93
+ const isMouse = pointerType === 'mouse';
94
95
return createEvent(type, {
96
altKey,
@@ -94,15 +98,11 @@ function createPointerEvent(
98
clientX: x,
99
clientY: y,
100
ctrlKey,
101
+ detail,
102
getModifierState(keyArg) {
103
createGetModifierState(keyArg, modifierState);
104
},
100
- height:
101
- pointerType === 'mouse'
102
- ? 1
103
- : height != null
104
- ? height
105
- : defaultPointerSize,
105
+ height: isMouse ? 1 : height != null ? height : defaultPointerSize,
106
metaKey,
107
movementX,
108
movementY,
@@ -114,15 +114,16 @@ function createPointerEvent(
114
pointerType,
115
pressure,
116
preventDefault,
117
- screenX: x,
118
- screenY: y + defaultBrowserChromeSize,
117
+ releasePointerCapture: emptyFunction,
118
+ screenX: screenX === 0 ? screenX : x,
119
+ screenY: screenY === 0 ? screenY : y + defaultBrowserChromeSize,
120
+ setPointerCapture: emptyFunction,
121
shiftKey,
122
tangentialPressure,
123
tiltX,
124
tiltY,
125
twist,
124
- width:
125
- pointerType === 'mouse' ? 1 : width != null ? width : defaultPointerSize,
126
+ width: isMouse ? 1 : width != null ? width : defaultPointerSize,
127
});
128
}
129
@@ -158,6 +159,7 @@ function createMouseEvent(
159
altKey = false,
160
buttons = buttonsType.none,
161
ctrlKey = false,
162
+ detail = 1,
163
metaKey = false,
164
movementX = 0,
165
movementY = 0,
@@ -166,81 +168,107 @@ function createMouseEvent(
168
pageX,
169
pageY,
170
preventDefault = emptyFunction,
171
+ screenX,
172
+ screenY,
173
shiftKey = false,
174
x = 0,
175
y = 0,
176
} = {},
173
- virtual = false,
177
) {
178
const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
179
180
return createEvent(type, {
181
altKey,
182
buttons,
180
- clientX: virtual ? 0 : x,
181
- clientY: virtual ? 0 : y,
183
+ clientX: x,
184
+ clientY: y,
185
ctrlKey,
183
- detail: virtual ? 0 : 1,
186
+ detail,
187
getModifierState(keyArg) {
188
createGetModifierState(keyArg, modifierState);
189
},
190
metaKey,
188
- movementX: virtual ? 0 : movementX,
189
- movementY: virtual ? 0 : movementY,
190
- offsetX: virtual ? 0 : offsetX,
191
- offsetY: virtual ? 0 : offsetY,
192
- pageX: virtual ? 0 : pageX || x,
193
- pageY: virtual ? 0 : pageY || y,
191
+ movementX,
192
+ movementY,
193
+ offsetX,
194
+ offsetY,
195
+ pageX: pageX || x,
196
+ pageY: pageY || y,
197
preventDefault,
195
- screenX: virtual ? 0 : x,
196
- screenY: virtual ? 0 : y + defaultBrowserChromeSize,
198
+ screenX: screenX === 0 ? screenX : x,
199
+ screenY: screenY === 0 ? screenY : y + defaultBrowserChromeSize,
200
shiftKey,
201
});
202
}
203
201
-function createTouchEvent(
202
- type,
203
- {
204
- altKey = false,
205
- ctrlKey = false,
206
- height = defaultPointerSize,
207
- metaKey = false,
208
- pageX,
209
- pageY,
210
- pointerId = 1,
211
- preventDefault = emptyFunction,
212
- shiftKey = false,
213
- twist = 0,
214
- width = defaultPointerSize,
215
- x = 0,
216
- y = 0,
217
- } = {},
218
-) {
219
- const touch = {
220
- clientX: x,
221
- clientY: y,
222
- force: 1,
223
- identifier: pointerId,
224
- pageX: pageX || x,
225
- pageY: pageY || y,
226
- radiusX: width / 2,
227
- radiusY: height / 2,
228
- rotationAngle: twist,
229
- screenX: x,
230
- screenY: y + defaultBrowserChromeSize,
231
- };
204
+function createTouchEvent(type, payload) {
205
+ const touchesPayload = Array.isArray(payload) ? payload : [payload];
206
+ const firstTouch = touchesPayload[0];
207
+ let altKey = false;
208
+ let ctrlKey = false;
209
+ let metaKey = false;
210
+ let preventDefault = emptyFunction;
211
+ let shiftKey = false;
212
+ if (firstTouch != null) {
213
+ if (firstTouch.altKey != null) {
214
+ altKey = firstTouch.altKey;
215
+ }
216
+ if (firstTouch.ctrlKey != null) {
217
+ ctrlKey = firstTouch.ctrlKey;
218
+ }
219
+ if (firstTouch.metaKey != null) {
220
+ metaKey = firstTouch.metaKey;
221
+ }
222
+ if (firstTouch.preventDefault != null) {
223
+ preventDefault = firstTouch.preventDefault;
224
+ }
225
+ if (firstTouch.shiftKey != null) {
226
+ shiftKey = firstTouch.shiftKey;
227
+ }
228
+ }
229
+
230
+ const touches = touchesPayload.map(
231
+ ({
232
+ height = defaultPointerSize,
233
+ pageX,
234
+ pageY,
235
+ pointerId = 1,
236
+ twist = 0,
237
+ width = defaultPointerSize,
238
+ x = 0,
239
+ y = 0,
240
+ } = {}) => {
241
+ return {
242
+ clientX: x,
243
+ clientY: y,
244
+ force: 1,
245
+ identifier: pointerId,
246
+ pageX: pageX || x,
247
+ pageY: pageY || y,
248
+ radiusX: width / 2,
249
+ radiusY: height / 2,
250
+ rotationAngle: twist,
251
+ screenX: x,
252
+ screenY: y + defaultBrowserChromeSize,
253
+ };
254
+ },
255
+ );
256
233
- const activeTouch = type !== 'touchend' ? [touch] : null;
257
+ const activeTouches = type !== 'touchend' ? touches : null;
258
259
return createEvent(type, {
260
altKey,
237
- changedTouches: [touch],
261
+ changedTouches: touches,
262
ctrlKey,
263
+ detail: 0,
264
metaKey,
265
preventDefault,
266
shiftKey,
242
- targetTouches: activeTouch,
243
- touches: activeTouch,
267
+ sourceCapabilities: {
268
+ firesTouchEvents: true,
269
+ },
270
+ targetTouches: activeTouches,
271
+ touches: activeTouches,
272
});
273
}
274
@@ -253,11 +281,24 @@ export function blur({relatedTarget} = {}) {
281
}
282
283
export function click(payload) {
256
- return createMouseEvent('click', payload, false);
284
+ return createMouseEvent('click', payload);
285
}
286
287
export function virtualclick(payload) {
260
- return createMouseEvent('click', payload, true);
288
+ return createMouseEvent('click', {
289
+ ...payload,
290
+ buttons: 0,
291
+ detail: 0,
292
+ height: 1,
293
+ pageX: 0,
294
+ pageY: 0,
295
+ pressure: 0,
296
+ screenX: 0,
297
+ screenY: 0,
298
+ width: 1,
299
+ x: 0,
300
+ y: 0,
301
+ });
302
}
303
304
export function contextmenu(payload) {
@@ -301,11 +342,24 @@ export function lostpointercapture(payload) {
342
}
343
344
export function pointercancel(payload) {
304
- return createPointerEvent('pointercancel', payload);
345
+ return createPointerEvent('pointercancel', {
346
+ ...payload,
347
+ buttons: 0,
348
+ detail: 0,
349
+ height: 1,
350
+ pageX: 0,
351
+ pageY: 0,
352
+ pressure: 0,
353
+ screenX: 0,
354
+ screenY: 0,
355
+ width: 1,
356
+ x: 0,
357
+ y: 0,
358
+ });
359
}
360
361
export function pointerdown(payload) {
308
- const isTouch = payload != null && payload.pointerType === 'mouse';
362
+ const isTouch = payload != null && payload.pointerType === 'touch';
363
return createPointerEvent('pointerdown', {
364
buttons: buttonsType.primary,
365
pressure: isTouch ? 1 : 0.5,
@@ -337,6 +391,7 @@ export function pointerup(payload) {
391
return createPointerEvent('pointerup', {
392
...payload,
393
buttons: buttonsType.none,
394
+ pressure: 0,
395
});
396
}
397
packages/react-events/src/dom/testing-library/index.js
+25
@@ -109,10 +109,35 @@ const createEventTarget = node => ({
109
},
110
});
111
112
+function describeWithPointerEvent(message, describeFn) {
113
+ const pointerEvent = 'PointerEvent';
114
+ const fallback = 'MouseEvent/TouchEvent';
115
+ describe.each`
116
+ value | name
117
+ ${true} | ${pointerEvent}
118
+ ${false} | ${fallback}
119
+ `(`${message}: $name`, entry => {
120
+ const hasPointerEvents = entry.value;
121
+ setPointerEvent(hasPointerEvents);
122
+ describeFn(hasPointerEvents);
123
+ });
124
+}
125
+
126
+function testWithPointerType(message, testFn) {
127
+ const table = hasPointerEvent()
128
+ ? ['mouse', 'touch', 'pen']
129
+ : ['mouse', 'touch'];
130
+ test.each(table)(`${message}: %s`, pointerType => {
131
+ testFn(pointerType);
132
+ });
133
+}
134
+
135
export {
136
buttonsType,
137
createEventTarget,
138
+ describeWithPointerEvent,
139
platform,
140
hasPointerEvent,
141
setPointerEvent,
142
+ testWithPointerType,
143
};