Skip to content

Commit 6abb4ee

Browse files
authored
feat: new setting for reported filename pattern (#244)
* feat: new setting for customizing file name pattern to report * test: add custom rule tester for testing library * refactor: use common rule tester config
1 parent 63c9d7b commit 6abb4ee

13 files changed

+99
-61
lines changed

lib/detect-testing-library-utils.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
22

33
export type TestingLibrarySettings = {
44
'testing-library/module'?: string;
5+
'testing-library/file-name'?: string;
56
};
67

78
export type TestingLibraryContext<
@@ -25,9 +26,12 @@ export type EnhancedRuleCreate<
2526

2627
export type DetectionHelpers = {
2728
getIsTestingLibraryImported: () => boolean;
29+
getIsValidFileName: () => boolean;
2830
canReportErrors: () => boolean;
2931
};
3032

33+
const DEFAULT_FILE_NAME_PATTERN = '^.*\\.(test|spec)\\.[jt]sx?$';
34+
3135
/**
3236
* Enhances a given rule `create` with helpers to detect Testing Library utils.
3337
*/
@@ -45,6 +49,9 @@ export function detectTestingLibraryUtils<
4549

4650
// Init options based on shared ESLint settings
4751
const customModule = context.settings['testing-library/module'];
52+
const fileNamePattern =
53+
context.settings['testing-library/file-name'] ??
54+
DEFAULT_FILE_NAME_PATTERN;
4855

4956
// Helpers for Testing Library detection.
5057
const helpers: DetectionHelpers = {
@@ -68,11 +75,21 @@ export function detectTestingLibraryUtils<
6875
return isImportingTestingLibraryModule || isImportingCustomModule;
6976
},
7077

78+
/**
79+
* Gets if name of the file being analyzed is valid or not.
80+
*
81+
* This is based on "testing-library/file-name" setting.
82+
*/
83+
getIsValidFileName() {
84+
const fileName = context.getFilename();
85+
return !!fileName.match(fileNamePattern);
86+
},
87+
7188
/**
7289
* Wraps all conditions that must be met to report rules.
7390
*/
7491
canReportErrors() {
75-
return this.getIsTestingLibraryImported();
92+
return this.getIsTestingLibraryImported() && this.getIsValidFileName();
7693
},
7794
};
7895

tests/create-testing-library-rule.test.ts

+29-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createRuleTester } from './lib/test-utils';
22
import rule, { RULE_NAME } from './fake-rule';
33

4-
const ruleTester = createRuleTester({
5-
ecmaFeatures: {
6-
jsx: true,
7-
},
8-
});
4+
const ruleTester = createRuleTester();
95

