omri_saadon
omri_saadon

Reputation: 10631

innerText is undefined in jest test

When testing using jest I saw that the property innerText is undefined while not in test it has the right value.

  it('get text from div', () => {
    const div = document.createElement('DIV')
    div.innerHTML = '<br>a<br>b<br>c'
    console.log('innerText', div.innerText) // undefined
    console.log('textContent', div.textContent) // 'abc'
    // expect(getTextFromDiv(div).length).toMatchSnapshot()
  })

But when using the same code not in jest test, the innerText shows :

'a

b

c'

and textContent is 'abc'.

Why innerText in jest is undefined and when it's not in a jest than the value is real?

This is the code where it works (not in jest):

const addTextInRichTextToPdf = (doc, text, offsetY) => {
  const div = document.createElement('DIV')
  div.innerHTML = '<br>a<br>b<br>c'
  console.log('innerText', div.innerText) // print the real value
  console.log('textContent', div.textContent) // 'abc'
  ...

Upvotes: 58

Views: 24952

Answers (3)

Ian Samz
Ian Samz

Reputation: 2119

Try using textContent instead of innerText

Upvotes: 19

fredericrous
fredericrous

Reputation: 3038

Building up on Matthew Souther's answer, here is the code snippet I came up with to get the text of multiple dom children elements in one go:

const getInnerText = (element) => element?.textContent
          ?.split('\n')
          .filter((text) => text && !text.match(/^\s+$/))
          .map((text) => text.trim());

textContent brings a lot of noise, it returns an empty string when a html element has no text (or a string with only spaces). Therefore I filter the empty lines and the ones that contain only spaces (or tabs). I also trim the resulting entries. The reason I use question marks everywhere (optional chaining) is that, in case the text is missing, I prefer to get "undefined" as a result than an error thrown.

Here is how to use the function:

const getInnerText = (element) => element?.textContent
          ?.split('\n')
          .filter((text) => text && !text.match(/^\s+$/))
          .map((text) => text.trim());

const div = document.createElement('DIV')
div.innerHTML = `
hello

world 👋
`;

const result = getInnerText(div);

// will display "world 👋"
console.log(result?.[1])

Upvotes: 2

Gabriel Bleu
Gabriel Bleu

Reputation: 10214

If you are using the default testEnvironment, then you are using jsdom.

You can check this issue to see why it is not implemented in jsdom : https://github.com/tmpvar/jsdom/issues/1245

The primary issue is the fact that innerText leans on the layout engine for guidance, and jsdom has no layout engine

If you want "full" browser support you can check puppeteer

Upvotes: 49

Related Questions