Aller au contenu principal

user-event v13

[Traduction Bêta Non Officielle]

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

user-event est une bibliothèque complémentaire à Testing Library qui offre une simulation plus avancée des interactions navigateur que la méthode intégrée fireEvent.

[Fin de vie]

Cette page décrit user-event@13.5.0.
Cette version n'est plus maintenue. Veuillez utiliser user-event@14 à la place, car elle inclut des corrections de bugs importantes et de nouvelles fonctionnalités.

Installation

npm install --save-dev @testing-library/user-event @testing-library/dom

Importez-la simplement dans vos tests :

import userEvent from '@testing-library/user-event'

// or

const {default: userEvent} = require('@testing-library/user-event')

API

Note : Toutes les méthodes de userEvent sont synchrones, sauf une exception : lorsque l'option delay est utilisée avec userEvent.type comme décrit ci-dessous. Nous déconseillons également d'utiliser userEvent dans les blocs before/after, pour les raisons importantes expliquées dans "Éviter l'imbrication dans les tests".

click(element, eventInit, options)

Clique sur element. Selon quel element est cliqué, l'appel de click() peut avoir différents effets secondaires.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('click', () => {
render(
<div>
<label htmlFor="checkbox">Check</label>
<input id="checkbox" type="checkbox" />
</div>,
)

userEvent.click(screen.getByText('Check'))
expect(screen.getByLabelText('Check')).toBeChecked()
})

Vous pouvez également effectuer des ctrlClick / shiftClick etc avec :

userEvent.click(elem, {ctrlKey: true, shiftKey: true})

Consultez la documentation du constructeur MouseEvent pour plus d'options.

Notez que click déclenchera des événements de survol avant le clic. Pour désactiver ce comportement, définissez l'option skipHover sur true.

Options des événements de pointeur

Tenter de cliquer sur un élément avec pointer-events défini sur "none" (c'est-à-dire non cliquable) générera une erreur. Pour désactiver ce comportement, définissez skipPointerEventsCheck sur true :

userEvent.click(elem, undefined, {skipPointerEventsCheck: true})

L'option skipPointerEventsCheck peut être passée à toute API liée aux pointeurs, notamment :

dblClick(element, eventInit, options)

Clique deux fois sur element. Selon ce qu'est element, cela peut avoir différents effets secondaires.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('double click', () => {
const onChange = jest.fn()
render(<input type="checkbox" onChange={onChange} />)
const checkbox = screen.getByRole('checkbox')
userEvent.dblClick(checkbox)
expect(onChange).toHaveBeenCalledTimes(2)
expect(checkbox).not.toBeChecked()
})

Note : options inclut les options d'événements de pointeur

type(element, text, [options])

Saisit text dans un <input> ou un <textarea>.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('type', () => {
render(<textarea />)

userEvent.type(screen.getByRole('textbox'), 'Hello,{enter}World!')
expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!')
})

options.delay représente le nombre de millisecondes entre la frappe de deux caractères. Par défaut à 0. Utilisez cette option si votre composant a un comportement différent pour les utilisateurs rapides ou lents. Dans ce cas, assurez-vous d'utiliser await !

type cliquera sur l'élément avant la saisie. Pour désactiver ce comportement, définissez skipClick sur true.

Caractères spéciaux

Les chaînes de caractères spéciales suivantes sont prises en charge :

Text stringKeyModifierNotes
{enter}EnterN/AWill insert a newline character (<textarea /> only).
{space}' 'N/A
{esc}EscapeN/A
{backspace}BackspaceN/AWill delete the previous character (or the characters within the selectedRange, see example below).
{del}DeleteN/AWill delete the next character (or the characters within the selectedRange, see example below)
{selectall}N/AN/ASelects all the text of the element. Note that this will only work for elements that support selection ranges (so, not email, password, number, among others)
{arrowleft}ArrowLeftN/A
{arrowright}ArrowRightN/A
{arrowup}ArrowUpN/A
{arrowdown}ArrowDownN/A
{home}HomeN/A
{end}EndN/A
{shift}ShiftshiftKeyDoes not capitalize following characters.
{ctrl}ControlctrlKey
{alt}AltaltKey
{meta}OSmetaKey
{capslock}CapsLockmodifierCapsLockFires both keydown and keyup when used (simulates a user clicking their "Caps Lock" button to enable caps lock).

