Coder Social home page Coder Social logo

testing-library / eslint-plugin-testing-library Goto Github PK

View Code? Open in Web Editor NEW
976.0 11.0 136.0 2.13 MB

ESLint plugin to follow best practices and anticipate common mistakes when writing tests with Testing Library

Home Page: https://npm.im/eslint-plugin-testing-library

License: MIT License

JavaScript 0.72% TypeScript 99.24% Shell 0.04%
eslint eslint-plugin javascript development testing-library best-practices dom angular react vue markojs reactjs

eslint-plugin-testing-library's Introduction

eslint-plugin-testing-library

ESLint plugin to follow best practices and anticipate common mistakes when writing tests with Testing Library


Package version eslint-remote-tester eslint-plugin-testing-library codecov MIT License
semantic-release PRs Welcome All Contributors

Installation

You'll first need to install ESLint:

$ npm install --save-dev eslint
# or
$ yarn add --dev eslint

Next, install eslint-plugin-testing-library:

$ npm install --save-dev eslint-plugin-testing-library
# or
$ yarn add --dev eslint-plugin-testing-library

Note: If you installed ESLint globally (using the -g flag) then you must also install eslint-plugin-testing-library globally.

Migrating

You can find detailed guides for migrating eslint-plugin-testing-library in the migration guide docs:

Usage

Add testing-library to the plugins section of your .eslintrc.js configuration file. You can omit the eslint-plugin- prefix:

module.exports = {
	plugins: ['testing-library'],
};

Then configure the rules you want to use within rules property of your .eslintrc:

module.exports = {
	rules: {
		'testing-library/await-async-queries': 'error',
		'testing-library/no-await-sync-queries': 'error',
		'testing-library/no-debugging-utils': 'warn',
		'testing-library/no-dom-import': 'off',
	},
};

Run the plugin only against test files

With the default setup mentioned before, eslint-plugin-testing-library will be run against your whole codebase. If you want to run this plugin only against your tests files, you have the following options:

ESLint overrides

One way of restricting ESLint config by file patterns is by using ESLint overrides.

Assuming you are using the same pattern for your test files as Jest by default, the following config would run eslint-plugin-testing-library only against your test files:

// .eslintrc.js
module.exports = {
	// 1) Here we have our usual config which applies to the whole project, so we don't put testing-library preset here.
	extends: ['airbnb', 'plugin:prettier/recommended'],

	// 2) We load other plugins than eslint-plugin-testing-library globally if we want to.
	plugins: ['react-hooks'],

	overrides: [
		{
			// 3) Now we enable eslint-plugin-testing-library rules or preset only for matching testing files!
			files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
			extends: ['plugin:testing-library/react'],
		},
	],
};

ESLint Cascading and Hierarchy

Another approach for customizing ESLint config by paths is through ESLint Cascading and Hierarchy. This is useful if all your tests are placed under the same folder, so you can place there another .eslintrc where you enable eslint-plugin-testing-library for applying it only to the files under such folder, rather than enabling it on your global .eslintrc which would apply to your whole project.

Shareable configurations

This plugin exports several recommended configurations that enforce good practices for specific Testing Library packages. You can find more info about enabled rules in the Supported Rules section, under the Configurations column.

Since each one of these configurations is aimed at a particular Testing Library package, they are not extendable between them, so you should use only one of them at once per .eslintrc file. For example, if you want to enable recommended configuration for React, you don't need to combine it somehow with DOM one:

// ❌ Don't do this
module.exports = {
	extends: ['plugin:testing-library/dom', 'plugin:testing-library/react'],
};
// ✅ Just do this instead
module.exports = {
	extends: ['plugin:testing-library/react'],
};

DOM Testing Library

Enforces recommended rules for DOM Testing Library.

To enable this configuration use the extends property in your .eslintrc.js config file:

module.exports = {
	extends: ['plugin:testing-library/dom'],
};

Angular

Enforces recommended rules for Angular Testing Library.

To enable this configuration use the extends property in your .eslintrc.js config file:

module.exports = {
	extends: ['plugin:testing-library/angular'],
};

React

Enforces recommended rules for React Testing Library.

To enable this configuration use the extends property in your .eslintrc.js config file:

module.exports = {
	extends: ['plugin:testing-library/react'],
};

Vue

Enforces recommended rules for Vue Testing Library.

To enable this configuration use the extends property in your .eslintrc.js config file:

module.exports = {
	extends: ['plugin:testing-library/vue'],
};

Marko

Enforces recommended rules for Marko Testing Library.

To enable this configuration use the extends property in your .eslintrc.js config file:

module.exports = {
	extends: ['plugin:testing-library/marko'],
};

Supported Rules

Remember that all rules from this plugin are prefixed by "testing-library/"

💼 Configurations enabled in.
⚠️ Configurations set to warn in.
🔧 Automatically fixable by the --fix CLI option.

