Search code examples
reactjschaienzymeopenseadragon

Testing a third party library(seadragon) using React


I ran into a problem running tests because one of the React components relies on a third party Sea Dragon which renders full-screen images and allows users to zoom in and out. The library interacts with the Dom directly and it's very hard to test.

My component looks like this:

class Viewer extends Component {
  static propTypes = {
    imageUrl: PropTypes.string.isRequired,
    showNavigator: PropTypes.bool
  };

  componentDidMount() {
    this.initSeaDragon();
  }

  renderZoomControls = () => {
    return (
      <div className={`${styles.toolbar}`}>
        <button id="zoom-in" className={`${styles.imageControl}`}>
          <span className="icon-search-plus" />
        </button>
        <button id="zoom-out" className={`${styles.imageControl}`}>
          <span className="icon-search-minus" />
        </button>
        <button id="reset" className={`${styles.imageControl}`}>
          <span className="icon-dot-circle-o" />
        </button>
        <button id="full-screen" className={`${styles.imageControl}`}>
          <span className={classNames(styles.mtsIcon, styles.fullscreenIcon)} />
        </button>
      </div>
    );
  };
  initSeaDragon() {
    const { imageUrl, showNavigator } = this.props;

    OpenSeadragon({
      id: 'seadragon',
      zoomInButton: 'zoom-in',
      zoomOutButton: 'zoom-out',
      homeButton: 'reset',
      fullPageButton: 'full-screen',
      tileSources: {
        type: 'image',
        url: '/cleric/resources/UDAU8Z2F4NC7ZDFXA909.png',
        buildPyramid: false
      },
      showNavigator: showNavigator
    });
  }
  render() {
    return (
      <div>
        <div id="seadragon" className={styles.container}>
          {this.renderZoomControls()}
        </div>
      </div>
    );
  }
}

export default Viewer;

The problem is that I need to use mount to test so that my life-cycles and render are called. But in doing so, I am getting an error:

TypeError: Cannot read property 'appendChild' of null

From experience, I know that the appendChild error message could possibly mean that the div tag with the Id is not being rendered so the library is not finding it. This is my best guess.

My Test looks like this:

import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import Viewer from '../viewer';

    const wrapper = mount(
      <Viewer
        showNavigator
        imageUrl={'/cleric/resources/UDAU8Z2F4NC7ZDFXA909.png'}
      />
    );

    describe.only('The Viewer should', () => {
      it('it renders a small navigator window to', () => {
        console.log(
          wrapper
            .find('#full-screen')
            .simulate('click')
            .debug()
        );
        console.log(wrapper.debug());
      });
    });

I am trying to simulate a click and when the image is rendered it applies a full-screen class that I would like to test.


Solution

  • I work with OpenSeadragon, but I don't have any experience using it with React. Anyway, if it's having trouble connecting with the div, one option would be to pass the element directly into OpenSeadragon rather than referencing it by ID. Instead of an id property use an element property. You could get the element as a ref from rendering it.

    Another thing to keep in mind is that the OpenSeadragon initialization is asynchronous. If you want to know when it's ready, use:

    viewer.addHandler('open', function() {
        // Ready
    });
    

    You'd have to save the viewer that the OpenSeadragon call returns, so you could use it here that way.