Create packages/dom-event-testing-library (#17660)
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.
Nicolas Gallagher committed
Dec 19, 2019 at 16:51 UTC
72592310a84ac2cfb132569de52b4c598494b203
25 files changed
+119
-18
.eslintrc.js
+1
@@ -141,6 +141,7 @@ module.exports = {
141
'**/__tests__/**/*.js',
142
'scripts/**/*.js',
143
'packages/*/npm/**/*.js',
144
+ 'packages/dom-event-testing-library/**/*.js',
145
'packages/react-devtools*/**/*.js'
146
],
147
rules: {
packages/dom-event-testing-library/README.md
new
+98
@@ -0,0 +1,98 @@
1
+# `dom-event-testing-library`
2
+
3
+A library for unit testing events via high-level interactions, e.g., `pointerdown`,
4
+that produce realistic and complete DOM event sequences.
5
+
6
+There are number of challenges involved in unit testing modules that work with
7
+DOM events.
8
+
9
+1. Gesture recognizers may need to support environments with and without support for
10
+ the `PointerEvent` API.
11
+2. Gesture recognizers may need to support various user interaction modes including
12
+ mouse, touch, and pen use.
13
+3. Gesture recognizers must account for the actual event sequences browsers produce
14
+ (e.g., emulated touch and mouse events.)
15
+4. Gesture recognizers must work with "virtual" events produced by tools like
16
+ screen-readers.
17
+
18
+Writing unit tests to cover all these scenarios is tedious and error prone. This
19
+event testing library is designed to solve these issues by allowing developers to
20
+more easily dispatch events in unit tests, and to more reliably test pointer
21
+interactions using a high-level API based on `PointerEvent`. Here's a basic example:
22
+
23
+```js
24
+import {
25
+ describeWithPointerEvent,
26
+ testWithPointerType,
27
+ createEventTarget,
28
+ setPointerEvent,
29
+ resetActivePointers
30
+} from 'dom-event-testing-library';
31
+
32
+describeWithPointerEvent('useTap', hasPointerEvent => {
33
+ beforeEach(() => {
34
+ // basic PointerEvent mock
35
+ setPointerEvent(hasPointerEvent);
36
+ });
37
+
38
+ afterEach(() => {
39
+ // clear active pointers between test runs
40
+ resetActivePointers();
41
+ });
42
+
43
+ // test all the pointer types supported by the environment
44
+ testWithPointerType('pointer down', pointerType => {
45
+ const ref = createRef(null);
46
+ const onTapStart = jest.fn();
47
+ render(() => {
48
+ useTap(ref, { onTapStart });
49
+ return <div ref={ref} />
50
+ });
51
+
52
+ // create an event target
53
+ const target = createEventTarget(ref.current);
54
+ // dispatch high-level pointer event
55
+ target.pointerdown({ pointerType });
56
+
57
+ expect(onTapStart).toBeCalled();
58
+ });
59
+});
60
+```
61
+
62
+This tests the interaction in multiple scenarios. In each case, a realistic DOM
63
+event sequence–with complete mock events–is produced. When running in a mock
64
+environment without the `PointerEvent` API, the test runs for both `mouse` and
65
+`touch` pointer types. When `touch` is the pointer type it produces emulated mouse
66
+events. When running in a mock environment with the `PointerEvent` API, the test
67
+runs for `mouse`, `touch`, and `pen` pointer types.
68
+
69
+It's important to cover all these scenarios because it's very easy to introduce
70
+bugs – e.g., double calling of callbacks – if not accounting for emulated mouse
71
+events, differences in target capturing between `touch` and `mouse` pointers, and
72
+the different semantics of `button` across event APIs.
73
+
74
+Default values are provided for the expected native events properties. They can
75
+also be customized as needed in a test.
76
+
77
+```js
78
+target.pointerdown({
79
+ button: 0,
80
+ buttons: 1,
81
+ pageX: 10,
82
+ pageY: 10,
83
+ pointerType,
84
+ // NOTE: use x,y instead of clientX,clientY
85
+ x: 10,
86
+ y: 10
87
+});
88
+```
89
+
90
+Tests that dispatch multiple pointer events will dispatch multi-touch native events
91
+on the target.
92
+
93
+```js
94
+// first pointer is active
95
+target.pointerdown({pointerId: 1, pointerType});
96
+// second pointer is active
97
+target.pointerdown({pointerId: 2, pointerType});
98
+```
packages/dom-event-testing-library/constants.js
renamed
packages/dom-event-testing-library/domEnvironment.js
renamed
packages/dom-event-testing-library/domEventSequences.js
renamed
packages/dom-event-testing-library/domEvents.js
renamed
packages/dom-event-testing-library/index.js
renamed
packages/dom-event-testing-library/package.json
new
+5
@@ -0,0 +1,5 @@
1
+{
2
+ "private": true,
3
+ "name": "dom-event-testing-library",
4
+ "version": "0.0.0"
5
+}
packages/dom-event-testing-library/testHelpers.js
renamed
packages/dom-event-testing-library/touchStore.js
renamed
-3
@@ -25,7 +25,6 @@ export function addTouch(touch) {
25
}
26
if (activeTouches.get(target).get(identifier)) {
27
// Do not allow existing touches to be overwritten
28
- // eslint-disable-next-line react-internal/no-production-logging
28
console.error(
29
'Touch with identifier %s already exists. Did not record touch start.',
30
identifier,
@@ -41,7 +40,6 @@ export function updateTouch(touch) {
40
if (activeTouches.get(target) != null) {
41
activeTouches.get(target).set(identifier, touch);
42
} else {
44
- // eslint-disable-next-line react-internal/no-production-logging
43
console.error(
44
'Touch with identifier %s does not exist. Cannot record touch move without a touch start.',
45
identifier,
@@ -56,7 +54,6 @@ export function removeTouch(touch) {
54
if (activeTouches.get(target).has(identifier)) {
55
activeTouches.get(target).delete(identifier);
56
} else {
59
- // eslint-disable-next-line react-internal/no-production-logging
57
console.error(
58
'Touch with identifier %s does not exist. Cannot record touch end without a touch start.',
59
identifier,
packages/react-interactions/accessibility/src/__tests__/FocusContain-test.internal.js
+1
-1
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
10
+import {createEventTarget} from 'dom-event-testing-library';
11
12
let React;
13
let ReactFeatureFlags;
packages/react-interactions/accessibility/src/__tests__/FocusGroup-test.internal.js
+1
-1
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
10
+import {createEventTarget} from 'dom-event-testing-library';
11
import {emulateBrowserTab} from '../shared/emulateBrowserTab';
12
13
let React;
packages/react-interactions/accessibility/src/__tests__/FocusTable-test.internal.js
+1
-1
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
10
+import {createEventTarget} from 'dom-event-testing-library';
11
import {emulateBrowserTab} from '../shared/emulateBrowserTab';
12
13
let React;
packages/react-interactions/accessibility/src/shared/emulateBrowserTab.js
+1
-1
@@ -9,7 +9,7 @@
9
10
'use strict';
11
12
-import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
12
+import {createEventTarget} from 'dom-event-testing-library';
13
14
// This function is used by the a11y modules for testing
15
export function emulateBrowserTab(backwards: boolean): void {
packages/react-interactions/events/src/dom/__tests__/ContextMenu-test.internal.js
+1
-1
@@ -14,7 +14,7 @@ import {
14
createEventTarget,
15
platform,
16
setPointerEvent,
17
-} from '../event-testing-library';
17
+} from 'dom-event-testing-library';
18
19
let React;
20
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/Focus-test.internal.js
+1
-1
@@ -13,7 +13,7 @@ import {
13
createEventTarget,
14
setPointerEvent,
15
platform,
16
-} from '../event-testing-library';
16
+} from 'dom-event-testing-library';
17
18
let React;
19
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js
+1
-1
@@ -9,7 +9,7 @@
9
10
'use strict';
11
12
-import {createEventTarget, setPointerEvent} from '../event-testing-library';
12
+import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13
14
let React;
15
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/Hover-test.internal.js
+1
-1
@@ -9,7 +9,7 @@
9
10
'use strict';
11
12
-import {createEventTarget, setPointerEvent} from '../event-testing-library';
12
+import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13
14
let React;
15
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/Keyboard-test.internal.js
+1
-1
@@ -14,7 +14,7 @@ let ReactFeatureFlags;
14
let ReactDOM;
15
let useKeyboard;
16
17
-import {createEventTarget} from '../event-testing-library';
17
+import {createEventTarget} from 'dom-event-testing-library';
18
19
function initializeModules(hasPointerEvents) {
20
jest.resetModules();
packages/react-interactions/events/src/dom/__tests__/MixedResponders-test-internal.js
+1
-1
@@ -9,7 +9,7 @@
9
10
'use strict';
11
12
-import {createEventTarget} from '../event-testing-library';
12
+import {createEventTarget} from 'dom-event-testing-library';
13
14
let React;
15
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/Press-test.internal.js
+1
-1
@@ -16,7 +16,7 @@ import {
16
describeWithPointerEvent,
17
resetActivePointers,
18
setPointerEvent,
19
-} from '../event-testing-library';
19
+} from 'dom-event-testing-library';
20
21
let React;
22
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js
+1
-1
@@ -15,7 +15,7 @@ import {
15
createEventTarget,
16
resetActivePointers,
17
setPointerEvent,
18
-} from '../event-testing-library';
18
+} from 'dom-event-testing-library';
19
20
let React;
21
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/Scroll-test.internal.js
+1
-1
@@ -9,7 +9,7 @@
9
10
'use strict';
11
12
-import {createEventTarget, setPointerEvent} from '../event-testing-library';
12
+import {createEventTarget, setPointerEvent} from 'dom-event-testing-library';
13
14
let React;
15
let ReactFeatureFlags;
packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js
+1
-1
@@ -17,7 +17,7 @@ import {
17
setPointerEvent,
18
testWithPointerType,
19
resetActivePointers,
20
-} from '../event-testing-library';
20
+} from 'dom-event-testing-library';
21
22
let React;
23
let ReactFeatureFlags;
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
+1
-1
@@ -9,7 +9,7 @@
9
10
'use strict';
11
12
-import {createEventTarget} from 'react-interactions/events/src/dom/event-testing-library';
12
+import {createEventTarget} from 'dom-event-testing-library';
13
14
let React;
15
let ReactFeatureFlags;