Name                            Description 💼 ⚠️ 🔧
await-async-events Enforce promises from async event methods are handled badge-angular badge-dom badge-marko badge-react badge-vue 🔧
await-async-queries Enforce promises from async queries to be handled badge-angular badge-dom badge-marko badge-react badge-vue
await-async-utils Enforce promises from async utils to be awaited properly badge-angular badge-dom badge-marko badge-react badge-vue
consistent-data-testid Ensures consistent usage of data-testid
no-await-sync-events Disallow unnecessary await for sync events badge-angular badge-dom badge-react
no-await-sync-queries Disallow unnecessary await for sync queries badge-angular badge-dom badge-marko badge-react badge-vue
no-container Disallow the use of container methods badge-angular badge-marko badge-react badge-vue
no-debugging-utils Disallow the use of debugging utilities like debug badge-angular badge-marko badge-react badge-vue
no-dom-import Disallow importing from DOM Testing Library badge-angular badge-marko badge-react badge-vue 🔧
no-global-regexp-flag-in-query Disallow the use of the global RegExp flag (/g) in queries badge-angular badge-dom badge-marko badge-react badge-vue 🔧
no-manual-cleanup Disallow the use of cleanup badge-react badge-vue
no-node-access Disallow direct Node access badge-angular badge-dom badge-marko badge-react badge-vue
no-promise-in-fire-event Disallow the use of promises passed to a fireEvent method badge-angular badge-dom badge-marko badge-react badge-vue
no-render-in-lifecycle Disallow the use of render in testing frameworks setup functions badge-angular badge-marko badge-react badge-vue
no-unnecessary-act Disallow wrapping Testing Library utils or empty callbacks in act badge-marko badge-react
no-wait-for-multiple-assertions Disallow the use of multiple expect calls inside waitFor badge-angular badge-dom badge-marko badge-react badge-vue
no-wait-for-side-effects Disallow the use of side effects in waitFor badge-angular badge-dom badge-marko badge-react badge-vue
no-wait-for-snapshot Ensures no snapshot is generated inside of a waitFor call badge-angular badge-dom badge-marko badge-react badge-vue
prefer-explicit-assert Suggest using explicit assertions rather than standalone queries
prefer-find-by Suggest using find(All)By* query instead of waitFor + get(All)By* to wait for elements badge-angular badge-dom badge-marko badge-react badge-vue 🔧
prefer-implicit-assert Suggest using implicit assertions for getBy* & findBy* queries
prefer-presence-queries Ensure appropriate get*/query* queries are used with their respective matchers badge-angular badge-dom badge-marko badge-react badge-vue
prefer-query-by-disappearance Suggest using queryBy* queries when waiting for disappearance badge-angular badge-dom badge-marko badge-react badge-vue
prefer-query-matchers Ensure the configured get*/query* query is used with the corresponding matchers
prefer-screen-queries Suggest using screen while querying badge-angular badge-dom badge-marko badge-react badge-vue
prefer-user-event Suggest using userEvent over fireEvent for simulating user interactions
render-result-naming-convention Enforce a valid naming for return value from render badge-angular badge-marko badge-react badge-vue

Aggressive Reporting

In v4 this plugin introduced a new feature called "Aggressive Reporting", which intends to detect Testing Library utils usages even if they don't come directly from a Testing Library package (i.e. using a custom utility file to re-export everything from Testing Library). You can read more about this feature here.

If you are looking to restricting or switching off this feature, please refer to the Shared Settings section to do so.

Shared Settings

There are some configuration options available that will be shared across all the plugin rules. This is achieved using ESLint Shared Settings. These Shared Settings are meant to be used if you need to restrict or switch off the Aggressive Reporting, which is an out of the box advanced feature to lint Testing Library usages in a simpler way for most of the users. So please before configuring any of these settings, read more about the advantages of eslint-plugin-testing-library Aggressive Reporting feature, and how it's affected by these settings.

If you are sure about configuring the settings, these are the options available:

testing-library/utils-module

The name of your custom utility file from where you re-export everything from the Testing Library package, or "off" to switch related Aggressive Reporting mechanism off. Relates to Aggressive Imports Reporting.

// .eslintrc.js
module.exports = {
	settings: {
		'testing-library/utils-module': 'my-custom-test-utility-file',
	},
};

You can find more details about the utils-module setting here.

testing-library/custom-renders

A list of function names that are valid as Testing Library custom renders, or "off" to switch related Aggressive Reporting mechanism off. Relates to Aggressive Renders Reporting.

// .eslintrc.js
module.exports = {
	settings: {
		'testing-library/custom-renders': ['display', 'renderWithProviders'],
	},
};

You can find more details about the custom-renders setting here.

testing-library/custom-queries

A list of query names/patterns that are valid as Testing Library custom queries, or "off" to switch related Aggressive Reporting mechanism off. Relates to Aggressive Reporting - Queries

// .eslintrc.js
module.exports = {
	settings: {
		'testing-library/custom-queries': ['ByIcon', 'getByComplexText'],
	},
};

You can find more details about the custom-queries setting here.

Switching all Aggressive Reporting mechanisms off

Since each Shared Setting is related to one Aggressive Reporting mechanism, and they accept "off" to opt out of that mechanism, you can switch the entire feature off by doing:

// .eslintrc.js
module.exports = {
	settings: {
		'testing-library/utils-module': 'off',
		'testing-library/custom-renders': 'off',
		'testing-library/custom-queries': 'off',
	},
};

Troubleshooting

Errors reported in non-testing files

