testing-library / testing-library/eslint-plugin-testing-library

prefer-presence-queries autofix leaves queryBy* in destructure while rewriting call site to getBy*, producing a ReferenceError

Open
#1,359 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
1.1k
Forks
168
Avg merge
15h 44m
Merged PRs (30d)
14

Description

Describe the bug

The prefer-presence-queries autofix (introduced in v7.4.0, #1020) only rewrites the Identifier at the call site inside expect(), but does not update the corresponding ObjectPattern destructure binding. This produces syntactically valid but broken code — getByRole is called but was never declared, causing a ReferenceError at runtime.

Minimal reproducible example

// broken.test.js — BEFORE eslint --fix
it('example', () => {
  const { queryByRole } = render(something());
  expect(queryByRole('alert')).toBeInTheDocument(); // ← fires prefer-presence-queries
});
// broken.test.js — AFTER eslint --fix
it('example', () => {
  const { queryByRole } = render(something()); // ← destructure untouched, now unused
  expect(getByRole('alert')).toBeInTheDocument(); // ← getByRole is NOT IN SCOPE → ReferenceError
});

Steps to reproduce

mkdir repro && cd repro
npm init -y
npm install eslint eslint-plugin-testing-library
// eslint.config.mjs
import testingLibrary from 'eslint-plugin-testing-library';
export default [{
  files: ['**/*.test.js'],
  plugins: { 'testing-library': testingLibrary },
  rules: { 'testing-library/prefer-presence-queries': 'error' },
}];
// broken.test.js
it('example', () => {
  const { queryByRole } = render(something());
  expect(queryByRole('alert')).toBeInTheDocument();
});
npx eslint --fix broken.test.js
cat broken.test.js

Output after fix:

it('example', () => {
  const { queryByRole } = render(something()); // ← not updated
  expect(getByRole('alert')).toBeInTheDocument(); // ← not in scope → ReferenceError at runtime
});

Expected behavior

The fix should update both the destructure binding and the call site:

it('example', () => {
  const { getByRole } = render(something()); // ← updated
  expect(getByRole('alert')).toBeInTheDocument(); // ← correct
});

Versions

  • eslint-plugin-testing-library: 7.16.2 (first appeared in 7.4.0 when autofix was added via #1020)
  • eslint: 9.x
  • Node: 22.x

Root cause

The fixer targets the Identifier node matched by the "CallExpression Identifier" selector — the identifier at the call site inside expect(). It does not walk up the scope to find and update the ObjectPattern in the VariableDeclarator that originally destructured the same query from the render result.

Related

  • #916 — the issue that requested autofixability (the resulting fix is incomplete)
  • #1020 — the PR that implemented the autofix

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Run the minimal reproduction with eslint --fix to observe the call-site rewrite. Inspect the prefer-presence-queries autofix, especially the “CallExpression Identifier” selector and the related ObjectPattern in the VariableDeclarator; done means both the destructure binding and call site use getByRole without producing a ReferenceError.

Written by the indexing model from the issue text.

Assessment

Tech stack
eslint, javascript, typescript
Domain
testing-qa, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.