Skip to content

Commit 0813194

Browse files
committed
fix: handle require, add tests
1 parent 2e469ba commit 0813194

File tree

4 files changed

+134
-43
lines changed

4 files changed

+134
-43
lines changed

lib/node-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function isRenderFunction(
5656
) {
5757
// returns true for `render` and e.g. `customRenderFn`
5858
// as well as `someLib.render` and `someUtils.customRenderFn`
59-
return ['render', ...renderFunctions].some(name => {
59+
return renderFunctions.some(name => {
6060
return (
6161
(isIdentifier(callNode.callee) && name === callNode.callee.name) ||
6262
(isMemberExpression(callNode.callee) &&

lib/rules/no-debug.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ function isRenderVariableDeclarator(
2222
if (isAwaitExpression(node.init)) {
2323
return (
2424
node.init.argument &&
25-
isRenderFunction(
26-
node.init.argument as TSESTree.CallExpression,
27-
renderFunctions
28-
)
25+
isRenderFunction(node.init.argument as TSESTree.CallExpression, [
26+
'render',
27+
...renderFunctions,
28+
])
2929
);
3030
} else {
3131
return (
3232
isCallExpression(node.init) &&
33-
isRenderFunction(node.init, renderFunctions)
33+
isRenderFunction(node.init, ['render', ...renderFunctions])
3434
);
3535
}
3636
}

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

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils';
22
import { getDocsUrl, TESTING_FRAMEWORK_SETUP_HOOKS } from '../utils';
33
import {
4+
isLiteral,
5+
isProperty,
46
isIdentifier,
7+
isObjectPattern,
58
isCallExpression,
69
isRenderFunction,
10+
isImportSpecifier,
711
} from '../node-utils';
812

913
export const RULE_NAME = 'no-render-in-setup';
@@ -69,7 +73,53 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({
6973
],
7074

7175
create(context, [{ renderFunctions, allowTestingFrameworkSetupHook }]) {
76+
let renderImportedFromTestingLib = false;
77+
let wildcardImportName: string | null = null;
78+
7279
return {
80+
// checks if import has shape:
81+
// import * as dtl from '@testing-library/dom';
82+
'ImportDeclaration[source.value=/testing-library/] ImportNamespaceSpecifier'(
83+
node: TSESTree.ImportNamespaceSpecifier
84+
) {
85+
wildcardImportName = node.local && node.local.name;
86+
},
87+
// checks if `render` is imported from a '@testing-library/foo'
88+
'ImportDeclaration[source.value=/testing-library/]'(
89+
node: TSESTree.ImportDeclaration
90+
) {
91+
renderImportedFromTestingLib = node.specifiers.some(specifier => {
92+
return (
93+
isImportSpecifier(specifier) && specifier.local.name === 'render'
94+
);
95+
});
96+
},
97+
[`VariableDeclarator > CallExpression > Identifier[name="require"]`](
98+
node: TSESTree.Identifier
99+
) {
100+
if (!isCallExpression(node.parent)) return;
101+
const { arguments: callExpressionArgs } = node.parent;
102+
const literalNodeScreenModuleName = callExpressionArgs.find(
103+
args =>
104+
isLiteral(args) &&
105+
typeof args.value === 'string' &&
106+
RegExp(/testing-library/, 'g').test(args.value)
107+
);
108+
if (!literalNodeScreenModuleName) {
109+
return;
110+
}
111+
const declaratorNode = node.parent
112+
.parent as TSESTree.VariableDeclarator;
113+
114+
renderImportedFromTestingLib =
115+
isObjectPattern(declaratorNode.id) &&
116+
declaratorNode.id.properties.some(
117+
property =>
118+
isProperty(property) &&
119+
isIdentifier(property.key) &&
120+
property.key.name === 'render'
121+
);
122+
},
73123
CallExpression(node) {
74124
let testingFrameworkSetupHooksToFilter = TESTING_FRAMEWORK_SETUP_HOOKS;
75125
if (allowTestingFrameworkSetupHook.length !== 0) {
@@ -81,7 +131,15 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({
81131
node,
82132
testingFrameworkSetupHooksToFilter
83133
);
84-
if (isRenderFunction(node, renderFunctions) && beforeHook) {
134+
// if `render` is imported from a @testing-library/foo or
135+
// imported with a wildcard, add `render` to the list of
136+
// disallowed render functions
137+
const disallowedRenderFns =
138+
renderImportedFromTestingLib || wildcardImportName
139+
? ['render', ...renderFunctions]
140+
: renderFunctions;
141+
142+
if (isRenderFunction(node, disallowedRenderFns) && beforeHook) {
85143
context.report({
86144
node,
87145
messageId: 'noRenderInSetup',

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

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ruleTester.run(RULE_NAME, rule, {
1212
valid: [
1313
{
1414
code: `
15+
import { render } from '@testing-library/foo';
1516
it('Test', () => {
1617
render(<Component/>)
1718
})
@@ -20,6 +21,7 @@ ruleTester.run(RULE_NAME, rule, {
2021
// test config options
2122
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
2223
code: `
24+
import { renderWithRedux } from '../test-utils';
2325
${setupHook}(() => {
2426
renderWithRedux(<Component/>)
2527
})
@@ -31,19 +33,44 @@ ruleTester.run(RULE_NAME, rule, {
3133
},
3234
],
3335
})),
34-
// // test usage of a non-Testing Library render fn
35-
// ...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
36-
// code: `import { render } from 'imNoTestingLibrary';
37-
38-
// ${setupHook}(() => {
39-
// render(<Component/>)
40-
// })`,
41-
// })),
36+
// test usage of a non-Testing Library render fn
37+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
38+
code: `
39+
import { render } from 'imNoTestingLibrary';
40+
${setupHook}(() => {
41+
render(<Component/>)
42+
})
43+
`,
44+
})),
45+
...TESTING_FRAMEWORK_SETUP_HOOKS.map(allowedSetupHook => {
46+
const [disallowedHook] = TESTING_FRAMEWORK_SETUP_HOOKS.filter(
47+
setupHook => setupHook !== allowedSetupHook
48+
);
49+
return {
50+
code: `
51+
import utils from 'imNoTestingLibrary';
52+
import { renderWithRedux } from '../test-utils';
53+
${allowedSetupHook}(() => {
54+
renderWithRedux(<Component/>)
55+
})
56+
${disallowedHook}(() => {
57+
utils.render(<Component/>)
58+
})
59+
`,
60+
options: [
61+
{
62+
allowTestingFrameworkSetupHook: allowedSetupHook,
63+
renderFunctions: ['renderWithRedux'],
64+
},
65+
],
66+
};
67+
}),
4268
],
4369

4470
invalid: [
4571
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
4672
code: `
73+
import { render } from '@testing-library/foo';
4774
${setupHook}(() => {
4875
render(<Component/>)
4976
})
@@ -56,6 +83,7 @@ ruleTester.run(RULE_NAME, rule, {
5683
})),
5784
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
5885
code: `
86+
import { render } from '@testing-library/foo';
5987
${setupHook}(function() {
6088
render(<Component/>)
6189
})
@@ -69,6 +97,7 @@ ruleTester.run(RULE_NAME, rule, {
6997
// custom render function
7098
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
7199
code: `
100+
import { renderWithRedux } from '../test-utils';
72101
${setupHook}(() => {
73102
renderWithRedux(<Component/>)
74103
})
@@ -87,6 +116,7 @@ ruleTester.run(RULE_NAME, rule, {
87116
// call render within a wrapper function
88117
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
89118
code: `
119+
import { render } from '@testing-library/foo';
90120
${setupHook}(() => {
91121
const wrapper = () => {
92122
render(<Component/>)
@@ -106,10 +136,11 @@ ruleTester.run(RULE_NAME, rule, {
106136
);
107137
return {
108138
code: `
109-
${disallowedHook}(() => {
110-
render(<Component/>)
111-
})
112-
`,
139+
import { render } from '@testing-library/foo';
140+
${disallowedHook}(() => {
141+
render(<Component/>)
142+
})
143+
`,
113144
options: [
114145
{
115146
allowTestingFrameworkSetupHook: allowedSetupHook,
@@ -123,46 +154,48 @@ ruleTester.run(RULE_NAME, rule, {
123154
};
124155
}),
125156
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
126-
code: `import { render } from '@testing-library/foo';
127-
128-
${setupHook}(() => {
129-
render(<Component/>)
130-
})`,
157+
code: `
158+
import * as testingLibrary from '@testing-library/foo';
159+
${setupHook}(() => {
160+
testingLibrary.render(<Component/>)
161+
})
162+
`,
131163
errors: [
132164
{
133165
messageId: 'noRenderInSetup',
134166
},
135167
],
136168
})),
137169
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
138-
code: `import * as testingLibrary from '@testing-library/foo';
139-
140-
${setupHook}(() => {
141-
testingLibrary.render(<Component/>)
142-
})`,
170+
code: `
171+
import { render } from 'imNoTestingLibrary';
172+
import * as testUtils from '../test-utils';
173+
${setupHook}(() => {
174+
testUtils.renderWithRedux(<Component/>)
175+
})
176+
it('Test', () => {
177+
render(<Component/>)
178+
})
179+
`,
180+
options: [
181+
{
182+
renderFunctions: ['renderWithRedux'],
183+
},
184+
],
143185
errors: [
144186
{
145187
messageId: 'noRenderInSetup',
146188
},
147189
],
148190
})),
149191
...TESTING_FRAMEWORK_SETUP_HOOKS.map(setupHook => ({
150-
code: `import { render } from 'imNoTestingLibrary';
151-
import * as testUtils from '../utils';
152-
153-
${setupHook}(() => {
154-
testUtils.renderWithRedux(<Component/>)
155-
})
192+
code: `
193+
const { render } = require('@testing-library/foo')
156194
157-
it('Test', () => {
158-
render(<Component/>)
159-
})
195+
${setupHook}(() => {
196+
render(<Component/>)
197+
})
160198
`,
161-
options: [
162-
{
163-
renderFunctions: ['renderWithRedux'],
164-
},
165-
],
166199
errors: [
167200
{
168201
messageId: 'noRenderInSetup',

0 commit comments

Comments
 (0)