feat(prefer-top-level-configure): add rule to disallow configure inside tests - #1371
feat(prefer-top-level-configure): add rule to disallow configure inside tests#1371gaurav-init wants to merge 2 commits into
Conversation
…de tests Calling configure() inside a test body sets global Testing Library options that persist across tests, creating implicit ordering dependencies and making individual tests harder to reason about. The rule warns when configure is called inside test, it, xit, xtest, or their .each/.only/.skip variants, and allows top-level calls and beforeAll/beforeEach hooks. Aliased imports (configure as tlConfigure) are handled via the imported specifier name. Closes testing-library#997.
There was a problem hiding this comment.
Pull request overview
Adds a new ESLint rule to prevent calling Testing Library’s global configure() from inside test bodies, avoiding cross-test side effects and order-dependent tests.
Changes:
- Introduces
testing-library/prefer-top-level-configurerule implementation and registers it in the plugin. - Adds rule tests and rule documentation.
- Enables the rule at
warnseverity across all framework configs and updates the README rules table.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/rules/prefer-top-level-configure.test.ts | Adds RuleTester coverage for valid/invalid configure() call locations. |
| src/rules/prefer-top-level-configure.ts | Implements the new rule and reports configure() calls found inside test bodies. |
| src/rules/index.ts | Registers the new rule in the exported rules map. |
| src/configs/angular.ts | Enables the rule at warn in the Angular preset. |
| src/configs/dom.ts | Enables the rule at warn in the DOM preset. |
| src/configs/react.ts | Enables the rule at warn in the React preset. |
| src/configs/vue.ts | Enables the rule at warn in the Vue preset. |
| src/configs/svelte.ts | Enables the rule at warn in the Svelte preset. |
| src/configs/marko.ts | Enables the rule at warn in the Marko preset. |
| README.md | Adds the new rule to the rules documentation table. |
| docs/rules/prefer-top-level-configure.md | Adds end-user documentation and examples for the new rule. |
| // test.each([1, 2])('title', fn) — curried form | ||
| // callee is the result of test.each([...]), i.e. a CallExpression whose | ||
| // own callee is the MemberExpression test.each / it.each | ||
| if ( | ||
| isCallExpression(callee) && | ||
| isMemberExpression(callee.callee) && | ||
| ASTUtils.isIdentifier(callee.callee.object) && | ||
| TEST_FUNCTION_NAMES.has(callee.callee.object.name) | ||
| ) { | ||
| return true; | ||
| } |
| import { configure, cleanup } from '@testing-library/react'; | ||
|
|
||
| let previousConfig; | ||
|
|
||
| beforeAll(() => { | ||
| // OK: inside a setup hook | ||
| previousConfig = configure({ asyncUtilTimeout: 5000 }); | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| configure(previousConfig); | ||
| }); |
| const SUPPORTED_TESTING_FRAMEWORKS = [ | ||
| '@testing-library/dom', | ||
| '@testing-library/angular', | ||
| '@testing-library/react', | ||
| '@testing-library/vue', | ||
| '@marko/testing-library', | ||
| ]; |
…d Svelte to test matrix Extend isInsideTestBody to walk the full MemberExpression/CallExpression chain leftward to the root identifier, so test.only.each([…])(…) and other chained variants are correctly detected in addition to test.each and test.only. Add @testing-library/svelte to the rule test matrix.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (5)
docs/rules/prefer-top-level-configure.md:58
- The example implies
configure()returns the previous config (previousConfig = configure(...)), butconfigure()returnsvoidin Testing Library. The snippet should usegetConfig()to capture the current config before overriding it (and thecleanupimport is unused).
import { configure, cleanup } from '@testing-library/react';
let previousConfig;
beforeAll(() => {
src/rules/prefer-top-level-configure.ts:111
- The rule currently only matches
configurewhen it’s a named ES import (ImportSpecifier). Ifconfigureis called via a Testing Library namespace import (import * as rtl from ...; rtl.configure(...)) or CommonJS require default (const rtl = require(...); rtl.configure(...)),findImportedTestingLibraryUtilSpecifier('configure')returns a non-ImportSpecifierand the rule silently skips reporting.
// Resolve the import specifier for this local identifier name and
// verify the original exported name is 'configure'.
// This handles both direct imports and aliased imports:
// import { configure } from '@testing-library/react'
// import { configure as tlConfigure } from '@testing-library/react'
tests/rules/prefer-top-level-configure.test.ts:94
- Test coverage doesn’t currently include calling
configurevia a Testing Library namespace import (import * as rtl) or CommonJS require (const rtl = require(...)). Since the rule is intended to warn wheneverconfigurecomes from a TL package, add invalid test cases for these import styles (and optionallyconst { configure } = require(...)).
// configure inside test.each
{
code: `
import { configure } from '@testing-library/react';
test.each([1, 2])('fails %i', () => {
docs/rules/prefer-top-level-configure.md:5
- The auto-generated rule header is missing the line that indicates which configs enable this rule (e.g. “
⚠️ This rule warns in the following configs …”), even though the file includes the<!-- end auto-generated rule header -->marker. This usually meanspnpm run generate:docswasn’t applied to this new rule doc output.
This issue also appears on line 54 of the same file.
# testing-library/prefer-top-level-configure
📝 Disallow calling `configure` inside test functions to avoid cross-test side effects.
<!-- end auto-generated rule header -->
README.md:359
- In the rules table,
prefer-top-level-configureis missing the config badges in the⚠️ (warn) column even though the rule is configured aswarnin all framework configs in this PR. This points to generated README output being out of sync.
| [prefer-top-level-configure](docs/rules/prefer-top-level-configure.md) | Disallow calling `configure` inside test functions to avoid cross-test side effects | | | |
|
Hey @Belco90 — would love your feedback when you get a chance! This implements the rule you confirmed in the issue. Happy to adjust anything. |
Summary
Closes #997.
Calling
configure()inside a test body sets global Testing Library options that persist for the rest of the test run. Because there is no automatic reset between tests, this creates implicit ordering dependencies — test results start to depend on which tests ran before them, and each test becomes harder to reason about in isolation.What the rule does
Warns when
configureimported from a Testing Library package is called inside atest,it,xit,xtest, or any of their.each/.only/.skipvariants. Top-level calls andbeforeAll/beforeEachhooks are allowed.Invalid:
Valid:
Details
import { configure as tlConfigure }) are correctly handled via the imported specifier name.warnseverity in all framework configs (dom,angular,react,vue,svelte,marko) — matching the issue's "warns about a potential error" category.beforeAll/beforeEachhooks, non-TL imports, aliased imports,test.each/it.each(curried form),test.only/it.skip, nested helper functions inside tests,xit/xtest.generate:docsandgenerate:configswere run; README and all config files updated automatically.