资讯动态

Vendure Dashboard E2E 测试架构实战:Playwright 双服务器基础设施与 Dashboard 扩展驱动的测试页面

发布时间:2026/9/16 14:24:44 来源:尧图企业网站定制
Vendure Dashboard E2E 测试架构实战Playwright 双服务器基础设施与 Dashboard 扩展驱动的测试页面【免费下载链接】vendureOpen-source headless commerce platform built with TypeScript, NestJS, React, and GraphQL项目地址: https://gitcode.com/GitHub_Trending/ve/vendureVendure 是一个基于 TypeScript、NestJS、React 与 GraphQL 构建的开源 headless 电商平台其管理后台Dashboard的端到端测试E2E是一套独立的 Playwright 测试套件。本文以 packages/dashboard/e2e/README.md 为核心结合仓库内的真实配置与源码系统讲解该套件的运行方式、双服务器架构、配置边界、测试目录组织以及如何通过 Dashboard 扩展机制为 E2E 测试添加自定义测试页面。读完本文你将掌握如何在本仓库中运行、理解并扩展这套 E2E 测试基础设施。套件概览Playwright 双服务器Dashboard E2E 套件位于 packages/dashboard/e2e/是一套基于Playwright的浏览器级端到端测试覆盖登录认证、商品目录、客户、营销、订单、系统设置、作业队列等管理后台核心功能。整套测试基础设施由两个相互独立的服务器协同工作Vendure 后端—— 在global-setup.ts中通过vendure/testing的createTestEnvironment启动监听 constants.ts 中定义的端口当前为3050。后端会用种子数据商品、客户等初始化并配置自定义字段与测试专用插件。Vite 开发服务器—— 由 Playwright 配置中的webServer选项启动见 playwright.config.ts负责提供 Dashboard 前端页面这也是 Playwright 测试实际导航访问的服务器。测试的完整生命周期为globalSetup启动 Vendure 后端 → PlaywrightwebServer构建并预览 Vite 前端 → 测试执行 →globalTeardown销毁后端。销毁逻辑在 global-teardown.ts 中实现它读取globalSetup挂到globalThis.__VENDURE_SERVER__上的服务实例并调用server.destroy()。运行测试在packages/dashboard/目录下执行以下命令即可运行整个套件CItrue VITE_TEST_PORT5176 npx playwright test --config e2e/playwright.config.ts --reporterlist运行单个测试文件CItrue VITE_TEST_PORT5176 npx playwright test --config e2e/playwright.config.ts e2e/tests/components/form-inputs.spec.ts --reporterlist几个关键环境变量与配置项见 playwright.config.tsVITE_TEST_PORTVite 预览服务器的端口未设置时默认回退到5174。测试的baseURL为http://localhost:${VITE_PORT}。CICI 环境下启用forbidOnly、将retries设为 1、workers限制为 4并使用githublist两种 reporter本地则默认使用 HTML reporterretries为 0、不限制并行 workerfullyParallel: true。VENDURE_CONFIG_PATH指向fixtures/e2e-vendure-config.ts供 Vite 插件发现 Dashboard 扩展。VITE_ADMIN_API_PORT/VITE_ADMIN_API_HOST由webServer注入告知前端后端的地址与端口。webServer的实际命令是npx vite build npx vite preview --port ${VITE_PORT}而非开发模式的vite dev超时时间为 120 秒本地运行时若端口上已有服务器reuseExistingServer: !process.env.CI会复用。双服务器如何共享一套配置两张配置表的分工两套服务器使用相互独立的配置这是理解本套件最关键的一点服务器配置用途Vendure 后端global-setup.ts导入 fixtures/e2e-shared-config.ts以测试数据库、CORS、自定义字段及仅服务器端插件启动 Vendure 服务Vite 开发服务器fixtures/e2e-vendure-config.ts通过VENDURE_CONFIG_PATH注入告诉 Vite 插件需要加载哪些 Dashboard 扩展从源码看后端的完整配置在 global-setup.ts 中通过mergeConfig(defaultTestConfig, {...})构建关键点包括apiOptions.port设为VENDURE_PORT3050paymentOptions.paymentMethodHandlers使用e2e-shared-config.ts导出的e2ePaymentMethodHandlers即dummyPaymentHandlerassetOptions.assetStorageStrategy使用自定义的E2eAssetStorageStrategy——该策略将资源 URL 生成为可解析的绝对地址http://test-asset.local/${identifier}避免默认测试策略产生的非法 URL 导致VendureImage组件抛出异常importExportOptions.importAssetsDir指向packages/core/e2e/fixtures/assets让种子商品如 Laptop直接拥有真实的主图资源相关的测试无需运行时再上传customFields使用e2eCustomFieldscatalogOptions.collectionFilters显式合并defaultCollectionFilters与e2eCollectionFiltersmergeConfig会替换数组必须手动保留默认值CORS 需手动设置为{ origin: true, credentials: true }——因为 Dashboard 的 fetch 使用credentials: include要求服务器回显请求来源而非通配符*并开启凭证mergeConfig无法用布尔值覆盖对象所以此处显式赋值global-setup.ts。而 fixtures/e2e-vendure-config.ts 中的配置并不会真正启动 Vendure 服务器——它的唯一职责是让 Vite 插件的配置内省机制编译出VendurePlugin装饰器中带dashboard入口点的插件如FormInputsTestPlugin、AlertTestPlugin从而在开发服务器启动时动态导入对应扩展。其中dbConnectionOptions、authOptions只是满足VendureConfig类型约束的占位符。重要边界自定义字段只属于后端配置自定义字段只能放在global-setup.ts经由e2e-shared-config.ts中绝不能放进e2e-vendure-config.ts。原因在于Vite 插件会根据其配置生成 Dashboard 的 GraphQL schema若在其中加入 struct 类型自定义字段会导致商品创建 mutation 失败表单会发送空 struct 数据被后端拒绝。而 Dashboard 本身会在运行时从后端 API 动态发现自定义字段因此无需也不应该在 Vite 侧重复声明。这一点在 e2e-shared-config.ts 的文件头注释中同样有明确说明。e2e-shared-config.ts之所以独立成文件还因为它只包含纯数据自定义字段、支付处理器、集合过滤器不含 NestJS 插件或装饰器因此不需要经过 SWC 编译可以直接被global-setup.ts静态导入。其中定义的e2eCustomFields覆盖了 string、float、int、boolean、datetime、text、localeString、localeText、list、struct 等几乎所有字段类型并按 General / SEO / Details / Lists / Struct 多个 tab 组织用于全面验证 Dashboard 的自定义字段渲染能力e2eCollectionFilters则复现了 issue #4987字符串列表参数在配置化操作输入框中丢失数字形式条目的场景。测试目录组织packages/dashboard/e2e/ ├── fixtures/ # 测试数据与插件 │ ├── e2e-shared-config.ts # 自定义字段与支付处理器仅后端 │ ├── e2e-vendure-config.ts # 供 Vite 插件发现扩展的 Vendure 配置 │ ├── form-inputs-test-plugin.ts # 示例仅用于 E2E 的 Dashboard 插件 │ ├── form-inputs-test-dashboard/ # 上述插件对应的 Dashboard 扩展 │ │ ├── index.tsx # 入口defineDashboardExtension │ │ └── form-inputs-test-page.tsx │ ├── alert-test-plugin.ts # 另一个仅用于 E2E 的插件多插件告警回归 #4729 │ ├── alert-test-dashboard/ │ │ └── index.tsx │ ├── custom-history-entry-plugin.ts │ └── initial-data.ts ├── page-objects/ # Page Object 模型login-page / list-page / detail-page ├── utils/ # 测试工具crud-test-factory、vendure-admin-client ├── tests/ │ ├── auth/ # 登录与认证含 auth.setup.ts 登录态预置 │ ├── catalog/ # 商品、集合、facet、资源 │ ├── components/ # 共享 UI 组件行为 │ ├── customers/ # 客户与客户分组 │ ├── marketing/ # 促销 │ ├── sales/ # 订单与订单修改 │ ├── settings/ # 渠道、角色、支付方式等 │ ├── system/ # 作业队列、健康检查、定时任务 │ ├── dashboard/ # Dashboard 首页与洞察 │ └── regression/ # 历史 issue 回归测试如 #4729、#4730 等 ├── global-setup.ts ├── global-teardown.ts ├── playwright.config.ts └── constants.ts目录布局与 README 描述基本一致仓库中还额外存在page-objects/login-page.ts、list-page.base.ts、detail-page.base.ts与utils/crud-test-factory.ts、vendure-admin-client.ts以及tests/dashboard/与tests/regression/两个分类。Playwright 的认证流程值得单独说明配置中定义了setup与chromium两个 projectplaywright.config.tssetup运行auth.setup.ts完成登录并把登录态写入.auth/admin.jsonchromiumproject 通过storageState复用该登录态并以dependencies: [setup]保证顺序避免每个测试重复执行登录。通过 Dashboard 扩展添加测试页面当 E2E 测试需要一个自定义页面例如在隔离环境中测试表单组件时应当使用Dashboard 扩展机制而不是直接把文件放进src/app/routes/目录。这样做既能把测试代码隔离在生产源码树之外又能顺带验证真实插件所使用的扩展基础设施。下面按 README 的步骤逐步展开并给出仓库中的真实实现作为对照。第 1 步创建 VendurePlugin在e2e/fixtures/中创建一个极简插件声明dashboard入口点// e2e/fixtures/my-test-plugin.ts import { VendurePlugin } from vendure/core; VendurePlugin({ dashboard: ./my-test-dashboard/index.tsx, }) export class MyTestPlugin {}仓库中的真实示例是 fixtures/form-inputs-test-plugin.tsFormInputsTestPlugin通过VendurePlugin({ dashboard: ./form-inputs-test-dashboard/index.tsx })声明扩展入口。文件头注释说明该扩展由 Vite 插件的配置内省发现并在开发服务器启动时被动态导入。第 2 步创建 Dashboard 扩展入口入口文件调用defineDashboardExtension注册路由可选地注册导航项、widget、表单组件等// e2e/fixtures/my-test-dashboard/index.tsx import { defineDashboardExtension } from vendure/dashboard; import { MyTestPage } from ./my-test-page; defineDashboardExtension({ routes: [ { path: /my-test-page, component: () MyTestPage /, }, ], });真实实现见 fixtures/form-inputs-test-dashboard/index.tsx它注册了/form-inputs-test路由fixtures/alert-test-dashboard/index.tsx 则通过另一个独立的defineDashboardExtension调用注册告警——两个插件各自贡献扩展的组合正是 issue #4729多插件场景下告警不轮询的触发条件对应回归测试见 tests/regression/issue-4729-alerts-do-not-poll-multi-plugin.spec.ts。第 3 步编写页面组件页面是普通的 React 组件无需使用 TanStack Router 的基于文件的动态路由。UI 组件一律从vendure/dashboard导入不要用内部的/vdb/路径// e2e/fixtures/my-test-dashboard/my-test-page.tsx import { Page, PageLayout, PageTitle, FullWidthPageBlock } from vendure/dashboard; export function MyTestPage() { return ( Page pageIdmy-test-page PageTitleMy Test Page/PageTitle PageLayout FullWidthPageBlock blockIdmy-test-page {/* test content */} /FullWidthPageBlock /PageLayout /Page ); }真实示例 fixtures/form-inputs-test-dashboard/form-inputs-test-page.tsx 展示了更完整的形态它用useFormreact-hook-form为每种输入类型string、int、boolean、datetime、带 options 的 string构造ConfigurableFieldDef通过FormControlAdapter渲染输入控件并用一个按钮切换Controller的disabled状态专门用于验证 issue #4424 中内置表单控件对 disabled 状态的处理Base UI 的 Switch、Select、Popover 使用 portal 与自定义事件处理器绕过了 HTML 原生fieldset disabled机制。第 4 步在 E2E 的 Vendure 配置中注册插件把插件加入e2e/fixtures/e2e-vendure-config.tsimport { MyTestPlugin } from ./my-test-plugin; export const config: VendureConfig { // ... plugins: [FormInputsTestPlugin, MyTestPlugin], };Playwright 配置已经通过VENDURE_CONFIG_PATH指向该文件因此 Vite 插件会自动发现新增的 Dashboard 扩展无需额外接线。真实文件 fixtures/e2e-vendure-config.ts 中注册的是[FormInputsTestPlugin, AlertTestPlugin]。第 5 步针对页面编写测试test(should do something on my test page, async ({ page }) { await page.goto(/my-test-page); // ... });仓库中的完整测试示例见 tests/components/form-inputs.spec.ts它覆盖了多种断言模式可直接作为模板用page.goto(/form-inputs-test)进入测试页并断言页面标题可见用[data-slotfield]定位器配合[data-slotfield-label]精确筛选指定字段filter({ has: ... })按控件语义角色断言文本框getByRole(textbox)、数字输入getByRole(spinbutton)、开关getByRole(switch)、日期触发按钮getByRole(button)、下拉getByRole(combobox)验证 disabled 行为点击toggle-disabled后断言控件toBeDisabled()并验证强制点击不会打开 popover / listbox、开关状态不变最后验证关闭 disabled 后所有控件恢复可交互确保状态切换是双向的。这套用例既覆盖了原生input/textarea原生 disabled 机制生效也覆盖了基于 Base UI 的复合控件必须由组件自身透传 disabled体现了该测试页存在的意义。为什么不用文件拷贝方式添加测试页面README 明确列出了把测试文件直接复制进src/app/routes/的三点弊端测试代码会进入生产源码树存在被随包发布的风险TanStack Router 的 Vite 插件会为测试页面生成路由条目污染routeTree.gen.ts文件拷贝方案需要在globalTeardown中清理一旦测试运行被中断就会留下脏文件非常脆弱。而通过扩展机制添加测试页面可以完全规避这些问题同时还能顺带验证扩展路由基础设施本身的正确性——即测试基础设施与产品基础设施互相印证。常见陷阱与建议综合 README 与源码实现运行或扩展本套件时有几点值得注意自定义字段的放置边界struct 类型自定义字段若出现在 Vite 侧配置中会导致商品创建 mutation 失败自定义字段一律放在e2e-shared-config.ts经global-setup.ts生效因为 Dashboard 在运行时从后端 API 发现字段。含装饰器的插件需要 SWC 编译global-setup.ts 中的importWithSwc函数专门用swc/core将 fixture 编译为 ES module开启decorators与decoratorMetadata因为 Playwright 内置的 esbuild/Babel 转译器不支持 NestJS 的emitDecoratorMetadata。CustomHistoryEntryPlugin正是因此采用动态加载而非静态导入。mergeConfig的数组与对象语义它会替换数组所以collectionFilters要显式保留默认值也无法用布尔值覆盖对象所以 CORS 要显式赋对象。端口约定后端固定为VENDURE_PORT 3050constants.ts前端端口由VITE_TEST_PORT决定默认5174。测试数据来源种子数据来自 fixtures/initial-data.ts商品 CSV 复用packages/core/e2e/fixtures/e2e-products-full.csv资源文件复用packages/core/e2e/fixtures/assets避免在套件内重复维护一套数据。结语Vendure Dashboard 的 E2E 套件展示了一套值得借鉴的双服务器测试架构用vendure/testing启动真实后端、用 PlaywrightwebServer承载 Vite 构建的前端二者通过VENDURE_CONFIG_PATH与环境变量衔接自定义字段等数据型配置与插件扩展等代码型配置严格分层避免 schema 生成与运行时行为互相干扰。而用扩展机制写测试页面这一实践更是把 Dashboard 的插件能力本身纳入了测试覆盖面——测试页面既是测试工具也是扩展基础设施的验证用例。对于任何基于 Vendure 做二次开发的团队这套套件既是质量保障也是学习 Dashboard 扩展机制的绝佳范本。【免费下载链接】vendureOpen-source headless commerce platform built with TypeScript, NestJS, React, and GraphQL项目地址: https://gitcode.com/GitHub_Trending/ve/vendure创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价