If you find ESLint errors related to eslint-plugin-testing-library in files other than testing, this could be caused by Aggressive Reporting.

You can avoid this by:

  1. running eslint-plugin-testing-library only against testing files
  2. limiting the scope of Aggressive Reporting through Shared Settings
  3. switching Aggressive Reporting feature off

If you think the error you are getting is not related to this at all, please fill a new issue with as many details as possible.

False positives in testing files

If you are getting false positive ESLint errors in your testing files, this could be caused by Aggressive Reporting.

You can avoid this by:

  1. limiting the scope of Aggressive Reporting through Shared Settings
  2. switching Aggressive Reporting feature off

If you think the error you are getting is not related to this at all, please fill a new issue with as many details as possible.

Other documentation

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Mario Beltrán Alarcón
Mario Beltrán Alarcón

💻 📖 👀 ⚠️ 🚇 🐛 🚧
Thomas Lombart
Thomas Lombart

💻 📖 👀 ⚠️ 🚇
Ben Monro
Ben Monro

💻 📖 ⚠️
Nicola Molinari
Nicola Molinari

💻 ⚠️ 📖 👀
Aarón García Hervás
Aarón García Hervás

📖
Matej Šnuderl
Matej Šnuderl

🤔 📖
Adrià Fontcuberta
Adrià Fontcuberta

💻 ⚠️
Jon Aldinger
Jon Aldinger

📖
Thomas Knickman
Thomas Knickman

💻 📖 ⚠️
Kevin Sullivan
Kevin Sullivan

📖
Jakub Jastrzębski
Jakub Jastrzębski

💻 📖 ⚠️
Nikolay Stoynov
Nikolay Stoynov

📖
marudor
marudor

💻 ⚠️
Tim Deschryver
Tim Deschryver

💻 📖 🤔 👀 ⚠️ 🐛 🚇 📦
Tobias Deekens
Tobias Deekens

🐛
Victor Cordova
Victor Cordova

💻 ⚠️ 🐛
Dmitry Lobanov
Dmitry Lobanov

💻 ⚠️
Kent C. Dodds
Kent C. Dodds

🐛
Gonzalo D'Elia
Gonzalo D'Elia

💻 ⚠️ 📖 👀
Jeff Rifwald
Jeff Rifwald

📖
Leandro Lourenci
Leandro Lourenci

🐛 💻 ⚠️
Miguel Erja González
Miguel Erja González

🐛
Pavel Pustovalov
Pavel Pustovalov

🐛
Jacob Parish
Jacob Parish

🐛 💻 ⚠️
Nick McCurdy
Nick McCurdy

🤔 💻 👀
Stefan Cameron
Stefan Cameron

🐛
Mateus Felix
Mateus Felix

💻 ⚠️ 📖
Renato Augusto Gama dos Santos
Renato Augusto Gama dos Santos

🤔 💻 📖 ⚠️
Josh Kelly
Josh Kelly

💻
Alessia Bellisario
Alessia Bellisario

💻 ⚠️ 📖
Spencer Miskoviak
Spencer Miskoviak

💻 ⚠️ 📖 🤔
Giorgio Polvara
Giorgio Polvara

💻 ⚠️ 📖
Josh David
Josh David

📖
Michaël De Boey
Michaël De Boey

💻 📦 🚧 🚇 👀
Jian Huang
Jian Huang

💻 ⚠️ 📖
Philipp Fritsche
Philipp Fritsche

💻
Tomas Zaicevas
Tomas Zaicevas

🐛 💻 ⚠️ 📖
Gareth Jones
Gareth Jones

💻 📖 ⚠️
HonkingGoose
HonkingGoose

📖 🚧
Julien Wajsberg
Julien Wajsberg

🐛 💻 ⚠️
Marat Dyatko
Marat Dyatko

🐛 💻
David Tolman
David Tolman

🐛
Ari Perkkiö
Ari Perkkiö

⚠️
Diego Castillo
Diego Castillo

💻
Bruno Pinto
Bruno Pinto

💻 ⚠️
themagickoala
themagickoala

💻 ⚠️
Prashant Ashok
Prashant Ashok

💻 ⚠️
Ivan Aprea
Ivan Aprea

💻 ⚠️
Dmitry Semigradsky
Dmitry Semigradsky

💻 ⚠️ 📖
Senja
Senja

💻 ⚠️ 📖
Breno Cota
Breno Cota

💻 ⚠️
Nick Bolles
Nick Bolles

💻 ⚠️ 📖
Bryan Mishkin
Bryan Mishkin

📖 🔧
Nim G
Nim G

📖
Patrick Ahmetovic
Patrick Ahmetovic

🤔 💻 ⚠️
Josh Justice
Josh Justice

💻 ⚠️ 📖 🤔
Dale Karp
Dale Karp

💻 ⚠️ 📖
Nathan
Nathan

💻 ⚠️
justintoman
justintoman

💻 ⚠️
Anthony Devick
Anthony Devick

💻 ⚠️ 📖
Richard Maisano
Richard Maisano

💻 ⚠️
Aleksei Androsov
Aleksei Androsov

💻 ⚠️
Nicolas Bonduel
Nicolas Bonduel

