LetsBeBiz-Redesign/letsbe-sysadmin-agent/app/playwright_scenarios/calcom/initial_setup.py

255 lines
9.0 KiB
Python
Raw Normal View History

"""Cal.com initial setup scenario.
Automates the first-time setup for a fresh Cal.com installation.
This scenario:
1. Navigates to the Cal.com setup page
2. Creates the admin account
3. Completes onboarding 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 CalcomInitialSetup(BaseScenario):
"""Automate Cal.com first-time admin account setup.
This scenario handles the initial account creation when
Cal.com is freshly installed. It navigates to the signup page,
fills in account details, and completes the onboarding wizard.
Required inputs:
base_url: The Cal.com instance URL (e.g., https://cal.example.com)
admin_email: Email address for the admin account
Optional inputs:
admin_password: Password for admin account (auto-generated if not provided)
admin_username: Username for the admin account (default: "admin")
admin_name: Display name for the admin account (default: "Admin")
Result data:
setup_completed: Whether initial setup was completed
admin_email: The configured admin email address
admin_password: The password (generated or provided) - STORE SECURELY
already_configured: True if Cal.com was already set up
"""
@property
def name(self) -> str:
return "calcom_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_username", "admin_name"]
@property
def description(self) -> str:
return "Automate Cal.com first-time admin account setup"
async def execute(
self,
page: Page,
inputs: dict[str, Any],
options: ScenarioOptions,
) -> ScenarioResult:
"""Execute the Cal.com 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_username = inputs.get("admin_username", "admin")
admin_name = inputs.get("admin_name", "Admin")
screenshots = []
result_data = {
"setup_completed": False,
"admin_email": admin_email,
"admin_password": admin_password,
"already_configured": False,
}
try:
# Navigate to Cal.com
await page.goto(base_url, wait_until="networkidle")
current_url = page.url
# Check if already configured (redirects to login)
if "/auth/login" in current_url:
result_data["already_configured"] = True
result_data["setup_completed"] = True
return ScenarioResult(
success=True,
data=result_data,
screenshots=screenshots,
error=None,
)
# Navigate to signup page
signup_url = f"{base_url}/signup"
await page.goto(signup_url, wait_until="networkidle")
# If redirected to login, the instance may already be set up
if "/auth/login" in page.url and "/signup" not 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 signup form
# Username
username_input = page.locator(
'input[name="username"], '
'input[id="username"], '
'input[placeholder*="username" i]'
).first
if await username_input.count() > 0:
await username_input.wait_for(state="visible", timeout=10000)
await username_input.fill(admin_username)
# Full name
name_input = page.locator(
'input[name="name"], '
'input[name="full_name"], '
'input[placeholder*="name" i]'
).first
if await name_input.count() > 0:
await name_input.fill(admin_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 / "calcom_pre_submit.png"
await page.screenshot(path=str(pre_submit_path))
screenshots.append(str(pre_submit_path))
# Click Sign up / Create Account button
submit_button = page.locator(
'button:has-text("Sign up"), '
'button:has-text("Create"), '
'button:has-text("Register"), '
'button[type="submit"]'
).first
await submit_button.click()
# Wait for onboarding or dashboard
await page.wait_for_timeout(3000)
# Cal.com has an onboarding wizard after signup
# Skip through onboarding steps
for _ in range(5):
skip_button = page.locator(
'button:has-text("Skip"), '
'a:has-text("Skip"), '
'button:has-text("Next"), '
'button:has-text("Continue"), '
'button:has-text("Finish")'
)
if await skip_button.count() > 0:
await skip_button.first.click()
await page.wait_for_timeout(2000)
else:
break
# Check if we reached the dashboard or event types page
await page.wait_for_timeout(2000)
current_url = page.url
if any(kw in current_url for kw in ["/event-types", "/dashboard", "/bookings", "/settings"]):
result_data["setup_completed"] = True
else:
# Check for dashboard indicators
dashboard_el = page.locator(
'[class*="event-type"], '
'[class*="dashboard"], '
':has-text("Event Types")'
)
if await dashboard_el.count() > 0:
result_data["setup_completed"] = True
# Take final screenshot
if options.screenshot_on_success and options.artifacts_dir:
final_path = options.artifacts_dir / "calcom_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 / "calcom_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"Cal.com setup failed: {str(e)}",
)