
WebdriverIO React Selectors 实战用react$/react$$精确查询 React 组件【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverio在 React 生态中配合构建工具与 CSS 模块化方案使用后className等 class 属性常常被压缩、改写或加上了随机后缀导致基于传统选择器如findElement/findElements、CSS 选择器难以稳定定位生成的 DOM。WebdriverIO 为此提供了两组针对 React 的查询命令browser.react$/browser.react$$以及同名的元素级命令让你可以直接按组件名查询 React 虚拟 DOM并可选地按 props 与 state 过滤从而绕过 class 被改写带来的脆弱性。读完本文你将掌握这两组命令的完整参数语义、在真实测试中如何组合过滤条件以及它们在 WebdriverIO 底层基于 resq 库是如何工作的。为什么需要 React 专属选择器React 是当下 Web 前端最广泛使用的框架之一大量项目同时使用各种样式工具CSS Modules、styled-components、Tailwind 等它们会把 JSX 中className对应的 class 属性值压缩或重写。由于无法保证 class 名保持不变使用 WebdriverIO 基于 DOM 的选择器$、$$、browser.findElement等去查询这些元素就变得很不稳定。React 选择器的思路是绕开最终渲染出的 class 属性直接查询 React 的虚拟 DOMVirtualDOM。WebdriverIO 在浏览器内部注入并执行 resq 库的脚本见 packages/webdriverio/src/scripts/resq.ts通过组件名定位任意组件并按state和/或props做过滤。这样即使 class 名被压缩只要组件结构稳定测试选择器就依然可靠。API 概览react$与react$$browser.react$和browser.react$$都接收三个参数参数类型是否必填说明selectorstring必填要查询的 React 组件名即 JSX 中的组件标识符propsobject可选用于过滤组件的 props 对象要求与组件当前 props 匹配statestring \| number \| boolean \| array \| object可选用于过滤组件的当前 state支持 React 中合法的任意 state 类型在 packages/webdriverio/src/types.ts 中这两组命令共用的配置类型被定义为export type ReactSelectorOptions { props?: Recordstring, unknown state?: Recordstring, unknown }两处命令的默认行为是当未传入props/state时两者都会被归一化为空对象{}源码中通过{ props {}, state {} }的默认参数实现。基础的调用方式如下const selector MyComponent const propFilter { someProp: true } const stateFilter this is my state browser.react$(selector, { props: propFilter, state: stateFilter })两者唯一的区别是返回类型browser.react$返回匹配到的第一个WebdriverIO 元素WebdriverIO.Element而browser.react$$返回所有匹配元素的数组WebdriverIO.ElementArray。无论哪个命令返回的元素都拥有 WebdriverIO 完整、可用的元素命令 API可以直接继续链式调用click()、getText()、isDisplayed()等。实战示例一个可复现的 React 应用下面以一个简单的 React 应用作为被测对象完整演示三种参数的使用。假设MyComponent依据传入的nameprop 渲染问候语并带有一个基于 state 的附加文本// mycomponent.jsx import React from react import ReactDOM from react-dom const MyComponent (props) { const { name } props; const [state] React.useState(name there ? , how are you? : ) return ( div Hello {name || World}{state} /div ) } ReactDOM.render( div MyComponent / MyComponent nameBarry/ MyComponent nameWebdriverIO/ MyComponent namethere/ /div, document.getElementById(#root), )该页面渲染了四个MyComponent实例输出的文本分别是Hello World、Hello Barry、Hello WebdriverIO和Hello there, how are you?。接下来针对不同断言场景组合过滤条件。场景一只按组件名选择不传过滤器只想验证第一个MyComponent实例可见时只传组件名即可// spec/mycomponent.test.js test(it should be displayed, () { const myComponent browser.react$(MyComponent) expect(myComponent.isDisplayed()).toBe(true) // pass })react$默认取首个匹配实例此时返回的是渲染了Hello World的那个元素。场景二按 props 过滤React 中 props 永远是一个对象因此props过滤器也只能传对象。要验证渲染出Hello WebdriverIO的那个实例用 props 过滤定位// spec/mycomponent.test.js test(it should correctly display Hello WebdriverIO, () { const myComponent browser.react$(MyComponent, { props: { name: WebdriverIO } }) expect(myComponent.getText()).toBe(Hello WebdriverIO) // pass })场景三按 state 过滤组件内部还有一个 state当name为there时 state 会被初始化为, how are you?。可以仅凭 state 过滤锁定这个实例// spec/mycomponent.test.js test(it should correctly display Hello WebdriverIO, () { const myComponent browser.react$(MyComponent, { state: , how are you? }) expect(myComponent.getText()).toBe(Hello there, how are you?) // pass })如类型定义所示state过滤器可以是字符串、数字、布尔值、数组或对象——所有这些类型在 React 中都是合法的 state 类型。批量匹配react$$当需要拿到全部匹配实例并逐一处理时改用react$$// spec/mycomponent.test.js test(it should return all instances, () { const allMyComponents browser.react$$(MyComponent) expect(allMyComponents).toHaveLength(4) })react$$与react$的参数完全相同唯一区别是它返回匹配选择器以及可选过滤条件的所有WebdriverIO 元素的数组从而可以对结果做.map()、按索引访问等数组操作。源码级解析底层如何工作调用链路从命令到虚拟 DOM以browser.react$为例它的执行流程在 packages/webdriverio/src/commands/browser/react$.ts 中非常清晰共分为四步export async function react$ ( this: WebdriverIO.Browser, selector: string, { props {}, state {} }: ReactSelectorOptions {} ): PromiseWebdriverIO.Element { await this.executeScript(resqScript.toString(), []) await this.execute(waitToLoadReact) const res await this.execute( react$Script, selector, props, state ) as unknown as ElementReference return getElement.call(this, selector, res, { isReactElement: true }) }注入 resq 脚本通过executeScript向页面注入完整 resq 运行时。该脚本字符串由构建期常量WDIO_RESQ_SCRIPT注入见 packages/webdriverio/src/commands/constant.ts并挂在window.resq上packages/webdriverio/src/scripts/resq.ts。等待 React 加载调用window.resq.waitToLoadReact()确保虚拟 DOM 已就绪。执行查询将selector、props、state三个参数传给react$Script由 resq 在页面内完成组件查找与过滤。包装元素把返回的元素引用交给getElement封装成 WebdriverIO 元素对象并以{ isReactElement: true }标记这是 React 查询的结果元素随即获得完整的元素命令 API。browser.react$$的流程与之几乎一致差别在于脚本换成react$$Script返回值交给getElements批量封装并通过enhanceElementsArray打上react$$的元数据见 packages/webdriverio/src/commands/browser/react$$.ts。页面内的过滤逻辑真正执行匹配的是注入页面里的脚本packages/webdriverio/src/scripts/resq.ts先以组件名调用window.resq.resq$(selector, reactElement)拿到组件当props非空时通过(element as any).byProps(props)进一步过滤当state非空时通过(element as any).byState(state)继续过滤若最终找不到组件会返回{ message: React element with selector ... wasnt found }这样的错误信息对于 Fragment 组件resq 返回的是 HTMLElement 数组脚本会取第一个子节点交给驱动避免嵌套数组无法被 WebDriver 理解react$$版本则会把所有 Fragment 的节点平铺成一个数组后返回。元素级命令在组件子树中查询除了browser级别的命令WebdriverIO 还提供了元素级的react$/react$$packages/webdriverio/src/commands/element/react$.ts。它先在已有的 WebdriverIO 元素上执行注入与等待然后把当前元素作为第四个参数reactElement传给脚本从而把查询范围限定在该元素代表的组件子树内——适合在大型应用里先定位根容器、再在局部范围内按组件查询的场景。版本与适用前提需要特别注意这些命令的源码注释明确指出它们仅适用于基于 React v16.x 构建的应用见react$.ts与react$$.ts的 JSDoc 提示。同时 resq 作为核心依赖被声明在 packages/webdriverio/package.jsonresq: ^1.11.0因此使用该能力时请确认被测应用的 React 版本与 WebdriverIO 版本相匹配。测试验证与调试建议WebdriverIO 自带的单测覆盖了上述行为可以作为理解命令语义的参考packages/webdriverio/tests/commands/browser/reactElement.test.ts 验证了react$会把props、state原样透传给页面脚本断言最终 execute 的args为[myComp, { some: props }, { some: state }]同一测试文件还验证了默认参数行为不传过滤条件时实际传给脚本的参数会被归一化为[myComp, {}, {}]以及返回的元素会被标记为isReactElement truepackages/webdriverio/tests/scripts/resq.test.ts 则针对页面内注入的匹配脚本做单元测试。在日常调试中官方建议配合React Dev Tools使用它可以帮助你直观看到应用中各组件的调用名称、当前 props 以及实时 state。一旦掌握了这些信息书写 WebdriverIO 的 React 选择器就会变得非常简单——先用 Dev Tools 确认组件名与过滤字段再将其填入react$/react$$即可。小结browser.react$与browser.react$$为 WebdriverIO 用户提供了一条“组件视角”的查询路径组件名定位 props/state 过滤 完整元素命令 API完美规避了 class 压缩改写导致的脆弱选择器问题。无论是单个组件的断言还是批量组件的遍历这套 API 都能让 React 应用的端到端测试更稳定、更易维护。配合 React Dev Tools 确认组件结构与状态再结合本文的示例你很快就能在项目中用上这套能力。【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考