React: Is there something similar to node.textContent?
Asked Answered
P

6

18

I'm trying to test a react component children and I wanted to get a text representation of its children.

Is there a tool that does something similar to node.textContent for React?

I would like to have something like this:

let testComponent = (
  <Test>
    <p>abc</p>
    <p>abc2</p>
  </Test>
);

expect(testComponent.props.children.toString()).to.equal('abc abc2');
Pith answered 10/12, 2015 at 14:53 Comment(1)
react-testing-library might be the answer to this and many other questions you may have concerning frontend code tests. Don't test for internal details, test for what a user actually sees.Naughton
W
36

react-getNodeText Sandbox screenshot

// this is an out of date version, use the one below
const getNodeText = node => {
  if (['string', 'number'].includes(typeof node)) return node
  if (node instanceof Array) return node.map(getNodeText).join('')
  if (typeof node === 'object' && node) return getNodeText(node.props.children)
}

https://codesandbox.io/s/react-getnodetext-h21ss

// in the screenshot above

const heading = <Heading>Hello <span className="red">Code</span>Sandbox</Heading>

return (
  <div>
    {heading}
    <pre ref={printInnerHTML}/>
    <pre>{getNodeText(heading)}</pre>
  </div>
)

TypeScript update

const getNodeText = (node: React.ReactNode): string => {
  if (node == null) return ''

  switch (typeof node) {
    case 'string':
    case 'number':
      return node.toString()

    case 'boolean':
      return ''

    case 'object': {
      if (node instanceof Array)
        return node.map(getNodeText).join('')

      if ('props' in node)
        return getNodeText(node.props.children)
    } // eslint-ignore-line no-fallthrough

    default: 
      console.warn('Unresolved `node` of type:', typeof node, node)
      return ''
  }
}
Wilkens answered 6/3, 2020 at 13:0 Comment(1)
for those of you who got confused like me by typeof node === 'object' && node, the type of null is object, so this check protects against null.Issi
H
2

You can write a small function that traverses through the react element and gets you the text.

import React from 'react';
import renderer from 'react-test-renderer';

const MyComponent = ()=>{
  return <div>
    <p>hello</p>
    <div>
      <p>to</p>
    </div>
  </div>
}

const getText=(tree)=>{
  if(typeof tree === 'string'){
    return tree;
  }
  return tree.children.map((child)=>{
    return getText(child);
  }).join('');
}


it('renders without crashing', () => {
  const tree = renderer
    .create(<MyComponent />)
    .toJSON();

  expect(getText(tree)).toBe("helloto");
});

Here MyComponent is the component from which you need the text.

getText() will get all the text nodes from the component in the form of a string.

Hickie answered 3/10, 2019 at 11:23 Comment(1)
You also need to handle boolean, number, null which are all possible types for tree, see type React.ReactNodeWilkens
A
0

you can use getDomNode() or findDomNode() for es6 Components, and then use normal DOM methods.

If your'e looking for the React way - There shouldn't be one, since a parent is quite blind of its children.

Ansela answered 10/12, 2015 at 15:34 Comment(0)
F
0

You can do this using AirBnB’s Enzyme library

shallow(<Foo>bar</Foo>).text  // bar

https://github.com/airbnb/enzyme/blob/master/docs/api/shallow.md#text--string

Fiora answered 3/10, 2019 at 21:5 Comment(0)
Q
0

Definitely you should try Jest (I usually use it together with the React Testing Library). Basically, you can take a snapshot of your component, so, you can test not only the text contained in your component, but you can also test the layout, the parameters of the elements, etc. It is very handy to detect unwanted changes. It also comes with great features as code coverage:

import React from 'react';
import { cleanup, render } from "@testing-library/react";
import { MyComponent } from "./MyComponent";

describe("MyComponent", (): void => {
  afterEach(cleanup);
  it("renders properly", (): void => {
    const { container } = render(
      <MyComponent prop1="myprop1" prop2="myprop2" />
    );
    expect(container).toMatchSnapshot();
  });
});

Quickstep answered 7/10, 2019 at 9:11 Comment(0)
B
0

Using renderToString and DOM APIs seems to work well, and this also supports components that obtain their text from properties:

import {renderToString} from "react-dom/server";

function textContentReact(node: ReactNode): string {
  const div = document.createElement("div");
  div.innerHTML = renderToString(node);
  return div.textContent;
}

Test case:

const Example = ({text = "text"}) => <div>{text}</div>;
console.log(textContentReact(<Example/>));
// => "text"

renderToStaticMarkup is another, potentially more lightweight alternative.

Keep note that adding react-dom to frontend will incur a ~500kB chunk size increase.

Boyar answered 28/5 at 16:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.