十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AionUi SkillsHub E2E 测试工程预备:Bridge 契约、data-testid 规划与可测性设计

AionUi SkillsHub E2E 测试工程预备:Bridge 契约、data-testid 规划与可测性设计 AionUi SkillsHub E2E 测试工程预备Bridge 契约、data-testid 规划与可测性设计【免费下载链接】AionUi免费、本地、开源的 24/7 全天候 Cowork 应用以及适用于 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等的 OpenClaw | 喜欢就点star吧项目地址: https://gitcode.com/iOfficeAI/AionUi导读本文以 AionUi 仓库中 SkillsHub E2E 工程师的预备笔记为核心系统梳理技能中心Skills Hub页面的 E2E 测试基础设施设计——从 IPC/Bridge 端点契约清单、UI 可测试性缺口分析data-testid 规划、Helper 层复用与新建到测试数据构造与隔离策略。读完本文你将掌握为复杂设置页编写稳定 E2E 测试的完整方法论并能直接对照 AionUi 仓库中的 SkillsHubSettings 源码 与 E2E 基础设施 落地同类测试工程。一、预备笔记的定位SkillsHub E2E 工程的门 1起点SkillsHub 是 AionUi 中集中管理 AI 技能包Skill Pack的设置页其核心定位在页面头部的 i18n 文案中一目了然Centrally manage AI skill packs — install once, use across all assistants.该页面承担四大类技能的管理My Skills自定义技能可增删导出、Official Skills内置官方技能只读、Extension Skills扩展贡献技能只读、Auto-injected Skills自动注入技能只读外加两个关键弹窗添加自定义路径 Modal、删除确认 Modal。由于它同时横跨 IPC Bridge后端状态与多区域复杂 UI是为数不多既有强后端断言需求、又有复杂 DOM 交互的设置页因此工程师以预备笔记形式先行沉淀测试工程方案并明确将其标记为门 1Gate 1——即需求文档评审前必须完成的可测试性摸底。预备笔记tests/e2e/docs/skills-hub/engineer-preliminary-notes.md本身就是一份可复用的 E2E 工程模板其结构为已掌握材料盘点E2E 基础设施 页面源码Bridge Skill 端点契约清单UI 现状与>export const ROUTES { guid: #/guid, settings: { gemini: #/settings/gemini, model: #/settings/model, agent: #/settings/agent, assistants: #/settings/assistants, skills: #/settings/skills, tools: #/settings/tools, display: #/settings/appearance, webui: #/settings/webui, system: #/settings/system, // ... }, } as const;这意味着goToSkillsHub(page)只需复用navigateTo(page, ROUTES.settings.skills)即可导航层零新增代码。三、ipcBridge Skill 端点契约测试断言的后端事实源预备笔记的核心产出之一是 Skill 相关 Bridge 端点的完整清单。经与 ipcBridge.ts第 846 行起为fs命名空间skill 端点集中在 901–1019 行核对这些端点当前全部映射为后端 HTTP 接口/api/skills/...这是理解Bridge 断言的关键UI 操作结束后测试通过invokeBridge调用这些端点验证后端状态是否与 UI 一致。3.1 端点清单与 HTTP 映射Bridge Key参数返回后端端点用途fs.listAvailableSkills无SkillInfo[]GET /api/skills列出已安装 skillMy Skills Extension Skills Builtinfs.listBuiltinAutoSkills无{ name, description }[]—列出内置自动注入 skillfs.detectAndCountExternalSkills无{ success, data: ExternalSource[] }GET /api/skills/detect-external检测外部 skill 源Claude Code CLI 等fs.getSkillPaths无{ userSkillsDir, builtinSkillsDir }GET /api/skills/paths获取 skill 存储路径fs.importSkillWithSymlink{ skillPath }IBridgeResponse{ skillName }—导入外部 skill符号链接方式fs.deleteSkill{ skillName }IBridgeResponseDELETE /api/skills/{skill_name}删除自定义 skillfs.exportSkillWithSymlink{ skillPath, targetDir }IBridgeResponse—导出 skill 到外部目录fs.addCustomExternalPath{ name, path }IBridgeResponsePOST /api/skills/external-paths添加自定义外部 skill 路径fs.readSkillInfo{ skill_path }{ name, description }POST /api/skills/info读取 skill 元信息验证导入结果fs.getCustomExternalPaths无Array{ name, path }GET /api/skills/external-paths列出自定义外部路径fs.listSkillImportHistory无SkillImportRecord[]GET /api/skills/import-history导入历史含失败明细fs.getSkillImportLimits无{ max_file_bytes, max_total_bytes }GET /api/skills/import-limits导入大小限制3.2 从源码确认数据类型listAvailableSkills的返回类型在 ipcBridge.ts 中有精确定义且与页面源码中的SkillInfo接口SkillsHubSettings.tsx一一对应{ name: string; description: string; location: string; relative_location?: string; // builtin 语料下的相对路径如 auto-inject/cron/SKILL.md is_auto_inject: boolean; is_custom: boolean; source: builtin | custom | cron | extension; }其中source字段直接决定了 skill 属于哪个 UI 区域Custom 页签 vs Official 页签是测试断言中区分删除按钮是否可见是否只读卡片的关键判据。而getSkillImportLimits返回的max_file_bytes / max_total_bytes会被页面渲染进 Custom 页签的导入提示文案中up to {{maxFileSize}} per file and {{maxTotalSize}} per skill测试可据此构造超限导入边界场景。3.3 一个重要的工程现实Bridge 契约与后端实现解耦从源码可以推断fs命名空间下的 skill 端点已被统一收敛到 HTTP 后端httpGet/httpPost/httpDelete包装而预备笔记中记录的listBuiltinAutoSkills、importSkillWithSymlink、exportSkillWithSymlink这类原生 IPC形态端点在当前ipcBridge.ts中已不再以同名 key 出现——这说明 Bridge 契约处于持续演进中可能已由后端fsBridge对应实现承载。对测试工程而言启示是Bridge key 清单必须与ipcBridge.ts实时同步核对任何 key 缺失都会在运行时表现为Bridge invoke timeout: xxxREADME 的 Troubleshooting 章节对此有专门说明。四、UI 可测试性现状data-testid 缺口的识别与演进预备笔记的核心判断是整页无data-testid所有元素依赖 CSS classUnoCSS 原子类如.bg-base、.rd-12px不稳定、文本匹配i18n key双语切换易失效、DOM 结构匹配layout 调整即崩。值得对照验证的是从当前仓库源码看该缺口已部分修复SkillsHubSettings.tsx已演进至 1042 行预备笔记记录为 765 行并落地了 19 处data-testid覆盖以下关键节点my-skills-section # Custom 页签区域容器 my-skill-card-{name} # 自定义 skill 卡片normalizeTestId 规范化 btn-delete-{name} # 单 skill 删除按钮仅 custom checkbox-skill-{name} # 批量模式勾选框 checkbox-select-all-skills # 全选 btn-batch-manage / btn-batch-delete / btn-batch-cancel # 批量管理模式 official-skills-section # Official 区域 extension-skills-section # 扩展技能区域readonlySection 注入 auto-skills-section # 自动注入区域 skills-header / btn-add-skill / btn-open-import-history skill-import-history-page / btn-back-to-skills skill-import-history-record-{sourceLabel}其中normalizeTestId是一个值得 E2E 复用的关键函数SkillsHubSettings.tsx// Normalize skill name for>// tests/e2e/helpers/skillsHubSettings.ts // ── 导航类 ────────────────────────────────────────────── export async function goToSkillsHub(page: Page): Promisevoid { await navigateTo(page, #/settings/skills); } // ── 断言类Bridge 后端状态──────────────────────────── export async function getMySkills(page: Page): PromiseSkillInfo[] { return invokeBridge(page, fs.listAvailableSkills); } export async function getExternalSources(page: Page): PromiseExternalSource[] { const result await invokeBridge(page, fs.detectAndCountExternalSkills); return result.success ? result.data : []; } // ── 操作类必须基于>// tests/e2e/helpers/selectors.ts — Skills Hub 段落 export function skillCardById(skillName: string): string { return [data-testidmy-skill-card-${skillName}]; } export const BTN_IMPORT_SKILL [data-testid^btn-import-]; export const BTN_DELETE_SKILL [data-testid^btn-delete-]; // ...其中[data-testid^btn-import-]这类前缀匹配特别适合不知道具体 skill 名、但要断言存在某个导入按钮的场景而skillCardById()函数工厂则保证对单个 skill 的精准定位。二者配合即可同时满足区域级断言与条目级操作两种粒度。六、可测试性阻塞项与数据构造方案6.1 阻塞项门 1 必须解决预备笔记把以下三项列为阻塞性缺口直接决定 E2E 能否落地所有交互元素无data-testid— 无法稳定定位元素列表项无唯一标识— 无法精准操作特定 skill纯文本匹配受 i18n 影响Modal 无 testid— 无法区分删除 vs 添加路径两种弹窗。结合当前源码第 3 项已通过wrapClassName: modal-delete-skill缓解第 1、2 项在 My Skills / Official / Extension / Auto 区域已大幅落地外部 Skills 区域与两个 Modal 内部仍是重点补测对象。6.2 前置条件构造Setup 策略SkillsHub 的 E2E 高度依赖测试数据先存在三路前置构造方案外部 skill 源存在— 测试环境预置 Claude Code CLI 的 skill 目录或通过fs.addCustomExternalPath动态注册一个外部源路径My Skills 有数据— 测试前经fs.importSkillWithSymlink导入测试 skillSetup 阶段允许走 Bridge测试后经fs.deleteSkill清理CleanupExtension Skills 存在— 依赖 E2E 启动时注入的AIONUI_EXTENSIONS_PATH环境变量指向仓库examples/目录例如 examples/e2e-full-extension 中的 skills 贡献从而让扩展技能区域有稳定数据源。6.3 数据隔离规范所有测试 skill 统一使用E2E-Test-前缀清理阶段批量删除避免与真实用户数据混淆自定义外部路径使用临时目录fs.mkdtempSync创建测试结束后随临时目录一并清理遵循 README 中测试开始时先清理残留E2E前缀数据的最佳实践保证测试可重复执行。6.4 不可直接 E2E 的需求及替代方案E2E 不读取文件系统、不验证符号链接因此部分需求必须降级为Bridge 断言 单测兜底需求不可 E2E 的原因替代方案导入后 skill 文件内容正确E2E 不读文件系统导入后invokeBridge(fs.readSkillInfo)验证元信息名称、描述符号链接创建成功E2E 不验证 symlink导入后 Bridge 查询路径断言其包含源路径关键字删除操作不影响源文件E2E 不访问外部路径仅验证 UI 卡片消失 Bridge 确认 skill 列表不含该项导出超时处理超时场景难复现由单元测试覆盖E2E 仅验证正常导出流程这张降级矩阵是本文档最具迁移价值的部分——它给出了任何文件系统强相关功能写 E2E 时的通用边界划分原则E2E 负责UI 与后端状态的最终一致性文件级细节交给单测。七、对需求文档的输入建议把可测性写进 PRD预备笔记对 Analyst 提出三条输入建议本质是把可测试性前置到需求阶段需求文档单独列出测试依赖项章节包含全部待补充data-testid清单、前置数据构造方案、测试环境配置要求外部 skill 源路径、Extension 路径每条需求标注可测方式UI 交互点击、输入、断言 DOM 可见性Bridge 断言操作后调invokeBridge验证后端状态截图验证复杂 UI 状态搜索结果、筛选后列表边界条件明确测试策略空列表状态无外部源、无 My Skills、搜索无结果、删除最后一个 custom skill、导入已存在 skill冲突处理、Modal 取消操作。这些边界用例都能在页面源码中找到对应的空态与提示实现——例如 My Skills 为空时渲染noSkills文案No skills found. Import some to get started.搜索无结果时渲染noSearchResultsNo matching skills.import 同名校验则在页面提示文案中明示importing the same name overwrites the existing skill——说明该建议与实现完全对齐可直接落地为可断言用例。八、Review 检查清单需求初稿的五维评审预备笔记在收到requirements.zh.md初稿后将从五个维度执行 Review这也是任何设置页 E2E 需求评审的通用模板完整性— 是否覆盖所有 UI 区域4 个 section 2 个 Modal可测性— 每条需求能否通过 UI 交互 Bridge 断言验证数据依赖— 前置条件是否可构造、清理策略是否明确testid 充足性— 对照 contenteditable="false">【免费下载链接】AionUi免费、本地、开源的 24/7 全天候 Cowork 应用以及适用于 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等的 OpenClaw | 喜欢就点star吧项目地址: https://gitcode.com/iOfficeAI/AionUi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表