wup 0.2.13__tar.gz → 0.2.15__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: wup
3
- Version: 0.2.13
3
+ Version: 0.2.15
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.13-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.10-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.15-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.40-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-4.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
34
34
 
35
- - 🤖 **LLM usage:** $2.1000 (14 commits)
36
- - 👤 **Human dev:** ~$319 (3.2h @ $100/h, 30min dedup)
35
+ - 🤖 **LLM usage:** $2.4000 (16 commits)
36
+ - 👤 **Human dev:** ~$411 (4.1h @ $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.13-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.15-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,86 @@ 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
297
+ ```
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
313
323
  ```
314
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
+
315
336
  ## Project Structure
316
337
 
317
338
  ```
318
339
  wup/
319
340
  ├── 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
341
+ │ ├── __init__.py # Package exports
342
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints
343
+ │ ├── config.py # Config loading/saving + .wup.env support
344
+ │ ├── core.py # WupWatcher: detection, inference, scheduling
345
+ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
346
+ │ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
347
+ │ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
348
+ │ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
349
+ │ └── models/
350
+ │ ├── __init__.py
351
+ │ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, TestQLConfig...
329
352
  ├── tests/
330
- └── test_wup.py # Unit tests
353
+ ├── test_wup.py # unit/integration tests (incl. VisualDiffer, config)
354
+ │ ├── test_testql_watcher.py # TestQLWatcher + VisualDiffer integration tests
355
+ │ └── test_e2e.py # end-to-end CLI tests
356
+ ├── examples/
357
+ │ ├── fastapi-app/ # FastAPI example project
358
+ │ ├── flask-app/ # Flask example project
359
+ │ ├── multi-service/ # Multi-service example
360
+ │ ├── testql_demo.py # TestQL simulation demo
361
+ │ ├── testql_integration.py # Custom TestQLWatcher + visual diff example
362
+ │ └── visual_diff_demo.py # Visual DOM diff demo (no Playwright required)
331
363
  ├── 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
364
+ └── TESTQL_INTEGRATION.md # TestQL integration guide
365
+ ├── testql-scenarios/ # Auto-generated TestQL scenarios
366
+ ├── pyproject.toml # Package config (setuptools)
367
+ └── README.md
338
368
  ```
339
369
 
340
370
  ## Development
@@ -343,23 +373,33 @@ wup/
343
373
 
344
374
  ```bash
345
375
  # Run all tests
346
- pytest
376
+ python3 -m pytest tests/ -v
377
+
378
+ # Run specific suite
379
+ python3 -m pytest tests/test_wup.py -v
380
+ python3 -m pytest tests/test_testql_watcher.py -v
347
381
 
348
382
  # Run with coverage
349
- pytest --cov=wup
383
+ python3 -m pytest tests/ --cov=wup
384
+ ```
385
+
386
+ ### Examples
387
+
388
+ ```bash
389
+ # Visual diff demo (no Playwright required)
390
+ python3 examples/visual_diff_demo.py
391
+
392
+ # With live page scan (requires playwright)
393
+ python3 examples/visual_diff_demo.py http://localhost:8100/health
350
394
 
351
- # Run specific test
352
- pytest tests/test_wup.py::TestDependencyMapper::test_init
395
+ # TestQL + visual diff integration
396
+ python3 examples/testql_integration.py /path/to/project
353
397
  ```
354
398
 
355
- ### Building for Distribution
399
+ ### Building & Publishing
356
400
 
357
401
  ```bash
358
- # Build wheel and source distribution
359
402
  python -m build
360
-
361
- # Install from dist
362
- pip install dist/wup-0.1.6-py3-none-any.whl
363
403
  ```
364
404
 
365
405
  ## 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.13-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.10-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.15-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.40-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-4.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $2.1000 (14 commits)
10
- - 👤 **Human dev:** ~$319 (3.2h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $2.4000 (16 commits)
10
+ - 👤 **Human dev:** ~$411 (4.1h @ $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.13-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.15-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,86 @@ 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
271
+ ```
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
287
297
  ```
288
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
+
289
310
  ## Project Structure
290
311
 
291
312
  ```
292
313
  wup/
293
314
  ├── 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
315
+ │ ├── __init__.py # Package exports
316
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints
317
+ │ ├── config.py # Config loading/saving + .wup.env support
318
+ │ ├── core.py # WupWatcher: detection, inference, scheduling
319
+ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
320
+ │ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
321
+ │ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
322
+ │ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
323
+ │ └── models/
324
+ │ ├── __init__.py
325
+ │ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, TestQLConfig...
303
326
  ├── tests/
304
- └── test_wup.py # Unit tests
327
+ ├── test_wup.py # unit/integration tests (incl. VisualDiffer, config)
328
+ │ ├── test_testql_watcher.py # TestQLWatcher + VisualDiffer integration tests
329
+ │ └── test_e2e.py # end-to-end CLI tests
330
+ ├── examples/
331
+ │ ├── fastapi-app/ # FastAPI example project
332
+ │ ├── flask-app/ # Flask example project
333
+ │ ├── multi-service/ # Multi-service example
334
+ │ ├── testql_demo.py # TestQL simulation demo
335
+ │ ├── testql_integration.py # Custom TestQLWatcher + visual diff example
336
+ │ └── visual_diff_demo.py # Visual DOM diff demo (no Playwright required)
305
337
  ├── 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
338
+ └── TESTQL_INTEGRATION.md # TestQL integration guide
339
+ ├── testql-scenarios/ # Auto-generated TestQL scenarios
340
+ ├── pyproject.toml # Package config (setuptools)
341
+ └── README.md
312
342
  ```
313
343
 
314
344
  ## Development
@@ -317,23 +347,33 @@ wup/
317
347
 
318
348
  ```bash
319
349
  # Run all tests
320
- pytest
350
+ python3 -m pytest tests/ -v
351
+
352
+ # Run specific suite
353
+ python3 -m pytest tests/test_wup.py -v
354
+ python3 -m pytest tests/test_testql_watcher.py -v
321
355
 
322
356
  # Run with coverage
323
- pytest --cov=wup
357
+ python3 -m pytest tests/ --cov=wup
358
+ ```
359
+
360
+ ### Examples
361
+
362
+ ```bash
363
+ # Visual diff demo (no Playwright required)
364
+ python3 examples/visual_diff_demo.py
365
+
366
+ # With live page scan (requires playwright)
367
+ python3 examples/visual_diff_demo.py http://localhost:8100/health
324
368
 
325
- # Run specific test
326
- pytest tests/test_wup.py::TestDependencyMapper::test_init
369
+ # TestQL + visual diff integration
370
+ python3 examples/testql_integration.py /path/to/project
327
371
  ```
328
372
 
329
- ### Building for Distribution
373
+ ### Building & Publishing
330
374
 
331
375
  ```bash
332
- # Build wheel and source distribution
333
376
  python -m build
334
-
335
- # Install from dist
336
- pip install dist/wup-0.1.6-py3-none-any.whl
337
377
  ```
338
378
 
339
379
  ## License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wup"
7
- version = "0.2.13"
7
+ version = "0.2.15"
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"