📖
Alexey Ryabov
Alexey Ryabov

🚧
Jemi Salo
Jemi Salo

💻 ⚠️

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

eslint-plugin-testing-library's People

Contributors

aarongarciah avatar adevick avatar allcontributors[bot] avatar ariperkkio avatar belco90 avatar bmish avatar bpinto avatar chamion avatar dependabot[bot] avatar dmitry-lobanov avatar dyatko avatar emmenko avatar gndelia avatar honkinggoose avatar lesha1201 avatar lourenci avatar michaeldeboey avatar nathanmmiller avatar neriyarden avatar nickbolles avatar nickmccurdy avatar patriscus avatar renatoagds avatar sjarva avatar skovy avatar thebinaryfelix avatar timdeschryver avatar tknickman avatar wolverineks avatar zaicevas 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

eslint-plugin-testing-library's Issues

Add new shareable configurations for libraries

Currently, we are supporting Angular, React and Vue for shareable configurations. The configuration contains the following rules:

  • await-async-query
  • await-async-utils
  • no-await-async-query
  • no-debug
  • no-dom-import

It would be nice to add shareable configurations for other libraries such as:

We would need to check that each rule can be applied to these packages.

No get* to assert element is in document

I have seen some people started using get* (because it is short) to assert elements are in DOM:

getByText("Text")

(note that there is no assignment or anything, just the call)

I would prefer if line was changed to:

expect(queryByText('Text')).toBeInTheDocument()

It's longer, but I think we should be explicit in tests on what exactly is being tested. Also, someone new to RTL has no idea get* selectors throw and this is why one can use them to be lazy.

Remove `prefer-expect-query-by` rule from shareable configs

After checking prefer-expect-query-by rule with different people and getting different feedback about it, this rule is definitely too opinionated to be enabled by default in shareable configs. Also, the motivation of the rule is not really clear, so we need to improve the rule doc to clarify what's the use case and corresponding motivation for it.

Replace `prefer-expect-query-by` by better rule

Motivated by #59 it looks like we need to replace prefer-expect-query-by* by better implementation to only force users to replace getBy queries by queryBy when:

This should be checked in several cases. A few I can think of at the moment:

  • asserting with .not.toBeInTheDocument()
  • asserting with .toBeNull()
  • asserting with .toBe(null) this is actually an equivalent for the previous one, probably more variations to cover as .toBeFalsy() but I think just this one is enough for now?
  • waiting for disappearance with waitForElementToBeRemoved

I can't think better name for this rule than no-get-by-for-element-absence, any suggestions?

*We could maintain prefer-expect-query-by when the new rule is implemented, but I think it wouldn't be that useful anymore and could even lead to confusion

Improvement: no-dom-import suggesting proper framework module

As discussed in #11 (comment) we need to use eslint context.options for passing proper testing library framework to no-dom-import rule so it can report the issue properly and autofix it properly in the future.

Additionaly, that option should be automatically setup for react, vue and angular configs by passing corresponding value.

Support assignments and awaiting them

We need to handle the case where a user stores the result of a find* query in a variable and later on await this variable, for example:

test("an example", async () => {
  // set up
  const myButton = findByText(/submit/i)
  await myButton
})

We need to keep track of the user assignements and check on every one of them if the user effectively uses an await statement.

TypeError: references is not iterable / cant parse expect(findBy*)

Error stack

TypeError: references is not iterable
Occurred while linting /Users/lukaszostrowski/projects/mp/src/app/flowers/__tests__/flowers-test.tsx:1

