跳转至

Playwright 端到端测试

本仓库的端到端测试统一放在 tests/e2e/,技术栈固定为 Playwright。当前方案覆盖真实管理员会话、API 预置/回收测试数据、Page Object Model(POM)分层、全后台路由 smoke,以及保留既有的 document recognition 视觉回归能力。

目标与边界

  • 端到端测试验证的是 admin-frontend 的关键后台业务流,不替代现有 pytest
  • 当前第一阶段覆盖 5 类场景:登录、全后台页面 smoke、evaluation、knowledge sources、MCP、document recognition。
  • 受保护页面必须通过真实管理员登录进入,不允许关闭鉴权或注入特判。
  • 测试数据必须通过 API 创建和回收,不允许依赖手工数据库初始化。

目录结构

tests/e2e/
  .auth/
    admin.json
  fixtures/
    admin-session.fixture.ts
  page-objects/
    AdminLoginPage.ts
    DocumentRecognitionPage.ts
    EvaluationDatasetDetailPage.ts
    KnowledgeSourceDetailPage.ts
    McpServersPage.ts
  support/
    api-client.ts
    cleanup.ts
    env.ts
    knowledge-sources.ts
    test-data.ts
  tests/
    setup/
      auth.setup.ts
    smoke/
      admin-login.spec.ts
      admin-pages.spec.ts
    workflows/
      evaluation-dataset-freeze.spec.ts
      knowledge-source-document-upload.spec.ts
      mcp-server-test-console.spec.ts
      document-recognition.spec.ts
    admin-document-recognition.spec.ts
  playwright.config.ts
  tsconfig.json
  .eslintrc.cjs

认证与 storageState

  • tests/setup/auth.setup.ts 是唯一的认证初始化入口。
  • setup 项目会通过真实登录页写出 tests/e2e/.auth/admin.json,其后业务流 spec 复用该 storageState
  • tests/e2e/.auth/.gitignore 忽略;不要提交 storageState,因为它包含真实 Cookie。
  • 有效模式读取规则如下:
  • 显式设置了 PLAYWRIGHT_STACK_MODE 时,以它为准
  • 未显式设置且 PLAYWRIGHT_SKIP_STACK_BOOT=1 时,默认按 dev 模式处理
  • 其他情况默认按 docker 模式处理
  • 默认 readiness / base URL 也跟随模式变化:
  • dockerhttp://127.0.0.1:28000/healthzhttp://127.0.0.1:28081
  • devhttp://127.0.0.1:8000/healthzhttp://127.0.0.1:5174
  • API seed client 默认直连 ai-service
  • dockerhttp://127.0.0.1:28000
  • devhttp://127.0.0.1:8000
  • 如果 API 不在这些地址,显式设置 PLAYWRIGHT_API_BASE_URL
  • 连接已存在的管理员账号时,登录标识读取顺序为:
  • PLAYWRIGHT_ADMIN_IDENTIFIER
  • PLAYWRIGHT_ADMIN_USERNAME
  • PLAYWRIGHT_ADMIN_EMAIL
  • ADMIN_ACCESS_BOOTSTRAP_USERNAME
  • ADMIN_ACCESS_BOOTSTRAP_EMAIL
  • 密码读取顺序为:
  • PLAYWRIGHT_ADMIN_PASSWORD
  • ADMIN_ACCESS_BOOTSTRAP_PASSWORD
  • 默认 Docker 模式会先执行 just _e2e-stack-up 并重建一套全新的隔离栈,因此必须同时提供 bootstrap 三元组:
  • PLAYWRIGHT_ADMIN_EMAIL
  • PLAYWRIGHT_ADMIN_USERNAME
  • PLAYWRIGHT_ADMIN_PASSWORD
  • 也可以直接提供等价的 ADMIN_ACCESS_BOOTSTRAP_*
  • Docker E2E 隔离栈会显式覆盖 ADMIN_ACCESS_COOKIE_SECURE=false,因为它默认跑在 http://127.0.0.1,否则 APIRequestContext 不会回传 Secure 管理员 Cookie。
  • Docker bootstrap 模式下,如果额外设置了 PLAYWRIGHT_ADMIN_IDENTIFIER,它必须等于同一次 bootstrap 使用的 PLAYWRIGHT_ADMIN_USERNAMEPLAYWRIGHT_ADMIN_EMAIL;否则会在启动前直接失败,避免出现“栈能起但登录永远 401”。
  • 如果你不是让 Playwright 启动隔离栈,而是通过 PLAYWRIGHT_SKIP_STACK_BOOT=1 连接一个已经存在的环境,则默认会改连本地 dev 栈的 8000/5174;如果现有环境不在这些地址,继续显式设置 PLAYWRIGHT_BASE_URLPLAYWRIGHT_AI_SERVICE_HEALTH_URL,必要时再单独覆盖 PLAYWRIGHT_API_BASE_URL

推荐配置分两种。

连接现有环境:

export PLAYWRIGHT_SKIP_STACK_BOOT="1"
export PLAYWRIGHT_ADMIN_IDENTIFIER="admin"
export PLAYWRIGHT_ADMIN_PASSWORD="ChangeMeAdmin123!"

# 如果现有环境不在默认 dev 端口,再显式覆盖:
# export PLAYWRIGHT_BASE_URL="https://admin.example.test"
# export PLAYWRIGHT_AI_SERVICE_HEALTH_URL="https://api.example.test/healthz"

使用默认 Docker 隔离栈:

export PLAYWRIGHT_ADMIN_EMAIL="admin@example.com"
export PLAYWRIGHT_ADMIN_USERNAME="admin"
export PLAYWRIGHT_ADMIN_PASSWORD="ChangeMeAdmin123!"

