diff --git a/README.md b/README.md index 4d88485..2ee5098 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ A Model Context Protocol (MCP) server for Robot Framework test automation with c - 📊 Performance monitoring and metrics collection - 🔄 Data-driven testing templates - 🌐 API integration testing capabilities +- 🖥️ Live browser control — launch, click, type, screenshot without writing `.robot` files +- 🚀 CI/CD pipeline generation for GitHub Actions ## Quick Demo Video @@ -178,6 +180,58 @@ The MCP server provides the following comprehensive tools for Robot Framework te ### 🔍 Validation & Syntax - **`validate_robot_framework_syntax(robot_code)`** - Validate Robot Framework syntax and provide improvement suggestions +### 🖥️ Live Browser Control + +Drive a real browser directly — no `.robot` file needed. All tools share a persistent session and support Robot Framework-style selector prefixes (`id=`, `css=`, `xpath=`, `name=`, `class=`, `tag=`, `link=`, `partial_link=`). + +| Tool | Description | +|---|---| +| **`browser_launch(url, browser="Chrome", headless=False)`** | Open Chrome or Firefox and navigate to a URL | +| **`browser_navigate(url)`** | Go to a new URL in the active session | +| **`browser_click(selector, timeout=10)`** | Click an element, waits until it is clickable | +| **`browser_send_keys(selector, text, clear_first=True, timeout=10)`** | Type into an input field | +| **`browser_get_text(selector, timeout=10)`** | Read visible text from an element | +| **`browser_wait_for_element(selector, state="visible", timeout=10)`** | Wait for `visible`, `present`, `clickable`, or `hidden` | +| **`browser_screenshot(filename="")`** | Save a PNG screenshot, returns the file path | +| **`browser_close()`** | Quit the browser and clean up the session | + +**Example — AI-driven login flow:** +```python +browser_launch("https://example.com") +browser_send_keys("id=username", "admin") +browser_send_keys("id=password", "secret") +browser_click("css=button[type='submit']") +browser_screenshot("results/after_login.png") +browser_close() +``` + +> **Note:** ChromeDriver must match your installed Chrome version. Update with `brew upgrade chromedriver` (macOS) or download from [googlechromelabs.github.io/chrome-for-testing](https://googlechromelabs.github.io/chrome-for-testing/). + +### 🚀 CI/CD Pipeline Generation + +- **`create_cicd_pipeline(project_name, python_version, test_directory, trigger_branches, schedule, output_dir, requirements_file)`** - Generate a GitHub Actions workflow file ready to drop into `.github/workflows/` + +The generated workflow: +- Triggers on push and pull requests to specified branches +- Supports optional cron schedule (e.g. `"0 9 * * 1-5"` for weekdays at 09:00 UTC) +- Auto-detects the Python version from the current environment (can be overridden) +- Sets up Chrome in headless mode on the runner +- Installs from `requirements.txt` if present, falls back to core packages +- Uploads the full `results/` folder as a build artifact (30-day retention) +- Parses `output.xml` and reports pass/fail counts; fails the job if any test fails + +**Example:** +```python +create_cicd_pipeline( + project_name="my-rf-tests", + trigger_branches="main, develop", + schedule="0 9 * * 1-5", # weekdays 09:00 UTC + test_directory="tests/", +) +``` + +Save the output to `.github/workflows/robot-tests.yml` in your repository. + ### 📋 Template Options The server supports multiple selector templates for different applications: diff --git a/mcp_server.py b/mcp_server.py index 78ec842..8af10a7 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -1379,6 +1379,147 @@ def validate_robot_framework_syntax(robot_code: str) -> str: return f"# VALIDATION ERROR: {str(e)}" +@mcp.tool() +def create_cicd_pipeline( + project_name: str = "robot-tests", + python_version: str = "", + test_directory: str = "tests/", + trigger_branches: str = "main", + schedule: str = "", + output_dir: str = "results/", + requirements_file: str = "requirements.txt", +) -> str: + """Generate a GitHub Actions workflow file for running Robot Framework tests. + + Returns the complete YAML content to save as .github/workflows/robot-tests.yml. + + project_name: workflow and artifact name (default 'robot-tests'). + python_version: Python version for the runner. Defaults to the version currently + running this MCP server (e.g. '3.11'). Pass explicitly to override. + test_directory: path to the Robot Framework test folder (default 'tests/'). + trigger_branches: comma-separated branch names that trigger the workflow (default 'main'). + schedule: optional cron expression for scheduled runs, e.g. '0 9 * * 1-5' for + weekdays at 09:00 UTC. Leave empty to disable scheduled runs. + output_dir: folder where Robot Framework writes results (default 'results/'). + requirements_file: pip requirements file path (default 'requirements.txt'). + """ + import sys + try: + if not python_version or not python_version.strip(): + python_version = f"{sys.version_info.major}.{sys.version_info.minor}" + branches = [b.strip() for b in trigger_branches.split(",") if b.strip()] + if not branches: + return "VALIDATION ERROR: trigger_branches cannot be empty." + + branch_lines = "\n".join(f" - {b}" for b in branches) + + schedule_block = "" + if schedule.strip(): + schedule_block = f""" schedule: + - cron: "{schedule.strip()}" +""" + + # Build the fallback requirements block used when no requirements.txt exists + fallback_install = ( + "pip install robotframework robotframework-seleniumlibrary selenium" + ) + + workflow = f"""\ +# GitHub Actions workflow — generated by robotframework-mcp +# Save this file to: .github/workflows/robot-tests.yml + +name: {project_name} + +on: + push: + branches: +{branch_lines} + pull_request: + branches: +{branch_lines} +{schedule_block} +jobs: + robot-tests: + name: Run Robot Framework Tests + runs-on: ubuntu-latest + + steps: + # ── Checkout ──────────────────────────────────────────────────────────── + - name: Checkout repository + uses: actions/checkout@v4 + + # ── Python ────────────────────────────────────────────────────────────── + - name: Set up Python {python_version} + uses: actions/setup-python@v5 + with: + python-version: "{python_version}" + cache: pip + + # ── Dependencies ──────────────────────────────────────────────────────── + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f {requirements_file} ]; then + pip install -r {requirements_file} + else + {fallback_install} + fi + + # ── Chrome (headless) ─────────────────────────────────────────────────── + - name: Set up Chrome + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + + - name: Verify Chrome installation + run: google-chrome --version + + # ── Run tests ─────────────────────────────────────────────────────────── + - name: Run Robot Framework tests + run: | + robot \\ + --outputdir {output_dir} \\ + --variable BROWSER:headlesschrome \\ + --loglevel INFO \\ + {test_directory} + continue-on-error: true # upload results even on test failure + + # ── Upload results ────────────────────────────────────────────────────── + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: {project_name}-results-${{{{ github.run_number }}}} + path: {output_dir} + retention-days: 30 + + # ── Summary ───────────────────────────────────────────────────────────── + - name: Report test outcome + if: always() + run: | + if [ -f {output_dir}output.xml ]; then + python - <<'PY' + import xml.etree.ElementTree as ET, sys + tree = ET.parse("{output_dir}output.xml") + stats = tree.find(".//total/stat") + if stats is not None: + passed = stats.get("pass", "?") + failed = stats.get("fail", "?") + print(f"Results — passed: {{passed}}, failed: {{failed}}") + sys.exit(1 if int(failed) > 0 else 0) + PY + else + echo "output.xml not found — tests may not have run" + exit 1 + fi +""" + + return workflow + + except Exception as e: + return f"UNEXPECTED ERROR: {str(e)}" + + def main(): """Entry point for the Robot Framework MCP server""" import sys