Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: no-get-by-for-checking-element-not-present #65

Merged
merged 10 commits into from
Jan 28, 2020

Conversation

thomaslombart
Copy link
Collaborator

@thomaslombart thomaslombart commented Jan 21, 2020

Closes #61

@thomaslombart thomaslombart marked this pull request as ready for review January 21, 2020 16:09
@thomaslombart
Copy link
Collaborator Author

@Belco90 If you ever see something missing in the docs or misexplained, could you handle it? I have to admit I'm still not really convinced by the usefulness of that rule and I think you have the use cases in mind.

Copy link
Contributor

@emmenko emmenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a quick look, looks good so far 👌

lib/index.js Outdated Show resolved Hide resolved
tests/lib/rules/no-get-by-for-absent-elements.js Outdated Show resolved Hide resolved
tests/lib/rules/no-get-by-for-absent-elements.js Outdated Show resolved Hide resolved
docs/rules/no-get-by-for-absent-elements.md Outdated Show resolved Hide resolved
@thomaslombart thomaslombart force-pushed the feat/no-get-by-for-absent-elements branch from 56e08ca to 0d11ebd Compare January 23, 2020 08:40
Copy link
Member

@Belco90 Belco90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the motivation for this rule has been misunderstood, in part it was my fault because of some wrong examples I added before to previous rule doc. If you see the original issue, you can see properly the two different cases when this rule should be applied:

  • Asserting elements are not present: here we need to look for a expect statement and few specific matchers as .not.toBeInTheDocument(), .toBeNull() or .toBeFalsy()
  • Waiting for disappearance: here we do not have to look for a expect statement, but waitForElementToBeRemoved method

The rule doesn't need a huge amount of changes tho, just clarifying things in the docs and examples, add more test cases and maybe improve the rule implementation for some edge cases.

README.md Outdated
| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |
| [no-get-by-for-asserting-element-not-present](docs/rules/no-get-by-for-asserting-element-not-present) | Disallow the use of `expect(getBy*)` when elements are not present | | |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| [no-get-by-for-asserting-element-not-present](docs/rules/no-get-by-for-asserting-element-not-present) | Disallow the use of `expect(getBy*)` when elements are not present | | |
| [no-get-by-for-asserting-element-not-present](docs/rules/no-get-by-for-asserting-element-not-present) | Disallow the use of `getBy*` queries when checking elements are not present | | |

@@ -1,75 +1,58 @@
# Disallow the use of `expect(getBy*)` (prefer-expect-query-by)
# Disallow the use of `expect(getBy*)` when elements are not present (no-get-by-for-asserting-element-not-present)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Disallow the use of `expect(getBy*)` when elements are not present (no-get-by-for-asserting-element-not-present)
# Disallow the use of `getBy*` queries when checking elements are not present (no-get-by-for-asserting-element-not-present)


The (DOM) Testing Library allows to query DOM elements using different types of queries such as `getBy*` and `queryBy*`. Using `getBy*` throws an error in case the element is not found. This is useful when:

- using method like `waitForElement`, which are `async` functions that will wait for the element to be found until a certain timeout, after that the test will fail.
- using `getBy` queries as an assert itself, so if the element is not found the error thrown will work as the check itself within the test.

However, when trying to assert if an element is not present or disappearance, we can't use `getBy*` as the test will fail immediately. Instead it is recommended to use `queryBy*`, which does not throw and therefore we can assert that e.g. `expect(queryByText("Foo")).not.toBeInTheDocument()`.
However, when trying to assert if an element is not present or disappearance, using `getBy*` will make the test fail immediately. Instead it is recommended to use `queryBy*`, which does not throw and therefore we can assert that e.g. `expect(queryByText("Foo")).not.toBeInTheDocument()`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
However, when trying to assert if an element is not present or disappearance, using `getBy*` will make the test fail immediately. Instead it is recommended to use `queryBy*`, which does not throw and therefore we can assert that e.g. `expect(queryByText("Foo")).not.toBeInTheDocument()`.
However, when asserting if an element is not present or waiting for disappearance, using `getBy*` will make the test fail immediately. Instead it is recommended to use `queryBy*`, which does not throw and therefore we can:
- assert element does not exist: `expect(queryByText("Foo")).not.toBeInTheDocument()`
- wait for disappearance: `await waitForElementToBeRemoved(() => queryByText('the mummy'))`


The (DOM) Testing Library allows to query DOM elements using different types of queries such as `getBy*` and `queryBy*`. Using `getBy*` throws an error in case the element is not found. This is useful when:

- using method like `waitForElement`, which are `async` functions that will wait for the element to be found until a certain timeout, after that the test will fail.
- using `getBy` queries as an assert itself, so if the element is not found the error thrown will work as the check itself within the test.