# 可选:如需设置 identifier,必须等于上面的 username 或 email
# export PLAYWRIGHT_ADMIN_IDENTIFIER="admin"

测试数据策略

  • 所有造数与回收逻辑都在 support/api-client.ts 中,通过 Playwright APIRequestContext 实现。
  • evaluation 用例通过 API 创建 draft dataset,再把 tests/fixtures/shared/evaluation_csv/evaluation-run-test.csv 转换成导入器接受的 input/expected CSV,并在 UI 中执行 import + freeze。
  • knowledge source 用例会复用一个确定名称的测试 source,并通过 UI 上传 tests/fixtures/shared/embedding_test_files/ 中的真实文档夹具;文档在测试结束后会清理,但由于后端暂时没有 source 删除接口,连接现有环境时该测试 source 会被复用而不是删除。
  • MCP 用例通过 API 预置一个 stdio server,再在 UI 中执行 health check 和 tools/list
  • document recognition 用例通过 API 创建 document_extraction agent,并上传一个小图片文件生成队列记录。
  • cleanup 由 CleanupRegistry 统一在每个测试后反向执行。

选择器与等待约定

  • 优先顺序:getByTestId > getByRole > getByLabel
  • 新增或修改关键交互元素时,优先补 data-testid
  • 禁止硬编码 sleep / waitForTimeout
  • 所有异步状态等待使用 Playwright web-first 机制,必要时使用 expect.poll

运行前准备

uv sync --all-groups
just e2e-install

默认 Docker 模式下,Playwright 会调用:

  • just _e2e-stack-up
  • just _e2e-stack-down

该隔离栈会把 PLAYWRIGHT_ADMIN_EMAIL/USERNAME/PASSWORD 映射到 ADMIN_ACCESS_BOOTSTRAP_*,用于启动时创建首个管理员;如果三者缺任何一项,或 PLAYWRIGHT_ADMIN_IDENTIFIER 与本次 bootstrap 的 username/email 不一致,just _e2e-stack-up 会直接失败,而不是启动一个无法登录的空栈。

常用命令

cd tests/e2e

# 运行业务流 E2E(自动跳过 @visual)
npm test

# 只跑 smoke
npm run test:smoke

# 跑全量套件(先业务流,再视觉)
npm run test:all

# 本地有头调试(串行执行,降低 headed 并发截图不稳定)
npm run test:headed

# 只跑视觉回归
npm run test:visual

# 更新视觉基线
npm run test:update

# E2E 代码静态检查
npm run lint
npm run typecheck

仓库级辅助命令仍保留:

just e2e-install
just e2e
just e2e-review
just e2e-update-snapshots

其中:

  • just e2e 继续保留为视觉回归快捷入口。
  • just e2e-review 的视觉 AI review 流程仍然可用,新业务流框架不会替换它。
  • npm run test:all 会通过 tests/e2e/scripts/run-all.mjs 先执行业务流套件,再单独执行视觉套件,并分别保留:
  • playwright-report/workflowstest-results/workflows
  • playwright-report/visualtest-results/visual
  • npm run test:headed 会强制 --workers=1。有头模式在当前 WSL/X 显示环境下如果并发拉起多个 Chromium,会偶发触发 Page.captureScreenshot 协议错误,串行执行更稳定。

CI 行为

GitHub Actions CI 工作流新增 e2e job,默认会:

  1. 使用 actions/setup-node 缓存 tests/e2e/package-lock.json
  2. 在仓库根目录把受版本控制的 .env.example 复制为 .env,满足 docker-compose.ymlai-service env_file: .env 的 clean-runner 运行前提
  3. 在 job 内生成一组仅用于隔离 Docker 栈的临时管理员凭据,不依赖仓库 secrets,因此 fork PR 也不会因为缺少 secrets 而直接失败
  4. 执行 npm ci
  5. 执行 npm run lintnpm run typecheck
  6. 安装 Chromium
  7. 运行 npm test
  8. 上传以下产物:
  9. tests/e2e/playwright-report/
  10. tests/e2e/test-results/
  11. tests/e2e/test-results/junit.xml

这里复制的是仓库里已经跟踪的默认模板,不是把真实开发者 .env 带进 CI;隔离栈真正使用的管理员 bootstrap 凭据仍由 job 级环境变量覆盖。

playwright.config.ts 在 CI 中启用有限次数重试,并保留失败时的 trace、video、screenshot。

视觉回归

tests/e2e/tests/admin-document-recognition.spec.ts 继续承担:

  • @review 截图产物导出
  • @visual 视觉回归断言

为保持视觉基线稳定,@visual 套件会单独运行,并把截图范围限制在 document recognition 页面的 shell 顶部区域,而不是包含动态队列表格和详情面板的整页快照。

本地排障建议

  • 登录 setup 失败时,先区分自己是“连接现有环境”还是“让 Playwright 启 Docker 栈”;两种模式的必填环境变量不同。
  • Docker 模式下如果在 just _e2e-stack-up 阶段就失败,优先补齐 PLAYWRIGHT_ADMIN_EMAIL/USERNAME/PASSWORD 三元组,或改用 PLAYWRIGHT_SKIP_STACK_BOOT=1 连接现有环境。
  • Docker 模式下如果仍然 401,再看 ai-service 启动日志,确认 bootstrap 管理员已创建。
  • dataset CSV 导入失败时,确认文件编码为 UTF-8,且至少包含 input,expected 两列。
  • 清理失败通常意味着测试在中途生成了额外引用;先看 test-results/ 中的 trace 和 junit.xml