Giter VIP home page Giter VIP logo

react-testing-library's Introduction

React Testing Library

goat

Simple and complete React DOM testing utilities that encourage good testing practices.


Read The Docs | Edit the docs



Build Status Code Coverage version downloads MIT License All Contributors PRs Welcome Code of Conduct Discord

Watch on GitHub Star on GitHub Tweet

Table of Contents

The problem

You want to write maintainable tests for your React components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended. As part of this, you want your testbase to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down.

The solution

The React Testing Library is a very lightweight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. Its primary guiding principle is:

The more your tests resemble the way your software is used, the more confidence they can give you.

Installation

This module is distributed via npm which is bundled with node and should be installed as one of your project's devDependencies:

npm install --save-dev @testing-library/react

or

for installation via yarn

yarn add --dev @testing-library/react

This library has peerDependencies listings for react and react-dom.

React Testing Library versions 13+ require React v18. If your project uses an older version of React, be sure to install version 12:

npm install --save-dev @testing-library/react@12


yarn add --dev @testing-library/react@12

You may also be interested in installing @testing-library/jest-dom so you can use the custom jest matchers.

Docs

Suppressing unnecessary warnings on React DOM 16.8

There is a known compatibility issue with React DOM 16.8 where you will see the following warning:

Warning: An update to ComponentName inside a test was not wrapped in act(...).

If you cannot upgrade to React DOM 16.9, you may suppress the warnings by adding the following snippet to your test configuration (learn more):

// this is just a little hack to silence a warning that we'll get until we
// upgrade to 16.9. See also: https://github.com/facebook/react/pull/14853
const originalError = console.error
beforeAll(() => {
  console.error = (...args) => {
    if (/Warning.*not wrapped in act/.test(args[0])) {
      return
    }
    originalError.call(console, ...args)
  }
})

afterAll(() => {
  console.error = originalError
})

Examples

Basic Example

// hidden-message.js
import * as React from 'react'

// NOTE: React Testing Library works well with React Hooks and classes.
// Your tests will be the same regardless of how you write your components.
function HiddenMessage({children}) {
  const [showMessage, setShowMessage] = React.useState(false)
  return (
    <div>
      <label htmlFor="toggle">Show Message</label>
      <input
        id="toggle"
        type="checkbox"
        onChange={e => setShowMessage(e.target.checked)}
        checked={showMessage}
      />
      {showMessage ? children : null}
    </div>
  )
}

export default HiddenMessage
// __tests__/hidden-message.js
// these imports are something you'd normally configure Jest to import for you
// automatically. Learn more in the setup docs: https://testing-library.com/docs/react-testing-library/setup#cleanup
import '@testing-library/jest-dom'
// NOTE: jest-dom adds handy assertions to Jest and is recommended, but not required

import * as React from 'react'
import {render, fireEvent, screen} from '@testing-library/react'
import HiddenMessage from '../hidden-message'

test('shows the children when the checkbox is checked', () => {
  const testMessage = 'Test Message'
  render(<HiddenMessage>{testMessage}</HiddenMessage>)

  // query* functions will return the element or null if it cannot be found
  // get* functions will return the element or throw an error if it cannot be found
  expect(screen.queryByText(testMessage)).toBeNull()

  // the queries can accept a regex to make your selectors more resilient to content tweaks and changes.
  fireEvent.click(screen.getByLabelText(/show/i))

  // .toBeInTheDocument() is an assertion that comes from jest-dom
  // otherwise you could use .toBeDefined()
  expect(screen.getByText(testMessage)).toBeInTheDocument()
})

Complex Example

// login.js
import * as React from 'react'

function Login() {
  const [state, setState] = React.useReducer((s, a) => ({...s, ...a}), {
    resolved: false,
    loading: false,
    error: null,
  })

  function handleSubmit(event) {
    event.preventDefault()
    const {usernameInput, passwordInput} = event.target.elements

    setState({loading: true, resolved: false, error: null})

    window
      .fetch('/api/login', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
          username: usernameInput.value,
          password: passwordInput.value,
        }),
      })
      .then(r => r.json().then(data => (r.ok ? data : Promise.reject(data))))
      .then(
        user => {
          setState({loading: false, resolved: true, error: null})
          window.localStorage.setItem('token', user.token)
        },
        error => {
          setState({loading: false, resolved: false, error: error.message})
        },
      )
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="usernameInput">Username</label>
          <input id="usernameInput" />
        </div>
        <div>
          <label htmlFor="passwordInput">Password</label>
          <input id="passwordInput" type="password" />
        </div>
        <button type="submit">Submit{state.loading ? '...' : null}</button>
      </form>
      {state.error ? <div role="alert">{state.error}</div> : null}
      {state.resolved ? (
        <div role="alert">Congrats! You're signed in!</div>
      ) : null}
    </div>
  )
}

export default Login
// __tests__/login.js
// again, these first two imports are something you'd normally handle in
// your testing framework configuration rather than importing them in every file.
import '@testing-library/jest-dom'
import * as React from 'react'
// import API mocking utilities from Mock Service Worker.
import {rest} from 'msw'
import {setupServer} from 'msw/node'
// import testing utilities
import {render, fireEvent, screen} from '@testing-library/react'
import Login from '../login'

const fakeUserResponse = {token: 'fake_user_token'}
const server = setupServer(
  rest.post('/api/login', (req, res, ctx) => {
    return res(ctx.json(fakeUserResponse))
  }),
)

beforeAll(() => server.listen())
afterEach(() => {
  server.resetHandlers()
  window.localStorage.removeItem('token')
})
afterAll(() => server.close())

test('allows the user to login successfully', async () => {
  render(<Login />)

  // fill out the form
  fireEvent.change(screen.getByLabelText(/username/i), {
    target: {value: 'chuck'},
  })
  fireEvent.change(screen.getByLabelText(/password/i), {
    target: {value: 'norris'},
  })

  fireEvent.click(screen.getByText(/submit/i))

  // just like a manual tester, we'll instruct our test to wait for the alert
  // to show up before continuing with our assertions.
  const alert = await screen.findByRole('alert')

  // .toHaveTextContent() comes from jest-dom's assertions
  // otherwise you could use expect(alert.textContent).toMatch(/congrats/i)
  // but jest-dom will give you better error messages which is why it's recommended
  expect(alert).toHaveTextContent(/congrats/i)
  expect(window.localStorage.getItem('token')).toEqual(fakeUserResponse.token)
})

test('handles server exceptions', async () => {
  // mock the server error response for this test suite only.
  server.use(
    rest.post('/api/login', (req, res, ctx) => {
      return res(ctx.status(500), ctx.json({message: 'Internal server error'}))
    }),
  )

  render(<Login />)

  // fill out the form
  fireEvent.change(screen.getByLabelText(/username/i), {
    target: {value: 'chuck'},
  })
  fireEvent.change(screen.getByLabelText(/password/i), {
    target: {value: 'norris'},
  })

  fireEvent.click(screen.getByText(/submit/i))

  // wait for the error message
  const alert = await screen.findByRole('alert')

  expect(alert).toHaveTextContent(/internal server error/i)
  expect(window.localStorage.getItem('token')).toBeNull()
})

We recommend using Mock Service Worker library to declaratively mock API communication in your tests instead of stubbing window.fetch, or relying on third-party adapters.

More Examples

We're in the process of moving examples to the docs site

You'll find runnable examples of testing with different libraries in the react-testing-library-examples codesandbox. Some included are:

Hooks

If you are interested in testing a custom hook, check out React Hooks Testing Library.

NOTE: it is not recommended to test single-use custom hooks in isolation from the components where it's being used. It's better to test the component that's using the hook rather than the hook itself. The React Hooks Testing Library is intended to be used for reusable hooks/libraries.

Guiding Principles

The more your tests resemble the way your software is used, the more confidence they can give you.

We try to only expose methods and utilities that encourage you to write tests that closely resemble how your React components are used.