TypeError: references is not iterable
Occurred while linting /Users/lukaszostrowski/projects/mp/src/app/flowers/__tests__/flowers-test.tsx:1
    at Program:exit.testingLibraryQueryUsage.forEach.node (/Users/lukaszostrowski/projects/mp/node_modules/eslint-plugin-testing-library/lib/rules/await-async-query.js:61:37)
    at Array.forEach (<anonymous>)
    at Program:exit (/Users/lukaszostrowski/projects/mp/node_modules/eslint-plugin-testing-library/lib/rules/await-async-query.js:38:34)
    at listeners.(anonymous function).forEach.listener (/Users/lukaszostrowski/projects/mp/node_modules/eslint/lib/linter/safe-emitter.js:45:58)
    at Array.forEach (<anonymous>)
    at Object.emit (/Users/lukaszostrowski/projects/mp/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
    at NodeEventGenerator.applySelector (/Users/lukaszostrowski/projects/mp/node_modules/eslint/lib/linter/node-event-generator.js:254:26)
    at NodeEventGenerator.applySelectors (/Users/lukaszostrowski/projects/mp/node_modules/eslint/lib/linter/node-event-generator.js:283:22)
    at NodeEventGenerator.leaveNode (/Users/lukaszostrowski/projects/mp/node_modules/eslint/lib/linter/node-event-generator.js:306:14)
    at CodePathAnalyzer.leaveNode (/Users/lukaszostrowski/projects/mp/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js:655:23)
Process finished with exit code -1

Probably related code:
https://github.com/Belco90/eslint-plugin-testing-library/blob/b38689c69702f08954b456d03752d73b1bee7171/lib/rules/await-async-query.js#L38

Can't provide right now an example , I have to strip few hundreds lines, but maybe its just a matter of checking for references if its actually an array?

Also basing on code it should be expecting await from query, I believe only find* queries should be matched.

I don't have any of them top level, I have few expect(findBy*()).resolves. When I remove them, error is gone

[Technical] Add baseUrl in tsconfig.json in order to avoid relative paths

Now that the library has been migrated to typescript 🎉 I think we could use the baseUrl option

"baseUrl": "./

By doing so, we could set all the imports as absolute from that URL. So, for example, this import

import rule, { RULE_NAME } from '../../../lib/rules/no-debug';

can be now converted to

import rule, { RULE_NAME } from 'lib/rules/no-debug';

and the same applies to all import. I usually find this more readable

In addition to that, when moving files, they get updated automatically 😄

New Rule: No use of `not.toBeNull()` with *AllBy queries.

*AllBy Queries always return an array. As such if you use them in conjunction with not.toBeNull() or not.toBe(null) you will have a false positive test as the result of query by will always be an array. This is probably less of an issue for get* and find* as they will throw, but query* will not throw so you can easily get a false positive test w/ that.

So this new rule should prevent the usage of query* by in conjunction with those matchers.

Rule no-get-by-for-checking-element-not-present is giving false positives

The rule no-get-by-for-checking-element-not-present is giving false positives for some matchers when it shouldn't.

If you are getting an element with getBy query and asserting something with a negated matcher, it should not complain unless the matcher is toBeInTheDocument (or additional ones checking the element itself is not present).

But if you are asserting something like expect(getByText('Submit')).not.toBeDisabled() the rule should complain about as it's not checking element absence but something else from the element.

No Recommended Rule Column

The readme says "you can find more info about enabled rules in Supported Rules section within Recommended column", but there is no recommended column in the rule list.

image

Support resolves/rejects matchers for asynchronous queries

As stated in #48, we are not handling the case where a user wraps a query in an expect statement and then uses the resolves or rejects matchers. For example, the following statement will trigger an error even if it's a valid case:

expect(findByText("foo")).resolves.toBe("bar")

prefer-screen-queries: false positives when query used from within

Rule prefer-screen-queries is throwing false positives when a query is called from within util:

import { render, within, screen } from 'react-testing-library';

// ...
render(<SomeComponent />);
const section = screen.getByTestId('some-id');

// this is fine but the rule complains about it.
const submit = within(section).getByText(/submit/i);

jest-dom rule for enabled/disabled

would be nice to have a rule (auto-fixable) that auto-fixes

expect(element).toHaveProperty('disabled', true)

into

expect(element).toBeDisabled()

and similar for toBeEnabled()

I realize this is part of jest-dom, but jest-dom is in the testing-library org, so it made sense to me to have the rule in here. could probably come up w/ a bunch of other rules like this for jest-dom.... thoughts?

False positives on `await-async-query` and `no-await-sync-query`

Related to #120, #114

Since we are checking testing library queries when calling queries member expressions for await-async-query and no-await-sync-query, we are getting some false positives in some cases:

  • when using queries in cypress files
  • when using methods with the same name in other places (e.g. findByTestId in enzyme tests in a codebase mixing both enzyme and testing library)

The main issue here is that we are checking the queries methods in every single file no matter where the method is coming from, but we should only check those imported from testing library or returned by something imported from testing library. However, I think a first approach could be at least check if testing library is imported in the file to check the methods or not.

Support promise-syntax

The await-async-query doesn't supports the Promise syntax yet. For example:

test("an example", async () => {
  // set up
  const myButton = findByText(/submit/i)
  myButton.then(btn => ...)
})

Using the promise syntax is not that common but it doesn't cost much to support it.

Using query* variants for anything except checking for non-existance

I found this project from @kentcdodds blog post Common mistakes with React Testing Library. Wish I had known about it earlier!

One of the mistakes that stood out to me was "using query variants for anything except checking for non-existance*".

// ❌
expect(screen.queryByRole('alert')).toBeInTheDocument()
// ✅
expect(screen.getByRole('alert')).toBeInTheDocument()
expect(screen.queryByRole('alert')).not.toBeInTheDocument()

After installing this plugin, it seems like there is no rule "no-query-except-for-existance-assertion" or something along those lines. Did I miss something? If not, I'd like to contribute that!

await-async-query not waiting when method is called from wrapper

Hello,

I've been trying to integrate the rule await-async-query with a React project but it doesn't seem to be working as expected. Here's how to reproduce the problem:

import React from "react";
import { render, screen } from "@testing-library/react";

const Foo = () => <div>Hello</div>;

it("should fail rule", () => {
  render(<Foo />);

  screen.findByText('Hello'); // DOES NOT FAIL await-async-query even though it should
  
  expect(1).toBe(1)
});

.eslintrc

{
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true,
        "jest": true
    },
    "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "plugins": ["testing-library"],
    "rules": {
        "testing-library/await-async-query": "error",
        "testing-library/await-async-utils": "error",
        "testing-library/no-await-sync-query": "error",
        "testing-library/prefer-explicit-assert": "error",
        "testing-library/prefer-presence-queries": "error",
        "testing-library/prefer-wait-for": "error"
    }
}

