Giter Club home page Giter Club logo

react-testing-library's Introduction

@testing-library/angular

Octopus with the Angular logo

Simple and complete Angular testing utilities that encourage good testing practices.


Read The Docs | Edit the docs



Build Status version downloads MIT License

All Contributors PRs Welcome Code of Conduct Discord

Watch on GitHub Star on GitHub Tweet

Open in GitHub Codespaces

Table of Contents

The problem

You want to write maintainable tests for your Angular 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.

This solution

The @testing-library/angular is a very lightweight solution for testing Angular components. It provides light utility functions on top of Angular and @testing-library/dom, 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.

Example

counter.component.ts

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="decrement()">-</button>
    <span>Current Count: {{ counter }}</span>
    <button (click)="increment()">+</button>
  `,
})
export class CounterComponent {
  @Input() counter = 0;

  increment() {
    this.counter += 1;
  }

  decrement() {
    this.counter -= 1;
  }
}

counter.component.spec.ts

import { render, screen, fireEvent } from '@testing-library/angular';
import { CounterComponent } from './counter.component';

describe('Counter', () => {
  test('should render counter', async () => {
    await render(CounterComponent, { componentProperties: { counter: 5 } });

    expect(screen.getByText('Current Count: 5'));
  });

  test('should increment the counter on click', async () => {
    await render(CounterComponent, { componentProperties: { counter: 5 } });

    const incrementButton = screen.getByRole('button', { name: '+' });
    fireEvent.click(incrementButton);

    expect(screen.getByText('Current Count: 6'));
  });
});

See more examples

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 @testing-library/angular --save-dev

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

Docs

Version compatibility

Angular Angular Testing Library
18.x 16.x, 15.x, 14.x, 13.x
17.x 16.x, 15.x, 14.x, 13.x
16.x 14.x, 13.x
>= 15.1 14.x, 13.x
< 15.1 12.x, 11.x
14.x 12.x, 11.x

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 Angular 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 Angular components or full Angular applications.
  3. Utility implementations and APIs should be simple and flexible.

At the end of the day, what we want is for this library to be pretty light-weight, simple, and understandable.

Contributors

Thanks goes to these people (emoji key):

Tim Deschryver
Tim Deschryver

πŸ’» πŸ“– πŸš‡ ⚠️
MichaΓ«l De Boey
MichaΓ«l De Boey

πŸ“–
Ignacio Le Fluk
Ignacio Le Fluk

πŸ’» ⚠️
TamΓ‘s SzabΓ³
TamΓ‘s SzabΓ³

πŸ’»
Gregor Woiwode
Gregor Woiwode

πŸ’»
Toni Villena
Toni Villena

πŸ› πŸ’» πŸ“– ⚠️
ShPelles
ShPelles

πŸ“–
Miluoshi
Miluoshi

πŸ’» ⚠️
Nick McCurdy
Nick McCurdy

πŸ“–
Srinivasan Sekar
Srinivasan Sekar

πŸ“–
Bitcollage
Bitcollage

πŸ“–
Emil Sundin
Emil Sundin

πŸ’»
Ombrax
Ombrax

πŸ’»
Rafael Santana
Rafael Santana

πŸ’» ⚠️ πŸ›
Benjamin Blackwood
Benjamin Blackwood

πŸ“– ⚠️
Gustavo Porto
Gustavo Porto

πŸ“–
Bo Vandersteene
Bo Vandersteene

πŸ’»
Janek
Janek

πŸ’» ⚠️
Gleb Irovich
Gleb Irovich

πŸ’» ⚠️
Arjen
Arjen

πŸ’» 🚧
Suguru Inatomi
Suguru Inatomi

πŸ’» πŸ€”
Amit Miran
Amit Miran

πŸš‡
Jan-Willem Willebrands
Jan-Willem Willebrands

πŸ’»
Sandro
Sandro

πŸ’» πŸ›
Michael Westphal
Michael Westphal

πŸ’» ⚠️
Lukas
Lukas

πŸ’»
Matan Borenkraout
Matan Borenkraout

🚧
mleimer
mleimer

πŸ“– ⚠️
MeIr
MeIr

πŸ› ⚠️
John Dengis
John Dengis

πŸ’» ⚠️
Rokas BrazdΕΎionis
Rokas BrazdΕΎionis

πŸ’»
Mateus Duraes
Mateus Duraes

πŸ’»
Josh Joseph
Josh Joseph

πŸ’» ⚠️
Torsten Knauf
Torsten Knauf

🚧
antischematic
antischematic

πŸ› πŸ€”
Florian Pabst
Florian Pabst

πŸ’»
Mark Goho
Mark Goho

🚧 πŸ“–
Jan-Willem Baart
Jan-Willem Baart

πŸ’» ⚠️

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

Docs

Read The Docs | Edit the docs

FAQ

I am using Reactive Forms and the jest-dom matcher toHaveFormValues always returns an empty object or there are missing fields. Why?

Only form elements with a name attribute will have their values passed to toHaveFormsValues.

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.

Getting started with GitHub Codespaces

To get started, create a codespace for this repository by clicking this πŸ‘‡

Open in GitHub Codespaces

A codespace will open in a web-based version of Visual Studio Code. The dev container is fully configured with software needed for this project.

Note: Dev containers is an open spec which is supported by GitHub Codespaces and other tools.

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

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

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:

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.

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!

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.

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

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?

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 :)

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).

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')

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

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.

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 :-)

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 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

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().

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 { // ... }

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.

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!

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 :)

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).

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:

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

Simulating uploading file

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

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?

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.

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!

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);
});

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:

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.

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.

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 πŸ“¦πŸš€

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...

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:

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.

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()
})

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.

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}

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.

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.

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.
-->

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!

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

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.

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 ?

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.

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?

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.

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.