Skip to content

Commit e2a5f43

Browse files
authored
feat(pointer)!: introduce pointerEventsCheck option (#823)
BREAKING CHANGE: `skipPointerEvents` has been removed. Use `pointerEventsCheck: PointerEventsCheckLevel.Never` instead.
1 parent b83b259 commit e2a5f43

File tree

30 files changed

+377
-176
lines changed

30 files changed

+377
-176
lines changed

src/clipboard/copy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {fireEvent} from '@testing-library/dom'
2-
import {Config, UserEvent} from '../setup'
2+
import {Config, Instance} from '../setup'
33
import {copySelection, writeDataTransferToClipboard} from '../utils'
44

5-
export async function copy(this: UserEvent) {
5+
export async function copy(this: Instance) {
66
const doc = this[Config].document
77
const target = doc.activeElement ?? /* istanbul ignore next */ doc.body
88

src/clipboard/cut.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {fireEvent} from '@testing-library/dom'
2-
import {Config, UserEvent} from '../setup'
2+
import {Config, Instance} from '../setup'
33
import {
44
copySelection,
55
isEditable,
66
prepareInput,
77
writeDataTransferToClipboard,
88
} from '../utils'
99

10-
export async function cut(this: UserEvent) {
10+
export async function cut(this: Instance) {
1111
const doc = this[Config].document
1212
const target = doc.activeElement ?? /* istanbul ignore next */ doc.body
1313

src/clipboard/paste.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {fireEvent} from '@testing-library/dom'
2-
import {Config, UserEvent} from '../setup'
2+
import {Config, Instance} from '../setup'
33
import {
44
createDataTransfer,
55
getSpaceUntilMaxLength,
@@ -9,7 +9,7 @@ import {
99
} from '../utils'
1010

1111
export async function paste(
12-
this: UserEvent,
12+
this: Instance,
1313
clipboardData?: DataTransfer | string,
1414
) {
1515
const doc = this[Config].document

src/convenience/click.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import type {PointerInput} from '../pointer'
2-
import {hasPointerEvents} from '../utils'
3-
import {Config, UserEvent} from '../setup'
4-
5-
export async function click(this: UserEvent, element: Element): Promise<void> {
6-
if (!this[Config].skipPointerEventsCheck && !hasPointerEvents(element)) {
7-
throw new Error(
8-
'unable to click element as it has or inherits pointer-events set to "none".',
9-
)
10-
}
2+
import {Config, Instance} from '../setup'
113

4+
export async function click(this: Instance, element: Element): Promise<void> {
125
const pointerIn: PointerInput = []
136
if (!this[Config].skipHover) {
147
pointerIn.push({target: element})
@@ -19,27 +12,15 @@ export async function click(this: UserEvent, element: Element): Promise<void> {
1912
}
2013

2114
export async function dblClick(
22-
this: UserEvent,
15+
this: Instance,
2316
element: Element,
2417
): Promise<void> {
25-
if (!this[Config].skipPointerEventsCheck && !hasPointerEvents(element)) {
26-
throw new Error(
27-
'unable to double-click element as it has or inherits pointer-events set to "none".',
28-
)
29-
}
30-
3118
return this.pointer([{target: element}, '[MouseLeft][MouseLeft]'])
3219
}
3320

3421
export async function tripleClick(
35-
this: UserEvent,
22+
this: Instance,
3623
element: Element,
3724
): Promise<void> {
38-
if (!this[Config].skipPointerEventsCheck && !hasPointerEvents(element)) {
39-
throw new Error(
40-
'unable to triple-click element as it has or inherits pointer-events set to "none".',
41-
)
42-
}
43-
4425
return this.pointer([{target: element}, '[MouseLeft][MouseLeft][MouseLeft]'])
4526
}

src/convenience/hover.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
1-
import {Config, UserEvent} from '../setup'
2-
import {hasPointerEvents} from '../utils'
3-
4-
export async function hover(this: UserEvent, element: Element) {
5-
if (!this[Config].skipPointerEventsCheck && !hasPointerEvents(element)) {
6-
throw new Error(
7-
'unable to hover element as it has or inherits pointer-events set to "none".',
8-
)
9-
}
1+
import {Instance} from '../setup'
102

3+
export async function hover(this: Instance, element: Element) {
114
return this.pointer({target: element})
125
}
136

14-
export async function unhover(this: UserEvent, element: Element) {
15-
if (!this[Config].skipPointerEventsCheck && !hasPointerEvents(element)) {
16-
throw new Error(
17-
'unable to unhover element as it has or inherits pointer-events set to "none".',
18-
)
19-
}
20-
7+
export async function unhover(this: Instance, element: Element) {
218
return this.pointer({target: element.ownerDocument.body})
229
}

src/convenience/tab.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import type {UserEvent} from '../setup'
1+
import type {Instance} from '../setup'
22

33
export async function tab(
4-
this: UserEvent,
4+
this: Instance,
55
{
66
shift,
77
}: {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export {userEvent as default} from './setup'
22
export type {keyboardKey} from './keyboard'
33
export type {pointerKey} from './pointer'
4+
export {PointerEventsCheckLevel} from './options'

src/keyboard/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import {Config, UserEvent} from '../setup'
1+
import {Config, Instance} from '../setup'
22
import {keyboardAction, KeyboardAction, releaseAllKeys} from './keyboardAction'
33
import {parseKeyDef} from './parseKeyDef'
44
import type {keyboardState, keyboardKey} from './types'
55

66
export {releaseAllKeys}
77
export type {keyboardKey, keyboardState}
88

9-
export async function keyboard(this: UserEvent, text: string): Promise<void> {
9+
export async function keyboard(this: Instance, text: string): Promise<void> {
1010
const {keyboardMap} = this[Config]
1111

1212
const actions: KeyboardAction[] = parseKeyDef(keyboardMap, text)

src/options.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ import type {pointerKey} from './pointer/types'
33
import {defaultKeyMap as defaultKeyboardMap} from './keyboard/keyMap'
44
import {defaultKeyMap as defaultPointerMap} from './pointer/keyMap'
55

6+
export enum PointerEventsCheckLevel {
7+
/**
8+
* Check pointer events on every user interaction that triggers a bunch of events.
9+
* E.g. once for releasing a mouse button even though this triggers `pointerup`, `mouseup`, `click`, etc...
10+
*/
11+
EachTrigger = 4,
12+
/** Check each target once per call to pointer (related) API */
13+
EachApiCall = 2,
14+
/** Check each event target once */
15+
EachTarget = 1,
16+
/** No pointer events check */
17+
Never = 0,
18+
}
19+
620
export interface Options {
721
/**
822
* When using `userEvent.upload`, automatically discard files
@@ -61,6 +75,17 @@ export interface Options {
6175
*/
6276
pointerMap?: pointerKey[]
6377

78+
/**
79+
* The pointer API includes a check if an element has or inherits `pointer-events: none`.
80+
* This check is known to be expensive and very expensive when checking deeply nested nodes.
81+
* This option determines how often the pointer related APIs perform the check.
82+
*
83+
* This is a binary flag option. You can combine multiple Levels.
84+
*
85+
* @default PointerEventsCheckLevel.EachCall
86+
*/
87+
pointerEventsCheck?: PointerEventsCheckLevel | number
88+
6489
/**
6590
* `userEvent.type` automatically releases any keys still pressed at the end of the call.
6691
* This option allows to opt out of this feature.
@@ -85,15 +110,6 @@ export interface Options {
85110
*/
86111
skipHover?: boolean
87112

88-
/**
89-
* Calling pointer related APIs on an element triggers a check if that element can receive pointer events.
90-
* This check is known to be expensive.
91-
* This option allows to skip the check.
92-
*
93-
* @default false
94-
*/
95-
skipPointerEventsCheck?: boolean
96-
97113
/**
98114
* Write selected data to Clipboard API when a `cut` or `copy` is triggered.
99115
*
@@ -116,10 +132,10 @@ export const defaultOptionsDirect: Required<Options> = {
116132
document: global.document,
117133
keyboardMap: defaultKeyboardMap,
118134
pointerMap: defaultPointerMap,
135+
pointerEventsCheck: PointerEventsCheckLevel.EachApiCall,
119136
skipAutoClose: false,
120137
skipClick: false,
121138
skipHover: false,
122-
skipPointerEventsCheck: false,
123139
writeToClipboard: false,
124140
}
125141

src/pointer/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Config, UserEvent} from '../setup'
1+
import {Config, Instance} from '../setup'
22
import {parseKeyDef} from './parseKeyDef'
33
import {
44
pointerAction,
@@ -16,7 +16,7 @@ type PointerActionInput =
1616
export type PointerInput = PointerActionInput | Array<PointerActionInput>
1717

1818
export async function pointer(
19-
this: UserEvent,
19+
this: Instance,
2020
input: PointerInput,
2121
): Promise<void> {
2222
const {pointerMap} = this[Config]

0 commit comments

Comments
 (0)