diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6a7b4bff..115d96e0 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,9 @@ @@ -79,17 +83,10 @@ facilitate testing implementation details). Read more about this in * [`render`](#render) * [`Simulate`](#simulate) * [`wait`](#wait) -* [Custom Jest Matchers](#custom-jest-matchers) - * [`toBeInTheDOM`](#tobeinthedom) - * [`toHaveTextContent`](#tohavetextcontent) - * [`toHaveAttribute`](#tohaveattribute) - * [Custom Jest Matchers - Typescript](#custom-jest-matchers---typescript) * [`TextMatch`](#textmatch) * [`query` APIs](#query-apis) * [Examples](#examples) * [FAQ](#faq) -* [Deprecated APIs](#deprecated-apis) - * [`flushPromises`](#flushpromises) * [Other Solutions](#other-solutions) * [Guiding Principles](#guiding-principles) * [Contributors](#contributors) @@ -108,13 +105,17 @@ npm install --save-dev react-testing-library This library has a `peerDependencies` listing for `react-dom`. +You may also be interested in installing `dom-testing-library` so you can use +[the custom jest matchers](https://github.com/kentcdodds/dom-testing-library/blob/master/README.md#custom-jest-matchers) + ## Usage ```javascript // __tests__/fetch.js import React from 'react' import {render, Simulate, wait} from 'react-testing-library' -import 'react-testing-library/extend-expect' // this adds custom expect matchers +// this add custom expect matchers from dom-testing-library +import 'dom-testing-library/extend-expect' import axiosMock from 'axios' // the mock lives in a __mocks__ directory import Fetch from '../fetch' // see the tests for a full implementation @@ -312,94 +313,6 @@ The default `interval` is `50ms`. However it will run your callback immediately on the next tick of the event loop (in a `setTimeout`) before starting the intervals. -## Custom Jest Matchers - -There are two simple API which extend the `expect` API of jest for making assertions easier. - -### `toBeInTheDOM` - -This allows you to assert whether an element present in the DOM or not. - -```javascript -// add the custom expect matchers -import 'react-testing-library/extend-expect' - -// ... -const {queryByTestId} = render(2) -expect(queryByTestId('count-value')).toBeInTheDOM() -expect(queryByTestId('count-value1')).not.toBeInTheDOM() -// ... -``` - -> Note: when using `toBeInTheDOM`, make sure you use a query function -> (like `queryByTestId`) rather than a get function (like `getByTestId`). -> Otherwise the `get*` function could throw an error before your assertion. - -### `toHaveTextContent` - -This API allows you to check whether the given element has a text content or not. - -```javascript -// add the custom expect matchers -import 'react-testing-library/extend-expect' - -// ... -const {getByTestId} = render(2) -expect(getByTestId('count-value')).toHaveTextContent('2') -expect(getByTestId('count-value')).not.toHaveTextContent('21') -// ... -``` - -### `toHaveAttribute` - -This allows you to check wether the given element has an attribute or not. You -can also optionally check that the attribute has a specific expected value. - -```javascript -// add the custom expect matchers -import 'react-testing-library/extend-expect' - -// ... -const {getByTestId} = render( - , -) -expect(getByTestId('ok-button')).toHaveAttribute('disabled') -expect(getByTestId('ok-button')).toHaveAttribute('type', 'submit') -expect(getByTestId('ok-button')).not.toHaveAttribute('type', 'button') -// ... -``` - -### Custom Jest Matchers - Typescript - -When you use custom Jest Matchers with Typescript, you will need to extend the type signature of `jest.Matchers`, then cast the result of `expect` accordingly. Here's a handy usage example: - -```typescript -// this adds custom expect matchers -import 'react-testing-library/extend-expect' -interface ExtendedMatchers extends jest.Matchers { - toHaveTextContent: (htmlElement: string) => object - toBeInTheDOM: () => void -} -test('renders the tooltip as expected', async () => { - const { - // getByLabelText, - getByText, - // getByTestId, - container, - } = render(Child) - // tests rendering of the child - getByText('Child') - // tests rendering of tooltip label - ;(expect(getByText('hello world')) as ExtendedMatchers).toHaveTextContent( - 'hello world', - ) - // snapshots work great with regular DOM nodes! - expect(container.firstChild).toMatchSnapshot() -}) -``` - ## `TextMatch` Several APIs accept a `TextMatch` which can be a `string`, `regex` or a @@ -681,27 +594,6 @@ react components. -## Deprecated APIs - -### `flushPromises` - -> This API was deprecated in favor of [`wait`](#wait). We try to avoid having -> two ways to do the same thing and you can accomplish everything with `wait` -> that you could with `flushPromises`. A big advantage of `wait`, is that -> `flushPromises` will not flush promises that have not been queued up already, -> for example, if they will queue up as a result of the initial promises. In -> consequence of that, you might have to call `flushPromises` multiple times to -> get your components to your desired state. You can accomplish the exact same -> behavior with `wait` as you had with `flushPromises` by calling `wait` with -> no arguments: `await wait()` - -This is a simple utility that's useful for when your component is doing some -async work that you've mocked out, but you still need to wait until the next -tick of the event loop before you can continue your assertions. It simply -returns a promise that resolves in a `setImmediate`. Especially useful when -you make your test function an `async` function and use -`await flushPromises()`. - ## Other Solutions In preparing this project, diff --git a/extend-expect.js b/extend-expect.js deleted file mode 100644 index 3cee4049..00000000 --- a/extend-expect.js +++ /dev/null @@ -1 +0,0 @@ -require('./dist/extend-expect') diff --git a/package.json b/package.json index e7cc1463..f506f47a 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,24 @@ }, "files": [ "dist", - "typings", - "extend-expect.js" + "typings" + ], + "keywords": [ + "testing", + "react", + "ui", + "dom", + "jsdom", + "unit", + "integration", + "functional", + "end-to-end", + "e2e" ], - "keywords": [], "author": "Kent C. Dodds (http://kentcdodds.com/)", "license": "MIT", "dependencies": { + "dom-testing-library": "^1.0.0", "wait-for-expect": "0.4.0" }, "devDependencies": { diff --git a/src/__tests__/__snapshots__/element-queries.js.snap b/src/__tests__/__snapshots__/element-queries.js.snap deleted file mode 100644 index 6d636275..00000000 --- a/src/__tests__/__snapshots__/element-queries.js.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`get throws a useful error message 1`] = `"Unable to find a label with the text of: LucyRicardo"`; - -exports[`get throws a useful error message 2`] = `"Unable to find an element with the placeholder text of: LucyRicardo"`; - -exports[`get throws a useful error message 3`] = `"Unable to find an element with the text: LucyRicardo. 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."`; - -exports[`get throws a useful error message 4`] = `"Unable to find an element by: [data-testid=\\"LucyRicardo\\"]"`; - -exports[`get throws a useful error message 5`] = `"Unable to find an element with the alt text: LucyRicardo"`; - -exports[`label with no form control 1`] = `"Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly."`; - -exports[`totally empty label 1`] = `"Found a label with the text of: , however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly."`; diff --git a/src/__tests__/deprecated.js b/src/__tests__/deprecated.js deleted file mode 100644 index 33398d43..00000000 --- a/src/__tests__/deprecated.js +++ /dev/null @@ -1,14 +0,0 @@ -import {flushPromises, waitForExpect} from '../' - -test('flushPromises (DEPRECATED) still works', async () => { - const fn = jest.fn() - Promise.resolve().then(fn) - await flushPromises() - expect(fn).toHaveBeenCalledTimes(1) -}) - -test('waitForExpect (DEPRECATED) still works', async () => { - const fn = jest.fn() - Promise.resolve().then(fn) - await waitForExpect(() => expect(fn).toHaveBeenCalledTimes(1)) -}) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js deleted file mode 100644 index cf21ecd0..00000000 --- a/src/__tests__/element-queries.js +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react' -import {render} from '../' -import '../extend-expect' - -test('query can return null', () => { - const { - queryByLabelText, - queryByPlaceholderText, - queryByText, - queryByTestId, - queryByAltText, - } = render(
) - expect(queryByTestId('LucyRicardo')).toBeNull() - expect(queryByLabelText('LucyRicardo')).toBeNull() - expect(queryByPlaceholderText('LucyRicardo')).toBeNull() - expect(queryByText('LucyRicardo')).toBeNull() - expect(queryByAltText('LucyRicardo')).toBeNull() -}) - -test('get throws a useful error message', () => { - const { - getByLabelText, - getByPlaceholderText, - getByText, - getByTestId, - getByAltText, - } = render(
) - expect(() => getByLabelText('LucyRicardo')).toThrowErrorMatchingSnapshot() - expect(() => - getByPlaceholderText('LucyRicardo'), - ).toThrowErrorMatchingSnapshot() - expect(() => getByText('LucyRicardo')).toThrowErrorMatchingSnapshot() - expect(() => getByTestId('LucyRicardo')).toThrowErrorMatchingSnapshot() - expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingSnapshot() -}) - -test('get can get form controls by label text', () => { - const {getByLabelText} = render( -
- -
- - -
-
- - -
-
, - ) - expect(getByLabelText('1st').id).toBe('first-id') - expect(getByLabelText('2nd').id).toBe('second-id') - expect(getByLabelText('3rd').id).toBe('third-id') -}) - -test('get can get form controls by placeholder', () => { - const {getByPlaceholderText} = render( - , - ) - expect(getByPlaceholderText('username').id).toBe('username-id') -}) - -test('label with no form control', () => { - const {getByLabelText, queryByLabelText} = render() - expect(queryByLabelText('alone')).toBeNull() - expect(() => getByLabelText('alone')).toThrowErrorMatchingSnapshot() -}) - -test('totally empty label', () => { - const {getByLabelText, queryByLabelText} = render(