"""n8n initial setup scenario. Automates the first-time setup for a fresh n8n installation. This scenario: 1. Navigates to the n8n setup page 2. Creates the owner account with email and password 3. Skips optional setup steps """ import secrets import string from typing import Any from playwright.async_api import Page from app.playwright_scenarios import register_scenario from app.playwright_scenarios.base import BaseScenario, ScenarioOptions, ScenarioResult def generate_secure_password(length: int = 24) -> str: """Generate a cryptographically secure password. Args: length: Password length (default: 24) Returns: A secure random password with mixed characters """ alphabet = string.ascii_letters + string.digits + "!@#$%^&*" password = [ secrets.choice(string.ascii_lowercase), secrets.choice(string.ascii_uppercase), secrets.choice(string.digits), secrets.choice("!@#$%^&*"), ] password.extend(secrets.choice(alphabet) for _ in range(length - 4)) password_list = list(password) secrets.SystemRandom().shuffle(password_list) return "".join(password_list) @register_scenario class N8nInitialSetup(BaseScenario): """Automate n8n first-time owner account setup. This scenario handles the initial owner account creation when n8n is freshly installed. It fills in the account details and completes the setup wizard. Required inputs: base_url: The n8n instance URL (e.g., https://n8n.example.com) admin_email: Email address for the owner account Optional inputs: admin_password: Password for owner account (auto-generated if not provided) admin_first_name: First name for the owner (default: "Admin") admin_last_name: Last name for the owner (default: "User") Result data: setup_completed: Whether initial setup was completed admin_email: The configured owner email address admin_password: The password (generated or provided) - STORE SECURELY already_configured: True if n8n was already set up """ @property def name(self) -> str: return "n8n_initial_setup" @property def required_inputs(self) -> list[str]: return ["base_url", "admin_email"] @property def optional_inputs(self) -> list[str]: return ["admin_password", "admin_first_name", "admin_last_name"] @property def description(self) -> str: return "Automate n8n first-time owner account setup" async def execute( self, page: Page, inputs: dict[str, Any], options: ScenarioOptions, ) -> ScenarioResult: """Execute the n8n initial setup. Args: page: Playwright Page object inputs: Scenario inputs (base_url, admin_email) options: Scenario options Returns: ScenarioResult with setup status and credentials """ base_url = inputs["base_url"].rstrip("/") admin_email = inputs["admin_email"] admin_password = inputs.get("admin_password") or generate_secure_password() admin_first_name = inputs.get("admin_first_name", "Admin") admin_last_name = inputs.get("admin_last_name", "User") screenshots = [] result_data = { "setup_completed": False, "admin_email": admin_email, "admin_password": admin_password, "already_configured": False, } try: # Navigate to n8n await page.goto(base_url, wait_until="networkidle") current_url = page.url # Check if already configured (redirects to signin) if "/signin" in current_url: result_data["already_configured"] = True result_data["setup_completed"] = True return ScenarioResult( success=True, data=result_data, screenshots=screenshots, error=None, ) # n8n setup page should show the owner setup form # Look for setup form elements email_input = page.locator( 'input[name="email"], ' 'input[type="email"], ' 'input[placeholder*="email" i], ' 'input[autocomplete="email"]' ) if await email_input.count() == 0: # Try navigating to setup URL await page.goto(f"{base_url}/setup", wait_until="networkidle") if "/signin" in page.url: result_data["already_configured"] = True result_data["setup_completed"] = True return ScenarioResult( success=True, data=result_data, screenshots=screenshots, error=None, ) # Fill in the owner setup form # First name first_name_input = page.locator( 'input[name="firstName"], ' 'input[name="first_name"], ' 'input[placeholder*="first" i], ' 'input[autocomplete="given-name"]' ).first if await first_name_input.count() > 0: await first_name_input.wait_for(state="visible", timeout=10000) await first_name_input.fill(admin_first_name) # Last name last_name_input = page.locator( 'input[name="lastName"], ' 'input[name="last_name"], ' 'input[placeholder*="last" i], ' 'input[autocomplete="family-name"]' ).first if await last_name_input.count() > 0: await last_name_input.fill(admin_last_name) # Email email_input = page.locator( 'input[name="email"], ' 'input[type="email"], ' 'input[placeholder*="email" i]' ).first await email_input.wait_for(state="visible", timeout=10000) await email_input.fill(admin_email) # Password password_input = page.locator( 'input[name="password"], ' 'input[type="password"]' ).first await password_input.fill(admin_password) # Take screenshot before submitting if options.screenshot_on_success and options.artifacts_dir: pre_submit_path = options.artifacts_dir / "n8n_pre_submit.png" await page.screenshot(path=str(pre_submit_path)) screenshots.append(str(pre_submit_path)) # Click Next / Create Account button submit_button = page.locator( 'button:has-text("Next"), ' 'button:has-text("Create"), ' 'button:has-text("Get started"), ' 'button[type="submit"]' ).first await submit_button.click() # Wait for next step or dashboard await page.wait_for_timeout(3000) # n8n may show additional setup steps (personalization, usage, etc.) # Skip through them for _ in range(3): skip_button = page.locator( 'button:has-text("Skip"), ' 'a:has-text("Skip"), ' 'button:has-text("Get started"), ' 'button:has-text("Next")' ) if await skip_button.count() > 0: await skip_button.first.click() await page.wait_for_timeout(2000) else: break # Check if we reached the workflow editor or dashboard await page.wait_for_timeout(2000) current_url = page.url if any(kw in current_url for kw in ["/workflow", "/home", "/dashboard"]): result_data["setup_completed"] = True else: # Check for indicators of successful setup canvas = page.locator( '.workflow-canvas, ' '[class*="workflow"], ' '[class*="canvas"], ' '#app' ) if await canvas.count() > 0: result_data["setup_completed"] = True # Take final screenshot if options.screenshot_on_success and options.artifacts_dir: final_path = options.artifacts_dir / "n8n_setup_complete.png" await page.screenshot(path=str(final_path)) screenshots.append(str(final_path)) return ScenarioResult( success=result_data["setup_completed"], data=result_data, screenshots=screenshots, error=None if result_data["setup_completed"] else "Setup may not have completed", ) except Exception as e: if options.screenshot_on_failure and options.artifacts_dir: error_path = options.artifacts_dir / "n8n_setup_error.png" await page.screenshot(path=str(error_path)) screenshots.append(str(error_path)) return ScenarioResult( success=False, data=result_data, screenshots=screenshots, error=f"n8n setup failed: {str(e)}", )