如何使用 React 测试库通过包含 html 标签的文本字符串进行查询? [英] How to query by text string which contains html tags using React Testing Library?

查看:16
本文介绍了如何使用 React 测试库通过包含 html 标签的文本字符串进行查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前的工作解决方案

使用此 html:

<p data-testid="foo">姓名:<strong>Bob</strong><em>(特邀嘉宾)</em></p>

我可以使用 React 测试库 getByTestId 方法找到textContent:

expect(getByTestId('foo').textContent).toEqual('Name: Bob (special guest)')

有更好的方法吗?

我想简单地使用这个 html:

<p>姓名:<strong>Bob</strong><em>(特邀嘉宾)</em></p>

并使用 React 测试库getByText 方法像这样:

expect(getByText('Name: Bob (special guest)')).toBeTruthy()

但这不起作用.

所以,问题......

是否有一种更简单的方法可以使用 React 测试库来查找带标签的文本内容字符串?

解决方案

更新 2

使用了很多次后,我创建了一个助手.以下是使用此帮助程序的示例测试.

测试助手:

//withMarkup.ts从@testing-library/react"导入{MatcherFunction}类型查询 = (f: MatcherFunction) =>HTML元素const withMarkup = (查询:查询) =>(文本:字符串):HTMLElement =>查询((内容:字符串,节点:HTMLElement)=> {const hasText = (node: HTMLElement) =>node.textContent === 文本const childrenDontHaveText = Array.from(node.children).every(孩子 =>!hasText(child as HTMLElement))返回 hasText(node) &&孩子们没有文字})使用标记导出默认值

测试:

//app.test.tsx从@testing-library/react"导入{渲染}从'./App'导入应用程序从 '../test/helpers/withMarkup' 导入 withMarkupit('测试 foo 和 bar', () => {const { getByText } = 渲染(<App/>)const getByTextWithMarkup = withMarkup(getByText)getByTextWithMarkup('姓名:鲍勃(特邀嘉宾)')})

<小时>

更新 1

这是一个创建新匹配器 getByTextWithMarkup 的示例.注意这个函数在测试中扩展了 getByText,因此它必须在那里定义.(当然可以更新该函数以接受 getByText 作为参数.)

import { render } from "@testing-library/react";导入jest-dom/extend-expect";test("将函数传递给匹配器", () => {const 你好 = () =>(<div>你好 <span>world</span>

);const { getByText } = render(<Hello/>);const getByTextWithMarkup = (text: string) =>{getByText((内容,节点)=> {const hasText = (node: HTMLElement) =>node.textContent === 文本const childrenDontHaveText = Array.from(node.children).every(孩子 =>!hasText(child as HTMLElement))返回 hasText(node) &&孩子们没有文字})}getByTextWithMarkup('Hello world')

<小时>

这是 您(可能)不知道的关于测试库的五件事 来自 Giorgio Polvara 的博客:

<小时>

查询也接受函数

您可能见过这样的错误:

<块引用>

无法找到带有文本的元素:Hello world.这可能是因为文本被多个元素分解了.在这种情况下,您可以为您的文本提供一个函数matcher 使您的匹配器更加灵活.

通常,这是因为您的 HTML 如下所示:

<div>Hello 

解决方案包含在错误消息中:[...] you can provide a function for your text matcher [...]".

这是怎么回事?结果证明匹配器接受字符串、正则表达式或函数.

为您渲染的每个节点调用该函数.它接收两个参数:节点的内容和节点本身.您所要做的就是根据节点是否是您想要的节点返回 true 或 false.

一个例子将阐明它:

import { render } from "@testing-library/react";导入jest-dom/extend-expect";test("将函数传递给匹配器", () => {const 你好 = () =>(<div>你好 <span>world</span>

);const { getByText } = render(<Hello/>);//这些不会匹配//getByText("Hello world");//getByText(/Hello world/);getByText((内容,节点)=> {const hasText = 节点 =>node.textContent === "你好世界";const nodeHasText = hasText(node);const childrenDontHaveText = Array.from(node.children).every(孩子 =>!hasText(child));返回 nodeHasText &&孩子们没有文本;});});

我们忽略了 content 参数,因为在这种情况下,它将是Hello"、world"或空字符串.

我们正在检查的是当前节点是否具有正确的 文本内容.hasText 是一个小辅助函数来做到这一点.我宣布它是为了保持清洁.

