wup 0.2.14__tar.gz → 0.2.16__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 (42) hide show
  1. {wup-0.2.14/wup.egg-info → wup-0.2.16}/PKG-INFO +138 -56
  2. {wup-0.2.14 → wup-0.2.16}/README.md +137 -55
  3. {wup-0.2.14 → wup-0.2.16}/pyproject.toml +1 -1
  4. {wup-0.2.14 → wup-0.2.16}/tests/test_testql_watcher.py +55 -1
  5. wup-0.2.16/tests/test_web_client.py +167 -0
  6. {wup-0.2.14 → wup-0.2.16}/tests/test_wup.py +278 -0
  7. {wup-0.2.14 → wup-0.2.16}/wup/__init__.py +1 -1
  8. {wup-0.2.14 → wup-0.2.16}/wup/config.py +41 -3
  9. {wup-0.2.14 → wup-0.2.16}/wup/models/config.py +11 -0
  10. {wup-0.2.14 → wup-0.2.16}/wup/testql_watcher.py +16 -0
  11. wup-0.2.16/wup/web_client.py +178 -0
  12. wup-0.2.16/wup-web/tests/__init__.py +0 -0
  13. wup-0.2.16/wup-web/tests/conftest.py +23 -0
  14. wup-0.2.16/wup-web/tests/test_dashboard.py +35 -0
  15. wup-0.2.16/wup-web/tests/test_drivers.py +50 -0
  16. wup-0.2.16/wup-web/tests/test_events.py +95 -0
  17. wup-0.2.16/wup-web/wup_web/__init__.py +7 -0
  18. wup-0.2.16/wup-web/wup_web/__main__.py +21 -0
  19. wup-0.2.16/wup-web/wup_web/main.py +44 -0
  20. wup-0.2.16/wup-web/wup_web/models.py +59 -0
  21. wup-0.2.16/wup-web/wup_web/routers/__init__.py +1 -0
  22. wup-0.2.16/wup-web/wup_web/routers/dashboard.py +24 -0
  23. wup-0.2.16/wup-web/wup_web/routers/drivers.py +129 -0
  24. wup-0.2.16/wup-web/wup_web/routers/events.py +48 -0
  25. wup-0.2.16/wup-web/wup_web/storage.py +110 -0
  26. {wup-0.2.14 → wup-0.2.16/wup.egg-info}/PKG-INFO +138 -56
  27. wup-0.2.16/wup.egg-info/SOURCES.txt +38 -0
  28. wup-0.2.16/wup.egg-info/top_level.txt +2 -0
  29. wup-0.2.14/wup.egg-info/SOURCES.txt +0 -22
  30. wup-0.2.14/wup.egg-info/top_level.txt +0 -1
  31. {wup-0.2.14 → wup-0.2.16}/LICENSE +0 -0
  32. {wup-0.2.14 → wup-0.2.16}/setup.cfg +0 -0
  33. {wup-0.2.14 → wup-0.2.16}/tests/test_e2e.py +0 -0
  34. {wup-0.2.14 → wup-0.2.16}/wup/cli.py +0 -0
  35. {wup-0.2.14 → wup-0.2.16}/wup/core.py +0 -0
  36. {wup-0.2.14 → wup-0.2.16}/wup/dependency_mapper.py +0 -0
  37. {wup-0.2.14 → wup-0.2.16}/wup/models/__init__.py +0 -0
  38. {wup-0.2.14 → wup-0.2.16}/wup/testql_discovery.py +0 -0
  39. {wup-0.2.14 → wup-0.2.16}/wup/visual_diff.py +0 -0
  40. {wup-0.2.14 → wup-0.2.16}/wup.egg-info/dependency_links.txt +0 -0
  41. {wup-0.2.14 → wup-0.2.16}/wup.egg-info/entry_points.txt +0 -0
  42. {wup-0.2.14 → wup-0.2.16}/wup.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wup
