waitless 0.1.0__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.
- waitless-0.1.0/PKG-INFO +243 -0
- waitless-0.1.0/README.md +215 -0
- waitless-0.1.0/pyproject.toml +55 -0
- waitless-0.1.0/setup.cfg +4 -0
- waitless-0.1.0/waitless/__init__.py +92 -0
- waitless-0.1.0/waitless/__main__.py +116 -0
- waitless-0.1.0/waitless/config.py +162 -0
- waitless-0.1.0/waitless/diagnostics.py +171 -0
- waitless-0.1.0/waitless/engine.py +275 -0
- waitless-0.1.0/waitless/exceptions.py +108 -0
- waitless-0.1.0/waitless/instrumentation.py +328 -0
- waitless-0.1.0/waitless/selenium_integration.py +296 -0
- waitless-0.1.0/waitless/signals.py +215 -0
- waitless-0.1.0/waitless.egg-info/PKG-INFO +243 -0
- waitless-0.1.0/waitless.egg-info/SOURCES.txt +17 -0
- waitless-0.1.0/waitless.egg-info/dependency_links.txt +1 -0
- waitless-0.1.0/waitless.egg-info/entry_points.txt +2 -0
- waitless-0.1.0/waitless.egg-info/requires.txt +4 -0
- waitless-0.1.0/waitless.egg-info/top_level.txt +1 -0
waitless-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: waitless
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Eliminate explicit waits in UI automation by detecting true UI stability
|
|
5
|
+
Author-email: Dhiraj Das <dhirajdas.66@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/godhiraj-code/waitless
|
|
8
|
+
Project-URL: Documentation, https://github.com/godhiraj-code/waitless#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/godhiraj-code/waitless.git
|
|
10
|
+
Project-URL: Issues, https://github.com/godhiraj-code/waitless/issues
|
|
11
|
+
Keywords: selenium,automation,testing,ui-testing,wait,stability,flaky-tests
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: selenium>=4.0; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# Waitless
|
|
30
|
+
|
|
31
|
+
**Zero-wait UI automation stabilization for Selenium**
|
|
32
|
+
|
|
33
|
+
Eliminate explicit waits and sleeps by automatically detecting true UI stability.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install waitless
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from selenium import webdriver
|
|
45
|
+
from selenium.webdriver.common.by import By
|
|
46
|
+
from waitless import stabilize
|
|
47
|
+
|
|
48
|
+
# Create driver as usual
|
|
49
|
+
driver = webdriver.Chrome()
|
|
50
|
+
|
|
51
|
+
# Enable automatic stabilization - ONE LINE
|
|
52
|
+
driver = stabilize(driver)
|
|
53
|
+
|
|
54
|
+
# All interactions now auto-wait for stability
|
|
55
|
+
driver.get("https://example.com")
|
|
56
|
+
driver.find_element(By.ID, "login-button").click() # ← Auto-waits!
|
|
57
|
+
driver.find_element(By.ID, "username").send_keys("user") # ← Auto-waits!
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Why Waitless?
|
|
61
|
+
|
|
62
|
+
### The Problem
|
|
63
|
+
|
|
64
|
+
Automation tests fail because interactions happen while the UI is still changing:
|
|
65
|
+
|
|
66
|
+
- DOM mutations from React/Vue/Angular updates
|
|
67
|
+
- In-flight AJAX requests
|
|
68
|
+
- CSS animations and transitions
|
|
69
|
+
- Layout shifts from lazy-loaded content
|
|
70
|
+
|
|
71
|
+
### Traditional Solutions (and why they fail)
|
|
72
|
+
|
|
73
|
+
| Approach | Problem |
|
|
74
|
+
|----------|---------|
|
|
75
|
+
| `time.sleep(2)` | Too slow, still fails sometimes |
|
|
76
|
+
| `WebDriverWait` | Only checks one element, misses page-wide state |
|
|
77
|
+
| Retries | Masks the real problem, adds flakiness |
|
|
78
|
+
|
|
79
|
+
### The Waitless Solution
|
|
80
|
+
|
|
81
|
+
Waitless monitors the **entire page** for stability signals:
|
|
82
|
+
|
|
83
|
+
- ✅ DOM mutation activity (MutationObserver)
|
|
84
|
+
- ✅ Pending network requests (XHR/fetch interception)
|
|
85
|
+
- ✅ CSS animations and transitions
|
|
86
|
+
- ✅ Layout stability (element movement)
|
|
87
|
+
|
|
88
|
+
When you interact, waitless ensures the page is truly ready.
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from waitless import stabilize, StabilizationConfig
|
|
94
|
+
|
|
95
|
+
config = StabilizationConfig(
|
|
96
|
+
timeout=5, # Max wait time (seconds)
|
|
97
|
+
dom_settle_time=0.1, # DOM quiet period needed
|
|
98
|
+
network_idle_threshold=0, # Max pending requests (0 = all must complete)
|
|
99
|
+
animation_detection=True, # Wait for animations to finish
|
|
100
|
+
strictness='normal', # 'strict' | 'normal' | 'relaxed'
|
|
101
|
+
debug_mode=True # Enable logging
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
driver = stabilize(driver, config=config)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Strictness Levels
|
|
108
|
+
|
|
109
|
+
| Level | What It Waits For |
|
|
110
|
+
|-------|-------------------|
|
|
111
|
+
| `strict` | DOM + Network + Animations + Layout |
|
|
112
|
+
| `normal` | DOM + Network (default) |
|
|
113
|
+
| `relaxed` | DOM only |
|
|
114
|
+
|
|
115
|
+
### Factory Methods
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# For strict testing
|
|
119
|
+
config = StabilizationConfig.strict()
|
|
120
|
+
|
|
121
|
+
# For apps with background traffic
|
|
122
|
+
config = StabilizationConfig.relaxed()
|
|
123
|
+
|
|
124
|
+
# For CI environments
|
|
125
|
+
config = StabilizationConfig.ci()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Manual Stabilization
|
|
129
|
+
|
|
130
|
+
If you don't want to wrap the driver:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from waitless import wait_for_stability
|
|
134
|
+
|
|
135
|
+
wait_for_stability(driver)
|
|
136
|
+
driver.find_element(By.ID, "button").click()
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Disabling Stabilization
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from waitless import unstabilize
|
|
143
|
+
|
|
144
|
+
driver = unstabilize(driver) # Back to original behavior
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Diagnostics
|
|
148
|
+
|
|
149
|
+
When tests fail, get detailed analysis:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from waitless import get_diagnostics, StabilizationTimeout
|
|
153
|
+
from waitless.diagnostics import print_report
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
driver.find_element(By.ID, "slow-button").click()
|
|
157
|
+
except StabilizationTimeout as e:
|
|
158
|
+
diagnostics = get_diagnostics(driver)
|
|
159
|
+
print_report(engine) # Print detailed report
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### CLI Doctor Command
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
python -m waitless doctor --file diagnostics.json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Sample output:
|
|
169
|
+
```
|
|
170
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
171
|
+
║ WAITLESS STABILITY REPORT ║
|
|
172
|
+
╠══════════════════════════════════════════════════════════════════╣
|
|
173
|
+
║ BLOCKING FACTORS: ║
|
|
174
|
+
║ ⚠ NETWORK: 2 request(s) still pending ║
|
|
175
|
+
║ → GET /api/users ║
|
|
176
|
+
║ ⚠ ANIMATIONS: 1 active animation(s) ║
|
|
177
|
+
╠══════════════════════════════════════════════════════════════════╣
|
|
178
|
+
║ SUGGESTIONS: ║
|
|
179
|
+
║ 1. Set network_idle_threshold=2 for background traffic ║
|
|
180
|
+
║ 2. Use animation_detection=False for infinite spinners ║
|
|
181
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Important Notes
|
|
185
|
+
|
|
186
|
+
### Network Threshold Warning
|
|
187
|
+
|
|
188
|
+
The default `network_idle_threshold=0` means **all** network requests must complete.
|
|
189
|
+
|
|
190
|
+
Many apps have background traffic that never stops:
|
|
191
|
+
- Analytics calls
|
|
192
|
+
- Long polling
|
|
193
|
+
- Feature flags
|
|
194
|
+
- WebSocket heartbeats
|
|
195
|
+
|
|
196
|
+
If tests timeout frequently, try:
|
|
197
|
+
```python
|
|
198
|
+
config = StabilizationConfig(network_idle_threshold=2)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Wrapped Elements
|
|
202
|
+
|
|
203
|
+
The stabilized driver returns wrapped elements that auto-wait. They behave like WebElements but:
|
|
204
|
+
|
|
205
|
+
- `isinstance(element, WebElement)` returns `False`
|
|
206
|
+
- Use `.unwrap()` to get the original element if needed
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
element = driver.find_element(By.ID, "button")
|
|
210
|
+
original = element.unwrap() # Gets the real WebElement
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## v0 Limitations
|
|
214
|
+
|
|
215
|
+
- **Selenium only** - Playwright support planned for v1
|
|
216
|
+
- **Sync only** - No async/await support yet
|
|
217
|
+
- **Main frame only** - iframes not monitored
|
|
218
|
+
- **No Shadow DOM** - MutationObserver doesn't see shadow roots
|
|
219
|
+
- **No Service Workers** - SW network requests not intercepted
|
|
220
|
+
|
|
221
|
+
## API Reference
|
|
222
|
+
|
|
223
|
+
### Functions
|
|
224
|
+
|
|
225
|
+
| Function | Description |
|
|
226
|
+
|----------|-------------|
|
|
227
|
+
| `stabilize(driver, config=None)` | Enable auto-stabilization |
|
|
228
|
+
| `unstabilize(driver)` | Disable and return original driver |
|
|
229
|
+
| `wait_for_stability(driver, timeout=None)` | Manual one-time wait |
|
|
230
|
+
| `get_diagnostics(driver)` | Get diagnostic data |
|
|
231
|
+
|
|
232
|
+
### Classes
|
|
233
|
+
|
|
234
|
+
| Class | Description |
|
|
235
|
+
|-------|-------------|
|
|
236
|
+
| `StabilizationConfig` | Configuration options |
|
|
237
|
+
| `StabilizedWebDriver` | Wrapped driver with auto-wait |
|
|
238
|
+
| `StabilizedWebElement` | Wrapped element with auto-wait |
|
|
239
|
+
| `StabilizationTimeout` | Exception when UI doesn't stabilize |
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT
|
waitless-0.1.0/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Waitless
|
|
2
|
+
|
|
3
|
+
**Zero-wait UI automation stabilization for Selenium**
|
|
4
|
+
|
|
5
|
+
Eliminate explicit waits and sleeps by automatically detecting true UI stability.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install waitless
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from selenium import webdriver
|
|
17
|
+
from selenium.webdriver.common.by import By
|
|
18
|
+
from waitless import stabilize
|
|
19
|
+
|
|
20
|
+
# Create driver as usual
|
|
21
|
+
driver = webdriver.Chrome()
|
|
22
|
+
|
|
23
|
+
# Enable automatic stabilization - ONE LINE
|
|
24
|
+
driver = stabilize(driver)
|
|
25
|
+
|
|
26
|
+
# All interactions now auto-wait for stability
|
|
27
|
+
driver.get("https://example.com")
|
|
28
|
+
driver.find_element(By.ID, "login-button").click() # ← Auto-waits!
|
|
29
|
+
driver.find_element(By.ID, "username").send_keys("user") # ← Auto-waits!
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Why Waitless?
|
|
33
|
+
|
|
34
|
+
### The Problem
|
|
35
|
+
|
|
36
|
+
Automation tests fail because interactions happen while the UI is still changing:
|
|
37
|
+
|
|
38
|
+
- DOM mutations from React/Vue/Angular updates
|
|
39
|
+
- In-flight AJAX requests
|
|
40
|
+
- CSS animations and transitions
|
|
41
|
+
- Layout shifts from lazy-loaded content
|
|
42
|
+
|
|
43
|
+
### Traditional Solutions (and why they fail)
|
|
44
|
+
|
|
45
|
+
| Approach | Problem |
|
|
46
|
+
|----------|---------|
|
|
47
|
+
| `time.sleep(2)` | Too slow, still fails sometimes |
|
|
48
|
+
| `WebDriverWait` | Only checks one element, misses page-wide state |
|
|
49
|
+
| Retries | Masks the real problem, adds flakiness |
|
|
50
|
+
|
|
51
|
+
### The Waitless Solution
|
|
52
|
+
|
|
53
|
+
Waitless monitors the **entire page** for stability signals:
|
|
54
|
+
|
|
55
|
+
- ✅ DOM mutation activity (MutationObserver)
|
|
56
|
+
- ✅ Pending network requests (XHR/fetch interception)
|
|
57
|
+
- ✅ CSS animations and transitions
|
|
58
|
+
- ✅ Layout stability (element movement)
|
|
59
|
+
|
|
60
|
+
When you interact, waitless ensures the page is truly ready.
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from waitless import stabilize, StabilizationConfig
|
|
66
|
+
|
|
67
|
+
config = StabilizationConfig(
|
|
68
|
+
timeout=5, # Max wait time (seconds)
|
|
69
|
+
dom_settle_time=0.1, # DOM quiet period needed
|
|
70
|
+
network_idle_threshold=0, # Max pending requests (0 = all must complete)
|
|
71
|
+
animation_detection=True, # Wait for animations to finish
|
|
72
|
+
strictness='normal', # 'strict' | 'normal' | 'relaxed'
|
|
73
|
+
debug_mode=True # Enable logging
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
driver = stabilize(driver, config=config)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Strictness Levels
|
|
80
|
+
|
|
81
|
+
| Level | What It Waits For |
|
|
82
|
+
|-------|-------------------|
|
|
83
|
+
| `strict` | DOM + Network + Animations + Layout |
|
|
84
|
+
| `normal` | DOM + Network (default) |
|
|
85
|
+
| `relaxed` | DOM only |
|
|
86
|
+
|
|
87
|
+
### Factory Methods
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# For strict testing
|
|
91
|
+
config = StabilizationConfig.strict()
|
|
92
|
+
|
|
93
|
+
# For apps with background traffic
|
|
94
|
+
config = StabilizationConfig.relaxed()
|
|
95
|
+
|
|
96
|
+
# For CI environments
|
|
97
|
+
config = StabilizationConfig.ci()
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Manual Stabilization
|
|
101
|
+
|
|
102
|
+
If you don't want to wrap the driver:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from waitless import wait_for_stability
|
|
106
|
+
|
|
107
|
+
wait_for_stability(driver)
|
|
108
|
+
driver.find_element(By.ID, "button").click()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Disabling Stabilization
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from waitless import unstabilize
|
|
115
|
+
|
|
116
|
+
driver = unstabilize(driver) # Back to original behavior
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Diagnostics
|
|
120
|
+
|
|
121
|
+
When tests fail, get detailed analysis:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from waitless import get_diagnostics, StabilizationTimeout
|
|
125
|
+
from waitless.diagnostics import print_report
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
driver.find_element(By.ID, "slow-button").click()
|
|
129
|
+
except StabilizationTimeout as e:
|
|
130
|
+
diagnostics = get_diagnostics(driver)
|
|
131
|
+
print_report(engine) # Print detailed report
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### CLI Doctor Command
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
python -m waitless doctor --file diagnostics.json
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Sample output:
|
|
141
|
+
```
|
|
142
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
143
|
+
║ WAITLESS STABILITY REPORT ║
|
|
144
|
+
╠══════════════════════════════════════════════════════════════════╣
|
|
145
|
+
║ BLOCKING FACTORS: ║
|
|
146
|
+
║ ⚠ NETWORK: 2 request(s) still pending ║
|
|
147
|
+
║ → GET /api/users ║
|
|
148
|
+
║ ⚠ ANIMATIONS: 1 active animation(s) ║
|
|
149
|
+
╠══════════════════════════════════════════════════════════════════╣
|
|
150
|
+
║ SUGGESTIONS: ║
|
|
151
|
+
║ 1. Set network_idle_threshold=2 for background traffic ║
|
|
152
|
+
║ 2. Use animation_detection=False for infinite spinners ║
|
|
153
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Important Notes
|
|
157
|
+
|
|
158
|
+
### Network Threshold Warning
|
|
159
|
+
|
|
160
|
+
The default `network_idle_threshold=0` means **all** network requests must complete.
|
|
161
|
+
|
|
162
|
+
Many apps have background traffic that never stops:
|
|
163
|
+
- Analytics calls
|
|
164
|
+
- Long polling
|
|
165
|
+
- Feature flags
|
|
166
|
+
- WebSocket heartbeats
|
|
167
|
+
|
|
168
|
+
If tests timeout frequently, try:
|
|
169
|
+
```python
|
|
170
|
+
config = StabilizationConfig(network_idle_threshold=2)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Wrapped Elements
|
|
174
|
+
|
|
175
|
+
The stabilized driver returns wrapped elements that auto-wait. They behave like WebElements but:
|
|
176
|
+
|
|
177
|
+
- `isinstance(element, WebElement)` returns `False`
|
|
178
|
+
- Use `.unwrap()` to get the original element if needed
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
element = driver.find_element(By.ID, "button")
|
|
182
|
+
original = element.unwrap() # Gets the real WebElement
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## v0 Limitations
|
|
186
|
+
|
|
187
|
+
- **Selenium only** - Playwright support planned for v1
|
|
188
|
+
- **Sync only** - No async/await support yet
|
|
189
|
+
- **Main frame only** - iframes not monitored
|
|
190
|
+
- **No Shadow DOM** - MutationObserver doesn't see shadow roots
|
|
191
|
+
- **No Service Workers** - SW network requests not intercepted
|
|
192
|
+
|
|
193
|
+
## API Reference
|
|
194
|
+
|
|
195
|
+
### Functions
|
|
196
|
+
|
|
197
|
+
| Function | Description |
|
|
198
|
+
|----------|-------------|
|
|
199
|
+
| `stabilize(driver, config=None)` | Enable auto-stabilization |
|
|
200
|
+
| `unstabilize(driver)` | Disable and return original driver |
|
|
201
|
+
| `wait_for_stability(driver, timeout=None)` | Manual one-time wait |
|
|
202
|
+
| `get_diagnostics(driver)` | Get diagnostic data |
|
|
203
|
+
|
|
204
|
+
### Classes
|
|
205
|
+
|
|
206
|
+
| Class | Description |
|
|
207
|
+
|-------|-------------|
|
|
208
|
+
| `StabilizationConfig` | Configuration options |
|
|
209
|
+
| `StabilizedWebDriver` | Wrapped driver with auto-wait |
|
|
210
|
+
| `StabilizedWebElement` | Wrapped element with auto-wait |
|
|
211
|
+
| `StabilizationTimeout` | Exception when UI doesn't stabilize |
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "waitless"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Eliminate explicit waits in UI automation by detecting true UI stability"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Dhiraj Das", email = "dhirajdas.66@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"selenium",
|
|
17
|
+
"automation",
|
|
18
|
+
"testing",
|
|
19
|
+
"ui-testing",
|
|
20
|
+
"wait",
|
|
21
|
+
"stability",
|
|
22
|
+
"flaky-tests"
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 4 - Beta",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.9",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Topic :: Software Development :: Testing",
|
|
35
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/godhiraj-code/waitless"
|
|
40
|
+
Documentation = "https://github.com/godhiraj-code/waitless#readme"
|
|
41
|
+
Repository = "https://github.com/godhiraj-code/waitless.git"
|
|
42
|
+
Issues = "https://github.com/godhiraj-code/waitless/issues"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
waitless = "waitless.__main__:main"
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=7.0",
|
|
50
|
+
"selenium>=4.0",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
where = ["."]
|
|
55
|
+
include = ["waitless*"]
|
waitless-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Waitless - Zero-wait UI automation stabilization library.
|
|
3
|
+
|
|
4
|
+
Eliminate explicit waits and sleeps in UI automation by automatically
|
|
5
|
+
waiting for true UI stability instead of time-based conditions.
|
|
6
|
+
|
|
7
|
+
Basic Usage:
|
|
8
|
+
from waitless import stabilize
|
|
9
|
+
|
|
10
|
+
driver = webdriver.Chrome()
|
|
11
|
+
driver = stabilize(driver) # That's it!
|
|
12
|
+
|
|
13
|
+
# All interactions now auto-wait for stability
|
|
14
|
+
driver.find_element(By.ID, "button").click()
|
|
15
|
+
|
|
16
|
+
Configuration:
|
|
17
|
+
from waitless import stabilize, StabilizationConfig
|
|
18
|
+
|
|
19
|
+
config = StabilizationConfig(
|
|
20
|
+
timeout=5, # Max wait time
|
|
21
|
+
strictness='strict', # All signals must be stable
|
|
22
|
+
debug_mode=True # Enable logging
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
driver = stabilize(driver, config=config)
|
|
26
|
+
|
|
27
|
+
Manual Stabilization:
|
|
28
|
+
from waitless import wait_for_stability
|
|
29
|
+
|
|
30
|
+
wait_for_stability(driver) # Explicit wait
|
|
31
|
+
driver.find_element(...).click()
|
|
32
|
+
|
|
33
|
+
Disable:
|
|
34
|
+
from waitless import unstabilize
|
|
35
|
+
|
|
36
|
+
driver = unstabilize(driver) # Back to original behavior
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
__version__ = '0.1.0'
|
|
40
|
+
__author__ = 'Dhiraj Das'
|
|
41
|
+
|
|
42
|
+
# Public API
|
|
43
|
+
from .config import StabilizationConfig, DEFAULT_CONFIG
|
|
44
|
+
from .selenium_integration import (
|
|
45
|
+
stabilize,
|
|
46
|
+
unstabilize,
|
|
47
|
+
wait_for_stability,
|
|
48
|
+
get_diagnostics,
|
|
49
|
+
StabilizedWebDriver,
|
|
50
|
+
StabilizedWebElement,
|
|
51
|
+
)
|
|
52
|
+
from .exceptions import (
|
|
53
|
+
WaitlessError,
|
|
54
|
+
StabilizationTimeout,
|
|
55
|
+
InstrumentationError,
|
|
56
|
+
ConfigurationError,
|
|
57
|
+
NotStabilizedError,
|
|
58
|
+
)
|
|
59
|
+
from .engine import StabilizationEngine
|
|
60
|
+
from .diagnostics import DiagnosticReport, generate_report, print_report
|
|
61
|
+
|
|
62
|
+
__all__ = [
|
|
63
|
+
# Version
|
|
64
|
+
'__version__',
|
|
65
|
+
|
|
66
|
+
# Main API
|
|
67
|
+
'stabilize',
|
|
68
|
+
'unstabilize',
|
|
69
|
+
'wait_for_stability',
|
|
70
|
+
'get_diagnostics',
|
|
71
|
+
|
|
72
|
+
# Configuration
|
|
73
|
+
'StabilizationConfig',
|
|
74
|
+
'DEFAULT_CONFIG',
|
|
75
|
+
|
|
76
|
+
# Types
|
|
77
|
+
'StabilizedWebDriver',
|
|
78
|
+
'StabilizedWebElement',
|
|
79
|
+
'StabilizationEngine',
|
|
80
|
+
|
|
81
|
+
# Exceptions
|
|
82
|
+
'WaitlessError',
|
|
83
|
+
'StabilizationTimeout',
|
|
84
|
+
'InstrumentationError',
|
|
85
|
+
'ConfigurationError',
|
|
86
|
+
'NotStabilizedError',
|
|
87
|
+
|
|
88
|
+
# Diagnostics
|
|
89
|
+
'DiagnosticReport',
|
|
90
|
+
'generate_report',
|
|
91
|
+
'print_report',
|
|
92
|
+
]
|