ZITADEL 功能型 UI 端到端测试实战:用 Nx + Cypress 驱动 Management Console 用户旅程验证
2026/9/14 8:47:02 网站建设 项目流程

ZITADEL 功能型 UI 端到端测试实战:用 Nx + Cypress 驱动 Management Console 用户旅程验证

【免费下载链接】zitadelZITADEL - Identity infrastructure, simplified for you.项目地址: https://gitcode.com/GitHub_Trending/zi/zitadel

本篇基于 ZITADEL 仓库中 tests/functional-ui/AGENTS.md 的官方 Agent 指南展开,介绍如何在这个 Nx monorepo 中运行针对 Management Console(管理控制台)的 Cypress 端到端测试。读完后你能掌握:五个已验证的 Nx 目标(打开交互式 Runner、跑测试套件、单独起测试库、单独起测试 API、停止测试基建)的完整用法,以及底层编排细节——临时 Postgres、API 构建启动链路、系统令牌签发机制和测试辅助层的实现原理,从而能够在本地可靠地复现与调试 Console 用户旅程测试。

一、functional-ui 测试的定位

tests/functional-ui 目录承载的是基于 Cypress 的端到端测试,聚焦于 Management Console 的用户流程(user journeys),其运行前提是一个“正在运行的 ZITADEL API”——即测试不是对着静态页面跑,而是对着一个完整的 Zitadel 后端(内嵌 Console 静态资源)进行真实交互验证。

指南中特别强调了两条工作流约束(Workflow Notes):

  1. 它是 Console 的主要测试路径:由于@zitadel/console项目本身没有定义 Nx 的test目标,functional-ui 套件就是验证 Console 用户旅程的第一(也是目前唯一的)E2E 测试通道;
  2. 强依赖 API 的构建/运行编排:这些测试依赖 API 的 build/run 编排,在没有同步更新本套件的前提下,应避免改动 API 的启动假设(startup assumptions)。

这一点从 tests/functional-ui/project.json 的依赖声明可以得到印证:run-api目标声明了dependsOn: ["@zitadel/api:build"]test目标则同时依赖run-dbrun-api@zitadel/api:build,且implicitDependencies指向了@zitadel/api@zitadel/console两个项目。

二、已验证的 Nx 目标速查

指南中列出了五个已经过验证(Verified)的 Nx 目标,这是复现整个测试环境的核心操作面:

目标命令作用
打开交互式 Cypress Runnerpnpm nx run @zitadel/functional-ui:open启动 Cypress Test Runner 交互界面,便于逐个调试用例
运行测试套件pnpm nx run @zitadel/functional-ui:test完整跑一次 functional UI(Console)测试
仅启动测试数据库pnpm nx run @zitadel/functional-ui:run-db启动一个本地临时(ephemeral)Postgres
仅启动测试 APIpnpm nx run @zitadel/functional-ui:run-api构建并运行内嵌 Management Console 的 Zitadel API
停止测试基建pnpm nx run @zitadel/functional-ui:stop停掉测试用的本地 Postgres

test目标支持通过参数指定浏览器,例如--browser electron;指南指出electron浏览器应当始终可用(Cypress 内置)。

编排细节:各目标到底做了什么

结合 tests/functional-ui/project.json 的源码,可以看清每个目标背后的实际动作:

  • run-db:执行nx run @zitadel/devcontainer:compose up --force-recreate --renew-anon-volumes db-functional-ui,即借助 devcontainer 项目的 compose 编排,强制重建并刷新匿名卷,拉起一个仅服务于 functional-ui 的临时 Postgres 容器。

  • run-api:先依赖@zitadel/api:build编译 Go 后端二进制,再执行nx run @zitadel/api:prod:test-functional-ui --excludeTaskDependencies。标记为continuous: true,作为长驻进程运行。

  • open:声明依赖@zitadel/console:devrun-dbrun-api,即同时拉起 Console 开发服务、数据库与 API 后再打开交互式 Cypress Runner。

  • test:以nx:run-commands顺序执行三条命令:

    1. cypress install——确保 Cypress 二进制就位;
    2. wait-on --verbose --interval 2000 --simultaneous 1 --timeout 30m "${CYPRESS_BACKEND_URL}/debug/ready"——每 2 秒轮询一次 API 的 ready 端点,最长等待 30 分钟,确保后端完全就绪后再跑浏览器;
    3. DISPLAY='' cypress run——以无显示环境(CI 风格)执行测试。

    该目标开启cache: true,其缓存输入除了默认项外,还显式纳入了 API 构建产物.artifacts/bin/*/*/zitadel.local——即 API 二进制一变,测试缓存即失效重跑。

  • stop:执行nx run @zitadel/devcontainer:compose down --volumes db-functional-ui,带卷清理地拆除临时 Postgres。

API 是如何被拉起来的

apps/api/project.json 显示prod目标通过nx:run-commands启动二进制:

./.artifacts/bin/$(go env GOOS)/$(go env GOARCH)/${ZITADEL_BINARY:-zitadel.local} \ start-from-init --config ${API_CONFIG_FILE} --steps ${API_CONFIG_FILE} \ --masterkey MasterkeyNeedsToHave32Characters

其中test-functional-ui配置(configuration)把API_CONFIG_FILE指向 apps/api/test-functional-ui.yaml。也就是说,functional-ui 测试用的是start-from-init子命令加一份专用 YAML——“init + start”一步完成,数据库从这份配置里现场初始化。

三、测试环境配置:test-functional-ui.yaml 解读

apps/api/test-functional-ui.yaml 是为 UI 端到端测试专门准备的 API 配置,关键项如下:

  • 数据库Database.postgres指向库名zitadel,应用账号zitadel/zitadel、管理账号postgres/postgres,SSL 均设为disable,并配置AwaitInitialConn: 5mMaxOpenConns: 15MaxIdleConns: 10。注释说明该配置可通过/etc/hosts条目复用到 Docker 之外的 Zitadel 进程,提升了环境复用性。
  • TLSEnabled: false,测试环境走明文 HTTP,与默认baseUrlhttp://前缀对应。
  • QuotasAccess.Enabled: trueDebounce.MinFrequency: 0sMaxBulkSize: 0——配额功能在测试中是开启且即时生效的。这解释了测试辅助层为何需要显式清理配额(见下文context())。
  • SystemAPIUsers:声明了一个名为cypress的系统 API 用户,KeyData是一段 Base64 编码的 RSA 公钥(-----BEGIN PUBLIC KEY-----...)。这正是 Cypress 侧签发系统令牌的验签公钥,两端由此配对(见下一节)。
  • DefaultInstanceMfaInitSkipLifetime: "0"Features.LoginV2.Required: false——为测试流程移除 MFA 等强制项的干扰。

四、Cypress 配置:baseUrl、系统令牌与 Webhook 桩

tests/functional-ui/cypress.config.ts 是整个测试基建的枢纽,值得逐项理解:

环境变量与默认值

变量默认值说明
CYPRESS_BASE_URLhttp://localhost:8083/ui/consoleConsole 页面入口
CYPRESS_BACKEND_URL由 baseUrl 去掉/ui/console后缀推导API 后端地址
CYPRESS_WEBHOOK_HANDLER_PORT8900内置 Webhook 接收桩端口
CYPRESS_WEBHOOK_HANDLER_HOSTlocalhostWebhook 桩监听主机
CYPRESS_ORGANIZATIONzitadel测试组织标识

其他关键参数:defaultCommandTimeout: 10000pageLoadTimeout: 180000(页面加载最长 3 分钟,适配冷启动)、video: true、测试报告输出到cypress/results(HTML + JSON)、trashAssetsBeforeRuns: false(保留历史产物便于回溯)。

系统令牌(system token)的签发链路

这是理解本套件鉴权模型的关键。setupNodeEvents中注册了若干cy.task

  • systemToken():使用配置中内置的 RSA 私钥,以RS256算法现场签发一个 JWT,claims 为iss=cypresssub=cypressaud=${CYPRESS_BACKEND_URL}exp设为约 999 年后。由于 test-functional-ui.yaml 中SystemAPIUsers声明了与私钥配对的cypress公钥,该令牌即被 Zitadel 识别为合法的系统 API 令牌,可访问/system/v1
  • safetoken({key, token})/loadtoken({key}):一个跨 spec 的令牌内存缓存(Map),避免同一会话重复登录。
  • Webhook 事件桩startWebhookEventHandler()在 Node 侧启动一个 HTTP 服务(默认 8900 端口),把收到的每个 Webhook 请求的 payload 与响应状态码记录到webhookEvents数组;resetWebhookEvents()handledWebhookEvents()failWebhookEvents(count)三个任务允许测试用例读取事件、并让前 N 个请求故意返回 500——用于验证通知/Webhook 投递与失败重试行为。

测试辅助层(support)

