Skip to content

Commit 8c2dc0c

Browse files
feat: plugin v3 (#97)
feat(await-async-utils): reflect waitFor changes (#89) feat: new rule no-wait-for-empty-callback (#94) feat: new rule prefer-wait-for (#88) feat: new rule prefer-screen-queries (#99) BREAKING CHANGE: drop support for node v8. Min version allowed is node v10.12 (#96) BREAKING CHANGE: rule `no-get-by-for-checking-element-not-present` removed in favor of new rule `prefer-presence-queries` (#98) Closes #85 Closes #86 Closes #90 Closes #92 Closes #95 Co-authored-by: timdeschryver <[email protected]>
1 parent 833277c commit 8c2dc0c

21 files changed

+1341
-292
lines changed

.travis.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
language: node_js
22

3-
node_js:
4-
- 8
5-
- 10
3+
env:
4+
- FORCE_COLOR=true
65

7-
branches:
8-
only: master
6+
node_js:
7+
- 10.12
8+
- 12
9+
- node
910

1011
jobs:
1112
include:
1213
- stage: release
14+
if: branch = master AND type != pull_request
1315
node_js: lts/*
1416
deploy:
1517
provider: script

README.md

+18-12
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
[![Tweet][tweet-badge]][tweet-url]
2424

2525
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
26+
2627
[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-)
28+
2729
<!-- ALL-CONTRIBUTORS-BADGE:END -->
2830

2931
## Installation
@@ -132,18 +134,21 @@ To enable this configuration use the `extends` property in your
132134

133135
## Supported Rules
134136

135-
| Rule | Description | Configurations | Fixable |
136-
| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------ |
137-
| [await-async-query](docs/rules/await-async-query.md) | Enforce async queries to have proper `await` | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
138-
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce async utils to be awaited properly | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
139-
| [await-fire-event](docs/rules/await-fire-event.md) | Enforce async fire event methods to be awaited | ![vue-badge][] | |
140-
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |
141-
| [no-await-sync-query](docs/rules/no-await-sync-query.md) | Disallow unnecessary `await` for sync queries | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
142-
| [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | ![angular-badge][] ![react-badge][] ![vue-badge][] | |
143-
| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
144-
| [no-get-by-for-checking-element-not-present](docs/rules/no-get-by-for-checking-element-not-present.md) | Disallow the use of `getBy*` queries when checking elements are not present | | |
145-
| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | |
146-
| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
137+
| Rule | Description | Configurations | Fixable |
138+
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------ |
139+
| [await-async-query](docs/rules/await-async-query.md) | Enforce async queries to have proper `await` | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
140+
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce async utils to be awaited properly | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
141+
| [await-fire-event](docs/rules/await-fire-event.md) | Enforce async fire event methods to be awaited | ![vue-badge][] | |
142+
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |
143+
| [no-await-sync-query](docs/rules/no-await-sync-query.md) | Disallow unnecessary `await` for sync queries | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
144+
| [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | ![angular-badge][] ![react-badge][] ![vue-badge][] | |
145+
| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
146+
| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | |
147+
| [no-wait-for-empty-callback](docs/rules/no-wait-for-empty-callback.md) | Disallow empty callbacks for `waitFor` and `waitForElementToBeRemoved` | | |
148+
| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
149+
| [prefer-presence-queries](docs/rules/prefer-presence-queries.md) | Enforce specific queries when checking element is present or not | | |
150+
| [prefer-screen-queries](docs/rules/prefer-screen-queries.md) | Suggest using screen while using queries | | |
151+
| [prefer-wait-for](docs/rules/prefer-wait-for.md) | Use `waitFor` instead of deprecated wait methods | | ![fixable-badge][] |
147152

148153
[build-badge]: https://img.shields.io/travis/testing-library/eslint-plugin-testing-library?style=flat-square
149154
[build-url]: https://travis-ci.org/testing-library/eslint-plugin-testing-library
@@ -194,6 +199,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
194199

195200
<!-- markdownlint-enable -->
196201
<!-- prettier-ignore-end -->
202+
197203
<!-- ALL-CONTRIBUTORS-LIST:END -->
198204

199205
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

docs/rules/await-async-utils.md

+10-15
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ Ensure that promises returned by async utils are handled properly.
66

77
Testing library provides several utilities for dealing with asynchronous code. These are useful to wait for an element until certain criteria or situation happens. The available async utils are:
88

9-
- `wait`
10-
- `waitForElement`
11-
- `waitForDomChange`
9+
- `waitFor` _(introduced in dom-testing-library v7)_
1210
- `waitForElementToBeRemoved`
11+
- `wait` _(**deprecated** in dom-testing-library v7)_
12+
- `waitForElement` _(**deprecated** in dom-testing-library v7)_
13+
- `waitForDomChange` _(**deprecated** in dom-testing-library v7)_
1314

1415
This rule aims to prevent users from forgetting to handle the returned promise from those async utils, which could lead to unexpected errors in the tests execution. The promises can be handled by using either `await` operator or `then` method.
1516

@@ -18,22 +19,19 @@ Examples of **incorrect** code for this rule:
1819
```js
1920
test('something incorrectly', async () => {
2021
// ...
21-
wait(() => getByLabelText('email'));
22+
waitFor(() => {});
2223

23-
const [usernameElement, passwordElement] = waitForElement(
24+
const [usernameElement, passwordElement] = waitFor(
2425
() => [
2526
getByLabelText(container, 'username'),
2627
getByLabelText(container, 'password'),
2728
],
2829
{ container }
2930
);
3031

31-
waitForDomChange(() => {
32-
return { container };
33-
});
32+
waitFor(() => {}, { timeout: 100 });
3433

3534
waitForElementToBeRemoved(() => document.querySelector('div.getOuttaHere'));
36-
// ...
3735
});
3836
```
3937

@@ -43,9 +41,9 @@ Examples of **correct** code for this rule:
4341
test('something correctly', async () => {
4442
// ...
4543
// `await` operator is correct
46-
await wait(() => getByLabelText('email'));
44+
await waitFor(() => getByLabelText('email'));
4745

48-
const [usernameElement, passwordElement] = await waitForElement(
46+
const [usernameElement, passwordElement] = await waitFor(
4947
() => [
5048
getByLabelText(container, 'username'),
5149
getByLabelText(container, 'password'),
@@ -54,16 +52,13 @@ test('something correctly', async () => {
5452
);
5553

5654
// `then` chained method is correct
57-
waitForDomChange(() => {
58-
return { container };
59-
})
55+
waitFor(() => {}, { timeout: 100 })
6056
.then(() => console.log('DOM changed!'))
6157
.catch(err => console.log(`Error you need to deal with: ${err}`));
6258

6359
// return the promise within a function is correct too!
6460
const makeCustomWait = () =>
6561
waitForElementToBeRemoved(() => document.querySelector('div.getOuttaHere'));
66-
// ...
6762
});
6863
```
6964

docs/rules/no-get-by-for-checking-element-not-present.md

-63
This file was deleted.
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Empty callbacks inside `waitFor` and `waitForElementToBeRemoved` are not preferred (no-wait-for-empty-callback)
2+
3+
## Rule Details
4+
5+
This rule aims to ensure the correct usage of `waitFor` and `waitForElementToBeRemoved`, in the way that they're intended to be used.
6+
If an empty callback is passed, these methods will just wait next tick of the event loop before proceeding, and that's not consistent with the philosophy of the library.
7+
**Instead, insert an assertion in that callback function.**
8+
9+
Examples of **incorrect** code for this rule:
10+
11+
```js
12+
const foo = async () => {
13+
await waitFor(() => {});
14+
await waitFor(function() {});
15+
await waitFor(noop);
16+
17+
await waitForElementToBeRemoved(() => {});
18+
await waitForElementToBeRemoved(function() {});
19+
await waitForElementToBeRemoved(noop);
20+
};
21+
```
22+
23+
Examples of **correct** code for this rule:
24+
25+
```js
26+
const foo = async () => {
27+
await waitFor(() => {
28+
screen.getByText(/submit/i);
29+
});
30+
31+
const submit = screen.getByText(/submit/i);
32+
await waitForElementToBeRemoved(() => submit);
33+
// or
34+
await waitForElementToBeRemoved(submit);
35+
};
36+
```
37+
38+
## Further Reading
39+
40+
- [dom-testing-library v7 release](https://github.com/testing-library/dom-testing-library/releases/tag/v7.0.0)

docs/rules/prefer-presence-queries.md

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Enforce specific queries when checking element is present or not (prefer-presence-queries)
2+
3+
The (DOM) Testing Library allows to query DOM elements using different types of queries such as `get*` and `query*`. Using `get*` throws an error in case the element is not found, while `query*` returns null instead of throwing (or empty array for `queryAllBy*` ones). These differences are useful in some situations:
4+
5+
- using `getBy*` queries when asserting if element is present, so if the element is not found the error thrown will offer better info than asserting with other queries which will not throw an error.
6+
- using `queryBy*` queries when asserting if element is not present, so the test doesn't fail immediately when the element is not found and the assertion can be executed properly.
7+
8+
## Rule details
9+
10+
This rule fires whenever:
11+
12+
- `queryBy*` or `queryAllBy*` are used to assert element **is** present with `.toBeInTheDocument()`, `toBeTruthy()` or `.toBeDefined()` matchers or negated matchers from case below.
13+
- `getBy*` or `getAllBy*` are used to assert element **is not** present with `.toBeNull()` or `.toBeFalsy()` matchers or negated matchers from case above.
14+
15+
Examples of **incorrect** code for this rule:
16+
17+
```js
18+
test('some test', () => {
19+
render(<App />);
20+
21+
// check element is present with `queryBy*`
22+
expect(screen.queryByText('button')).toBeInTheDocument();
23+
expect(screen.queryAllByText('button')[0]).toBeTruthy();
24+
expect(screen.queryByText('button')).toBeNull();
25+
expect(screen.queryAllByText('button')[2]).not.toBeNull();
26+
expect(screen.queryByText('button')).not.toBeFalsy();
27+
28+
// check element is NOT present with `getBy*`
29+
expect(screen.getByText('loading')).not.toBeInTheDocument();
30+
expect(screen.getAllByText('loading')[1]).not.toBeTruthy();
31+
expect(screen.getByText('loading')).not.toBeNull();
32+
expect(screen.getAllByText('loading')[3]).toBeNull();
33+
expect(screen.getByText('loading')).toBeFalsy();
34+
});
35+
```
36+
37+
Examples of **correct** code for this rule:
38+
39+
```js
40+
test('some test', async () => {
41+
render(<App />);
42+
// check element is present with `getBy*`
43+
expect(screen.getByText('button')).toBeInTheDocument();
44+
expect(screen.getAllByText('button')[9]).toBeTruthy();
45+
expect(screen.getByText('button')).toBeNull();
46+
expect(screen.getAllByText('button')[7]).not.toBeNull();
47+
expect(screen.getByText('button')).not.toBeFalsy();
48+
49+
// check element is NOT present with `queryBy*`
50+
expect(screen.queryByText('loading')).not.toBeInTheDocument();
51+
expect(screen.queryAllByText('loading')[8]).not.toBeTruthy();
52+
expect(screen.queryByText('loading')).not.toBeNull();
53+
expect(screen.queryAllByText('loading')[6]).toBeNull();
54+
expect(screen.queryByText('loading')).toBeFalsy();
55+
56+
// `findBy*` queries are out of the scope for this rule
57+
const button = await screen.findByText('submit');
58+
expect(button).toBeInTheDocument();
59+
});
60+
```
61+
62+
## Further Reading
63+
64+
- [Testing Library queries cheatsheet](https://testing-library.com/docs/dom-testing-library/cheatsheet#queries)
65+
- [Asserting elements are not present](https://testing-library.com/docs/guide-disappearance#asserting-elements-are-not-present)
66+
- [jest-dom note about using `getBy` within assertions](https://testing-library.com/docs/ecosystem-jest-dom)
67+
- [Waiting for appearance](https://testing-library.com/docs/guide-disappearance#waiting-for-appearance)

docs/rules/prefer-screen-queries.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Suggest using screen while using queries (prefer-screen-queries)
2+
3+
## Rule Details
4+
5+
DOM Testing Library (and other Testing Library frameworks built on top of it) exports a `screen` object which has every query (and a `debug` method). This works better with autocomplete and makes each test a little simpler to write and maintain.
6+
This rule aims to force writing tests using queries directly from `screen` object rather than destructuring them from `render` result.
7+
8+
Examples of **incorrect** code for this rule:
9+
10+
```js
11+
// calling a query from the `render` method
12+
const { getByText } = render(<Component />);
13+
getByText('foo');
14+
15+
const utils = render(<Component />);
16+
utils.getByText('foo');
17+
```
18+
19+
Examples of **correct** code for this rule:
20+
21+
```js
22+
import { screen } from '@testing-library/any-framework';
23+
24+
render(<Component />);
25+
screen.getByText('foo');
26+
```
27+
28+
## Further Reading
29+
30+
- [`screen` documentation](https://testing-library.com/docs/dom-testing-library/api-queries#screen)

0 commit comments

Comments
 (0)