À propos des modificateurs : Les touches modificateurs ({shift}, {ctrl}, {alt}, {meta}) activeront leurs modificateurs d'événement correspondants pendant la durée de la commande de saisie ou jusqu'à leur fermeture explicite (via {/shift}, {/ctrl}, etc.). Si elles ne sont pas fermées explicitement, des événements de fermeture seront automatiquement déclenchés (définissez skipAutoClose sur true pour désactiver ce comportement).

Nous adoptons la même position que Cypress en ne simulant pas le comportement des combinaisons de touches modifiables, car les différents systèmes d'exploitation fonctionnent différemment à cet égard.

Exemple d'utilisation avec une plage de sélection :

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('delete characters within the selectedRange', () => {
render(
<div>
<label htmlFor="my-input">Example:</label>
<input id="my-input" type="text" value="This is a bad example" />
</div>,
)
const input = screen.getByLabelText(/example/i)
input.setSelectionRange(10, 13)
userEvent.type(input, '{backspace}good')

expect(input).toHaveValue('This is a good example')

Par défaut, type ajoute au texte existant. Pour préfixer du texte, réinitialisez la plage de sélection de l'élément et fournissez les options initialSelectionStart et initialSelectionEnd :

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('prepend text', () => {
render(<input defaultValue="World!" />)
const element = screen.getByRole('textbox')

// Prepend text
element.setSelectionRange(0, 0)
userEvent.type(element, 'Hello, ', {
initialSelectionStart: 0,
initialSelectionEnd: 0,
})

expect(element).toHaveValue('Hello, World!')
})

Prise en charge de <input type="time" />

Voici un exemple d'utilisation de cette bibliothèque avec <input type="time" /> :

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('types into the input', () => {
render(
<>
<label for="time">Enter a time</label>
<input type="time" id="time" />
</>,
)
const input = screen.getByLabelText(/enter a time/i)
userEvent.type(input, '13:58')
expect(input.value).toBe('13:58')
})

keyboard(text, options)

Simule les événements clavier décrits par text. Similaire à userEvent.type() mais sans cliquer ni modifier la plage de sélection.

Utilisez userEvent.keyboard pour simuler simplement l'appui sur des touches. Préférez userEvent.type pour insérer facilement du texte dans un champ de saisie ou une zone de texte.

Les frappes de touches peuvent être décrites :

  • Par caractère imprimable

    userEvent.keyboard('foo') // translates to: f, o, o

    Les crochets { et [ sont des caractères spéciaux et peuvent être référencés en les doublant.

    userEvent.keyboard('{{a[[') // translates to: {, a, [
  • Par KeyboardEvent.key (supporte uniquement les valeurs alphanumériques de key)

    userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o

    Ne maintient aucune touche enfoncée. Shift sera relâché avant d'appuyer sur f.

  • Par KeyboardEvent.code

    userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // translates to: Shift, f, o, o
  • Par modificateur/spécialChar hérité de userEvent.type
    Les modificateurs comme {shift} (en minuscules) restent automatiquement enfoncés. Annulez ce comportement avec un / à la fin du descripteur.

    userEvent.keyboard('{shift}{ctrl/}a{/shift}') // translates to: Shift(down), Control(down+up), a, Shift(up)

Maintenez les touches enfoncées avec > à la fin du descripteur - et relâchez-les avec / au début du descripteur :

userEvent.keyboard('{Shift>}A{/Shift}') // translates to: Shift(down), A, Shift(up)

userEvent.keyboard renvoie un état clavier utilisable pour poursuivre les opérations.

const keyboardState = userEvent.keyboard('[ControlLeft>]') // keydown [ControlLeft]
// ... inspect some changes ...
userEvent.keyboard('a', {keyboardState}) // press [KeyA] with active ctrlKey modifier

Le mapping key vers code utilise une table de correspondance par défaut représentant un clavier US "par défaut". Fournissez votre propre mapping via l'option.

userEvent.keyboard('?', {keyboardMap: myOwnLocaleKeyboardMap})

Les versions futures pourraient interpoler automatiquement les modificateurs nécessaires pour atteindre une touche imprimable (ex: appuyer automatiquement sur {Shift} pour A si VerrMaj est désactivé). Si vous ne souhaitez pas ce comportement, vous pouvez passer autoModify: false lors de l'utilisation de userEvent.keyboard dans votre code.

upload(element, file, [{ clickInit, changeInit }], [options])

Téléverse un fichier vers un <input>. Pour plusieurs fichiers, utilisez un <input> avec l'attribut multiple et le second argument upload comme un tableau. Initialisez un événement clic ou changement avec le troisième argument.

Si options.applyAccept est défini sur true et qu'un attribut accept est présent sur l'élément, les fichiers non conformes seront ignorés.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('upload file', () => {
const file = new File(['hello'], 'hello.png', {type: 'image/png'})

render(
<div>
<label htmlFor="file-uploader">Upload file:</label>
<input id="file-uploader" type="file" />
</div>,
)
const input = screen.getByLabelText(/upload file/i)
userEvent.upload(input, file)

expect(input.files[0]).toStrictEqual(file)
expect(input.files.item(0)).toStrictEqual(file)
expect(input.files).toHaveLength(1)
})

test('upload multiple files', () => {
const files = [
new File(['hello'], 'hello.png', {type: 'image/png'}),
new File(['there'], 'there.png', {type: 'image/png'}),
]

render(
<div>
<label htmlFor="file-uploader">Upload file:</label>
<input id="file-uploader" type="file" multiple />
</div>,
)
const input = screen.getByLabelText(/upload file/i)
userEvent.upload(input, files)

expect(input.files).toHaveLength(2)
expect(input.files[0]).toStrictEqual(files[0])
expect(input.files[1]).toStrictEqual(files[1])
})

clear(element)

Sélectionne le texte à l'intérieur d'un <input> ou <textarea> et le supprime.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('clear', () => {
render(<textarea defaultValue="Hello, World!" />)

userEvent.clear(screen.getByRole('textbox'))
expect(screen.getByRole('textbox')).toHaveValue('')
})

selectOptions(element, values, options)

Sélectionne les options spécifiées d'un élément <select> ou <select multiple>.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('selectOptions', () => {
render(
<select multiple>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>,
)

userEvent.selectOptions(screen.getByRole('listbox'), ['1', '3'])

expect(screen.getByRole('option', {name: 'A'}).selected).toBe(true)
expect(screen.getByRole('option', {name: 'B'}).selected).toBe(false)
expect(screen.getByRole('option', {name: 'C'}).selected).toBe(true)
})

Le paramètre values peut être soit un tableau de valeurs soit une valeur scalaire unique.

Accepte également des nœuds d'option :

userEvent.selectOptions(screen.getByTestId('select-multiple'), [
screen.getByText('A'),
screen.getByText('B'),
])

Note : options inclut les options d'événements de pointeur

deselectOptions(element, values, options)

Désélectionne les options spécifiées d'un élément <select multiple>.

import * as React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('deselectOptions', () => {
render(
<select multiple>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>,
)

userEvent.selectOptions(screen.getByRole('listbox'), '2')
expect(screen.getByText('B').selected).toBe(true)
userEvent.deselectOptions(screen.getByRole('listbox'), '2')
expect(screen.getByText('B').selected).toBe(false)
// can do multiple at once as well:
// userEvent.deselectOptions(screen.getByRole('listbox'), ['1', '2'])
})

Le paramètre values peut être soit un tableau de valeurs soit une valeur scalaire unique.

Note : options inclut les options d'événements de pointeur

tab({shift, focusTrap})

Déclenche un événement de tabulation modifiant le document.activeElement comme le ferait un navigateur.

Options :

  • shift (par défaut false) : true ou false pour inverser la direction de tabulation.

  • focusTrap (par défaut document) : élément conteneur pour restreindre la tabulation.

Note concernant la tabulation :
jsdom ne prend pas en charge la tabulation,
cette fonctionnalité permet de tester la tabulation du point de vue utilisateur.
Cependant, cette limitation implique que des composants comme
focus-trap-react ne
fonctionneront pas avec userEvent.tab() sous jsdom. L'option focusTrap
permet donc de simuler la restriction dans une zone de focus.

import React from 'react'
import {render, screen} from '@testing-library/react'
import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'

it('should cycle elements in document tab order', () => {
render(
<div>
<input data-testid="element" type="checkbox" />
<input data-testid="element" type="radio" />
<input data-testid="element" type="number" />
</div>,
)

const [checkbox, radio, number] = screen.getAllByTestId('element')

expect(document.body).toHaveFocus()

userEvent.tab()

expect(checkbox).toHaveFocus()

userEvent.tab()

expect(radio).toHaveFocus()

userEvent.tab()

expect(number).toHaveFocus()

userEvent.tab()

// cycle goes back to the body element
expect(document.body).toHaveFocus()

userEvent.tab()

expect(checkbox).toHaveFocus()
})

hover(element, options)

Survole element avec le curseur.

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Tooltip from '../tooltip'

test('hover', () => {
const messageText = 'Hello'
render(
<Tooltip messageText={messageText}>
<TrashIcon aria-label="Delete" />
</Tooltip>,
)

userEvent.hover(screen.getByLabelText(/delete/i))
expect(screen.getByText(messageText)).toBeInTheDocument()
userEvent.unhover(screen.getByLabelText(/delete/i))
expect(screen.queryByText(messageText)).not.toBeInTheDocument()
})

Note : options inclut les options d'événements de pointeur

unhover(element, options)

Désactive le survol de element.

Voir exemple ci-dessus

Note : options inclut les options d'événements de pointeur

paste(element, text, eventInit, options)

Permet de simuler le collage de texte par l'utilisateur dans un champ.

test('should paste text in input', () => {
render(<MyInput />)

const text = 'Hello, world!'
const element = getByRole('textbox', {name: /paste your greeting/i})
userEvent.paste(element, text)
expect(element).toHaveValue(text)
})

Vous pouvez utiliser eventInit si ce que vous collez doit avoir clipboardData (comme files).

specialChars

Ensemble de caractères spéciaux utilisés dans la méthode type.

KeyCharacter
arrowLeft{arrowleft}
arrowRight{arrowright}
arrowDown{arrowdown}
arrowUp{arrowup}
home{home}
end{end}
enter{enter}
escape{esc}
delete{del}
backspace{backspace}
selectAll{selectall}
space{space}
whitespace' '

Exemple d'utilisation :

import React, {useState} from 'react'
import {render, screen} from '@testing-library/react'
import userEvent, {specialChars} from '@testing-library/user-event'

const InputElement = () => {
const [currentValue, setCurrentValue] = useState('This is a bad example')
return (
<div>
<label htmlFor="my-input">Example:</label>
<input
id="my-input"
type="text"
value={currentValue}
onChange={e => setCurrentValue(e.target.value)}
/>
</div>
)
}

test('delete characters within the selectedRange', () => {
render(<InputElement />)
const input = screen.getByLabelText(/example/i)
input.setSelectionRange(10, 13)
userEvent.type(input, `${specialChars.backspace}good`)

expect(input).toHaveValue('This is a good example')
})