128 lines
4.8 KiB
Python
128 lines
4.8 KiB
Python
|
|
"""公司信息设置端到端验证。
|
|||
|
|
|
|||
|
|
覆盖的链路(只有真实浏览器才能验证的组合):
|
|||
|
|
- 登录 → 系统管理分组出现「公司信息」菜单 → 进入设置页
|
|||
|
|
- 修改公司名称并保存 → 全局提示「已保存」
|
|||
|
|
- 退出登录 → 登录页品牌位显示**新**公司名(public 端点即时生效)
|
|||
|
|
- 还原公司名(finally:防污染 login_flow/system_management 的品牌名断言)
|
|||
|
|
|
|||
|
|
运行前置(缺一不可):
|
|||
|
|
docker compose up -d # MySQL + Redis
|
|||
|
|
cd backend && uv run uvicorn app.main:app --port 8000
|
|||
|
|
cd frontend && pnpm dev
|
|||
|
|
cd frontend && E2E_ADMIN_PASSWORD=<开发库admin密码> python e2e/settings_flow.py
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
from playwright.sync_api import Page, sync_playwright
|
|||
|
|
|
|||
|
|
from common import (
|
|||
|
|
BASE_URL,
|
|||
|
|
E2EFailure,
|
|||
|
|
collect_console_errors,
|
|||
|
|
current_path,
|
|||
|
|
expect_toast,
|
|||
|
|
fail,
|
|||
|
|
login,
|
|||
|
|
open_menu,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
ADMIN = "admin"
|
|||
|
|
console_errors: list[str] = []
|
|||
|
|
|
|||
|
|
|
|||
|
|
def step_change_company_name(page: Page, admin_password: str) -> str:
|
|||
|
|
"""进设置页改公司名(加随机后缀避免撞库),返回新名供后续断言。"""
|
|||
|
|
login(page, ADMIN, admin_password)
|
|||
|
|
open_menu(page, "公司信息", "系统管理")
|
|||
|
|
if current_path(page) != "/system/settings":
|
|||
|
|
fail("菜单导航", f"未进入公司信息页,当前 {page.url}")
|
|||
|
|
|
|||
|
|
# 随机名:单行特性下库可复用,避免与既有公司名撞车;断言不依赖「默认」前提
|
|||
|
|
new_name = f"衍水e2e{os.getpid()}{os.urandom(2).hex()}"
|
|||
|
|
page.fill('input[placeholder="展示在登录页与导出页眉"]', new_name)
|
|||
|
|
page.click("button:has-text('保存')")
|
|||
|
|
expect_toast(page, "已保存", "修改公司名称")
|
|||
|
|
print(f"✓ 1. 设置页导航 进入公司信息并保存新名称「{new_name}」")
|
|||
|
|
return new_name
|
|||
|
|
|
|||
|
|
|
|||
|
|
def step_logout(page: Page) -> None:
|
|||
|
|
page.click("text=退出登录")
|
|||
|
|
page.wait_for_url("**/login**", timeout=10000)
|
|||
|
|
print("✓ 2. 退出登录 已回到登录页")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def step_login_page_reflects(page: Page, new_name: str) -> None:
|
|||
|
|
"""登录页品牌位应显示新公司名(public 端点即时生效)。
|
|||
|
|
|
|||
|
|
等 brand 元素文本变为新名(而非固定 sleep):public 请求是异步的,
|
|||
|
|
固定等待在慢机上会偶发拿到旧值。
|
|||
|
|
"""
|
|||
|
|
page.goto(f"{BASE_URL}/login", wait_until="networkidle")
|
|||
|
|
try:
|
|||
|
|
page.locator(".card-brand__name").filter(has_text=new_name).wait_for(
|
|||
|
|
state="visible", timeout=8000
|
|||
|
|
)
|
|||
|
|
except Exception:
|
|||
|
|
brand = page.locator(".card-brand__name").inner_text().strip()
|
|||
|
|
fail("登录页品牌", f"品牌位未显示新名称「{new_name}」,当前:{brand}")
|
|||
|
|
foot = page.locator(".login-foot").inner_text()
|
|||
|
|
if new_name not in foot:
|
|||
|
|
fail("登录页页脚", f"页脚未含新名称「{new_name}」,当前:{foot}")
|
|||
|
|
print(f"✓ 3. 品牌即时生效 登录页品牌位与页脚已显示「{new_name}」")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def step_restore(page: Page, admin_password: str) -> None:
|
|||
|
|
"""还原默认公司名,防污染后续 e2e 的品牌名断言。"""
|
|||
|
|
login(page, ADMIN, admin_password)
|
|||
|
|
open_menu(page, "公司信息", "系统管理")
|
|||
|
|
page.fill('input[placeholder="展示在登录页与导出页眉"]', "衍水集团")
|
|||
|
|
page.click("button:has-text('保存')")
|
|||
|
|
expect_toast(page, "已保存", "还原公司名称")
|
|||
|
|
print("✓ 4. 数据清理 公司名已还原为「衍水集团」")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def run(page: Page) -> None:
|
|||
|
|
collect_console_errors(page, console_errors)
|
|||
|
|
admin_password = os.environ.get("E2E_ADMIN_PASSWORD", "")
|
|||
|
|
if not admin_password:
|
|||
|
|
fail("环境变量", "未设置 E2E_ADMIN_PASSWORD(开发库 admin 密码)")
|
|||
|
|
|
|||
|
|
new_name = step_change_company_name(page, admin_password)
|
|||
|
|
try:
|
|||
|
|
step_logout(page)
|
|||
|
|
step_login_page_reflects(page, new_name)
|
|||
|
|
finally:
|
|||
|
|
step_restore(page, admin_password)
|
|||
|
|
|
|||
|
|
if console_errors:
|
|||
|
|
print(f" (控制台错误 {len(console_errors)} 条:{console_errors[:2]})")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> int:
|
|||
|
|
with sync_playwright() as playwright:
|
|||
|
|
browser = playwright.chromium.launch(headless=True)
|
|||
|
|
page = browser.new_page()
|
|||
|
|
page.set_default_timeout(10000)
|
|||
|
|
try:
|
|||
|
|
run(page)
|
|||
|
|
except E2EFailure as exc:
|
|||
|
|
page.screenshot(path="e2e/failure.png", full_page=True)
|
|||
|
|
print(f"\n ✗ {exc}")
|
|||
|
|
print(" 已保存截图 e2e/failure.png")
|
|||
|
|
return 1
|
|||
|
|
except Exception as exc: # noqa: BLE001 — 测试脚本需打印完整失败原因
|
|||
|
|
page.screenshot(path="e2e/failure.png", full_page=True)
|
|||
|
|
print(f"\n ✗ 执行中断:{type(exc).__name__}: {exc}")
|
|||
|
|
return 1
|
|||
|
|
finally:
|
|||
|
|
browser.close()
|
|||
|
|
print("\n全部通过。")
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
raise SystemExit(main())
|