This is strange for me because the rule is recommended to work with the react version of testing-library which uses either screen or wrapper instead of the direct export of find* methods.

I also noticed that the rule's selector used to work with this example previously but was changed in 60862ad#diff-b939ebc192e4cb00915398cde14390f7R29

Please let me know if this rule is not intended to be used this way.

Thanks for the help :)

TypeError: Cannot read property 'name' of undefined for `no-debug`

We adopted the react preset recently and are seeing failing builds on CI with the error TypeError: Cannot read property 'name' of undefined. Which seems to originate from this code:

ImportDeclaration(node) {
    const screenModuleName = LIBRARY_MODULES_WITH_SCREEN.find(
      module => module === node.source.value
    );

    if (
      screenModuleName &&
      node.specifiers.some(
        specifier => specifier.imported.name === 'screen'
      )
    ) {
      hasImportedScreen = true;
    }
},

in no-debug.js on line 95.

CleanShot 2020-03-30 at 10 29 58@2x

We are also seeing the error on a GitHub Action build to inspect for you.

https://github.com/commercetools/merchant-center-application-kit/pull/1419/checks?check_run_id=544774992

await-async-query matching `cy.findBy*` commands

Hey, I've got an .eslintrc.js file at the root of the project that extends plugin:testing-library/react.

This is working great for Jest test files, however, it is also reporting errors in Cypress test files when using commands like cy.findByText() or any of the other find commands.

I'm using eslint-plugin-testing-library 3.0.4

New Rule: Disallow DOM exploration properties

This is not a great way to select elements, but is allowed:
parentElement.parentElement.parentElement

It would be great if there were an optional rule that discouraged the use of properties such as parentElement, previousSibling, and the rest.

The rule could be highly configurable by allowing each property key to be enabled or disabled. It could also allow a certain level of traversing so that one use of a property is okay, but a chain is not.

Lint RenderResult when returned from function

When returning a RenderResult from a function, some destructured methods are caught by the linter, but others aren't.

it('displays a loader', () => {
  const { getByText, debug } = render(<MyLoader />);
  getByText('Loading'); // Caught by linter
  debug(); // Caught by linter
});

it('displays a loader', () => {
  function renderLoader(): RenderResult {
    return render(<MyLoader />);
  }

  const { getByText, debug } = renderLoader();
  getByText('Loading'); // Caught by linter
  debug(); // Linter doesn't care
});

no-manual-cleanup does not like require(modulaName)

I've got code like this:

require(moduleName)

and no-manual-cleanup does this:

TypeError: Cannot read property 'match' of undefined
Occurred while linting /Users/kentcdodds/code/kcd-scripts/src/utils.js:39
    at VariableDeclarator (/Users/kentcdodds/code/kcd-scripts/node_modules/eslint-plugin-testing-library/rules/no-manual-cleanup.js:71:73)
    at /Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/safe-emitter.js:45:58
    at Array.forEach (<anonymous>)
    at Object.emit (/Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
    at NodeEventGenerator.applySelector (/Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/node-event-generator.js:254:26)
    at NodeEventGenerator.applySelectors (/Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/node-event-generator.js:283:22)
    at NodeEventGenerator.enterNode (/Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/node-event-generator.js:297:14)
    at CodePathAnalyzer.enterNode (/Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js:634:23)
    at /Users/kentcdodds/code/kcd-scripts/node_modules/eslint/lib/linter/linter.js:936:32
    at Array.forEach (<anonymous>)

This is because of this code:

const value = node.source.value as string;
const testingLibraryWithCleanup = value.match(CLEANUP_LIBRARY_REGEX);

It should check whether value exists before calling match :)

Avoid using throwing queries with expect: no-expect-get-by-query

Problem

According to the cheatsheet, getBy, getAllBy are queries that throw an error if there are no matches. Thus, it doesn't make sense to use expect with a query that throws:

expect(getByText("foo")).toBeInTheDocument()

I don't know if that's a thing but I'm more used to use:

getByText("hello")
// OR
expect(queryByText("foo")).toBeInTheDocument()

Solution

The rule (let's call it no-expect-throwing-query but I'm not sure of that name) would prevent getBy and getAllBy to be used with expect as they already throw an error.

Valid cases:

// ✅ Valid
getByPlaceholderText("foo")
expect(queryByText("foo")).toBeInTheDocument()
expect(queryAllByText("foo")).toHaveLength(3)
getAllByTitle("foo")

Invalid cases:

// ❌Invalid
expect(getByPlaceholderText("foo")).toBeInTheDocument()
expect(getAllByText("foo")).toHaveLength(3)

const foo = getByText("foo")
expect(foo).toBeInTheDocument()

Code coverage improvements

Now that we have 100% coverage in the repo, it would be nice to set proper coverage threshold and check it on corresponding PRs.

For accomplishing this we would have to:

  • set coverage threshold to 100%
  • add necessary script to execute the tests in coverage mode
  • execute the coverage mode in the CI
  • prevent PRs to be merged in coverage has been decreased

peer dependency question

Thank you for building this plugin, it's useful.

