Skip to content

Commit 966f521

Browse files
committed
refactor: address review comments
1 parent 3fb5364 commit 966f521

File tree

4 files changed

+89
-20
lines changed

4 files changed

+89
-20
lines changed

docs/rules/no-render-in-setup.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Rule Details
44

5-
This rule disallows the usage of `render` in setup functions (`beforeEach` or `beforeAll`) in favor of a single test with multiple assertions.
5+
This rule disallows the usage of `render` in setup functions (`beforeEach` and `beforeAll`) in favor of moving `render` closer to test assertions.
66

77
Examples of **incorrect** code for this rule:
88

@@ -38,5 +38,11 @@ it('Should have foo, bar and baz', () => {
3838
If you use [custom render functions](https://testing-library.com/docs/example-react-redux) then you can set a config option in your `.eslintrc` to look for these.
3939

4040
```
41-
"testing-library/no-render-in-setup": ["error", {"renderFunctions":["renderWithRedux", "renderWithRouter"]}],
41+
"testing-library/no-render-in-setup": ["error", {"renderFunctions": ["renderWithRedux", "renderWithRouter"]}],
42+
```
43+
44+
If you would would like to allow the use of `render` (or custom render function) in _either_ `beforeAll` or `beforeEach`, this can be configured using the option `allowTestingFrameworkSetupHook`. This may be useful if you have configured your tests to [skip auto cleanup](https://testing-library.com/docs/react-testing-library/setup#skipping-auto-cleanup). `allowTestingFrameworkSetupHook` is an enum that accepts either `"beforeAll"` or `"beforeEach"`.
45+
46+
```
47+
"testing-library/no-render-in-setup": ["error", {"allowTestingFrameworkSetupHook": "beforeAll"}],
4248
```

lib/rules/no-render-in-setup.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils';
2-
import { getDocsUrl, BEFORE_HOOKS } from '../utils';
2+
import { getDocsUrl, TESTING_FRAMEWORK_SETUP_HOOKS } from '../utils';
33
import {
44
isIdentifier,
55
isCallExpression,
@@ -10,18 +10,19 @@ export const RULE_NAME = 'no-render-in-setup';
1010
export type MessageIds = 'noRenderInSetup';
1111

1212
export function findClosestBeforeHook(
13-
node: TSESTree.Node
13+
node: TSESTree.Node,
14+
testingFrameworkSetupHooksToFilter: string[]
1415
): TSESTree.Identifier | null {
1516
if (node === null) return null;
1617
if (
1718
isCallExpression(node) &&
1819
isIdentifier(node.callee) &&
19-
BEFORE_HOOKS.includes(node.callee.name)
20+
testingFrameworkSetupHooksToFilter.includes(node.callee.name)
2021
) {
2122
return node.callee;
2223
}
2324

24-
return findClosestBeforeHook(node.parent);
25+
return findClosestBeforeHook(node.parent, testingFrameworkSetupHooksToFilter);
2526
}
2627

2728
export default ESLintUtils.RuleCreator(getDocsUrl)({
@@ -35,7 +36,7 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({
3536
},
3637
messages: {
3738
noRenderInSetup:
38-
'Combine assertions into a single test instead of re-rendering the component.',
39+
'Move `render` out of `{{name}}` and into individual tests.',
3940
},
4041
fixable: null,
4142
schema: [
@@ -45,20 +46,41 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({
4546
renderFunctions: {
4647
type: 'array',
4748
},
49+
allowTestingFrameworkSetupHook: {
50+
enum: TESTING_FRAMEWORK_SETUP_HOOKS,
51+
},
4852
},
53+
anyOf: [
54+
{
55+
required: ['renderFunctions'],
56+
},
57+
{
58+
required: ['allowTestingFrameworkSetupHook'],
59+
},
60+
],
4961
},
5062
],
5163
},
5264
defaultOptions: [
5365
{
5466
renderFunctions: [],
67+
allowTestingFrameworkSetupHook: '',
5568
},
5669
],
5770

58-
create(context, [{ renderFunctions }]) {
71+
create(context, [{ renderFunctions, allowTestingFrameworkSetupHook }]) {
5972
return {
6073
CallExpression(node) {
61-
const beforeHook = findClosestBeforeHook(node);
74+
let testingFrameworkSetupHooksToFilter = TESTING_FRAMEWORK_SETUP_HOOKS;
75+
if (allowTestingFrameworkSetupHook.length !== 0) {
76+
testingFrameworkSetupHooksToFilter = TESTING_FRAMEWORK_SETUP_HOOKS.filter(
77+
hook => hook !== allowTestingFrameworkSetupHook
78+
);
79+
}
80+
const beforeHook = findClosestBeforeHook(
81+
node,
82+
testingFrameworkSetupHooksToFilter
83+
);
6284
if (isRenderFunction(node, renderFunctions) && beforeHook) {
6385
context.report({
6486
node,

lib/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const ASYNC_UTILS = [
6363
'waitForDomChange',
6464
];
6565

66-
const BEFORE_HOOKS = ['beforeEach', 'beforeAll'];
66+
const TESTING_FRAMEWORK_SETUP_HOOKS = ['beforeEach', 'beforeAll'];
6767

6868
export {
6969
getDocsUrl,
@@ -75,6 +75,6 @@ export {
7575
ASYNC_QUERIES_COMBINATIONS,
7676
ALL_QUERIES_COMBINATIONS,
7777
ASYNC_UTILS,
78-
BEFORE_HOOKS,
78+
TESTING_FRAMEWORK_SETUP_HOOKS,
7979
LIBRARY_MODULES,
8080
};

tests/lib/rules/no-render-in-setup.test.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createRuleTester } from '../test-utils';
2-
import { BEFORE_HOOKS } from '../../../lib/utils';
2+
import { TESTING_FRAMEWORK_SETUP_HOOKS } from '../../../lib/utils';
33
import rule, { RULE_NAME } from '../../../lib/rules/no-render-in-setup';
44

55
const ruleTester = createRuleTester({
@@ -17,12 +17,28 @@ ruleTester.run(RULE_NAME, rule, {
1717
})
1818
`,
1919
},
20+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
21+
code: `
22+
${setupHook}(() => {
23+
const wrapper = () => {
24+
renderWithRedux(<Component/>)
25+
}
26+
wrapper();
27+
})
28+
`,
29+
options: [
30+
{
31+
allowTestingFrameworkSetupHook: setupHook,
32+
renderFunctions: ['renderWithRedux'],
33+
},
34+
],
35+
})),
2036
],
2137

2238
invalid: [
23-
...BEFORE_HOOKS.map(beforeHook => ({
39+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
2440
code: `
25-
${beforeHook}(() => {
41+
${setupHook}(() => {
2642
render(<Component/>)
2743
})
2844
`,
@@ -32,9 +48,9 @@ ruleTester.run(RULE_NAME, rule, {
3248
},
3349
],
3450
})),
35-
...BEFORE_HOOKS.map(beforeHook => ({
51+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
3652
code: `
37-
${beforeHook}(function() {
53+
${setupHook}(function() {
3854
render(<Component/>)
3955
})
4056
`,
@@ -45,9 +61,9 @@ ruleTester.run(RULE_NAME, rule, {
4561
],
4662
})),
4763
// custom render function
48-
...BEFORE_HOOKS.map(beforeHook => ({
64+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
4965
code: `
50-
${beforeHook}(() => {
66+
${setupHook}(() => {
5167
renderWithRedux(<Component/>)
5268
})
5369
`,
@@ -63,9 +79,9 @@ ruleTester.run(RULE_NAME, rule, {
6379
],
6480
})),
6581
// call render within a wrapper function
66-
...BEFORE_HOOKS.map(beforeHook => ({
82+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
6783
code: `
68-
${beforeHook}(() => {
84+
${setupHook}(() => {
6985
const wrapper = () => {
7086
render(<Component/>)
7187
}
@@ -78,5 +94,30 @@ ruleTester.run(RULE_NAME, rule, {
7894
},
7995
],
8096
})),
97+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(allowedSetupHook => {
98+
const [disallowedHook] = TESTING_FRAMEWORK_SETUP_HOOKS.filter(
99+
setupHook => setupHook !== allowedSetupHook
100+
);
101+
return {
102+
code: `
103+
${disallowedHook}(() => {
104+
const wrapper = () => {
105+
render(<Component/>)
106+
}
107+
wrapper();
108+
})
109+
`,
110+
options: [
111+
{
112+
allowTestingFrameworkSetupHook: allowedSetupHook,
113+
},
114+
],
115+
errors: [
116+
{
117+
messageId: 'noRenderInSetup',
118+
},
119+
],
120+
};
121+
}),
81122
],
82123
});

0 commit comments

Comments
 (0)