CopilotKit Mastra 集成中的预置 CopilotPopup 弹窗:从演示实现到 QA 验收的完整指南
【免费下载链接】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
本文以 Mastra 集成 showcase 中prebuilt-popup演示的 QA 验收文档为骨架,完整继承其中的测试步骤与预期结果,并结合该演示页面源码、Playwright E2E 测试用例和@copilotkit/react-core/v2中CopilotPopup的实现,讲解如何在 Next.js 页面里接入 CopilotKit 的预置浮动聊天弹窗、各配置项的生效机制,以及如何用自动化测试验证"默认展开、自定义占位符、建议条目、开/关切换"等行为是否符合预期。
1. 验收前置条件
QA 文档 showcase/integrations/mastra/qa/prebuilt-popup.md 列出了执行验证前必须满足的两项前置条件:
- Demo is deployed and accessible —— 演示应用已部署且可通过浏览器访问;
- Agent backend is healthy —— 代理后端(即本 showcase 内
src/app/api/copilotkit/route.ts暴露的 CopilotKit runtime 路由)处于健康状态。
这两条对应演示页面上的两个依赖:前端组件需要runtimeUrl指向的 runtime 端点可用,agent="prebuilt-popup"指定的代理需要已在该端点注册(详见第 4 节)。任一不满足,页面虽然能渲染,但消息发送后会一直无响应。
2. 演示页面实现:最小可用的 CopilotPopup 接入
演示页位于 showcase/integrations/mastra/src/app/demos/prebuilt-popup/page.tsx,这是理解整个 QA 清单的核心。源码使用了@region[popup-basic-setup]标记,说明这段代码就是官方文档中"popup-basic-setup"片段的实际来源:
// @region[popup-basic-setup] <CopilotKit runtimeUrl="/api/copilotkit" agent="prebuilt-popup"> <MainContent /> <CopilotPopup agentId="prebuilt-popup" defaultOpen={true} labels={{ chatInputPlaceholder: "Ask the popup anything...", }} /> <Suggestions /> </CopilotKit> // @endregion[popup-basic-setup]这段接入涉及 5 个关键点,逐一拆解:
CopilotKit容器:来自@copilotkit/react-core/v2,runtimeUrl="/api/copilotkit"指向 showcase 自带的 runtime 路由,agent="prebuilt-popup"为整个应用树指定默认代理。agentId="prebuilt-popup":让弹窗与命名代理绑定,消息经该代理处理并独立维护工作记忆。defaultOpen={true}:弹窗首次渲染即为打开状态——这正是 QA 步骤中"Verify the popup is open by default"这一条的验证对象。labels.chatInputPlaceholder:覆盖聊天输入框的占位符文案为 "Ask the popup anything..."——对应 QA 中"Verify input placeholder reads ..."这一条。<Suggestions />:一个无渲染输出的挂载组件,负责注册建议条目(见第 3 节)。
页面主体 main-content.tsx 渲染了一个标题为"Popup demo"的居中文档,并说明<CopilotPopup />会浮动于页面之上、角落有 launcher 气泡、打开后是覆盖式聊天,且现有布局在下方保持原样——这正是 QA 第一条"Verify the main content heading is visible"和最后一条预期结果"Popup renders as a floating window over page content"的验证依据。
3. 建议条目:useConfigureSuggestions 与 "Say hi" 的注册
QA 步骤"Click 'Say hi' suggestion; verify it is sent"依赖的建议条目,由 suggestions.ts 中的自定义 hook 注册:
"use client"; import { useConfigureSuggestions } from "@copilotkit/react-core/v2"; export function usePrebuiltPopupSuggestions() { useConfigureSuggestions({ suggestions: [ { title: "Say hi", message: "Say hi from the popup!" }, { title: "Limerick", message: "Write me a quick limerick." }, { title: "Is 17 prime?", message: "Walk me through whether 17 is prime." }, ], available: "always", }); }其中available: "always"表示建议胶囊(suggestion pill)常驻显示,不依赖对话状态。点击 "Say hi" 胶囊时,实际发送的是message字段的完整文本 "Say hi from the popup!",而不是标题 "Say hi" 本身——这一细节直接决定了 E2E 测试的断言方式(见第 6 节)。
挂载方式在 suggestions-mount.tsx 中:组件调用 hook 后返回null,即建议注册是纯副作用,不产生 DOM 节点。
4. 后端代理注册:demoAgentNames 清单
前端指定agent="prebuilt-popup"后,runtime 端必须能解析出同名代理。在 route.ts 中,demoAgentNames数组显式列出了"prebuilt-popup"(第 56 行附近),与"prebuilt-sidebar"、"chat-slots"等并列。该文件中的注释说明了这一组"Parity-with-langgraph-python demos"当前都映射到同一个底层代理(weatherAgent),每个演示拥有独立的 resourceId 以避免工作记忆桶串扰;同时注释提到仓库中有专门的 parity 测试强制"每个页面agent="…"字面量都必须出现在该清单中"。因此若新增弹窗演示却忘记注册代理名,runtime 会返回 agent-not-found,聊天永远不会启动——这就是 QA 前置条件"Agent backend is healthy"在配置层面的具体含义。
5. CopilotPopup 源码级实现:defaultOpen 与 labels 如何生效
结合 packages/react-core/src/v2/components/chat/CopilotPopup.tsx 的实现可以进一步理解演示中的两个属性:
defaultOpen:CopilotPopupView中该属性默认值即为true,并透传为<CopilotChatConfigurationProvider isModalDefaultOpen={...}>(见 CopilotPopupView.tsx)。源码注释特别说明:若调用方通过受控的open管理状态,则弹窗位置由外部状态决定,而不是"跳回"defaultOpen——演示未使用受控模式,因此"首帧即展开"由defaultOpen={true}直接保证。labels.chatInputPlaceholder:在 CopilotChatInput.tsx 中,输入框 placeholder 的解析顺序为placeholder ?? labels.chatInputPlaceholder,即未传独立placeholder时回退到 labels 配置——演示正是走这条回退路径,所以 QA 通过断言占位符字面量 "Ask the popup anything..." 就能同时证明"弹窗渲染了"和"labels 覆盖生效了"。- 开/关生命周期:从源码结构看,
CopilotPopupView关闭时会卸载(unmount)聊天内容(内部由isRendered状态跟踪),因此 DOM 上data-testid="copilot-popup"节点会整体消失;而浮动 launcher(data-testid="copilot-chat-toggle")常驻页面,再次点击即重新挂载弹窗。这就是 QA 预期结果"Popup can be minimized/closed via its header controls"背后的真实行为。
6. QA 检查项与 E2E 测试的一一对应
QA 文档的手工检查项在 tests/e2e/prebuilt-popup.spec.ts 中被逐条自动化,映射关系如下:
| QA 文档检查项 | E2E 测试对应断言 |
|---|---|
Navigate to/demos/prebuilt-popup | beforeEach中page.goto("/demos/prebuilt-popup") |
| 主内容标题可见 | getByRole("heading", { name: "Popup demo" })可见(标题逐字取自演示源码,确认路由已挂载) |
| launcher 浮动按钮可见 | data-testid="copilot-chat-toggle"可见 |
| 弹窗默认打开 + 占位符为 "Ask the popup anything..." | getByPlaceholder("Ask the popup anything...")可见(同时证明defaultOpen与 labels 覆盖生效) |
| 点击 "Say hi" 建议并被发送 | 定位data-testid="copilot-suggestion"并过滤文案 "Say hi",点击后等待data-testid="copilot-assistant-message"出现 |
| 发送消息后助手在弹窗内响应 | 向占位符输入 "Hello",点击data-testid="copilot-send-button"后等待助手消息出现 |
| 头部控件可关闭弹窗 | 点击data-testid="copilot-close-button"后data-testid="copilot-popup"从 DOM 消失,再点击 launcher 重新出现,且 URL 保持不变(纯客户端状态切换) |
测试代码中还有两处值得注意的工程细节,直接解释了 QA 步骤的措辞:
- 提交方式选择:测试刻意点击发送按钮而不是在文本域按回车,注释说明该部署环境下 Enter 提交曾间歇性丢失,
copilot-send-button是稳定的提交触发器。 - 关闭按钮用 JS 层
.click():开发模式下 localhost 自动启用的<cpk-web-inspector>覆盖层会拦截 Playwright 的指针级点击,因此测试通过page.evaluate执行 DOM 级点击绕过覆盖层(与仓库共享工具_genuine-shared.ts中clickByJs相同模式)。
另外,E2E 注释指出该代理为"无工具的纯文本代理",因此发送 "Say hi from the popup!" 或 "Hello" 后,预期结果都是纯文本助手回复在弹窗内出现,这也是 QA 中"verify assistant responds in the popup"的可验收判据。
7. 小结
围绕prebuilt-popup这条演示链路,各文件职责清晰:
- QA 规范:showcase/integrations/mastra/qa/prebuilt-popup.md
- 页面接入(
CopilotKit+CopilotPopup+ 建议挂载):page.tsx - 主内容与建议条目:main-content.tsx、suggestions.ts
- 代理注册:route.ts
- 自动化验收:tests/e2e/prebuilt-popup.spec.ts
- 弹窗组件实现:CopilotPopup.tsx、CopilotPopupView.tsx
按此链路复现一个浮动式 Copilot 弹窗,只需三件事:在<CopilotKit runtimeUrl="…" agent="…">下放置<CopilotPopup agentId="…">、在 runtime 端注册同名代理、按需通过defaultOpen与labels定制初始状态与文案;再用"标题可见、占位符文案、launcher 存在、建议可点、关闭/重开后 URL 不变"这几条可断言的行为完成验收。
【免费下载链接】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),仅供参考