Just a question, please: I noticed that it (indirectly, thru it's dependencies) requires a peer dependency of typescript which I'm not using in my project.

└─┬ [email protected]
  └─┬ @typescript-eslint/[email protected]
    └─┬ @typescript-eslint/[email protected]
      └── [email protected] 
npm WARN [email protected] requires a peer of typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta but none is installed. You must install peer dependencies yourself.

...any way around this warning, or am i stuck with it?

Feature Request: new rule prefer-targeted-queries

it would be nice to have an autofixable rule that converted
this

expect(getAllByText('foo')).toHaveLength(1);

to this:

expect(getByText('foo')).toBeInTheDocument();

and similarly:

expect(queryAllByText('foo')).toHaveLength(0);

to this:

expect(queryByText('foo')).toBeNull()

if autofix is too hard, at least reporting on it would be nice. :)

Rule to enforce consistent data-testid format

Hey, great plugin!

I recently wrote a custom rule that we are using internally at my company to enforce consistent values for data-testid. (I found this plugin because I wanted to use the same name 😂). We have a giant monorepo and teams were using everything from camelCase to PascalCase to kebab-case to snake_case to who-knows-what-case when writing test Id's.

This rule allows specifying a regex (that can include an inferred component name) that is used to validate all data-testid values. It has proved to be useful in larger codebases because it

  1. Avoids the dreaded naming problem
  2. Allows automation and developers to move faster by inferring test Id's without having to look them up every time.

Here is what the rule config looks like

'@custom/my-secret-plugin-name/data-testid': [
  2,
  {
    testIdPattern: '^{componentName}(__([A-Z]+[a-z]*?)+)*$',
    excludePaths: ['__tests__']
  }
],

If you think this is generally useful I would be happy to submit a PR to add this rule.
Thanks!

Release v3

Recent update on dom-testing-library to v7 has originated few improvements for some existing rules and ideas for some new others. I'll wrap all those changes under release of plugin v3 for several reasons:

  • One of them implies a breaking change so I think it's better to put everything under same major version rather than be releasing different minors and one major at some point.
  • Most of them are related to new waitFor async util so I'd like to test all changes together before releasing them to check they make sense as a whole.
  • Changes are closed to be finished so this won't take too long.

Changes under v3 will be:

  • BREAKING CHANGE: drop node v8 support #96
  • BREAKING CHANGE: replace rule no-get-by-for-checking-element-not-present with prefer-presence-queries #90
  • Feature: update rule await-async-utils with new waitFor async util #89
  • Feature: new rule prefer-wait-for #88
  • Feature: new rule no-wait-for-empty-callback #92
  • Feature: new rule prefer-screen-queries #95 (optional, only if it ready when releasing v3. Otherwise it will go on v3.1)

All PRs for mentioned changes will point to branch v3 where everything will be tested together before releasing everything. I'll test this myself in a huge codebase with more than 3k tests, but I'd like to get other people running this on their projects too.

Thoughts?

Error with no-manual-cleanup in files with "require" imports

TypeError: Cannot read property 'match' of undefined at VariableDeclarator (node_modules/eslint-plugin-testing-library/lib/rules/no-manual-cleanup.js:62:66)

This is the only rule that fails in any file. It seems to fail in every file which uses require imports.

This is the value of node.init.arguments[0] when it has no value

Node {
  type: 'CallExpression',
  start: 3230,
  end: 3302,
  loc: SourceLocation {
    start: Position { line: 116, column: 23 },
    end: Position { line: 118, column: 6 }
  },
  range: [ 3230, 3302 ],
  callee: Node {
    type: 'MemberExpression',
    start: 3230,
    end: 3242,
    loc: SourceLocation { start: [Position], end: [Position] },
    range: [ 3230, 3242 ],
    object: Node {
      type: 'Identifier',
      start: 3230,
      end: 3237,
      loc: [SourceLocation],
      range: [Array],
      name: 'resolve',
      _babelType: 'Identifier',
      parent: [Circular]
    },
    property: Node {
      type: 'Identifier',
      start: 3238,
      end: 3242,
      loc: [SourceLocation],
      range: [Array],
      name: 'sync',
      _babelType: 'Identifier',
      parent: [Circular]
    },
    computed: false,
    _babelType: 'MemberExpression',
    parent: [Circular]
  },
  arguments: [
    Node {
      type: 'Literal',
      start: 3243,
      end: 3255,
      loc: [SourceLocation],
      range: [Array],
      value: 'typescript',
      raw: '"typescript"',
      _babelType: 'Literal',
      parent: [Circular]
    },
    Node {
      type: 'ObjectExpression',
      start: 3257,
      end: 3301,
      loc: [SourceLocation],
      range: [Array],
      properties: [Array],
      extra: [Object],
      _babelType: 'ObjectExpression',
      parent: [Circular]
    }
  ],
  _babelType: 'CallExpression',
  parent: Node {
    type: 'CallExpression',
    start: 3222,
    end: 3303,
    loc: SourceLocation { start: [Position], end: [Position] },
    range: [ 3222, 3303 ],
    callee: Node {
      type: 'Identifier',
      start: 3222,
      end: 3229,
      loc: [SourceLocation],
      range: [Array],
      name: 'require',
      _babelType: 'Identifier',
      parent: [Circular]
    },
    arguments: [ [Circular] ],
    _babelType: 'CallExpression',
    parent: Node {
      type: 'VariableDeclarator',
      start: 3217,
      end: 3303,
      loc: [SourceLocation],
      range: [Array],
      id: [Node],
      init: [Circular],
      _babelType: 'VariableDeclarator',
      parent: [Node]
    }
  }
}

