Enzyme 的.matchesElement()全解析用 ReactElement 通配模式校验渲染树根节点【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme导读.matchesElement(patternNode)是 Enzyme 为 ReactWrapper 与 ShallowWrapper 提供的节点匹配方法它接受一个 ReactElement而非 CSS 选择器以“通配模式”判断该元素是否与 wrapper 渲染树的根节点匹配。本文基于仓库源码与共享测试套件完整讲解其匹配规则、参数、返回值、典型用法、常见误区并深入Utils.js底层实现帮助你在编写 React 组件测试时准确断言“某个节点长得像什么”。方法签名与返回值.matchesElement(patternNode) BooleanpatternNodeReactElement作为匹配模式传入的 React 元素用于检测 wrapper 的单个节点是否符合该模式。返回值Boolean当前 wrapper 的根节点是否与传入的patternNode匹配。该方法有两个关键约束文档中明确强调必须是单节点single-nodewrapper——与.find()、.filter()不同.matchesElement()只检查根节点如果 wrapper 包含多个节点会抛错底层通过single(matchesElement, ...)强制单节点语义。只检查根节点——它不会像.containsMatchingElement()那样遍历整棵渲染树因此传入一个多层结构的 pattern 时pattern 的整个结构都必须与根节点含其所有子孙节点递归匹配。匹配规则patternNode 是一个“通配模式”.matchesElement()的核心设计是单向包含匹配pattern 是“期望值”wrapper 的根节点是“实际值”只要实际值能“覆盖”期望值即视为匹配。具体规则如下摘自文档并辅以源码印证标签名必须一致pattern 与 wrapper 节点的 tag name元素类型必须完全相同。内容必须一致文本节点忽略首尾空白但中间空白必须精确一致子元素必须按这些规则递归匹配。props 单向包含pattern 中出现的 props属性必须出现在 wrapper 节点的 props 中但 wrapper 节点上多余的 props 不影响匹配凡是 pattern 中出现的 props其值必须与 wrapper 节点对应值相等。style 单向包含pattern 中出现的 style CSS 属性必须出现在 wrapper 节点 style 中wrapper 多余的 style 属性不影响匹配凡是出现的属性值必须相等。以测试套件 matchesElement.jsx 中的用例为例const wrapper Wrap(( div div onClick{spy} style{{ fontSize: 12, color: red }}Hello World/div /div )).first(); // 下列 pattern 全部匹配成功 expect(wrapper.matchesElement(divdivHello World/div/div)).to.equal(true); expect(wrapper.matchesElement(( div div onClick{spy} style{{ fontSize: 12, color: red }}Hello World/div /div ))).to.equal(true); // onClick 可省略、style 可省略均视为匹配 expect(wrapper.matchesElement(( div div onClick{spy}Hello World/div /div ))).to.equal(true); expect(wrapper.matchesElement(( div div style{{ fontSize: 12, color: red }}Hello World/div /div ))).to.equal(true);而下方“反向”用例则全部返回false文本内容不同Bonjour le monde、style 值不同color: blue、fontSize: 13、事件处理器是另一个 spyspy2都会导致不匹配expect(wrapper.matchesElement(divdivBonjour le monde/div/div)).to.equal(false); expect(wrapper.matchesElement(( div div onClick{spy} style{{ fontSize: 12, color: blue }}Hello World/div /div ))).to.equal(false);注意测试中对spy与spy2的callCount断言为0匹配过程不会触发任何事件处理器或调用组件方法.matchesElement()是纯结构比对。完整示例文档示例展示了一个经典的“只关心关键属性”的断言场景/* eslint-disable react/button-has-type */ class MyComponent extends React.Component { constructor(props) { super(props); this.handleClick this.handleClick.bind(this); } handleClick() { // ... } render() { return ( button typebutton onClick{this.handleClick} classNamefoo barHello/button ); } } const wrapper mount(MyComponent /).childAt(0); expect(wrapper.matchesElement(buttonHello/button)).to.equal(true); expect(wrapper.matchesElement(button classNamefoo barHello/button)).to.equal(true);这里有两处值得拆解wrapper是mount(MyComponent /).childAt(0)得到的根节点 wrapper即button元素本身因此.matchesElement()直接作用于按钮节点。第一个断言只传了buttonHello/buttononClick、type、className这些额外 props 被“通配”忽略文本Hello精确匹配因此返回true。第二个断言补上了classNamefoo bar该值确实存在于按钮上也返回true。同理shallow渲染也能使用该方法因为ShallowWrapper提供了完全相同的 API实现一致见下文源码分析。常见误区Common Gotchas文档明确列出两个高频踩坑点.matchesElement()期望的是 ReactElement而不是 selector。它与.find()、.filter()、.closest()等接受字符串/CSS 选择器的方法不同调用时必须传入 JSX 表达式或React.createElement的结果否则无法进行元素结构比对。例如wrapper.matchesElement(button)是错误的用法。匹配结果取决于子节点的递归匹配。因为根节点的 children 也参与递归比对所以当你只希望“大致检查一下根节点”时pattern 中的子节点结构同样必须与渲染结果吻合。源码实现剖析matchesElement 到底怎么工作1. ReactWrapper 与 ShallowWrapper 的入口在 ReactWrapper.js 中matchesElement(node) { return this.single(matchesElement, () { const adapter getAdapter(this[OPTIONS]); const rstNode adapter.elementToNode(node); return nodeMatches(rstNode, this.getNodeInternal(), (a, b) a b); }); }ShallowWrapper.js 中是完全相同的实现。这段代码包含三个关键环节single(matchesElement, ...)保证 wrapper 只有一个节点否则抛错对应文档“必须是单节点 wrapper”的约束。adapter.elementToNode(node)把传入的 ReactElement 转换为内部的 RSTNodeReact Standard Tree Node结构之后所有的比对都在 RST 数据结构上进行因此 pattern 与 wrapper 节点使用同一套表示可比性一致。nodeMatches(rstNode, this.getNodeInternal(), (a, b) a b)第一个参数是 pattern第二个是 wrapper 的根节点第三个是“长度比较器”。(a, b) a b的含义是pattern 的 props 数量可以小于等于实际节点的 props 数量——这正是“pattern props 必须出现但反向不要求”这一单向包含规则的直接体现。2. 底层核心nodeMatches 与 internalNodeComparenodeMatches定义在 Utils.jsexport function nodeMatches(a, b, lenComp is) { return internalNodeCompare(a, b, lenComp, true); }注意最后一个参数isLoose true它与另一个方法nodeEqual用于.equals()isLoose false区分开matchesElement走的是宽松loose匹配路径。在internalNodeCompareUtils.js中宽松模式的影响体现在剔除空值 propsremoveNullaryReducer会把值为null/undefined的 props 从两侧移除避免空值干扰比对。children 使用childrenMatch递归比对最终落到internalChildrenCompare其中对文本节点会通过childrenToSimplifiedArray合并相邻文本并trim掉首尾空白——这就是文档所说“文本节点忽略首尾空格、但保留中间空格”的源码出处见 Utils.js。props 单向包含left取自 pattern逐 key 检查leftKeys中的每个 prop 是否存在于rightwrapper 节点中不存在则返回false存在则要求值相等对象类型用isEqual深度比较。而right中多余的 props 不会被检查。数量约束非文本节点还会用lenComp(leftKeys.length - leftHasChildren, rightKeys.length - rightHasChildren)校验 props 数量关系a b保证“pattern 不多于实际”。3. 与.containsMatchingElement()的本质区别.containsMatchingElement()ReactWrapper.js与.matchesElement()共享同一个nodeMatches底层但扫描范围完全不同containsMatchingElement(node) { const rstNode getAdapter(this[OPTIONS]).elementToNode(node); const predicate (other) nodeMatches(rstNode, other, (a, b) a b); return findWhereUnwrapped(this, predicate).length 0; }它通过findWhereUnwrapped遍历 wrapper 中的每一个节点及其全部深度只要任意一个节点匹配 pattern 即返回true而.matchesElement()只检查根节点。文档在 “Related Methods” 中给出的描述与之完全一致“searches all nodes in the wrapper, and searches their entire depth”。选型建议断言“根节点长什么样” → 用.matchesElement()严格、单节点、快速。断言“渲染树任意深处存在某个元素” → 用.containsMatchingElement()。测试套件中的验证该方法的共享测试位于 matchesElement.jsx通过describeMatchesElement({ Wrap, WrapRendered })同时驱动 ReactWrapper 与 ShallowWrapper两个 wrapper 分别注入实现。测试覆盖了四类场景匹配成功根节点“看起来像”渲染结果含省略 props、省略 style、完整结构。匹配失败文本不同、style 值不同、事件处理器不同均返回false。简单节点匹配class Test extends React.Component { render() { return h1test/h1; } }渲染后用h1test/h1直接匹配成功。无副作用匹配过程不会调用任何传入的事件回调。这套共享测试同时印证了文档示例mount(MyComponent /).childAt(0)后的matchesElement(buttonHello/button)返回true的可靠性。相关方法.containsMatchingElement() ReactWrapper搜索 wrapper 中所有节点及全部深度任一节点匹配 pattern 即返回true是与.matchesElement()对比学习的最佳参照。ShallowWrapper.matchesElementshallow渲染下使用同一匹配语义。ReactWrapper.single 语义理解“单节点 wrapper”约束可参考 wrapper 相关方法的整体约定。【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考