尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Elastic UI:用 EuiColorPickerObject 为 EuiColorPicker 编写 Playwright 组件对象测试

Elastic UI:用 EuiColorPickerObject 为 EuiColorPicker 编写 Playwright 组件对象测试 Elastic UI用 EuiColorPickerObject 为 EuiColorPicker 编写 Playwright 组件对象测试【免费下载链接】euiElastic UI Framework 项目地址: https://gitcode.com/GitHub_Trending/eu/euiEuiColorPicker 是一个由文本输入锚点加浮层面板构成的复合组件直接拼 CSS 选择器写 Playwright 测试既脆弱又难维护。Elastic UIEUI官方测试辅助包elastic/eui-test-helpers为此提供了EuiColorPickerObject这个 Playwright Component Object它封装了锚点、浮层面板与色板swatch的稳定定位逻辑并解释了 EUI 为颜色选择器生成的复合data-test-subj值为何必须用 token 匹配而非精确匹配。读完后你可以在 Playwright/Scout 消费方测试中可靠地打开颜色选择器、选择色板、读写输入框颜色值并理解这套封装背后的选择器设计与取舍。一、背景elastic/eui-test-helpers与 Component Object 模式elastic/eui-test-helpers是 EUI monorepo 中与elastic/eui独立版本化的测试辅助库目前提供的是面向 Playwright/Scout 消费方的Component Object——围绕单个 PlaywrightLocator的语义化封装把如何可靠地找到并操作某个 EUI 组件的知识沉淀在库内消费方不再需要猜测哪些 CSS 类名只看起来对见 包级 README。安装方式为yarn add --dev elastic/eui-test-helpers注意该库与elastic/eui独立版本化。因为辅助库依赖的是 EUI 组件的 DOM 结构和data-test-subj约定所以要选择与你被测elastic/eui版本兼容的辅助库版本playwright/test被声明为peerDependency运行时由消费方自带。所有 Component Object 的构造函数统一签名(scope, testSubj)scope— PlaywrightPage或Locator即搜索范围testSubj— 你在应用中给组件根元素设置的data-test-subj值。EuiColorPickerObject即从包入口统一导出见 入口文件import { EuiColorPickerObject } from elastic/eui-test-helpers; const colorPicker new EuiColorPickerObject(page, myColorPicker); await colorPicker.locator.click(); await colorPicker.swatches.first().click();二、关键前提把data-test-subj设在EuiColorPicker本身上以及复合值问题这是使用EuiColorPickerObject最容易被踩坑的一点也是组件对象文档反复强调的前提Setdata-test-subjonEuiColorPickeritself.EuiColorPickerrenders it on its anchor aseuiColorPickerAnchor yourSubj, a compound value the shared root lookup matches by token. Without that,getByTestIdon your own subj finds nothing.也就是说你必须把data-test-subj设置在EuiColorPicker组件 props 上而不能期望锚点元素的data-test-subj就是你自己写的那个值。从源码看EuiColorPicker 实现中有const testSubjAnchor classNames(euiColorPickerAnchor, dataTestSubj);随后这个复合值会被写到锚点上默认文本输入锚点data-test-subj{testSubjAnchor}color_picker.tsx#L642自定义buttonprop 场景克隆你的 button 并注入同样的data-test-subjcolor_picker.tsx#L590。因此锚点上的最终属性形如data-test-subjeuiColorPickerAnchor myColorPicker。这正是 Playwright 原生page.getByTestId(myColorPicker)精确匹配找不到的原因——属性值并不等于你传的那个字符串。Component Object 基类BaseObject用一个按空白分词匹配单个 token的 CSS 选择器解决了这个问题见 base_object.ts#L18-L19const testSubjSelector (testSubj: string): string [data-test-subj~${testSubj.replace(/\\/g, \\\\).replace(//g, \\)}];这里用的是 CSS 的~单词匹配操作符只要data-test-subj的空白分隔列表中包含你的 token 即可命中这与 Kibana 的kbn/test-subj-selector约定一致。构造函数还会把你传入的componentSelector交给内部的组件类型守卫assertComponent()base_object.ts#L104-L124它通过root.and(scope.locator(componentSelector))校验命中的元素确实带有所需的组件类名防止你拿EuiColorPickerObject去操作另一个恰好使用同名data-test-subj的非颜色选择器元素。三、三条稳定选择器锚点、面板、色板EuiColorPickerObject的全部定位能力建立在三个稳定的 CSS 选择器常量上集中定义在 selectors.tsexport const EuiColorPickerSelectors { /** The default text-input anchor. A custom button prop has no class of its own. */ ANCHOR_SELECTOR: .euiColorPicker__input, /** The popover panel, rendered in a portal and only while open. */ PANEL_SELECTOR: [data-popover-panel].euiColorPicker__popoverPanel, /** A swatch button inside the panel. */ SWATCH_SELECTOR: .euiColorPickerSwatch, };对照 EUI 组件源码可以逐条印证选择器源码出处说明.euiColorPicker__input默认锚点EuiFieldText classNameeuiColorPicker__inputcolor_picker.tsx#L627仅默认文本输入锚点拥有该类名自定义buttonprop 没有.euiColorPicker__popoverPanelEuiPopover的panelClassNameeuiColorPicker__popoverPanelcolor_picker.tsx#L681-L704浮层面板的类名同时携带EuiPopover生成的data-popover-panel标记.euiColorPickerSwatch色板按钮快照中可见classeuiColorPickerSwatch emotion-...color_picker 测试快照面板内每个色板对应一个按钮组件对象的实现非常薄——构造时把锚点选择器交给基类做类型守卫两个 getter 分别给出面板与色板定位器见 object.tsexport class EuiColorPickerObject extends BaseObject { constructor(scope: ObjectScope, testSubj: string) { super(scope, testSubj, EuiColorPickerSelectors.ANCHOR_SELECTOR); } /** * The popover panel. Rendered in a portal, so it is found on the page, not * under the anchor. Resolves to zero elements while closed. */ public get panel(): Locator { return this.root.page().locator(EuiColorPickerSelectors.PANEL_SELECTOR); } /** The swatch buttons inside {link panel}. */ public get swatches(): Locator { return this.panel.locator(EuiColorPickerSelectors.SWATCH_SELECTOR); } }注意panelgetter 刻意调用this.root.page()把搜索范围提升到整个页面而不是在锚点子树内查找——因为EuiColorPicker的浮层面板由EuiPopover通过 portal 渲染到页面其他位置且只在打开时存在。四、API 详解panel、swatches与locator组件对象文档给出的 API 面如下这里结合实现逐条展开MemberDescriptionpanel浮层面板的Locator。面板渲染在 portal 中因此它在页面上而非锚点之下才能找到。关闭时解析为零个元素打开后携带data-popover-opentrueswatches面板内部色板按钮的Locatorpanel的子定位locator继承自BaseObject的锚点定位器。默认锚点是一个普通文本输入框点击它打开选择器并用 Playwright 自带的inputValue()/fill()读取或写入颜色值几个实战要点locator就是那个 HEX 文本输入框。EuiColorPicker的默认锚点显示当前颜色的大写 HEX 值无颜色时显示占位符你既可以用await colorPicker.locator.fill(#FF0000)直接改值也可以用await colorPicker.locator.inputValue()断言当前颜色无需自己拼输入框选择器。panel是打开才有的定位器。关闭状态下count()为 0打开后EuiPopover会同步设置data-popover-opentrue所以打开与否可以用toHaveAttribute(data-popover-open, true)断言。swatches依赖panel。它等价于panel.locator(.euiColorPickerSwatch)因此色板只在面板打开后可见Playground 故事使用默认调色板至少渲染一个色板。五、用测试用例验证对着 EUI Storybook 的自检规范EuiColorPickerObject自带了一个对真实组件的验证用例直接跑在 EUI 的 Storybook iframe 上。它用包内工具 storybook.ts 生成带参数的故事 URL并通过 URL 参数data-test-subj:testColorPicker给 Playground 故事注入被测子句见 object.spec.tsconst TEST_SUBJ testColorPicker; const PLAYGROUND_URL storyUrl( forms-euicolorpicker-euicolorpicker--playground, data-test-subj:${TEST_SUBJ} ); test.describe(EuiColorPickerObject, () { let colorPicker: EuiColorPickerObject; test.beforeEach(async ({ page }) { await page.goto(PLAYGROUND_URL); colorPicker new EuiColorPickerObject(page, TEST_SUBJ); await colorPicker.locator.waitFor({ state: visible }); }); test(resolves to exactly one element despite the compound contenteditable="false">【免费下载链接】euiElastic UI Framework 项目地址: https://gitcode.com/GitHub_Trending/eu/eui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表