尧图精选

EUI @elastic/eui-test-helpers:用 Playwright Component Objects 可靠测试 EUI 组件

🕒 发布时间:2026/9/17 20:12:57 📁 来源:尧图网络
EUI elastic/eui-test-helpers用 Playwright Component Objects 可靠测试 EUI 组件【免费下载链接】euiElastic UI Framework 项目地址: https://gitcode.com/GitHub_Trending/eu/euielastic/eui-test-helpers是 Elastic UIEUI框架官方提供的测试辅助库它封装了针对 EUI 组件 DOM 结构尤其是data-test-subj属性的语义化操作封装让消费方应用的端到端测试不再需要猜 CSS 选择器。本文基于该包的 README、CONTRIBUTING 指南以及包内源码系统讲解其定位边界、安装方式、Component Object 的构造约定、当前已提供的 16 个组件对象并结合BaseObject与EuiComboBoxObject的源码剖析其配置无关、自动检测的设计实现最后给出本地运行验证测试与发布前对真实消费方做验证的完整流程。一、定位与边界它解决什么问题不解决什么问题根据 README该库的目标是为那些直接写测试很麻烦的 EUI 组件提供操作辅助。其 CONTRIBUTING.md 进一步划清了边界它是用来做什么的让消费方的端到端测试能够可靠地建立与拆除组件状态让测试聚焦于真正的断言而不是在 EUI 的 DOM 里导航。它不是用来做什么的测试 EUI 组件本身的行为。EUI 仓库自身已具备 RTL 单元测试、Cypress E2E 测试与 Loki 视觉回归测试如果目的是验证某个 EUI 特性例如点击清除按钮后onChange是否触发那属于 EUI 自己的测试套件。本包内的 spec 文件是验证辅助工具本身工作正常的测试不能替代、也不应重复 EUI 的测试。因此对于简单的、类原生交互的组件README 建议直接使用你所选测试框架自带的内置方法而不是引入本库。README 同时标注该库尚处于早期开发阶段仍缺少许多实用工具。Cypress 与 React Testing Library 版本的 helpers 可能随后提供当前版本仅面向Playwright含 Kibana 的 Scout 测试框架消费者。二、安装与版本策略yarn add --dev elastic/eui-test-helpersREADME 特别强调一个选型要点该库独立于elastic/eui进行版本管理在 package.json 中可见其为 monorepo 中独立 workspace当前版本 1.6.0。由于这些 helpers 直接针对 EUI 组件的 DOM 结构和data-test-subj值编写你需要挑选与被测elastic/eui版本兼容的 helpers 版本——跨版本使用可能出现 DOM 不一致导致的失败。另外playwright/test在 package.json 中被声明为peerDependency^1.50.0且标记optional: true消费者在运行时提供自己项目中的 Playwright 版本helpers 不锁定也不重复安装它。三、Component Object 模型构造约定与底层实现README 给出的核心用法是import { EuiComboBoxObject } from elastic/eui-test-helpers; const comboBox new EuiComboBoxObject(page, dataViewSelector); await comboBox.setSelectedOptions([logs-*]); expect(await comboBox.getSelectedOptions()).toEqual([logs-*]);组件对象是围绕单个 PlaywrightLocator的语义化封装把某个 EUI 组件的用户式交互收敛到一个类里。所有 Component Object 的构造函数都接受(scope, testSubj)两个参数scope—— 在其中进行搜索的 PlaywrightPage或LocatortestSubj—— 你在应用中组件根元素上设置的data-test-subj值。3.1 BaseObject所有组件对象的基类从源码结构看所有对象都继承自 BaseObject它实现了三个关键机制1按空格分隔 token匹配data-test-subj而非全值精确匹配。// packages/test-helpers/src/playwright/base_object.ts const testSubjSelector (testSubj: string): string [data-test-subj~${testSubj.replace(/\\/g, \\\\).replace(//g, \\)}];~选择器只匹配data-test-subj属性中某一个空格分隔的 token。这一设计针对 EUI 中某些组件如EuiColorPicker会在消费者传入的 subj 之外额外追加自己的 token的情况——Playwright 的getByTestId是精确全值匹配会因此漏选。该约定遵循 Kibana 的kbn/test-subj-selector惯例。2Proxy 自动守卫每个异步公开方法执行前自动校验组件类型。data-test-subj并非组件类型独有的标识你可能把对象指向一个恰好撞了名、但实际是别的组件的元素。BaseObject的构造函数返回一个Proxy拦截所有async方法在调用前执行assertComponent()若根元素存在却不匹配该对象声明的componentSelector例如.euiComboBox会抛出明确错误提示你是否用错了 Component Object若元素暂不存在则跳过缺失由具体方法自行处理避免构造函数无法异步的局限。同步方法不被包装保持同步返回类型。3作用域与组合。基类暴露protected scope原始搜索范围用于查询root子树之外的兄弟/关联元素如 portal、protected root组件根Locator、protected testSubj用于区分多实例的 portal 内容以及locatorgetter 作为逃生舱口——当组件对象 API 覆盖不到时可直接拿到底层Locator做断言。子类还支持组合嵌套把另一个 Component Object 作为scope传入即可嵌套查询其 DOM 子树。3.2 选择器的单一事实来源按照 CONTRIBUTING.md 的Single source of truth for selectors原则每个组件的data-test-subj值与 CSS 选择器都集中在src/components/ /selectors.ts中辅助类与 spec 文件里禁止内联 test-subj 字符串。以 combo_box 的 selectors 为例ROOT_SELECTOR: .euiComboBox—— 根元素携带消费者的data-test-subjPILL_SELECTOR: .euiComboBoxPill—— 已选项药丸刻意用类名而非data-test-subj读取EUI 会把选项自己的data-test-subj展开到药丸上、覆盖默认的euiComboBoxPillsubj依赖它枚举内部元素会静默返回空optionFor(testSubj)——[data-test-subj~${testSubj}-optionsList] [roleoption]EUI 会把消费者的 subj 以${testSubj}-optionsList的形式传播到下拉列表上从而在多实例页面中把选项限定到某一个 combo box 内。包级的通用选择器在 src/selectors.ts 中统一再导出。四、当前可用的 Component Objects16 个README 的组件清单各组件的完整 API 见对应 README下表链接为仓库内相对路径组件对象组件级文档EuiComboBoxObjectcombo_box/README.mdEuiDataGridObjectdatagrid/README.mdEuiSuperSelectObjectform/super_select/README.mdEuiGlobalToastListObjecttoast/README.mdEuiSelectableObjectselectable/README.mdEuiDraggableObjectdrag_and_drop/README.mdEuiFilterButtonObjectfilter_button/README.mdEuiRangeObjectform/range/README.mdEuiPopoverObjectpopover/README.mdEuiFlyoutObjectflyout/README.mdEuiAccordionObjectaccordion/README.mdEuiContextMenuObjectcontext_menu/README.mdEuiModalObjectmodal/README.mdEuiBasicTableObjectbasic_table/README.mdEuiColorPickerObjectcolor_picker/README.mdEuiToolTipObjecttool_tip/README.md这些类全部从包入口 src/index.ts 导出BaseObject与ObjectScope类型同样对外导出方便消费方自定义派生对象。五、深入案例EuiComboBoxObject 的自动检测机制EuiComboBox是文档最详实的参考实现combo_box/README.md它的公开 API 体现了本包最重要的设计原则——公开方法不感知配置变体方法说明getSelectedOptions()返回已选项标签string[]setSelectedOptions(labels, { timeout? })以集合语义替换当前选择顺序无关已有的保留、缺少的补上、多余的移除已匹配时为空操作。timeout默认2500ms界定每个选项在输入后被等待的时长——对异步/服务端取数的 combo 可调大setCustomSelectedOptions(labels, { timeout? })通过onCreateOption创建自由文本值值不存在于选项中时使用逐条键入后以 Enter 提交并验证getAllVisibleOptions()打开下拉并返回当前可见的选项标签虚拟化列表只会挂载子集clear()清除全部已选项无选择时为空操作使用注意来自组件 README 与 object.ts 注释data-test-subj必须设置在外层EuiComboBox包裹元素.euiComboBox上而不是内层的comboBoxInput。clear()是智能自动检测原则的典型体现。EUI 的 combo box 存在多种选择模式Pill 模式singleSelection{false}默认或true纯文本模式singleSelection{{ asPlainText: true }}但调用者始终只写await comboBox.clear()。源码中 clear() 依次探测 DOM 状态并分派到内部策略有药丸且药丸带关闭按钮 → 逐个点击药丸上的×药丸无关闭按钮singleSelection下→ 在空的搜索输入上按Backspace移除选择asPlainText且输入中有已确认的选择 → 直接删除输入内容兜底 → 打开下拉逐个点击aria-selectedtrue的选项反选。其他值得注意的源码细节虚拟化读取约束getAllVisibleOptions()打开下拉后通过${testSubj}-optionsList定位列表读取roleoption的可见文本注释明确指出虚拟化列表只挂载子集所以读、匹配、枚举类方法不应假设能看到全部项——需要时先输入过滤词把目标筛进 DOM再断言。避免键盘事件冒泡setSelectedOptions收尾时用searchInput.blur()关闭下拉而不是按Escape——Escape会冒泡到页面级处理器modal/flyout 的关闭监听。这正是 CONTRIBUTING 中键盘事件限定到元素、避免Escape原则的落地。轮询而非单次断言asPlainText的选择经由消费者onChange提交可能晚一拍到达因此用expect.poll()等待集合相等并始终注明为什么必须 poll。可子类化EUI 组件体系存在继承关系EuiInMemoryTable基于EuiBasicTable其又基于EuiTable内部 getter 因此声明为protected未来的EuiInMemoryTableObject可以扩展EuiBasicTableObject复用其 locators。六、包内目录结构src/ playwright/ base_object.ts # 共享的 Playwright 基类 components/ name/ object.ts # Component ObjectPlaywright object.spec.ts # 验证测试 —— 默认配置 object.props.spec.ts # 验证测试 —— 非默认 props object.multiple_instances.spec.ts # 可选 —— 多实例作用域 components/ name/ selectors.ts # 框架无关的 test-subj 常量 README.md # 组件级 API 文档 storybook.ts # 框架无关的 Storybook URL 构造器 selectors.ts # 包级通用选择器 index.ts # 公开导出spec 文件遵循按关注点分文件而非按 story 或按方法分文件object.spec.ts只覆盖默认配置公开方法各自一个嵌套describeobject.props.spec.ts覆盖所有改变 DOM 或交互模型的非默认配置每个配置一个describebeforeEach可跨多个 Storybook storyobject.multiple_instances.spec.ts仅在页面上可能并存多实例时出现。实例命名以组件名开头comboBox多实例测试追加序号comboBox1/comboBox2。Storybook 页面 URL 统一用 storyUrl() 构造生成/iframe.html?id...viewModestory形式支持分号分隔的 args 串禁止内联/iframe.html字符串。七、本地运行验证测试验证测试运行在 EUI 的 Storybook 上。按 CONTRIBUTING.md 的流程# 一次性构建主题依赖包 yarn workspace elastic/eui build:workspaces # 启动 Storybook位于 http://localhost:6006 yarn workspace elastic/eui start等待 Storybook 编译完成后在仓库根目录执行yarn workspace elastic/eui-test-helpers test该命令依次执行tsc --noEmit类型检查与playwright test对应 package.json 中test: yarn lint yarn test-e2e。只跑 Playwright 测试用test-e2e全新检出需先安装一次浏览器yarn workspace elastic/eui-test-helpers exec playwright install chromium失败后查看含 trace、截图与完整调用日志的 HTML 报告yarn workspace elastic/eui-test-helpers show-reportplaywright.config.ts 中的关键配置值得了解webServer本地以reuseExistingServer: !CI复用已运行的:6006开发服务器CI 中则用http-server托管预构建的静态 Storybook../eui/storybook-static该目录是 gitignored 构建产物CI 会先执行yarn workspace elastic/eui build-storybook生成testIdAttribute: data-test-subj—— 这也是BaseObject要求消费方配置的前提fullyParallel: true、retries: 0有意不重试以便暴露 flake、timeout: 60s、expect.timeout: 10s、screenshot: only-on-failure默认值有意与 Kibanakbn-scout的配置对齐test-id 属性、超时、不自动重试保证跨团队一致性。这些测试在 EUI 的 Buildkite CI 上随每个 PR 运行组件变更时带 flake 检测详见 wiki 的 EUI test helpers 测试说明。flake 检测按目录路径把组件与其 helper 关联packages/eui/src/components/name、src/playwright/components/namespec或src/components/nameselectors任一处的改动都会重跑该 helper 的 specname可以是嵌套路径如form/super_select。新增 Component Object 时保持这种目录对齐即可无需额外接线。八、发布前对真实消费方Kibana做验证Storybook 验证测试只证明 helper 在受控环境可用不能证明它在真实消费方应用中可用——生产 DOM、父组件控制的状态、更严格的断言会暴露 Storybook 覆盖不到的问题。CONTRIBUTING 因此要求在发布前完成四步先在 Kibana 的kbn-scout包中原型把对象放在 Kibana 的eui_components目录下、注册到page.componentsfixture用法形如page.components.comboBox(myComboBox)转换相关 spec 并本地跑通——让 helper 直面它必须支持的真实 DOM而不是精心挑选的 story。开 Kibana draft PR 跑 CI本地只覆盖一部分 laneCI 会跑全部 stateful/serverless lane确认使用 helper 的 spec 在各 lane 通过。移植回 EUI 并通过 snapshot 发布验证Kibana CI 从 npm registry 安装而非 git ref而本包是 EUI monorepo 中的一个 workspace无法把 Kibana 指向某个 EUI 分支/提交因此需要一个 snapshot 版本工作日的 nightly 会以snapshotdist-tag 自动发布调度见仓库 CI workflowupdate_kibana_dependencies.yml或在 EUI PR 上加ci:regression-integration-test-kibana标签从 PR head 构建 snapshot 并触发 Kibana 集成链路需要对elastic/eui有 write/label 权限。Kibana PR 合并前须重新 pin 到正式发布版本——snapshot 是会移动的预发布版本且会被清理。关注首次尝试失败而不只是最终状态Scout 会对失败的 spec 重试一次lane 整体可能变绿但首次尝试已失败使用该 helper 的 spec 若首尝失败往往指向 helper 自身的竞态或时序 bug被重试掩盖应在发布前排查修复。九、新增一个 Component Object 的步骤按 CONTRIBUTING.md 的五步流程Selectors在src/components/name/selectors.ts添加data-test-subj常量其他任何地方都不允许内联 test-subj 字符串Object在src/playwright/components/name/object.ts添加继承BaseObject的类从selectors.ts导入常量公开面保持最小检测逻辑放进公开方法内部而不是为每个变体暴露新方法验证测试遵循上述 spec 文件结构导出从 src/index.ts 导出新类文档新增src/components/name/README.md记录公开 API 与自动检测行为。贯穿其中的设计原则还包括公开方法保持最小面方法保持private直到出现真实外部用例减少与 EUI 内部 DOM 细节的耦合优先读取同步渲染的 DOM 状态render 函数中同步设置的 CSS 类而非异步副作用异步加载的data-icon-type、延迟的aria-*、动画确需expect.poll()时必须写明原因所有 locator 一律 scope 到this.root而不是pageportal 元素使用${testSubj}-optionsList模式防止跨实例串扰。十、小结elastic/eui-test-helpers把如何可靠地操作 EUI 组件这一重复性问题收敛成了可复用的 Component Object以(scope, testSubj)为统一入口用 token 级data-test-subj匹配、Proxy 自动组件类型守卫、单一事实来源的选择器常量以及公开方法自动检测配置变体的策略把消费方测试从 DOM 细节中解放出来。当前版本聚焦 Playwright/Scout 消费者覆盖 combo box、data grid、table、flyout、modal 等 16 个常用组件结合包内selectors.ts与组件级 README 可以精确了解每个对象的公开 API、超时参数与变体行为。对于需要在其应用中端到端测试 EUI 组件的场景这是官方推荐的起点对于贡献新组件对象则应遵循Storybook 验证测试 Kibana 消费方验证的双层流程。【免费下载链接】euiElastic UI Framework 项目地址: https://gitcode.com/GitHub_Trending/eu/eui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →