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 也跟随模式变化:
docker:http://127.0.0.1:28000/healthz和http://127.0.0.1:28081dev:http://127.0.0.1:8000/healthz和http://127.0.0.1:5174- API seed client 默认直连
ai-service: docker:http://127.0.0.1:28000dev:http://127.0.0.1:8000- 如果 API 不在这些地址,显式设置
PLAYWRIGHT_API_BASE_URL - 连接已存在的管理员账号时,登录标识读取顺序为:
PLAYWRIGHT_ADMIN_IDENTIFIERPLAYWRIGHT_ADMIN_USERNAMEPLAYWRIGHT_ADMIN_EMAILADMIN_ACCESS_BOOTSTRAP_USERNAMEADMIN_ACCESS_BOOTSTRAP_EMAIL- 密码读取顺序为:
PLAYWRIGHT_ADMIN_PASSWORDADMIN_ACCESS_BOOTSTRAP_PASSWORD- 默认 Docker 模式会先执行
just _e2e-stack-up并重建一套全新的隔离栈,因此必须同时提供 bootstrap 三元组: PLAYWRIGHT_ADMIN_EMAILPLAYWRIGHT_ADMIN_USERNAMEPLAYWRIGHT_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_USERNAME或PLAYWRIGHT_ADMIN_EMAIL;否则会在启动前直接失败,避免出现“栈能起但登录永远 401”。 - 如果你不是让 Playwright 启动隔离栈,而是通过
PLAYWRIGHT_SKIP_STACK_BOOT=1连接一个已经存在的环境,则默认会改连本地 dev 栈的8000/5174;如果现有环境不在这些地址,继续显式设置PLAYWRIGHT_BASE_URL和PLAYWRIGHT_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中,通过 PlaywrightAPIRequestContext实现。 - evaluation 用例通过 API 创建 draft dataset,再把
tests/fixtures/shared/evaluation_csv/evaluation-run-test.csv转换成导入器接受的input/expectedCSV,并在 UI 中执行 import + freeze。 - knowledge source 用例会复用一个确定名称的测试 source,并通过 UI 上传
tests/fixtures/shared/embedding_test_files/中的真实文档夹具;文档在测试结束后会清理,但由于后端暂时没有 source 删除接口,连接现有环境时该测试 source 会被复用而不是删除。 - MCP 用例通过 API 预置一个
stdioserver,再在 UI 中执行 health check 和tools/list。 - document recognition 用例通过 API 创建
document_extractionagent,并上传一个小图片文件生成队列记录。 - 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-upjust _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/workflows、test-results/workflowsplaywright-report/visual、test-results/visualnpm run test:headed会强制--workers=1。有头模式在当前 WSL/X 显示环境下如果并发拉起多个 Chromium,会偶发触发Page.captureScreenshot协议错误,串行执行更稳定。
CI 行为
GitHub Actions CI 工作流新增 e2e job,默认会:
- 使用
actions/setup-node缓存tests/e2e/package-lock.json - 在仓库根目录把受版本控制的
.env.example复制为.env,满足docker-compose.yml中ai-service env_file: .env的 clean-runner 运行前提 - 在 job 内生成一组仅用于隔离 Docker 栈的临时管理员凭据,不依赖仓库 secrets,因此 fork PR 也不会因为缺少 secrets 而直接失败
- 执行
npm ci - 执行
npm run lint与npm run typecheck - 安装 Chromium
- 运行
npm test - 上传以下产物:
tests/e2e/playwright-report/tests/e2e/test-results/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。