Utilities are included in this project based on the following guiding principles:

  1. If it relates to rendering components, it deals with DOM nodes rather than component instances, nor should it encourage dealing with component instances.
  2. It should be generally useful for testing individual React components or full React applications. While this library is focused on react-dom, utilities could be included even if they don't directly relate to react-dom.
  3. Utility implementations and APIs should be simple and flexible.

Most importantly, we want React Testing Library to be pretty light-weight, simple, and easy to understand.

Docs

Read The Docs | Edit the docs

Issues

Looking to contribute? Look for the Good First Issue label.

πŸ› Bugs

Please file an issue for bugs, missing documentation, or unexpected behavior.

See Bugs

πŸ’‘ Feature Requests

Please file an issue to suggest new features. Vote on feature requests by adding a πŸ‘. This helps maintainers prioritize what to work on.

See Feature Requests

❓ Questions

For questions related to using the library, please visit a support community instead of filing an issue on GitHub.

Contributors

Thanks goes to these people (emoji key):

Kent C. Dodds
Kent C. Dodds

πŸ’» πŸ“– πŸš‡ ⚠️
Ryan Castner
Ryan Castner

πŸ“–
Daniel Sandiego
Daniel Sandiego

πŸ’»
PaweΕ‚ MikoΕ‚ajczyk
PaweΕ‚ MikoΕ‚ajczyk

πŸ’»
Alejandro ÑÑñez Ortiz
Alejandro ÑÑñez Ortiz

πŸ“–
Matt Parrish
Matt Parrish

πŸ› πŸ’» πŸ“– ⚠️
Justin Hall
Justin Hall

πŸ“¦
Anto Aravinth
Anto Aravinth

πŸ’» ⚠️ πŸ“–
Jonah Moses
Jonah Moses

πŸ“–
Łukasz Gandecki
Łukasz Gandecki

πŸ’» ⚠️ πŸ“–
Ivan Babak
Ivan Babak

πŸ› πŸ€”
Jesse Day
Jesse Day

πŸ’»
Ernesto GarcΓ­a
Ernesto GarcΓ­a

πŸ’¬ πŸ’» πŸ“–
Josef Maxx Blake
Josef Maxx Blake

πŸ’» πŸ“– ⚠️
Michal Baranowski
Michal Baranowski

πŸ“ βœ…
Arthur Puthin
Arthur Puthin

πŸ“–
Thomas Chia
Thomas Chia

πŸ’» πŸ“–
Thiago Galvani
Thiago Galvani

πŸ“–
Christian
Christian

⚠️
Alex Krolick
Alex Krolick

πŸ’¬ πŸ“– πŸ’‘ πŸ€”
Johann Hubert Sonntagbauer
Johann Hubert Sonntagbauer

πŸ’» πŸ“– ⚠️
Maddi Joyce
Maddi Joyce

πŸ’»
Ryan Vice
Ryan Vice

πŸ“–
Ian Wilson
Ian Wilson

πŸ“ βœ…
Daniel
Daniel

πŸ› πŸ’»
Giorgio Polvara
Giorgio Polvara

πŸ› πŸ€”
John Gozde
John Gozde

πŸ’»
Sam Horton
Sam Horton

πŸ“– πŸ’‘ πŸ€”
Richard Kotze (mobile)
Richard Kotze (mobile)

πŸ“–
Brahian E. Soto Mercedes
Brahian E. Soto Mercedes

πŸ“–
Benoit de La Forest
Benoit de La Forest

πŸ“–
Salah
Salah

πŸ’» ⚠️
Adam Gordon
Adam Gordon

πŸ› πŸ’»
Matija Marohnić
Matija Marohnić

πŸ“–
Justice Mba
Justice Mba

πŸ“–
Mark Pollmann
Mark Pollmann

πŸ“–
Ehtesham Kafeel
Ehtesham Kafeel

πŸ’» πŸ“–
Julio PavΓ³n
Julio PavΓ³n

πŸ’»
Duncan L
Duncan L

πŸ“– πŸ’‘
Tiago Almeida
Tiago Almeida

πŸ“–
Robert Smith
Robert Smith

πŸ›
Zach Green
Zach Green

πŸ“–
dadamssg
dadamssg

πŸ“–
Yazan Aabed
Yazan Aabed

πŸ“
Tim
Tim

πŸ› πŸ’» πŸ“– ⚠️
Divyanshu Maithani
Divyanshu Maithani

βœ… πŸ“Ή
Deepak Grover
Deepak Grover

βœ… πŸ“Ή
Eyal Cohen
Eyal Cohen

πŸ“–
Peter Makowski
Peter Makowski

πŸ“–
Michiel Nuyts
Michiel Nuyts

πŸ“–
Joe Ng'ethe
Joe Ng'ethe

πŸ’» πŸ“–
Kate
Kate

πŸ“–
Sean
Sean

πŸ“–
James Long
James Long

πŸ€” πŸ“¦
Herb Hagely
Herb Hagely

πŸ’‘
Alex Wendte
Alex Wendte

πŸ’‘
Monica Powell
Monica Powell

πŸ“–
Vitaly Sivkov
Vitaly Sivkov

πŸ’»
Weyert de Boer
Weyert de Boer

πŸ€” πŸ‘€ 🎨
EstebanMarin
EstebanMarin

πŸ“–
Victor Martins
Victor Martins

πŸ“–
Royston Shufflebotham
Royston Shufflebotham

πŸ› πŸ“– πŸ’‘
chrbala
chrbala

πŸ’»
Donavon West
Donavon West

πŸ’» πŸ“– πŸ€” ⚠️
Richard Maisano
Richard Maisano

πŸ’»
Marco Biedermann
Marco Biedermann

πŸ’» 🚧 ⚠️
Alex Zherdev
Alex Zherdev

πŸ› πŸ’»
AndrΓ© Matulionis dos Santos
AndrΓ© Matulionis dos Santos

πŸ’» πŸ’‘ ⚠️
Daniel K.
Daniel K.

πŸ› πŸ’» πŸ€” ⚠️ πŸ‘€
mohamedmagdy17593
mohamedmagdy17593

πŸ’»
Loren ☺️
Loren ☺️

πŸ“–
MarkFalconbridge
MarkFalconbridge

πŸ› πŸ’»
Vinicius
Vinicius

πŸ“– πŸ’‘
Peter Schyma
Peter Schyma

πŸ’»
Ian Schmitz
Ian Schmitz

πŸ“–
Joel Marcotte
Joel Marcotte

πŸ› ⚠️ πŸ’»
Alejandro Dustet
Alejandro Dustet

πŸ›
Brandon Carroll
Brandon Carroll

πŸ“–
Lucas Machado
Lucas Machado

πŸ“–
Pascal Duez
Pascal Duez

πŸ“¦
Minh Nguyen
Minh Nguyen

πŸ’»
LiaoJimmy
LiaoJimmy

πŸ“–
Sunil Pai
Sunil Pai

πŸ’» ⚠️
Dan Abramov
Dan Abramov

πŸ‘€
Christian Murphy
Christian Murphy

πŸš‡
Ivakhnenko Dmitry
Ivakhnenko Dmitry

πŸ’»
James George
James George

πŸ“–
JoΓ£o Fernandes
JoΓ£o Fernandes

πŸ“–
Alejandro Perea
Alejandro Perea

πŸ‘€
Nick McCurdy
Nick McCurdy

πŸ‘€ πŸ’¬ πŸš‡
Sebastian Silbermann
Sebastian Silbermann

πŸ‘€
AdriΓ  Fontcuberta
AdriΓ  Fontcuberta

πŸ‘€ πŸ“–
John Reilly
John Reilly

πŸ‘€
MichaΓ«l De Boey
MichaΓ«l De Boey

πŸ‘€ πŸ’»
Tim Yates
Tim Yates

πŸ‘€
Brian Donovan
Brian Donovan

πŸ’»
Noam Gabriel Jacobson
Noam Gabriel Jacobson

πŸ“–
Ronald van der Kooij
Ronald van der Kooij

⚠️ πŸ’»
Aayush Rajvanshi
Aayush Rajvanshi

