diff --git a/README.md b/README.md
index e834f74d..26e0c687 100644
--- a/README.md
+++ b/README.md
@@ -134,21 +134,21 @@ To enable this configuration use the `extends` property in your
## Supported Rules
-| Rule | Description | Configurations | Fixable |
-| ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------ |
-| [await-async-query](docs/rules/await-async-query.md) | Enforce async queries to have proper `await` | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
-| [await-async-utils](docs/rules/await-async-utils.md) | Enforce async utils to be awaited properly | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
-| [await-fire-event](docs/rules/await-fire-event.md) | Enforce async fire event methods to be awaited | ![vue-badge][] | |
-| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |
-| [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][] | |
-| [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | ![angular-badge][] ![react-badge][] ![vue-badge][] | |
-| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
-| [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 | | |
-| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | |
-| [no-wait-for-empty-callback](docs/rules/no-wait-for-empty-callback.md) | Disallow empty callbacks for `waitFor` and `waitForElementToBeRemoved` | | |
-| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
-| [prefer-screen-queries](docs/rules/prefer-screen-queries.md) | Suggest using screen while using queries | | |
-| [prefer-wait-for](docs/rules/prefer-wait-for.md) | Use `waitFor` instead of deprecated wait methods | | ![fixable-badge][] |
+| Rule | Description | Configurations | Fixable |
+| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------ |
+| [await-async-query](docs/rules/await-async-query.md) | Enforce async queries to have proper `await` | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
+| [await-async-utils](docs/rules/await-async-utils.md) | Enforce async utils to be awaited properly | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
+| [await-fire-event](docs/rules/await-fire-event.md) | Enforce async fire event methods to be awaited | ![vue-badge][] | |
+| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |
+| [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][] | |
+| [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | ![angular-badge][] ![react-badge][] ![vue-badge][] | |
+| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
+| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | |
+| [no-wait-for-empty-callback](docs/rules/no-wait-for-empty-callback.md) | Disallow empty callbacks for `waitFor` and `waitForElementToBeRemoved` | | |
+| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
+| [prefer-presence-queries](docs/rules/prefer-presence-queries.md) | Enforce specific queries when checking element is present or not | | |
+| [prefer-screen-queries](docs/rules/prefer-screen-queries.md) | Suggest using screen while using queries | | |
+| [prefer-wait-for](docs/rules/prefer-wait-for.md) | Use `waitFor` instead of deprecated wait methods | | ![fixable-badge][] |
[build-badge]: https://img.shields.io/travis/testing-library/eslint-plugin-testing-library?style=flat-square
[build-url]: https://travis-ci.org/testing-library/eslint-plugin-testing-library
diff --git a/docs/rules/no-get-by-for-checking-element-not-present.md b/docs/rules/no-get-by-for-checking-element-not-present.md
deleted file mode 100644
index 0dd2a354..00000000
--- a/docs/rules/no-get-by-for-checking-element-not-present.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Disallow the use of `getBy*` queries when checking elements are not present (no-get-by-for-checking-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 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'))`
-
-## Rule details
-
-This rule fires 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
-
-Examples of **incorrect** code for this rule:
-
-```js
-test('some test', () => {
- const { getByText } = render();
- expect(getByText('Foo')).not.toBeInTheDocument();
- expect(getByText('Foo')).not.toBeTruthy();
- expect(getByText('Foo')).toBeFalsy();
- expect(getByText('Foo')).toBeNull();
-});
-```
-
-```js
-test('some test', async () => {
- const utils = render();
- await waitForElementToBeRemoved(() => utils.getByText('Foo'));
-});
-```
-
-Examples of **correct** code for this rule:
-
-```js
-test('some test', () => {
- const { getByText } = render();
- expect(getByText('Foo')).toBeInTheDocument();
- expect(getByText('Foo')).not.toBeDisabled();
- expect(queryByText('Foo')).not.toBeInTheDocument();
- expect(queryByText('Foo')).toBeFalsy();
-});
-```
-
-```js
-test('some test', async () => {
- const utils = render();
- await waitForElementToBeRemoved(() => utils.queryByText('Foo'));
-});
-```
-
-## Further Reading
-
-- [Asserting elements are not present](https://testing-library.com/docs/guide-disappearance#asserting-elements-are-not-present)
-- [Waiting for disappearance](https://testing-library.com/docs/guide-disappearance#waiting-for-disappearance)
-- [jest-dom note about using `getBy` within assertions](https://testing-library.com/docs/ecosystem-jest-dom)
-- [Testing Library queries cheatsheet](https://testing-library.com/docs/dom-testing-library/cheatsheet#queries)
diff --git a/docs/rules/prefer-presence-queries.md b/docs/rules/prefer-presence-queries.md
new file mode 100644
index 00000000..c178ea8a
--- /dev/null
+++ b/docs/rules/prefer-presence-queries.md
@@ -0,0 +1,67 @@
+# Enforce specific queries when checking element is present or not (prefer-presence-queries)
+
+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:
+
+- 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.
+- 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.
+
+## Rule details
+
+This rule fires whenever:
+
+- `queryBy*` or `queryAllBy*` are used to assert element **is** present with `.toBeInTheDocument()`, `toBeTruthy()` or `.toBeDefined()` matchers or negated matchers from case below.
+- `getBy*` or `getAllBy*` are used to assert element **is not** present with `.toBeNull()` or `.toBeFalsy()` matchers or negated matchers from case above.
+
+Examples of **incorrect** code for this rule:
+
+```js
+test('some test', () => {
+ render();
+
+ // check element is present with `queryBy*`
+ expect(screen.queryByText('button')).toBeInTheDocument();
+ expect(screen.queryAllByText('button')[0]).toBeTruthy();
+ expect(screen.queryByText('button')).toBeNull();
+ expect(screen.queryAllByText('button')[2]).not.toBeNull();
+ expect(screen.queryByText('button')).not.toBeFalsy();
+
+ // check element is NOT present with `getBy*`
+ expect(screen.getByText('loading')).not.toBeInTheDocument();
+ expect(screen.getAllByText('loading')[1]).not.toBeTruthy();
+ expect(screen.getByText('loading')).not.toBeNull();
+ expect(screen.getAllByText('loading')[3]).toBeNull();
+ expect(screen.getByText('loading')).toBeFalsy();
+});
+```
+
+Examples of **correct** code for this rule:
+
+```js
+test('some test', async () => {
+ render();
+ // check element is present with `getBy*`
+ expect(screen.getByText('button')).toBeInTheDocument();
+ expect(screen.getAllByText('button')[9]).toBeTruthy();
+ expect(screen.getByText('button')).toBeNull();
+ expect(screen.getAllByText('button')[7]).not.toBeNull();
+ expect(screen.getByText('button')).not.toBeFalsy();
+
+ // check element is NOT present with `queryBy*`
+ expect(screen.queryByText('loading')).not.toBeInTheDocument();
+ expect(screen.queryAllByText('loading')[8]).not.toBeTruthy();
+ expect(screen.queryByText('loading')).not.toBeNull();
+ expect(screen.queryAllByText('loading')[6]).toBeNull();
+ expect(screen.queryByText('loading')).toBeFalsy();
+
+ // `findBy*` queries are out of the scope for this rule
+ const button = await screen.findByText('submit');
+ expect(button).toBeInTheDocument();
+});
+```
+
+## Further Reading
+
+- [Testing Library queries cheatsheet](https://testing-library.com/docs/dom-testing-library/cheatsheet#queries)
+- [Asserting elements are not present](https://testing-library.com/docs/guide-disappearance#asserting-elements-are-not-present)
+- [jest-dom note about using `getBy` within assertions](https://testing-library.com/docs/ecosystem-jest-dom)
+- [Waiting for appearance](https://testing-library.com/docs/guide-disappearance#waiting-for-appearance)
diff --git a/lib/index.js b/lib/index.js
index d5874718..0c16e932 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -8,10 +8,10 @@ const rules = {
'no-await-sync-query': require('./rules/no-await-sync-query'),
'no-debug': require('./rules/no-debug'),
'no-dom-import': require('./rules/no-dom-import'),
- 'no-get-by-for-checking-element-not-present': require('./rules/no-get-by-for-checking-element-not-present'),
'no-manual-cleanup': require('./rules/no-manual-cleanup'),
'no-wait-for-empty-callback': require('./rules/no-wait-for-empty-callback'),
'prefer-explicit-assert': require('./rules/prefer-explicit-assert'),
+ 'prefer-presence-queries': require('./rules/prefer-presence-queries'),
'prefer-screen-queries': require('./rules/prefer-screen-queries'),
'prefer-wait-for': require('./rules/prefer-wait-for'),
};
diff --git a/lib/rules/no-get-by-for-checking-element-not-present.js b/lib/rules/no-get-by-for-checking-element-not-present.js
deleted file mode 100644
index 3eeaba5a..00000000
--- a/lib/rules/no-get-by-for-checking-element-not-present.js
+++ /dev/null
@@ -1,83 +0,0 @@
-'use strict';
-
-const { getDocsUrl } = require('../utils');
-
-const FALSY_MATCHERS = ['toBeNull', 'toBeFalsy'];
-const NOT_ALLOWED_NEGATED_MATCHERS = [
- 'toBeInTheDocument',
- 'toBeTruthy',
- 'toBeDefined',
-];
-
-module.exports = {
- meta: {
- docs: {
- category: 'Best Practices',
- description:
- 'Disallow the use of `getBy*` queries when checking elements are not present',
- recommended: 'error',
- url: getDocsUrl('no-get-by-for-checking-element-not-present'),
- },
- messages: {
- expectQueryBy:
- 'Use `getBy*` only when checking elements are present, otherwise use `queryBy*`',
- },
- schema: [],
- type: 'suggestion',
- fixable: null,
- },
-
- create: context => ({
- [`Identifier[name=/getBy|getAllBy/]`](node) {
- const expectCallNode = findClosestCallNode(node, 'expect');
-
- // expect(getByText("foo"))...
- if (expectCallNode) {
- const expectStatement = expectCallNode.parent;
- const matcher = expectStatement.property.name;
-
- if (matcher === 'not') {
- const negatedMatcher = expectStatement.parent.property.name;
-
- if (NOT_ALLOWED_NEGATED_MATCHERS.includes(negatedMatcher)) {
- return context.report({
- node,
- messageId: 'expectQueryBy',
- });
- }
- }
-
- if (FALSY_MATCHERS.includes(matcher)) {
- return context.report({
- node,
- messageId: 'expectQueryBy',
- });
- }
- }
-
- const waitCallNode = findClosestCallNode(
- node,
- 'waitForElementToBeRemoved'
- );
-
- if (waitCallNode) {
- return context.report({
- node,
- messageId: 'expectQueryBy',
- });
- }
- },
- }),
-};
-
-function findClosestCallNode(node, name) {
- if (!node.parent) {
- return false;
- }
-
- if (node.type === 'CallExpression' && node.callee.name === name) {
- return node;
- } else {
- return findClosestCallNode(node.parent, name);
- }
-}
diff --git a/lib/rules/prefer-presence-queries.js b/lib/rules/prefer-presence-queries.js
new file mode 100644
index 00000000..94832173
--- /dev/null
+++ b/lib/rules/prefer-presence-queries.js
@@ -0,0 +1,94 @@
+'use strict';
+
+const { getDocsUrl, ALL_QUERIES_METHODS } = require('../utils');
+
+const QUERIES_REGEXP = new RegExp(
+ `^(get|query)(All)?(${ALL_QUERIES_METHODS.join('|')})$`
+);
+const PRESENCE_MATCHERS = ['toBeInTheDocument', 'toBeTruthy', 'toBeDefined'];
+const ABSENCE_MATCHERS = ['toBeNull', 'toBeFalsy'];
+
+module.exports = {
+ meta: {
+ docs: {
+ category: 'Best Practices',
+ description:
+ 'Ensure appropriate get*/query* queries are used with their respective matchers',
+ recommended: 'error',
+ url: getDocsUrl('prefer-presence-queries'),
+ },
+ messages: {
+ presenceQuery:
+ 'Use `getBy*` queries rather than `queryBy*` for checking element is present',
+ absenceQuery:
+ 'Use `queryBy*` queries rather than `getBy*` for checking element is NOT present',
+ expectQueryBy:
+ 'Use `getBy*` only when checking elements are present, otherwise use `queryBy*`',
+ },
+ schema: [],
+ type: 'suggestion',
+ fixable: null,
+ },
+
+ create: context => ({
+ [`CallExpression Identifier[name=${QUERIES_REGEXP}]`](node) {
+ const expectCallNode = findClosestCallNode(node, 'expect');
+
+ if (expectCallNode) {
+ const expectStatement = expectCallNode.parent;
+ let matcher = expectStatement.property && expectStatement.property.name;
+ let isNegatedMatcher = false;
+
+ if (matcher === 'not') {
+ isNegatedMatcher = true;
+ matcher =
+ expectStatement.parent &&
+ expectStatement.parent.property &&
+ expectStatement.parent.property.name;
+ }
+
+ if (!matcher) {
+ return;
+ }
+
+ const validMatchers = isThrowingQuery(node)
+ ? PRESENCE_MATCHERS
+ : ABSENCE_MATCHERS;
+
+ const invalidMatchers = isThrowingQuery(node)
+ ? ABSENCE_MATCHERS
+ : PRESENCE_MATCHERS;
+
+ const messageId = isThrowingQuery(node)
+ ? 'absenceQuery'
+ : 'presenceQuery';
+
+ if (
+ (!isNegatedMatcher && invalidMatchers.includes(matcher)) ||
+ (isNegatedMatcher && validMatchers.includes(matcher))
+ ) {
+ return context.report({
+ node,
+ messageId,
+ });
+ }
+ }
+ },
+ }),
+};
+
+function isThrowingQuery(node) {
+ return node.name.startsWith('get');
+}
+
+function findClosestCallNode(node, name) {
+ if (!node.parent) {
+ return false;
+ }
+
+ if (node.type === 'CallExpression' && node.callee.name === name) {
+ return node;
+ } else {
+ return findClosestCallNode(node.parent, name);
+ }
+}
diff --git a/tests/lib/rules/no-get-by-for-checking-element-not-present.js b/tests/lib/rules/no-get-by-for-checking-element-not-present.js
deleted file mode 100644
index fc1ae8dc..00000000
--- a/tests/lib/rules/no-get-by-for-checking-element-not-present.js
+++ /dev/null
@@ -1,110 +0,0 @@
-'use strict';
-
-const RuleTester = require('eslint').RuleTester;
-const rule = require('../../../lib/rules/no-get-by-for-checking-element-not-present');
-const { ALL_QUERIES_METHODS } = require('../../../lib/utils');
-
-const ruleTester = new RuleTester({
- parserOptions: { ecmaVersion: 2017, sourceType: 'module' },
-});
-
-const getByQueries = ALL_QUERIES_METHODS.map(method => `get${method}`);
-const queryByQueries = ALL_QUERIES_METHODS.map(method => `query${method}`);
-
-const allQueryUseInAssertion = queryName => [
- queryName,
- `rendered.${queryName}`,
-];
-
-const getValidAssertion = (query, matcher) =>
- allQueryUseInAssertion(query).map(query => ({
- code: `expect(${query}('Hello'))${matcher}`,
- }));
-
-const getInvalidAssertion = (query, matcher) =>
- allQueryUseInAssertion(query).map(query => ({
- code: `expect(${query}('Hello'))${matcher}`,
- errors: [{ messageId: 'expectQueryBy' }],
- }));
-
-ruleTester.run('no-get-by-for-checking-element-not-present', rule, {
- valid: [
- ...getByQueries.reduce(
- (validRules, queryName) => [
- ...validRules,
- ...getValidAssertion(queryName, '.toBeInTheDocument()'),
- ...getValidAssertion(queryName, '.toBe("foo")'),
- ...getValidAssertion(queryName, '.toBeTruthy()'),
- ...getValidAssertion(queryName, '.toEqual("World")'),
- ...getValidAssertion(queryName, '.not.toBeFalsy()'),
- ...getValidAssertion(queryName, '.not.toBeNull()'),
- ...getValidAssertion(queryName, '.not.toBeDisabled()'),
- ...getValidAssertion(queryName, '.not.toHaveClass("btn")'),
- ],
- []
- ),
- ...queryByQueries.reduce(
- (validRules, queryName) => [
- ...validRules,
- ...getValidAssertion(queryName, '.not.toBeInTheDocument()'),
- ...getValidAssertion(queryName, '.toBeNull()'),
- ...getValidAssertion(queryName, '.not.toBeTruthy()'),
- ...getValidAssertion(queryName, '.not.toBeDefined()'),
- ...getValidAssertion(queryName, '.toBeFalsy()'),
- {
- code: `(async () => {
- await waitForElementToBeRemoved(() => {
- return ${queryName}("hello")
- })
- })()`,
- },
- {
- code: `(async () => {
- await waitForElementToBeRemoved(() => ${queryName}("hello"))
- })()`,
- },
- {
- code: `(async () => {
- await waitForElementToBeRemoved(function() {
- return ${queryName}("hello")
- })
- })()`,
- },
- ],
- []
- ),
- ],
- invalid: getByQueries.reduce(
- (invalidRules, queryName) => [
- ...invalidRules,
- ...getInvalidAssertion(queryName, '.not.toBeInTheDocument()'),
- ...getInvalidAssertion(queryName, '.toBeNull()'),
- ...getInvalidAssertion(queryName, '.not.toBeTruthy()'),
- ...getInvalidAssertion(queryName, '.not.toBeDefined()'),
- ...getInvalidAssertion(queryName, '.toBeFalsy()'),
- {
- code: `(async () => {
- await waitForElementToBeRemoved(() => {
- return ${queryName}("hello")
- })
- })()`,
- errors: [{ messageId: 'expectQueryBy' }],
- },
- {
- code: `(async () => {
- await waitForElementToBeRemoved(() => ${queryName}("hello"))
- })()`,
- errors: [{ messageId: 'expectQueryBy' }],
- },
- {
- code: `(async () => {
- await waitForElementToBeRemoved(function() {
- return ${queryName}("hello")
- })
- })()`,
- errors: [{ messageId: 'expectQueryBy' }],
- },
- ],
- []
- ),
-});
diff --git a/tests/lib/rules/prefer-presence-queries.js b/tests/lib/rules/prefer-presence-queries.js
new file mode 100644
index 00000000..76469be6
--- /dev/null
+++ b/tests/lib/rules/prefer-presence-queries.js
@@ -0,0 +1,187 @@
+'use strict';
+
+const RuleTester = require('eslint').RuleTester;
+const rule = require('../../../lib/rules/prefer-presence-queries');
+const { ALL_QUERIES_METHODS } = require('../../../lib/utils');
+
+const ruleTester = new RuleTester({
+ parserOptions: { ecmaVersion: 2017, sourceType: 'module' },
+});
+
+const getByQueries = ALL_QUERIES_METHODS.map(method => `get${method}`);
+const getAllByQueries = ALL_QUERIES_METHODS.map(method => `getAll${method}`);
+const queryByQueries = ALL_QUERIES_METHODS.map(method => `query${method}`);
+const queryAllByQueries = ALL_QUERIES_METHODS.map(
+ method => `queryAll${method}`
+);
+
+const allQueryUseInAssertion = queryName => [queryName, `screen.${queryName}`];
+
+const getValidAssertion = (query, matcher) =>
+ allQueryUseInAssertion(query).map(query => ({
+ code: `expect(${query}('Hello'))${matcher}`,
+ }));
+
+const getInvalidAssertion = (query, matcher, messageId) =>
+ allQueryUseInAssertion(query).map(query => ({
+ code: `expect(${query}('Hello'))${matcher}`,
+ errors: [{ messageId }],
+ }));
+
+ruleTester.run('prefer-presence-queries', rule, {
+ valid: [
+ ...getByQueries.reduce(
+ (validRules, queryName) => [
+ ...validRules,
+ ...getValidAssertion(queryName, '.toBeInTheDocument()'),
+ ...getValidAssertion(queryName, '.toBeTruthy()'),
+ ...getValidAssertion(queryName, '.toBeDefined()'),
+ ...getValidAssertion(queryName, '.toBe("foo")'),
+ ...getValidAssertion(queryName, '.toEqual("World")'),
+ ...getValidAssertion(queryName, '.not.toBeFalsy()'),
+ ...getValidAssertion(queryName, '.not.toBeNull()'),
+ ...getValidAssertion(queryName, '.not.toBeDisabled()'),
+ ...getValidAssertion(queryName, '.not.toHaveClass("btn")'),
+ ],
+ []
+ ),
+ ...getAllByQueries.reduce(
+ (validRules, queryName) => [
+ ...validRules,
+ ...getValidAssertion(queryName, '.toBeInTheDocument()'),
+ ...getValidAssertion(queryName, '.toBeTruthy()'),
+ ...getValidAssertion(queryName, '.toBeDefined()'),
+ ...getValidAssertion(queryName, '.toBe("foo")'),
+ ...getValidAssertion(queryName, '.toEqual("World")'),
+ ...getValidAssertion(queryName, '.not.toBeFalsy()'),
+ ...getValidAssertion(queryName, '.not.toBeNull()'),
+ ...getValidAssertion(queryName, '.not.toBeDisabled()'),
+ ...getValidAssertion(queryName, '.not.toHaveClass("btn")'),
+ ],
+ []
+ ),
+ ...queryByQueries.reduce(
+ (validRules, queryName) => [
+ ...validRules,
+ ...getValidAssertion(queryName, '.toBeNull()'),
+ ...getValidAssertion(queryName, '.toBeFalsy()'),
+ ...getValidAssertion(queryName, '.not.toBeInTheDocument()'),
+ ...getValidAssertion(queryName, '.not.toBeTruthy()'),
+ ...getValidAssertion(queryName, '.not.toBeDefined()'),
+ ...getValidAssertion(queryName, '.toEqual("World")'),
+ ...getValidAssertion(queryName, '.not.toHaveClass("btn")'),
+ ],
+ []
+ ),
+ ...queryAllByQueries.reduce(
+ (validRules, queryName) => [
+ ...validRules,
+ ...getValidAssertion(queryName, '.toBeNull()'),
+ ...getValidAssertion(queryName, '.toBeFalsy()'),
+ ...getValidAssertion(queryName, '.not.toBeInTheDocument()'),
+ ...getValidAssertion(queryName, '.not.toBeTruthy()'),
+ ...getValidAssertion(queryName, '.not.toBeDefined()'),
+ ...getValidAssertion(queryName, '.toEqual("World")'),
+ ...getValidAssertion(queryName, '.not.toHaveClass("btn")'),
+ ],
+ []
+ ),
+ {
+ code: 'const el = getByText("button")',
+ },
+ {
+ code: 'const el = queryByText("button")',
+ },
+ {
+ code:
+ 'expect(getByNonTestingLibraryQuery("button")).not.toBeInTheDocument()',
+ },
+ {
+ code:
+ 'expect(queryByNonTestingLibraryQuery("button")).toBeInTheDocument()',
+ },
+ {
+ code: `async () => {
+ const el = await findByText('button')
+ expect(el).toBeInTheDocument()
+ }`,
+ },
+ // some weird examples after here to check guard against parent nodes
+ {
+ code: 'expect(getByText("button")).not()',
+ },
+ {
+ code: 'expect(queryByText("button")).not()',
+ },
+ ],
+ invalid: [
+ ...getByQueries.reduce(
+ (invalidRules, queryName) => [
+ ...invalidRules,
+ ...getInvalidAssertion(queryName, '.toBeNull()', 'absenceQuery'),
+ ...getInvalidAssertion(queryName, '.toBeFalsy()', 'absenceQuery'),
+ ...getInvalidAssertion(
+ queryName,
+ '.not.toBeInTheDocument()',
+ 'absenceQuery'
+ ),
+ ...getInvalidAssertion(queryName, '.not.toBeTruthy()', 'absenceQuery'),
+ ...getInvalidAssertion(queryName, '.not.toBeDefined()', 'absenceQuery'),
+ ],
+ []
+ ),
+ ...getAllByQueries.reduce(
+ (invalidRules, queryName) => [
+ ...invalidRules,
+ ...getInvalidAssertion(queryName, '.toBeNull()', 'absenceQuery'),
+ ...getInvalidAssertion(queryName, '.toBeFalsy()', 'absenceQuery'),
+ ...getInvalidAssertion(
+ queryName,
+ '.not.toBeInTheDocument()',
+ 'absenceQuery'
+ ),
+ ...getInvalidAssertion(queryName, '.not.toBeTruthy()', 'absenceQuery'),
+ ...getInvalidAssertion(queryName, '.not.toBeDefined()', 'absenceQuery'),
+ ],
+ []
+ ),
+ {
+ code: 'expect(screen.getAllByText("button")[1]).not.toBeInTheDocument()',
+ errors: [{ messageId: 'absenceQuery' }],
+ },
+ ...queryByQueries.reduce(
+ (validRules, queryName) => [
+ ...validRules,
+ ...getInvalidAssertion(queryName, '.toBeTruthy()', 'presenceQuery'),
+ ...getInvalidAssertion(queryName, '.toBeDefined()', 'presenceQuery'),
+ ...getInvalidAssertion(
+ queryName,
+ '.toBeInTheDocument()',
+ 'presenceQuery'
+ ),
+ ...getInvalidAssertion(queryName, '.not.toBeFalsy()', 'presenceQuery'),
+ ...getInvalidAssertion(queryName, '.not.toBeNull()', 'presenceQuery'),
+ ],
+ []
+ ),
+ ...queryAllByQueries.reduce(
+ (validRules, queryName) => [
+ ...validRules,
+ ...getInvalidAssertion(queryName, '.toBeTruthy()', 'presenceQuery'),
+ ...getInvalidAssertion(queryName, '.toBeDefined()', 'presenceQuery'),
+ ...getInvalidAssertion(
+ queryName,
+ '.toBeInTheDocument()',
+ 'presenceQuery'
+ ),
+ ...getInvalidAssertion(queryName, '.not.toBeFalsy()', 'presenceQuery'),
+ ...getInvalidAssertion(queryName, '.not.toBeNull()', 'presenceQuery'),
+ ],
+ []
+ ),
+ {
+ code: 'expect(screen.queryAllByText("button")[1]).toBeInTheDocument()',
+ errors: [{ messageId: 'presenceQuery' }],
+ },
+ ],
+});