New Rule Proposal: no-unsettled-absence-query
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 35/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- eslint, javascript, typescript
- Ambito
- testing-qa, tooling
Direzione di ricerca
Non vengono indicati file del repository, test o punti di ingresso. Inizia esaminando le regole Testing Library ESLint esistenti e i relativi test, quindi confronta gli esempi proposti che falliscono e che passano. Il lavoro è completo quando il nuovo avviso rileva in modo coerente le asserzioni di assenza non stabilizzate, consentendo al contempo le espressioni di stabilizzazione documentate.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Name for new rule
no-unsettled-absence-query
Description of the new rule
Asserting absence with queryBy* + .not.toBeInTheDocument() immediately after render(), before the component has settled, can produce a false positive. The element isn't there yet, not because it won't be there. The community calls this Testing Ghosts.
Testing Library's render() does flush the initial synchronous render (it's wrapped in act() internally), so for a plain component with no async behavior, checking absence right after render() is technically correct. The problem shows up when the component has async side effects like data fetching, lazy loading, state updates from useEffect, etc. In those cases the UI hasn't finished updating, and the absence check passes vacuously. Since the rule can't know whether a component has async behavior, it flags all unsettled absence assertions and lets the developer confirm intent by adding a settling expression.
This rule would enforce that an absence assertion (expect(screen.queryBy*(...)).not.toBeInTheDocument() / .not.toBeVisible()) must be preceded by a "settling" expression within the test body.
Why existing rules don't cover this:
| Existing Rule | Why it doesn't solve this |
|---|---|
prefer-presence-queries |
Enforces correct query type, but doesn't track timing relative to render |
no-await-sync-queries |
Only flags await directly on sync queries, not timing of assertions |
await-async-queries / await-async-utils |
About promise handling, not assertion ordering |
prefer-find-by |
Simplifies waitFor + getBy -> findBy, doesn't detect premature absence checks |
Related issues: #269, #411, #518 - all adjacent but none address this specific pattern.
Testing Library feature
This rule relates to the absence/presence assertion pattern described in the Appearance and Disappearance guide. Specifically, it enforces correct ordering of queryBy* absence assertions relative to async settling utilities (findBy*, waitFor, act) and sync settling queries (getBy*).
Testing Library framework(s)
All frameworks: React, Angular, Vue, Svelte, etc. The pattern applies anywhere screen.queryBy* + .not.toBeInTheDocument() is used.
What category of rule is this?
Warns about a potential error. Less valuable as an error given the call-out above of vanilla components.
Code examples
Fail:
// Absence assertion before component has settled, "Testing a Ghost"
test('shows no error', () => {
render(<AsyncComponent />);
expect(screen.queryByText('error')).not.toBeInTheDocument();
});
// Absence assertion BEFORE the awai, order matters
test('shows no error', async () => {
render(<AsyncComponent />);
expect(screen.queryByText('error')).not.toBeInTheDocument();
await screen.findByText('loaded');
});
// queryAllBy variant
test('shows no alerts', () => {
render(<AsyncComponent />);
expect(screen.queryAllByRole('alert')).not.toBeInTheDocument();
});
// Absence assertion inside waitFor - passes on first retry, still a ghost
test('shows no error', async () => {
render(<AsyncComponent />);
await waitFor(() => {
expect(screen.queryByText('error')).not.toBeInTheDocument();
});
});
Pass:
// findBy* settles the component first
test('shows no error', async () => {
render(<AsyncComponent />);
await screen.findByText('loaded');
expect(screen.queryByText('error')).not.toBeInTheDocument();
});
// waitFor settles the component first
test('shows no error', async () => {
render(<AsyncComponent />);
await waitFor(() => expect(something).toBe(true));
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
});
// act settles the component first
test('shows no error', async () => {
await act(() => render(<AsyncComponent />));
expect(screen.queryByText('error')).not.toBeInTheDocument();
});
// getBy* proves sync render completed
test('shows no error', () => {
render(<Component />);
screen.getByText('visible heading');
expect(screen.queryByText('error')).not.toBeInTheDocument();
});
What counts as "settled":
- Any
awaitexpression on a preceding statement — coversfindBy*,waitFor,act, custom async helpers - A
screen.getBy*/screen.getAllBy*call — proves the sync render produced expected output
Matchers checked: .not.toBeInTheDocument(), .not.toBeVisible()
Anything else?
References:
- Testing Library: Appearance and Disappearance guide
- Kent C. Dodds: Common mistakes with React Testing Library
- Gerardo Perrucci: Stop Testing Ghosts
- react-testing-library #865 (vague similarity; in the same realm with component mounting/unmounting states)
Do you want to submit a pull request to make the new rule?
I have a working implementation with full test coverage that I'm happy to submit as a PR if this proposal is accepted.
- Lingua principale
- TypeScript
- Stelle
- 1.1k
- Fork
- 167
- Merge medio
- 16h 56m
- PR unite (30g)
- 5
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di testing-library/eslint-plugin-testing-library
-
bug triage
Difficoltà 3/5 Mezza giornata Idoneità per principianti 58/100
testing-library/eslint-plugin-testing-library#1401 ·
I maintainer di solito rispondono entro 1 giorno
-
bug triage
Difficoltà 3/5 1-2 giorni Idoneità per principianti 76/100
testing-library/eslint-plugin-testing-library#1370 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Rules don’t recognize @testing-library/angular/zoneless importsForse già presa @ThiloAschebrock l’ha presa 84 giorni fa. Apertabug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 70/100
testing-library/eslint-plugin-testing-library#1366 ·
I maintainer di solito rispondono entro 1 giorno
-
prefer-presence-queries autofix leaves queryBy* in destructure while rewriting call site to getBy*, producing a ReferenceErrorForse già presa @j1ang3 l’ha presa 90 giorni fa. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
testing-library/eslint-plugin-testing-library#1359 ·
I maintainer di solito rispondono entro 1 giorno
-
Dependency DashboardApertapinned
Difficoltà 5/5 Più di una settimana Idoneità per principianti 10/100
testing-library/eslint-plugin-testing-library#1140 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di testing-library/eslint-plugin-testing-library
Issue simili
-
[Android] Nested transforms are composed in reverse orderForse già presa @Danyalo l’ha presa oggi. ApertaRepro provided
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
software-mansion/react-native-svg#3090 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
radix-ui/primitives#4228 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 95/100
rocketride-org/rocketride-server#2705 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
archon-ready archon-size-s archon-type-bug area: web
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
coleam00/Archon#4707 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
HarperFast/grafana-datasource#177 ·