πŸ“–
Ely Alamillo
Ely Alamillo

πŸ’» ⚠️
Daniel Afonso
Daniel Afonso

πŸ’» ⚠️
Laurens Bosscher
Laurens Bosscher

πŸ’»
Sakito Mukai
Sakito Mukai

πŸ“–
TΓΌrker Teke
TΓΌrker Teke

πŸ“–
Zach Brogan
Zach Brogan

πŸ’» ⚠️
Ryota Murakami
Ryota Murakami

πŸ“–
Michael Hottman
Michael Hottman

πŸ€”
Steven Fitzpatrick
Steven Fitzpatrick

πŸ›
Juan Je GarcΓ­a
Juan Je GarcΓ­a

πŸ“–
Championrunner
Championrunner

πŸ“–
Sam Tsai
Sam Tsai

πŸ’» ⚠️ πŸ“–
Christian Rackerseder
Christian Rackerseder

πŸ’»
Andrei Picus
Andrei Picus

πŸ› πŸ‘€
Artem Zakharchenko
Artem Zakharchenko

πŸ“–
Michael
Michael

πŸ“–
Braden Lee
Braden Lee

πŸ“–
Kamran Ayub
Kamran Ayub

πŸ’» ⚠️
Matan Borenkraout
Matan Borenkraout

πŸ’»
Ryan Bigg
Ryan Bigg

🚧
Anton Halim
Anton Halim

πŸ“–
Artem Malko
Artem Malko

πŸ’»
Gerrit Alex
Gerrit Alex

πŸ’»
Karthick Raja
Karthick Raja

πŸ’»
Abdelrahman Ashraf
Abdelrahman Ashraf

πŸ’»
Lidor Avitan
Lidor Avitan

πŸ“–
Jordan Harband
Jordan Harband

πŸ‘€ πŸ€”
Marco Moretti
Marco Moretti

πŸ’»
sanchit121
sanchit121

πŸ› πŸ’»
Solufa
Solufa

πŸ› πŸ’»
Ari PerkkiΓΆ
Ari PerkkiΓΆ

⚠️
Johannes Ewald
Johannes Ewald

πŸ’»
Angus J. Pope
Angus J. Pope

πŸ“–
Dominik Lesch
Dominik Lesch

πŸ“–
Marcos GΓ³mez
Marcos GΓ³mez

πŸ“–
Akash Shyam
Akash Shyam

πŸ›
Fabian Meumertzheim
Fabian Meumertzheim

πŸ’» πŸ›
Sebastian Malton
Sebastian Malton

πŸ› πŸ’»
Martin BΓΆttcher
Martin BΓΆttcher

πŸ’»
Dominik Dorfmeister
Dominik Dorfmeister

πŸ’»
Stephen Sauceda
Stephen Sauceda

πŸ“–
Colin Diesh
Colin Diesh

πŸ“–
Yusuke Iinuma
Yusuke Iinuma

πŸ’»
Jeff Way
Jeff Way

πŸ’»

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

MIT

react-testing-library's People

Contributors

alejandronanez avatar alexkrolick avatar allcontributors[bot] avatar andrewmat avatar antsmartian avatar benmonro avatar duncanleung avatar enikol avatar eps1lon avatar fredyc avatar gnapse avatar gpx avatar jpavon avatar just-boris avatar kentcdodds avatar markpollmann avatar matanbobi avatar michaeldeboey avatar miklet avatar nickmccurdy avatar nminhnguyen avatar pbomb avatar roystons avatar rvdkooy avatar samtsai avatar solufa avatar thchia avatar timbonicus avatar vctormb avatar weyert avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-testing-library's Issues

Documentation missing on getByText matching strategy

It should be clear that getByText does not match when there are leading or trailing words in the text.
I spend two hours trying to debug why foo did not match foo bar.
I admit I got a mislead by a lot of react and relay stuff, but you should consider including a simple sentence about this :)

Love this library, btw

Deprecate renderIntoDocument()

The fundamental (and great) aspect of this library is:

The more your tests resemble the way your software is used, the more confidence they can give you.

I've watched all your talks and you keep saying that Simulate is a bad idea (even Dan Abramov told us that, if I'm not wrong) and I really agree with you!

We all want to make testing easier, and the only confusing thing that I see in this great library is having two render methods.

My suggestion is that we just deprecate renderIntoDocument() and move renderIntoDocument()'s behavior to render().

I don't see any reason to use render() over renderIntoDocument().

Add debugging helper?

I know you're trying to keep this awesome library lightweight. In general, I advocate avoiding using Enzyme's .debug() method, but there are times where it's invaluable in hunting down a mistake in a test. I'm currently using a few helpers to supplement your library, like this:

/* debug.js */
import pretty from 'pretty'

export default element => {
  console.log(pretty(element.outerHTML))
}
/* changeInputValue.js */
import { Simulate } from 'react-testing-library'

export default (input, value) => {
  input.value = value
  Simulate.change(input)
}

Usage:

import { debug, changeInputValue } from 'helpers/react-testing-library'

it('Adds a new resource', () => {
    const { container, getByTestId, queryByTestId } = render(<App />)
    expect(queryByTestId('resource-1')).not.toBeInTheDOM()
    const input = getByTestId('textInput')
    changeInputValue(input, 'Test Value')
    Simulate.click(getByTestId('button-with-redux'))
    debug(container)
    expect(getByTestId('resource-1').textContent).toBe('Test Value')
})

Are you open to including helpers like this in the core library?

React.createElement: type is invalid when using antd layout component?

  • react-testing-library version: "^3.1.4"
  • react version: "^16.4.0"
  • node version: v8.9.0
  • npm (or yarn) version: 5.5.1
  • antd version: "^3.6.2",

Relevant code or config:

// Login component
import {default as LoginForm } from 'components/Login/Form'
import Layout from 'antd' // layout component https://ant.design/components/layout/

const Login = () => {
  return (
    <Layout>  {/*  this markup breaks the test */}
      <h2 data-testid='login-screen'>Login</h2>
      <LoginForm>
      </LoginForm>
    </Layout>
  )
}

export default Login
// test
import React from 'react'
import 'jest-dom/extend-expect';
import {cleanup, renderIntoDocument} from 'react-testing-library'
import {default as Login} from '../containers/Login/';

afterEach(cleanup)

describe('<Login />', () => {
  let comp
  beforeEach(() => {
    comp = renderIntoDocument(<Login />)
  })

  describe('when rendered', () => {
    it('then should show login welcome text', () => {
       const { getByTestId } = comp

      expect(getByTestId('login-screen', { selector: 'h2' })).toBeInTheDOM()
    })
  });
})

What you did:

I'm using CRA boiler plate and just run npm test

What happened:

React.createElement: type is invalid
image

Reproduction:

Problem description:

Not sure if it's a bug with jsdom or the test library?

Suggested solution:

Removing the Layout markup from the code would run the test successfully like so

