"""司机端小程序 fixture — 通过 subprocess 驱动 miniprogram-automator (Node.js)。 原理: Python pytest → subprocess → node runner.js → miniprogram-automator → 微信开发者工具 → 小程序 返回 JSON: {"ok": true/false, "message": "...", "screenshot": "path"} 前置条件: 1. 微信开发者工具已安装 + 开启服务端口 2. npm install(在 tests/web/mini_program/automator/ 下) 3. uni-app 已编译为微信小程序 """ import json import subprocess from datetime import datetime from pathlib import Path import pytest PROJECT_ROOT = Path(__file__).parent.parent.parent.parent RUNNER = Path(__file__).parent / "automator" / "runner.js" SCREENSHOTS_DIR = Path("output/screenshots") def _run_runner(project_path: str, action: str, **kwargs) -> dict: """调用 node runner.js,返回解析后的 JSON。""" cmd = [ "node", str(RUNNER), "--action", action, "--project-path", project_path, "--timeout", str(kwargs.get("timeout", 30000)), ] for key, value in kwargs.items(): if value is not None and key not in ("timeout",): cmd.extend([f"--{key.replace('_', '-')}", str(value)]) result = subprocess.run(cmd, capture_output=True, text=True, timeout=120, cwd=str(PROJECT_ROOT)) try: data = json.loads(result.stdout.strip()) except json.JSONDecodeError: data = {"ok": False, "message": result.stderr or result.stdout, "screenshot": None} data["_raw_stdout"] = result.stdout data["_raw_stderr"] = result.stderr return data @pytest.fixture def mini_program_config(config): """小程序配置。""" return config["mini_program"] @pytest.fixture def run_mini_action(mini_program_config): """调用 miniprogram-automator 执行一个操作并返回结果。 Usage: result = run_mini_action("smoke") assert result["ok"], result["message"] # result["screenshot"] 是截图路径 """ def _run(action: str, **kwargs) -> dict: project_path = mini_program_config.get("project_path", ".") dev_tool = mini_program_config.get("dev_tool_path", "") timeout = mini_program_config.get("timeout_seconds", 120) * 1000 return _run_runner( project_path, action, dev_tool_path=dev_tool, timeout=timeout, **kwargs, ) return _run def save_failure_screenshot(name: str) -> str: """保存 Python 端的失败截图(如 runner 调用超时)。""" scenario_dir = SCREENSHOTS_DIR / "mini_program" scenario_dir.mkdir(parents=True, exist_ok=True) ts = datetime.now().strftime("%Y%m%d_%H%M%S") path = str(scenario_dir / f"FAIL_{name}_{ts}.png") # 此层无法截图小程序,创建空标记文件用于日志追溯 Path(path).touch() return path