waitless 1.0.2__tar.gz → 1.0.3__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.
Files changed (25) hide show
  1. {waitless-1.0.2/waitless.egg-info → waitless-1.0.3}/PKG-INFO +39 -38
  2. {waitless-1.0.2 → waitless-1.0.3}/README.md +34 -33
  3. {waitless-1.0.2 → waitless-1.0.3}/pyproject.toml +5 -5
  4. {waitless-1.0.2 → waitless-1.0.3}/waitless/__init__.py +4 -4
  5. {waitless-1.0.2 → waitless-1.0.3}/waitless/__main__.py +1 -1
  6. {waitless-1.0.2 → waitless-1.0.3}/waitless/config.py +2 -1
  7. {waitless-1.0.2 → waitless-1.0.3}/waitless/instrumentation.py +11 -3
  8. {waitless-1.0.2 → waitless-1.0.3/waitless.egg-info}/PKG-INFO +39 -38
  9. {waitless-1.0.2 → waitless-1.0.3}/LICENSE +0 -0
  10. {waitless-1.0.2 → waitless-1.0.3}/setup.cfg +0 -0
  11. {waitless-1.0.2 → waitless-1.0.3}/waitless/adapters/__init__.py +0 -0
  12. {waitless-1.0.2 → waitless-1.0.3}/waitless/adapters/angular.py +0 -0
  13. {waitless-1.0.2 → waitless-1.0.3}/waitless/adapters/base.py +0 -0
  14. {waitless-1.0.2 → waitless-1.0.3}/waitless/adapters/react.py +0 -0
  15. {waitless-1.0.2 → waitless-1.0.3}/waitless/adapters/vue.py +0 -0
  16. {waitless-1.0.2 → waitless-1.0.3}/waitless/diagnostics.py +0 -0
  17. {waitless-1.0.2 → waitless-1.0.3}/waitless/engine.py +0 -0
  18. {waitless-1.0.2 → waitless-1.0.3}/waitless/exceptions.py +0 -0
  19. {waitless-1.0.2 → waitless-1.0.3}/waitless/selenium_integration.py +0 -0
  20. {waitless-1.0.2 → waitless-1.0.3}/waitless/signals.py +0 -0
  21. {waitless-1.0.2 → waitless-1.0.3}/waitless.egg-info/SOURCES.txt +0 -0
  22. {waitless-1.0.2 → waitless-1.0.3}/waitless.egg-info/dependency_links.txt +0 -0
  23. {waitless-1.0.2 → waitless-1.0.3}/waitless.egg-info/entry_points.txt +0 -0
  24. {waitless-1.0.2 → waitless-1.0.3}/waitless.egg-info/requires.txt +0 -0
  25. {waitless-1.0.2 → waitless-1.0.3}/waitless.egg-info/top_level.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waitless
3
- Version: 1.0.2
4
- Summary: Eliminate explicit waits in UI automation by detecting true UI stability
3
+ Version: 1.0.3
4
+ Summary: Reduce explicit waits in Selenium by evaluating UI stability signals
5
5
  Author-email: Dhiraj Das <dhirajdas.666@gmail.com>
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://www.dhirajdas.dev/project/waitless
8
- Project-URL: Documentation, https://www.dhirajdas.dev/project/waitless
9
- Project-URL: Repository, https://github.com/godhiraj-code/waitless
7
+ Project-URL: Homepage, https://www.dhirajdas.dev
8
+ Project-URL: Documentation, https://github.com/godhiraj-code/waitless#readme
9
+ Project-URL: Repository, https://github.com/godhiraj-code/waitless.git
10
10
  Project-URL: Issues, https://github.com/godhiraj-code/waitless/issues
11
11
  Keywords: selenium,automation,testing,ui-testing,wait,stability,flaky-tests
12
12
  Classifier: Development Status :: 4 - Beta
@@ -36,9 +36,7 @@ Dynamic: license-file
36
36
 
37
37
  Reduce explicit waits and sleeps by automatically evaluating multiple UI stability signals.
