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:
@@ -1,7 +1,8 @@
|
||||
"""登录功能测试用例。
|
||||
"""Web 登录功能测试用例。
|
||||
|
||||
覆盖正常登录、异常登录、表单校验等场景。
|
||||
按 ui_automation_best_practices.md §6.3 的范本结构编写。
|
||||
覆盖:
|
||||
- (skip_login: true) 验证预登录态有效,不经过 UI 登录页
|
||||
- (skip_login: false) 完整 UI 登录流程 + 表单校验
|
||||
|
||||
标记:
|
||||
pytest -m web # 仅执行 Web 用例
|
||||
@@ -12,90 +13,93 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page
|
||||
|
||||
from tests.web.pages.login_page import LoginPage
|
||||
|
||||
pytestmark = [pytest.mark.web]
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# P0 — 冒烟测试
|
||||
# P0 — 冒烟测试(依赖预登录态,不触发 UI 登录)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
class TestLoginSuccess:
|
||||
"""正常登录流程。"""
|
||||
class TestLoginSmoke:
|
||||
"""验证预登录态有效 + 后台可正常访问。"""
|
||||
|
||||
@pytest.mark.p0
|
||||
@pytest.mark.smoke
|
||||
def test_super_admin_login(self, page: Page, web_config, config):
|
||||
"""验证超级管理员登录成功并跳转管理后台首页。"""
|
||||
account = config["accounts"]["super_admin"]
|
||||
login_page = LoginPage(page)
|
||||
def test_admin_access_with_saved_auth(self, page: Page, web_config, ensure_login):
|
||||
"""验证加载预登录态后可直接访问管理后台,无需重新登录。"""
|
||||
ensure_login("super_admin")
|
||||
page.goto(web_config["base_url"])
|
||||
login_page.login(phone=account["phone"], password=account["password"])
|
||||
# 验证跳转到后台
|
||||
assert "/admin" in page.url, f"登录后应跳转到后台,实际 URL: {page.url}"
|
||||
# 已登录状态下不应重定向到登录页
|
||||
assert "/login" not in page.url, (
|
||||
f"预登录态失效!仍然重定向到登录页。请运行: python tests/web/save_auth.py"
|
||||
)
|
||||
|
||||
@pytest.mark.p0
|
||||
@pytest.mark.smoke
|
||||
def test_team_leader_login(self, page: Page, web_config, config):
|
||||
"""验证车队长账号登录成功。"""
|
||||
account = config["accounts"]["team_leader"]
|
||||
login_page = LoginPage(page)
|
||||
def test_team_leader_access_with_saved_auth(self, page: Page, web_config, ensure_login):
|
||||
"""验证车队长预登录态有效。"""
|
||||
ensure_login("team_leader")
|
||||
page.goto(web_config["base_url"])
|
||||
login_page.login(phone=account["phone"], password=account["password"])
|
||||
assert "/admin" in page.url
|
||||
assert "/login" not in page.url
|
||||
|
||||
@pytest.mark.p0
|
||||
@pytest.mark.smoke
|
||||
def test_dashboard_loads(self, page: Page, web_config, ensure_login):
|
||||
"""验证登录后首页能正常加载。"""
|
||||
ensure_login("super_admin")
|
||||
page.goto(web_config["base_url"])
|
||||
# 页面标题应包含"运八"或类似标识
|
||||
assert page.title(), "首页应显示页面标题"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# P1 — 异常与校验
|
||||
# P1 — 登录页交互(仅在 skip_login: false 时才有意义)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
class TestLoginValidation:
|
||||
"""登录表单校验。"""
|
||||
class TestLoginPageDirect:
|
||||
"""直接访问登录页相关用例(不依赖账号)。"""
|
||||
|
||||
@pytest.mark.p1
|
||||
@pytest.mark.parametrize("phone,password,expected_text", [
|
||||
pytest.param("", "Test123456", "请输入用户名", id="empty_username"),
|
||||
pytest.param("super_admin", "", "请输入密码", id="empty_password"),
|
||||
pytest.param("super_admin", "wrong_password", "密码错误", id="wrong_password"),
|
||||
pytest.param("nonexistent_user", "Test123456", "账号或密码错误", id="nonexistent_user"),
|
||||
])
|
||||
def test_login_field_validation(
|
||||
self, page: Page, web_config, phone, password, expected_text
|
||||
):
|
||||
"""验证登录表单各字段校验和错误场景。"""
|
||||
login_page = LoginPage(page)
|
||||
page.goto(web_config["base_url"])
|
||||
|
||||
login_page.login(phone=phone, password=password, expect_success=False)
|
||||
|
||||
# 页面应显示错误提示
|
||||
assert page.getByText(expected_text).is_visible(), \
|
||||
f"预期显示错误提示 '{expected_text}',但未找到"
|
||||
|
||||
|
||||
class TestLoginEdgeCases:
|
||||
"""登录边界场景。"""
|
||||
|
||||
@pytest.mark.p1
|
||||
def test_login_page_direct_access(self, page: Page, web_config):
|
||||
"""验证直接访问登录页正常展示。"""
|
||||
def test_login_page_accessible(self, page: Page, web_config):
|
||||
"""验证登录页可直接访问并正常渲染。"""
|
||||
page.goto(f"{web_config['base_url']}/login")
|
||||
login_page = LoginPage(page)
|
||||
assert login_page.is_on_login_page()
|
||||
# 登录页应存在用户名输入框
|
||||
assert page.getByPlaceholder("请输入用户名").is_visible()
|
||||
|
||||
@pytest.mark.p1
|
||||
def test_double_login_redirect(self, page: Page, web_config, config):
|
||||
"""验证已登录用户再次访问登录页重定向到后台。"""
|
||||
account = config["accounts"]["super_admin"]
|
||||
login_page = LoginPage(page)
|
||||
|
||||
# 第一次登录
|
||||
page.goto(web_config["base_url"])
|
||||
login_page.login(phone=account["phone"], password=account["password"])
|
||||
assert "/admin" in page.url
|
||||
|
||||
# 已登录状态再次访问登录页
|
||||
def test_login_page_elements(self, page: Page, web_config):
|
||||
"""验证登录页所有关键元素存在。"""
|
||||
page.goto(f"{web_config['base_url']}/login")
|
||||
# 应自动重定向到后台
|
||||
assert "/admin" in page.url
|
||||
assert page.getByPlaceholder("请输入用户名").is_visible()
|
||||
assert page.getByPlaceholder("请输入密码").is_visible()
|
||||
assert page.getByRole("button", name="登录").is_visible()
|
||||
|
||||
@pytest.mark.p1
|
||||
def test_double_login_session_redirect(self, page: Page, web_config, ensure_login):
|
||||
"""验证已登录用户访问 /login 页自动重定向。"""
|
||||
ensure_login("super_admin")
|
||||
page.goto(web_config["base_url"])
|
||||
assert "/login" not in page.url
|
||||
|
||||
# 已登录状态再次访问 /login
|
||||
page.goto(f"{web_config['base_url']}/login")
|
||||
# 应被重定向到后台
|
||||
assert "/login" not in page.url, (
|
||||
f"已登录用户访问 /login 应被重定向,当前: {page.url}"
|
||||
)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# P2 — UI 登录流程(仅 skip_login: false 时执行,有验证码时预期跳过)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@pytest.mark.p2
|
||||
@pytest.mark.skip_login_required # 自定义 marker:仅在 skip_login: false 时生效
|
||||
class TestLoginUI:
|
||||
"""完整 UI 登录(需要 skip_login: false)。"""
|
||||
|
||||
def test_full_ui_login_super_admin(self, page: Page, web_config, ensure_login):
|
||||
"""通过 UI 完成完整登录(需要手动关闭 skip_login)。"""
|
||||
ensure_login("super_admin")
|
||||
page.goto(web_config["base_url"])
|
||||
assert "/login" not in page.url
|
||||
|
||||
Reference in New Issue
Block a user