但这还不是全部.我们的 div 不是我们要查找的文本的唯一节点.例如,本例中的 body 具有相同的文本.为了避免返回比需要更多的节点,我们确保没有任何子节点与其父节点具有相同的文本.通过这种方式,我们可以确保我们返回的节点是最小的——换句话说,是靠近 DOM 树底部的节点.

<小时>

阅读其余的您需要做的五件事(可能)不知道测试库

Current Working Solution

Using this html:

<p data-testid="foo">Name: <strong>Bob</strong> <em>(special guest)</em></p>

I can use the React Testing Library getByTestId method to find the textContent:

expect(getByTestId('foo').textContent).toEqual('Name: Bob (special guest)')

Is there a better way?

I would like to simply use this html:

<p>Name: <strong>Bob</strong> <em>(special guest)</em></p>

And use React Testing Library's getByText method like this:

expect(getByText('Name: Bob (special guest)')).toBeTruthy()

But this does not work.

So, the question…

Is there a simpler way to use React Testing Library to find strings of text content with the tags striped out?

解决方案

Update 2

Having used this many times, I've created a helper. Below is an example test using this helper.

Test helper:

// withMarkup.ts
import { MatcherFunction } from '@testing-library/react'

type Query = (f: MatcherFunction) => HTMLElement

const withMarkup = (query: Query) => (text: string): HTMLElement =>
  query((content: string, node: HTMLElement) => {
    const hasText = (node: HTMLElement) => node.textContent === text
    const childrenDontHaveText = Array.from(node.children).every(
      child => !hasText(child as HTMLElement)
    )
    return hasText(node) && childrenDontHaveText
  })

export default withMarkup

Test:

// app.test.tsx
import { render } from '@testing-library/react'
import App from './App'
import withMarkup from '../test/helpers/withMarkup'

it('tests foo and bar', () => {
  const { getByText } = render(<App />)
  const getByTextWithMarkup = withMarkup(getByText)
  getByTextWithMarkup('Name: Bob (special guest)')
})


Update 1

Here is an example where a new matcher getByTextWithMarkup is created. Note that this function extends getByText in a test, thus it must be defined there. (Sure the function could be updated to accept getByText as a parameter.)

import { render } from "@testing-library/react";
import "jest-dom/extend-expect";

test("pass functions to matchers", () => {
  const Hello = () => (
    <div>
      Hello <span>world</span>
    </div>
  );
  const { getByText } = render(<Hello />);

  const getByTextWithMarkup = (text: string) => {
    getByText((content, node) => {
      const hasText = (node: HTMLElement) => node.textContent === text
      const childrenDontHaveText = Array.from(node.children).every(
        child => !hasText(child as HTMLElement)
      )
      return hasText(node) && childrenDontHaveText
    })
  }

  getByTextWithMarkup('Hello world')


Here is a solid answer from the 4th of Five Things You (Probably) Didn't Know About Testing Library from Giorgio Polvara's Blog:


Queries accept functions too

You have probably seen an error like this one:

Unable to find an element with the text: Hello world. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Usually, it happens because your HTML looks like this:

<div>Hello <span>world</span></div>

The solution is contained inside the error message: "[...] you can provide a function for your text matcher [...]".

What's that all about? It turns out matchers accept strings, regular expressions or functions.

The function gets called for each node you're rendering. It receives two arguments: the node's content and the node itself. All you have to do is to return true or false depending on if the node is the one you want.

An example will clarify it:

import { render } from "@testing-library/react";
import "jest-dom/extend-expect";

test("pass functions to matchers", () => {
  const Hello = () => (
    <div>
      Hello <span>world</span>
    </div>
  );
  const { getByText } = render(<Hello />);

  // These won't match
  // getByText("Hello world");
  // getByText(/Hello world/);

  getByText((content, node) => {
    const hasText = node => node.textContent === "Hello world";
    const nodeHasText = hasText(node);
    const childrenDontHaveText = Array.from(node.children).every(
      child => !hasText(child)
    );

    return nodeHasText && childrenDontHaveText;
  });
});

We're ignoring the content argument because in this case, it will either be "Hello", "world" or an empty string.

What we are checking instead is that the current node has the right textContent. hasText is a little helper function to do that. I declared it to keep things clean.

That's not all though. Our div is not the only node with the text we're looking for. For example, body in this case has the same text. To avoid returning more nodes than needed we are making sure that none of the children has the same text as its parent. In this way we're making sure that the node we're returning is the smallest—in other words the one closes to the bottom of our DOM tree.


Read the rest of Five Things You (Probably) Didn't Know About Testing Library

这篇关于如何使用 React 测试库通过包含 html 标签的文本字符串进行查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