mirror of
https://github.com/zebrajr/react.git
synced 2025-12-07 00:20:28 +01:00
Moves the unit testing library for events into the `packages` directory so it can more easily be used in tests for other react packages, and mirrored internally to help with testing of event hooks we prototype in www.
362 lines
9.7 KiB
JavaScript
362 lines
9.7 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @emails react-core
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import {
|
|
buttonType,
|
|
buttonsType,
|
|
defaultPointerId,
|
|
defaultPointerSize,
|
|
defaultBrowserChromeSize,
|
|
} from './constants';
|
|
import * as domEvents from './domEvents';
|
|
import {hasPointerEvent, platform} from './domEnvironment';
|
|
import * as touchStore from './touchStore';
|
|
|
|
/**
|
|
* Converts a PointerEvent payload to a Touch
|
|
*/
|
|
function createTouch(target, payload) {
|
|
const {
|
|
height = defaultPointerSize,
|
|
pageX,
|
|
pageY,
|
|
pointerId,
|
|
pressure = 1,
|
|
twist = 0,
|
|
width = defaultPointerSize,
|
|
x = 0,
|
|
y = 0,
|
|
} = payload;
|
|
|
|
return {
|
|
clientX: x,
|
|
clientY: y,
|
|
force: pressure,
|
|
identifier: pointerId,
|
|
pageX: pageX || x,
|
|
pageY: pageY || y,
|
|
radiusX: width / 2,
|
|
radiusY: height / 2,
|
|
rotationAngle: twist,
|
|
target,
|
|
screenX: x,
|
|
screenY: y + defaultBrowserChromeSize,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Converts a PointerEvent to a TouchEvent
|
|
*/
|
|
function createTouchEventPayload(target, touch, payload) {
|
|
const {
|
|
altKey = false,
|
|
ctrlKey = false,
|
|
metaKey = false,
|
|
preventDefault,
|
|
shiftKey = false,
|
|
timeStamp,
|
|
} = payload;
|
|
|
|
return {
|
|
altKey,
|
|
changedTouches: [touch],
|
|
ctrlKey,
|
|
metaKey,
|
|
preventDefault,
|
|
shiftKey,
|
|
targetTouches: touchStore.getTargetTouches(target),
|
|
timeStamp,
|
|
touches: touchStore.getTouches(),
|
|
};
|
|
}
|
|
|
|
function getPointerType(payload) {
|
|
let pointerType = 'mouse';
|
|
if (payload != null && payload.pointerType != null) {
|
|
pointerType = payload.pointerType;
|
|
}
|
|
return pointerType;
|
|
}
|
|
|
|
/**
|
|
* Pointer events sequences.
|
|
*
|
|
* Creates representative browser event sequences for high-level gestures based on pointers.
|
|
* This allows unit tests to be written in terms of simple pointer interactions while testing
|
|
* that the responses to those interactions account for the complex sequence of events that
|
|
* browsers produce as a result.
|
|
*
|
|
* Every time a new pointer touches the surface a 'touchstart' event should be dispatched.
|
|
* - 'changedTouches' contains the new touch.
|
|
* - 'targetTouches' contains all the active pointers for the target.
|
|
* - 'touches' contains all the active pointers on the surface.
|
|
*
|
|
* Every time an existing pointer moves a 'touchmove' event should be dispatched.
|
|
* - 'changedTouches' contains the updated touch.
|
|
*
|
|
* Every time an existing pointer leaves the surface a 'touchend' event should be dispatched.
|
|
* - 'changedTouches' contains the released touch.
|
|
* - 'targetTouches' contains any of the remaining active pointers for the target.
|
|
*/
|
|
|
|
export function contextmenu(
|
|
target,
|
|
defaultPayload,
|
|
{pointerType = 'mouse', modified} = {},
|
|
) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
pointerType,
|
|
...defaultPayload,
|
|
};
|
|
|
|
const preventDefault = payload.preventDefault;
|
|
|
|
if (pointerType === 'touch') {
|
|
if (hasPointerEvent()) {
|
|
dispatch(
|
|
domEvents.pointerdown({
|
|
...payload,
|
|
button: buttonType.primary,
|
|
buttons: buttonsType.primary,
|
|
}),
|
|
);
|
|
}
|
|
const touch = createTouch(target, payload);
|
|
touchStore.addTouch(touch);
|
|
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
|
dispatch(domEvents.touchstart(touchEventPayload));
|
|
dispatch(
|
|
domEvents.contextmenu({
|
|
button: buttonType.primary,
|
|
buttons: buttonsType.none,
|
|
preventDefault,
|
|
}),
|
|
);
|
|
touchStore.removeTouch(touch);
|
|
} else if (pointerType === 'mouse') {
|
|
if (modified === true) {
|
|
const button = buttonType.primary;
|
|
const buttons = buttonsType.primary;
|
|
const ctrlKey = true;
|
|
if (hasPointerEvent()) {
|
|
dispatch(
|
|
domEvents.pointerdown({button, buttons, ctrlKey, pointerType}),
|
|
);
|
|
}
|
|
dispatch(domEvents.mousedown({button, buttons, ctrlKey}));
|
|
if (platform.get() === 'mac') {
|
|
dispatch(
|
|
domEvents.contextmenu({button, buttons, ctrlKey, preventDefault}),
|
|
);
|
|
}
|
|
} else {
|
|
const button = buttonType.secondary;
|
|
const buttons = buttonsType.secondary;
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerdown({button, buttons, pointerType}));
|
|
}
|
|
dispatch(domEvents.mousedown({button, buttons}));
|
|
dispatch(domEvents.contextmenu({button, buttons, preventDefault}));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function pointercancel(target, defaultPayload) {
|
|
const dispatchEvent = arg => target.dispatchEvent(arg);
|
|
const pointerType = getPointerType(defaultPayload);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
pointerType,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (hasPointerEvent()) {
|
|
dispatchEvent(domEvents.pointercancel(payload));
|
|
} else {
|
|
if (pointerType === 'mouse') {
|
|
dispatchEvent(domEvents.dragstart(payload));
|
|
} else {
|
|
const touch = createTouch(target, payload);
|
|
touchStore.removeTouch(touch);
|
|
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
|
dispatchEvent(domEvents.touchcancel(touchEventPayload));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function pointerdown(target, defaultPayload) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
const pointerType = getPointerType(defaultPayload);
|
|
|
|
const payload = {
|
|
button: buttonType.primary,
|
|
buttons: buttonsType.primary,
|
|
pointerId: defaultPointerId,
|
|
pointerType,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (pointerType === 'mouse') {
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerover(payload));
|
|
dispatch(domEvents.pointerenter(payload));
|
|
}
|
|
dispatch(domEvents.mouseover(payload));
|
|
dispatch(domEvents.mouseenter(payload));
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerdown(payload));
|
|
}
|
|
dispatch(domEvents.mousedown(payload));
|
|
if (document.activeElement !== target) {
|
|
dispatch(domEvents.focus());
|
|
}
|
|
} else {
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerover(payload));
|
|
dispatch(domEvents.pointerenter(payload));
|
|
dispatch(domEvents.pointerdown(payload));
|
|
}
|
|
const touch = createTouch(target, payload);
|
|
touchStore.addTouch(touch);
|
|
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
|
dispatch(domEvents.touchstart(touchEventPayload));
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.gotpointercapture(payload));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function pointerenter(target, defaultPayload) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerover(payload));
|
|
dispatch(domEvents.pointerenter(payload));
|
|
}
|
|
dispatch(domEvents.mouseover(payload));
|
|
dispatch(domEvents.mouseenter(payload));
|
|
}
|
|
|
|
export function pointerexit(target, defaultPayload) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerout(payload));
|
|
dispatch(domEvents.pointerleave(payload));
|
|
}
|
|
dispatch(domEvents.mouseout(payload));
|
|
dispatch(domEvents.mouseleave(payload));
|
|
}
|
|
|
|
export function pointerhover(target, defaultPayload) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointermove(payload));
|
|
}
|
|
dispatch(domEvents.mousemove(payload));
|
|
}
|
|
|
|
export function pointermove(target, defaultPayload) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
const pointerType = getPointerType(defaultPayload);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
pointerType,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (hasPointerEvent()) {
|
|
dispatch(
|
|
domEvents.pointermove({
|
|
pressure: pointerType === 'touch' ? 1 : 0.5,
|
|
...payload,
|
|
}),
|
|
);
|
|
} else {
|
|
if (pointerType === 'mouse') {
|
|
dispatch(domEvents.mousemove(payload));
|
|
} else {
|
|
const touch = createTouch(target, payload);
|
|
touchStore.updateTouch(touch);
|
|
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
|
dispatch(domEvents.touchmove(touchEventPayload));
|
|
}
|
|
}
|
|
}
|
|
|
|
export function pointerup(target, defaultPayload) {
|
|
const dispatch = arg => target.dispatchEvent(arg);
|
|
const pointerType = getPointerType(defaultPayload);
|
|
|
|
const payload = {
|
|
pointerId: defaultPointerId,
|
|
pointerType,
|
|
...defaultPayload,
|
|
};
|
|
|
|
if (pointerType === 'mouse') {
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerup(payload));
|
|
}
|
|
dispatch(domEvents.mouseup(payload));
|
|
dispatch(domEvents.click(payload));
|
|
} else {
|
|
if (hasPointerEvent()) {
|
|
dispatch(domEvents.pointerup(payload));
|
|
dispatch(domEvents.lostpointercapture(payload));
|
|
dispatch(domEvents.pointerout(payload));
|
|
dispatch(domEvents.pointerleave(payload));
|
|
}
|
|
const touch = createTouch(target, payload);
|
|
touchStore.removeTouch(touch);
|
|
const touchEventPayload = createTouchEventPayload(target, touch, payload);
|
|
dispatch(domEvents.touchend(touchEventPayload));
|
|
dispatch(domEvents.mouseover(payload));
|
|
dispatch(domEvents.mousemove(payload));
|
|
dispatch(domEvents.mousedown(payload));
|
|
if (document.activeElement !== target) {
|
|
dispatch(domEvents.focus());
|
|
}
|
|
dispatch(domEvents.mouseup(payload));
|
|
dispatch(domEvents.click(payload));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function should be called after each test to ensure the touchStore is cleared
|
|
* in cases where the mock pointers weren't released before the test completed
|
|
* (e.g., a test failed or ran a partial gesture).
|
|
*/
|
|
export function resetActivePointers() {
|
|
touchStore.clear();
|
|
}
|