```js
// Login component
import {default as LoginForm } from 'components/Login/Form'
import Layout from 'antd' // layout component https://ant.design/components/layout/

const Login = () => {
  return (
       <div>
            <h2 data-testid='login-screen'>Login</h2>
            <LoginForm>
            </LoginForm>
       <div>
  )
}

export default Login
<!--
It's ok if you don't have a suggested solution, but it really helps if you could
do a little digging to come up with some suggestion of how to improve things.
-->

How to make this extend-expect work in Typescript?

extend-expect throws a couple more things onto extend and of course typescript doesnt like that. Is there a way to also expand the types or otherwise modify index.d.ts to accommodate for the changed type signature of expect?

for google purposes this is the error i get:

[ts] Property 'toHaveTextContent' does not exist on type 'Matchers<void>'.
any

TypeError: Cannot read property textContent of null when you expect a property on a missing element queried by data-testid

  • react-testing-library version: 1.0.1
  • node version: n/a
  • npm (or yarn) version: n/a

Relevant code or config

expect(queryByTestId('greeting-text').textContent).toBe('hello there')

What you did:

Did not render the element with 'greeting-text' identifier.

What happened:

TypeError: Cannot read property textContent of null

Reproduction repository:

https://codesandbox.io/s/4q3zol71y9

Problem description:

The error does not describe what has gone wrong.

Suggested solution:

Throw a user-friendly error.

Supporting Older Versions of React?

  • react-testing-library version: 2.3.0
  • node version: 8.1.1
  • npm (or yarn) version: 5.1.0
  • react version: 0.14.7
  • react-dom version: 0.14.7

Relevant code or config

const reactTestingLibrary = require('react-testing-library');

What you did:

Simply importing react-testing-library when React-DOM is less than version 0.15.5 when react-addons-test-utils was deprecated in favor of using react-dom/test-utils.

What happened:

Error message:

Error: Cannot find module 'react-dom/test-utils'
    at Function.Module._resolveFilename (module.js:485:15)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    ...

Problem description:

I'm currently writing front end tests for some legacy code that had no existing tests in order to provide some security and confidence that the code will remain working as I upgrade to newer versions of libraries and improved app design.

Obviously, upgrading my version of React is a non-starter, since I'm writing these tests in preparation for doing just that.

Enzyme has failed me in multiple ways, simply being unable or unwilling to test what I need to test, though it does support React 14.

Suggested solution:

Adjust codebase to handle test-utils being retrieved from react-addons-test-utils and make any necessary adjustments to the available API under that condition. Or, provide an alternative plugin or import for react-testing-library that handles a specific older version.

At the very least, update the package.json file to warn against using versions of React prior to 0.15.5.

Cannot import extend-expect as per README.

Hello @kentcdodds ,

In the README, we have specified that we could use the extend assertions by importing:

import 'react-testing-library/extend-expect'

infact this throws an error:

  Cannot find module 'react-testing-library/extend-expect' from 'HelpTest.js'

only the below works:

import 'react-testing-library/dist/extend-expect'

Will debug this and let you know whats happening here.

Add debug method

Describe the feature you'd like:

When I'm writing my tests I often find myself in a state when I need to know what's going on on the page. To accomplish this I usually write something like this: getByText('something that for sure is not on the page'). This way I have an error and the HTML is shown. I think it would be useful to expose a debug method that does more or less the same thing.

Suggested implementation:

I guess we can reuse the same code used to show error messages

Describe alternatives you've considered:

I tried to console.log(container.innerHTML) but it's all on the same line and hard to follow.

Teachability, Documentation, Adoption, Migration Strategy:

Encourage using aria selectors

I've been in a discussion @neoziro on my blog where he suggests using aria- attributes and text content instead of data-testid. He makes great points and I think that it would be great if this library could provide selectors to encourage this behavior. I'm thinking that aria attributes wont always make sense for every test, in which case we could fallback to data-testid, and for localization reasons text content may not make the most sense either, but making it easier to select based on text content could still make sense for some use cases and we could explain what those are.

So, out of this issue, I'd like to see some examples of what it would take to make utilities for selecting based on aria attributes as well as text content. If it works out, then we could recommend the following order of preference for selecting:

  1. Text Content (99% of your users will be using your app by looking at the text content, so if you don't have issues with localization then this should be the best)
  2. aria- attributes (your app should be accessible anyway, and this would encourage that as well).
  3. data-testids because they're way better than using class names, dom structure, or querying by React component classes.

Anyone want to try doing this?

Best approach to verify props pass through

I have a component which should pass props through to it's child. What's the best way to verify this?

import React, { PureComponent } from 'react'
import classnames from 'classnames'

import styles from './styles.css'

export default class Box extends PureComponent {
    render() {
        const {
            children,
            className,
            direction,
            element: Elem = 'div',
            ...props
        } = this.props
        const _className = classnames([
            className,
            direction && styles[direction],
        ])

        return (
            <Elem className={_className} {...props}>
                {children}
            </Elem>
        )
    }
}

My current solution is to pass a data- prop but feels like there should be a better way.

import _ from 'lodash'
import React from 'react'
import { render } from 'react-testing-library'

import Box from '../index'

test('pass through props', () => {
    const { container } = render(
        <Box data-my-custom-prop="my-custom-prop">
            <div>foo</div>
        </Box>,
    )

    expect(container.firstChild.dataset).toMatchSnapshot()
})

TypeScript fails: A type literal property cannot have an initializer

  • react-testing-library version: 3.0.1
  • react version: 16.3.2
  • node version: 8.11.1
  • npm version: 5.6.0
  • TypeScript version: 2.8.3

It seems like there is a problem with the typings for TypeScript. If I'm import the library and try to compile TypeScript fails.

import {  } from 'react-testing-library';
/home/node/app/client/node_modules/react-testing-library/typings/index.d.ts
(6,21): A type literal property cannot have an initializer.

Tried to render a non renderable component

Thanks for making this project. This will simply a lot of my tests and test configs.
I am starting to replace enzyme with it wherever possible

  • react-testing-library version: 1.6.0
  • node version: 9.4
  • npm (or yarn) version: 1.5.1
  • jest version: 22.4.2

Relevant code or config

import { render } from 'react-testing-library'

global.console = {
  error: jest.fn(),
}

...

test('should warn incase child is not provided', () => {
  render(<TestComponent />)
  expect(global.console.error).toHaveBeenCalledWith(`You must provide children`)
})

What you did:
Tried to render a non renderable component , to test if my custom console errors are thrown

What happened:
render throws error on non-renderable component rather than silently failing

Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

Reproduction repository:
currently i am playing with react-testing-library and trying to replace enzyme on a small react util i wrote.

This is a sample test currently written in enzyme, but on my local branch i am replacing it react-testing-library.
https://github.com/shrynx/react-render-function/blob/master/tests/index.test.js#L118

Problem description:
TestComponent shouldn't be able to render without Children, and for missing Children i am throwing errors from inside TestComponent which i want to test for using jest (i.e, test console.error message).
But the test fails at render itself throwing, rather than throwing my error.

Earlier i was using enzyne and used shallow in the same way i used render above and this used to work.

Suggested solution:
The render should fail silently or better if could be configured to not throw error, maybe like

const {container, unmount} = render(<Component />, {renderError: false})

I can try making a PR for it. should be easy to put try catch on ReactDOM.render and not throwing errors only if user has configured not to.

Cannot find module 'expect' from 'extend-expect.js'

This only happens when I include import 'react-testing-library/extend-expect'. Otherwise react-testing-library works well. What might be the cause of this problem?

I am using latest create-react-app.

Error:

 FAIL  src/App.test.js
  ● Test suite failed to run

    Cannot find module 'expect' from 'extend-expect.js'

      at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:179:17)
      at Object.<anonymous> (node_modules/react-testing-library/dist/extend-expect.js:7:15)

Failing test:

it('should have a title', () => {
  const { getByText } = render(<App />);
  const text = 'Welcome to React';
  expect(getByText(text)).toBeInTheDOM();
});

Passing test:

it('should have a title', () => {
  const { getByText } = render(<App />);
  const text = 'Welcome to React';
  expect(getByText(text).innerHTML).toBe(text);
});

Add support for getByAltText

I've actually already implemented this. Normally I would have made a PR for it but I needed it right away. Just letting all you folks who are watching the repo know that this is now something you can do! Check the docs :)

Expose sel()

React Native Web's testID prop emits data-testid instead of data-test. Would be great if this could be set. Seems like you've already thought about it.

Awesome lib Kent!

Any possibility of using this for react native project?

I really appreciate the guiding principles behind this library, have felt some of the pain associated with the drawbacks you all have identified with enzyme, and I'm curious how much work would be involved to get this library to work for a react native project? Given the reliance on DOM testing, I'm guessing it might not be possible or a good idea. Any guidance is greatly appreciated.

rename data-testid to data-test-id

Describe the feature you'd like:

Just a renaming of the attribute data-testid to data-test-id or create a custom babel plugin (fork) of https://www.npmjs.com/package/babel-plugin-jsx-remove-data-test-id which removes the attributes on prod builds.

Suggested implementation:

Describe alternatives you've considered:

Hmm... I'll create a fork of the babel plugin for my self πŸ˜†

Teachability, Documentation, Adoption, Migration Strategy:

It's a breaking change for solution 1 or we have to maintain a babel plugin on solution 2

Unused variable in example code in docs

Relevant code or config:

In the documentation at https://chrisnoring.gitbooks.io/react/content/testing/react-testing-library.html there is this example.

it('load data', async() => {
    const {getByText, getByTestId, getByPlaceholderText, container} = render(<Select />);

    Simulate.click(getByText('Load'));
    await wait(() => getByTestId('data'))
    const data = getByTestId('data')
    const elem = getByTestId('item');
    expect(elem).toHaveTextContent('test');
  })

Problem description:

The variable data is never used, so the line const data = getByTestId('data') is unnecessary since it doesn't have any effect on the outcome of the test.

Suggested solution:

Remove the line const data = getByTestId('data')

Typo in TextMatch example

In the second getByText example of TextMatch, the closing parentesis should be at the end of the statement:

getByText(container, 'llo Worl', {exact: false}) instead of

getByText(container, 'llo Worl'), {exact: false}

Set default LANG

  • react-testing-library version: 3.1.3
  • react version: 16.3.1
  • node version: 8.11.2
  • yarn version: 1.6.0

What you did:

I wanted to learn how to test React app and found this library promising. I want to mount my main component and test the title in it. I use react-i18next to handle translation. My default lang is French but my environment is in English. I have EN translation so the test try to load the project in english.

How can I specify the lang ? How do the test look for my env lang ? Do I have to launch my test setting up an environment variable ?

Can't access the component's instance

  • react-testing-library version: 2.1.1
  • node version: 9.4.0
  • npm (or yarn) version: 5.6.0

Relevant code or config

I'm trying to test this component

import React from 'react'
import pick from 'lodash.pick'
import { fetchTrips } from '../../../../../api/trips'

class TripListState extends React.Component {
  static defaultProps = { fetchTrips }

  state = { status: 'LOADING', filter: 'ALL' }

  async componentDidMount() {
    const trips = await this.props.fetchTrips('ALL')
    this.setState({ trips, status: 'READY' })
  }

  handleChangeFilter = async filter => {
    this.setState({ filter, status: 'FILTERING' })
    const trips = await this.props.fetchTrips(filter)
    this.setState({ trips, status: 'READY' })
  }

  render() {
    return this.props.children({
      ...pick(this.state, ['status', 'trips', 'filter']),
      onChangeFilter: this.handleChangeFilter,
    })
  }
}

export default TripListState

It simpliy fetches a list of trips and and exposes a method to change the filter value.

What you did:

This is my attempt to test it:

import React from 'react'
import { render, wait } from 'react-testing-library'
import TripListState from './TripListState'

describe('<TripListState>', () => {
  it('should fetch the trips on load', async () => {
    const children = jest.fn(() => null)
    const trips = [{ id: 1 }]
    // I pass this as a prop so I can avoid mocking the dependency
    const fetchTrips = x => Promise.resolve(trips)
    render(
      <TripListState children={children} fetchTrips={fetchTrips} />
    )
    // This works just fine
    expect(children).toHaveBeenCalledTimes(1)
    // This errors out
    expect(children).toHaveBeenCalledWith({
      status: 'LOADING',
      filter: 'ALL',
      onChangeFilter: /* Not sure what to put here */
    })
  })
})

What happened:

fail-test

Problem description:

The problem is that I can't access my component's instance and its methods. Not sure what the best approach is here.

Suggested solution:

I'm unsure of this, maybe I'm using react-testing-libraray for something it was not intended to, or maybe my testing approach is wrong. I'm hoping for suggestions :)

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this πŸ’ͺ.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


The push permission to the Git repository is required.

semantic-release cannot push the version tag to the branch master on remote Git repository with URL git+https://github.com/kentcdodds/react-testing-library.git.

Please refer to the authentication configuration documentation to configure the Git credentials on your CI environment and make sure the repositoryUrl is configured with a valid Git URL.


Good luck with your project ✨

Your semantic-release bot πŸ“¦πŸš€

getByTestId vs Others

First: @kentcdodds, thank you for this libray and also for all your eye-opening articles. This helped us move to an integration-tests-first approach and made our test suite far more resilient.

I have a best practice question regarding this library.

The documentation names getByTestId an escape hatch, to be used exceptionally, and prefers getByText etc.

However in the article "Making your UI tests resilient to change" you show an example by Kyle Shevlin, where you disapprove the selection of elements by content. The reason: A single button title change (or total removal of a title, as in the example) would break many tests.

Could you please clarify your opinion on that topic?

Thanks in advance!

POST under _mocks_

Problem description:
Hey Kent,
I see the lib only has a get method under _mocks_, I guess it will be nice to have a POST method too. If that sounds good to you I can take a look at it.

Thanks!

Export the RenderResult interface in typings

  • react-testing-library version: 3.1.4
  • react version: N/A
  • node version: N/A
  • npm (or yarn) version: N/A

Relevant code or config:

Nothing relevant

What you did:

import { RenderResult } from 'react-testing-library';

What happened:

error in typescript compiler:

Module '"/Users/kna/work/project/skills/stib-skill-frontend/node_modules/react-testing-library/typings/index"' has no exported member 'RenderResult'.

Reproduction:

Try to import RenderResult in a test

Problem description:

Can't use type of the value returned the render() function.

Suggested solution:

exporting the RenderResult interface in file typings.index.d.ts
export interface RenderResult extends GetsAndQueries { // ... }

Bug with native checkbox

  • react-testing-library version: 3.1.3
  • react version: 16.3.2
  • node version: n/a
  • npm (or yarn) version: n/a

What you did:

Testing controlled native checkbox

What happened:

Controlled native checkbox stops working if there are tests.

Reproduction:

https://codesandbox.io/s/405p15qj80

Problem description:

This is very strange, but I was testing something in a different project and realised that there's some kind of bug with a controlled native checkbox if there's a test (any test really) with react-testing-library. It only happens with the native version, anything else works fine!

This is really strange and I can't find any reason why it's breaking like that. Do you have any clues? Thanks!

having ref in react component causes renderIntoDocument DOM to become empty

  • react-testing-library version: 3.1.0
  • react version: 16.3.2
  • node version: 8.9.1
  • npm (or yarn) version: 5.5.1

Relevant code or config:

describe("WebhookSender Fetch", () => {

    beforeEach(() => {
        Rendered = renderIntoDocument(componentMarkup);
    });

    afterEach(() => {
        cleanup();
        fetch.resetMocks();
    });

    it("should show render results", async (done) => {

        fetch.mockReject(fetchRejectMsg);
        const sendBtnNode = Rendered.getByText('Send');
        fireEvent(sendBtnNode, new MouseEvent('click', {
            bubbles: true, 
            cancelable: true,
          }));

        await wait(() => Rendered.getByText(fetchRejectMsg));
        expect(Rendered.getByText(fetchRejectMsg, {exact: false})).toBeInTheDOM();
        done();
    });

});

What you did:

The test is successful if I comment out the following div with ref, in my react component being tested:

<div ref={(bottom) => { this.fetchBottom = bottom; }}>
    <p>&nbsp;</p>
</div>

What happened:

When the div with ref is there, the test fails and the console shows

console.error node_modules/react-dom/cjs/react-dom.development.js:9643
  The above error occurred in the <WebhookSender> component:
      in WebhookSender (at WebhookSender.spec.js:18)
      in MuiThemeProvider (at WebhookSender.spec.js:17)
  
  Consider adding an error boundary to your tree to customize error handling behavior.
  Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
console.error node_modules/fbjs/lib/warning.js:33
  Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
      in WebhookSender (at WebhookSender.spec.js:18)
      in MuiThemeProvider (at WebhookSender.spec.js:17)

Reproduction:

Problem description:

I find that if I have the ref in my react component, after the button click, the DOM (look at PrettyDOM(Rendered.container)) becomes just an empty div , thus failing the subsequent getByText.

Is there known issue with having ref for such tests?

Suggested solution:

flushPromises() once again - how about waitForExpect

Hey Kent!
Fantastic work!

I was doing an intro to TDD workshop yesterday and decided to use your library instead of enzyme, as I did in the past. I also did much less unity-style of testing, following the principle of "use it as a user would" instead. It went a bit less smooth than I would like, but the guys were excited and liked it, for the most part. :-) I think I told you about this - we just code for hours on end without touching the browser, and then in the end, after the last test turns green, we open our browsers hoping for the best.. this time, 5/5 apps worked, it feels like magic. And with your framework we were able to do this MUCH quicker than before, writing many fewer tests, but still ending up having the same end result - very high level of confidence in the app. And easier to refactor. :)

To the point - there was one thing missing for me in this kind of testing when I was quickly rewriting my notes.

I have a component that loads a data from an external API. I mock it everywhere else, but in one place I do the full e2e call, to verify that it's correct.
https://github.com/TheBrainFamily/TDD-NASA-POD/blob/final/src/__tests__/UnmockedPicture.test.js#L19

test("Displays a title, description and link for a picture after fetching it from actual API server", async () => {
  const { getByTestId, getByText } = render(
    <Picture date={CONTRACT_TEST_DATE} />
  );

  expect(getByText("Loading...")).toBeTruthy();

  await waitForExpect(() => {
    expect(getByTestId("title").textContent).toEqual(
      "NGC 1316: After Galaxies Collide"
    );
    expect(getByTestId("description").textContent).toMatch(
      /An example of violence on a cosmic scale/
    );
    expect(getByTestId("link").src).toMatch(/1024d/);
  });
});

I want to see the loader, and then the component actually renders the fetched data. But it comes back at a random time and I definitely NOT want to put my expectation in setTimeout(). So I went ahead and created a simple tool called waitForExpect ( https://github.com/TheBrainFamily/wait-for-expect/ - btw, looks like one of your guys at PayPal already somehow found it and starred it! Made my day ;-) ).

It basically repeats the expectation over and over and in the end - if things are not working correctly - instead of timing out it gives you a familiar from your test runner error.

I thought it would be great to use it also for the mocked calls -
https://github.com/TheBrainFamily/TDD-NASA-POD/blob/final/src/__tests__/App.test.js#L119

Thinking as a user - I don't want to think about flushing promises, isn't this a very low-level detail in otherwise such a nicely user/developer-oriented library? :) I remember the first time I stumbled upon this problem with react, it took me a few hours of debugging to figure out how to get this to work. I think you can be a very successful frontend dev without understanding the problem of flushing promises and what is setImmediate doing.

The thing that makes me feel like this is the right way (but I can be wrong! wouldn't be the first time), is the fact that I use the same abstraction in both mocked and integration (or e2e) testing.
If I set up my mock that way and programmatically turn it off/on I could basically run the same test, one with a remote server, and one with mocked data, having this way a really nice contract test.

I was curious what would you think about recommending (or including) this tiny tool in your docs?

Thanks!

Edited: there is one more thing I forgot to mention. When after a result of flushing promises another promise is queued, you have to flush promises twice. This happens for example when using a mockednetworkinterface with apollo and components wrapped with multiple queries. And then those components in turn run more queries. They fire one after another and I ended up adding tens of those flushes in a helper to get it to work. Now I will just use this waitForExpect instead and just stop worrying :-)

Feature Proposal - include and export renderWithRedux/Router

When @kentcdodds published an article about doing more integration tests using, we did a POC and right away we spotted a couple of defects in our app.

We put together a small util that would allow us to renderScreen or renderApp. This util saved us from provisioning the store, default state. The redux examples of this project show a very similar method "renderWithRedux".

When we saw this module coming alive we stopped adding any type of feature to our util and started planning to migrate to this module instead.

So we are wondering if this community would consider as part of the scope of this module adding such features:

  • renderScreen
  • renderApp

Our understanding is that the React/Redux/Router combination is common enough in the react community that a feature like this would cater to a significant audience.

Maybe the particularities of each project make this proposal invalid. Anyway, before preparing a PR we wanted to socialize it.

Thank you for this great and simple solution.

toHaveAttribute custom matcher

I'm finding my self commonly checking if an element has a given attribute or not, and possibly also expecting a certain value off some attribute.

expect(getByTestId('ok-button').hasAttribute('disabled')).toBeTruthy()
expect(getByTestId('dropdown-toggle').getAttribute('aria-expanded')).toEqual('false')

I wonder if it would be acceptable to the goals and intentions of this library to provide certain other custom matchers for common tasks. In this particular case I'd be proposing a .toHaveAttribute custom matcher that could work both to assert that an element has an attribute present or not, but also to assert that the attribute is present and has a certain value:

expect(getByTestId('ok-button')).toHaveAttribute('disabled')
expect(getByTestId('dropdown-toggle')).toHaveAttribute('aria-expanded', 'false')

It could be considered syntactic sugar, but I think it makes tests more readable. Although I'd also understand if these are considered out of the scope of this library.

Tests don't fail gracefully when using wait

  • react-testing-library version:
    "react-testing-library": "^2.5.1",
  • react version:
    "react": "^16.3.0",
  • node version:
    v8.9.4
  • npm (or yarn) version:
    5.6.0

Relevant code or config:

// Test
import sinon from "sinon"
import { Simulate, wait } from "react-testing-library"
import { client } from "../../../_old/graphql/apolloClient"
import { mountApp } from "../../../test/testBehaviors"

jest.mock("../../../_old/graphql/apolloClient")

afterEach(() => {
  client.resetStore()
})

describe("Given we load the App", () => {
  describe("When data is returned and there is no user ", () => {
    describe("And we enter a valid username and password and click submit", () => {
      it("Then we call the login mutation with correct arguments and are redirected to the search page", async () => {
        const userResolverStub = sinon.stub()

        userResolverStub.onFirstCall().returns(null)

        userResolverStub.onSecondCall().returns({
          id: "fakeId",
          email: "fakeEmail",
          role: "fakeRole",
          firstName: "fakeFirstName",
          lastName: "fakeLastName",
          algoliaSearchApiKey: "fakeAlgoliaSearchApiKey"
        })

        const loginMuationMock = sinon.expectation.create("loginMutationMock")

        loginMuationMock
          .withArgs(sinon.match.any, {
            input: { email: "[email protected]", password: "fakePassword" }
          })
          .once()
          .returns({
            user: {
              id: "fakeId",
              email: "fakeEmail",
              role: "fakeRole",
              firstName: "fakeFirstName",
              lastName: "fakeLastName",
              algoliaSearchApiKey: "fakeAlgoliaSearchApiKey"
            },
            company: {
              hasPurchasing: true
            }
          })

        const mocks = {
          User: userResolverStub,
          Mutation: () => ({
            // This needed to match the name of the mutation in MutationType.js
            login: loginMuationMock
          })
        }

        const { getByTestId } = mountApp({ mocks })

        await wait(() => {
          const emailTextBox = getByTestId("emailTextBox")

          emailTextBox.value = "[email protected]"

          Simulate.change(emailTextBox)
        })

        await wait(() => {
          const passwordTextBox = getByTestId("passwordTextBox")

          passwordTextBox.value = "fakePassword"

          Simulate.change(passwordTextBox)
        })

        await wait(() => {
          const loginForm = getByTestId("loginButton")

          Simulate.submit(loginForm)
        })

        await wait(() => {
          loginMuationMock.verify()
          expect(getByTestId("search-page")).toBeTruthy()
        })
      })
    })
  })
})

// Component
import _ from "lodash"
import React, { Component } from "react"
import { Link, withRouter } from "react-router-dom"
import styled from "styled-components"
import { withLogin } from "../graphql/mutations/loginMutation"
import { ERRORS } from "../../../../errors"
import { Routes } from "../../../../Routes/index"
import Alert from "../../../../sharedComponents/Alert/index"
import BackgroundButton, {
  Colors
} from "../../../../sharedComponents/forms/BackgroundButton"
import Input from "../../../../sharedComponents/forms/Input"
import GuestContainer from "../../../../sharedContainers/GuestContainer/index"

const LoginStyled = styled(GuestContainer)`
  .password-container {
    position: relative;

    .forgot-password-link {
      position: absolute;
      right: 0;
      top: 33px;
      font-size: 10px;
      font-weight: 100;
      text-transform: uppercase;
    }
  }
