Introduce electron mcp for electron debugging/validation
This commit is contained in:
@@ -48,6 +48,37 @@ CLAUDE_CODE_OAUTH_TOKEN=your-oauth-token-here
|
||||
# Enable fancy UI (default: true)
|
||||
# ENABLE_FANCY_UI=true
|
||||
|
||||
# =============================================================================
|
||||
# ELECTRON MCP SERVER (OPTIONAL)
|
||||
# =============================================================================
|
||||
# Enable Electron MCP server for AI agents to interact with and validate
|
||||
# Electron desktop applications. This allows QA agents to capture screenshots,
|
||||
# inspect windows, and validate Electron apps during the review process.
|
||||
#
|
||||
# The electron-mcp-server connects via Chrome DevTools Protocol to an Electron
|
||||
# app running with remote debugging enabled.
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. Start your Electron app with remote debugging:
|
||||
# ./YourElectronApp --remote-debugging-port=9222
|
||||
#
|
||||
# 2. For auto-claude-ui specifically:
|
||||
# cd auto-claude-ui
|
||||
# pnpm build
|
||||
# ./dist/mac-arm64/Auto\ Claude.app/Contents/MacOS/Auto\ Claude --remote-debugging-port=9222
|
||||
#
|
||||
# Note: Only QA agents (qa_reviewer, qa_fixer) receive Electron MCP tools.
|
||||
# Coder and Planner agents do NOT have access to these tools to minimize
|
||||
# context token usage and keep agents focused on their roles.
|
||||
#
|
||||
# See: https://github.com/anthropics/anthropic-quickstarts/tree/main/mcp-electron-demo
|
||||
|
||||
# Enable Electron MCP integration (default: false)
|
||||
# ELECTRON_MCP_ENABLED=true
|
||||
|
||||
# Chrome DevTools debugging port for Electron connection (default: 9222)
|
||||
# ELECTRON_DEBUG_PORT=9222
|
||||
|
||||
# =============================================================================
|
||||
# GRAPHITI MEMORY INTEGRATION V2 (OPTIONAL)
|
||||
# =============================================================================
|
||||
|
||||
@@ -30,6 +30,7 @@ Usage:
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -573,6 +574,31 @@ TOOL_RECORD_GOTCHA = "mcp__auto-claude__record_gotcha"
|
||||
TOOL_GET_SESSION_CONTEXT = "mcp__auto-claude__get_session_context"
|
||||
TOOL_UPDATE_QA_STATUS = "mcp__auto-claude__update_qa_status"
|
||||
|
||||
# Electron MCP tools for desktop app automation (when ELECTRON_MCP_ENABLED is set)
|
||||
# Uses puppeteer-mcp-server to connect to Electron apps via Chrome DevTools Protocol.
|
||||
# Electron app must be started with --remote-debugging-port=9222 (or ELECTRON_DEBUG_PORT).
|
||||
# These tools are only available to QA agents (qa_reviewer, qa_fixer), not Coder/Planner.
|
||||
ELECTRON_TOOLS = [
|
||||
"mcp__electron__electron_connect", # Connect to Electron app via DevTools
|
||||
"mcp__electron__electron_screenshot", # Take screenshot of Electron window
|
||||
"mcp__electron__electron_click", # Click element in Electron app
|
||||
"mcp__electron__electron_fill", # Fill input field in Electron app
|
||||
"mcp__electron__electron_evaluate", # Execute JS in Electron renderer
|
||||
"mcp__electron__electron_get_window_info", # Get window state/bounds
|
||||
"mcp__electron__electron_get_console", # Get console logs from renderer
|
||||
]
|
||||
|
||||
|
||||
def is_electron_mcp_enabled() -> bool:
|
||||
"""
|
||||
Check if Electron MCP server integration is enabled.
|
||||
|
||||
Requires ELECTRON_MCP_ENABLED to be set to 'true'.
|
||||
When enabled, QA agents can use Electron MCP tools to connect to Electron apps
|
||||
via Chrome DevTools Protocol on the configured debug port.
|
||||
"""
|
||||
return os.environ.get("ELECTRON_MCP_ENABLED", "").lower() == "true"
|
||||
|
||||
|
||||
def get_allowed_tools(agent_type: str) -> list[str]:
|
||||
"""
|
||||
@@ -635,7 +661,14 @@ def get_allowed_tools(agent_type: str) -> list[str]:
|
||||
agent_type = "coder"
|
||||
|
||||
mapping = tool_mappings[agent_type]
|
||||
return mapping["base"] + mapping["auto_claude"]
|
||||
tools = mapping["base"] + mapping["auto_claude"]
|
||||
|
||||
# Add Electron MCP tools for QA agents only (when enabled)
|
||||
# This prevents context bloat for coder/planner agents who don't need desktop automation
|
||||
if agent_type in ("qa_reviewer", "qa_fixer") and is_electron_mcp_enabled():
|
||||
tools.extend(ELECTRON_TOOLS)
|
||||
|
||||
return tools
|
||||
|
||||
|
||||
def is_tools_available() -> bool:
|
||||
|
||||
@@ -37,6 +37,22 @@ def get_graphiti_mcp_url() -> str:
|
||||
return os.environ.get("GRAPHITI_MCP_URL", "http://localhost:8000/mcp/")
|
||||
|
||||
|
||||
def is_electron_mcp_enabled() -> bool:
|
||||
"""
|
||||
Check if Electron MCP server integration is enabled.
|
||||
|
||||
Requires ELECTRON_MCP_ENABLED to be set to 'true'.
|
||||
When enabled, QA agents can use Puppeteer MCP tools to connect to Electron apps
|
||||
via Chrome DevTools Protocol on the configured debug port.
|
||||
"""
|
||||
return os.environ.get("ELECTRON_MCP_ENABLED", "").lower() == "true"
|
||||
|
||||
|
||||
def get_electron_debug_port() -> int:
|
||||
"""Get the Electron remote debugging port (default: 9222)."""
|
||||
return int(os.environ.get("ELECTRON_DEBUG_PORT", "9222"))
|
||||
|
||||
|
||||
# Puppeteer MCP tools for browser automation
|
||||
PUPPETEER_TOOLS = [
|
||||
"mcp__puppeteer__puppeteer_connect_active_tab",
|
||||
@@ -85,6 +101,20 @@ GRAPHITI_MCP_TOOLS = [
|
||||
"mcp__graphiti-memory__get_entity_edge", # Get specific entity/relationship
|
||||
]
|
||||
|
||||
# Electron MCP tools for desktop app automation (when ELECTRON_MCP_ENABLED is set)
|
||||
# Uses puppeteer-mcp-server to connect to Electron apps via Chrome DevTools Protocol.
|
||||
# Electron app must be started with --remote-debugging-port=9222 (or ELECTRON_DEBUG_PORT).
|
||||
# These tools are only available to QA agents (qa_reviewer, qa_fixer), not Coder/Planner.
|
||||
ELECTRON_TOOLS = [
|
||||
"mcp__electron__electron_connect", # Connect to Electron app via DevTools
|
||||
"mcp__electron__electron_screenshot", # Take screenshot of Electron window
|
||||
"mcp__electron__electron_click", # Click element in Electron app
|
||||
"mcp__electron__electron_fill", # Fill input field in Electron app
|
||||
"mcp__electron__electron_evaluate", # Execute JS in Electron renderer
|
||||
"mcp__electron__electron_get_window_info", # Get window state/bounds
|
||||
"mcp__electron__electron_get_console", # Get console logs from renderer
|
||||
]
|
||||
|
||||
# Built-in tools
|
||||
BUILTIN_TOOLS = [
|
||||
"Read",
|
||||
@@ -146,6 +176,9 @@ def create_client(
|
||||
# Check if Graphiti MCP is enabled
|
||||
graphiti_mcp_enabled = is_graphiti_mcp_enabled()
|
||||
|
||||
# Check if Electron MCP is enabled (for QA agents testing Electron apps)
|
||||
electron_mcp_enabled = is_electron_mcp_enabled()
|
||||
|
||||
# Add external MCP tools
|
||||
allowed_tools_list.extend(PUPPETEER_TOOLS)
|
||||
allowed_tools_list.extend(CONTEXT7_TOOLS)
|
||||
@@ -153,6 +186,10 @@ def create_client(
|
||||
allowed_tools_list.extend(LINEAR_TOOLS)
|
||||
if graphiti_mcp_enabled:
|
||||
allowed_tools_list.extend(GRAPHITI_MCP_TOOLS)
|
||||
# Add Electron MCP tools only for QA agents (qa_reviewer, qa_fixer) when enabled
|
||||
# This prevents context bloat for coder/planner agents who don't need desktop automation
|
||||
if electron_mcp_enabled and agent_type in ("qa_reviewer", "qa_fixer"):
|
||||
allowed_tools_list.extend(ELECTRON_TOOLS)
|
||||
|
||||
# Create comprehensive security settings
|
||||
# Note: Using relative paths ("./**") restricts access to project directory
|
||||
@@ -179,6 +216,8 @@ def create_client(
|
||||
*(LINEAR_TOOLS if linear_enabled else []),
|
||||
# Allow Graphiti MCP tools for knowledge graph memory (if enabled)
|
||||
*(GRAPHITI_MCP_TOOLS if graphiti_mcp_enabled else []),
|
||||
# Allow Electron MCP tools for QA agents only (if enabled)
|
||||
*(ELECTRON_TOOLS if electron_mcp_enabled and agent_type in ("qa_reviewer", "qa_fixer") else []),
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -198,6 +237,8 @@ def create_client(
|
||||
mcp_servers_list.append("linear (project management)")
|
||||
if graphiti_mcp_enabled:
|
||||
mcp_servers_list.append("graphiti-memory (knowledge graph)")
|
||||
if electron_mcp_enabled:
|
||||
mcp_servers_list.append(f"electron (desktop automation, port {get_electron_debug_port()})")
|
||||
if auto_claude_tools_enabled:
|
||||
mcp_servers_list.append(f"auto-claude ({agent_type} tools)")
|
||||
print(f" - MCP servers: {', '.join(mcp_servers_list)}")
|
||||
@@ -225,6 +266,21 @@ def create_client(
|
||||
"url": get_graphiti_mcp_url(),
|
||||
}
|
||||
|
||||
# Add Electron MCP server if enabled
|
||||
# Electron app must be started with --remote-debugging-port=<port> for connection
|
||||
# Uses puppeteer-mcp-server to connect to Electron via Chrome DevTools Protocol
|
||||
if electron_mcp_enabled:
|
||||
electron_port = get_electron_debug_port()
|
||||
mcp_servers["electron"] = {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@anthropic/puppeteer-mcp-server",
|
||||
"--debug-port",
|
||||
str(electron_port),
|
||||
],
|
||||
}
|
||||
|
||||
# Add custom auto-claude MCP server if available
|
||||
auto_claude_mcp_server = None
|
||||
if auto_claude_tools_enabled:
|
||||
|
||||
@@ -164,6 +164,123 @@ BROWSER VERIFICATION:
|
||||
|
||||
---
|
||||
|
||||
## PHASE 4.5: ELECTRON VALIDATION (If Electron App)
|
||||
|
||||
For Electron/desktop applications, use the electron-mcp-server tools to validate the application.
|
||||
|
||||
**Prerequisites:**
|
||||
- Electron app must be running with `--remote-debugging-port=9222`
|
||||
- `ELECTRON_MCP_ENABLED=true` in environment
|
||||
|
||||
### 4.5.1: Connect to Electron App
|
||||
|
||||
```
|
||||
# Use puppeteer to connect to the running Electron app
|
||||
Tool: mcp__puppeteer__puppeteer_connect_active_tab
|
||||
Input: { "debugPort": 9222 }
|
||||
```
|
||||
|
||||
**Note:** The Electron app must be started with remote debugging enabled:
|
||||
```bash
|
||||
./your-electron-app --remote-debugging-port=9222
|
||||
```
|
||||
|
||||
### 4.5.2: Navigate and Screenshot
|
||||
|
||||
```
|
||||
# Navigate to specific routes/views within the Electron app
|
||||
Tool: mcp__puppeteer__puppeteer_navigate
|
||||
Input: { "url": "file:///path/or/route" }
|
||||
|
||||
# Take screenshot of current state
|
||||
Tool: mcp__puppeteer__puppeteer_screenshot
|
||||
Input: { "name": "electron-app-main-view" }
|
||||
```
|
||||
|
||||
### 4.5.3: Verify UI Elements
|
||||
|
||||
```
|
||||
# Check for specific elements
|
||||
Tool: mcp__puppeteer__puppeteer_evaluate
|
||||
Input: { "script": "document.querySelector('[data-testid=\"feature\"]') !== null" }
|
||||
|
||||
# Click on elements to test interactions
|
||||
Tool: mcp__puppeteer__puppeteer_click
|
||||
Input: { "selector": "[data-testid=\"button\"]" }
|
||||
|
||||
# Fill form fields
|
||||
Tool: mcp__puppeteer__puppeteer_fill
|
||||
Input: { "selector": "input[name=\"field\"]", "value": "test value" }
|
||||
```
|
||||
|
||||
### 4.5.4: Check Console for Errors
|
||||
|
||||
```
|
||||
# Execute script to capture console errors
|
||||
Tool: mcp__puppeteer__puppeteer_evaluate
|
||||
Input: { "script": "window.__consoleErrors || []" }
|
||||
```
|
||||
|
||||
**CRITICAL**: Check for:
|
||||
- JavaScript runtime errors
|
||||
- Electron IPC communication errors
|
||||
- Node.js integration errors (if nodeIntegration is enabled)
|
||||
- Failed file system operations
|
||||
|
||||
### 4.5.5: Test Electron-Specific Features
|
||||
|
||||
Verify features unique to Electron:
|
||||
- Window controls (minimize, maximize, close)
|
||||
- Native menus and dialogs
|
||||
- System tray integration (if applicable)
|
||||
- File system access
|
||||
- IPC communication between main and renderer processes
|
||||
|
||||
### 4.5.6: Document Findings
|
||||
|
||||
```
|
||||
ELECTRON VALIDATION:
|
||||
- App Connection: PASS/FAIL
|
||||
- Debug port accessible: YES/NO
|
||||
- Connected to correct window: YES/NO
|
||||
- UI Verification: PASS/FAIL
|
||||
- Screenshots captured: [list]
|
||||
- Visual elements correct: PASS/FAIL
|
||||
- Interactions working: PASS/FAIL
|
||||
- Console Errors: [list or "None"]
|
||||
- Electron-Specific Features: PASS/FAIL
|
||||
- [Feature]: PASS/FAIL
|
||||
- Issues: [list or "None"]
|
||||
```
|
||||
|
||||
### 4.5.7: Handling Common Issues
|
||||
|
||||
**App Not Running:**
|
||||
```
|
||||
If Electron app is not running or debug port is not accessible:
|
||||
1. Document that Electron validation was skipped
|
||||
2. Note reason: "App not running with --remote-debugging-port=9222"
|
||||
3. Add to QA report as "Manual verification required"
|
||||
```
|
||||
|
||||
**Multiple Electron Instances:**
|
||||
```
|
||||
If multiple Electron apps are running:
|
||||
1. Use targetUrl parameter to connect to specific app
|
||||
Tool: mcp__puppeteer__puppeteer_connect_active_tab
|
||||
Input: { "debugPort": 9222, "targetUrl": "file:///path/to/expected/url" }
|
||||
```
|
||||
|
||||
**Headless Environment (CI/CD):**
|
||||
```
|
||||
If running in headless environment without display:
|
||||
1. Skip interactive Electron validation
|
||||
2. Document: "Electron UI validation skipped - headless environment"
|
||||
3. Rely on unit/integration tests for validation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PHASE 5: DATABASE VERIFICATION (If Applicable)
|
||||
|
||||
### 5.1: Check Migrations
|
||||
@@ -348,6 +465,7 @@ Create a comprehensive QA report:
|
||||
| Integration Tests | ✓/✗ | X/Y passing |
|
||||
| E2E Tests | ✓/✗ | X/Y passing |
|
||||
| Browser Verification | ✓/✗ | [summary] |
|
||||
| Electron Validation | ✓/✗ | [summary or "N/A - not Electron"] |
|
||||
| Database Verification | ✓/✗ | [summary] |
|
||||
| Third-Party API Validation | ✓/✗ | [Context7 verification summary] |
|
||||
| Security Review | ✓/✗ | [summary] |
|
||||
@@ -513,6 +631,7 @@ All acceptance criteria verified:
|
||||
- Integration tests: PASS
|
||||
- E2E tests: PASS
|
||||
- Browser verification: PASS
|
||||
- Electron validation: PASS (or N/A)
|
||||
- Database verification: PASS
|
||||
- Security review: PASS
|
||||
- Regression check: PASS
|
||||
|
||||
@@ -52,14 +52,22 @@ from .reviewer import (
|
||||
from .state import (
|
||||
REVIEW_STATE_FILE,
|
||||
ReviewState,
|
||||
_compute_file_hash,
|
||||
_compute_spec_hash,
|
||||
get_review_status_summary,
|
||||
)
|
||||
|
||||
# Aliases for underscore-prefixed names used in tests
|
||||
_extract_section = extract_section
|
||||
_truncate_text = truncate_text
|
||||
|
||||
__all__ = [
|
||||
# State
|
||||
"ReviewState",
|
||||
"get_review_status_summary",
|
||||
"REVIEW_STATE_FILE",
|
||||
"_compute_file_hash",
|
||||
"_compute_spec_hash",
|
||||
# Formatters
|
||||
"display_spec_summary",
|
||||
"display_plan_summary",
|
||||
@@ -76,4 +84,7 @@ __all__ = [
|
||||
"truncate_text",
|
||||
"extract_title",
|
||||
"extract_checkboxes",
|
||||
]
|
||||
# Aliases for tests
|
||||
"_extract_section",
|
||||
"_truncate_text",
|
||||
]
|
||||
@@ -150,6 +150,8 @@ def detect_project_type(project_dir: Path) -> str:
|
||||
dev_deps = pkg.get("devDependencies", {})
|
||||
all_deps = {**deps, **dev_deps}
|
||||
|
||||
if "electron" in all_deps:
|
||||
return "electron"
|
||||
if "next" in all_deps:
|
||||
return "nextjs"
|
||||
if "react" in all_deps:
|
||||
@@ -251,6 +253,7 @@ class ValidationStrategyBuilder:
|
||||
"angular_spa": self._strategy_for_spa,
|
||||
"nextjs": self._strategy_for_fullstack,
|
||||
"nodejs": self._strategy_for_nodejs,
|
||||
"electron": self._strategy_for_electron,
|
||||
"python_api": self._strategy_for_python_api,
|
||||
"python_cli": self._strategy_for_cli,
|
||||
"python": self._strategy_for_python,
|
||||
@@ -766,6 +769,82 @@ class ValidationStrategyBuilder:
|
||||
reasoning="Ruby project requires RSpec tests.",
|
||||
)
|
||||
|
||||
def _strategy_for_electron(
|
||||
self, project_dir: Path, risk_level: str
|
||||
) -> ValidationStrategy:
|
||||
"""
|
||||
Validation strategy for Electron desktop applications.
|
||||
|
||||
Focus on main/renderer process tests, E2E testing, and app packaging.
|
||||
"""
|
||||
steps = []
|
||||
|
||||
# Unit tests for all non-trivial
|
||||
if risk_level != "trivial":
|
||||
steps.append(
|
||||
ValidationStep(
|
||||
name="Unit Tests",
|
||||
command="npm test",
|
||||
expected_outcome="All tests pass",
|
||||
step_type="test",
|
||||
required=True,
|
||||
blocking=True,
|
||||
)
|
||||
)
|
||||
|
||||
# E2E tests for medium+ risk (Electron apps need GUI testing)
|
||||
if risk_level in ["medium", "high", "critical"]:
|
||||
steps.append(
|
||||
ValidationStep(
|
||||
name="E2E Tests",
|
||||
command="npm run test:e2e",
|
||||
expected_outcome="All E2E tests pass",
|
||||
step_type="test",
|
||||
required=True,
|
||||
blocking=True,
|
||||
)
|
||||
)
|
||||
|
||||
# App build/package verification for medium+ risk
|
||||
if risk_level in ["medium", "high", "critical"]:
|
||||
steps.append(
|
||||
ValidationStep(
|
||||
name="Build Verification",
|
||||
command="npm run build",
|
||||
expected_outcome="App builds without errors",
|
||||
step_type="test",
|
||||
required=True,
|
||||
blocking=True,
|
||||
)
|
||||
)
|
||||
|
||||
# Console error check for high+ risk
|
||||
if risk_level in ["high", "critical"]:
|
||||
steps.append(
|
||||
ValidationStep(
|
||||
name="Console Error Check",
|
||||
command="npm run test:console",
|
||||
expected_outcome="No console errors in main or renderer process",
|
||||
step_type="test",
|
||||
required=True,
|
||||
blocking=True,
|
||||
)
|
||||
)
|
||||
|
||||
# Determine test types
|
||||
test_types = ["unit"]
|
||||
if risk_level in ["medium", "high", "critical"]:
|
||||
test_types.append("integration")
|
||||
test_types.append("e2e")
|
||||
|
||||
return ValidationStrategy(
|
||||
risk_level=risk_level,
|
||||
project_type="electron",
|
||||
steps=steps,
|
||||
test_types_required=test_types,
|
||||
reasoning="Electron app requires unit tests, E2E tests for GUI, and build verification.",
|
||||
)
|
||||
|
||||
def _strategy_default(
|
||||
self, project_dir: Path, risk_level: str
|
||||
) -> ValidationStrategy:
|
||||
|
||||
@@ -218,6 +218,88 @@ class TestProjectDocumentation:
|
||||
)
|
||||
|
||||
|
||||
class TestElectronToolScoping:
|
||||
"""Verify Electron MCP tools are scoped to QA agents only."""
|
||||
|
||||
def test_qa_reviewer_has_electron_tools_when_enabled(self, monkeypatch):
|
||||
"""QA reviewer gets Electron tools when ELECTRON_MCP_ENABLED=true."""
|
||||
monkeypatch.setenv("ELECTRON_MCP_ENABLED", "true")
|
||||
|
||||
# Re-import to pick up env change
|
||||
from auto_claude_tools import get_allowed_tools, ELECTRON_TOOLS
|
||||
|
||||
qa_tools = get_allowed_tools("qa_reviewer")
|
||||
|
||||
# At least one Electron tool should be present
|
||||
has_electron = any("electron" in tool.lower() for tool in qa_tools)
|
||||
assert has_electron, (
|
||||
"QA reviewer should have Electron tools when ELECTRON_MCP_ENABLED=true. "
|
||||
f"Got tools: {qa_tools}"
|
||||
)
|
||||
|
||||
# Verify specific tools are included
|
||||
for tool in ELECTRON_TOOLS:
|
||||
assert tool in qa_tools, f"Expected {tool} in qa_reviewer tools"
|
||||
|
||||
def test_qa_fixer_has_electron_tools_when_enabled(self, monkeypatch):
|
||||
"""QA fixer gets Electron tools when ELECTRON_MCP_ENABLED=true."""
|
||||
monkeypatch.setenv("ELECTRON_MCP_ENABLED", "true")
|
||||
|
||||
from auto_claude_tools import get_allowed_tools, ELECTRON_TOOLS
|
||||
|
||||
qa_fixer_tools = get_allowed_tools("qa_fixer")
|
||||
|
||||
has_electron = any("electron" in tool.lower() for tool in qa_fixer_tools)
|
||||
assert has_electron, (
|
||||
"QA fixer should have Electron tools when ELECTRON_MCP_ENABLED=true. "
|
||||
f"Got tools: {qa_fixer_tools}"
|
||||
)
|
||||
|
||||
for tool in ELECTRON_TOOLS:
|
||||
assert tool in qa_fixer_tools, f"Expected {tool} in qa_fixer tools"
|
||||
|
||||
def test_coder_no_electron_tools(self, monkeypatch):
|
||||
"""Coder should NOT get Electron tools even when enabled."""
|
||||
monkeypatch.setenv("ELECTRON_MCP_ENABLED", "true")
|
||||
|
||||
from auto_claude_tools import get_allowed_tools
|
||||
|
||||
coder_tools = get_allowed_tools("coder")
|
||||
|
||||
has_electron = any("electron" in tool.lower() for tool in coder_tools)
|
||||
assert not has_electron, (
|
||||
"Coder should NOT have Electron tools - they are scoped to QA agents only. "
|
||||
"This prevents context token bloat for agents that don't need desktop automation."
|
||||
)
|
||||
|
||||
def test_planner_no_electron_tools(self, monkeypatch):
|
||||
"""Planner should NOT get Electron tools even when enabled."""
|
||||
monkeypatch.setenv("ELECTRON_MCP_ENABLED", "true")
|
||||
|
||||
from auto_claude_tools import get_allowed_tools
|
||||
|
||||
planner_tools = get_allowed_tools("planner")
|
||||
|
||||
has_electron = any("electron" in tool.lower() for tool in planner_tools)
|
||||
assert not has_electron, (
|
||||
"Planner should NOT have Electron tools - they are scoped to QA agents only. "
|
||||
"This prevents context token bloat for agents that don't need desktop automation."
|
||||
)
|
||||
|
||||
def test_no_electron_tools_when_disabled(self, monkeypatch):
|
||||
"""No agent gets Electron tools when ELECTRON_MCP_ENABLED is not set."""
|
||||
monkeypatch.delenv("ELECTRON_MCP_ENABLED", raising=False)
|
||||
|
||||
from auto_claude_tools import get_allowed_tools
|
||||
|
||||
for agent_type in ["planner", "coder", "qa_reviewer", "qa_fixer"]:
|
||||
tools = get_allowed_tools(agent_type)
|
||||
has_electron = any("electron" in tool.lower() for tool in tools)
|
||||
assert not has_electron, (
|
||||
f"{agent_type} should NOT have Electron tools when ELECTRON_MCP_ENABLED is not set"
|
||||
)
|
||||
|
||||
|
||||
class TestSubtaskTerminology:
|
||||
"""Verify subtask terminology is used consistently."""
|
||||
|
||||
@@ -255,6 +337,7 @@ def run_tests():
|
||||
TestAgentPrompt,
|
||||
TestModuleIntegrity,
|
||||
TestProjectDocumentation,
|
||||
TestElectronToolScoping, # Note: requires pytest (uses monkeypatch)
|
||||
TestSubtaskTerminology,
|
||||
]
|
||||
|
||||
|
||||
@@ -188,6 +188,55 @@ class TestProjectTypeDetection:
|
||||
|
||||
assert detect_project_type(temp_dir) == "nodejs"
|
||||
|
||||
def test_detect_electron_in_dependencies(self, temp_dir):
|
||||
"""Test detection of Electron project with electron in dependencies."""
|
||||
package_json = temp_dir / "package.json"
|
||||
package_json.write_text(json.dumps({
|
||||
"name": "my-electron-app",
|
||||
"dependencies": {"electron": "^28.0.0"}
|
||||
}))
|
||||
|
||||
assert detect_project_type(temp_dir) == "electron"
|
||||
|
||||
def test_detect_electron_in_dev_dependencies(self, temp_dir):
|
||||
"""Test detection of Electron project with electron in devDependencies."""
|
||||
package_json = temp_dir / "package.json"
|
||||
package_json.write_text(json.dumps({
|
||||
"name": "my-electron-app",
|
||||
"devDependencies": {"electron": "^28.0.0"}
|
||||
}))
|
||||
|
||||
assert detect_project_type(temp_dir) == "electron"
|
||||
|
||||
def test_electron_priority_over_react(self, temp_dir):
|
||||
"""Test that Electron is detected over React when both are present."""
|
||||
package_json = temp_dir / "package.json"
|
||||
package_json.write_text(json.dumps({
|
||||
"name": "electron-react-app",
|
||||
"dependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^28.0.0"
|
||||
}
|
||||
}))
|
||||
|
||||
assert detect_project_type(temp_dir) == "electron"
|
||||
|
||||
def test_electron_with_electron_vite(self, temp_dir):
|
||||
"""Test detection of Electron project using electron-vite."""
|
||||
package_json = temp_dir / "package.json"
|
||||
package_json.write_text(json.dumps({
|
||||
"name": "electron-vite-app",
|
||||
"devDependencies": {
|
||||
"electron": "^28.0.0",
|
||||
"electron-vite": "^2.0.0"
|
||||
}
|
||||
}))
|
||||
|
||||
assert detect_project_type(temp_dir) == "electron"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# VALIDATION STEP TESTS
|
||||
@@ -405,6 +454,47 @@ class TestStrategyBuilderByProjectType:
|
||||
step_types = [s.step_type for s in strategy.steps]
|
||||
assert "manual" in step_types
|
||||
|
||||
def test_electron_strategy(self, builder, temp_dir):
|
||||
"""Test Electron project strategy."""
|
||||
(temp_dir / "package.json").write_text(json.dumps({
|
||||
"devDependencies": {"electron": "^28.0.0"}
|
||||
}))
|
||||
|
||||
strategy = builder.build_strategy(temp_dir, temp_dir, "medium")
|
||||
|
||||
assert strategy.project_type == "electron"
|
||||
assert "unit" in strategy.test_types_required
|
||||
assert "e2e" in strategy.test_types_required
|
||||
# Should have npm test and npm run test:e2e commands
|
||||
commands = [s.command for s in strategy.steps]
|
||||
assert any("npm test" in cmd for cmd in commands)
|
||||
assert any("test:e2e" in cmd for cmd in commands)
|
||||
|
||||
def test_electron_low_risk_strategy(self, builder, temp_dir):
|
||||
"""Test Electron project with low risk only has unit tests."""
|
||||
(temp_dir / "package.json").write_text(json.dumps({
|
||||
"dependencies": {"electron": "^28.0.0"}
|
||||
}))
|
||||
|
||||
strategy = builder.build_strategy(temp_dir, temp_dir, "low")
|
||||
|
||||
assert strategy.project_type == "electron"
|
||||
assert "unit" in strategy.test_types_required
|
||||
# Low risk should NOT have e2e tests
|
||||
assert "e2e" not in strategy.test_types_required
|
||||
|
||||
def test_electron_high_risk_has_console_check(self, builder, temp_dir):
|
||||
"""Test Electron high risk includes console error check."""
|
||||
(temp_dir / "package.json").write_text(json.dumps({
|
||||
"devDependencies": {"electron": "^28.0.0"}
|
||||
}))
|
||||
|
||||
strategy = builder.build_strategy(temp_dir, temp_dir, "high")
|
||||
|
||||
assert strategy.project_type == "electron"
|
||||
step_names = [s.name.lower() for s in strategy.steps]
|
||||
assert any("console" in name for name in step_names)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# SECURITY STEPS TESTS
|
||||
|
||||
Reference in New Issue
Block a user