Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugix/fire type events on active element #299

62 changes: 61 additions & 1 deletion src/__tests__/type.js
@@ -1,4 +1,4 @@
import React from 'react'
import React, {Fragment} from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '../../src'

Expand Down Expand Up @@ -256,3 +256,63 @@ test.each(['input', 'textarea'])(
expect(onKeyUp).not.toHaveBeenCalled()
},
)

test('should fire events on the currently focussed element', async () => {
kayleighridd marked this conversation as resolved.
Show resolved Hide resolved
const changeFocusLimit = 7
const onKeyDown = jest.fn(event => {
if (event.target.value.length === changeFocusLimit) {
screen.getByTestId('input2').focus()
}
})

render(
<Fragment>
<input data-testid="input1" onKeyDown={onKeyDown} />
<input data-testid="input2" />
</Fragment>,
)

const text = 'Hello, world!'

const input1 = screen.getByTestId('input1')
const input2 = screen.getByTestId('input2')

await userEvent.type(input1, text)

expect(input1).toHaveValue(text.slice(0, changeFocusLimit))
expect(input2).toHaveValue(text.slice(changeFocusLimit))
expect(input2).toHaveFocus()
})

test('should enter text up to maxLength of the current element if provided', async () => {
const changeFocusLimit = 7
const input2MaxLength = 2

const onKeyDown = jest.fn(event => {
if (event.target.value.length === changeFocusLimit) {
screen.getByTestId('input2').focus()
}
})

render(
<>
<input data-testid="input" onKeyDown={onKeyDown} />
<input data-testid="input2" maxLength={input2MaxLength} />
</>,
)

const text = 'Hello, world!'
const input2ExpectedValue = text.slice(
changeFocusLimit,
changeFocusLimit + input2MaxLength,
)

const input1 = screen.getByTestId('input')
const input2 = screen.getByTestId('input2')

await userEvent.type(input1, text)

expect(input1).toHaveValue(text.slice(0, changeFocusLimit))
expect(input2).toHaveValue(input2ExpectedValue)
expect(input2).toHaveFocus()
})
37 changes: 23 additions & 14 deletions src/index.js
Expand Up @@ -334,21 +334,30 @@ async function type(...args) {

async function typeImpl(element, text, {allAtOnce = false, delay} = {}) {
if (element.disabled) return
kayleighridd marked this conversation as resolved.
Show resolved Hide resolved
const previousText = element.value

const computedText =
element.maxLength > 0
? text.slice(0, Math.max(element.maxLength - previousText.length, 0))
element.focus()

// The focussed element could change between each event, so get the currently active element each time
const currentElement = () => element.ownerDocument.activeElement
const currentValue = () => element.ownerDocument.activeElement.value

const computeText = () =>
currentElement().maxLength > 0
? text.slice(
0,
Math.max(currentElement().maxLength - currentValue().length, 0),
)
: text

if (allAtOnce) {
if (!element.readOnly) {
const previousText = element.value

fireEvent.input(element, {
target: {value: previousText + computedText},
target: {value: previousText + computeText()},
})
}
} else {
let actuallyTyped = previousText
for (let index = 0; index < text.length; index++) {
const char = text[index]
const key = char // TODO: check if this also valid for characters with diacritic markers e.g. úé etc
Expand All @@ -357,28 +366,28 @@ async function typeImpl(element, text, {allAtOnce = false, delay} = {}) {
// eslint-disable-next-line no-await-in-loop
if (delay > 0) await wait(delay)

const downEvent = fireEvent.keyDown(element, {
if (currentElement.disabled) return
kayleighridd marked this conversation as resolved.
Show resolved Hide resolved

const downEvent = fireEvent.keyDown(currentElement(), {
key,
keyCode,
which: keyCode,
})

if (downEvent) {
const pressEvent = fireEvent.keyPress(element, {
const pressEvent = fireEvent.keyPress(currentElement(), {
key,
keyCode,
charCode: keyCode,
})

const isTextPastThreshold =
(actuallyTyped + key).length > (previousText + computedText).length
const isTextPastThreshold = !computeText().length

if (pressEvent && !isTextPastThreshold) {
actuallyTyped += key
if (!element.readOnly) {
fireEvent.input(element, {
fireEvent.input(currentElement(), {
target: {
value: actuallyTyped,
value: currentValue() + key,
},
bubbles: true,
cancelable: true,
Expand All @@ -387,7 +396,7 @@ async function typeImpl(element, text, {allAtOnce = false, delay} = {}) {
}
}

fireEvent.keyUp(element, {
fireEvent.keyUp(currentElement(), {
key,
keyCode,
which: keyCode,
Expand Down