support/api/apiauth.ts 封装了两条鉴权路径:

  • apiAuth():以 IAM Admin 用户密码登录(Password1!),返回一组版本化 API 基地址(/management/v1/admin/v1/auth/v1/oidc/v1/saml/v2/v2/features等),供 UI 之外的直接 API 断言使用;
  • systemAuth():通过cy.task('systemToken')获取系统令牌,封装/system/v1调用。

custom 命令层 进一步提供了cy.context():依次完成systemAuth()instanceUnderTest()→ 清除AuthenticatedRequestsExecutionSeconds两类配额(呼应 YAML 中 Quotas 的开启)→apiAuth(),最终返回{ system, api, instanceId }三元组,让每个 spec 在统一的“干净上下文”中执行。注意 support/api/instances.ts 中instanceUnderTest会断言“API 上恰好只有一个 instance”——这与start-from-init全新初始化的环境假设严格匹配,也是“不要改动 API 启动假设”这条工作流约束的具体落点。

此外还有shouldNotExist()(等待某选择器元素数归零,用于验证删除后的 UI 状态)、shouldConfirmSuccess()(断言.data-e2e-success出现且.data-e2e-failure不存在)等通用断言命令。cypress/support/api/下还按领域拆分了orgs.tsprojects.tsmembers.tsgrants.tspolicies.tsoidc-settings.tssmtp.tssms.tssearch.ts等 helper,覆盖组织、项目、成员、授权、策略与外部服务的预置操作。

五、测试覆盖面

从 tests/functional-ui/cypress/e2e 目录结构看,spec 按 Console 功能域组织:

  • organization/organizations.cy.tsprojects/projects.cy.tspermissions/permissions.cy.ts——组织、项目与权限管理;
  • humans/humans.cy.tsmachines/machines.cy.ts——人类用户与机器用户(Service Account 一类身份)管理;
  • applications/applications.cy.ts——应用管理;
  • instance/settings/notifications.cy.tssecret-generator.cy.ts)与settings/login-policy.cy.tspassword-complexity.cy.tsoidc-settings.cy.tsfeatures.cy.tsprivate-labeling.cy.tsexternal-links-settings.cy.ts)——实例级设置;
  • events/events.cy.tsi18n/api.cy.ts——事件审计与国际化。

结合 tests/functional-ui/package.json,套件依赖cypress ^15.14.2cypress-wait-until(自定义等待)、jsonwebtoken(配合 Node 侧签 token)与uuid(生成测试数据标识)。

六、实操步骤与注意事项

一次完整的功能型 UI 测试流程可以归纳为:

  1. 在仓库根目录确认 pnpm 依赖已安装(工作区根package.json定义了 Nx workspace);
  2. pnpm nx run @zitadel/functional-ui:test(或交互调试时...:open)——该命令会自动拉起临时 Postgres、构建并启动内嵌 Console 的 Zitadel API、等待${CYPRESS_BACKEND_URL}/debug/ready就绪后再跑 Cypress;
  3. 测试完成后执行pnpm nx run @zitadel/functional-ui:stop清理临时数据库。

需要注意的适用前提与限制:

  • 测试环境默认 HTTP(TLS 关闭)、localhost 地址与固定端口(Console 8083、Webhook 桩 8900),如自定义端口需通过CYPRESS_BASE_URLCYPRESS_WEBHOOK_HANDLER_PORT等环境变量传入;
  • instanceUnderTest要求“API 上恰好一个实例”,依赖start-from-init的全新初始化语义;若更换启动方式,必须同步更新本套件(AGENTS.md 的明确警告);
  • 浏览器选择通过test目标参数传入(如--browser electron),electron始终可用;
  • 由于 Console 项目自身无 Nxtest目标,任何针对 Console 用户旅程的回归验证都应走本套件。

小结

tests/functional-ui是 ZITADEL 仓库中验证 Management Console 用户旅程的主通道:以 AGENTS.md 的五个已验证 Nx 目标为操作入口,以 project.json 的依赖编排、test-functional-ui.yaml 的全新初始化配置、cypress.config.ts 的系统令牌与 Webhook 桩机制为底层支撑,构成了一条从临时数据库到浏览器断言的完整、可复现的端到端测试链路。理解这条链路,也就掌握了在本地复现、调试和扩展 ZITADEL Console 功能测试所需的全部关键事实。

【免费下载链接】zitadelZITADEL - Identity infrastructure, simplified for you.项目地址: https://gitcode.com/GitHub_Trending/zi/zitadel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询