#!/usr/bin/env python3 """手动登录并保存浏览器登录态(cookies + localStorage)。 目的: 绕过图形验证码——人工完成一次验证码登录后, cookies + localStorage 序列化到 JSON 文件,后续用例自动复用。 用法: # 保存超级管理员的登录态(最常用) python tests/web/save_auth.py # 保存指定角色的登录态 python tests/web/save_auth.py --role team_leader # 指定浏览器 python tests/web/save_auth.py --browser firefox # 查看帮助 python tests/web/save_auth.py --help 生成的 auth 文件: tests/web/auth/super_admin_chromium.json tests/web/auth/team_leader_firefox.json ... """ import argparse import sys from pathlib import Path # 确保 tests/ 在 Python path 中 sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from tests.config.test_config import load_config # 登录页的 URL 后缀 LOGIN_PATH = "/login" # 登录后验证成功的 URL 关键字 SUCCESS_URL_KEYWORD = "/admin" def main(): parser = argparse.ArgumentParser( description="手动登录并保存浏览器登录态(绕过图形验证码)" ) parser.add_argument( "--role", default="super_admin", choices=["super_admin", "team_leader", "driver"], help="登录角色(默认: super_admin)", ) parser.add_argument( "--browser", default="chromium", choices=["chromium", "firefox", "webkit"], help="浏览器类型(默认: chromium)", ) parser.add_argument( "--headless", action="store_true", help="无头模式(开启后将无法手动输入验证码,仅用于测试已保存的登录态)", ) args = parser.parse_args() config = load_config() web_config = config["web"] account = config["accounts"].get(args.role) if not account: print(f"❌ 未知角色 '{args.role}',可用: {list(config['accounts'].keys())}") sys.exit(1) base_url = web_config["base_url"] login_url = f"{base_url}/#/login" # ── 打开浏览器 ── from playwright.sync_api import sync_playwright with sync_playwright() as p: browser_launcher = getattr(p, args.browser) browser = browser_launcher.launch(headless=args.headless) context = browser.new_context( viewport={"width": 1920, "height": 1080}, locale="zh-CN", ) page = context.new_page() # ── 导航到登录页 ── print(f"\n🔗 打开登录页: {login_url}") page.goto(login_url) # ── 填入账号密码 ── page.get_by_placeholder("请输入用户名").fill(account["phone"]) page.get_by_placeholder("请输入密码").fill(account["password"]) print(f"👤 已填入账号: {account['phone']}(角色: {args.role})") print("📝 请在浏览器中手动完成验证码并点击登录...") print(" (登录成功后脚本会自动检测并保存)\n") # ── 等待用户手动完成验证码 + 登录 ── try: # Hash 路由模式,用元素可见判断登录成功而不是 URL 变化 page.wait_for_selector(".ant-layout-sider, .sidebar, [class*='sider']", timeout=300_000) except Exception: print("\n⚠️ 等待超时(5 分钟)。可能原因:") print(" 1. 未在浏览器中完成验证码登录") print(" 2. 账号密码错误") print(" 3. 网络不可达") print(" 请检查后重试。") browser.close() sys.exit(1) # ── 验证登录成功 ── if "/login" in page.url or "/#/login" in page.url: print("\n❌ 登录失败,仍然停留在登录页。请检查账号密码。") browser.close() sys.exit(1) print(f"✅ 登录成功!当前页面: {page.url}") # ── 保存登录态 ── auth_dir = Path(web_config.get("storage_state_dir", "tests/web/auth")) auth_dir.mkdir(parents=True, exist_ok=True) auth_file = auth_dir / f"{args.role}_{args.browser}.json" context.storage_state(path=str(auth_file)) print(f"💾 登录态已保存到: {auth_file}") browser.close() print("\n🎉 完成!现在可以运行用例了:") print(f" pytest tests/web/ -m p0") print(f"\n登录态过期后重新运行即可:") print(f" python tests/web/save_auth.py --role {args.role}") if __name__ == "__main__": main()