`
class Login extends Component {
  state = {
    email: "",
    password: ""
  }

  handleChange = (name, value) => this.setState({ [name]: value })

  login = () => {
    const { email, password } = this.state
    this.props
      .mutate({
        variables: { 
          input: { 
            email, 
            password: password + 'changed'  // <==== trying to break the test
          }} 
      })
      .then(() =>
        this.props.history.push(
          _.get(this.props, "location.state.from", Routes.ROOT)
        )
      )
      .catch(({ graphQLErrors }) => {
        switch (graphQLErrors[0].message) {
          case ERRORS.WrongEmailOrPassword:
            Alert.error("Email or password is incorrect")
            break
          default:
            break
        }
      })
  }

  handleSubmit = e => {
    e.preventDefault()
    this.login()
  }

  render() {
    const { email, password } = this.state
    return (
      <LoginStyled header="Welcome Back" subHeader="Your kitchen awaits">
        <form data-testid="login-form" onSubmit={this.handleSubmit}>
          <Input
            data-testid="emailTextBox"
            value={email}
            label="Email"
            onChange={e => this.handleChange("email", e.target.value)}
            autoFocus
          />

          <div className="password-container">
            <Input
              data-testid="passwordTextBox"
              value={password}
              type="password"
              label="Password"
              onChange={e => this.handleChange("password", e.target.value)}
            />

            <Link
              className="forgot-password-link"
              to={`${Routes.RESET}?email=${email}`}
            >
              Forgot password?
            </Link>
          </div>

          <BackgroundButton
            data-testid="loginButton"
            noHorizontalPadding
            color={Colors.PAPAYA}
            type="submit"
            disabled={!(email && password)}
          >
            Login
          </BackgroundButton>
        </form>
      </LoginStyled>
    )
  }
}

export default withLogin(withRouter(Login))

What you did:

I got the above test working and then I wanted to verify that it would break so I changed the password code like this password: password + 'changed' so that the test would fail and I could make sure the error was good and all that.

What happened:

The test failed eventually but it took a really long time and then didn't provided useful error feedback. We were instead getting a timeout error.

Reproduction:

I think this reproduces the issue. This seems to run for a really long time and never provide useful error feedback.

https://codesandbox.io/s/rjozql4jnm

Problem description:

We need the tests to fail fast and report back a useful error.

Suggested solution:

wait should run several times quickly and if it's not able to get a clean run with no errors then it should Promise.reject(error) with the error was thrown by the callback function.

engines config breaks deploys to Netlify

  • react-testing-library version: 1.4.0
  • node version: 6.13.1
  • npm (or yarn) version: yarn 0.18.1

Relevant code or config:

react-testing-library package.json:

"engines": {
  "node": ">=8",
  "npm": ">=5"
},

What you did:

  • deployed existing site to Netlify after adding react-testing-library to devDependencies

What happened:

  • Netlify deploy failed:
7:57:52 PM: error [email protected]: The engine "node" is incompatible with this module. Expected version ">=8".
7:57:52 PM: error Found incompatible module

Problem description:

  • react-testing-library uses a somewhat restrictive engines stanza in package.json

Suggested solution:

Variables in JSX strings put on new line

  • react-testing-library version: 2.1.0
  • node version: 8.9.3
  • npm (or yarn) version: yarn 1.3.2

Relevant code or config

Component to be tested:

const Component = ({ currentStep }) => <div>Step {currentStep} of 4</div>

What you did:

Test code:

const { getByText } = render(<Component currentStep={1} />)

expect(getByText('Step 1 of 4')).toBeDefined()

What happened:

Unable to find an element with the text: Step 1 of 4. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

<div>
    Step
    1 // <--------- newline
      of 4
 </div>

Reproduction repository:

This can be reproduced in the current repository test cases by inserting a variable into the JSX.

Problem description:

Variables in JSX strings cause new line issues (and failing tests). I believe that in the spirit of "not testing implementation details", this should be considered a harmful problem. If I add an extra space around the "1", the test passes (period signifies space):

expect(getByText('Step..1..of 4')).toBeDefined() // passing

Suggested solution:

I don't know why this is happening, but I'm guessing it is something to do with how the component is being serialised. I'm also guessing that this issue should be here and not dom-testing-library but I don't know for sure where the root of the problem lies.

Simulating uploading file

Is there a way to simulate or fire event to trigger change event on input that is used for uploading files?

Explore alternative event dispatch/simulation

Based on a twitter conversation with Dan Abramov where he suggests that this dispatch events rather than use TestUtils.Simulate, I think it may be worth looking into. As he mentioned:

Just note dispatchEvent will only work if you enable bubbling and node is in the document

I think adding the container to the document should be fine normally, especially in Jest where the test files run in isolated environments. But I'm a little bit worried that we'd fill up the document with mounted components that never get unmounted. We could encourage people to have a beforeEach that cleans things up, but that'd be annoying and reduce the simplicity. We could simplify it with a import 'react-testing-library/autoclean' or something, but still that's a reduction in simplicity. So I'm not certain how to solve this problem...

It would be cool if we could have a utility that does actual events and also encourage people to just use .click() and .focus() methods native to DOM nodes. So I'd definitely like to explore this.

Simulate.click on a submit button

  • react-testing-library version: 2.1.1
  • node version: 8.9.3
  • npm (or yarn) version: yarn 1.3.2

Relevant code or config

const Component = () => {
  return <form onSubmit={handleSubmit}>
    <button type="submit">Submit</button>
  </form>
}

What you did:

const submitButton = getByText('Submit')

// does not work
Simulate.click(submitButton)

// works
Simulate.submit(submitButton)

What happened:

Submit buttons cannot be clicked using Simulate.click.

Problem description:

This is not a bug as far as Simulate is concerned, but I think that in the spirit of the Guiding Principles, resorting to calling .submit should be discouraged. Users click submit buttons, they do not invoke the submit event.

Certain libraries like react-final-form rely on the submit event to display validation errors. If for example, you have logic to disable a submit button from being clickable, calling Simulate.submit will ignore this and call the submit event anyway. This can result in incorrect assertions.

Suggested solution:

Add a quick caveat in the docs under Simulate. Note that calling

submitButton.dispatchEvent(new MouseEvent('click'))

can solve this issue. I am good to submit a PR if this is deemed worth it.

EDIT: I guess this is technically covered in the fireEvent use case...

Change props on same component instance

  • react-testing-library version: 2.3.0
  • node version: 9.9.0
  • yarn version: 1.5.1

No way to update props on same component instance like enzyme's

wrapper.setProps({ url: url1 });

What you did:

I'm looking to port react-fetch-component to use react-testing-library mostly due to Enzyme incomplete React 16 support especially for React.createContext() which I plan to use for a feature. As of now they have a merged fixed but has not been released yet.

With that said, I'm having difficulty porting over to react-testing-library. I understand it's goal of encouraging good testing practices by not accessing an instance of a component. Pretty much all of my tests were accessing the promises instance prop to coordinate when a fetch was complete / etc. For most of these cases, I've been able to wait() for the number of mock calls to match my expectations (not exactly my intentions, but it works).

await wait(() => expect(mockChildren.mock.calls.length).toBe(3));

One case where I'm having difficultly working around this issue is not having an equivalent setProps() capability like enzyme. I have a test that renders the <Fetch /> component 3 times with cache enabled which is by default instance level. With enzyme's setProps I can guarantee the same instance is being used, but I don't see a way to do this with react-testing-library.

What happened:

Unable to change props

Suggested solution:

Expose a setProps equivalent helper from render (although this might be against the goal of the library).

prettyDOM is not exported from typings file

prettyDOM should be re-exported by react-testing-library's typings file. I will file a PR after entering this.

  • react-testing-library version: 3.1.3
  • react version: N/A
  • node version: N/A
  • npm (or yarn) version: N/A

Relevant code or config:

N/A

What you did:

import { prettyDOM } from 'react-testing-library'

What happened:

Typescript compiler errors w/

error TS2305: Module '"..../node_modules/react-testing-library/typings/index"' has no exported member 'prettyDOM'.

Reproduction:

N/A

Problem description:

Breaks Typescript compiler

Suggested solution:

Simply adding export { prettyDOM } from 'dom-testing-library' to typings/index.d.ts will fix this issue.

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this πŸ’ͺ.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


The push permission to the Git repository is required.

semantic-release cannot push the version tag to the branch master on remote Git repository.

Please refer to the authentication configuration documentation to configure the Git credentials on your CI environment.


Good luck with your project ✨

Your semantic-release bot πŸ“¦πŸš€

How to use with redux-little-router

  • react-testing-library version: 1.10.0
  • node version: 8.9.4
  • npm (or yarn) version: 5.6.0

Relevant code or config

I used the renderWithRedux util from the redux example:

export default (
  ui,
  {
    initialState,
    store = configureStore(initialState),
  } = {},
) => ({
  // adding `store` to the returned utilities to allow us
  // to reference it in our tests (just try to avoid using
  // this to test implementation details).
  ...render(<Provider store={store}>{ui}</Provider>),
  store,
})

What you did:

(Note: I'm not sure if this issue belongs here or in the redux-little-router repo. Also, I tried looking at the react-router example to get ideas of how to work around this.)

I wrote a test that simulates some actions that modify redux state and then clicks on a link that goes to another route. I would like to then verify that the UI on this page is correct per redux state.

What happened:

The component tree reflects that no routing happened (stays on the same page).

Reproduction repository:

Problem description:

Suggested solution:

How to test React.Portal

react-testing-library: 2.1.1
node: 8.9.3
yarn: 1.6.0

import React from "react";
import { render, Simulate } from "react-testing-library";

import Button from "material-ui/Button";
import Dialog, {
  DialogActions,
  DialogContent,
  DialogTitle
} from "material-ui/Dialog";

export const CommonDialog = props => {
  const { body, children, hideModal, isVisible, title } = props;
  return (
    <Dialog open={isVisible}>
      <DialogTitle>{title}</DialogTitle>
      <DialogContent>
        {body}
        {children}
      </DialogContent>
      <DialogActions>
        <Button id="close" onClick={hideModal}>
          Close
        </Button>
      </DialogActions>
    </Dialog>
  );
};

test("render dialog", () => {
  const mockCallback = jest.fn();
  const { getByText, getByTestId, container } = render(
    <CommonDialog title="test" isVisible={true} hideModal={mockCallback} />
  );
  Simulate.click(getByText("Close"));
  expect(mockCallback).toBeCalled();
  expect(container).toMatchSnapshot();
});

in the snapshot, it is just a simple div, and the Close button could not be found. It is not immediately not obvious what's went wrong here.
I was using enzyme and it is working fine.

No extend-expect type definitions

  • react-testing-library version: 1.9.3
  • node version: 9.2.1

What you did:
Created fresh project from CRA + typescript scripts and then wrote a simple test

test('renders App without crashing', () => {

  const { queryByTestId } = render(<App/>);

  expect(queryByTestId('app-hoc')).toBeInTheDOM();
});

What happened:
The test is passing but the project does not build. Throwing this error:

Property 'toBeInTheDOM' does not exist on type 'Matchers'.

Problem description:
Typescript won't allow building project when we are using property which does not exist on some particular type (Matchers in this case)

Suggested solution:
Extend jest expect typings with a new types definition for methods from extend-expect.js

Simulate doesnt export method types (TypeScript)

surprised no one ran into this yet but when you try using Simulate.click, this happens:

[ts] Cannot invoke an expression whose type lacks a call signature. Type 'void' has no compatible call signatures.
(alias) const Simulate: typeof ReactSimulate
import Simulate

currently i'm trying to rewrite index.d.ts as:

import  * as Utils  from 'react-dom/test-utils';
const { Simulate } = Utils;
const ReactSimulate = Simulate;

export Simulate: typeof ReactSimulate;

but this doesnt seem to be valid (i'm not super experienced at TS). anyway figured i'd report this, as someone else probably has the canonical "typescripty" way of doing this

Add updateProps function

As noted in the docs, if I want to update props I simply call render with the same container:

const {container, queryByTestId} = render(<NumberDisplay number={1} />)
expect(queryByTestId('number-display').textContent).toBe('1')

// re-render the same component with different props
// but pass the same container in the options argument.
// which will cause a re-render of the same instance (normal React behavior).
render(<NumberDisplay number={2} />, {container})
expect(queryByTestId('number-display').textContent).toBe('2')

But this is cumbersome and it'd be nice to just have an updateProps function instead. It could have a note to avoid using it when you can, but it would be nice to have.

I'm not 100% certain that I really want it. Just thinking... One of the nice things about this library is what it doesn't give you and how that encourages better tests. But maybe this wont be so bad... It's especially useful for reusable components (like downshift).

Breaking change proposal: Use exact string match by default

Describe the feature you'd like:

Make exact match the default instead of partial case-insensitive match. Regex would be recommended for those cases instead.

  • partial: /mystring/
  • case-insensitive partial: /mystring/i
  • prefix: /^mystring/
  • postfix: /mystring$/
  • case-insensitive full string: /^mystring$/i

One thing that would more involved to replicate with inline regexes is that the current implementation trims the string and replaces symbols with spaces:

- const normalizedText = textToMatch.trim().replace(/\s+/g, ' ')

See #74 (comment)

Suggested implementation:

I could see implementing this by exposing the final options argument to the getAllByAttribute helper to the public methods that use it so that exact: true could toggled.

https://github.com/kentcdodds/dom-testing-library/blob/master/src/queries.js#L73

Describe alternatives you've considered:

Leave the matcher alone

Teachability, Documentation, Adoption, Migration Strategy:

Show common regex patterns like the ones above^ in the docs.

Is this a breaking change?

Yes

Why does `flushPromises` work the way that it does

From https://twitter.com/Dru89/status/976472959345815552

I've seen examples before where flushPromises is written basically the way that you say:

const scheduler = typeof setImmedate === 'function' ? setImmediate : setTimeout;

export function flushPromises() {
  return new Promise(res => scheduler(res));
}

What I don't really understand is why this actually works. I'm assuming it has something to do with scheduling and how JavaScript actually handles asynchronous published behavior.

And relatedly:
Is this a "hack" that only works because of an implementation detail in Node/V8? (Would this break in a browser or in something like Chakra?) If it's not something in the spec, is it something that's likely to change?

Object.entries is not a function when importing react-testing-library

Reporting this as an issue along with the fix in hopes that it helps others find the answer quicker.

Problem
When importing react-testing-library and running my tests, they failed with the following error

Object.entries is not a function

Version Information

  • react-testing-library version: 3.1.4
  • react version: N/A
  • node version: 6.x
  • npm (or yarn) version: 6.x

Solution

Per @kentcdodds suggestion in a glamorous ticket:

Make sure you're on the latest stable node (>=8) and npm (>= 5). Sorry about that!

Upgrading to the latest version of node did indeed resolve my issue.

Simple text input value mutation.

  • react-testing-library version: 2.3.0
  • node version: v8.9.1
  • npm (or yarn) version: [email protected]

What you did:
I am just testing out this lib to see if I want to propose a switch from enzyme and I'm having trouble with the most basic stuff.

What happened:
Simply attempting to render a text input, snapshot it, change the value and snapshot it again.

Reproduction repository: https://github.com/hally9k/react-testing-library-example

Problem description: Changing the value of the text input doesn't reflect in the snapshot or invoke the onChange handler.

Suggested solution: Explain to me what I'm missing as I clearly misunderstand something here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.