A lightweight yet robust test automation framework designed for learning and architectural demonstration.
It utilizes the Page Object Model (POM) pattern, a custom Click-based CLI orchestrator, and Pytest to handle UI automation with Playwright.
This project is augmented with a shared core testing layer providing failure classification, process-safe flaky test history tracking, and self-healing locators (supporting both local heuristics and AI models).
-
Execution Branches
tests/no_ai/: Completely deterministic production-ready tests using localized self-healing heuristics.tests/ai/: AI-assisted testing integrating open-source local and cloud-hosted LLM endpoints.
-
Failure Classification
- Parses test failures and categorizes exceptions into typed results (
Timeout,Locator Issue,Assertion,Other). - Automatically extracts failed locators from exception messages.
- Parses test failures and categorizes exceptions into typed results (
-
Process-Safe Flaky Detection
- Tracks historical results using SQLite (WAL-mode) or JSON (with cross-platform file locking).
- Enforces
min_runs=3sample guard before flagging test cases as flaky.
-
Self-Healing Locator Guardrails
- Captures
before_heal.pngandafter_heal.pngscreenshots on successful locator healing. - Caps healing attempts at 3 per action, requiring a minimum similarity score threshold of 0.5.
- Healed tests are tracked as
PASSED (healed)in summaries.
- Captures
-
Multi-Provider AI Client
- Dynamically routes requests for selector healing and planning to:
- Local Ollama:
qwen2.5-coder:1.5brunning onhttp://localhost:11434. - Hugging Face Serverless API:
Qwen/Qwen2.5-Coder-7B-InstructusingHF_API_TOKEN. - Gemini API:
gemini-2.5-flashusingGEMINI_API_KEY. - Local Heuristics: Falls back to offline similarity logic if APIs are unavailable.
- Local Ollama:
- Dynamically routes requests for selector healing and planning to:
For a complete architectural overview, refer to docs/architecture.md.
- Action-Level Interception: Intercepts element failures in real-time inside
HealingLocator._run_action_with_healing()before the test case fails. - Before/After Evidence: Captures
before_heal_<ts>.png(showing missing/broken selector state) andafter_heal_<ts>.png(showing recovered state). - Candidate Scoring:
dom_extractor.pyscans active browser elements via client-side JavaScript, andstrategies.pyranks candidates using string similarity & attribute intersection boosts (ID +0.2, Name +0.2, Data-Test +0.3). - Git Patch Generation: Writes
reports/no_ai/healing_patch.diffwith 3-line unified diff context lines, allowing CI/CD to auto-create Pull Requests.
- Invoked By:
pytest_runtest_makereporthook in conftest.py. - When: Runs at test completion (
rep.when == 'call') wheneverrep.failed == Trueand--classifyis active. - Rules: Categorizes failures into taxonomy buckets (
Assertion,Locator Issue,Timeout,Other) and extracts target locators using regex.
-
Phase 1 (Recording):
conftest.pyrecords'passed'or'failed'for every run/retry intoreports/flaky_history.json(atomic viaFileLock). -
Phase 2 (Scoring):
reporter.pycomputes variance score:$$\text{Flaky Score} = \frac{2 \times \min(\text{passes}, \text{failures})}{\text{total runs}}$$ Tests alternating between pass and fail ($\text{score} \ge 0.2$ ) are flagged as FLAKY DETECTED on the HTML dashboard.
test-forge/
โโโ src/
โ โโโ pages/ # Page Object Model classes (LoginPage, Inventory, Cart, Checkout)
โ โโโ framework/ # Core framework layer
โ โ โโโ no_ai/ # Heuristic healing: healer, strategies, dom_extractor
โ โ โโโ ai/ # AI-assisted healing: ai_helper routing client
โ โ โโโ core/ # Shared core features: failure_classification, flaky_detection
โ โ โโโ core/ # Shared core features: failure_classification, flaky_detection
โ โโโ tests/ # Internal test helpers / utilities
โโโ tests/ # Pytest tests divided by execution branch
โ โโโ no_ai/ # Deterministic healing & classification (unique test flows)
โ โโโ ai/ # AI-assisted healing (distinct checkout and locator flows)
โ โโโ utils/ # Framework unit tests
โ โโโ conftest.py # Pytest hooks, page wrappers, command flags
โโโ scripts/
โ โโโ generate_dashboard.py # Python script generating GitHub Pages dashboard index.html
โโโ runner.py # CLI entrypoint
โโโ orchestrator.py # Runner pipeline execution manager
โโโ reporter.py # Summary generator & visual report manager
โโโ Makefile # Standardized local execution commands (Ruff, Install, Test)
โโโ docs/ # Design architecture & documentation
All execution and environment setup is standardized around the Makefile.
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activatemake install
make install-browsersWe use Ruff for linting and code formatting:
make lint # Checks lint and formatting
make lint-fix # Automatically fixes lint violations and reformats filesRun branch-specific suites with automated setup flags using simple make targets:
- Deterministic Suite (No AI): Runs with self-healing, classification, and 2 retries.
make test-no-ai
- AI-Assisted Suite: Runs with AI-assisted locator healing and classification.
# Set your API token before running: export HF_API_TOKEN="your-hf-token" # Or: export GEMINI_API_KEY="your-gemini-key" make test-ai
- Run All Suites: Runs both execution branches sequentially.
make test-all
- Clean Outputs: Cleans all generated reports, screenshots, videos, and logs.
make clear
Outputs are saved under the reports/[branch_name]/ directory (e.g., reports/no_ai/ or reports/ai/):
report.html: Custom HTML report with embedded screenshots and Playwright execution videos.run_summary.json: Unified statistics containing passed, healed, failed, and flaky counts.failure_classification.json: Categorized exceptions and target locator details.healing_patch.diff: Git diff containing the suggested code modifications for healed locators.
The repository uses two scheduled and event-driven GitHub Actions workflows:
- Test Forge CI/CD Pipeline (
.github/workflows/test-forge.yml):- Triggers: Push to
master, manually, or daily at 02:00 UTC. - Jobs: Enforces Ruff linter, runs all three test suites in parallel, automatically opens a Pull Request with healed locators if a patch is found, and publishes the consolidated dashboard to GitHub Pages.
- PR Title Formats:
[AI Self-Healing] Fix locator drift in ai - DO NOT MERGE[Non-AI Self-Healing] Fix locator drift in no_ai - DO NOT MERGE
- Triggers: Push to
- Cleanup Stale PRs (
.github/workflows/cleanup-stale-prs.yml):- Triggers: Daily at 03:00 UTC (1 hour after tests) or manually.
- Jobs: Finds open Pull Requests containing
Self-Healingin the title that are older than 1 day, automatically closes them, and deletes their temporary branches to keep the repository clean.
Problem: Running the full suite on every push is expensive and slow.
Solution: Build an impact_analyzer.py pre-flight script that runs before Pytest:
- Run
git diff origin/masterto identify changed application files. - Send the diff + test file mapping to
ai_helper(Ollama / Gemini). - The LLM returns a list of test IDs affected by those changes.
runner.pyconsumes the output as a-kexpression, slicing 500 tests down to the 10 that matter.
Outcome: CI runtimes drop by 80โ90%. PR feedback loops shrink from minutes to seconds.
Problem: The framework heals in-flight every single run, even for the same broken locator โ paying the full timeout + API cost each time.
Solution: Implement a reports/active_heals.json healing cache:
- On successful healing, write
{ "original_selector": "healed_selector" }to the cache. - Intercept
HealingPage.locator()before it even tries the original โ if a cached mapping exists, swap immediately. - Bypass the timeout and AI query entirely until the developer merges the
.diff.
Outcome: Zero-latency re-healing for known-broken locators. Eliminates redundant API calls entirely.
Problem: DOM extraction can fail on obfuscated dynamic class names (React, Angular minified output). A JSON DOM snapshot is insufficient context for ambiguous layouts.
Solution: When a locator fails, take a full-page Playwright screenshot and send the base64-encoded image alongside the DOM snippet to gemini-2.5-flash (multimodal) or gpt-4o:
"I was trying to click the 'Checkout' button. Look at this screenshot, find the button visually, and return its CSS selector from the DOM provided."
Outcome: Healing accuracy on complex/obfuscated UIs improves dramatically. Positions the framework for real-world enterprise applications.
Problem: How do you know the AI healed the right element and didn't hallucinate? (e.g., healed "Submit Order" to "Cancel Order" instead.)
Solution: Create an evals/ directory with a nightly evaluation script:
- Feed historical
pytest_healing.jsonlogs into DeepEval. - DeepEval uses an LLM judge to score healing quality: "Was the healed locator semantically equivalent to the original intent?"
- Flag low-confidence heals for human review.
Outcome: Makes AI healing trustworthy and auditable, not just functional. Critical for any regulated or enterprise environment.
Problem: The hardcoded prompts in ai_helper.py were written once and never optimized. Their performance degrades as page complexity grows and models change.
Solution: Replace hardcoded string prompts with DSPy modules:
- Collect 20+ examples of
(broken_locator, DOM_context) -> correct_healed_locatorfrom past run logs. - DSPy automatically rewrites and optimizes the prompt via few-shot bootstrapping.
- The optimized prompt is serialized and checked into
prompts/healer_prompt.json.
Outcome: The framework self-improves its own AI instructions over time. Prompt quality compounds โ the more the framework is used, the better it gets.
Problem: FileLock + SQLite WAL works perfectly on a single machine. With 10 parallel GitHub Actions runners, there is no shared filesystem.
Solution: Add a FLAKY_STORAGE=redis environment variable flag:
- If set,
FlakyDetectorwrites results to a free Redis cloud instance (e.g., Upstash) or PostgreSQL via a simple REST API call. - All parallel runners converge on the same shared state.
- Keep the local JSON fallback when
FLAKY_STORAGEis not set.
Outcome: Proves enterprise-scale architectural awareness. Flakiness detection works correctly across massive distributed test grids.
Recommended implementation order: Priority 1 โ 2 โ 3 โ 4 โ 5 โ 6