- 新增 e2e:settings_flow(改名→登出→登录页即时生效→finally 还原防污染)、 secondary_allocation_flow(建客户/员→录两行 30%+40%→编辑回显与合计条 余额提示→删除清理;宽视口防弹窗遮挡、alloc-table 专属类防选择器错位) - 修复 e2e 首跑抓出的两处缺陷:① public 端点 300s 响应缓存让登录页拿旧 公司名(登录页改时间戳 fetch + cache:no-store,手动解包信封)② logo 同 扩展名覆盖时先写后删(清理移到写入前且仅异扩展名执行) - 分配表加 alloc-table 专属类(与产品明细 items-table 区分) - 全量回归 697 passed(基线 625 + 72);e2e 七脚本全过(既有五脚本回归 + 两个新增);pnpm type-check 零错误 - tests/README.md 增 M3 节(44 例明细)/运行记录/缺陷 3 行;frontend/ README.md 增 M3 e2e 节 Co-Authored-By: Claude Code <noreply@anthropic.com>
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())
|