feat: 框架增加登录态复用机制,绕过图形验证码

Web 端 (storage_state):
- 新增 tests/web/save_auth.py: 手动登录后保存 cookies+localStorage
- conftest.py: skip_login:true 时自动加载 storage_state,跳过 UI 登录
- ensure_login fixture: 登录态有效则直接通过,过期则给出明确指引

App 端 (noReset):
- conftest.py: ensure_logged_in fixture,手动登录后无需重新登录
- noReset=true 持久化登录态,未登录自动检测并给出明确引导

配置文件:
- web.skip_login: true + storage_state_dir
- app.skip_login: true + 手动登录说明
This commit is contained in:
xst
2026-07-15 10:47:34 +08:00
parent aade9e39d0
commit 7b4a54f1ce
6 changed files with 518 additions and 161 deletions
+136
View File
@@ -0,0 +1,136 @@
#!/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.getByPlaceholder("请输入用户名").fill(account["phone"])
page.getByPlaceholder("请输入密码").fill(account["password"])
print(f"👤 已填入账号: {account['phone']}(角色: {args.role}")
print("📝 请在浏览器中手动完成验证码并点击登录...")
print(" (登录成功后脚本会自动检测并保存)\n")
# ── 等待用户手动完成验证码 + 登录 ──
try:
page.wait_for_url(f"{base_url}/**", timeout=300_000) # 5 分钟超时
except Exception:
print("\n⚠️ 等待超时(5 分钟)。可能原因:")
print(" 1. 未在浏览器中完成验证码登录")
print(" 2. 账号密码错误")
print(" 3. 网络不可达")
print(" 请检查后重试。")
browser.close()
sys.exit(1)
# ── 验证登录成功 ──
if "/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()