38
38
 
39
- [Watch the demo and read the architecture case study](https://www.dhirajdas.dev/project/waitless) · [Install from PyPI](https://pypi.org/project/waitless/)
40
39
 
41
- ![Waitless stability demo](examples/waitless_demo.webp)
42
40
 
43
41
  ## Installation
44
42
 
@@ -53,16 +51,16 @@ from selenium import webdriver
53
51
  from selenium.webdriver.common.by import By
54
52
  from waitless import stabilize
55
53
 
56
- # Create driver as usual
57
54
  driver = webdriver.Chrome()
58
-
59
- # Enable automatic stabilization - ONE LINE
60
55
  driver = stabilize(driver)
61
56
 
62
- # All interactions now auto-wait for stability
63
- driver.get("https://example.com")
64
- driver.find_element(By.ID, "login-button").click() # ← Auto-waits!
65
- driver.find_element(By.ID, "username").send_keys("user") # ← Auto-waits!
57
+ # Navigation, lookups, and common element actions now wait for stability.
58
+ driver.get(
59
+ "data:text/html,<input id='username'><button id='login-button'>Log in</button>"
60
+ )
61
+ driver.find_element(By.ID, "username").send_keys("user")
62
+ driver.find_element(By.ID, "login-button").click()
63
+ driver.quit()
66
64
  ```
67
65
 
68
66
  ## Why Waitless?
@@ -81,22 +79,23 @@ Automation tests fail because interactions happen while the UI is still changing
81
79
  | Approach | Problem |
82
80
  |----------|---------|
83
81
  | `time.sleep(2)` | Too slow, still fails sometimes |
84
- | `WebDriverWait` | Only checks one element, misses page-wide state |
82
+ | `WebDriverWait` | Requires an explicit condition at each synchronization point |
85
83
  | Retries | Masks the real problem, adds flakiness |
86
84
 
87
85
  ### The Waitless Solution
88
86
 
89
- Waitless monitors the **entire page** for stability signals:
87
+ Waitless evaluates page-level stability signals:
90
88
 
91
- - ✅ DOM mutation activity (MutationObserver, including **Shadow DOM**)
92
- - ✅ Pending network requests (XHR/fetch interception)
93
- - ✅ CSS animations and transitions
94
- - ✅ Layout stability (element movement)
95
- - ✅ WebSocket/SSE activity (opt-in)
96
- - ✅ Framework hooks (React/Angular/Vue, opt-in)
97
- - ✅ Same-origin iframe load readiness (opt-in; not full child-frame signal injection)
89
+ - DOM mutation activity (MutationObserver, including **Shadow DOM**)
90
+ - Pending network requests (XHR/fetch interception after instrumentation is installed)
91
+ - CSS animations and transitions
92
+ - Layout stability for interactive elements
93
+ - WebSocket/SSE activity (opt-in)
94
+ - Framework hooks (React/Angular/Vue, opt-in)
95
+ - Same-origin iframe load readiness (opt-in; not full child-frame signal injection)
98
96
 
99
- When you interact, waitless ensures the page is truly ready.
97
+ Before supported interactions, Waitless polls until the enabled mandatory signals
98
+ meet their configured thresholds.
100
99
 
101
100
  ## Configuration
102
101
 
@@ -204,14 +203,15 @@ Many apps have background traffic that never stops:
204
203
  - Feature flags
205
204
  - WebSocket heartbeats
206
205
 
207
- If tests timeout frequently, try:
206
+ If known background traffic exceeds the default, raise the threshold carefully:
208
207
  ```python
209
- config = StabilizationConfig(network_idle_threshold=2)
208
+ config = StabilizationConfig(network_idle_threshold=3)
210
209
  ```
211
210
 
212
211
  ### Wrapped Elements
213
212
 
214
- The stabilized driver returns wrapped elements that auto-wait. They behave like WebElements but:
213
+ The stabilized driver returns wrapped elements that auto-wait before `click()`,
214
+ `send_keys()`, `submit()`, and `clear()`. They behave like WebElements but:
215
215
 
216
216
  - `isinstance(element, WebElement)` returns `False`
217
217
  - Use `.unwrap()` to get the original element if needed
@@ -221,15 +221,14 @@ element = driver.find_element(By.ID, "button")
221
221
  original = element.unwrap() # Gets the real WebElement
222
222
  ```
223
223
 
224
- ## v1.0.0 New Features
224
+ ## Optional Signals
225
225
 
226
226
  - **WebSocket/SSE Awareness** - Track WebSocket and Server-Sent Events activity
227
227
  - **Framework Adapters** - React, Angular, Vue hooks for framework-specific settling
228
228
  - **iframe Support** - Monitor same-origin iframes
229
- - **Performance Benchmarks** - Built-in benchmark suite
229
+ - **Performance benchmarks** - Run the repository benchmark against your environment
230
230
 
231
231
  ```python
232
- # Enable new v1.0 features
233
232
  config = StabilizationConfig(
234
233
  track_websocket=True, # WebSocket monitoring
235
234
  track_sse=True, # SSE monitoring
@@ -242,10 +241,10 @@ config = StabilizationConfig(
242
241
 
243
242
  | Metric | Typical Value |
244
243
  |--------|---------------|
245
- | Instrumentation injection | Environment-dependent; run `python -m benchmarks.overhead_test` |
246
- | Per-poll overhead | Environment-dependent; run the bundled benchmark |
244
+ | Instrumentation injection | Environment-dependent; from a repository checkout, run `python benchmarks/overhead_test.py` |
245
+ | Per-poll overhead | Environment-dependent; run the repository benchmark |
247
246
  | Poll interval (default) | 50ms |
248
- | Typical stabilization | 50-200ms after activity |
247
+ | Stabilization time | Depends on page activity, thresholds, and environment |
249
248
 
250
249
  ### Navigation Handling
251
250
 
@@ -260,12 +259,18 @@ JavaScript, Waitless validates/re-injects instrumentation on the next wait:
260
259
  This does not observe routes continuously, and cross-origin iframe internals remain
261
260
  outside the browser same-origin boundary.
262
261
 
262
+ Because instrumentation is installed after Selenium's synchronous navigation call
263
+ returns, requests that start and finish during navigation are not observed. Requests
264
+ started after instrumentation is installed are tracked.
265
+
263
266
  `find_elements()` keeps Selenium's immediate-empty lookup semantics: after the
264
267
  page-stability wait, it performs one lookup and returns `[]` when there are no matches.
268
+ By contrast, `find_element()` retries `NoSuchElementException` until the configured
269
+ timeout after the page-stability wait.
265
270
 
266
271
  ## Current Limitations
267
272
 
268
- - **Selenium only** - Playwright support planned
273
+ - **Selenium only** - No Playwright integration
269
274
  - **Sync only** - No async/await support yet
270
275
  - **No Service Workers** - SW network requests not intercepted
271
276
 
@@ -294,7 +299,3 @@ See [CHANGELOG.md](CHANGELOG.md) for version history.
294
299
  ## License
295
300
 
296
301
  MIT
297
-
298
- ## Try It on a Real Flaky Flow
299
-
300
- Run Waitless against one Selenium flow that currently uses sleeps or retries, then compare failures and elapsed time before and after. Share a sanitized result in an issue. If it removes a sleep or retry, [star the repository](https://github.com/godhiraj-code/waitless). The [full case study](https://www.dhirajdas.dev/project/waitless) explains the architecture and trade-offs.
@@ -6,9 +6,7 @@
6
6
 
7
7
  Reduce explicit waits and sleeps by automatically evaluating multiple UI stability signals.
8
8
 
9
- [Watch the demo and read the architecture case study](https://www.dhirajdas.dev/project/waitless) · [Install from PyPI](https://pypi.org/project/waitless/)
10
9
 
11
- ![Waitless stability demo](examples/waitless_demo.webp)
12
10
 
13
11
  ## Installation
14
12
 
@@ -23,16 +21,16 @@ from selenium import webdriver
23
21
  from selenium.webdriver.common.by import By
24
22
  from waitless import stabilize
25
23
 
26
- # Create driver as usual
27
24
  driver = webdriver.Chrome()
28
-
29
- # Enable automatic stabilization - ONE LINE
30
25
  driver = stabilize(driver)
31
26
 
32
- # All interactions now auto-wait for stability
33
- driver.get("https://example.com")
34
- driver.find_element(By.ID, "login-button").click() # ← Auto-waits!
35
- driver.find_element(By.ID, "username").send_keys("user") # ← Auto-waits!
27
+ # Navigation, lookups, and common element actions now wait for stability.
28
+ driver.get(
29
+ "data:text/html,<input id='username'><button id='login-button'>Log in</button>"
30
+ )
31
+ driver.find_element(By.ID, "username").send_keys("user")
32
+ driver.find_element(By.ID, "login-button").click()
33
+ driver.quit()
36
34
  ```
37
35
 
38
36
  ## Why Waitless?
@@ -51,22 +49,23 @@ Automation tests fail because interactions happen while the UI is still changing
51
49
  | Approach | Problem |
52
50
  |----------|---------|
53
51
  | `time.sleep(2)` | Too slow, still fails sometimes |
54
- | `WebDriverWait` | Only checks one element, misses page-wide state |
52
+ | `WebDriverWait` | Requires an explicit condition at each synchronization point |
55
53
  | Retries | Masks the real problem, adds flakiness |
56
54
 
57
55
  ### The Waitless Solution
58
56
 
59
- Waitless monitors the **entire page** for stability signals:
57
+ Waitless evaluates page-level stability signals:
60
58
 
61
- - ✅ DOM mutation activity (MutationObserver, including **Shadow DOM**)
62
- - ✅ Pending network requests (XHR/fetch interception)
63
- - ✅ CSS animations and transitions
64
- - ✅ Layout stability (element movement)
65
- - ✅ WebSocket/SSE activity (opt-in)
66
- - ✅ Framework hooks (React/Angular/Vue, opt-in)
67
- - ✅ Same-origin iframe load readiness (opt-in; not full child-frame signal injection)
59
+ - DOM mutation activity (MutationObserver, including **Shadow DOM**)
60
+ - Pending network requests (XHR/fetch interception after instrumentation is installed)
61
+ - CSS animations and transitions
62
+ - Layout stability for interactive elements
63
+ - WebSocket/SSE activity (opt-in)
64
+ - Framework hooks (React/Angular/Vue, opt-in)
65
+ - Same-origin iframe load readiness (opt-in; not full child-frame signal injection)
68
66
 
69
- When you interact, waitless ensures the page is truly ready.
67
+ Before supported interactions, Waitless polls until the enabled mandatory signals
68
+ meet their configured thresholds.
70
69
 
71
70
  ## Configuration
72
71
 
@@ -174,14 +173,15 @@ Many apps have background traffic that never stops:
174
173
  - Feature flags
175
174
  - WebSocket heartbeats
176
175
 
177
- If tests timeout frequently, try:
176
+ If known background traffic exceeds the default, raise the threshold carefully:
178
177
  ```python
179
- config = StabilizationConfig(network_idle_threshold=2)
178
+ config = StabilizationConfig(network_idle_threshold=3)
180
179
  ```
181
180
 
182
181
  ### Wrapped Elements
183
182
 
184
- The stabilized driver returns wrapped elements that auto-wait. They behave like WebElements but:
183
+ The stabilized driver returns wrapped elements that auto-wait before `click()`,
184
+ `send_keys()`, `submit()`, and `clear()`. They behave like WebElements but:
185
185
 
186
186
  - `isinstance(element, WebElement)` returns `False`
187
187
  - Use `.unwrap()` to get the original element if needed
@@ -191,15 +191,14 @@ element = driver.find_element(By.ID, "button")
191
191
  original = element.unwrap() # Gets the real WebElement
192
192
  ```
193
193
 
194
- ## v1.0.0 New Features
194
+ ## Optional Signals
195
195
 
196
196
  - **WebSocket/SSE Awareness** - Track WebSocket and Server-Sent Events activity
197
197
  - **Framework Adapters** - React, Angular, Vue hooks for framework-specific settling
198
198
  - **iframe Support** - Monitor same-origin iframes
199
- - **Performance Benchmarks** - Built-in benchmark suite
199
+ - **Performance benchmarks** - Run the repository benchmark against your environment
200
200
 
201
201
  ```python
202
- # Enable new v1.0 features
203
202
  config = StabilizationConfig(
204
203
  track_websocket=True, # WebSocket monitoring
205
204
  track_sse=True, # SSE monitoring
@@ -212,10 +211,10 @@ config = StabilizationConfig(
212
211
 
213
212
  | Metric | Typical Value |
214
213
  |--------|---------------|
215
- | Instrumentation injection | Environment-dependent; run `python -m benchmarks.overhead_test` |
216
- | Per-poll overhead | Environment-dependent; run the bundled benchmark |
214
+ | Instrumentation injection | Environment-dependent; from a repository checkout, run `python benchmarks/overhead_test.py` |
215
+ | Per-poll overhead | Environment-dependent; run the repository benchmark |
217
216
  | Poll interval (default) | 50ms |
218
- | Typical stabilization | 50-200ms after activity |
217
+ | Stabilization time | Depends on page activity, thresholds, and environment |
219
218
 
220
219
  ### Navigation Handling
221
220
 
@@ -230,12 +229,18 @@ JavaScript, Waitless validates/re-injects instrumentation on the next wait:
230
229
  This does not observe routes continuously, and cross-origin iframe internals remain
231
230
  outside the browser same-origin boundary.
232
231
 
232
+ Because instrumentation is installed after Selenium's synchronous navigation call
233
+ returns, requests that start and finish during navigation are not observed. Requests
234
+ started after instrumentation is installed are tracked.
235
+
233
236
  `find_elements()` keeps Selenium's immediate-empty lookup semantics: after the
234
237
  page-stability wait, it performs one lookup and returns `[]` when there are no matches.
238
+ By contrast, `find_element()` retries `NoSuchElementException` until the configured
239
+ timeout after the page-stability wait.
235
240
 
236
241
  ## Current Limitations
237
242
 
238
- - **Selenium only** - Playwright support planned
243
+ - **Selenium only** - No Playwright integration
239
244
  - **Sync only** - No async/await support yet
240
245
  - **No Service Workers** - SW network requests not intercepted
241
246
 
@@ -264,7 +269,3 @@ See [CHANGELOG.md](CHANGELOG.md) for version history.
264
269
  ## License
265
270
 
266
271
  MIT
267
-
268
- ## Try It on a Real Flaky Flow
269
-
270
- Run Waitless against one Selenium flow that currently uses sleeps or retries, then compare failures and elapsed time before and after. Share a sanitized result in an issue. If it removes a sleep or retry, [star the repository](https://github.com/godhiraj-code/waitless). The [full case study](https://www.dhirajdas.dev/project/waitless) explains the architecture and trade-offs.
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "waitless"
7
- version = "1.0.2"
8
- description = "Eliminate explicit waits in UI automation by detecting true UI stability"
7
+ version = "1.0.3"
8
+ description = "Reduce explicit waits in Selenium by evaluating UI stability signals"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  requires-python = ">=3.9"
@@ -38,9 +38,9 @@ dependencies = [
38
38
  ]
39
39
 
40
40
  [project.urls]
41
- Homepage = "https://www.dhirajdas.dev/project/waitless"
42
- Documentation = "https://www.dhirajdas.dev/project/waitless"
43
- Repository = "https://github.com/godhiraj-code/waitless"
41
+ Homepage = "https://www.dhirajdas.dev"
42
+ Documentation = "https://github.com/godhiraj-code/waitless#readme"
43
+ Repository = "https://github.com/godhiraj-code/waitless.git"
44
44
  Issues = "https://github.com/godhiraj-code/waitless/issues"
45
45
 
46
46
  [project.scripts]
@@ -1,8 +1,8 @@
1
1
  """
2
- Waitless - Zero-wait UI automation stabilization library.
2
+ Waitless - Automatic Selenium UI stabilization library.
3
3
 
4
- Eliminate explicit waits and sleeps in UI automation by automatically
5
- waiting for true UI stability instead of time-based conditions.
4
+ Reduce explicit waits and sleeps by evaluating configurable UI stability
5
+ signals before supported Selenium interactions.
6
6
 
7
7
  Basic Usage:
8
8
  from waitless import stabilize
@@ -36,7 +36,7 @@ Disable:
36
36
  driver = unstabilize(driver) # Back to original behavior
37
37
  """
38
38
 
39
- __version__ = '1.0.2'
39
+ __version__ = '1.0.3'
40
40
  __author__ = 'Dhiraj Das'
41
41
 
42
42
  # Public API
@@ -14,7 +14,7 @@ def main():
14
14
  """Main CLI entry point."""
15
15
  parser = argparse.ArgumentParser(
16
16
  prog='waitless',
17
- description='Waitless - Zero-wait UI automation stabilization'
17
+ description='Waitless - Automatic Selenium UI stabilization'
18
18
  )
19
19
 
20
20
  subparsers = parser.add_subparsers(dest='command', help='Available commands')
@@ -66,7 +66,8 @@ class StabilizationConfig:
66
66
  Options: 'react', 'angular', 'vue'. Default empty (auto-detect off).
67
67
  When specified, waitless will inject framework-specific hooks.
68
68
 
69
- track_iframes: Whether to inject instrumentation into same-origin iframes.
69
+ track_iframes: Whether to monitor same-origin iframe load readiness.
70
+ This does not inject full child-frame instrumentation.
70
71
  Default False. Cross-origin iframes cannot be accessed.
71
72
  """
72
73
 
@@ -12,7 +12,7 @@ INSTRUMENTATION_SCRIPT = """
12
12
 
13
13
  window.__waitless__ = {
14
14
  _initialized: true,
15
- _version: '1.0.1',
15
+ _version: '1.0.3',
16
16
 
17
17
  // State tracking
18
18
  pendingRequests: 0,
@@ -249,8 +249,16 @@ INSTRUMENTATION_SCRIPT = """
249
249
  xhr.addEventListener('loadend', function() {
250
250
  self._requestEnded(url, 'xhr', xhr.status);
251
251
  });
252
-
253
- return self._originalXHRSend.apply(this, arguments);
252
+
253
+ try {
254
+ return self._originalXHRSend.apply(this, arguments);
255
+ } catch (error) {
256
+ // Synchronous failures (for example, send() before open()) do
257
+ // not emit loadend. Balance the counter before preserving the
258
+ // browser's original exception behavior.
259
+ self._requestEnded(url, 'xhr', 'error');
260
+ throw error;
261
+ }
254
262
  };
255
263
  },
256
264
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: waitless
3
- Version: 1.0.2
4
- Summary: Eliminate explicit waits in UI automation by detecting true UI stability
3
+ Version: 1.0.3
4
+ Summary: Reduce explicit waits in Selenium by evaluating UI stability signals
5
5
  Author-email: Dhiraj Das <dhirajdas.666@gmail.com>
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://www.dhirajdas.dev/project/waitless
8
- Project-URL: Documentation, https://www.dhirajdas.dev/project/waitless
9
- Project-URL: Repository, https://github.com/godhiraj-code/waitless
7
+ Project-URL: Homepage, https://www.dhirajdas.dev
8
+ Project-URL: Documentation, https://github.com/godhiraj-code/waitless#readme
9
+ Project-URL: Repository, https://github.com/godhiraj-code/waitless.git
10
10
  Project-URL: Issues, https://github.com/godhiraj-code/waitless/issues
11
11
  Keywords: selenium,automation,testing,ui-testing,wait,stability,flaky-tests
12
12
  Classifier: Development Status :: 4 - Beta
@@ -36,9 +36,7 @@ Dynamic: license-file
36
36
 
37
37
  Reduce explicit waits and sleeps by automatically evaluating multiple UI stability signals.
38
38
 
39
- [Watch the demo and read the architecture case study](https://www.dhirajdas.dev/project/waitless) · [Install from PyPI](https://pypi.org/project/waitless/)
40
39
 
41
- ![Waitless stability demo](examples/waitless_demo.webp)
42
40
 
43
41
  ## Installation
44
42
 
@@ -53,16 +51,16 @@ from selenium import webdriver
53
51
  from selenium.webdriver.common.by import By
54
52
  from waitless import stabilize
55
53
 
56
- # Create driver as usual
57
54
  driver = webdriver.Chrome()
58
-
59
- # Enable automatic stabilization - ONE LINE
60
55
  driver = stabilize(driver)
61
56
 
62
- # All interactions now auto-wait for stability
63
- driver.get("https://example.com")
64
- driver.find_element(By.ID, "login-button").click() # ← Auto-waits!
65
- driver.find_element(By.ID, "username").send_keys("user") # ← Auto-waits!
57
+ # Navigation, lookups, and common element actions now wait for stability.
58
+ driver.get(
59
+ "data:text/html,<input id='username'><button id='login-button'>Log in</button>"
60
+ )
61
+ driver.find_element(By.ID, "username").send_keys("user")
62
+ driver.find_element(By.ID, "login-button").click()
63
+ driver.quit()
66
64
  ```
67
65
 
68
66
  ## Why Waitless?
@@ -81,22 +79,23 @@ Automation tests fail because interactions happen while the UI is still changing
81
79
  | Approach | Problem |
82
80
  |----------|---------|
83
81
  | `time.sleep(2)` | Too slow, still fails sometimes |
84
- | `WebDriverWait` | Only checks one element, misses page-wide state |
82
+ | `WebDriverWait` | Requires an explicit condition at each synchronization point |
85
83
  | Retries | Masks the real problem, adds flakiness |
86
84
 
87
85
  ### The Waitless Solution
88
86
 
89
- Waitless monitors the **entire page** for stability signals:
87
+ Waitless evaluates page-level stability signals:
90
88
 
91
- - ✅ DOM mutation activity (MutationObserver, including **Shadow DOM**)
92
- - ✅ Pending network requests (XHR/fetch interception)
93
- - ✅ CSS animations and transitions
94
- - ✅ Layout stability (element movement)
95
- - ✅ WebSocket/SSE activity (opt-in)
96
- - ✅ Framework hooks (React/Angular/Vue, opt-in)
97
- - ✅ Same-origin iframe load readiness (opt-in; not full child-frame signal injection)
89
+ - DOM mutation activity (MutationObserver, including **Shadow DOM**)
90
+ - Pending network requests (XHR/fetch interception after instrumentation is installed)
91
+ - CSS animations and transitions
92
+ - Layout stability for interactive elements
93
+ - WebSocket/SSE activity (opt-in)
94
+ - Framework hooks (React/Angular/Vue, opt-in)
95
+ - Same-origin iframe load readiness (opt-in; not full child-frame signal injection)
98
96
 
99
- When you interact, waitless ensures the page is truly ready.
97
+ Before supported interactions, Waitless polls until the enabled mandatory signals
98
+ meet their configured thresholds.
100
99
 
101
100
  ## Configuration
102
101
 
@@ -204,14 +203,15 @@ Many apps have background traffic that never stops:
204
203
  - Feature flags
205
204
  - WebSocket heartbeats
206
205
 
207
- If tests timeout frequently, try:
206
+ If known background traffic exceeds the default, raise the threshold carefully:
208
207
  ```python
209
- config = StabilizationConfig(network_idle_threshold=2)
208
+ config = StabilizationConfig(network_idle_threshold=3)
210
209
  ```
211
210
 
212
211
  ### Wrapped Elements
213
212
 
214
- The stabilized driver returns wrapped elements that auto-wait. They behave like WebElements but:
213
+ The stabilized driver returns wrapped elements that auto-wait before `click()`,
214
+ `send_keys()`, `submit()`, and `clear()`. They behave like WebElements but:
215
215
 
216
216
  - `isinstance(element, WebElement)` returns `False`
217
217
  - Use `.unwrap()` to get the original element if needed
@@ -221,15 +221,14 @@ element = driver.find_element(By.ID, "button")
221
221
  original = element.unwrap() # Gets the real WebElement
222
222
  ```
223
223
 
224
- ## v1.0.0 New Features
224
+ ## Optional Signals
225
225
 
226
226
  - **WebSocket/SSE Awareness** - Track WebSocket and Server-Sent Events activity
227
227
  - **Framework Adapters** - React, Angular, Vue hooks for framework-specific settling
228
228
  - **iframe Support** - Monitor same-origin iframes
229
- - **Performance Benchmarks** - Built-in benchmark suite
229
+ - **Performance benchmarks** - Run the repository benchmark against your environment
230
230
 
231
231
  ```python
232
- # Enable new v1.0 features
233
232
  config = StabilizationConfig(
234
233
  track_websocket=True, # WebSocket monitoring
235
234
  track_sse=True, # SSE monitoring
@@ -242,10 +241,10 @@ config = StabilizationConfig(
242
241
 
243
242
  | Metric | Typical Value |
244
243
  |--------|---------------|
245
- | Instrumentation injection | Environment-dependent; run `python -m benchmarks.overhead_test` |
246
- | Per-poll overhead | Environment-dependent; run the bundled benchmark |
244
+ | Instrumentation injection | Environment-dependent; from a repository checkout, run `python benchmarks/overhead_test.py` |
245
+ | Per-poll overhead | Environment-dependent; run the repository benchmark |
247
246
  | Poll interval (default) | 50ms |
248
- | Typical stabilization | 50-200ms after activity |
247
+ | Stabilization time | Depends on page activity, thresholds, and environment |
249
248
 
250
249
  ### Navigation Handling
251
250
 
@@ -260,12 +259,18 @@ JavaScript, Waitless validates/re-injects instrumentation on the next wait:
260
259
  This does not observe routes continuously, and cross-origin iframe internals remain
261
260
  outside the browser same-origin boundary.
262
261
 
262
+ Because instrumentation is installed after Selenium's synchronous navigation call
263
+ returns, requests that start and finish during navigation are not observed. Requests
264
+ started after instrumentation is installed are tracked.
265
+
263
266
  `find_elements()` keeps Selenium's immediate-empty lookup semantics: after the
264
267
  page-stability wait, it performs one lookup and returns `[]` when there are no matches.
268
+ By contrast, `find_element()` retries `NoSuchElementException` until the configured
269
+ timeout after the page-stability wait.
265
270
 
266
271
  ## Current Limitations
267
272
 
268
- - **Selenium only** - Playwright support planned
273
+ - **Selenium only** - No Playwright integration
269
274
  - **Sync only** - No async/await support yet
270
275
  - **No Service Workers** - SW network requests not intercepted
271
276
 
@@ -294,7 +299,3 @@ See [CHANGELOG.md](CHANGELOG.md) for version history.
294
299
  ## License
295
300
 
296
301
  MIT
297
-
298
- ## Try It on a Real Flaky Flow
299
-
300
- Run Waitless against one Selenium flow that currently uses sleeps or retries, then compare failures and elapsed time before and after. Share a sanitized result in an issue. If it removes a sleep or retry, [star the repository](https://github.com/godhiraj-code/waitless). The [full case study](https://www.dhirajdas.dev/project/waitless) explains the architecture and trade-offs.
File without changes
File without changes
File without changes
File without changes