From 3eb2eadaf13f374df87651caedac9c8a91f2fede Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Tue, 16 Dec 2025 14:50:05 +0100 Subject: [PATCH] Introduce electron mcp for electron debugging/validation --- auto-claude/.env.example | 31 ++++++++ auto-claude/auto_claude_tools.py | 35 ++++++++- auto-claude/client.py | 56 ++++++++++++++ auto-claude/prompts/qa_reviewer.md | 119 +++++++++++++++++++++++++++++ auto-claude/review/__init__.py | 13 +++- auto-claude/validation_strategy.py | 79 +++++++++++++++++++ tests/test_agent_architecture.py | 83 ++++++++++++++++++++ tests/test_validation_strategy.py | 90 ++++++++++++++++++++++ 8 files changed, 504 insertions(+), 2 deletions(-) diff --git a/auto-claude/.env.example b/auto-claude/.env.example index c4ed765a..d794366d 100644 --- a/auto-claude/.env.example +++ b/auto-claude/.env.example @@ -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) # ============================================================================= diff --git a/auto-claude/auto_claude_tools.py b/auto-claude/auto_claude_tools.py index 23477a77..52f8b44f 100644 --- a/auto-claude/auto_claude_tools.py +++ b/auto-claude/auto_claude_tools.py @@ -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: diff --git a/auto-claude/client.py b/auto-claude/client.py index ce296389..8a628f2a 100644 --- a/auto-claude/client.py +++ b/auto-claude/client.py @@ -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= 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: diff --git a/auto-claude/prompts/qa_reviewer.md b/auto-claude/prompts/qa_reviewer.md index 3acaa64b..272670e5 100644 --- a/auto-claude/prompts/qa_reviewer.md +++ b/auto-claude/prompts/qa_reviewer.md @@ -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 diff --git a/auto-claude/review/__init__.py b/auto-claude/review/__init__.py index 0b877fdf..7ab52a8a 100644 --- a/auto-claude/review/__init__.py +++ b/auto-claude/review/__init__.py @@ -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", +] \ No newline at end of file diff --git a/auto-claude/validation_strategy.py b/auto-claude/validation_strategy.py index f39b8952..cad467e2 100644 --- a/auto-claude/validation_strategy.py +++ b/auto-claude/validation_strategy.py @@ -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: diff --git a/tests/test_agent_architecture.py b/tests/test_agent_architecture.py index e60d1afa..0c081b72 100644 --- a/tests/test_agent_architecture.py +++ b/tests/test_agent_architecture.py @@ -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, ] diff --git a/tests/test_validation_strategy.py b/tests/test_validation_strategy.py index 57f938d9..c392f2b7 100644 --- a/tests/test_validation_strategy.py +++ b/tests/test_validation_strategy.py @@ -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