question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Workaround for `TypeError: Cannot read property 'h' of undefined` and `TypeError: Cannot read property 'indexOf' of undefined`

See original GitHub issue

I ran into the same error as https://github.com/cytoscape/cytoscape.js/issues/2453 when doing ReactDOM.render and snapshot testing. It works fine in the browser.

Workaround further down

// CustomCytoscape.jsx
import React, { useRef } from 'react'
import Cytoscape from 'react-cytoscapejs'

function CustomCytoscape({ headless, elements }) {
  const layout = { name: 'preset' }

  const cyRef = useRef()

  return (
    <Cytoscape
      className={classes.cytoscape}
      layout={layout}
      cy={cy => {
        cyRef.current = cy
      }}
      elements={elements}
      stylesheet={stylesheet}
    />
  )
}
// CustomCytoscape.test.js
import React from 'react'
import ReactDOM from 'react-dom'
import renderer from 'react-test-renderer'
import CustomCytoscape from '../CustomCytoscape'

describe('CustomCytoscape', () => {
  const elements = []
  it('renders without crashing', () => {
    const div = document.createElement('div')
    ReactDOM.render(
      <CustomCytoscape elements={elements} />,
      div
    )
  })

  it('matches snapshot', () => {
    const component = renderer.create(
      <CustomCytoscape elements={elements} />
    )
    let tree = component.toJSON()
    expect(tree).toMatchSnapshot()
  })
})

returns TypeError: Cannot read property 'h' of undefined

 it('renders without crashing', () => {
       8 |     const div = document.createElement('div')
    >  9 |     ReactDOM.render(<App />, div)
         |              ^
      10 |   })
      11 | 
      12 |   const component = renderer.create(<App />)

And TypeError: Cannot read property 'indexOf' of undefined

const component = renderer.create(<App />)
         |                              ^
      13 |   let tree = component.toJSON()
      14 |   expect(tree).toMatchSnapshot()
      15 | })

At the moment I cannot spend more time looking into it, but I will try to look into it when I can.

Workaround

// App.jsx
const App = () => {
  const elements = []
  const headless = process.env.NODE_ENV === 'test'

  return (
    <div className={classes.container}>
      <CustomCytoscape elements={elements} headless={headless} />
    </div>
  )
}
// CustomCytoscape.jsx
import React, { useEffect, useRef } from 'react'
import cytoscape from 'cytoscape'
import Cytoscape from 'react-cytoscapejs'

function CustomCytoscape({ headless, elements }) {
  const layout = { name: 'preset' }

  const cyRef = useRef()

  // Use cytoscape (non-react) if headless === true
  useEffect(() => {
    if (headless) {
      cyRef.current = cytoscape({
        container: document.getElementById('cyHeadless'),
        layout,
        elements,
        style: stylesheet
      })
    }
  }, [headless, elements, layout])

  return headless ? (
    <div id="cyHeadless" className={classes.cytoscape} />
  ) : (
    <Cytoscape
      className={classes.cytoscape}
      layout={layout}
      cy={cy => {
        cyRef.current = cy
      }}
      elements={elements}
      stylesheet={stylesheet}
    />
  )
}

The following tests now works

CustomCytoscape.test.js
import React from 'react'
import ReactDOM from 'react-dom'
import renderer from 'react-test-renderer'
import CustomCytoscape from '../CustomCytoscape'

describe('CustomCytoscape', () => {
  const elements = []
  it('renders without crashing', () => {
    const div = document.createElement('div')
    ReactDOM.render(
      <CustomCytoscape elements={elements} headless={true} />,
      div
    )
  })

  it('matches snapshot', () => {
    const component = renderer.create(
      <CustomCytoscape elements={elements} headless={true} />
    )
    let tree = component.toJSON()
    expect(tree).toMatchSnapshot()
  })
})

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:6

github_iconTop GitHub Comments

3reactions
maxkfranzcommented, Aug 2, 2019

If you’re doing UI tests, you should probably use a browser like Puppeteer. I can’t speak to the scope of @mattorp’s tests. He could answer better about how his approach fits in with his use-case.

Generally speaking, you could use a headless cy object for unit tests of your model – if the graph is a part of your model – but I would think that would be decoupled from React. I’m of the opinion that the majority of automated UI tests are low utility and high cost, but to each his own.

For now, let’s close this issue since it seems to just be a Jest setup problem.

1reaction
maxkfranzcommented, Jul 31, 2019

https://facebook.github.io/create-react-app/docs/running-tests

Create React App uses Jest as its test runner. To prepare for this integration, we did a major revamp of Jest so if you heard bad things about it years ago, give it another try.

Jest is a Node-based runner. This means that the tests always run in a Node environment and not in a real browser. This lets us enable fast iteration speed and prevent flakiness.

While Jest provides browser globals such as window thanks to jsdom, they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks.

We recommend that you use a separate tool for browser end-to-end tests if you need them. They are beyond the scope of Create React App.

So Jest creates a fake window, which seems only sufficient for very basic rendering tests (e.g. simple divs). That is insufficient for testing real-world visualisations – and probably other things since Jest breaks fundamental assumptions about the window object.

So ultimately, this is a limitation of Jest and there would always need to be some kind of workaround. Even if something like a headless flag is exposed in the props of this component, you would need to detect Jest-mode versus app-mode and set headless appropriately.

If you want to write automated UI tests, you probably need to use something more sophisticated than Jest. You would need to use something like Selenium or Mocha+Puppeteer or Jest+Puppeteer.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Cannot Read Property of Undefined in JavaScript - Rollbar
TypeError : Cannot read property of undefined occurs when a property is read or a function is called on an undefined variable.
Read more >
"cannot read property 'indexOf' of undefined" - Stack Overflow
Right now, from the log, I can see the row number and the clean url. Problem is that the script stops with this...
Read more >
Cannot read property 'index' of undefined - WebViewer
When I import the saved xpdfstring with annotManager.importAnnotations I have the following error: CoreControls.js:600 Uncaught TypeError: ...
Read more >
How to Fix TypeError: Cannot read Property 'push' of ...
You call the method on a variable previously set to undefined . · You call the method on a variable before it has...
Read more >
Cannot read properties of undefined' - JavaScript Debugging
How To Fix 'Uncaught TypeError : Cannot read properties of undefined ' - JavaScript Debugging. Watch later. Share. Copy link.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found