waitless 0.3.0__tar.gz → 0.3.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waitless
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Eliminate explicit waits in UI automation by detecting true UI stability
5
5
  Author-email: Dhiraj Das <dhirajdas.666@gmail.com>
6
6
  License: MIT
@@ -84,7 +84,7 @@ Automation tests fail because interactions happen while the UI is still changing
84
84
 
85
85
  Waitless monitors the **entire page** for stability signals:
86
86
 
87
- - ✅ DOM mutation activity (MutationObserver)
87
+ - ✅ DOM mutation activity (MutationObserver, including **Shadow DOM**)
88
88
  - ✅ Pending network requests (XHR/fetch interception)
89
89
  - ✅ CSS animations and transitions
90
90
  - ✅ Layout stability (element movement)
@@ -219,7 +219,6 @@ original = element.unwrap() # Gets the real WebElement
219
219
  - **Selenium only** - Playwright support planned for v1
220
220
  - **Sync only** - No async/await support yet
221
221
  - **Main frame only** - iframes not monitored
222
- - **No Shadow DOM** - MutationObserver doesn't see shadow roots
223
222
  - **No Service Workers** - SW network requests not intercepted
224
223
 
225
224
  See [CHANGELOG.md](CHANGELOG.md) for version history.
@@ -54,7 +54,7 @@ Automation tests fail because interactions happen while the UI is still changing
54
54
 
55
55
  Waitless monitors the **entire page** for stability signals:
56
56
 
57
- - ✅ DOM mutation activity (MutationObserver)
57
+ - ✅ DOM mutation activity (MutationObserver, including **Shadow DOM**)
58
58
  - ✅ Pending network requests (XHR/fetch interception)
59
59
  - ✅ CSS animations and transitions
60
60
  - ✅ Layout stability (element movement)
@@ -189,7 +189,6 @@ original = element.unwrap() # Gets the real WebElement
189
189
  - **Selenium only** - Playwright support planned for v1
190
190
  - **Sync only** - No async/await support yet
191
191
  - **Main frame only** - iframes not monitored
192
- - **No Shadow DOM** - MutationObserver doesn't see shadow roots
193
192
  - **No Service Workers** - SW network requests not intercepted
194
193
 
195
194
  See [CHANGELOG.md](CHANGELOG.md) for version history.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "waitless"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Eliminate explicit waits in UI automation by detecting true UI stability"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -36,7 +36,7 @@ Disable:
36
36
  driver = unstabilize(driver) # Back to original behavior
37
37
  """
38
38
 
39
- __version__ = '0.3.0'
39
+ __version__ = '0.3.1'
40
40
  __author__ = 'Dhiraj Das'
41
41
 
42
42
  # Public API
@@ -71,6 +71,7 @@ INSTRUMENTATION_SCRIPT = """
71
71
  // Rolling window for mutation rate calculation
72
72
  _mutationTimestamps: [],
73
73
  _mutationWindowMs: 1000, // 1 second window for rate calculation
74
+ _observedShadowRoots: new WeakSet(),
74
75
 
75
76
  _setupMutationObserver: function() {
76
77
  var self = this;
@@ -87,17 +88,77 @@ INSTRUMENTATION_SCRIPT = """
87
88
  self._mutationTimestamps.shift();
88
89
  }
89
90
 
91
+ // Check for new shadow roots in added nodes
92
+ mutations.forEach(function(mutation) {
93
+ mutation.addedNodes.forEach(function(node) {
94
+ if (node.nodeType === 1) { // Element node
95
+ self._observeShadowRoots(node);
96
+ }
97
+ });
98
+ });
99
+
90
100
  self._log('DOM mutation', { count: mutations.length, rate: self.getMutationRate() });
91
101
  });
92
102
 
93
- observer.observe(document.documentElement || document.body, {
103
+ var config = {
94
104
  childList: true,
95
105
  subtree: true,
96
106
  attributes: true,
97
107
  characterData: true
98
- });
108
+ };
99
109
 
110
+ observer.observe(document.documentElement || document.body, config);
100
111
  this._observers.push(observer);
112
+
113
+ // Initial scan for shadow roots
114
+ this._observeShadowRoots(document);
115
+ },
116
+
117
+ _observeShadowRoots: function(root) {
118
+ var self = this;
119
+
120
+ // Function to recursively find and observe shadow roots
121
+ var walk = function(node) {
122
+ if (node.shadowRoot && !self._observedShadowRoots.has(node.shadowRoot)) {
123
+ self._observedShadowRoots.add(node.shadowRoot);
124
+
125
+ var observer = new MutationObserver(function(mutations) {
126
+ var now = Date.now();
127
+ self.lastMutationTime = now;
128
+ self._mutationTimestamps.push(now);
129
+ self._log('Shadow DOM mutation', { count: mutations.length });
130
+
131
+ // Scan new nodes in shadow DOM for nested shadow roots
132
+ mutations.forEach(function(mutation) {
133
+ mutation.addedNodes.forEach(function(newNode) {
134
+ if (newNode.nodeType === 1) walk(newNode);
135
+ });
136
+ });
137
+ });
138
+
139
+ observer.observe(node.shadowRoot, {
140
+ childList: true,
141
+ subtree: true,
142
+ attributes: true,
143
+ characterData: true
144
+ });
145
+
146
+ self._observers.push(observer);
147
+ self._log('Observing shadow root', { host: node.tagName });
148
+
149
+ // Recurse into the shadow root
150
+ walk(node.shadowRoot);
151
+ }
152
+
153
+ // Traverse children
154
+ var child = node.firstElementChild;
155
+ while (child) {
156
+ walk(child);
157
+ child = child.nextElementSibling;
158
+ }
159
+ };
160
+
161
+ walk(root);
101
162
  },
102
163
 
103
164
  // Calculate mutations per second from rolling window
@@ -239,8 +300,26 @@ INSTRUMENTATION_SCRIPT = """
239
300
  },
