https://github.com/integration-automation/webrunner
A framework for Web automation
https://github.com/integration-automation/webrunner
automation keyword-driven-testing python report-generator selenium selenium-python selenium-webdriver selenium4 testing web-testing
Last synced: about 1 month ago
JSON representation
A framework for Web automation
- Host: GitHub
- URL: https://github.com/integration-automation/webrunner
- Owner: Integration-Automation
- License: mit
- Created: 2021-12-23T06:26:42.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2026-04-25T09:28:22.000Z (about 1 month ago)
- Last Synced: 2026-04-25T10:27:39.250Z (about 1 month ago)
- Topics: automation, keyword-driven-testing, python, report-generator, selenium, selenium-python, selenium-webdriver, selenium4, testing, web-testing
- Language: Python
- Homepage: https://integration-automation.github.io/WebRunner/
- Size: 21.4 MB
- Stars: 6
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# WebRunner
Cross-platform web automation: Selenium + Playwright, plus a JSON-driven action executor with batteries included.
---
WebRunner (`je_web_runner`) started as a Selenium wrapper and grew into a full automation platform: a Selenium and a Playwright backend behind one JSON-driven action executor, plus modules for reporting, observability, orchestration, security, and AI assistance. Every executor command has a deterministic name (`WR_*`) and a single dispatch point, so an action JSON can mix browser, HTTP, database, and webhook calls in the same script.
> **Auto-generated reference** — every registered `WR_*` command (signature + summary) is exported under [`docs/reference/command_reference.md`](docs/reference/command_reference.md), and a JSON Schema for action JSON files lives at [`docs/reference/webrunner-action-schema.json`](docs/reference/webrunner-action-schema.json).
## Table of Contents
- [Highlights](#highlights)
- [Installation](#installation)
- [Architecture](#architecture)
- [System overview](#system-overview)
- [Action lifecycle](#action-lifecycle)
- [Backend dispatch](#backend-dispatch)
- [Module map](#module-map)
- [Quick Start](#quick-start)
- [Core API](#core-api)
- [Action Executor](#action-executor)
- [Backends](#backends)
- [Selenium (default)](#selenium-default)
- [Playwright (full)](#playwright-full)
- [Cloud Grid](#cloud-grid)
- [Appium (mobile)](#appium-mobile)
- [Reports](#reports)
- [Observability](#observability)
- [Test Orchestration](#test-orchestration)
- [Quality & Security](#quality--security)
- [Browser Internals](#browser-internals)
- [Test Data](#test-data)
- [Auth & APIs](#auth--apis)
- [Recorder](#recorder)
- [CI / Integrations](#ci--integrations)
- [AI Assistance](#ai-assistance)
- [CLI Usage](#cli-usage)
- [Test Record](#test-record)
- [Exception Handling](#exception-handling)
- [Logging](#logging)
- [Supported Browsers](#supported-browsers)
- [Supported Platforms](#supported-platforms)
- [License](#license)
## Highlights
- **Two backends, one executor.** Selenium is the default; the Playwright backend mirrors the same operational surface under `WR_pw_*` and is fully opt-in.
- **Action JSON as a contract.** Every command resolves through `Executor.event_dict`; legacy aliases stay alongside snake_case names for back-compat, and a JSON Schema is exported for IDE autocomplete.
- **Reports in five formats.** HTML, JSON, XML, JUnit XML (CI-native), and Allure result files; a single manifest binds every output for downstream globs.
- **Orchestration built in.** Tag filters, dependency declarations with topological order, ledger-backed re-run-only-failed, flaky detection, A/B run mode, multi-user matrix, deterministic sharding, watch mode, and a stdlib scheduler.
- **Observability without extra plumbing.** Auto-screenshot on failure, retry policy, OpenTelemetry hook, live HTTP dashboard, replay studio (HTML timeline), HAR capture + diff.
- **Quality & security guards.** Action linter, migration helper, hard-coded secrets scanner, HTTP security headers audit, axe-core accessibility audit, Lighthouse runner, perf metrics (FCP/LCP/CLS), visual regression, snapshot testing, network throttling, arbitrary-script gate.
- **Browser internals.** Raw CDP, console + network event capture, localStorage / sessionStorage / IndexedDB, service worker / cache control, Shadow DOM piercing, multi-iframe, file upload / download, browser extension loaders.
- **Test data & fixtures.** Faker integration, factory pattern, testcontainers (Postgres / Redis / generic), per-environment `.env` loader with `${ENV.X}` placeholder expansion, CSV/JSON data-driven runner with `${ROW.x}`.
- **Auth, API, DB.** OAuth2 / OIDC client-credentials / password / refresh-token flows with token cache, HTTP API testing commands with JSON assertions, SQLAlchemy-backed database validation.
- **Integrations.** TCP socket server with token + TLS, BrowserStack / Sauce Labs / LambdaTest cloud Grid, Appium mobile, JIRA + TestRail, Slack / generic webhook notifier, GitHub Actions inline annotations, Locust load testing.
- **AI hooks.** Pluggable LLM callable powers self-healing locators and natural-language → action JSON drafts.
- **Cross-platform & multi-browser.** Windows, macOS, Linux, Raspberry Pi · Chrome, Chromium, Firefox, Edge, IE, Safari · Chromium, Firefox, WebKit (Playwright).
## Installation
**Stable:**
```bash
pip install je_web_runner
```
**Development:**
```bash
pip install je_web_runner_dev
```
**Optional dependencies** (each enables a slice of features; install only what you use):
```bash
pip install playwright # Playwright backend
python -m playwright install # Browser binaries for Playwright
pip install Pillow # Visual regression
pip install faker # Random test data (WR_faker_*)
pip install sqlalchemy # Database validation (WR_db_*)
pip install opentelemetry-sdk # Distributed traces (WR_set_action_span_factory)
pip install Appium-Python-Client # Mobile native (WR_appium_*)
pip install testcontainers # Spin up Postgres / Redis (WR_tc_*)
pip install locust # Load testing (WR_locust_*)
```
Hard requirements: Python **3.10+**, `selenium>=4.0.0`, `requests`, `python-dotenv`, `webdriver-manager`, `defusedxml`, `Pillow`.
## Architecture
### System overview
```mermaid
flowchart LR
subgraph Authoring
A1["Action JSON files"]
A2["Programmatic Python API"]
A3["Browser recorder
(JS injection)"]
A4["LLM NL → action draft"]
end
subgraph Core
EXE["Executor
event_dict"]
REC["Test record
singleton"]
LDG["Run ledger /
flaky detection"]
end
subgraph Backends
SEL["Selenium
WebDriverWrapper"]
PW["Playwright
PlaywrightWrapper"]
APM["Appium
Mobile"]
HTTP["HTTP API
requests"]
DB["Database
SQLAlchemy"]
end
subgraph Outputs
REP["Reports
HTML/JSON/XML/JUnit/Allure"]
OBS["Observability
OTel · dashboard · replay"]
NOT["Notifiers
Slack · webhook · GH · JIRA · TestRail"]
end
A1 --> EXE
A2 --> EXE
A3 --> A1
A4 --> A1
EXE --> SEL
EXE --> PW
EXE --> APM
EXE --> HTTP
EXE --> DB
SEL --> REC
PW --> REC
APM --> REC
HTTP --> REC
DB --> REC
REC --> LDG
REC --> REP
REC --> OBS
REC --> NOT
```
### Action lifecycle
```mermaid
flowchart LR
IN["Action
[cmd, args, kwargs]"] --> VAL["JSON validator
(WR_validate_*)"]
VAL --> ENV["${ENV.X} / ${ROW.x}
placeholder expansion"]
ENV --> SPAN["OTel span factory
(optional)"]
SPAN --> RETRY["Retry policy
retries × backoff"]
RETRY --> GATE["Arbitrary-script
gate"]
GATE --> DISP["event_dict[cmd](*args, **kwargs)"]
DISP --> RECORD["test_record_instance
append()"]
DISP -- failure --> SHOT["Auto-screenshot
(failure dir)"]
RECORD --> DONE["Result dict"]
SHOT --> DONE
```
### Backend dispatch
```mermaid
flowchart TB
CMD["Action command name"] --> ROUTE{"prefix?"}
ROUTE -- "WR_pw_*" --> PW["Playwright backend
(PlaywrightWrapper)"]
ROUTE -- "WR_pw_element_*" --> PWE["Playwright element
(PlaywrightElementWrapper)"]
ROUTE -- "WR_appium_*" --> APM["Appium driver"]
ROUTE -- "WR_http_*" --> HTTP["requests wrapper"]
ROUTE -- "WR_db_*" --> DB["SQLAlchemy validator"]
ROUTE -- "WR_pw_a11y_* / WR_a11y_*" --> AXE["axe-core audit"]
ROUTE -- "WR_pw_throttle / WR_throttle" --> THR["Network throttling
(CDP)"]
ROUTE -- "WR_pw_route_*" --> ROUTE_MOCK["Playwright route mock"]
ROUTE -- "WR_*
(default)" --> SEL["Selenium backend
(WebDriverWrapper)"]
ROUTE -- "WR_element_*
(default)" --> SE["Selenium element
(WebElementWrapper)"]
```
### Module map
```
je_web_runner/
├── __init__.py
├── __main__.py # CLI: --execute_dir / --watch / --tag / --shard / --migrate ...
├── element/web_element_wrapper.py
├── manager/webrunner_manager.py
├── webdriver/
│ ├── webdriver_wrapper.py # Selenium core
│ ├── webdriver_with_options.py
│ ├── playwright_wrapper.py # Playwright sync backend (full)
│ ├── playwright_element_wrapper.py
│ └── playwright_locator.py # TestObject ↔ Playwright selector
└── utils/
├── ab_run/ # A/B run mode (run_ab + diff_records)
├── accessibility/ # axe-core audit
├── ai_assist/ # Pluggable LLM scaffold
├── api/ # HTTP API testing commands
├── appium_integration/ # Mobile native via Appium
├── auth/ # OAuth2 / OIDC token helpers
├── callback/ # Callback executor
├── cdp/ # Raw CDP passthrough
├── ci_annotations/ # GitHub Actions ::error::
├── cli/ # CLI parser, watch mode, dispatch
├── cloud_grid/ # BrowserStack / Sauce Labs / LambdaTest
├── dashboard/ # Live progress HTTP server
├── database/ # SQL validation (SQLAlchemy)
├── data_driven/ # CSV/JSON dataset + ${ROW.x}
├── docs/ # Auto-generated command reference
├── dom_traversal/ # Shadow DOM / iframe helpers
├── env_config/ # .env loader + ${ENV.X}
├── exception/ # Exception hierarchy
├── executor/ # Action executor + retry/screenshot/gate
├── extensions/ # Browser extension loaders
├── factories/ # Factory pattern helpers
├── file_process/ # File utilities
├── file_transfer/ # Upload / download helpers
├── generate_report/ # HTML/JSON/XML/JUnit/Allure + manifest
├── har_diff/ # HAR file diff
├── json/ # JSON I/O + validator (length 1/2/3)
├── lighthouse/ # Lighthouse CLI runner
├── linter/ # action_linter + migration
├── load_test/ # Locust wrapper
├── logging/ # Rotating file handler
├── multi_user/ # Multi-user matrix runner
├── network_emulation/ # Throttling presets via CDP
├── notifier/ # Slack / generic webhooks
├── observability/ # Console+network capture · OTel
├── package_manager/ # Dynamic plugin loader
├── perf_metrics/ # FCP / LCP / CLS / TTFB
├── pom_generator/ # POM skeleton from URL/HTML
├── project/ # Project template generator
├── recorder/ # JS-injection recorder + PII mask
├── replay_studio/ # HTML timeline studio
├── run_ledger/ # ledger · flaky · classifier
├── schema/ # Action JSON Schema export
├── scheduler/ # stdlib-sched scheduled runner
├── secrets_scanner/ # Hard-coded credential scanner
├── security_headers/ # HTTP headers audit
├── selenium_utils_wrapper/ # Keys / Capabilities
├── self_healing/ # Fallback locator registry
├── service_worker/ # SW unregister + cache clear
├── sharding/ # Deterministic test sharding
├── snapshot/ # Text/DOM snapshot testing
├── socket_server/ # TCP server with token + TLS
├── storage/ # localStorage / session / IDB
├── test_data/ # Faker integration
├── test_filter/ # Tag filter + dependency graph
├── test_management/ # JIRA + TestRail
├── test_object/ # TestObject + record
├── test_record/ # Action recording
├── testcontainers_integration/ # Postgres / Redis / generic
├── visual_regression/ # Pillow-based image diff
└── xml/ # XML utilities
```
## Quick Start
### Direct API
```python
from je_web_runner import TestObject, get_webdriver_manager, web_element_wrapper
manager = get_webdriver_manager("chrome")
manager.webdriver_wrapper.to_url("https://www.google.com")
manager.webdriver_wrapper.implicitly_wait(2)
search_box = TestObject("q", "name")
manager.webdriver_wrapper.find_element(search_box)
web_element_wrapper.click_element()
web_element_wrapper.input_to_element("WebRunner automation")
manager.quit()
```
### JSON action list (modern aliases)
```python
from je_web_runner import execute_action
actions = [
["WR_new_driver", {"webdriver_name": "chrome"}],
["WR_to_url", {"url": "https://www.google.com"}],
["WR_implicitly_wait", {"time_to_wait": 2}],
["WR_save_test_object", {"test_object_name": "q", "object_type": "NAME"}],
["WR_find_recorded_element", {"element_name": "q"}],
["WR_element_click"],
["WR_element_input", {"input_value": "WebRunner automation"}],
["WR_quit_all"],
]
execute_action(actions)
```
The legacy names (`WR_get_webdriver_manager`, `WR_SaveTestObject`, `WR_quit`, `WR_input_to_element`, …) still work — see [Quality & Security](#quality--security) for the one-shot migration helper.
### Mixed positional + keyword arguments
```python
[
["WR_to_url", ["https://example.com"], {"timeout": 30}],
]
```
The validator accepts length-1, length-2 (`[cmd, dict_or_list]`), and length-3 (`[cmd, [positional], {kwargs}]`) actions.
## Core API
The original Selenium-flavoured API remains the canonical entry point for programmatic use. Sections preserved from the original README:
- **WebDriver Manager** — `get_webdriver_manager`, `new_driver`, `change_webdriver`, `close_choose_webdriver`, `quit`.
- **WebDriver Wrapper** — `to_url`, `forward`, `back`, `refresh`, `find_element`, `find_elements`, `implicitly_wait`, `explict_wait` (alias `WR_explicit_wait`), `set_script_timeout`, `set_page_load_timeout`, the full ActionChains-backed mouse/keyboard surface, cookies, `execute_script`, window management, screenshots, frame/window/alert switching, `get_log`.
- **Web Element Wrapper** — `click_element`, `input_to_element`, `clear`, `submit`, `get_attribute`, `get_property`, `get_dom_attribute`, `is_displayed`, `is_enabled`, `is_selected`, `value_of_css_property`, `screenshot`, `change_web_element`, `check_current_web_element`, plus the new `select_by_value` / `select_by_index` / `select_by_visible_text`.
- **TestObject** — `TestObject(name, type)`, `create_test_object`, `get_test_object_type_list` (returns `['ID', 'NAME', 'XPATH', 'CSS_SELECTOR', 'CLASS_NAME', 'TAG_NAME', 'LINK_TEXT', 'PARTIAL_LINK_TEXT']`).
Programmatic examples for each surface are kept identical to the previous edition; see the relevant Sphinx pages under `docs/source/Eng/doc/` for full code snippets.
## Action Executor
The executor maps a string command name to a Python callable. Every backend, integration, and helper registers under `event_dict`.
### Action shapes
```python
["command"] # no args
["command", {"key": "value"}] # kwargs
["command", [arg1, arg2]] # positional
["command", [arg1], {"key": "value"}] # positional + kwargs (length 3)
```
### Length-3 example
```python
[
["WR_pw_evaluate", ["() => document.title"], {"arg": None}],
]
```
### Top-level shapes
```python
[ ...actions... ] # bare list
{
"webdriver_wrapper": [ ...actions... ],
"meta": {"tags": ["smoke", "fast"], "depends_on": ["login"]} # optional
}
```
`meta.tags` and `meta.depends_on` are picked up by the CLI for filtering and topological execution.
### Adding custom commands
```python
from je_web_runner import add_command_to_executor
def my_step(name: str) -> None:
print(f"hello {name}")
add_command_to_executor({"my_command": my_step})
```
### Retry, screenshots, scripts
```python
from je_web_runner.utils.executor.action_executor import executor
executor.set_retry_policy(retries=2, backoff=0.5) # global retry
executor.set_failure_screenshot_dir("./failures") # auto PNG on raise
executor.set_allow_arbitrary_script(False) # gate WR_execute_script / WR_pw_evaluate / WR_cdp
```
## Backends
### Selenium (default)
Selenium is the original backend. Every legacy command (and its modern alias) routes here unless an explicit `WR_pw_*` / `WR_appium_*` prefix is used.
### Playwright (full)
The Playwright backend mirrors the operational surface of the Selenium wrapper under `WR_pw_*`:
- **Lifecycle / pages / navigation** — `WR_pw_launch`, `WR_pw_quit`, `WR_pw_new_page`, `WR_pw_switch_to_page`, `WR_pw_close_page`, `WR_pw_to_url`, `WR_pw_forward`, `WR_pw_back`, `WR_pw_refresh`, `WR_pw_url`, `WR_pw_title`, `WR_pw_content`.
- **Find** — `WR_pw_find_element`, `WR_pw_find_elements`, `WR_pw_find_element_with_test_object_record`, `WR_pw_find_with_healing`.
- **Page-level shortcuts** — `WR_pw_click`, `WR_pw_dblclick`, `WR_pw_hover`, `WR_pw_fill`, `WR_pw_type_text`, `WR_pw_press`, `WR_pw_check`, `WR_pw_uncheck`, `WR_pw_select_option`, `WR_pw_drag_and_drop`.
- **Element-level (after `WR_pw_find_element_with_test_object_record`)** — `WR_pw_element_click`, `WR_pw_element_dblclick`, `WR_pw_element_fill`, `WR_pw_element_type_text`, `WR_pw_element_press`, `WR_pw_element_check`, `WR_pw_element_uncheck`, `WR_pw_element_select_option`, `WR_pw_element_get_attribute`, `WR_pw_element_inner_text`, `WR_pw_element_inner_html`, `WR_pw_element_is_visible`, `WR_pw_element_is_enabled`, `WR_pw_element_is_checked`, `WR_pw_element_scroll_into_view`, `WR_pw_element_screenshot`, `WR_pw_element_change`.
- **Script / cookies / waits / viewport / mouse / keyboard / frames** — `WR_pw_evaluate`, `WR_pw_get_cookies`, `WR_pw_add_cookies`, `WR_pw_clear_cookies`, `WR_pw_screenshot`, `WR_pw_wait_for_selector`, `WR_pw_wait_for_load_state`, `WR_pw_wait_for_timeout`, `WR_pw_wait_for_url`, `WR_pw_set_viewport_size`, `WR_pw_mouse_*`, `WR_pw_keyboard_*`.
- **Mobile emulation / locale / clock** — `WR_pw_emulate("iPhone 13")`, `WR_pw_set_locale`, `WR_pw_set_timezone`, `WR_pw_clock_install` / `_set_time` / `_run_for`, `WR_pw_set_geolocation`, `WR_pw_grant_permissions`.
- **HAR + route mock** — `WR_pw_start_har_recording`, `WR_pw_stop_har_recording`, `WR_pw_route_mock`, `WR_pw_route_mock_json`, `WR_pw_route_unmock`, `WR_pw_route_clear`.
Existing scripts can move to Playwright incrementally; `TestObject` records are translated to Playwright selectors automatically (`CSS_SELECTOR` → as-is, `XPATH` → `xpath=…`, `ID` → `#…`, `NAME` → `[name="…"]`, `LINK_TEXT` → `text=…`, `PARTIAL_LINK_TEXT` → `:has-text("…")`).
### Cloud Grid
```python
from je_web_runner import (
connect_browserstack,
build_browserstack_capabilities,
)
connect_browserstack(
username="...",
access_key="...",
capabilities=build_browserstack_capabilities(
browser_name="chrome",
browser_version="latest",
os_name="Windows",
os_version="11",
project="WebRunner",
build="ci-2026-04-26",
),
)
# All existing WR_* commands now run against the cloud session.
```
`connect_saucelabs` and `connect_lambdatest` follow the same shape.
### Appium (mobile)
```python
from je_web_runner import (
start_appium_session,
build_android_caps,
build_ios_caps,
)
start_appium_session(
"https://appium.example/wd/hub",
capabilities=build_android_caps(app="/path/to/app.apk"),
)
# WR_* commands now drive the mobile session.
```
## Reports
```python
from je_web_runner import (
generate_html_report,
generate_json_report,
generate_xml_report,
generate_junit_xml_report,
generate_allure_report,
)
from je_web_runner.utils.generate_report.report_manifest import generate_all_reports
# Run every generator + write a manifest binding all outputs:
result = generate_all_reports("run_2026_04_26", allure_dir="allure-results")
print(result["manifest_path"]) # → run_2026_04_26.manifest.json
```
| Format | Output shape | Spec-driven? |
|-----------|----------------------------------------------------------|--------------|
| JSON | `_success.json` + `_failure.json` | split |
| HTML | `.html` | single |
| XML | `_success.xml` + `_failure.xml` | split |
| JUnit XML | `_junit.xml` | single |
| Allure | `/-result.json` (× N) | directory |
The manifest captures the actual paths produced — CI globs no longer need to know the per-format conventions.
## Observability
```python
from je_web_runner import (
test_record_instance,
summarise_run,
notify_run_summary,
)
from je_web_runner.utils.executor.action_executor import executor
from je_web_runner.utils.observability.otel_tracing import install_executor_tracing
from je_web_runner.utils.dashboard.live_dashboard import start_dashboard
from je_web_runner.utils.replay_studio.replay_studio import export_replay_studio
executor.set_failure_screenshot_dir("./failures")
install_executor_tracing("webrunner") # one OTel span per action
start_dashboard("127.0.0.1", 8080) # browser-friendly progress UI
test_record_instance.set_record_enable(True)
# … run actions …
export_replay_studio("./run.html", screenshot_dir="./failures")
notify_run_summary("https://hooks.slack.com/services/...")
```
Failure screenshot, OpenTelemetry tracing, retry policy, and the live dashboard all hook into the same `Executor.event_dict` so they compose without coupling.
## Test Orchestration
```bash
# Filter by tag, run in parallel processes, persist a ledger, fail fast on dep breaks.
python -m je_web_runner \
--execute_dir ./actions \
--tag smoke,fast \
--exclude-tag slow \
--parallel 4 \
--parallel-mode process \
--ledger ./.run_ledger.json
# Re-run only the files that failed last time:
python -m je_web_runner --execute_dir ./actions --rerun-failed ./.run_ledger.json
# Watch a directory and re-run on file change:
python -m je_web_runner --execute_dir ./actions --watch ./actions
# Distribute across 4 runners deterministically (per machine):
python -m je_web_runner --execute_dir ./actions --shard 1/4
python -m je_web_runner --execute_dir ./actions --shard 2/4
python -m je_web_runner --execute_dir ./actions --shard 3/4
python -m je_web_runner --execute_dir ./actions --shard 4/4
```
Companion APIs — `WR_run_for_users` (multi-user matrix), `WR_run_ab` (A/B mode), `WR_flakiness_stats`, `WR_classify_failure`, `WR_schedule` + `WR_run_scheduler_for`.
## Quality & Security
- **Action linter** — `WR_lint_action` / `WR_lint_action_file` flag legacy command names, hard-coded URLs, dangerous scripts, missing tags, duplicate consecutive actions.
- **Migration helper** — `python -m je_web_runner --migrate ./actions` rewrites the eleven legacy aliases to their preferred names (`--migrate-dry-run` reports without writing).
- **Hard-coded secrets scanner** — `WR_scan_secrets_file` / `WR_assert_no_secrets` catch AWS / GitHub / Slack / JWT / Google / private-key strings before they land in commits.
- **Security headers audit** — `WR_audit_security_headers_url` checks HSTS / CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy.
- **Accessibility audit** — `WR_a11y_run_audit` injects user-supplied axe-core (`load_axe_source`) and runs against the active session; Playwright variant `WR_pw_a11y_run_audit`.
- **Lighthouse** — `WR_lighthouse_run` shells out to the official `lighthouse` Node CLI; `WR_lighthouse_assert_scores` enforces budgets.
- **Page perf metrics** — `WR_perf_collect` / `WR_pw_perf_collect` snapshot FCP / LCP / CLS / TTFB / domContentLoaded / load via `PerformanceObserver`; `WR_perf_assert_within` checks thresholds.
- **Visual regression** — `WR_visual_capture_baseline` + `WR_visual_compare` (Pillow soft-dep).
- **Snapshot testing** — `WR_match_snapshot` / `WR_update_snapshot` (text/DOM, unified diff on mismatch).
- **Network throttling** — `WR_throttle("slow_3g")` / `WR_pw_throttle("offline")`; presets cover Slow 3G, Fast 3G, Regular 4G, Wi-Fi, Offline, no-throttling.
- **HAR diff** — `WR_diff_har` / `WR_diff_har_files` show added / removed / status-changed requests between two runs.
- **Arbitrary-script gate** — `executor.set_allow_arbitrary_script(False)` blocks `WR_execute_script` / `WR_execute_async_script` / `WR_pw_evaluate` / `WR_cdp` / `WR_pw_cdp` for untrusted action JSON.
## Browser Internals
```python
from je_web_runner import (
selenium_cdp, # raw CDP
pw_emulate, pw_set_locale, # mobile / locale
)
from je_web_runner.utils.storage.browser_storage import (
selenium_local_storage_set,
selenium_indexed_db_drop,
)
from je_web_runner.utils.observability.event_capture import (
start_event_capture,
assert_no_console_errors,
assert_no_5xx,
)
from je_web_runner.utils.dom_traversal.shadow_iframe import (
selenium_query_in_shadow,
playwright_shadow_selector,
selenium_switch_iframe_chain,
)
from je_web_runner.utils.file_transfer.file_helpers import (
selenium_upload_file,
wait_for_download,
)
from je_web_runner.utils.extensions.extension_loader import (
selenium_chrome_options_with_extension,
playwright_extension_launch_args,
)
```
Service worker / cache control, console + network event capture and assertions, file upload via element + download dir watcher, browser extension loaders for Chromium-family.
## Test Data
```python
from je_web_runner import (
load_env, get_env, expand_in_action, # .env + ${ENV.X}
load_dataset_csv, load_dataset_json, run_with_dataset, # data-driven + ${ROW.x}
fake_email, fake_name, fake_credit_card, fake_value, # faker
)
from je_web_runner.utils.factories.factory import user_factory, order_factory
from je_web_runner.utils.testcontainers_integration.containers import (
start_postgres,
start_redis,
cleanup_all,
)
```
Every helper is JSON-callable too (`WR_load_env`, `WR_load_dataset_csv`, `WR_run_with_dataset`, `WR_faker_email`, `WR_user_factory`, `WR_tc_postgres`, …).
## Auth & APIs
```python
from je_web_runner import (
http_get, http_post, http_assert_status, http_assert_json_contains,
)
from je_web_runner.utils.auth.oauth import (
client_credentials_token,
bearer_header,
)
from je_web_runner.utils.database.db_validate import (
db_query,
db_assert_count,
db_assert_value,
)
token = client_credentials_token(
"https://idp.example/oauth2/token",
"client-id", "client-secret",
cache_key="default",
)
http_get("https://api.example/users/me", headers=bearer_header(token["access_token"]))
http_assert_status(200)
http_assert_json_contains("role", "admin")
db_assert_count(
"postgresql+psycopg://user:pw@host/db",
"SELECT 1 FROM orders WHERE user_id = :uid",
expected=1,
params={"uid": 42},
)
```
OAuth2 helpers cache tokens in-process and refresh 30 seconds before expiry.
## Recorder
```python
from je_web_runner import (
recorder_start,
recorder_stop,
recorder_save_recording,
)
recorder_start(webdriver_wrapper_instance)
# … user clicks / inputs in the browser …
recorder_save_recording(
webdriver_wrapper_instance,
output_path="./recorded.json",
raw_events_path="./raw.json", # optional — debugging
)
```
The recorder injects a static JS listener (no CDP, no eval), so it works on Chrome / Firefox / Edge alike. **Sensitive fields are masked by default** — `type=password`, names matching `password / card_number / cvv / ssn / secret / token / api_key / otp / passcode`, and 13–19-digit numeric values are replaced with `***MASKED***`.
## CI / Integrations
```python
from je_web_runner.utils.notifier.webhook_notifier import notify_run_summary
from je_web_runner.utils.test_management.jira_client import jira_create_failure_issues
from je_web_runner.utils.test_management.testrail_client import (
testrail_send_results,
testrail_results_from_pairs,
)
from je_web_runner.utils.ci_annotations.github_annotations import (
emit_failure_annotations,
emit_from_junit_xml,
)
```
For GitHub Actions inline annotations, run `emit_from_junit_xml("run_junit.xml")` after `generate_junit_xml_report` — failed test cases surface as `::error file=…::` lines on the PR diff.
`docker/docker-compose.yml` ships a Selenium Grid 4 stack (hub + Chrome + Firefox nodes); `docker/.env.example` exposes the version pin and concurrency settings.
The IDE config examples under [`docs/ide/`](docs/ide/) wire VS Code and JetBrains to the action JSON schema produced by `WR_export_action_schema`.
## AI Assistance
```python
from je_web_runner.utils.ai_assist.llm_assist import (
set_llm_callable,
suggest_locator,
generate_actions_from_prompt,
)
# Plug in any callable that returns a string:
def my_llm(prompt: str) -> str:
# call OpenAI / Anthropic / local Ollama / mock
...
set_llm_callable(my_llm)
locator = suggest_locator(html_blob, description="primary submit button")
draft = generate_actions_from_prompt("log in as alice and place an order")
```
WebRunner intentionally ships **no built-in LLM client** — the boundary is a single `Callable[[str], str]` so swapping provider is one line.
## CLI Usage
```bash
# Original entry points (unchanged):
python -m je_web_runner -e actions.json
python -m je_web_runner -d ./actions/
python -m je_web_runner --execute_str '[["WR_quit_all"]]'
# Newer flags:
python -m je_web_runner -d ./actions --tag smoke --exclude-tag slow
python -m je_web_runner -d ./actions --parallel 4 --parallel-mode process
python -m je_web_runner -d ./actions --ledger ledger.json
python -m je_web_runner -d ./actions --rerun-failed ledger.json
python -m je_web_runner -d ./actions --shard 1/4
python -m je_web_runner -d ./actions --watch ./actions
python -m je_web_runner --report run # JSON + HTML + XML + JUnit
python -m je_web_runner --validate ./action_smoke.json
python -m je_web_runner --migrate ./actions --migrate-dry-run
```
Compose any of the flags above; the dispatcher applies tag filters → ledger / re-run-failed → sharding → dependency-aware ordering before handing files to the runner.
## Test Record
```python
from je_web_runner import test_record_instance
test_record_instance.set_record_enable(True)
# … perform automation …
records = test_record_instance.test_record_list
# Each record: {"function_name", "local_param", "time", "program_exception"}
test_record_instance.clean_record()
```
## Exception Handling
WebRunner provides a hierarchy of custom exceptions — every helper raises a domain-specific subclass of `WebRunnerException`:
| Exception | Description |
|--------------------------------------------|--------------------------------------------------|
| `WebRunnerException` | Base |
| `WebRunnerWebDriverNotFoundException` | WebDriver not found |
| `WebRunnerOptionsWrongTypeException` | Invalid options type |
| `WebRunnerArgumentWrongTypeException` | Invalid argument type |
| `WebRunnerWebDriverIsNoneException` | WebDriver is None |
| `WebRunnerExecuteException` | Action execution error |
| `WebRunnerJsonException` | JSON processing error |
| `WebRunnerGenerateJsonReportException` | JSON / XML / JUnit / Allure report error |
| `WebRunnerHTMLException` | HTML report error |
| `WebRunnerAddCommandException` | Custom command registration error |
| `WebRunnerAssertException` | Assertion failure |
| `XMLException` / `XMLTypeException` | XML processing error |
| `CallbackExecutorException` | Callback execution error |
| `PlaywrightBackendError` | Playwright backend / element failure |
| `PlaywrightLocatorError` | TestObject → Playwright selector mapping |
| `RecorderError` / `VisualRegressionError` | Recorder / visual regression |
| `HealingError` / `EnvConfigError` / `DataDrivenError` | Self-healing / env / dataset |
| `HttpAssertionError` / `HttpError` | HTTP API assertions |
| `AccessibilityError` / `LighthouseError` | a11y / Lighthouse |
| `NotifierError` / `JiraError` / `TestRailError` | Notifications / test management |
| `CDPError` / `StorageError` / `ServiceWorkerError` | Browser internals |
| `OAuthError` / `DatabaseValidationError` | Auth / DB |
| `NetworkEmulationError` / `LoadTestError` | Throttling / Locust |
| `ShardingError` / `MigrationError` / `ActionLinterError` | Orchestration / linting |
| `LLMAssistError` / `OTelTracingError` | AI / observability |
## Logging
WebRunner uses a rotating file handler:
- **Log file:** `WEBRunner.log`
- **Level:** WARNING+
- **Max size:** 1 GB
- **Format:** `%(asctime)s | %(name)s | %(levelname)s | %(message)s`
## Supported Browsers
| Browser | Selenium key | Playwright |
|-------------------|--------------|--------------|
| Google Chrome | `chrome` | `chromium` |
| Chromium | `chromium` | `chromium` |
| Mozilla Firefox | `firefox` | `firefox` |
| Microsoft Edge | `edge` | `chromium` |
| Internet Explorer | `ie` | n/a |
| Apple Safari | `safari` | `webkit` |
## Supported Platforms
- Windows
- macOS
- Ubuntu / Linux
- Raspberry Pi
## License
This project is licensed under the [MIT License](LICENSE).