Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

New Rule Proposal: no-unsettled-absence-query

未关闭
#1,237 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

@JMiltier 已经在做这个了。

开始于 2026年3月31日。

  • #1278 来自 @JMiltier —— 未关闭

评估

难度
5/5
预计耗时
一周以上
新手友好度
35/100
Issue 类型
功能
描述清晰度
基本清楚
活跃度
冷清
技术栈
eslint, javascript, typescript

调研方向

未指定任何仓库文件、测试或入口点。首先检查现有的 Testing Library ESLint 规则及其测试,然后比较所提议的失败示例和通过示例。完成的标准是:新警告能够一致地检测出尚未稳定的缺失断言,同时允许文档中说明的稳定表达式。

由索引模型根据 Issue 内容生成。

描述

enhancement
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":

  1. Any await expression on a preceding statement — covers findBy*, waitFor, act, custom async helpers
  2. A screen.getBy* / screen.getAllBy* call — proves the sync render produced expected output

Matchers checked: .not.toBeInTheDocument(), .not.toBeVisible()

Anything else?

References:

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.

主要语言
TypeScript
星标
1.1k
派生
167
平均合并
19 小时 58 分钟
30 天内合并 PR
2

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

testing-library/eslint-plugin-testing-library 的其他 Issue

查看 testing-library/eslint-plugin-testing-library 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。