106
ruleTester.run(RULE_NAME, rule, {
117
valid: [
@@ -38,6 +34,15 @@ ruleTester.run(RULE_NAME, rule, {
3834
'testing-library/module': 'test-utils',
3935
},
4036
},
37+
{
38+
code: `
39+
// case: import module forced to be reported but not matching file name
40+
import { foo } from 'report-me'
41+
`,
42+
settings: {
43+
'testing-library/file-name': 'testing-library\\.js',
44+
},
45+
},
4146
],
4247
invalid: [
4348
{
@@ -47,6 +52,25 @@ ruleTester.run(RULE_NAME, rule, {
4752
`,
4853
errors: [{ line: 3, column: 7, messageId: 'fakeError' }],
4954
},
55+
{
56+
filename: 'MyComponent.spec.js',
57+
code: `
58+
// case: import module forced to be reported but from .spec.js named file
59+
import { foo } from 'report-me'
60+
`,
61+
errors: [{ line: 3, column: 7, messageId: 'fakeError' }],
62+
},
63+
{
64+
filename: 'MyComponent.testing-library.js',
65+
code: `
66+
// case: import module forced to be reported with custom file name
67+
import { foo } from 'report-me'
68+
`,
69+
settings: {
70+
'testing-library/file-name': 'testing-library\\.js',
71+
},
72+
errors: [{ line: 3, column: 7, messageId: 'fakeError' }],
73+
},
5074
{
5175
code: `
5276
// case: render imported from any module by default (aggressive reporting)

tests/lib/rules/consistent-data-testid.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createRuleTester } from '../test-utils';
22
import rule, { RULE_NAME } from '../../../lib/rules/consistent-data-testid';
33

4-
const ruleTester = createRuleTester({
5-
ecmaFeatures: {
6-
jsx: true,
7-
},
8-
});
4+
const ruleTester = createRuleTester();
95

106
ruleTester.run(RULE_NAME, rule, {
117
valid: [

tests/lib/rules/no-container.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createRuleTester } from '../test-utils';
22
import rule, { RULE_NAME } from '../../../lib/rules/no-container';
33

4-
const ruleTester = createRuleTester({
5-
ecmaFeatures: {
6-
jsx: true,
7-
},
8-
});
4+
const ruleTester = createRuleTester();
95

106
ruleTester.run(RULE_NAME, rule, {
117
valid: [

tests/lib/rules/no-debug.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createRuleTester } from '../test-utils';
22
import rule, { RULE_NAME } from '../../../lib/rules/no-debug';
33

4-
const ruleTester = createRuleTester({
5-
ecmaFeatures: {
6-
jsx: true,
7-
},
8-
});
4+
const ruleTester = createRuleTester();
95

106
ruleTester.run(RULE_NAME, rule, {
117
valid: [

tests/lib/rules/no-multiple-assertions-wait-for.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import rule, {
33
RULE_NAME,
44
} from '../../../lib/rules/no-multiple-assertions-wait-for';
55

6-
const ruleTester = createRuleTester({
7-
ecmaFeatures: {
8-
jsx: true,
9-
},
10-
});
6+
const ruleTester = createRuleTester();
117

128
ruleTester.run(RULE_NAME, rule, {
139
valid: [

tests/lib/rules/no-node-access.test.ts

+6-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createRuleTester } from '../test-utils';
22
import rule, { RULE_NAME } from '../../../lib/rules/no-node-access';
33

4-
const ruleTester = createRuleTester({
5-
ecmaFeatures: {
6-
jsx: true,
7-
},
8-
});
4+
const ruleTester = createRuleTester();
95

106
ruleTester.run(RULE_NAME, rule, {
117
valid: [
@@ -51,18 +47,16 @@ ruleTester.run(RULE_NAME, rule, {
5147
within(signinModal).getByPlaceholderText('Username');
5248
`,
5349
},
54-
/*{
55-
// TODO: this one should be valid indeed. Rule implementation must be improved
56-
// to track where the nodes are coming from. This one wasn't reported before
57-
// just because this code is not importing TL module, but that's a really
58-
// brittle check. Instead, this one shouldn't be reported since `children`
59-
// it's just a property not related to a node
50+
{
6051
code: `
6152
const Component = props => {
6253
return <div>{props.children}</div>
6354
}
6455
`,
65-
},*/
56+
settings: {
57+
'testing-library/file-name': 'testing-library\\.js',
58+
},
59+
},
6660
{
6761
code: `
6862
// case: importing custom module

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ import { createRuleTester } from '../test-utils';
22
import { TESTING_FRAMEWORK_SETUP_HOOKS } from '../../../lib/utils';
33
import rule, { RULE_NAME } from '../../../lib/rules/no-render-in-setup';
44

5-
const ruleTester = createRuleTester({
6-
ecmaFeatures: {
7-
jsx: true,
8-
},
9-
});
5+
const ruleTester = createRuleTester();
106

117
ruleTester.run(RULE_NAME, rule, {
128
valid: [

tests/lib/rules/no-side-effects-wait-for.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { createRuleTester } from '../test-utils';
22
import rule, { RULE_NAME } from '../../../lib/rules/no-side-effects-wait-for';
33

4-
const ruleTester = createRuleTester({
5-
ecmaFeatures: {
6-
jsx: true,
7-
},
8-
});
4+
const ruleTester = createRuleTester();
95

106
ruleTester.run(RULE_NAME, rule, {
117
valid: [

tests/lib/rules/prefer-find-by.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ import rule, {
1414
MessageIds,
1515
} from '../../../lib/rules/prefer-find-by';
1616

17-
const ruleTester = createRuleTester({
18-
ecmaFeatures: {
19-
jsx: true,
20-
},
21-
});
17+
const ruleTester = createRuleTester();
2218

2319
function buildFindByMethod(queryMethod: string) {
2420
return `${getFindByQueryVariant(queryMethod)}${queryMethod.split('By')[1]}`;

tests/lib/rules/render-result-naming-convention.test.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import rule, {
33
RULE_NAME,
44
} from '../../../lib/rules/render-result-naming-convention';
55

6-
const ruleTester = createRuleTester({
7-
ecmaFeatures: {
8-
jsx: true,
9-
},
10-
});
6+
const ruleTester = createRuleTester();
117

128
ruleTester.run(RULE_NAME, rule, {
139
valid: [

tests/lib/test-utils.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,49 @@
11
import { resolve } from 'path';
22
import { TSESLint } from '@typescript-eslint/experimental-utils';
33

4+
const DEFAULT_TEST_CASE_CONFIG = {
5+
filename: 'MyComponent.test.js',
6+
};
7+
8+
class TestingLibraryRuleTester extends TSESLint.RuleTester {
9+
run<TMessageIds extends string, TOptions extends Readonly<unknown[]>>(
10+
ruleName: string,
11+
rule: TSESLint.RuleModule<TMessageIds, TOptions>,
12+
tests: TSESLint.RunTests<TMessageIds, TOptions>
13+
): void {
14+
const { valid, invalid } = tests;
15+
16+
const finalValid = valid.map((testCase) => {
17+
if (typeof testCase === 'string') {
18+
return {
19+
...DEFAULT_TEST_CASE_CONFIG,
20+
code: testCase,
21+
};
22+
}
23+
24+
return { ...DEFAULT_TEST_CASE_CONFIG, ...testCase };
25+
});
26+
const finalInvalid = invalid.map((testCase) => ({
27+
...DEFAULT_TEST_CASE_CONFIG,
28+
...testCase,
29+
}));
30+
31+
super.run(ruleName, rule, { valid: finalValid, invalid: finalInvalid });
32+
}
33+
}
34+
435
export const createRuleTester = (
536
parserOptions: Partial<TSESLint.ParserOptions> = {}
6-
): TSESLint.RuleTester =>
7-
new TSESLint.RuleTester({
37+
): TSESLint.RuleTester => {
38+
return new TestingLibraryRuleTester({
839
parser: resolve('./node_modules/@typescript-eslint/parser'),
940
parserOptions: {
1041
ecmaVersion: 2018,
1142
sourceType: 'module',
43+
ecmaFeatures: {
44+
jsx: true,
45+
},
1246
...parserOptions,
1347
},
1448
});
49+
};

tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"target": "es5",
3+
"target": "es6",
44
"module": "commonjs",
55
"moduleResolution": "node",
66
"esModuleInterop": true,

0 commit comments

Comments
 (0)