Skip to content

Commit 50057e3

Browse files
nickservkentcdodds
andauthored
feat(hover): add hover API (fixes testing-library#254) (testing-library#331)
Closes testing-library#254 Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
1 parent dbdefef commit 50057e3

File tree

5 files changed

+84
-1
lines changed

5 files changed

+84
-1
lines changed

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ change the state of the checkbox.
5050
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
5151
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
5252

53-
5453
- [Installation](#installation)
5554
- [API](#api)
5655
- [`click(element)`](#clickelement)
@@ -61,6 +60,8 @@ change the state of the checkbox.
6160
- [`selectOptions(element, values)`](#selectoptionselement-values)
6261
- [`toggleSelectOptions(element, values)`](#toggleselectoptionselement-values)
6362
- [`tab({shift, focusTrap})`](#tabshift-focustrap)
63+
- [`async hover(element)`](#async-hoverelement)
64+
- [`async unhover(element)`](#async-unhoverelement)
6465
- [Issues](#issues)
6566
- [🐛 Bugs](#-bugs)
6667
- [💡 Feature Requests](#-feature-requests)
@@ -398,6 +399,37 @@ it('should cycle elements in document tab order', () => {
398399
})
399400
```
400401

402+
### `async hover(element)`
403+
404+
Hovers over `element`.
405+
406+
```jsx
407+
import React from 'react'
408+
import {render, screen} from '@testing-library/react'
409+
import userEvent from '@testing-library/user-event'
410+
import Tooltip from '../tooltip'
411+
412+
test('hover', async () => {
413+
const messageText = 'Hello'
414+
render(
415+
<Tooltip messageText={messageText}>
416+
<TrashIcon aria-label="Delete" />
417+
</Tooltip>,
418+
)
419+
420+
await userEvent.hover(screen.getByLabelText(/delete/i))
421+
expect(screen.getByText(messageText)).toBeInTheDocument()
422+
await userEvent.unhover(screen.getByLabelText(/delete/i))
423+
expect(screen.queryByText(messageText)).not.toBeInTheDocument()
424+
})
425+
```
426+
427+
### `async unhover(element)`
428+
429+
Unhovers out of `element`.
430+
431+
> See [above](#async-hoverelement) for an example
432+
401433
## Issues
402434

403435
_Looking to contribute? Look for the [Good First Issue][good-first-issue]
@@ -482,6 +514,7 @@ Thanks goes to these people ([emoji key][emojis]):
482514

483515
<!-- markdownlint-enable -->
484516
<!-- prettier-ignore-end -->
517+
485518
<!-- ALL-CONTRIBUTORS-LIST:END -->
486519

487520
This project follows the [all-contributors][all-contributors] specification.

src/__tests__/hover.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react'
2+
import userEvent from '..'
3+
import {setup} from './helpers/utils'
4+
5+
test('hover', async () => {
6+
const {element, getEventCalls} = setup(<button />)
7+
8+
await userEvent.hover(element)
9+
expect(getEventCalls()).toMatchInlineSnapshot(`
10+
mouseover: Left (0)
11+
mouseenter: Left (0)
12+
mousemove: Left (0)
13+
`)
14+
})

src/__tests__/unhover.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react'
2+
import userEvent from '..'
3+
import {setup} from './helpers/utils'
4+
5+
test('unhover', async () => {
6+
const {element, getEventCalls} = setup(<button />)
7+
8+
await userEvent.unhover(element)
9+
expect(getEventCalls()).toMatchInlineSnapshot(`
10+
mousemove: Left (0)
11+
mouseleave: Left (0)
12+
`)
13+
})

src/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {fireEvent} from '@testing-library/dom'
22
import {type} from './type'
3+
import {tick} from './tick'
34

45
function isMousePressEvent(event) {
56
return (
@@ -451,6 +452,24 @@ function tab({shift = false, focusTrap = document} = {}) {
451452
}
452453
}
453454

455+
async function hover(element, init) {
456+
await tick()
457+
fireEvent.mouseOver(element, getMouseEventOptions('mouseover', init))
458+
await tick()
459+
fireEvent.mouseEnter(element, getMouseEventOptions('mouseenter', init))
460+
await tick()
461+
fireEvent.mouseMove(element, getMouseEventOptions('mousemove', init))
462+
}
463+
464+
async function unhover(element, init) {
465+
await tick()
466+
fireEvent.mouseMove(element, getMouseEventOptions('mousemove', init))
467+
await tick()
468+
fireEvent.mouseOut(element, getMouseEventOptions('mouseout', init))
469+
await tick()
470+
fireEvent.mouseLeave(element, getMouseEventOptions('mouseleave', init))
471+
}
472+
454473
const userEvent = {
455474
click,
456475
dblClick,
@@ -460,6 +479,8 @@ const userEvent = {
460479
type,
461480
upload,
462481
tab,
482+
hover,
483+
unhover,
463484
}
464485

465486
export default userEvent

typings/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ declare const userEvent: {
4545
userOpts?: ITypeOpts,
4646
) => Promise<void>
4747
tab: (userOpts?: ITabUserOptions) => void
48+
hover: (element: TargetElement, init?: MouseEventInit) => Promise<void>
49+
unhover: (element: TargetElement, init?: MouseEventInit) => Promise<void>
4850
}
4951

5052
export default userEvent

0 commit comments

Comments
 (0)