{"id":15288931,"url":"https://github.com/integration-automation/webrunner","last_synced_at":"2026-04-26T06:00:53.688Z","repository":{"id":37617701,"uuid":"441084354","full_name":"Integration-Automation/WebRunner","owner":"Integration-Automation","description":"A framework for Web automation","archived":false,"fork":false,"pushed_at":"2026-04-25T09:28:22.000Z","size":22397,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-25T10:27:39.250Z","etag":null,"topics":["automation","keyword-driven-testing","python","report-generator","selenium","selenium-python","selenium-webdriver","selenium4","testing","web-testing"],"latest_commit_sha":null,"homepage":"https://integration-automation.github.io/WebRunner/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Integration-Automation.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-12-23T06:26:42.000Z","updated_at":"2026-04-25T09:28:26.000Z","dependencies_parsed_at":"2025-02-23T03:31:19.354Z","dependency_job_id":"6fedebcd-3cbc-46c1-b63e-c6d005da0f3f","html_url":"https://github.com/Integration-Automation/WebRunner","commit_stats":null,"previous_names":["integration-automation/webrunner","intergration-automation-testing/webrunner","je-chen/seleniumwrapper_je"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Integration-Automation/WebRunner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Integration-Automation%2FWebRunner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Integration-Automation%2FWebRunner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Integration-Automation%2FWebRunner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Integration-Automation%2FWebRunner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Integration-Automation","download_url":"https://codeload.github.com/Integration-Automation/WebRunner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Integration-Automation%2FWebRunner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32287398,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T18:29:39.964Z","status":"online","status_checked_at":"2026-04-26T02:00:05.962Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","keyword-driven-testing","python","report-generator","selenium","selenium-python","selenium-webdriver","selenium4","testing","web-testing"],"created_at":"2024-09-30T15:54:43.021Z","updated_at":"2026-04-26T06:00:53.668Z","avatar_url":"https://github.com/Integration-Automation.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WebRunner\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eCross-platform web automation: Selenium + Playwright, plus a JSON-driven action executor with batteries included.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pypi.org/project/je-web-runner/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/je_web_runner\" alt=\"PyPI Version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.org/project/je-web-runner/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/pyversions/je_web_runner\" alt=\"Python Version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Intergration-Automation-Testing/WebRunner/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/Intergration-Automation-Testing/WebRunner\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://webrunner.readthedocs.io/en/latest/\"\u003e\u003cimg src=\"https://readthedocs.org/projects/webrunner/badge/?version=latest\" alt=\"Documentation Status\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"README/README_zh-TW.md\"\u003e繁體中文\u003c/a\u003e |\n  \u003ca href=\"README/README_zh-CN.md\"\u003e简体中文\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nWebRunner (`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.\n\n\u003e **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).\n\n## Table of Contents\n\n- [Highlights](#highlights)\n- [Installation](#installation)\n- [Architecture](#architecture)\n  - [System overview](#system-overview)\n  - [Action lifecycle](#action-lifecycle)\n  - [Backend dispatch](#backend-dispatch)\n  - [Module map](#module-map)\n- [Quick Start](#quick-start)\n- [Core API](#core-api)\n- [Action Executor](#action-executor)\n- [Backends](#backends)\n  - [Selenium (default)](#selenium-default)\n  - [Playwright (full)](#playwright-full)\n  - [Cloud Grid](#cloud-grid)\n  - [Appium (mobile)](#appium-mobile)\n- [Reports](#reports)\n- [Observability](#observability)\n- [Test Orchestration](#test-orchestration)\n- [Quality \u0026 Security](#quality--security)\n- [Browser Internals](#browser-internals)\n- [Test Data](#test-data)\n- [Auth \u0026 APIs](#auth--apis)\n- [Recorder](#recorder)\n- [CI / Integrations](#ci--integrations)\n- [AI Assistance](#ai-assistance)\n- [CLI Usage](#cli-usage)\n- [Test Record](#test-record)\n- [Exception Handling](#exception-handling)\n- [Logging](#logging)\n- [Supported Browsers](#supported-browsers)\n- [Supported Platforms](#supported-platforms)\n- [License](#license)\n\n## Highlights\n\n- **Two backends, one executor.** Selenium is the default; the Playwright backend mirrors the same operational surface under `WR_pw_*` and is fully opt-in.\n- **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.\n- **Reports in five formats.** HTML, JSON, XML, JUnit XML (CI-native), and Allure result files; a single manifest binds every output for downstream globs.\n- **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.\n- **Observability without extra plumbing.** Auto-screenshot on failure, retry policy, OpenTelemetry hook, live HTTP dashboard, replay studio (HTML timeline), HAR capture + diff.\n- **Quality \u0026 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.\n- **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.\n- **Test data \u0026 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}`.\n- **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.\n- **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.\n- **AI hooks.** Pluggable LLM callable powers self-healing locators and natural-language → action JSON drafts.\n- **Cross-platform \u0026 multi-browser.** Windows, macOS, Linux, Raspberry Pi · Chrome, Chromium, Firefox, Edge, IE, Safari · Chromium, Firefox, WebKit (Playwright).\n\n## Installation\n\n**Stable:**\n\n```bash\npip install je_web_runner\n```\n\n**Development:**\n\n```bash\npip install je_web_runner_dev\n```\n\n**Optional dependencies** (each enables a slice of features; install only what you use):\n\n```bash\npip install playwright           # Playwright backend\npython -m playwright install     # Browser binaries for Playwright\npip install Pillow               # Visual regression\npip install faker                # Random test data (WR_faker_*)\npip install sqlalchemy           # Database validation (WR_db_*)\npip install opentelemetry-sdk    # Distributed traces (WR_set_action_span_factory)\npip install Appium-Python-Client # Mobile native (WR_appium_*)\npip install testcontainers       # Spin up Postgres / Redis (WR_tc_*)\npip install locust               # Load testing (WR_locust_*)\n```\n\nHard requirements: Python **3.10+**, `selenium\u003e=4.0.0`, `requests`, `python-dotenv`, `webdriver-manager`, `defusedxml`, `Pillow`.\n\n## Architecture\n\n### System overview\n\n```mermaid\nflowchart LR\n  subgraph Authoring\n    A1[\"Action JSON files\"]\n    A2[\"Programmatic Python API\"]\n    A3[\"Browser recorder\u003cbr/\u003e(JS injection)\"]\n    A4[\"LLM NL → action draft\"]\n  end\n\n  subgraph Core\n    EXE[\"Executor\u003cbr/\u003eevent_dict\"]\n    REC[\"Test record\u003cbr/\u003esingleton\"]\n    LDG[\"Run ledger /\u003cbr/\u003eflaky detection\"]\n  end\n\n  subgraph Backends\n    SEL[\"Selenium\u003cbr/\u003eWebDriverWrapper\"]\n    PW[\"Playwright\u003cbr/\u003ePlaywrightWrapper\"]\n    APM[\"Appium\u003cbr/\u003eMobile\"]\n    HTTP[\"HTTP API\u003cbr/\u003erequests\"]\n    DB[\"Database\u003cbr/\u003eSQLAlchemy\"]\n  end\n\n  subgraph Outputs\n    REP[\"Reports\u003cbr/\u003eHTML/JSON/XML/JUnit/Allure\"]\n    OBS[\"Observability\u003cbr/\u003eOTel · dashboard · replay\"]\n    NOT[\"Notifiers\u003cbr/\u003eSlack · webhook · GH · JIRA · TestRail\"]\n  end\n\n  A1 --\u003e EXE\n  A2 --\u003e EXE\n  A3 --\u003e A1\n  A4 --\u003e A1\n  EXE --\u003e SEL\n  EXE --\u003e PW\n  EXE --\u003e APM\n  EXE --\u003e HTTP\n  EXE --\u003e DB\n  SEL --\u003e REC\n  PW --\u003e REC\n  APM --\u003e REC\n  HTTP --\u003e REC\n  DB --\u003e REC\n  REC --\u003e LDG\n  REC --\u003e REP\n  REC --\u003e OBS\n  REC --\u003e NOT\n```\n\n### Action lifecycle\n\n```mermaid\nflowchart LR\n  IN[\"Action\u003cbr/\u003e[cmd, args, kwargs]\"] --\u003e VAL[\"JSON validator\u003cbr/\u003e(WR_validate_*)\"]\n  VAL --\u003e ENV[\"${ENV.X} / ${ROW.x}\u003cbr/\u003eplaceholder expansion\"]\n  ENV --\u003e SPAN[\"OTel span factory\u003cbr/\u003e(optional)\"]\n  SPAN --\u003e RETRY[\"Retry policy\u003cbr/\u003eretries × backoff\"]\n  RETRY --\u003e GATE[\"Arbitrary-script\u003cbr/\u003egate\"]\n  GATE --\u003e DISP[\"event_dict[cmd](*args, **kwargs)\"]\n  DISP --\u003e RECORD[\"test_record_instance\u003cbr/\u003eappend()\"]\n  DISP -- failure --\u003e SHOT[\"Auto-screenshot\u003cbr/\u003e(failure dir)\"]\n  RECORD --\u003e DONE[\"Result dict\"]\n  SHOT --\u003e DONE\n```\n\n### Backend dispatch\n\n```mermaid\nflowchart TB\n  CMD[\"Action command name\"] --\u003e ROUTE{\"prefix?\"}\n  ROUTE -- \"WR_pw_*\" --\u003e PW[\"Playwright backend\u003cbr/\u003e(PlaywrightWrapper)\"]\n  ROUTE -- \"WR_pw_element_*\" --\u003e PWE[\"Playwright element\u003cbr/\u003e(PlaywrightElementWrapper)\"]\n  ROUTE -- \"WR_appium_*\" --\u003e APM[\"Appium driver\"]\n  ROUTE -- \"WR_http_*\" --\u003e HTTP[\"requests wrapper\"]\n  ROUTE -- \"WR_db_*\" --\u003e DB[\"SQLAlchemy validator\"]\n  ROUTE -- \"WR_pw_a11y_* / WR_a11y_*\" --\u003e AXE[\"axe-core audit\"]\n  ROUTE -- \"WR_pw_throttle / WR_throttle\" --\u003e THR[\"Network throttling\u003cbr/\u003e(CDP)\"]\n  ROUTE -- \"WR_pw_route_*\" --\u003e ROUTE_MOCK[\"Playwright route mock\"]\n  ROUTE -- \"WR_*\u003cbr/\u003e(default)\" --\u003e SEL[\"Selenium backend\u003cbr/\u003e(WebDriverWrapper)\"]\n  ROUTE -- \"WR_element_*\u003cbr/\u003e(default)\" --\u003e SE[\"Selenium element\u003cbr/\u003e(WebElementWrapper)\"]\n```\n\n### Module map\n\n```\nje_web_runner/\n├── __init__.py\n├── __main__.py                    # CLI: --execute_dir / --watch / --tag / --shard / --migrate ...\n├── element/web_element_wrapper.py\n├── manager/webrunner_manager.py\n├── webdriver/\n│   ├── webdriver_wrapper.py             # Selenium core\n│   ├── webdriver_with_options.py\n│   ├── playwright_wrapper.py            # Playwright sync backend (full)\n│   ├── playwright_element_wrapper.py\n│   └── playwright_locator.py            # TestObject ↔ Playwright selector\n└── utils/\n    ├── ab_run/                  # A/B run mode (run_ab + diff_records)\n    ├── accessibility/           # axe-core audit\n    ├── ai_assist/               # Pluggable LLM scaffold\n    ├── api/                     # HTTP API testing commands\n    ├── appium_integration/      # Mobile native via Appium\n    ├── auth/                    # OAuth2 / OIDC token helpers\n    ├── callback/                # Callback executor\n    ├── cdp/                     # Raw CDP passthrough\n    ├── ci_annotations/          # GitHub Actions ::error::\n    ├── cli/                     # CLI parser, watch mode, dispatch\n    ├── cloud_grid/              # BrowserStack / Sauce Labs / LambdaTest\n    ├── dashboard/               # Live progress HTTP server\n    ├── database/                # SQL validation (SQLAlchemy)\n    ├── data_driven/             # CSV/JSON dataset + ${ROW.x}\n    ├── docs/                    # Auto-generated command reference\n    ├── dom_traversal/           # Shadow DOM / iframe helpers\n    ├── env_config/              # .env loader + ${ENV.X}\n    ├── exception/               # Exception hierarchy\n    ├── executor/                # Action executor + retry/screenshot/gate\n    ├── extensions/              # Browser extension loaders\n    ├── factories/               # Factory pattern helpers\n    ├── file_process/            # File utilities\n    ├── file_transfer/           # Upload / download helpers\n    ├── generate_report/         # HTML/JSON/XML/JUnit/Allure + manifest\n    ├── har_diff/                # HAR file diff\n    ├── json/                    # JSON I/O + validator (length 1/2/3)\n    ├── lighthouse/              # Lighthouse CLI runner\n    ├── linter/                  # action_linter + migration\n    ├── load_test/               # Locust wrapper\n    ├── logging/                 # Rotating file handler\n    ├── multi_user/              # Multi-user matrix runner\n    ├── network_emulation/       # Throttling presets via CDP\n    ├── notifier/                # Slack / generic webhooks\n    ├── observability/           # Console+network capture · OTel\n    ├── package_manager/         # Dynamic plugin loader\n    ├── perf_metrics/            # FCP / LCP / CLS / TTFB\n    ├── pom_generator/           # POM skeleton from URL/HTML\n    ├── project/                 # Project template generator\n    ├── recorder/                # JS-injection recorder + PII mask\n    ├── replay_studio/           # HTML timeline studio\n    ├── run_ledger/              # ledger · flaky · classifier\n    ├── schema/                  # Action JSON Schema export\n    ├── scheduler/               # stdlib-sched scheduled runner\n    ├── secrets_scanner/         # Hard-coded credential scanner\n    ├── security_headers/        # HTTP headers audit\n    ├── selenium_utils_wrapper/  # Keys / Capabilities\n    ├── self_healing/            # Fallback locator registry\n    ├── service_worker/          # SW unregister + cache clear\n    ├── sharding/                # Deterministic test sharding\n    ├── snapshot/                # Text/DOM snapshot testing\n    ├── socket_server/           # TCP server with token + TLS\n    ├── storage/                 # localStorage / session / IDB\n    ├── test_data/               # Faker integration\n    ├── test_filter/             # Tag filter + dependency graph\n    ├── test_management/         # JIRA + TestRail\n    ├── test_object/             # TestObject + record\n    ├── test_record/             # Action recording\n    ├── testcontainers_integration/   # Postgres / Redis / generic\n    ├── visual_regression/       # Pillow-based image diff\n    └── xml/                     # XML utilities\n```\n\n## Quick Start\n\n### Direct API\n\n```python\nfrom je_web_runner import TestObject, get_webdriver_manager, web_element_wrapper\n\nmanager = get_webdriver_manager(\"chrome\")\nmanager.webdriver_wrapper.to_url(\"https://www.google.com\")\nmanager.webdriver_wrapper.implicitly_wait(2)\n\nsearch_box = TestObject(\"q\", \"name\")\nmanager.webdriver_wrapper.find_element(search_box)\nweb_element_wrapper.click_element()\nweb_element_wrapper.input_to_element(\"WebRunner automation\")\n\nmanager.quit()\n```\n\n### JSON action list (modern aliases)\n\n```python\nfrom je_web_runner import execute_action\n\nactions = [\n    [\"WR_new_driver\", {\"webdriver_name\": \"chrome\"}],\n    [\"WR_to_url\", {\"url\": \"https://www.google.com\"}],\n    [\"WR_implicitly_wait\", {\"time_to_wait\": 2}],\n    [\"WR_save_test_object\", {\"test_object_name\": \"q\", \"object_type\": \"NAME\"}],\n    [\"WR_find_recorded_element\", {\"element_name\": \"q\"}],\n    [\"WR_element_click\"],\n    [\"WR_element_input\", {\"input_value\": \"WebRunner automation\"}],\n    [\"WR_quit_all\"],\n]\nexecute_action(actions)\n```\n\nThe legacy names (`WR_get_webdriver_manager`, `WR_SaveTestObject`, `WR_quit`, `WR_input_to_element`, …) still work — see [Quality \u0026 Security](#quality--security) for the one-shot migration helper.\n\n### Mixed positional + keyword arguments\n\n```python\n[\n    [\"WR_to_url\", [\"https://example.com\"], {\"timeout\": 30}],\n]\n```\n\nThe validator accepts length-1, length-2 (`[cmd, dict_or_list]`), and length-3 (`[cmd, [positional], {kwargs}]`) actions.\n\n## Core API\n\nThe original Selenium-flavoured API remains the canonical entry point for programmatic use. Sections preserved from the original README:\n\n- **WebDriver Manager** — `get_webdriver_manager`, `new_driver`, `change_webdriver`, `close_choose_webdriver`, `quit`.\n- **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`.\n- **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`.\n- **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']`).\n\nProgrammatic 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.\n\n## Action Executor\n\nThe executor maps a string command name to a Python callable. Every backend, integration, and helper registers under `event_dict`.\n\n### Action shapes\n\n```python\n[\"command\"]                                    # no args\n[\"command\", {\"key\": \"value\"}]                  # kwargs\n[\"command\", [arg1, arg2]]                      # positional\n[\"command\", [arg1], {\"key\": \"value\"}]          # positional + kwargs (length 3)\n```\n\n### Length-3 example\n\n```python\n[\n    [\"WR_pw_evaluate\", [\"() =\u003e document.title\"], {\"arg\": None}],\n]\n```\n\n### Top-level shapes\n\n```python\n[ ...actions... ]                                                  # bare list\n\n{\n  \"webdriver_wrapper\": [ ...actions... ],\n  \"meta\": {\"tags\": [\"smoke\", \"fast\"], \"depends_on\": [\"login\"]}     # optional\n}\n```\n\n`meta.tags` and `meta.depends_on` are picked up by the CLI for filtering and topological execution.\n\n### Adding custom commands\n\n```python\nfrom je_web_runner import add_command_to_executor\n\ndef my_step(name: str) -\u003e None:\n    print(f\"hello {name}\")\n\nadd_command_to_executor({\"my_command\": my_step})\n```\n\n### Retry, screenshots, scripts\n\n```python\nfrom je_web_runner.utils.executor.action_executor import executor\n\nexecutor.set_retry_policy(retries=2, backoff=0.5)             # global retry\nexecutor.set_failure_screenshot_dir(\"./failures\")              # auto PNG on raise\nexecutor.set_allow_arbitrary_script(False)                     # gate WR_execute_script / WR_pw_evaluate / WR_cdp\n```\n\n## Backends\n\n### Selenium (default)\n\nSelenium is the original backend. Every legacy command (and its modern alias) routes here unless an explicit `WR_pw_*` / `WR_appium_*` prefix is used.\n\n### Playwright (full)\n\nThe Playwright backend mirrors the operational surface of the Selenium wrapper under `WR_pw_*`:\n\n- **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`.\n- **Find** — `WR_pw_find_element`, `WR_pw_find_elements`, `WR_pw_find_element_with_test_object_record`, `WR_pw_find_with_healing`.\n- **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`.\n- **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`.\n- **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_*`.\n- **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`.\n- **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`.\n\nExisting 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(\"…\")`).\n\n### Cloud Grid\n\n```python\nfrom je_web_runner import (\n    connect_browserstack,\n    build_browserstack_capabilities,\n)\n\nconnect_browserstack(\n    username=\"...\",\n    access_key=\"...\",\n    capabilities=build_browserstack_capabilities(\n        browser_name=\"chrome\",\n        browser_version=\"latest\",\n        os_name=\"Windows\",\n        os_version=\"11\",\n        project=\"WebRunner\",\n        build=\"ci-2026-04-26\",\n    ),\n)\n# All existing WR_* commands now run against the cloud session.\n```\n\n`connect_saucelabs` and `connect_lambdatest` follow the same shape.\n\n### Appium (mobile)\n\n```python\nfrom je_web_runner import (\n    start_appium_session,\n    build_android_caps,\n    build_ios_caps,\n)\n\nstart_appium_session(\n    \"https://appium.example/wd/hub\",\n    capabilities=build_android_caps(app=\"/path/to/app.apk\"),\n)\n# WR_* commands now drive the mobile session.\n```\n\n## Reports\n\n```python\nfrom je_web_runner import (\n    generate_html_report,\n    generate_json_report,\n    generate_xml_report,\n    generate_junit_xml_report,\n    generate_allure_report,\n)\nfrom je_web_runner.utils.generate_report.report_manifest import generate_all_reports\n\n# Run every generator + write a manifest binding all outputs:\nresult = generate_all_reports(\"run_2026_04_26\", allure_dir=\"allure-results\")\nprint(result[\"manifest_path\"])  # → run_2026_04_26.manifest.json\n```\n\n| Format    | Output shape                                             | Spec-driven? |\n|-----------|----------------------------------------------------------|--------------|\n| JSON      | `\u003cbase\u003e_success.json` + `\u003cbase\u003e_failure.json`            | split        |\n| HTML      | `\u003cbase\u003e.html`                                            | single       |\n| XML       | `\u003cbase\u003e_success.xml` + `\u003cbase\u003e_failure.xml`              | split        |\n| JUnit XML | `\u003cbase\u003e_junit.xml`                                       | single       |\n| Allure    | `\u003callure_dir\u003e/\u003cuuid\u003e-result.json` (× N)                  | directory    |\n\nThe manifest captures the actual paths produced — CI globs no longer need to know the per-format conventions.\n\n## Observability\n\n```python\nfrom je_web_runner import (\n    test_record_instance,\n    summarise_run,\n    notify_run_summary,\n)\nfrom je_web_runner.utils.executor.action_executor import executor\nfrom je_web_runner.utils.observability.otel_tracing import install_executor_tracing\nfrom je_web_runner.utils.dashboard.live_dashboard import start_dashboard\nfrom je_web_runner.utils.replay_studio.replay_studio import export_replay_studio\n\nexecutor.set_failure_screenshot_dir(\"./failures\")\ninstall_executor_tracing(\"webrunner\")                 # one OTel span per action\nstart_dashboard(\"127.0.0.1\", 8080)                    # browser-friendly progress UI\ntest_record_instance.set_record_enable(True)\n\n# … run actions …\n\nexport_replay_studio(\"./run.html\", screenshot_dir=\"./failures\")\nnotify_run_summary(\"https://hooks.slack.com/services/...\")\n```\n\nFailure screenshot, OpenTelemetry tracing, retry policy, and the live dashboard all hook into the same `Executor.event_dict` so they compose without coupling.\n\n## Test Orchestration\n\n```bash\n# Filter by tag, run in parallel processes, persist a ledger, fail fast on dep breaks.\npython -m je_web_runner \\\n    --execute_dir ./actions \\\n    --tag smoke,fast \\\n    --exclude-tag slow \\\n    --parallel 4 \\\n    --parallel-mode process \\\n    --ledger ./.run_ledger.json\n\n# Re-run only the files that failed last time:\npython -m je_web_runner --execute_dir ./actions --rerun-failed ./.run_ledger.json\n\n# Watch a directory and re-run on file change:\npython -m je_web_runner --execute_dir ./actions --watch ./actions\n\n# Distribute across 4 runners deterministically (per machine):\npython -m je_web_runner --execute_dir ./actions --shard 1/4\npython -m je_web_runner --execute_dir ./actions --shard 2/4\npython -m je_web_runner --execute_dir ./actions --shard 3/4\npython -m je_web_runner --execute_dir ./actions --shard 4/4\n```\n\nCompanion 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`.\n\n## Quality \u0026 Security\n\n- **Action linter** — `WR_lint_action` / `WR_lint_action_file` flag legacy command names, hard-coded URLs, dangerous scripts, missing tags, duplicate consecutive actions.\n- **Migration helper** — `python -m je_web_runner --migrate ./actions` rewrites the eleven legacy aliases to their preferred names (`--migrate-dry-run` reports without writing).\n- **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.\n- **Security headers audit** — `WR_audit_security_headers_url` checks HSTS / CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy.\n- **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`.\n- **Lighthouse** — `WR_lighthouse_run` shells out to the official `lighthouse` Node CLI; `WR_lighthouse_assert_scores` enforces budgets.\n- **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.\n- **Visual regression** — `WR_visual_capture_baseline` + `WR_visual_compare` (Pillow soft-dep).\n- **Snapshot testing** — `WR_match_snapshot` / `WR_update_snapshot` (text/DOM, unified diff on mismatch).\n- **Network throttling** — `WR_throttle(\"slow_3g\")` / `WR_pw_throttle(\"offline\")`; presets cover Slow 3G, Fast 3G, Regular 4G, Wi-Fi, Offline, no-throttling.\n- **HAR diff** — `WR_diff_har` / `WR_diff_har_files` show added / removed / status-changed requests between two runs.\n- **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.\n\n## Browser Internals\n\n```python\nfrom je_web_runner import (\n    selenium_cdp,                 # raw CDP\n    pw_emulate, pw_set_locale,    # mobile / locale\n)\nfrom je_web_runner.utils.storage.browser_storage import (\n    selenium_local_storage_set,\n    selenium_indexed_db_drop,\n)\nfrom je_web_runner.utils.observability.event_capture import (\n    start_event_capture,\n    assert_no_console_errors,\n    assert_no_5xx,\n)\nfrom je_web_runner.utils.dom_traversal.shadow_iframe import (\n    selenium_query_in_shadow,\n    playwright_shadow_selector,\n    selenium_switch_iframe_chain,\n)\nfrom je_web_runner.utils.file_transfer.file_helpers import (\n    selenium_upload_file,\n    wait_for_download,\n)\nfrom je_web_runner.utils.extensions.extension_loader import (\n    selenium_chrome_options_with_extension,\n    playwright_extension_launch_args,\n)\n```\n\nService worker / cache control, console + network event capture and assertions, file upload via element + download dir watcher, browser extension loaders for Chromium-family.\n\n## Test Data\n\n```python\nfrom je_web_runner import (\n    load_env, get_env, expand_in_action,                   # .env + ${ENV.X}\n    load_dataset_csv, load_dataset_json, run_with_dataset, # data-driven + ${ROW.x}\n    fake_email, fake_name, fake_credit_card, fake_value,   # faker\n)\nfrom je_web_runner.utils.factories.factory import user_factory, order_factory\nfrom je_web_runner.utils.testcontainers_integration.containers import (\n    start_postgres,\n    start_redis,\n    cleanup_all,\n)\n```\n\nEvery 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`, …).\n\n## Auth \u0026 APIs\n\n```python\nfrom je_web_runner import (\n    http_get, http_post, http_assert_status, http_assert_json_contains,\n)\nfrom je_web_runner.utils.auth.oauth import (\n    client_credentials_token,\n    bearer_header,\n)\nfrom je_web_runner.utils.database.db_validate import (\n    db_query,\n    db_assert_count,\n    db_assert_value,\n)\n\ntoken = client_credentials_token(\n    \"https://idp.example/oauth2/token\",\n    \"client-id\", \"client-secret\",\n    cache_key=\"default\",\n)\nhttp_get(\"https://api.example/users/me\", headers=bearer_header(token[\"access_token\"]))\nhttp_assert_status(200)\nhttp_assert_json_contains(\"role\", \"admin\")\n\ndb_assert_count(\n    \"postgresql+psycopg://user:pw@host/db\",\n    \"SELECT 1 FROM orders WHERE user_id = :uid\",\n    expected=1,\n    params={\"uid\": 42},\n)\n```\n\nOAuth2 helpers cache tokens in-process and refresh 30 seconds before expiry.\n\n## Recorder\n\n```python\nfrom je_web_runner import (\n    recorder_start,\n    recorder_stop,\n    recorder_save_recording,\n)\n\nrecorder_start(webdriver_wrapper_instance)\n# … user clicks / inputs in the browser …\nrecorder_save_recording(\n    webdriver_wrapper_instance,\n    output_path=\"./recorded.json\",\n    raw_events_path=\"./raw.json\",  # optional — debugging\n)\n```\n\nThe 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***`.\n\n## CI / Integrations\n\n```python\nfrom je_web_runner.utils.notifier.webhook_notifier import notify_run_summary\nfrom je_web_runner.utils.test_management.jira_client import jira_create_failure_issues\nfrom je_web_runner.utils.test_management.testrail_client import (\n    testrail_send_results,\n    testrail_results_from_pairs,\n)\nfrom je_web_runner.utils.ci_annotations.github_annotations import (\n    emit_failure_annotations,\n    emit_from_junit_xml,\n)\n```\n\nFor 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.\n\n`docker/docker-compose.yml` ships a Selenium Grid 4 stack (hub + Chrome + Firefox nodes); `docker/.env.example` exposes the version pin and concurrency settings.\n\nThe IDE config examples under [`docs/ide/`](docs/ide/) wire VS Code and JetBrains to the action JSON schema produced by `WR_export_action_schema`.\n\n## AI Assistance\n\n```python\nfrom je_web_runner.utils.ai_assist.llm_assist import (\n    set_llm_callable,\n    suggest_locator,\n    generate_actions_from_prompt,\n)\n\n# Plug in any callable that returns a string:\ndef my_llm(prompt: str) -\u003e str:\n    # call OpenAI / Anthropic / local Ollama / mock\n    ...\n\nset_llm_callable(my_llm)\n\nlocator = suggest_locator(html_blob, description=\"primary submit button\")\ndraft = generate_actions_from_prompt(\"log in as alice and place an order\")\n```\n\nWebRunner intentionally ships **no built-in LLM client** — the boundary is a single `Callable[[str], str]` so swapping provider is one line.\n\n## CLI Usage\n\n```bash\n# Original entry points (unchanged):\npython -m je_web_runner -e actions.json\npython -m je_web_runner -d ./actions/\npython -m je_web_runner --execute_str '[[\"WR_quit_all\"]]'\n\n# Newer flags:\npython -m je_web_runner -d ./actions --tag smoke --exclude-tag slow\npython -m je_web_runner -d ./actions --parallel 4 --parallel-mode process\npython -m je_web_runner -d ./actions --ledger ledger.json\npython -m je_web_runner -d ./actions --rerun-failed ledger.json\npython -m je_web_runner -d ./actions --shard 1/4\npython -m je_web_runner -d ./actions --watch ./actions\npython -m je_web_runner --report run                          # JSON + HTML + XML + JUnit\npython -m je_web_runner --validate ./action_smoke.json\npython -m je_web_runner --migrate ./actions --migrate-dry-run\n```\n\nCompose any of the flags above; the dispatcher applies tag filters → ledger / re-run-failed → sharding → dependency-aware ordering before handing files to the runner.\n\n## Test Record\n\n```python\nfrom je_web_runner import test_record_instance\n\ntest_record_instance.set_record_enable(True)\n# … perform automation …\nrecords = test_record_instance.test_record_list\n# Each record: {\"function_name\", \"local_param\", \"time\", \"program_exception\"}\ntest_record_instance.clean_record()\n```\n\n## Exception Handling\n\nWebRunner provides a hierarchy of custom exceptions — every helper raises a domain-specific subclass of `WebRunnerException`:\n\n| Exception                                  | Description                                      |\n|--------------------------------------------|--------------------------------------------------|\n| `WebRunnerException`                       | Base                                             |\n| `WebRunnerWebDriverNotFoundException`      | WebDriver not found                              |\n| `WebRunnerOptionsWrongTypeException`       | Invalid options type                             |\n| `WebRunnerArgumentWrongTypeException`      | Invalid argument type                            |\n| `WebRunnerWebDriverIsNoneException`        | WebDriver is None                                |\n| `WebRunnerExecuteException`                | Action execution error                           |\n| `WebRunnerJsonException`                   | JSON processing error                            |\n| `WebRunnerGenerateJsonReportException`     | JSON / XML / JUnit / Allure report error         |\n| `WebRunnerHTMLException`                   | HTML report error                                |\n| `WebRunnerAddCommandException`             | Custom command registration error                |\n| `WebRunnerAssertException`                 | Assertion failure                                |\n| `XMLException` / `XMLTypeException`        | XML processing error                             |\n| `CallbackExecutorException`                | Callback execution error                         |\n| `PlaywrightBackendError`                   | Playwright backend / element failure             |\n| `PlaywrightLocatorError`                   | TestObject → Playwright selector mapping         |\n| `RecorderError` / `VisualRegressionError`  | Recorder / visual regression                     |\n| `HealingError` / `EnvConfigError` / `DataDrivenError` | Self-healing / env / dataset            |\n| `HttpAssertionError` / `HttpError`         | HTTP API assertions                              |\n| `AccessibilityError` / `LighthouseError`   | a11y / Lighthouse                                |\n| `NotifierError` / `JiraError` / `TestRailError` | Notifications / test management            |\n| `CDPError` / `StorageError` / `ServiceWorkerError` | Browser internals                        |\n| `OAuthError` / `DatabaseValidationError`   | Auth / DB                                        |\n| `NetworkEmulationError` / `LoadTestError`  | Throttling / Locust                              |\n| `ShardingError` / `MigrationError` / `ActionLinterError` | Orchestration / linting              |\n| `LLMAssistError` / `OTelTracingError`      | AI / observability                               |\n\n## Logging\n\nWebRunner uses a rotating file handler:\n\n- **Log file:** `WEBRunner.log`\n- **Level:** WARNING+\n- **Max size:** 1 GB\n- **Format:** `%(asctime)s | %(name)s | %(levelname)s | %(message)s`\n\n## Supported Browsers\n\n| Browser           | Selenium key | Playwright   |\n|-------------------|--------------|--------------|\n| Google Chrome     | `chrome`     | `chromium`   |\n| Chromium          | `chromium`   | `chromium`   |\n| Mozilla Firefox   | `firefox`    | `firefox`    |\n| Microsoft Edge    | `edge`       | `chromium`   |\n| Internet Explorer | `ie`         | n/a          |\n| Apple Safari      | `safari`     | `webkit`     |\n\n## Supported Platforms\n\n- Windows\n- macOS\n- Ubuntu / Linux\n- Raspberry Pi\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintegration-automation%2Fwebrunner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fintegration-automation%2Fwebrunner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintegration-automation%2Fwebrunner/lists"}