RFC: fixer for `wait` (V7)

Would it be an option to add a fixable rule for the changes made to wait?
The implementation could look like this

export default function(context) {
  return {
    "CallExpression > Identifier[name=wait]"(node) {
      context.report({
        node: node,
        message: "waitForRename",
        fix: fixer => fixer.replaceText(node, "waitFor")
      });
    },
    "CallExpression[callee.name=waitFor][arguments.length=0]"(node) {
      context.report({
        node: node,
        message: "emptyCallBack",
        fix: fixer => fixer.replaceText(node, "waitFor(() => {})")
      });
    }
  };
}

input:

async () => {
	await wait(() => {});
  	await wait();
}

output:

async () => {
	await waitFor(() => {});
  	await waitFor(() => {});
}

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.


Missing package.json file.

A package.json file at the root of your project is required to release on npm.

Please follow the npm guideline to create a valid package.json file.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Allow destructuring from screen with prefer-screen-queries

It would be great if prefer-screen-queries could allow destructuring from screen, for example:

it("renders foo", () => {
  const { getByText } = screen;

  render(<App />);

  // currently flagged by rule
  expect(getByText("foo")).toBeTruthy();
});

or

const { getByText } = screen;

it("renders foo", () => {
  render(<App />);

  // currently flagged by rule
  expect(getByText("foo")).toBeTruthy();
});

Support ESLint 7.x

ESLint v7.0.0 is released 🎉

It would be awesome to have official ESLint 7 support. 👊
I'm happy to help where I can of course 🙂

Transfer to testing library org on GitHub.

Love this plugin. If you guys are interested we can transfer it over to the testing library org on GitHub. This would give the plugin a lot of visibility and maybe spur more contributions. I did this for the testcafe-testing-library I wrote and it definitely helped. Up to you of course.

`debug` doesn't seem to work when wrapped in a test case

I just encountered a case where debug doesn't seem to work.

describe("Home", () => {
  test("displays no posts if there are no posts", async () => {
    const { debug } = await render(<Home />);
    debug();
  });
})

Apparently, the render method doesn't seem to be detected when it's wrapped in a test case. Moving the render method outside the test case seems to trigger the no-debug rule.

I'm going to see how to fix it as soon as I can. But if anyone feels like contributing, feel free to fix it 🙂

By the way, it's not the first time something doesn't work because some code is wrapped in a test case. I really think we should provide more real-world examples to all of our rules.

UPDATE: it turns out the bug is caused by my await statement.

new rule request: prefer-find-by

as described in Kent's blog post, it would be nice to have an autofix rule that converts

await waitFor(() => {
  expect(screen.getByText("foo")).toBeInTheDocument()
}

into this:

expect (await screen.findByText("foo")).toBeInTheDocument()

await-async-utils false positives

The await-async-utils rule is finding function calls that aren't actually from testing library. For example, if I define a function wait in my test code:

function wait(delay) {
  return new Promise((resolve) => {
    setTimeout(resolve, delay);
  });
}

The lint rule will match it since it has the same name as the testing library's async util.

Would it make sense to first detect the import of testing-library, before applying the rule?

Feature request: convert no-get-by-for-checking-element-not-present to be prefer-appropriate-query-for-expect

// reports:
expect(getByText("Foo")).not.toBeInTheDocument() // as it already does
expect(queryByText("Foo")).toBeInTheDocument()

// does not report:
expect(queryByText("Foo")).not.toBeInTheDocument()
expect(getByText("Foo")).toBeInTheDocument()

In addition as discussed below in the comments, the use of get* queries inside of waitForElementToBeRemoved will no longer report an error either in a callback or directly as a parameter.

  //does not report:
  await waitForElementToBeRemoved(screen.getByTitle("Loading..."));
  await waitForElementToBeRemoved(() => screen.getByTitle("Loading..."));

RFC: new rule to prefer the usage of screen

It's preferred to use screen when using the query functions.

// bad
const component = render()
component.getBy()

// good
screen.getBy()

For a first implementation we can verify that the queries used have screen as callee.
This doesn't take the following into account, but it makes the rule simpler.

const screen = render()
screen.getBy

New rule: no-wait-for-empty-cb

New waitFor method added in dom-testing-library v7 introduces a new restriction on empty async utils: a callback must be passed as param. Empty callbacks are allowed but not recommended. Quoting changelog itself:

it's recommended to avoid an empty callback and instead insert an assertion in that callback function. This is because otherwise your test is relying on the "next tick of the event loop" before proceeding, and that's not consistent with the philosophy of the library:

Because of that, a new rule called no-wait-for-empty-cb could enforce to pass non-empty callbacks to waitFor.

What would be considered a non-empty callback tho? I would say any of these:

  • empty arrow function (i.e. () => {})
  • empty regular function (i.e. function () {})
  • a var referencing any of previously mentioned
  • a function or var called noop?
  • falsy values?

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.