Skip to content

Commit 266b0e1

Browse files
authored
Merge pull request #86 from Integration-Automation/dev
Extended capabilities round 2 + cookbook + integration tests
2 parents 3ad2975 + fcaa697 commit 266b0e1

163 files changed

Lines changed: 13395 additions & 46 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/e2e_browser.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: E2E Browser Smoke
2+
3+
# Manually triggered job that boots Selenium Grid in Docker and runs the
4+
# real-browser smoke tests under test/e2e_test/. Kept off the unit-test
5+
# critical path because the Grid pull is heavy and depends on Docker Hub.
6+
7+
on:
8+
workflow_dispatch:
9+
schedule:
10+
# Daily at 06:00 UTC; failures don't block PRs.
11+
- cron: "0 6 * * *"
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
e2e:
18+
name: E2E (Selenium Grid)
19+
runs-on: ubuntu-latest
20+
21+
services:
22+
selenium-hub:
23+
image: selenium/hub:4.20.0
24+
ports: ["4444:4444"]
25+
options: >-
26+
--health-cmd "curl -fsS http://localhost:4444/wd/hub/status || exit 1"
27+
--health-interval 10s --health-timeout 5s --health-retries 6
28+
chrome-node:
29+
image: selenium/node-chrome:4.20.0
30+
env:
31+
SE_EVENT_BUS_HOST: selenium-hub
32+
SE_EVENT_BUS_PUBLISH_PORT: 4442
33+
SE_EVENT_BUS_SUBSCRIBE_PORT: 4443
34+
SE_NODE_MAX_SESSIONS: 2
35+
SE_NODE_OVERRIDE_MAX_SESSIONS: "true"
36+
37+
steps:
38+
- uses: actions/checkout@v4
39+
40+
- name: Set up Python
41+
uses: actions/setup-python@v5
42+
with:
43+
python-version: "3.12"
44+
45+
- name: Install dependencies
46+
run: |
47+
python -m pip install --upgrade pip wheel
48+
pip install -r dev_requirements.txt
49+
50+
- name: Wait for Grid to be ready
51+
run: |
52+
for i in $(seq 1 30); do
53+
if curl -fsS http://localhost:4444/wd/hub/status >/dev/null; then
54+
echo "Grid ready after $i tries"
55+
exit 0
56+
fi
57+
sleep 2
58+
done
59+
echo "Grid did not become ready"
60+
exit 1
61+
62+
- name: Run real-browser E2E
63+
env:
64+
WEBRUNNER_E2E_HUB: http://localhost:4444/wd/hub
65+
run: python -m pytest test/e2e_test/ -m e2e -v

.github/workflows/test_dev.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ jobs:
3737
- name: Run unit tests
3838
run: python -m pytest test/unit_test/test_*.py -v
3939

40+
- name: Run integration tests
41+
# Each subprocess test sets its own timeout; pytest-timeout isn't a dep.
42+
run: python -m pytest test/integration_test/ -v
43+
4044
integration-test:
4145
name: Integration Tests (Python ${{ matrix.python-version }})
4246
needs: unit-test

.github/workflows/test_stable.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ jobs:
3737
- name: Run unit tests
3838
run: python -m pytest test/unit_test/test_*.py -v
3939

40+
- name: Run integration tests
41+
# Each subprocess test sets its own timeout; pytest-timeout isn't a dep.
42+
run: python -m pytest test/integration_test/ -v
43+
4044
integration-test:
4145
name: Integration Tests (Python ${{ matrix.python-version }})
4246
needs: unit-test

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,7 @@ dmypy.json
123123
*.exe
124124

125125
/.claude/
126-
/.claude
126+
/.claude
127+
issues.json
128+
hotspots.json
129+
codacy.json

README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,114 @@ serve_stdio(server=server)
628628

629629
The server speaks MCP `2024-11-05`: `initialize`, `tools/list`, `tools/call`, `resources/list`, `ping`, `shutdown`.
630630

