"""公司信息设置端到端验证。 覆盖的链路(只有真实浏览器才能验证的组合): - 登录 → 系统管理分组出现「公司信息」菜单 → 进入设置页 - 修改公司名称并保存 → 全局提示「已保存」 - 退出登录 → 登录页品牌位显示**新**公司名(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())