# Guide: migrating to v4 Previous versions of `eslint-plugin-testing-library` weren't checking common things consistently: Testing Library imports, renamed methods, wrappers around Testing Library methods, etc. One of the most important changes of `eslint-plugin-testing-library` v4 is the new detection mechanism implemented to be shared across all the rules, so each one of them has been rewritten to detect and report Testing Library usages consistently and correctly from a core module. ## Overview - [Aggressive Reporting](#aggressive-reporting) opted-in to avoid silencing possible errors - 7 new rules - `no-container` - `no-node-access` - `no-promise-in-fire-event` - `no-wait-for-multiple-assertions` - `no-wait-for-side-effects` - `prefer-user-event` - `render-result-naming-convention` - Shareable Configs updated - `recommended` renamed to `dom` - list of rules enabled has changed - Some rules option removed in favor of new Shared Settings + Aggressive Reporting - More consistent and flexible core rules detection - Tons of errors and small issues fixed - Dependencies updates ## Breaking Changes ### Dependencies - Min ESLint version required is `7.5` - Min Node version required didn't change (`10.12`) Please make sure you have Node and ESLint installed satisfying these required versions. ### New errors reported Since v4 also fixes a lot of issues and detect invalid usages in a more consistent way, you might find new errors reported in your codebase. Just be aware of this when migrating to v4. ### `recommended` Shareable Config has been renamed If you were using `recommended` Shareable Config, it has been renamed to `dom` so you'll need to update it in your ESLint config file: ```diff { ... - "extends": ["plugin:testing-library/recommended"] + "extends": ["plugin:testing-library/dom"] } ``` This Shareable Config has been renamed to clarify there is no _recommended_ config by default, so it depends on which Testing Library package you are using: DOM, Angular, React, or Vue (for now). ### Shareable Configs updated Shareable Configs have been updated with: - `dom` - `no-promise-in-fire-event` enabled as "error" - `no-wait-for-empty-callback` enabled as "error" - `prefer-screen-queries` enabled as "error" - `angular` - `no-container` enabled as "error" - `no-debug` changed from "warning" to "error" - `no-node-access` enabled as "error" - `no-promise-in-fire-event` enabled as "error" - `no-wait-for-empty-callback` enabled as "error" - `prefer-screen-queries` enabled as "error" - `render-result-naming-convention` enabled as "error" - `react` - `no-container` enabled as "error" - `no-debug` changed from "warning" to "error" - `no-node-access` enabled as "error" - `no-promise-in-fire-event` enabled as "error" - `no-wait-for-empty-callback` enabled as "error" - `prefer-screen-queries` enabled as "error" - `render-result-naming-convention` enabled as "error" - `vue` - `no-container` enabled as "error" - `no-debug` changed from "warning" to "error" - `no-node-access` enabled as "error" - `no-promise-in-fire-event` enabled as "error" - `no-wait-for-empty-callback` enabled as "error" - `prefer-screen-queries` enabled as "error" - `render-result-naming-convention` enabled as "error" ### `customQueryNames` rules option has been removed Until now, those rules reporting errors related to Testing Library queries needed an option called `customQueryNames` so you could specify which extra queries you'd like to report apart from built-in ones. This option has been removed in favor of reporting every method matching Testing Library queries pattern. The only thing you need to do is removing `customQueryNames` from your rules config if any. You can read more about it in corresponding [Aggressive Reporting - Queries](#queries) section. ### `renderFunctions` rules option has been removed Until now, those rules reporting errors related to Testing Library `render` needed an option called `renderFunctions` so you could specify which extra functions from your codebase should be assumed as extra `render` methods apart from built-in one. This option has been removed in favor of reporting every method which contains `*render*` on its name. The only thing you need to do is removing `renderFunctions` from your rules config if any. You can read more about it in corresponding [Aggressive Reporting - Render](#renders) section, and available config in [Shared Settings](#shared-settings) section. ## Aggressive Reporting So what is this Aggressive Reporting introduced on v4? Until v3, `eslint-plugin-testing-library` had assumed that all Testing Libraries utils would be imported from some `@testing-library/*` or `*-testing-library` package. However, this is not always true since: - users can [add their own Custom Render](https://testing-library.com/docs/react-testing-library/setup/#custom-render) methods, so it can be named other than `render`. - users can [re-export Testing Library utils from a custom module](https://testing-library.com/docs/react-testing-library/setup/#configuring-jest-with-test-utils), so they won't be imported from a Testing Library package but a custom one. - users can [add their own Custom Queries](https://testing-library.com/docs/react-testing-library/setup/#add-custom-queries), so it's possible to use other queries than built-in ones. These customization possibilities make it impossible for `eslint-plugin-testing-library` to figure out if some utils are related to Testing Library or not. Here you have some examples illustrating it: ```javascript import { render, screen } from '@testing-library/react'; // ... // ✅ this render has to be reported since it's named `render` // and it's imported from @testing-library/* package const wrapper = render(); // ✅ this query has to be reported since it's named after a built-in query // and it's imported from @testing-library/* package const el = screen.findByRole('button'); ``` ```javascript // importing from Custom Module import { renderWithRedux, findByIcon } from 'test-utils'; // ... // ❓ we don't know if this render has to be reported since it's NOT named `render` // and it's NOT imported from @testing-library/* package const wrapper = renderWithRedux(); // ❓ we don't know if this query has to be reported since it's NOT named after a built-in query // and it's NOT imported from @testing-library/* package const el = findByIcon('profile'); ``` How can the `eslint-plugin-testing-library` be aware of this? Until v3, the plugin offered some options to indicate some of these custom things, so the plugin would check them when reporting usages. This can lead to false negatives though since the users might not be aware of the necessity of indicating such custom utils or just forget about doing so. Instead, in `eslint-plugin-testing-library` v4 we have opted-in into a more **aggressive reporting** feature which, by default, will assume any method named following the same patterns as Testing Library utils has to be reported too: ```javascript // importing from Custom Module import { renderWithRedux, findByIcon } from 'test-utils'; // ... // ✅ this render has to be reported since its name contains "*render*" // and it doesn't matter where it's imported from const wrapper = renderWithRedux(); // ✅ this render has to be reported since its name starts by "findBy*" // and it doesn't matter where it's imported from const el = findByIcon('profile'); ``` There are 3 mechanisms that can be aggressively reported: imports, renders, and queries. This new Aggressive Reporting feature will work fine out of the box and won't create false positives for most of the users. However, it's possible to restrict or switch off these mechanisms using [Shared Settings](#shared-settings). We recommend you to keep reading this section to know more about these Aggressive Reporting mechanisms and afterwards check the Shared Settings if you think you'd still need them for some particular reason. _You can find the motivation behind this behavior on [this issue comment](https://github.com/testing-library/eslint-plugin-testing-library/issues/222#issuecomment-679592434)._ ### Imports By default, `eslint-plugin-testing-library` v4 won't check from which module the utils are imported. This means it doesn't matter if you are importing the utils from `@testing-library/*`, `test-utils` or `whatever`: they'll be assumed as Testing Library related if matching Testing Library utils patterns. There is a Shared Setting property to restrict or switch off this mechanism though: [`utils-module`](#testing-libraryutils-module). By using this setting, only utils imported from `@testing-library/*` packages + those indicated in this setting (if any) will be reported. ### Renders By default, `eslint-plugin-testing-library` v4 will assume that all methods which names contain "render" should be reported. This means it doesn't matter if you are rendering your elements for testing using `render`, `customRender` or `renderWithRedux`. There is a Shared Setting property to restrict or switch off this mechanism though: [`custom-renders`](#testing-librarycustom-renders). By using this setting, only methods strictly named `render` + those indicated in this setting (if any) will be reported. ### Queries `eslint-plugin-testing-library` v4 will assume that all methods named following the pattern `get(All)By*`, `query(All)By*`, or `find(All)By*` are queries to be reported. This means it doesn't matter if you are using a built-in query (`getByText`), or a custom one (`getByIcon`): if it matches this pattern, it will be assumed as a potential query to be reported. There is a Shared Setting property to restrict or switch off this mechanism though: [`custom-queries`](#testing-librarycustom-queries). By using this setting, only [built-in queries](https://testing-library.com/docs/queries/about) + those indicated in this setting (if any) will be reported. ## Shared Settings ESLint provides a way of configuring data that must be shared across all its rules: [Shared Settings](https://eslint.org/docs/user-guide/configuring/configuration-files#adding-shared-settings). Since `eslint-plugin-testing-library` v4 we are using this Shared Settings to config global things for the plugin. To avoid collision with settings from other ESLint plugins, all the properties for this one are prefixed with `testing-library/`. ⚠️ **Please be aware of using these settings will disable part of [Aggressive Reporting](#aggressive-reporting).** ### `testing-library/utils-module` Relates to the [Aggressive Imports Reporting mechanism](#imports). This setting accepts any string value. If you pass a string other than `"off"` to this option, it will represent your custom utility file from where you re-export everything from Testing Library package. ```js // .eslintrc.js module.exports = { settings: { 'testing-library/utils-module': 'my-custom-test-utility-file', }, }; ``` Configuring this setting like that, you'll restrict the errors reported by the plugin to only those utils being imported from this custom utility file, or some `@testing-library/*` package. The previous setting example would cause: ```javascript import { waitFor } from '@testing-library/react'; test('testing-library/utils-module setting example', () => { // ✅ this would be reported since this invalid usage of an util // is imported from `@testing-library/*` package waitFor(/* some invalid usage to be reported */); }); ``` ```javascript import { waitFor } from '../my-custom-test-utility-file'; test('testing-library/utils-module setting example', () => { // ✅ this would be reported since this invalid usage of an util // is imported from specified custom utility file. waitFor(/* some invalid usage to be reported */); }); ``` ```javascript import { waitFor } from '../somewhere-else'; test('testing-library/utils-module setting example', () => { // ❌ this would NOT be reported since this invalid usage of an util // is NOT imported from either `@testing-library/*` package or specified custom utility file. waitFor(/* some invalid usage to be reported */); }); ``` You can also set this setting to `"off"` to entirely opt-out Aggressive Imports Reporting mechanism, so only utils coming from Testing Library packages are reported. ```js // .eslintrc.js module.exports = { settings: { 'testing-library/utils-module': 'off', }, }; ``` ### `testing-library/custom-renders` Relates to the [Aggressive Renders Reporting mechanism](#renders). This setting accepts an array of strings or `"off"`. If you pass an array of strings to this option, it will represent a list of function names that are valid as Testing Library custom renders. ```js // .eslintrc.js module.exports = { settings: { 'testing-library/custom-renders': ['display', 'renderWithProviders'], }, }; ``` Configuring this setting like that, you'll restrict the errors reported by the plugin related to `render` somehow to only those functions sharing a name with one of the elements of that list, or built-in `render`. The previous setting example would cause: ```javascript import { render, display, renderWithProviders, renderWithRedux, } from 'test-utils'; import Component from 'somewhere'; const setupA = () => renderWithProviders(); const setupB = () => renderWithRedux(); test('testing-library/custom-renders setting example', () => { // ✅ this would be reported since `render` is a built-in Testing Library util const invalidUsage = render(); // ✅ this would be reported since `display` has been set as `custom-render` const invalidUsage = display(); // ✅ this would be reported since `renderWithProviders` has been set as `custom-render` const invalidUsage = renderWithProviders(); // ❌ this would NOT be reported since `renderWithRedux` isn't a `custom-render` or built-in one const invalidUsage = renderWithRedux(); // ✅ this would be reported since it wraps `renderWithProviders`, // which has been set as `custom-render` const invalidUsage = setupA(); // ❌ this would NOT be reported since it wraps `renderWithRedux`, // which isn't a `custom-render` or built-in one const invalidUsage = setupB(); }); ``` You can also set this setting to `"off"` to entirely opt-out Aggressive Renders Reporting mechanism, so only methods named `render` are reported as Testing Library render util. ```js // .eslintrc.js module.exports = { settings: { 'testing-library/custom-renders': 'off', }, }; ``` ### `testing-library/custom-queries` Relates to the [Aggressive Queries Reporting mechanism](#queries). This setting accepts an array of strings or `"off"`. If you pass an array of strings to this option, it will represent a list of query names/variants that are the only valid Testing Library custom queries. Each string passed to this list of custom queries can be: - **pattern query (recommended)**: a custom query variant (suffix starting with "By") to be reported, so all query combinations around it are reported. For instance: `"ByIcon"` would report all `getByIcon()`, `getAllByIcon()`, `queryByIcon()` and `findByIcon()`. - **strict query**: a specific custom query name to be reported, so only that very exact query would be reported but not any related variant. For instance: `"getByIcon"` would make the plugin to report `getByIcon()` but not `getAllByIcon()`, `queryByIcon()` or `findByIcon()`. ```js // .eslintrc.js module.exports = { settings: { 'testing-library/custom-queries': ['ByIcon', 'getByComplexText'], }, }; ``` Configuring this setting like that, you'll restrict the errors reported by the plugin related to the queries to only those custom queries matching name or pattern from that list, or [built-in queries](https://testing-library.com/docs/queries/about). The previous setting example would cause: ```javascript // ✅ this would be reported since `getByText` is a built-in Testing Library query getByText('foo'); // ✅ this would be reported since `findAllByRole` is a built-in Testing Library query findAllByRole('foo'); // ✅ this would be reported since `getByIcon` is a custom query matching "ByIcon" setting getByIcon('foo'); // ✅ this would be reported since `findAllByIcon` is a custom query matching "ByIcon" setting findAllByIcon('foo'); // ✅ this would be reported since `getByComplexText` is a custom query matching "getByComplexText" etting getByComplexText('foo'); // ❌ this would NOT be reported since `getAllByComplexText` is a custom query but not matching any setting getAllByComplexText('foo'); // ❌ this would NOT be reported since `findBySomethingElse` is a custom query but not matching any setting findBySomethingElse('foo'); ``` You can also set this setting to `"off"` to entirely opt-out Aggressive Queries Reporting mechanism, so only built-in queries are reported. ```js // .eslintrc.js module.exports = { settings: { 'testing-library/custom-queries': 'off', }, }; ```