3
- Version: 0.2.14
3
+ Version: 0.2.16
4
4
  Summary: WUP (What's Up) - Intelligent file watcher for regression testing in large projects
5
5
  Author-email: Tom Sapletta <tom@sapletta.com>
6
6
  License: Apache-2.0
@@ -29,17 +29,17 @@ Dynamic: license-file
29
29
 
30
30
  ## AI Cost Tracking
31
31
 
32
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.14-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
33
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.25-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-3.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
32
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
33
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.55-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-4.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
34
34
 
35
- - 🤖 **LLM usage:** $2.2500 (15 commits)
36
- - 👤 **Human dev:** ~$324 (3.2h @ $100/h, 30min dedup)
35
+ - 🤖 **LLM usage:** $2.5500 (17 commits)
36
+ - 👤 **Human dev:** ~$445 (4.5h @ $100/h, 30min dedup)
37
37
 
38
38
  Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
39
39
 
40
40
  ---
41
41
 
42
- ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.14-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
42
+ ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
43
43
 
44
44
  **WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
45
45
 
@@ -285,56 +285,128 @@ export WUP_CPU_THROTTLE=0.5
285
285
  export WUP_DEBOUNCE=3
286
286
  ```
287
287
 
288
- ## Integration with Test Frameworks
289
-
290
- WUP is designed to work with any test framework. The current implementation includes placeholder test methods that you can customize:
291
-
292
- ```python
293
- # In wup/core.py, customize these methods:
294
-
295
- async def run_quick_test(self, service: str, endpoints: List[str]) -> bool:
296
- # Integrate with your test framework (pytest, unittest, TestQL, etc.)
297
- # Example:
298
- result = subprocess.run([
299
- "pytest", f"tests/{service}/test_smoke.py",
300
- "--maxfail=1", "-q"
301
- ])
302
- return result.returncode == 0
303
-
304
- async def run_detail_test(self, service: str, endpoints: List[str]) -> Dict:
305
- # Run full test suite with blame reporting
306
- # Example:
307
- result = subprocess.run([
308
- "pytest", f"tests/{service}/",
309
- "--cov", f"app/{service}",
310
- "--cov-report=json"
311
- ])
312
- return parse_coverage_report("coverage.json")
288
+ ## Visual DOM Diff
289
+
290
+ WUP optionally scans configured pages with Playwright after each successful quick test, compares the DOM structure to the previous snapshot, and reports significant changes.
291
+
292
+ ### Setup
293
+
294
+ ```bash
295
+ pip install playwright
296
+ playwright install chromium
313
297
  ```
314
298
 
299
+ ### Configuration
300
+
301
+ ```yaml
302
+ visual_diff:
303
+ enabled: true
304
+ base_url: "http://localhost:8100" # or leave empty and set WUP_BASE_URL env var
305
+ base_url_env: "WUP_BASE_URL"
306
+ delay_seconds: 5.0 # wait after file change before scanning
307
+ max_depth: 10 # DOM snapshot depth
308
+ pages:
309
+ - "/health"
310
+ - "/dashboard"
311
+ pages_from_endpoints: true # also scan endpoints from testql config
312
+ threshold_added: 3 # min node additions to report as "changed"
313
+ threshold_removed: 3
314
+ threshold_changed: 5
315
+ headless: true
316
+ ```
317
+
318
+ Or set the base URL in `.wup.env` in the project root (not committed to git):
319
+
320
+ ```bash
321
+ # .wup.env
322
+ WUP_BASE_URL=http://localhost:8100
323
+ ```
324
+
325
+ ### Output
326
+
327
+ - **Snapshots** — `.wup/visual-snapshots/<service>/<page>.json`
328
+ - **Diff events** — `.wup/visual-diffs/<service>/<page>.jsonl` (appended on each change)
329
+
330
+ Visible in `wup status` as a "Visual DOM diffs" section.
331
+
332
+ ### Graceful degradation
333
+
334
+ If Playwright is not installed, the visual diff module logs a warning and skips scanning — it does **not** break the watcher.
335
+
336
+ ## Web Dashboard (wup-web)
337
+
338
+ Optional FastAPI backend that receives events from WUP agents and renders a live dashboard.
339
+
340
+ ### Run
341
+
342
+ ```bash
343
+ pip install -e wup-web/
344
+ wup-web --reload --port 8000
345
+ ```
346
+
347
+ Open <http://localhost:8000/> to see regressions, passes, anomalies, visual diffs, and health transitions in real time.
348
+
349
+ ### Configure agent → backend
350
+
351
+ ```yaml
352
+ # wup.yaml
353
+ web:
354
+ enabled: true
355
+ endpoint: "http://localhost:8000"
356
+ timeout_s: 2.0
357
+ ```
358
+
359
+ Or via env:
360
+
361
+ ```bash
362
+ export WUP_WEB_ENDPOINT=http://localhost:8000
363
+ ```
364
+
365
+ The agent fire-and-forgets `REGRESSION`, `PASS`, `ANOMALY`, `VISUAL_DIFF`, and `HEALTH_TRANSITION` events. Network errors never break the watcher (soft-fail).
366
+
367
+ See `wup-web/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
368
+
315
369
  ## Project Structure
316
370
 
317
371
  ```
318
372
  wup/
319
373
  ├── wup/
320
- │ ├── __init__.py # Package exports
321
- │ ├── config.py # Configuration loader
322
- │ ├── models/
323
- ├── __init__.py # Models package
324
- │ └── config.py # Configuration dataclasses
325
- │ ├── core.py # WupWatcher implementation
326
- │ ├── dependency_mapper.py # Dependency mapping logic
327
- │ ├── testql_watcher.py # TestQL integration
328
- └── cli.py # CLI interface
374
+ │ ├── __init__.py # Package exports
375
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints
376
+ │ ├── config.py # Config loading/saving + .wup.env support
377
+ │ ├── core.py # WupWatcher: detection, inference, scheduling
378
+ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
379
+ │ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
380
+ │ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
381
+ │ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
382
+ ├── web_client.py # WebClient: async HTTP event sink → wup-web
383
+ │ └── models/
384
+ │ ├── __init__.py
385
+ │ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, WebConfig...
386
+ ├── wup-web/ # Optional FastAPI dashboard (separate package)
387
+ │ ├── wup_web/
388
+ │ │ ├── main.py # FastAPI app
389
+ │ │ ├── routers/ # events, drivers, dashboard
390
+ │ │ ├── storage.py # EventStore (in-memory + JSONL)
391
+ │ │ └── templates/ # index.html dashboard
392
+ │ └── tests/ # FastAPI endpoint tests (pytest + TestClient)
329
393
  ├── tests/
330
- └── test_wup.py # Unit tests
394
+ ├── test_wup.py # unit/integration tests (incl. VisualDiffer, config)
395
+ │ ├── test_testql_watcher.py # TestQLWatcher + VisualDiffer integration tests
396
+ │ ├── test_web_client.py # WebClient + WebConfig tests
397
+ │ └── test_e2e.py # end-to-end CLI tests
398
+ ├── examples/
399
+ │ ├── fastapi-app/ # FastAPI example project
400
+ │ ├── flask-app/ # Flask example project
401
+ │ ├── multi-service/ # Multi-service example
402
+ │ ├── testql_demo.py # TestQL simulation demo
403
+ │ ├── testql_integration.py # Custom TestQLWatcher + visual diff example
404
+ │ └── visual_diff_demo.py # Visual DOM diff demo (no Playwright required)
331
405
  ├── docs/
332
- ├── 2.md # Refactoring documentation
333
- ├── 3.md # Configuration plan
334
- │ └── TESTQL_INTEGRATION.md # TestQL integration docs
335
- ├── wup.yaml.example # Example configuration
336
- ├── pyproject.toml # Package configuration
337
- └── README.md # This file
406
+ └── TESTQL_INTEGRATION.md # TestQL integration guide
407
+ ├── testql-scenarios/ # Auto-generated TestQL scenarios
408
+ ├── pyproject.toml # Package config (setuptools)
409
+ └── README.md
338
410
  ```
339
411
 
340
412
  ## Development
@@ -343,23 +415,33 @@ wup/
343
415
 
344
416
  ```bash
345
417
  # Run all tests
346
- pytest
418
+ python3 -m pytest tests/ -v
419
+
420
+ # Run specific suite
421
+ python3 -m pytest tests/test_wup.py -v
422
+ python3 -m pytest tests/test_testql_watcher.py -v
347
423
 
348
424
  # Run with coverage
349
- pytest --cov=wup
425
+ python3 -m pytest tests/ --cov=wup
426
+ ```
350
427
 
351
- # Run specific test
352
- pytest tests/test_wup.py::TestDependencyMapper::test_init
428
+ ### Examples
429
+
430
+ ```bash
431
+ # Visual diff demo (no Playwright required)
432
+ python3 examples/visual_diff_demo.py
433
+
434
+ # With live page scan (requires playwright)
435
+ python3 examples/visual_diff_demo.py http://localhost:8100/health
436
+
437
+ # TestQL + visual diff integration
438
+ python3 examples/testql_integration.py /path/to/project
353
439
  ```
354
440
 
355
- ### Building for Distribution
441
+ ### Building & Publishing
356
442
 
357
443
  ```bash
358
- # Build wheel and source distribution
359
444
  python -m build
360
-
361
- # Install from dist
362
- pip install dist/wup-0.1.6-py3-none-any.whl
363
445
  ```
364
446
 
365
447
  ## License
@@ -3,17 +3,17 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.14-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.25-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-3.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.55-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-4.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $2.2500 (15 commits)
10
- - 👤 **Human dev:** ~$324 (3.2h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $2.5500 (17 commits)
10
+ - 👤 **Human dev:** ~$445 (4.5h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
- ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.14-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
16
+ ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
17
17
 
18
18
  **WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
19
19
 
@@ -259,56 +259,128 @@ export WUP_CPU_THROTTLE=0.5
259
259
  export WUP_DEBOUNCE=3
260
260
  ```
261
261
 
262
- ## Integration with Test Frameworks
263
-
264
- WUP is designed to work with any test framework. The current implementation includes placeholder test methods that you can customize:
265
-
266
- ```python
267
- # In wup/core.py, customize these methods:
268
-
269
- async def run_quick_test(self, service: str, endpoints: List[str]) -> bool:
270
- # Integrate with your test framework (pytest, unittest, TestQL, etc.)
271
- # Example:
272
- result = subprocess.run([
273
- "pytest", f"tests/{service}/test_smoke.py",
274
- "--maxfail=1", "-q"
275
- ])
276
- return result.returncode == 0
277
-
278
- async def run_detail_test(self, service: str, endpoints: List[str]) -> Dict:
279
- # Run full test suite with blame reporting
280
- # Example:
281
- result = subprocess.run([
282
- "pytest", f"tests/{service}/",
283
- "--cov", f"app/{service}",
284
- "--cov-report=json"
285
- ])
286
- return parse_coverage_report("coverage.json")
262
+ ## Visual DOM Diff
263
+
264
+ WUP optionally scans configured pages with Playwright after each successful quick test, compares the DOM structure to the previous snapshot, and reports significant changes.
265
+
266
+ ### Setup
267
+
268
+ ```bash
269
+ pip install playwright
270
+ playwright install chromium
287
271
  ```
288
272
 
273
+ ### Configuration
274
+
275
+ ```yaml
276
+ visual_diff:
277
+ enabled: true
278
+ base_url: "http://localhost:8100" # or leave empty and set WUP_BASE_URL env var
279
+ base_url_env: "WUP_BASE_URL"
280
+ delay_seconds: 5.0 # wait after file change before scanning
281
+ max_depth: 10 # DOM snapshot depth
282
+ pages:
283
+ - "/health"
284
+ - "/dashboard"
285
+ pages_from_endpoints: true # also scan endpoints from testql config
286
+ threshold_added: 3 # min node additions to report as "changed"
287
+ threshold_removed: 3
288
+ threshold_changed: 5
289
+ headless: true
290
+ ```
291
+
292
+ Or set the base URL in `.wup.env` in the project root (not committed to git):
293
+
294
+ ```bash
295
+ # .wup.env
296
+ WUP_BASE_URL=http://localhost:8100
297
+ ```
298
+
299
+ ### Output
300
+
301
+ - **Snapshots** — `.wup/visual-snapshots/<service>/<page>.json`
302
+ - **Diff events** — `.wup/visual-diffs/<service>/<page>.jsonl` (appended on each change)
303
+
304
+ Visible in `wup status` as a "Visual DOM diffs" section.
305
+
306
+ ### Graceful degradation
307
+
308
+ If Playwright is not installed, the visual diff module logs a warning and skips scanning — it does **not** break the watcher.
309
+
310
+ ## Web Dashboard (wup-web)
311
+
312
+ Optional FastAPI backend that receives events from WUP agents and renders a live dashboard.
313
+
314
+ ### Run
315
+
316
+ ```bash
317
+ pip install -e wup-web/
318
+ wup-web --reload --port 8000
319
+ ```
320
+
321
+ Open <http://localhost:8000/> to see regressions, passes, anomalies, visual diffs, and health transitions in real time.
322
+
323
+ ### Configure agent → backend
324
+
325
+ ```yaml
326
+ # wup.yaml
327
+ web:
328
+ enabled: true
329
+ endpoint: "http://localhost:8000"
330
+ timeout_s: 2.0
331
+ ```
332
+
333
+ Or via env:
334
+
335
+ ```bash
336
+ export WUP_WEB_ENDPOINT=http://localhost:8000
337
+ ```
338
+
339
+ The agent fire-and-forgets `REGRESSION`, `PASS`, `ANOMALY`, `VISUAL_DIFF`, and `HEALTH_TRANSITION` events. Network errors never break the watcher (soft-fail).
340
+
341
+ See `wup-web/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
342
+
289
343
  ## Project Structure
290
344
 
291
345
  ```
292
346
  wup/
293
347
  ├── wup/
294
- │ ├── __init__.py # Package exports
295
- │ ├── config.py # Configuration loader
296
- │ ├── models/
297
- ├── __init__.py # Models package
298
- │ └── config.py # Configuration dataclasses
299
- │ ├── core.py # WupWatcher implementation
300
- │ ├── dependency_mapper.py # Dependency mapping logic
301
- │ ├── testql_watcher.py # TestQL integration
302
- └── cli.py # CLI interface
348
+ │ ├── __init__.py # Package exports
349
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints
350
+ │ ├── config.py # Config loading/saving + .wup.env support
351
+ │ ├── core.py # WupWatcher: detection, inference, scheduling
352
+ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
353
+ │ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
354
+ │ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
355
+ │ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
356
+ ├── web_client.py # WebClient: async HTTP event sink → wup-web
357
+ │ └── models/
358
+ │ ├── __init__.py
359
+ │ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, WebConfig...
360
+ ├── wup-web/ # Optional FastAPI dashboard (separate package)
361
+ │ ├── wup_web/
362
+ │ │ ├── main.py # FastAPI app
363
+ │ │ ├── routers/ # events, drivers, dashboard
364
+ │ │ ├── storage.py # EventStore (in-memory + JSONL)
365
+ │ │ └── templates/ # index.html dashboard
366
+ │ └── tests/ # FastAPI endpoint tests (pytest + TestClient)
303
367
  ├── tests/
304
- └── test_wup.py # Unit tests
368
+ ├── test_wup.py # unit/integration tests (incl. VisualDiffer, config)
369
+ │ ├── test_testql_watcher.py # TestQLWatcher + VisualDiffer integration tests
370
+ │ ├── test_web_client.py # WebClient + WebConfig tests
371
+ │ └── test_e2e.py # end-to-end CLI tests
372
+ ├── examples/
373
+ │ ├── fastapi-app/ # FastAPI example project
374
+ │ ├── flask-app/ # Flask example project
375
+ │ ├── multi-service/ # Multi-service example
376
+ │ ├── testql_demo.py # TestQL simulation demo
377
+ │ ├── testql_integration.py # Custom TestQLWatcher + visual diff example
378
+ │ └── visual_diff_demo.py # Visual DOM diff demo (no Playwright required)
305
379
  ├── docs/
306
- ├── 2.md # Refactoring documentation
307
- ├── 3.md # Configuration plan
308
- │ └── TESTQL_INTEGRATION.md # TestQL integration docs
309
- ├── wup.yaml.example # Example configuration
310
- ├── pyproject.toml # Package configuration
311
- └── README.md # This file
380
+ └── TESTQL_INTEGRATION.md # TestQL integration guide
381
+ ├── testql-scenarios/ # Auto-generated TestQL scenarios
382
+ ├── pyproject.toml # Package config (setuptools)
383
+ └── README.md
312
384
  ```
313
385
 
314
386
  ## Development
@@ -317,23 +389,33 @@ wup/
317
389
 
318
390
  ```bash
319
391
  # Run all tests
320
- pytest
392
+ python3 -m pytest tests/ -v
393
+
394
+ # Run specific suite
395
+ python3 -m pytest tests/test_wup.py -v
396
+ python3 -m pytest tests/test_testql_watcher.py -v
321
397
 
322
398
  # Run with coverage
323
- pytest --cov=wup
399
+ python3 -m pytest tests/ --cov=wup
400
+ ```
324
401
 
325
- # Run specific test
326
- pytest tests/test_wup.py::TestDependencyMapper::test_init
402
+ ### Examples
403
+
404
+ ```bash
405
+ # Visual diff demo (no Playwright required)
406
+ python3 examples/visual_diff_demo.py
407
+
408
+ # With live page scan (requires playwright)
409
+ python3 examples/visual_diff_demo.py http://localhost:8100/health
410
+
411
+ # TestQL + visual diff integration
412
+ python3 examples/testql_integration.py /path/to/project
327
413
  ```
328
414
 
329
- ### Building for Distribution
415
+ ### Building & Publishing
330
416
 
331
417
  ```bash
332
- # Build wheel and source distribution
333
418
  python -m build
334
-
335
- # Install from dist
336
- pip install dist/wup-0.1.6-py3-none-any.whl
337
419
  ```
338
420
 
339
421
  ## License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wup"
7
- version = "0.2.14"
7
+ version = "0.2.16"
8
8
  description = "WUP (What's Up) - Intelligent file watcher for regression testing in large projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from subprocess import CompletedProcess
7
7
 
8
8
  from wup.testql_watcher import TestQLWatcher
9
- from wup.models.config import WupConfig, ProjectConfig, TestQLConfig
9
+ from wup.models.config import WupConfig, ProjectConfig, TestQLConfig, VisualDiffConfig
10
10
 
11
11
 
12
12
  def test_process_changed_file_creates_track_on_failure():
@@ -199,3 +199,57 @@ def test_service_health_transitions_are_persisted():
199
199
  statuses = [event.get("status") for event in events if event.get("service") == "connect-config"]
200
200
  assert "down" in statuses
201
201
  assert "up" in statuses
202
+
203
+
204
+ def test_visual_differ_disabled_by_default():
205
+ """visual_differ exists but is disabled (no-op) when visual_diff.enabled=False."""
206
+ with tempfile.TemporaryDirectory() as tmpdir:
207
+ root = Path(tmpdir)
208
+ cfg = WupConfig(
209
+ project=ProjectConfig(name="demo"),
210
+ testql=TestQLConfig(scenario_dir="testql-scenarios"),
211
+ visual_diff=VisualDiffConfig(enabled=False),
212
+ )
213
+
214
+ watcher = TestQLWatcher(
215
+ project_root=str(root),
216
+ deps_file=str(root / "deps.json"),
217
+ scenarios_dir="testql-scenarios",
218
+ track_dir=".wup/tracks",
219
+ config=cfg,
220
+ )
221
+
222
+ # Differ is created but flagged disabled — run_for_service() must be a no-op
223
+ assert watcher.visual_differ is not None
224
+ assert watcher.visual_differ.cfg.enabled is False
225
+ results = asyncio.run(watcher.visual_differ.run_for_service("svc", ["/x"]))
226
+ assert results == []
227
+
228
+
229
+ def test_visual_differ_initialized_when_enabled():
230
+ """When visual_diff.enabled=True, TestQLWatcher.visual_differ is a VisualDiffer."""
231
+ from wup.visual_diff import VisualDiffer
232
+
233
+ with tempfile.TemporaryDirectory() as tmpdir:
234
+ root = Path(tmpdir)
235
+ cfg = WupConfig(
236
+ project=ProjectConfig(name="demo"),
237
+ testql=TestQLConfig(scenario_dir="testql-scenarios"),
238
+ visual_diff=VisualDiffConfig(
239
+ enabled=True,
240
+ base_url="http://localhost:9000",
241
+ pages=["/dashboard"],
242
+ ),
243
+ )
244
+
245
+ watcher = TestQLWatcher(
246
+ project_root=str(root),
247
+ deps_file=str(root / "deps.json"),
248
+ scenarios_dir="testql-scenarios",
249
+ track_dir=".wup/tracks",
250
+ config=cfg,
251
+ )
252
+
253
+ assert isinstance(watcher.visual_differ, VisualDiffer)
254
+ assert watcher.visual_differ.cfg.enabled is True
255
+ assert watcher.visual_differ.base_url == "http://localhost:9000"