-
Notifications
You must be signed in to change notification settings - Fork 147
/
Copy pathdetect-testing-library-utils.ts
106 lines (93 loc) · 3.45 KB
/
detect-testing-library-utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
export type DetectionHelpers = {
getIsTestingLibraryImported: () => boolean;
canReportErrors: () => boolean;
};
export type TestingLibrarySettings = {
'testing-library/module'?: string;
};
/**
* Enhances a given rule `create` with helpers to detect Testing Library utils.
*/
export function detectTestingLibraryUtils<
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener
>(
ruleCreate: (
context: Readonly<
TSESLint.RuleContext<TMessageIds, TOptions> & {
settings: TestingLibrarySettings;
}
>,
optionsWithDefault: Readonly<TOptions>,
detectionHelpers: Readonly<DetectionHelpers>
) => TRuleListener
) {
return (
context: Readonly<
TSESLint.RuleContext<TMessageIds, TOptions> & {
settings: TestingLibrarySettings;
}
>,
optionsWithDefault: Readonly<TOptions>
): TSESLint.RuleListener => {
let isImportingTestingLibraryModule = false;
let isImportingCustomModule = false;
// Init options based on shared ESLint settings
const customModule = context.settings['testing-library/module'];
// Helpers for Testing Library detection.
const helpers: DetectionHelpers = {
getIsTestingLibraryImported() {
if (!customModule) {
return true;
}
return isImportingTestingLibraryModule || isImportingCustomModule;
},
canReportErrors() {
return this.getIsTestingLibraryImported();
},
};
// Instructions for Testing Library detection.
// `ImportDeclaration` must be first in order to know if Testing Library
// is imported ASAP.
const detectionInstructions: TSESLint.RuleListener = {
ImportDeclaration(node: TSESTree.ImportDeclaration) {
if (!isImportingTestingLibraryModule) {
// check only if testing library import not found yet so we avoid
// to override isImportingTestingLibraryModule after it's found
isImportingTestingLibraryModule = /testing-library/g.test(
node.source.value as string
);
}
if (!isImportingCustomModule) {
// check only if custom module import not found yet so we avoid
// to override isImportingCustomModule after it's found
const importName = String(node.source.value);
isImportingCustomModule = importName.endsWith(customModule);
}
},
};
// update given rule to inject Testing Library detection
const ruleInstructions = ruleCreate(context, optionsWithDefault, helpers);
const enhancedRuleInstructions: TSESLint.RuleListener = {};
// The order here is important too: detection instructions must come before
// than rule instructions to:
// - detect Testing Library things before the rule is applied
// - be able to prevent the rule about to be applied if necessary
const allKeys = new Set(
Object.keys(detectionInstructions).concat(Object.keys(ruleInstructions))
);
allKeys.forEach((instruction) => {
enhancedRuleInstructions[instruction] = (node) => {
if (instruction in detectionInstructions) {
detectionInstructions[instruction](node);
}
if (helpers.canReportErrors() && ruleInstructions[instruction]) {
return ruleInstructions[instruction](node);
}
};
});
return enhancedRuleInstructions;
};
}