However, when trying to assert if an element is not present or disappearance, we can't use `getBy*` as the test will fail immediately. Instead it is recommended to use `queryBy*`, which does not throw and therefore we can assert that e.g. `expect(queryByText("Foo")).not.toBeInTheDocument()`.
However, when trying to assert if an element is not present or disappearance, using `getBy*` will make the test fail immediately. Instead it is recommended to use `queryBy*`, which does not throw and therefore we can assert that e.g. `expect(queryByText("Foo")).not.toBeInTheDocument()`.

> The same applies for the `getAll*` and `queryAll*` queries too.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure if this rule should be applied to getAllBy. You can't check expect(getAllByText('Foo')[0]).toBeNull() as that would return undefined, so you'll need to use a different matcher. In addition, there is no reference to getAllBy in testing library docs, so I would remove this comment and apply this rule only to getBy.


> The same applies for the `getAll*` and `queryAll*` queries too.

## Rule details

This rule gives a notification whenever `expect` is used with one of the query functions that throw an error if the element is not found.
This rule gives a notification whenever `expect` is used with one of the query functions that throws an error if the element is not found.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This rule gives a notification whenever `expect` is used with one of the query functions that throws an error if the element is not found.
This rule gives a notification whenever:
- `expect` is used to assert element does not exist with `.not.toBeInTheDocument()` or `.toBeNull()` matchers
- `waitForElementToBeRemoved` async util is used to wait for element to be removed from DOM

docs: {
category: 'Best Practices',
description:
'Disallow the use of getBy* queries in expect calls when elements may be absent',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'Disallow the use of getBy* queries in expect calls when elements may be absent',
'Disallow the use of `getBy*` queries when checking elements are not present',

{ code: `expect(${queryName}('Hello')).toBeTruthy()` },
{ code: `expect(rendered.${queryName}('Hello')).toEqual("World")` },
{ code: `expect(rendered.${queryName}('Hello')).not.toBeFalsy()` },
],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need valid tests for waitForElementToBeRemoved, which is the second part of the rule.

],
[]
),
invalid: getByVariants.reduce(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should apply this only to getBy

errors: [{ messageId: 'expectQueryBy' }],
},
{
code: `expect(${queryName}('Hello')).toBeUndefined()`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this one. toBeUndefined won't work even using queryBy as it returns null so we should not include this one in the rule probably.

},
{
code: `(async () => {
await waitForElementToBeRemoved(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would other variants for this one having arrow function returning implicitly and having a regular function.

@Belco90 Belco90 changed the title feat: add no-get-by-for-absent-elements feat: no-get-by-for-asserting-element-not-present Jan 23, 2020
@Belco90 Belco90 changed the title feat: no-get-by-for-asserting-element-not-present feat: no-get-by-for-checking-element-not-present Jan 23, 2020
Copy link
Member

@Belco90 Belco90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule has to be renamed again for not mentioning asserting in the name, right?

Also: this is a breaking change as we are removing previous prefer-expect-query-by rule, so we need to add BREAKING CHANGE comment when merging this.

README.md Outdated Show resolved Hide resolved
Copy link
Contributor

@emmenko emmenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank, we're almost there 🙌

```js
test('some test', () => {
const { getByText } = render(<App />);
expect(getByText('Foo')).toBeInTheDocument();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(getByText('Foo')).toBeInTheDocument();
expect(getByText('Foo')).toBeInTheDocument();
expect(queryByText('Foo')).not.toBeInTheDocument();
expect(queryByText('Foo')).toBeFalsy();

},
messages: {
expectQueryBy:
'Use `expect(getBy*)` only when elements are present, otherwise use `expect(queryBy*)`.',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'Use `expect(getBy*)` only when elements are present, otherwise use `expect(queryBy*)`.',
'Use `getBy*` only when elements are present, otherwise use `queryBy*` when checking that elements are not present.',

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is when cheking that elements are not present necessary? I feel like it's too much, what about:

Use getBy* only when checking elements are present, otherwise use queryBy*

Belco90
Belco90 previously approved these changes Jan 23, 2020
Copy link
Member

@Belco90 Belco90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brilliant! Thank you so much for your effort and sorry for previous misleading comments.

emmenko
emmenko previously approved these changes Jan 23, 2020
Copy link
Contributor

@emmenko emmenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, thanks again for improving the rule! 💯

@Belco90
Copy link
Member

Belco90 commented Jan 23, 2020

Awesome. Tomorrow morning I'll create the PR for await-async-utils so maybe we can wait these 2 together to release v2?

@Belco90 Belco90 dismissed stale reviews from emmenko and themself via f39276b January 28, 2020 14:47
@Belco90 Belco90 merged commit 1aa9238 into master Jan 28, 2020
@Belco90 Belco90 deleted the feat/no-get-by-for-absent-elements branch January 28, 2020 15:04
@Belco90
Copy link
Member

Belco90 commented Jan 28, 2020

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Replace prefer-expect-query-by by better rule
4 participants