Introduce electron mcp for electron debugging/validation

This commit is contained in:
AndyMik90
2025-12-16 14:50:05 +01:00
parent 56ff586c4d
commit 3eb2eadaf1
8 changed files with 504 additions and 2 deletions
+31
View File
@@ -48,6 +48,37 @@ CLAUDE_CODE_OAUTH_TOKEN=your-oauth-token-here
# Enable fancy UI (default: true) # Enable fancy UI (default: true)
# ENABLE_FANCY_UI=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) # GRAPHITI MEMORY INTEGRATION V2 (OPTIONAL)
# ============================================================================= # =============================================================================
+34 -1
View File
@@ -30,6 +30,7 @@ Usage:
""" """
import json import json
import os
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Any 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_GET_SESSION_CONTEXT = "mcp__auto-claude__get_session_context"
TOOL_UPDATE_QA_STATUS = "mcp__auto-claude__update_qa_status" 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]: 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" agent_type = "coder"
mapping = tool_mappings[agent_type] 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: def is_tools_available() -> bool:
+56
View File
@@ -37,6 +37,22 @@ def get_graphiti_mcp_url() -> str:
return os.environ.get("GRAPHITI_MCP_URL", "http://localhost:8000/mcp/") 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 MCP tools for browser automation
PUPPETEER_TOOLS = [ PUPPETEER_TOOLS = [
"mcp__puppeteer__puppeteer_connect_active_tab", "mcp__puppeteer__puppeteer_connect_active_tab",
@@ -85,6 +101,20 @@ GRAPHITI_MCP_TOOLS = [
"mcp__graphiti-memory__get_entity_edge", # Get specific entity/relationship "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 # Built-in tools
BUILTIN_TOOLS = [ BUILTIN_TOOLS = [
"Read", "Read",
@@ -146,6 +176,9 @@ def create_client(
# Check if Graphiti MCP is enabled # Check if Graphiti MCP is enabled
graphiti_mcp_enabled = is_graphiti_mcp_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 # Add external MCP tools
allowed_tools_list.extend(PUPPETEER_TOOLS) allowed_tools_list.extend(PUPPETEER_TOOLS)
allowed_tools_list.extend(CONTEXT7_TOOLS) allowed_tools_list.extend(CONTEXT7_TOOLS)
@@ -153,6 +186,10 @@ def create_client(
allowed_tools_list.extend(LINEAR_TOOLS) allowed_tools_list.extend(LINEAR_TOOLS)
if graphiti_mcp_enabled: if graphiti_mcp_enabled:
allowed_tools_list.extend(GRAPHITI_MCP_TOOLS) 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 # Create comprehensive security settings
# Note: Using relative paths ("./**") restricts access to project directory # Note: Using relative paths ("./**") restricts access to project directory
@@ -179,6 +216,8 @@ def create_client(
*(LINEAR_TOOLS if linear_enabled else []), *(LINEAR_TOOLS if linear_enabled else []),
# Allow Graphiti MCP tools for knowledge graph memory (if enabled) # Allow Graphiti MCP tools for knowledge graph memory (if enabled)
*(GRAPHITI_MCP_TOOLS if graphiti_mcp_enabled else []), *(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)") mcp_servers_list.append("linear (project management)")
if graphiti_mcp_enabled: if graphiti_mcp_enabled:
mcp_servers_list.append("graphiti-memory (knowledge graph)") 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: if auto_claude_tools_enabled:
mcp_servers_list.append(f"auto-claude ({agent_type} tools)") mcp_servers_list.append(f"auto-claude ({agent_type} tools)")
print(f" - MCP servers: {', '.join(mcp_servers_list)}") print(f" - MCP servers: {', '.join(mcp_servers_list)}")
@@ -225,6 +266,21 @@ def create_client(
"url": get_graphiti_mcp_url(), "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 # Add custom auto-claude MCP server if available
auto_claude_mcp_server = None auto_claude_mcp_server = None
if auto_claude_tools_enabled: if auto_claude_tools_enabled:
+119
View File
@@ -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) ## PHASE 5: DATABASE VERIFICATION (If Applicable)
### 5.1: Check Migrations ### 5.1: Check Migrations
@@ -348,6 +465,7 @@ Create a comprehensive QA report:
| Integration Tests | ✓/✗ | X/Y passing | | Integration Tests | ✓/✗ | X/Y passing |
| E2E Tests | ✓/✗ | X/Y passing | | E2E Tests | ✓/✗ | X/Y passing |
| Browser Verification | ✓/✗ | [summary] | | Browser Verification | ✓/✗ | [summary] |
| Electron Validation | ✓/✗ | [summary or "N/A - not Electron"] |
| Database Verification | ✓/✗ | [summary] | | Database Verification | ✓/✗ | [summary] |
| Third-Party API Validation | ✓/✗ | [Context7 verification summary] | | Third-Party API Validation | ✓/✗ | [Context7 verification summary] |
| Security Review | ✓/✗ | [summary] | | Security Review | ✓/✗ | [summary] |
@@ -513,6 +631,7 @@ All acceptance criteria verified:
- Integration tests: PASS - Integration tests: PASS
- E2E tests: PASS - E2E tests: PASS
- Browser verification: PASS - Browser verification: PASS
- Electron validation: PASS (or N/A)
- Database verification: PASS - Database verification: PASS
- Security review: PASS - Security review: PASS
- Regression check: PASS - Regression check: PASS
+12 -1
View File
@@ -52,14 +52,22 @@ from .reviewer import (
from .state import ( from .state import (
REVIEW_STATE_FILE, REVIEW_STATE_FILE,
ReviewState, ReviewState,
_compute_file_hash,
_compute_spec_hash,
get_review_status_summary, get_review_status_summary,
) )
# Aliases for underscore-prefixed names used in tests
_extract_section = extract_section
_truncate_text = truncate_text
__all__ = [ __all__ = [
# State # State
"ReviewState", "ReviewState",
"get_review_status_summary", "get_review_status_summary",
"REVIEW_STATE_FILE", "REVIEW_STATE_FILE",
"_compute_file_hash",
"_compute_spec_hash",
# Formatters # Formatters
"display_spec_summary", "display_spec_summary",
"display_plan_summary", "display_plan_summary",
@@ -76,4 +84,7 @@ __all__ = [
"truncate_text", "truncate_text",
"extract_title", "extract_title",
"extract_checkboxes", "extract_checkboxes",
] # Aliases for tests
"_extract_section",
"_truncate_text",
]
+79
View File
@@ -150,6 +150,8 @@ def detect_project_type(project_dir: Path) -> str:
dev_deps = pkg.get("devDependencies", {}) dev_deps = pkg.get("devDependencies", {})
all_deps = {**deps, **dev_deps} all_deps = {**deps, **dev_deps}
if "electron" in all_deps:
return "electron"
if "next" in all_deps: if "next" in all_deps:
return "nextjs" return "nextjs"
if "react" in all_deps: if "react" in all_deps:
@@ -251,6 +253,7 @@ class ValidationStrategyBuilder:
"angular_spa": self._strategy_for_spa, "angular_spa": self._strategy_for_spa,
"nextjs": self._strategy_for_fullstack, "nextjs": self._strategy_for_fullstack,
"nodejs": self._strategy_for_nodejs, "nodejs": self._strategy_for_nodejs,
"electron": self._strategy_for_electron,
"python_api": self._strategy_for_python_api, "python_api": self._strategy_for_python_api,
"python_cli": self._strategy_for_cli, "python_cli": self._strategy_for_cli,
"python": self._strategy_for_python, "python": self._strategy_for_python,
@@ -766,6 +769,82 @@ class ValidationStrategyBuilder:
reasoning="Ruby project requires RSpec tests.", 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( def _strategy_default(
self, project_dir: Path, risk_level: str self, project_dir: Path, risk_level: str
) -> ValidationStrategy: ) -> ValidationStrategy:
+83
View File
@@ -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: class TestSubtaskTerminology:
"""Verify subtask terminology is used consistently.""" """Verify subtask terminology is used consistently."""
@@ -255,6 +337,7 @@ def run_tests():
TestAgentPrompt, TestAgentPrompt,
TestModuleIntegrity, TestModuleIntegrity,
TestProjectDocumentation, TestProjectDocumentation,
TestElectronToolScoping, # Note: requires pytest (uses monkeypatch)
TestSubtaskTerminology, TestSubtaskTerminology,
] ]
+90
View File
@@ -188,6 +188,55 @@ class TestProjectTypeDetection:
assert detect_project_type(temp_dir) == "nodejs" 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 # VALIDATION STEP TESTS
@@ -405,6 +454,47 @@ class TestStrategyBuilderByProjectType:
step_types = [s.step_type for s in strategy.steps] step_types = [s.step_type for s in strategy.steps]
assert "manual" in step_types 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 # SECURITY STEPS TESTS