@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dineroregnskab/eslint-plugin-custom-rules",
3
- "version": "4.0.1",
3
+ "version": "4.2.0",
4
4
  "description": "ESLint plugin with custom rules for Dinero Regnskab",
5
5
  "main": "eslint-plugin-custom-rules.js",
6
6
  "scripts": {
@@ -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