ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WebdriverIO 的 `wdio/await-expect` 规则:为 `expect` 断言强制补全 `await` 的 ESLint 静态检查实践

WebdriverIO 的 `wdio/await-expect` 规则:为 `expect` 断言强制补全 `await` 的 ESLint 静态检查实践 WebdriverIO 的wdio/await-expect规则为expect断言强制补全await的 ESLint 静态检查实践【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriveriowdio/await-expect是 eslint-plugin-wdio 提供的一条 ESLint 规则它强制要求所有 WebdriverIO 的expect断言调用必须以await前缀从静态分析层面杜绝“断言静默失败”这类难以排查的隐性 bug。本文以 原规则文档 为主体结合 规则源码、matchers 常量表 与 单元测试 展开讲解读者读完后将掌握该规则的触发原理、支持的全部断言 matcher、快照断言的边界处理以及在不同 ESLint 版本与 TypeScript 工程中的正确配置方法。规则概述为什么expect必须加awaitWebdriverIO 的所有断言均由expect-webdriverio提供而这些断言本质上是异步操作断言的执行需要驱动浏览器或移动端会话去完成元素查询、属性读取、请求拦截比对等动作因此全部返回 Promise。如果遗漏await断言语句就会被当作一个“被丢弃的 Promise”直接跳过断言可能根本没有执行测试“错误地通过”或者断言在后台异步失败错误被 Promise 吞掉报告异常后续代码不会等待断言结果执行时序完全失控。因此规则原文的判定标准非常直接expect调用必须以await前缀。规则文档中给出的错误示例是describe(my feature, () { it(should do something, async () { await browser.url(/); expect(browser).toHaveTitle(Foobar); }); });而正确写法是在expect(...)前显式补上awaitdescribe(my feature, () { it(should do something, async () { await browser.url(/); await expect(browser).toHaveTitle(Foobar); }); });从规则元数据见 await-expect.ts可以看到它的type为problem实际问题而非风格问题、category为Possible Errors并且开启了hasSuggestions——即支持在编辑器中一键应用自动修复建议。规则触发原理源码级静态检测逻辑与许多“见到expect就报错”的粗暴规则不同wdio/await-expect的检测相当精准。下面逐条拆解 await-expect.ts 中的判定流程。1. 识别形如expect(...).toXxx()的调用链规则监听 AST 中的CallExpression节点只有同时满足以下三个条件才会继续node.callee.type MemberExpression最外层是expect(...).toXxx这样的成员访问调用node.callee.object.type CallExpressionobject本身是函数调用node.callee.object.callee.name expect这个内层调用就是expect。也就是说规则只关心expect(...).matcher(...)这种标准的链式断言形态。2. 仅匹配 WebdriverIO 的 matcher 名单拿到toXxx方法名后规则会检查它是否命中 constants.ts 中维护的MATCHERS白名单或属于快照断言。目前白名单中收录了 40 个 WebdriverIO 常用 matchertoBeChecked、toBeClickable、toBeDisabled、toBeDisplayed、toBeDisplayedInViewport、toBeElementsArrayOfSize、toBeEnabled、toBeExisting、toBeFocused、toBePresent、toBeRequested、toBeRequestedTimes、toBeRequestedWith、toBeRequestedWithResponse、toBeSelected、toExist、toHaveAttr、toHaveAttribute、toHaveAttributeAndValue、toHaveChildren、toHaveClass、toHaveClipboardText、toHaveComputedLabel、toHaveComputedRole、toHaveElementClass、toHaveElementProperty、toHaveHTML、toHaveHeight、toHaveHref、toHaveId、toHaveLink、toHaveLocalStorageItem、toHaveSize、toHaveStyle、toHaveText、toHaveTitle、toHaveUrl、toHaveValue、toHaveWidth。这样做的好处是普通的前端断言如 Jest 生态的expect(1).toBe(1)不会被打扰——它们不在白名单内直接return。这一点在 单元测试 中有明确用例expect(1).toBe(1)被列为 valid合法代码。3. 快照断言的特殊判定只针对 WebdriverIO 元素对于toMatchSnapshot与toMatchInlineSnapshot两个快照 matcher规则做了更细致的边界处理只有断言对象看起来是 WebdriverIO 元素时才会检查await。判定依据是expect(...)的参数是否为$、$$选择器函数调用或browser.$、browser.$$这类成员调用见 await-expect.ts。这是因为在 TypeScript 与快照测试混用、或对普通对象做快照断言时toMatchSnapshot可能并不返回 Promise。因此await expect($(.foo)).toMatchSnapshot(); // ✅ 命中规则需要 await expect(someObject).toMatchSnapshot(); // ✅ 普通对象快照不触发 expect($(.foo)).toMatchSnapshot(); // ❌ 触发 missingAwait expect($$(.foo)).toMatchInlineSnapshot(); // ❌ 触发 missingAwait expect(browser.$(.foo)).toMatchSnapshot(); // ❌ 触发 missingAwait4. 只在语句级遗漏时报告最后一个关键条件是node.parent.type ExpressionStatement。也就是说只有当expect(...)被当作独立的表达式语句使用即真正“裸奔”时才报错。若它被嵌入到表达式中——比如作为await Promise.all([...])的数组元素——则不会误报这正是 测试用例 中await Promise.all([ expect(...).toHaveTitle() ])被判定为合法的原因。触发报错后规则通过messageId: missingAwait输出提示Missing await before an expect statement见 await-expect.ts。matcher 名单的可持续维护测试兜底规则的可靠性依赖于MATCHERS名单与expect-webdriverio实际提供的 matcher 保持同步。为此await-expect.test.ts 专门写了一条“覆盖性”测试它动态导入expect-webdriverio取出wdioCustomMatchers的所有键过滤掉两个快照 matcher然后断言该集合与MATCHERS排序后完全相等。只要上游新增或改名了 matcher而constants.ts没有同步更新这条测试就会失败从而保证规则永远不会因为名单过时而漏检。配置方法从 ESLint v8 到 v9 Flat Config安装依赖在安装插件前需先安装 ESLint 本体然后安装插件详见 READMEnpm i eslint --save-dev npm install eslint-plugin-wdio --save-devESLint v8 及以下.eslintrcextends 方式插件导出一份 recommended 配置开箱即用{ plugins: [wdio], extends: [ eslint:recommended, plugin:wdio/recommended ] }在 legacy 推荐配置中wdio/await-expect被置为error与wdio/no-debug、wdio/no-pause一同启用见 index.ts。ESLint v9Flat Config 方式ESLint v9 采用扁平配置可通过eslint.config.mjs直接引入// eslint.config.mjs import { configs as wdioConfig } from eslint-plugin-wdio; export default [ wdioConfig[flat/recommended], ];对应的 flat 推荐配置定义在 js-recommended.ts它同样将wdio/await-expect设为error。独立启用/调整该规则如果你不想整包引入推荐配置也可以只开启这一条规则并沿用默认的错误级别或按需降级为warn/ 关闭// eslint.config.mjsESLint v9 export default [ { plugins: { wdio: (await import(eslint-plugin-wdio)).default }, rules: { wdio/await-expect: error, }, }, ];// .eslintrcESLint v8 { plugins: [wdio], rules: { wdio/await-expect: warn } }TypeScript 工程中的自动切换no-floating-promise接管这是本规则在真实项目中一个非常值得注意的行为差异。当工程中同时安装了typescript与typescript-eslint/eslint-plugin时TypeScript 推荐配置会把wdio/await-expect自动关闭并换成类型感知的wdio/no-floating-promise见 ts-recommended.tsrules: { wdio/await-expect: off, wdio/no-debug: error, wdio/no-pause: error, wdio/no-floating-promise: error }原理在于await-expect走的是纯 AST 静态匹配只认识白名单里的 matcher而no-floating-promise借助typescript-eslint的类型信息配置项parserOptions.projectService: true见 typeAware.ts 与 ts-recommended.ts能识别任意返回 Promise 的表达式覆盖面更广、误报更少。这同时也是 README 中强调的推荐做法npm install --save-dev typescript typescript-eslint/eslint-plugin需要注意的是typescript-eslint被声明为插件的可选 peerDependency见 package.json且isTypeAware会在模块加载时通过require(typescript-eslint)探测其是否存在因此未安装时 TypeScript 推荐配置会回退为undefined不影响 JavaScript 场景的使用。另外为规避projectService带来的解析报错该配置默认 ignore 了eslint.config.js、eslint.config.cjs、eslint.config.mjs等 ESLint 自身配置文件。与其他 wdio 规则的协作定位await-expect是 eslint-plugin-wdio 的规则之一插件当前共维护四个规则见 plugin.ts 与 READMEwdio/await-expectexpect调用必须加await本文主题wdio/no-debug禁止browser.debug()语句残留wdio/no-pause禁止browser.pause(number)硬等待wdio/no-floating-promise类型感知地检查未处理的 Promise。四条规则配合 recommended 配置使用可以在 CI 阶段就拦截 WebdriverIO 测试代码中最常见的三类问题——遗漏await的断言、误提交的调试中断、以及用于“睡过去”的硬编码暂停让测试代码在进入运行环境前就通过静态检查保证质量。小结wdio/await-expect通过白名单式 matcher 匹配、快照断言边界识别和语句级漏检判定在“足够精准、不误伤前端断言”与“保证 WebdriverIO 断言全部被 await”之间取得了平衡其 matcher 名单又有覆盖性测试兜底可长期与expect-webdriverio保持同步。在 JavaScript 工程中开启 recommended 配置即可生效在 TypeScript 工程中它会被类型感知的no-floating-promise自动取代实现更严格的 Promise 管理。无论走哪条路径最终目标都一致让每一条 WebdriverIO 断言都被真正等待执行从根本上消除异步测试中“静默通过”的隐患。【免费下载链接】webdriverioNext-gen browser and mobile automation test framework for Node.js项目地址: https://gitcode.com/GitHub_Trending/we/webdriverio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进