Skip to content

Commit

Permalink
fix(prefer-presence-queries): ignore getBy* inside within for on …
Browse files Browse the repository at this point in the history
…absence queries (#740)

* fix: ignore getBy inside of within for prefer-presence-queries on absence queries

* docs: within treatment in prefer-presence-queries

---------

Co-authored-by: Mario Beltrán <belco90@gmail.com>
  • Loading branch information
nathanmmiller and Belco90 authored Aug 5, 2023
1 parent 8a6db01 commit b75dc73
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
4 changes: 3 additions & 1 deletion docs/rules/prefer-presence-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The (DOM) Testing Library allows to query DOM elements using different types of

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.
- `queryBy*` or `queryAllBy*` are used to assert element **is** present with `.toBeInTheDocument()`, `toBeTruthy()` or `.toBeDefined()` matchers or negated matchers from case below, or when used inside a `within()` clause.
- `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:
Expand All @@ -28,6 +28,7 @@ test('some test', () => {
expect(screen.queryByText('button')).not.toBeNull();
expect(screen.queryAllByText('button')[2]).not.toBeNull();
expect(screen.queryByText('button')).not.toBeFalsy();
...(within(screen.queryByText('button')))...

// check element is NOT present with `getBy*`
expect(screen.getByText('loading')).not.toBeInTheDocument();
Expand All @@ -50,6 +51,7 @@ test('some test', async () => {
expect(screen.getByText('button')).not.toBeNull();
expect(screen.getAllByText('button')[7]).not.toBeNull();
expect(screen.getByText('button')).not.toBeFalsy();
...(within(screen.getByText('button')))...

// check element is NOT present with `queryBy*`
expect(screen.queryByText('loading')).not.toBeInTheDocument();
Expand Down
14 changes: 12 additions & 2 deletions lib/rules/prefer-presence-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
return {
'CallExpression Identifier'(node: TSESTree.Identifier) {
const expectCallNode = findClosestCallNode(node, 'expect');
const withinCallNode = findClosestCallNode(node, 'within');

if (!expectCallNode || !isMemberExpression(expectCallNode.parent)) {
return;
Expand All @@ -79,9 +80,18 @@ export default createTestingLibraryRule<Options, MessageIds>({
return;
}

if (presence && isPresenceAssert && !isPresenceQuery) {
if (
presence &&
(withinCallNode || isPresenceAssert) &&
!isPresenceQuery
) {
context.report({ node, messageId: 'wrongPresenceQuery' });
} else if (absence && isAbsenceAssert && isPresenceQuery) {
} else if (
!withinCallNode &&
absence &&
isAbsenceAssert &&
isPresenceQuery
) {
context.report({ node, messageId: 'wrongAbsenceQuery' });
}
},
Expand Down
48 changes: 48 additions & 0 deletions tests/lib/rules/prefer-presence-queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,12 @@ ruleTester.run(RULE_NAME, rule, {
// right after clicking submit button it disappears
expect(submitButton).not.toBeInTheDocument()
`,
`// checking absence on getBy* inside a within with queryBy* outside the within
expect(within(screen.getByRole("button")).queryByText("Hello")).not.toBeInTheDocument()
`,
`// checking presence on getBy* inside a within with getBy* outside the within
expect(within(screen.getByRole("button")).getByText("Hello")).toBeInTheDocument()
`,
],
invalid: [
// cases: asserting absence incorrectly with `getBy*` queries
Expand Down Expand Up @@ -1199,5 +1205,47 @@ ruleTester.run(RULE_NAME, rule, {
`,
errors: [{ line: 4, column: 14, messageId: 'wrongAbsenceQuery' }],
},
{
code: `
// case: asserting within check does still work with improper outer clause
expect(within(screen.getByRole("button")).getByText("Hello")).not.toBeInTheDocument()`,
errors: [{ line: 3, column: 46, messageId: 'wrongAbsenceQuery' }],
},
{
code: `
// case: asserting within check does still work with improper outer clause
expect(within(screen.getByRole("button")).queryByText("Hello")).toBeInTheDocument()`,
errors: [{ line: 3, column: 46, messageId: 'wrongPresenceQuery' }],
},
{
code: `
// case: asserting within check does still work with improper outer clause and improper inner clause
expect(within(screen.queryByRole("button")).getByText("Hello")).not.toBeInTheDocument()`,
errors: [
{ line: 3, column: 25, messageId: 'wrongPresenceQuery' },
{ line: 3, column: 48, messageId: 'wrongAbsenceQuery' },
],
},
{
code: `
// case: asserting within check does still work with proper outer clause and improper inner clause
expect(within(screen.queryByRole("button")).queryByText("Hello")).not.toBeInTheDocument()`,
errors: [{ line: 3, column: 25, messageId: 'wrongPresenceQuery' }],
},
{
code: `
// case: asserting within check does still work with proper outer clause and improper inner clause
expect(within(screen.queryByRole("button")).getByText("Hello")).toBeInTheDocument()`,
errors: [{ line: 3, column: 25, messageId: 'wrongPresenceQuery' }],
},
{
code: `
// case: asserting within check does still work with improper outer clause and improper inner clause
expect(within(screen.queryByRole("button")).queryByText("Hello")).toBeInTheDocument()`,
errors: [
{ line: 3, column: 25, messageId: 'wrongPresenceQuery' },
{ line: 3, column: 48, messageId: 'wrongPresenceQuery' },
],
},
],
});

0 comments on commit b75dc73

Please sign in to comment.