plotly / react-cytoscapejs

React component for Cytoscape.js network visualisations
MIT License
483 stars 68 forks source link

[Question] How to test using Jest? #26

Closed Remimstr closed 5 years ago

Remimstr commented 5 years ago

Hi!

I am attempting to write tests for my code that uses react-cytoscapejs using the Jest testing framework but can't get it working. I have replicated the setup and teardown procedure of the mocha tests you provide within my own test (see below) and this is the error I am getting:


    TypeError: Cannot read property 'h' of undefined

      62 |     document.body.appendChild(root);
      63 | 
    > 64 |     ReactDOM.render(
         |              ^
      65 |       React.createElement(TestComponent, {
      66 |         setStateRef: ref => (setState = ref),
      67 |         defaults: json

      at Layout.Object.<anonymous>.GridLayout.run (../../node_modules/cytoscape/dist/cytoscape.cjs.js:20308:10)
      at setElesAndLayout (../../node_modules/cytoscape/dist/cytoscape.cjs.js:17905:27)
      at ../../node_modules/cytoscape/dist/cytoscape.cjs.js:17917:5
      at loadExtData (../../node_modules/cytoscape/dist/cytoscape.cjs.js:17864:7)
      at new Core (../../node_modules/cytoscape/dist/cytoscape.cjs.js:17908:3)
      at new cytoscape (../../node_modules/cytoscape/dist/cytoscape.cjs.js:30747:12)
      at t.value (../../node_modules/react-cytoscapejs/dist/react-cytoscape.js:1:3894)
      at commitLifeCycles (../../node_modules/react-dom/cjs/react-dom.development.js:17334:22)
      at commitAllLifeCycles (../../node_modules/react-dom/cjs/react-dom.development.js:18736:7)
      at HTMLUnknownElement.callCallback (../../node_modules/react-dom/cjs/react-dom.development.js:149:14)
      at Object.invokeGuardedCallbackDev (../../node_modules/react-dom/cjs/react-dom.development.js:199:16)
      at invokeGuardedCallback (../../node_modules/react-dom/cjs/react-dom.development.js:256:31)
      at commitRoot (../../node_modules/react-dom/cjs/react-dom.development.js:18948:7)
      at ../../node_modules/react-dom/cjs/react-dom.development.js:20418:5
      at Object.unstable_runWithPriority (../../node_modules/scheduler/cjs/scheduler.development.js:255:12)
      at completeRoot (../../node_modules/react-dom/cjs/react-dom.development.js:20417:13)
      at performWorkOnRoot (../../node_modules/react-dom/cjs/react-dom.development.js:20346:9)
      at performWork (../../node_modules/react-dom/cjs/react-dom.development.js:20254:7)
      at performSyncWork (../../node_modules/react-dom/cjs/react-dom.development.js:20228:3)
      at requestWork (../../node_modules/react-dom/cjs/react-dom.development.js:20097:5)
      at scheduleWork (../../node_modules/react-dom/cjs/react-dom.development.js:19911:5)
      at scheduleRootUpdate (../../node_modules/react-dom/cjs/react-dom.development.js:20572:3)
      at updateContainerAtExpirationTime (../../node_modules/react-dom/cjs/react-dom.development.js:20600:10)
      at updateContainer (../../node_modules/react-dom/cjs/react-dom.development.js:20657:10)
      at ReactRoot.Object.<anonymous>.ReactRoot.render (../../node_modules/react-dom/cjs/react-dom.development.js:20953:3)
      at ../../node_modules/react-dom/cjs/react-dom.development.js:21090:14
      at unbatchedUpdates (../../node_modules/react-dom/cjs/react-dom.development.js:20459:10)
      at legacyRenderSubtreeIntoContainer (../../node_modules/react-dom/cjs/react-dom.development.js:21086:5)
      at Object.render (../../node_modules/react-dom/cjs/react-dom.development.js:21155:12)
      at Object.render (src/tests/learningTests/cytoscape.test.js:64:14)

    console.error ../../node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Uncaught [TypeError: Cannot read property 'h' of undefined]
          at reportException (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
          at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
          at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
          at commitRoot (/node_modules/react-dom/cjs/react-dom.development.js:18948:7)
          at /node_modules/react-dom/cjs/react-dom.development.js:20418:5 TypeError: Cannot read property 'h' of undefined
          at Layout.Object.<anonymous>.GridLayout.run (/node_modules/cytoscape/dist/cytoscape.cjs.js:20308:10)
          at setElesAndLayout (/node_modules/cytoscape/dist/cytoscape.cjs.js:17905:27)
          at /node_modules/cytoscape/dist/cytoscape.cjs.js:17917:5
          at loadExtData (/node_modules/cytoscape/dist/cytoscape.cjs.js:17864:7)
          at new Core (/node_modules/cytoscape/dist/cytoscape.cjs.js:17908:3)
          at new cytoscape (/node_modules/cytoscape/dist/cytoscape.cjs.js:30747:12)
          at t.value (/node_modules/react-cytoscapejs/dist/react-cytoscape.js:1:3894)
          at commitLifeCycles (/node_modules/react-dom/cjs/react-dom.development.js:17334:22)
          at commitAllLifeCycles (/node_modules/react-dom/cjs/react-dom.development.js:18736:7)
          at HTMLUnknownElement.callCallback (/node_modules/react-dom/cjs/react-dom.development.js:149:14)
          at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
          at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
          at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
          at commitRoot (/node_modules/react-dom/cjs/react-dom.development.js:18948:7)
          at /node_modules/react-dom/cjs/react-dom.development.js:20418:5
          at Object.unstable_runWithPriority (/node_modules/scheduler/cjs/scheduler.development.js:255:12)
          at completeRoot (/node_modules/react-dom/cjs/react-dom.development.js:20417:13)
          at performWorkOnRoot (/node_modules/react-dom/cjs/react-dom.development.js:20346:9)
          at performWork (/node_modules/react-dom/cjs/react-dom.development.js:20254:7)
          at performSyncWork (/node_modules/react-dom/cjs/react-dom.development.js:20228:3)
          at requestWork (/node_modules/react-dom/cjs/react-dom.development.js:20097:5)
          at scheduleWork (/node_modules/react-dom/cjs/react-dom.development.js:19911:5)
          at scheduleRootUpdate (/node_modules/react-dom/cjs/react-dom.development.js:20572:3)
          at updateContainerAtExpirationTime (/node_modules/react-dom/cjs/react-dom.development.js:20600:10)
          at updateContainer (/node_modules/react-dom/cjs/react-dom.development.js:20657:10)
          at ReactRoot.Object.<anonymous>.ReactRoot.render (/node_modules/react-dom/cjs/react-dom.development.js:20953:3)
          at /node_modules/react-dom/cjs/react-dom.development.js:21090:14
          at unbatchedUpdates (/node_modules/react-dom/cjs/react-dom.development.js:20459:10)
          at legacyRenderSubtreeIntoContainer (/node_modules/react-dom/cjs/react-dom.development.js:21086:5)
          at Object.render (/node_modules/react-dom/cjs/react-dom.development.js:21155:12)
          at Object.render (/apps/linepulse/src/tests/learningTests/cytoscape.test.js:64:14)
          at Object.asyncJestLifecycle (/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
          at resolve (/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
          at new Promise (<anonymous>)
          at mapper (/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
          at promise.then (/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
          at process.internalTickCallback (internal/process/next_tick.js:77:7)
    console.error ../../node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Uncaught [TypeError: Cannot read property 'destroy' of undefined]
          at reportException (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
          at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
          at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
          at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
          at safelyCallComponentWillUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17176:5https://github.com/Remimstr/broken-cytoscape-test)
          at commitUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17553:11) TypeError: Cannot read property 'destroy' of undefined
          at t.value (/node_modules/react-cytoscapejs/dist/react-cytoscape.js:1:4283)
          at callComponentWillUnmountWithTimer (/node_modules/react-dom/cjs/react-dom.development.js:17169:12)
          at HTMLUnknownElement.callCallback (/node_modules/react-dom/cjs/react-dom.development.js:149:14)
          at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
          at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
          at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
          at safelyCallComponentWillUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17176:5)
          at commitUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17553:11)
          at unmountHostComponents (/node_modules/react-dom/cjs/react-dom.development.js:17873:7)
          at commitDeletion (/node_modules/react-dom/cjs/react-dom.development.js:17904:5)
          at commitAllHostEffects (/node_modules/react-dom/cjs/react-dom.development.js:18685:11)
          at HTMLUnknownElement.callCallback (/node_modules/react-dom/cjs/react-dom.development.js:149:14)
          at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
          at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
          at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
          at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
          at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
          at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
          at commitRoot (/node_modules/react-dom/cjs/react-dom.development.js:18913:7)
          at /node_modules/react-dom/cjs/react-dom.development.js:20418:5
          at Object.unstable_runWithPriority (/node_modules/scheduler/cjs/scheduler.development.js:255:12)
          at completeRoot (/node_modules/react-dom/cjs/react-dom.development.js:20417:13)
          at performWorkOnRoot (/node_modules/react-dom/cjs/react-dom.development.js:20346:9)
          at performWork (/node_modules/react-dom/cjs/react-dom.development.js:20254:7)
          at performSyncWork (/node_modules/react-dom/cjs/react-dom.development.js:20228:3)
          at requestWork (/node_modules/react-dom/cjs/react-dom.development.js:20097:5)
          at scheduleWork (/node_modules/react-dom/cjs/react-dom.development.js:19911:5)
          at scheduleRootUpdate (/node_modules/react-dom/cjs/react-dom.development.js:20572:3)
          at updateContainerAtExpirationTime (/node_modules/react-dom/cjs/react-dom.development.js:20600:10)
          at updateContainer (/node_modules/react-dom/cjs/react-dom.development.js:20657:10)
          at ReactRoot.Object.<anonymous>.ReactRoot.render (/node_modules/react-dom/cjs/react-dom.development.js:20953:3)
          at /node_modules/react-dom/cjs/react-dom.development.js:21090:14
          at unbatchedUpdates (/node_modules/react-dom/cjs/react-dom.development.js:20459:10)
          at legacyRenderSubtreeIntoContainer (/node_modules/react-dom/cjs/react-dom.development.js:21086:5)
          at Object.render (/node_modules/react-dom/cjs/react-dom.development.js:21155:12)
          at Object.render (/apps/linepulse/src/tests/learningTests/cytoscape.test.js:64:14)
          at Object.asyncJestLifecycle (/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
          at resolve (/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
          at new Promise (<anonymous>)
          at mapper (/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
          at promise.then (/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
          at process.internalTickCallback (internal/process/next_tick.js:77:7)
    console.error ../../node_modules/react-dom/cjs/react-dom.development.js:17117
      The above error occurred in the <t> component:
          in t (created by TestComponent)
          in TestComponent

      Consider adding an error boundary to your tree to customize error handling behavior.
      Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
    console.error ../../node_modules/react-dom/cjs/react-dom.development.js:17117
      The above error occurred in the <t> component:
          in t (created by TestComponent)
          in TestComponent

      Consider adding an error boundary to your tree to customize error handling behavior.
      Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

The test itself is this:

import React from "react";
import ReactDOM from "react-dom";
import CytoscapeComponent from "react-cytoscapejs";

const defaults = {
  global: "cy",
  id: "cy",
  style: { width: "500px", height: "500px" },
  zoom: 1,
  pan: {
    x: 0,
    y: 0
  },
  elements: [
    {
      data: { id: "a", label: "apple" },
      position: { x: 0, y: 0 },
      scratch: { _test: 1 },
      classes: "foo bar"
    },
    {
      data: { id: "b", label: "banana" },
      position: { x: 0, y: 0 },
      scratch: { _test: 2 },
      classes: "foo bar"
    },
    {
      data: { id: "c", label: "cherry" },
      position: { x: 0, y: 0 },
      scratch: { _test: 3 },
      classes: "foo bar"
    }
  ]
};

const cloneDefaults = () => JSON.parse(JSON.stringify(defaults));

class TestComponent extends React.Component {
  constructor(props) {
    super(props);
    props.setStateRef(this.setState.bind(this));
    this.state = props.default;
  }

  render() {
    return React.createElement(CytoscapeComponent, this.state);
  }
}

describe.only("Component", () => {
  let root, setState, json;
https://github.com/Remimstr/broken-cytoscape-test
  let updateCyProps = props =>
    new Promise(resolve => setState(Object.assign({}, json, props), resolve));

  beforeEach(() => {
    json = cloneDefaults();

    root = document.createElement("div");

    document.body.appendChild(root);

    ReactDOM.render(
      React.createElement(TestComponent, {
        setStateRef: ref => (setState = ref),
        defaults: json
      }),
      root
    );
  });

  afterEach(() => {
    ReactDOM.unmountComponentAtNode(root);
    document.body.removeChild(root);
  });

  it("creates Cytoscape Instance", () => {});
});

I get generally the same errors whether I run this test or try to mount the component using enzyme.

I had difficulty creating a sandbox example of this issue so I make a new repository that you can checkout and play with: https://github.com/Remimstr/broken-cytoscape-test

I would really appreciate any insights you may have on this issue!

Remimstr commented 5 years ago

We've solved this problem by running our tests in headless browser environment using jest-puppeteer.