@dineroregnskab/eslint-plugin-custom-rules 4.0.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/eslint-plugin-custom-rules.js +1 -0
- package/package.json +1 -1
- package/rules/no-feature-toggle-without-await.js +132 -0
- package/.DS_Store +0 -0
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ module.exports = {
|
|
|
63
63
|
### Publish & install new rule locally
|
|
64
64
|
|
|
65
65
|
1. Log in to npm using `npm login`
|
|
66
|
-
2. run:
|
|
66
|
+
2. run (in ./Packages/JavaScript/eslint-plugin-custom-rules/):
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
# {version_type}: The type of version increment (patch, minor, major)
|
|
@@ -11,6 +11,7 @@ const rules = {
|
|
|
11
11
|
'no-viewencapsulation-none': require('./rules/no-viewencapsulation-none'),
|
|
12
12
|
'enum-comparison-reminder': require('./rules/enum-comparison-reminder'),
|
|
13
13
|
'enum-lowercase': require('./rules/enum-lowercase'),
|
|
14
|
+
'no-feature-toggle-without-await': require('./rules/no-feature-toggle-without-await'),
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
console.log('Custom ESLint rules loaded:', Object.keys(rules)); // Debug log
|
package/package.json
CHANGED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: 'suggestion',
|
|
4
|
+
docs: {
|
|
5
|
+
description:
|
|
6
|
+
'Warn when createUserWithOrganization uses featureToggles in beforeEach but awaitFeatureReady is not used in the it() block',
|
|
7
|
+
category: 'Best Practices',
|
|
8
|
+
recommended: true,
|
|
9
|
+
},
|
|
10
|
+
messages: {
|
|
11
|
+
missingAwaitFeatureReady:
|
|
12
|
+
'Feature toggles detected in beforeEach via createUserWithOrganization, but cy.awaitFeatureReady() not found in this it() block.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
create(context) {
|
|
17
|
+
let describeStack = [];
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
'CallExpression[callee.name="describe"], CallExpression[callee.name="context"]'() {
|
|
21
|
+
describeStack.push({
|
|
22
|
+
beforeEachHasFeatureToggles: false,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
'CallExpression[callee.name="beforeEach"]'(node) {
|
|
27
|
+
if (describeStack.length === 0) return;
|
|
28
|
+
|
|
29
|
+
const currentLevel = describeStack[describeStack.length - 1];
|
|
30
|
+
const callback = node.arguments[0];
|
|
31
|
+
if (!callback) return;
|
|
32
|
+
|
|
33
|
+
let hasFeatureToggles = false;
|
|
34
|
+
walkNode(callback, (child) => {
|
|
35
|
+
if (
|
|
36
|
+
child.type === 'CallExpression' &&
|
|
37
|
+
child.callee &&
|
|
38
|
+
child.callee.name === 'createUserWithOrganization'
|
|
39
|
+
) {
|
|
40
|
+
const arg = child.arguments[0];
|
|
41
|
+
if (arg && arg.type === 'ObjectExpression') {
|
|
42
|
+
arg.properties.forEach((prop) => {
|
|
43
|
+
if (
|
|
44
|
+
prop.type === 'Property' &&
|
|
45
|
+
prop.key &&
|
|
46
|
+
(prop.key.name || prop.key.value)
|
|
47
|
+
) {
|
|
48
|
+
const keyName = prop.key.name || prop.key.value;
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
keyName === 'featureToggles' &&
|
|
52
|
+
prop.value.type === 'ArrayExpression' &&
|
|
53
|
+
prop.value.elements.length > 0
|
|
54
|
+
) {
|
|
55
|
+
hasFeatureToggles = true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (hasFeatureToggles) {
|
|
64
|
+
currentLevel.beforeEachHasFeatureToggles = true;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
'CallExpression[callee.name="it"]'(node) {
|
|
69
|
+
if (describeStack.length === 0) return;
|
|
70
|
+
|
|
71
|
+
const hasFeatureTogglesInAnyAncestor = describeStack.some(
|
|
72
|
+
(level) => level.beforeEachHasFeatureToggles
|
|
73
|
+
);
|
|
74
|
+
if (!hasFeatureTogglesInAnyAncestor) return;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const callback = node.arguments[1];
|
|
78
|
+
if (!callback) return;
|
|
79
|
+
|
|
80
|
+
let hasAwaitFeatureReady = false;
|
|
81
|
+
|
|
82
|
+
walkNode(callback, (child) => {
|
|
83
|
+
if (
|
|
84
|
+
child.type === 'CallExpression' &&
|
|
85
|
+
child.callee &&
|
|
86
|
+
child.callee.type === 'MemberExpression' &&
|
|
87
|
+
child.callee.object &&
|
|
88
|
+
child.callee.object.name === 'cy' &&
|
|
89
|
+
child.callee.property &&
|
|
90
|
+
(child.callee.property.name === 'awaitFeatureReady' ||
|
|
91
|
+
child.callee.property.name === 'awaitFeaturesReady')
|
|
92
|
+
) {
|
|
93
|
+
hasAwaitFeatureReady = true;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (!hasAwaitFeatureReady) {
|
|
98
|
+
context.report({
|
|
99
|
+
node: node,
|
|
100
|
+
messageId: 'missingAwaitFeatureReady',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
'CallExpression[callee.name="describe"]:exit, CallExpression[callee.name="context"]:exit'() {
|
|
106
|
+
describeStack.pop();
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
function walkNode(node, callback) {
|
|
113
|
+
if (!node) return;
|
|
114
|
+
|
|
115
|
+
callback(node);
|
|
116
|
+
|
|
117
|
+
for (const key in node) {
|
|
118
|
+
if (key === 'parent' || key === 'loc' || key === 'range') continue;
|
|
119
|
+
|
|
120
|
+
const child = node[key];
|
|
121
|
+
|
|
122
|
+
if (Array.isArray(child)) {
|
|
123
|
+
child.forEach((item) => {
|
|
124
|
+
if (item && typeof item === 'object' && item.type) {
|
|
125
|
+
walkNode(item, callback);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
} else if (child && typeof child === 'object' && child.type) {
|
|
129
|
+
walkNode(child, callback);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
package/.DS_Store
DELETED
|
Binary file
|