631+
## Action JSON LSP
632+
633+
A standard Language Server Protocol implementation for action JSON files:
634+
635+
```bash
636+
python -m je_web_runner.action_lsp
637+
```
638+
639+
`textDocument/completion` returns every registered `WR_*` command; `textDocument/publishDiagnostics` runs the action linter on `didOpen` / `didChange`. Pair with VS Code's *Configure JSON Language Servers* or the JetBrains LSP plugin.
640+
641+
## Even More Capabilities (polish wave)
642+
643+
CLI & orchestration polish:
644+
645+
- **Regex test selector**`test_filter.name_filter.filter_paths(paths, include=["smoke.*"], exclude=["slow"])` keeps only matching candidate paths; orthogonal to the existing tag filter.
646+
- **Process supervisor**`process_supervisor.ProcessSupervisor().kill_orphans()` walks the OS process table for `chromedriver` / `geckodriver` / `msedgedriver` and kills stragglers (skips `os.getpid()` automatically). `with_watchdog(callable, timeout_seconds=300)` wraps a long callable with a hard wall-clock raise.
647+
- **Pipeline DSL**`pipeline.load_pipeline({"stages": [...]})` + `run_pipeline(pipeline, runner)` execute multi-stage gates: `continue_on_failure=True` makes a stage non-blocking (linters / scanners), otherwise downstream stages skip.
648+
649+
Frontend / mobile / coverage:
650+
651+
- **Storybook visual snapshots**`storybook.visual_snapshots.capture_story_snapshots(stories, base_url, take_screenshot, navigate, baseline_dir=...)` walks every story, persists deterministic filenames (`components-button--primary.png`), and diffs against an optional baseline. `assert_no_visual_regressions(report)` is the gate.
652+
- **Appium gestures**`appium_integration.gestures` ships `swipe`, `scroll`, `long_press`, `pinch`, `double_tap` that prefer Appium's `mobile:` named-gesture extension and fall back to W3C Actions on older drivers.
653+
- **Coverage map**`coverage_map.build_coverage_map("./actions")` walks every action JSON file, normalises `WR_to_url` paths (`/users/42``/users/:id`) and produces a route → files reverse index. `coverage.uncovered(declared_routes)` answers "which routes have no test?".
654+
655+
## Even More Capabilities (final wave)
656+
657+
Debugging & reproducibility:
658+
659+
- **CDP message tap**`cdp_tap.CdpRecorder("cdp.ndjson").attach(driver)` wraps `execute_cdp_cmd` so every command + return value is appended to an ndjson log; `CdpReplayer(load_recording(...))` plays it back against a stub for offline debugging.
660+
- **Cross-browser parity**`cross_browser.diff_runs([chromium_run, firefox_run, webkit_run])` diffs title / DOM hash / console / network status / screenshot hash, classifying each finding as `major` (5xx, title, DOM mismatch) or `minor`. `assert_parity(report, only_major=True)` is the gate.
661+
- **Browser state diff**`state_diff.capture_state(driver)` snapshots cookies + localStorage + sessionStorage; `diff_states(before, after)` lists added / removed / changed keys per section so cart / auth flows stay traceable.
662+
663+
Authoring / scaffolding:
664+
665+
- **Page Object codegen**`pom_codegen.discover_elements_from_html(html)` walks every element with `data-testid` / `id` / form `name`; `render_pom_module(elements, class_name="LoginPage")` returns a Python module with one `TestObject` property per element.
666+
667+
CI reproducibility:
668+
669+
- **Workspace lock file**`workspace_lock.build_lock(drivers=..., playwright_versions={"chromium": "127.0.0.0"})` snapshots every Python distribution + driver version + Playwright browser version; `write_lock(lock, ".webrunner/lock.json")` and `diff_locks(before, after)` complete the pipeline.
670+
671+
Long-running observability:
672+
673+
- **A11y trend dashboard**`a11y_trend.aggregate_history(history)` buckets axe runs by day and impact; `render_html(points)` produces a self-contained SVG line chart so regressions are visible at a glance.
674+
- **Perf drift detector**`perf_drift.detect_drift({"lcp_ms": samples}, baseline_window=20, recent_window=5)` compares the recent P95 against a rolling baseline P95 and flags drift outside `tolerance`. `assert_no_regression(report)` is the strict path; `higher_is_better={"frame_rate"}` for inverted metrics.
675+
676+
## Even More Capabilities (newest wave)
677+
678+
Authoring / formatting:
679+
680+
- **Action JSON formatter**`action_formatter.format_actions(actions)` writes a canonical multi-line array with kwargs in a stable preferred-then-alphabetical order; `format_file(path)` reformats in place and reports `(text, changed)`.
681+
- **Markdown → action JSON**`md_authoring.parse_markdown(text)` understands `- open <url>`, `- click #id`, `- type "x" into <selector>`, `- wait 3s`, `- assert title "..."`, `- press Enter`, `- screenshot`, `- run template <name>`, `- quit`. Lines that don't match are preserved as `WR__note` so the round-trip is loss-less.
682+
683+
Triage / production observability:
684+
685+
- **Failure clustering**`failure_cluster.cluster_failures(failures, top_n=5)` reduces each error message to a stable signature (strips timestamps, hex addresses, line numbers, paths, large numerics, quoted substrings) so the same root cause across runs lands in one bucket.
686+
- **Synthetic monitoring**`synthetic_monitoring.SyntheticMonitor(alert_sink).register("homepage", check)` reruns checks; the sink only fires on edge transitions (`green → red` / `red → green`) with `failure_threshold` / `recovery_threshold` to silence flapping.
687+
- **OTLP exporter**`observability.otlp_exporter.configure_otlp_export(provider, OtlpExportConfig(endpoint="https://otlp:4317"))` ships the existing OTel spans to Jaeger / Tempo / any OTLP backend (gRPC by default, HTTP fallback).
688+
689+
Frontend / component:
690+
691+
- **Storybook integration**`storybook.discover_stories(index_path)` reads Storybook 7+ `index.json` (or legacy `stories.json`); `plan_actions_for_stories(stories, base_url, run_a11y=True)` builds a flat action list visiting each story in iframe mode and running axe + screenshot.
692+
- **Shadow DOM auto-pierce**`dom_traversal.shadow_pierce.find_first(driver, "button.primary")` recursively walks open shadow roots (Selenium `execute_script` or Playwright `evaluate`) so a single CSS selector can match across shadow boundaries.
693+
694+
## Even More Capabilities (latest wave)
695+
696+
Onboarding / migration:
697+
698+
- **Workspace bootstrapper**`python -m je_web_runner --init` (or `bootstrapper.init_workspace("my-tests")`) drops `actions/sample.json`, `.webrunner/ledger.json`, pinned-driver template, JSON schema, pre-commit hook, and a starter GitHub Actions workflow.
699+
- **Driver pinner**`driver_pin.install_for_browser(".webrunner/drivers.json", "firefox")` reads a JSON pin file (`name` / `version` / `url` / `archive_format` / `binary_inside`), downloads + extracts once, then serves from cache. Bypasses the GitHub API rate limit that webdriver-manager hits in CI.
700+
- **Selenium → Playwright translator**`sel_to_pw.translate_python_source(text)` rewrites `driver.find_element(By.ID, "x")``page.locator("#x")` and similar; `translate_action_list(actions)` rewrites `WR_*` action JSON to its `WR_pw_*` equivalent (drops `WR_implicitly_wait` since Playwright auto-waits).
701+
702+
Test authoring:
703+
704+
- **Form auto-fill**`form_autofill.plan_fill_actions(fields, fixture, submit_locator=...)` infers each field from `data-testid` / `id` / `name` / `placeholder` / `label` / `type` and emits a ready-to-run `WR_save_test_object` + `WR_element_input` sequence.
705+
706+
Quality:
707+
708+
- **A11y diff**`accessibility.a11y_diff.diff_violations(baseline, current)` buckets axe-core findings into `added` / `resolved` / `persisting` keyed on `(rule_id, target)`; `assert_no_regressions(diff, allow_rules=...)` is the CI gate.
709+
710+
Performance / orchestration:
711+
712+
- **Fan-out**`fanout.run_fan_out([("preflight-a", task_a), task_b, ...], max_workers=4)` runs read-only callables concurrently inside one test, returning per-task duration + outcome with `raise_for_failures()` for the strict path.
713+
- **Event bus**`event_bus.EventBus(".webrunner/events.log").publish("setup-done", {"shard": 1})`; subscribers `poll()` from a remembered offset or `wait_for(topic, predicate=..., timeout=30)`. File-backed ndjson — no Redis dependency.
714+
715+
Browser internals:
716+
717+
- **Extension test harness**`extension_harness.parse_manifest("./ext")` reads MV2 / MV3 manifests; `apply_to_chrome_options(options, [ext_dir])` adds `--load-extension` flags; `playwright_persistent_context_args(...)` returns the kwargs needed for `launch_persistent_context`.
718+
719+
## Even More Capabilities
720+
721+
Reliability & dev-loop:
722+
723+
- **Browser pool**`browser_pool.BrowserPool(factory, size=4, max_uses=50).warm()`; `with pool.session() as ses: …` removes browser cold-start from local dev. Health check + recycle policy built in.
724+
- **WebDriver BiDi bridge**`bidi_backend.BidiBridge().subscribe(target, "console", callback)` works against either Selenium 4 BiDi (`driver.script.add_console_message_handler`) or Playwright `page.on(...)`. `register_translator` lets you wire custom event names.
725+
726+
Determinism & offline runs:
727+
728+
- **HAR replay server**`har_replay.HarReplayServer(load_har("recorded.har")).start()` boots a local HTTP server that serves recorded responses; supports literal / glob / `re:` URL matching with rotation across duplicates. Drop-in for staging-API outages.
729+
730+
Quality / privacy:
731+
732+
- **PII scanner**`pii_scanner.scan_text(text)` finds emails, E.164 phones, Luhn-validated credit cards, US SSN, ROC ID, and IPv4. `assert_no_pii(text, allow_categories=...)` for CI gates; `redact_text(text)` returns a sanitised copy.
733+
- **Visual diff review UI**`visual_review.VisualReviewServer(baseline_dir, current_dir).start()` opens a local web UI showing each baseline / current pair side-by-side with an *Accept current as baseline* button (idempotent file copy with path-traversal guard).
734+
735+
Test orchestration:
736+
737+
- **Test impact analysis**`impact_analysis.build_index("./actions")` walks every action JSON file and projects locator names, URLs, template names, and `WR_*` commands into a reverse index; `affected_action_files(index, locators=["primary_cta"])` answers "which tests touch this?" so diff-aware shards can go beyond filename matching.
738+
631739
## Browser Internals
632740

633741
```python
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
==========================
2+
API reference
3+
==========================
4+
5+
Auto-generated reference for the thematic façade ``je_web_runner.api``.
6+
Each theme below is a thin re-export of the underlying
7+
``je_web_runner.utils.<area>`` modules; advanced users can still reach
8+
into the underlying modules directly.
9+
10+
.. autosummary::
11+
:toctree: generated
12+
:recursive:
13+
14+
je_web_runner.api.authoring
15+
je_web_runner.api.debugging
16+
je_web_runner.api.frontend
17+
je_web_runner.api.infra
18+
je_web_runner.api.mobile
19+
je_web_runner.api.networking
20+
je_web_runner.api.observability
21+
je_web_runner.api.quality
22+
je_web_runner.api.reliability
23+
je_web_runner.api.security
24+
je_web_runner.api.test_data
25+
26+
The MCP server and Action LSP have their own top-level packages:
27+
28+
.. autosummary::
29+
:toctree: generated
30+
:recursive:
31+
32+
je_web_runner.mcp_server
33+
je_web_runner.action_lsp

0 commit comments

Comments
 (0)