CopilotKit 示例 E2E 测试体系实战指南:基于 Playwright 的轻量冒烟测试架构
【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit
导读
本文围绕 CopilotKit 仓库中 examples/e2e/AGENTS.md 展开,系统讲解仓库为examples/目录下所有示例应用搭建的端到端(E2E)冒烟测试体系:包括测试框架的选取逻辑、Next.js 纯前端示例与"UI + Agent"混合示例的区别化启动方式、本地运行命令、编写测试的规范,以及 GitHub Actions 中的 CI 矩阵配置。读完本文,你将掌握如何用EXAMPLE环境变量驱动 Playwright 逐个测试示例、如何为新增示例编写最小冒烟用例并接入 CI,以及如何排查测试运行中的常见问题。
一、这个 E2E 测试体系要解决什么问题
examples/目录下存放着大量形态各异的示例应用:既有纯 Next.js 前端应用,也有附带 Python Agent 的混合应用。仓库选择用一套统一的 Playwright 测试工程来覆盖它们,核心目标有三个(见 examples/e2e/AGENTS.md):
- 提供一致的冒烟测试方式:本地与 CI 使用同一套命令、同一套配置,降低维护成本;
- 兼容多种示例形态:Next-only 与"UI + Agent"混合示例都能被同一套机制驱动;
- 保持测试轻量且稳定:避免 flaky,且不要求真实 API Key。
从工程结构看,这套测试位于 examples/e2e,其 package.json 只依赖@playwright/test、typescript和@types/node三个包,是一个完全独立、聚焦的冒烟测试工程,不依赖示例自身的运行时代码。
二、核心设计:一次只测一个示例
这套套件刻意设计为一次只运行一个示例。激活哪个示例由环境变量EXAMPLE决定,未设置时默认回退到form-filling。这一逻辑在 playwright.config.ts 中体现得最为直接:
const EXAMPLE = process.env.EXAMPLE ?? "form-filling"; const PORT = Number(process.env.PORT ?? "3000"); const HYBRID_EXAMPLES = new Set(["travel", "research-canvas"]); const webServerCommand = HYBRID_EXAMPLES.has(EXAMPLE) ? "pnpm dev:ui" : "pnpm dev"; const exampleDir = path.resolve(__dirname, "../v1", EXAMPLE);这里有几个值得注意的细节:
- 端口可配置:默认端口 3000,可通过
PORT环境变量覆盖,CI 中避免多任务撞端口; - 示例目录解析:示例统一存放在
examples/v1/${EXAMPLE}下,测试工程通过path.resolve从自身目录向上定位; - 混合示例白名单:
travel与research-canvas属于混合示例,其余按 Next-only 处理。
playwright.config.ts随后用这些变量设置webServer:cwd指向所选示例目录,command根据示例类型选择启动命令。
为什么每个 spec 都要写const EXAMPLE = process.env.EXAMPLE ?? "form-filling";
每个 spec 文件都带有一行"门控"代码,例如 tests/v1.x/form-filling.spec.ts:
const EXAMPLE = process.env.EXAMPLE ?? "form-filling";配合test.skip(EXAMPLE !== "form-filling", ...)实现的效果是:当运行某一个示例时,只有匹配的 spec 会真正执行,其余 spec 全部跳过。这样的好处是:所有测试仍然集中在一个文件夹中,但 CI 可以按"每个示例一个 job"跑矩阵,互不干扰。
三、两种示例类型:Next-only 与 Hybrid
Next.js-only 示例
这类示例只包含前端,直接用pnpm dev启动即可。以 examples/v1/form-filling/package.json 为例,其dev脚本为next dev --turbopack,测试框架只需等待这个命令就绪。
Hybrid 示例(UI + Agent)
travel和research-canvas同时带有 Python Agent。以 examples/v1/travel/package.json 为例:
{ "dev": "concurrently -k -n ui,agent -c blue,red \"PORT=3000 npm run dev:ui\" \"PORT=8000 npm run dev:agent\"", "dev:ui": "next dev", "dev:agent": "cd agent && uv run main.py" }pnpm dev会用 concurrently 同时拉起 UI(端口 3000)和 Agent(端口 8000)。但对于 UI 冒烟测试,通常只需要前端能启动即可,因此 Playwright 配置对混合示例选择pnpm dev:ui,从而避免在纯 UI 冒烟测试中启动 Python Agent,既快又稳定。
四、本地环境搭建与运行
1. 安装测试工程依赖
在examples/e2e目录下执行:
pnpm install pnpm exec playwright install --with-deps chromium--with-deps会顺带安装 Chromium 运行所需的系统库;如果系统已具备这些库,可只执行pnpm exec playwright install chromium(CI 正是采用这种精简方式,原因见后文)。
2. 安装目标示例的依赖
每个示例有自己的package.json,需要在对应目录单独安装,例如:
cd examples/v1/travel && pnpm install注意事项:如果示例的postinstall脚本依赖非 Node 工具链(例如 Python 的uv),而你又想要 CI 风格的行为,可以改用pnpm install --ignore-scripts跳过这些钩子。
3. 运行单个示例
回到examples/e2e目录,通过EXAMPLE环境变量指定目标:
EXAMPLE=form-filling pnpm test EXAMPLE=travel pnpm test EXAMPLE=research-canvas pnpm test EXAMPLE=chat-with-your-data pnpm test EXAMPLE=state-machine pnpm test设置EXAMPLE后,应当能看到结果为1 passed,而其他示例的 spec 显示为skipped。
五、测试布局与最小的冒烟用例长什么样
测试统一放在 examples/e2e/tests/v1.x 目录下,每个示例对应一个独立的冒烟 spec。目录中当前包含 7 个 spec 文件:form-filling、travel、research-canvas、chat-with-your-data、state-machine,以及两个针对聊天窗口布局的回归测试chat-window-layout。
以 tests/v1.x/form-filling.spec.ts 为例,冒烟用例保持最小化断言:
test.describe("form-filling", () => { test.skip(EXAMPLE !== "form-filling", `EXAMPLE=${EXAMPLE}`); test("loads", async ({ page }) => { await page.goto("/"); await expect( page.getByRole("heading", { name: "Security Incident Report" }), ).toBeVisible(); await expect( page .getByRole("contentinfo") .filter({ hasText: /Powered by CopilotKit/i }) .first(), ).toBeVisible(); }); });可见规范要点:使用getByRole等语义化选择器定位明显的标题/按钮;断言只验证"页面能加载、关键 UI 元素可见",不涉及任何 LLM 交互。
补充:一个"非典型"spec——聊天窗口布局回归测试
目录中还有 tests/v1.x/chat-window-layout.spec.ts,它演示了冒烟测试之外的一种用法:针对 1.55 版本回归的 flex 布局 bug 编写精确的 CSS 断言。它通过toHaveCSS("flex", "1 1 0%")、boundingBox()比较聊天消息区与窗口的高度占比、验证输入区位于窗口下半部、以及派发dragenter事件模拟拖拽态,来锁定"拖拽包装层无 class 导致聊天区塌陷"这一历史问题。这说明该测试工程虽然以冒烟为主,但也支持承载更精细的 UI 回归场景。
六、编写冒烟测试的规范
examples/e2e/AGENTS.md 对冒烟用例提出了三条铁律:
- 稳定(Stable):优先使用
getByRole选择器以及显而易见的标题、按钮; - 廉价(Cheap):不依赖 LLM 输出;
- 非侵入(Non-invasive):避免发送聊天消息或触发昂贵的后台工作。
配套的固定写法:
test.skip(EXAMPLE !== "<example>", ...) // 门控,仅当 EXAMPLE 匹配时才执行 await expect(page).toHaveTitle(/.../); // 校验页面标题 await expect(page.getByRole("heading", { name: "..." })).toBeVisible(); // 校验关键标题如果示例会自动弹出 Copilot UI 或触发调用,建议增加一个查询参数来禁用该行为。travel示例就是典型:其 app/page.tsx 会读取?copilotOpen=false参数关闭聊天窗口,因此对应 spec 通过page.goto("/?copilotOpen=false")规避自动弹窗:
await page.goto("/?copilotOpen=false"); await expect(page).toHaveTitle(/CopilotKit Travel/i);七、CI 接入:GitHub Actions 矩阵
CI 工作流位于 .github/workflows/test_e2e-legacy-v1.yml,核心是 5 个示例组成的矩阵:
form-fillingtravelresearch-canvaschat-with-your-datastate-machine
工作流的 key 行为(与 AGENTS.md 描述一致):
- 安装
examples/e2e依赖和 Playwright Chromium; - 安装所选示例自身的依赖;
- 使用
pnpm install --frozen-lockfile保证安装可复现; - 针对
research-canvas,以--ignore-scripts安装,避免为了跑 UI 冒烟测试而要求 Python 工具链; - 失败时总是上传Playwright 产物(
test-results与playwright-report,保留 7 天)用于排查。
两个从仓库源码中确认的额外细节,可以作为对 AGENTS.md 的补充:
- 浏览器安装刻意不加
--with-deps。工作流注释说明:--with-deps会调用apt,在 runner 上可能长时间访问不到 Ubuntu 软件源,导致超时烧掉整个 job 的预算;而 runner 镜像已预装 Chromium 的系统库,所以只执行pnpm exec playwright install chromium。 - 使用 pnpm 的
packageManager字段锁定版本。工作流特意省略version参数,让pnpm/action-setup通过 corepack 继承仓库根 package.json 中声明的 pnpm 版本,保证本地与 CI 工具链一致。
此外,工作流在"Run e2e tests"步骤中显式注入环境变量:OPENAI_API_KEY: test、NEXT_PUBLIC_CPK_PUBLIC_API_KEY: ""等,与 Playwright 配置中的默认值对应,确保 CI 环境里即使没有真实密钥也能完成 UI 冒烟。
八、Playwright 配置细节速览
playwright.config.ts 中还有若干值得留意的配置项:
testDir: "./tests"、timeout: 60_000、expect.timeout: 10_000:测试超时 60 秒,断言等待最长 10 秒;use.baseURL: http://127.0.0.1:${PORT}:统一走本机回环地址,避免 hostname 解析差异;trace与video均设为retain-on-failure:失败时保留 trace 与视频,便于回放定位;webServer.reuseExistingServer: !process.env.CI:本地复用已启动的服务器(加速迭代),CI 中强制自建;webServer.timeout: 180_000:等待服务器就绪的上限为 3 分钟;webServer.env在继承当前环境的基础上注入:NEXT_TELEMETRY_DISABLED: "1"(关闭 Next.js 遥测)、OPENAI_API_KEY: "test"(占位密钥)、REMOTE_ACTION_URL默认指向http://127.0.0.1:8000/copilotkit(本地 Agent 服务);- 只启用
chromium一个 project(Desktop Chrome),reporter 在 CI 下用github,本地用list。
九、常见问题与调试
examples/e2e/AGENTS.md 记录了三个高频问题:
next: command not found:说明所选示例的node_modules未安装,去示例目录执行pnpm install即可;- 传递依赖 module not found(如
shiki):将该依赖显式声明到示例的dependencies中并重新安装。仓库中form-filling、travel等示例的 package.json 均显式声明了shiki,正是这一规范的实际落地; - Next.js 开发模式关于跨域(
allowedDevOrigins)的警告:当前按警告处理,不影响测试通过。
十、如何为新增示例接入这套体系
AGENTS.md 给出了清晰的四步流程:
- 确保示例可通过
pnpm dev(Next-only)或pnpm dev:ui(Hybrid)启动; - 在
tests/v1.x/下新增<example>.spec.ts,沿用门控写法; - 本地验证:
EXAMPLE=<example> pnpm test; - 将示例名加入 CI 矩阵 .github/workflows/test_e2e-legacy-v1.yml。
如果你要接入的是混合示例,还需要同步把示例名加入 playwright.config.ts 的HYBRID_EXAMPLES集合,否则框架会错误地使用pnpm dev启动(连带拉起 Python Agent)。这个集合是区分示例类型的唯一事实来源,属于文档未显式强调但必须遵循的实现约束。
结语
CopilotKit 的这套 E2E 冒烟测试体系,用"一次一个示例 + 环境变量门控 + CI 矩阵"的简洁设计,覆盖了形态差异巨大的数十个示例应用,同时把稳定性、廉价性、非侵入性作为测试编写的基本纪律。对于想要为自己的多示例仓库搭建轻量冒烟测试的开发者,examples/e2e 是一个结构清晰、开箱即用的参考模板。
【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考