240
301
 
241
302
  _checkLayoutStability: function() {
242
- // Track key interactive elements
243
- var elements = document.querySelectorAll('button, a, input, [onclick], [role="button"]');
303
+ // Track key interactive elements, including those in shadow DOM
304
+ var elements = [];
305
+
306
+ var collectElements = function(root) {
307
+ var found = root.querySelectorAll('button, a, input, [onclick], [role="button"]');
308
+ for (var i = 0; i < found.length; i++) {
309
+ elements.push(found[i]);
310
+ }
311
+
312
+ // Recursively check shadow roots
313
+ var all = root.querySelectorAll('*');
314
+ for (var j = 0; j < all.length; j++) {
315
+ if (all[j].shadowRoot) {
316
+ collectElements(all[j].shadowRoot);
317
+ }
318
+ }
319
+ };
320
+
321
+ collectElements(document);
322
+
244
323
  var isShifting = false;
245
324
  var self = this;
246
325
 
@@ -169,14 +169,26 @@ class SignalEvaluator:
169
169
  # Primary: Use mutation rate if available
170
170
  if mutation_rate is not None:
171
171
  threshold = self.config.mutation_rate_threshold
172
- is_stable = mutation_rate <= threshold
172
+ rate_stable = mutation_rate <= threshold
173
+
174
+ # Also check settle time to ensure we are quiet AFTER a mutation
175
+ time_since_mutation = (current_time * 1000) - last_mutation
176
+ threshold_ms = self.config.dom_settle_time * 1000
177
+ settle_stable = time_since_mutation >= threshold_ms
178
+
179
+ is_stable = rate_stable and settle_stable
180
+
181
+ details = f"Mutation rate: {mutation_rate:.0f}/sec (threshold: {threshold:.0f}/sec)"
182
+ if not settle_stable:
183
+ details += f", but only {time_since_mutation:.0f}ms since last mutation (need {threshold_ms:.0f}ms)"
184
+
173
185
  return Signal(
174
186
  signal_type=SignalType.DOM_MUTATIONS,
175
187
  state=SignalState.STABLE if is_stable else SignalState.UNSTABLE,
176
188
  value=mutation_rate,
177
189
  threshold=threshold,
178
190
  is_mandatory=True,
179
- details=f"Mutation rate: {mutation_rate:.0f}/sec (threshold: {threshold:.0f}/sec)",
191
+ details=details,
180
192
  )
181
193
 
182
194
  # Fallback: Use time since last mutation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waitless
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Eliminate explicit waits in UI automation by detecting true UI stability
5
5
  Author-email: Dhiraj Das <dhirajdas.666@gmail.com>
6
6
  License: MIT
@@ -84,7 +84,7 @@ Automation tests fail because interactions happen while the UI is still changing
84
84
 
85
85
  Waitless monitors the **entire page** for stability signals:
86
86
 
87
- - ✅ DOM mutation activity (MutationObserver)
87
+ - ✅ DOM mutation activity (MutationObserver, including **Shadow DOM**)
88
88
  - ✅ Pending network requests (XHR/fetch interception)
89
89
  - ✅ CSS animations and transitions
90
90
  - ✅ Layout stability (element movement)
@@ -219,7 +219,6 @@ original = element.unwrap() # Gets the real WebElement
219
219
  - **Selenium only** - Playwright support planned for v1
220
220
  - **Sync only** - No async/await support yet
221
221
  - **Main frame only** - iframes not monitored
222
- - **No Shadow DOM** - MutationObserver doesn't see shadow roots
223
222
  - **No Service Workers** - SW network requests not intercepted
224
223
 
225
224
  See [CHANGELOG.md](CHANGELOG.md) for version history.
File without changes
File without changes
File without changes
File without changes
File without changes