154 lines
4.0 KiB
Python
154 lines
4.0 KiB
Python
"""Scheduled runner for the Playwright signup E2E test.
|
|
|
|
Site Config Keys:
|
|
enable_signup_e2e: truthy (1/true/yes/on) to run during scheduled job.
|
|
signup_e2e_base_url: override BASE_URL (defaults to frappe.utils.get_url()).
|
|
signup_e2e_timeout_seconds: overall subprocess timeout (default 900).
|
|
signup_e2e_otp_helper_endpoint: forwarded to OTP_HELPER_ENDPOINT for the test.
|
|
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import os
|
|
import random
|
|
import signal
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import frappe
|
|
|
|
TRUTHY = {"1", "true", "yes", "on"}
|
|
|
|
|
|
def _truthy(val: object) -> bool:
|
|
if val is None:
|
|
return False
|
|
if isinstance(val, bool):
|
|
return val
|
|
return str(val).lower() in TRUTHY
|
|
|
|
|
|
def run_signup_e2e(): # noqa: C901
|
|
if not _truthy(frappe.conf.get("enable_signup_e2e")):
|
|
return
|
|
|
|
app_root = Path(__file__).resolve().parent.parent # .../apps/press
|
|
dashboard_dir = app_root / "dashboard"
|
|
|
|
if not dashboard_dir.exists():
|
|
frappe.log(f"signup_e2e: dashboard directory not found at {dashboard_dir}")
|
|
return
|
|
|
|
product_trials_list = frappe.db.get_all(
|
|
"Product Trial",
|
|
filters={"published": 1},
|
|
pluck="name",
|
|
order_by="name asc",
|
|
)
|
|
if not product_trials_list:
|
|
frappe.log("signup_e2e: no published product trials found; aborting run")
|
|
return
|
|
|
|
random.shuffle(product_trials_list)
|
|
product_trials_list = product_trials_list[:3]
|
|
|
|
base_url = frappe.conf.get("signup_e2e_base_url") or frappe.utils.get_url()
|
|
timeout = int(frappe.conf.get("signup_e2e_timeout_seconds") or 900)
|
|
otp_helper = frappe.conf.get("signup_e2e_otp_helper_endpoint")
|
|
|
|
env = os.environ.copy()
|
|
env["PRODUCT_TRIALS"] = ",".join(product_trials_list)
|
|
env["BASE_URL"] = base_url
|
|
if otp_helper:
|
|
env["OTP_HELPER_ENDPOINT"] = str(otp_helper)
|
|
|
|
cmd = ["npm", "run", "test:e2e", "--", "--project=cron"]
|
|
frappe.log(
|
|
f"signup_e2e: starting Playwright test (products={env.get('PRODUCT_TRIALS')} base_url={env.get('BASE_URL')} timeout={timeout}s)"
|
|
)
|
|
|
|
try:
|
|
proc = subprocess.Popen(
|
|
cmd,
|
|
cwd=str(dashboard_dir),
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
env=env,
|
|
text=True,
|
|
)
|
|
except FileNotFoundError:
|
|
frappe.log("signup_e2e: failed to spawn npm (is Node/npm installed in this environment?)")
|
|
return
|
|
|
|
output_lines = []
|
|
try:
|
|
try:
|
|
while True:
|
|
line = proc.stdout.readline() # type: ignore[attr-defined]
|
|
if not line and proc.poll() is not None:
|
|
break
|
|
if line:
|
|
output_lines.append(line.rstrip())
|
|
except Exception:
|
|
remaining, _ = proc.communicate(timeout=max(5, timeout // 3))
|
|
if remaining:
|
|
output_lines.append(remaining)
|
|
proc.wait(timeout=timeout)
|
|
except subprocess.TimeoutExpired:
|
|
frappe.log(f"signup_e2e: timeout after {timeout}s; terminating process")
|
|
with contextlib.suppress(Exception):
|
|
proc.send_signal(signal.SIGINT)
|
|
with contextlib.suppress(Exception):
|
|
proc.kill()
|
|
except Exception as e:
|
|
frappe.log(f"signup_e2e: unexpected error: {e}")
|
|
|
|
exit_code = proc.returncode if proc.returncode is not None else -1
|
|
|
|
MAX_LINES = 2000
|
|
if len(output_lines) > MAX_LINES:
|
|
trimmed = len(output_lines) - MAX_LINES
|
|
output_lines = output_lines[-MAX_LINES:]
|
|
output_lines.insert(0, f"[signup_e2e] (truncated {trimmed} earlier lines)")
|
|
|
|
frappe.log(
|
|
f"signup_e2e: completed with exit_code={exit_code} lines={len(output_lines)}\n"
|
|
+ "\n".join(output_lines)
|
|
)
|
|
|
|
if exit_code != 0:
|
|
frappe.log_error(
|
|
title="Signup E2E failed",
|
|
message=f"Exit code: {exit_code}\nLast 50 lines:\n" + "\n".join(output_lines[-50:]),
|
|
)
|
|
|
|
clean_up()
|
|
|
|
|
|
def clean_up():
|
|
signup_teams = frappe.db.get_all(
|
|
"Team", {"user": ("like", "%fc-signup-test+%"), "enabled": 1}, pluck="name"
|
|
)
|
|
if not signup_teams:
|
|
return
|
|
|
|
trial_sites = frappe.db.get_all(
|
|
"Site",
|
|
{"team": ("in", signup_teams), "status": "Active", "standby_for_product": ("is", "set")},
|
|
pluck="name",
|
|
)
|
|
for site in trial_sites:
|
|
try:
|
|
frappe.get_pg("Site", site).archive()
|
|
frappe.db.commit()
|
|
except Exception:
|
|
frappe.db.rollback()
|
|
|
|
frappe.db.set_value("Team", {"name": ("in", signup_teams)}, "enabled", 0)
|
|
frappe.db.commit()
|
|
|
|
|
|
__all__ = ["run_signup_e2e"]
|