npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

jasmine-react-helpers

v0.2.2

Published

a small suite of helper functions to make unit testing React.js components painless.

Downloads

2,122

Readme

jasmine-react is a small suite of helper functions to make unit testing React.js components painless.

Why do I need jasmine-react.js?

React.js' architecture is based on the idea that mutation is hard, so your React.js components should represent a state machine which can represent your UI under any state. This is an extremely powerful idea and means that you will often compose components inside of other components. While this is great for code reuse, it makes isolating one component for a unit test slightly more difficult. React.js will also provide some very helpful features (like auto binding), which make it more difficult to get under the covers and stub things out. jasmine-react aims to solve these testing issues.

Synopsis

Here's an overview of how jasmineReact can be used:

Spying on a method in a React Class

/** @jsx React.DOM */
window.HelloWorld = React.createClass({
  getInitialState: function(){
    return { number: this.randomNumber() };
  },
  randomNumber: function(){
    return Math.random();
  },
  render: function() {
    return (<div>Hello {this.state.number}</div>);
  }
});

describe("HelloWorld", function(){
  it("can spy on a function for a React class", function(){
    jasmineReact.spyOnClass(HelloWorld, "randomNumber").andReturn(42);

    // jasmineReact wraps React.render, so you don't have to worry
    //  about the async nature of when the actual DOM get's rendered, or selecting
    //  where your component needs to get rendered (default is #jasmine_content)
    var myWorld = jasmineReact.render(<HelloWorld />);

    expect(myWorld.state.number).toBe(42);
  });

  it("can assert that a spy has been called", function(){
    jasmineReact.spyOnClass(HelloWorld, "randomNumber");

    jasmineReact.render(<HelloWorld />;

    // because we spy on the class and not the instance, we have to assert that the
    //   function on the class' prototype was called.
    expect(jasmineReact.classPrototype(HelloWorld).randomNumber).toHaveBeenCalled();
  });
});

Replacing a component's subcomponent with a test double

This is very helpful for isolating your component tests to just that component, and avoiding testing its subcomponents.

/** @jsx React.DOM */
window.Avatar = React.createClass({
  render: function() {
    return (
      <div>
        <Profile username="Zuck" ref="pic" />
      </div>
    );
  }
});

window.Profile = React.createClass({
  render: function(){
    throw("I like to blow up");
  }
});

describe("Avatar", function(){

  it("should spy on a subcomponent and use a test double component", function(){
   jasmineReact.createStubComponent(window, "Profile");

   // This line won't throw the "I like to blow up" error because we've replaced the class with a test double!
   var avatar = jasmineReact.render(<Avatar />);

   expect(avatar.refs.pic.props.username).toBe("Zuck")
  });

});

Adding a method to a component class

This is needed to make a test double implement an interface which the component under test requires.

/** @jsx React.DOM */
window.Avatar = React.createClass({
  render: function() {
    return (
      <div>
        <Profile username="Zuck" ref="pic" />
      </div>
    );
  },

  rotateProfile: function(){
    this.refs.pic.rotate();
  }
});

describe("Avatar", function(){
  describe("rotateProfile", function(){
    it("should call 'rotate' on the Profile subcomponent", function(){
      var profileClassStub = jasmineReact.createStubComponent(window, "Profile");

      // We could also do: jasmineReact.addMethodToClass(window.Profile, "rotate", function(){});
      jasmineReact.addMethodToClass(profileClassStub, "rotate", function(){});
      jasmineReact.spyOnClass(profileClassStub, "rotate");

      var avatar = jasmineReact.render(<Avatar />);

      expect(jasmineReact.classPrototype(profileClassStub).rotate).not.toHaveBeenCalled();
      avatar.rotateProfile();
      expect(jasmineReact.classPrototype(profileClassStub).rotate).toHaveBeenCalled();
    });
  });
});

API

jasmineReact.render

jasmineReact.render(component, [container], [callback]);

When rendering a React component, this is a convenience function for React.render.

It has a few helpful features:

  • the component is actually rendered to an attached DOM node (unlike `React.addons.TestUtils.renderIntoDocument which renders into a detached DOM node).
  • the component will be automatically unmounted after the test is complete. NOTE: If you call React.render in a jasmine test and the component is not unmounted, that component will pollute any subsequent tests which try to render into that container.
  • the container argument is optional. By default it will be: document.getElementById("jasmine_content"). If you want to override this behavior, look at the documentation for jasmineReact.getDefaultContainer`
  • React.render will return before the rendering has occurred. jasmineReact.render will wait until the async render has been performed.

Just like React.render, this method will return the component instance.

jasmineReact.spyOnClass

jasmineReact.spyOnClass(componentClass, functionName);

When you want to render a component and stub on a function for that component, you need to spyOn the function before the instance has been created because important functions (default props/state, render) happen during initialization. This means you need to spyOn the component class, not the component instance.

This function performs the following:

  • uses the vanilla jasmine.spyOn to spy on the component class prototype
  • React does some performance tricks for autobinding functions, so this function will abstract those away from you
  • returns a regular jasmine spy object, so you can chain additional spy functions onto it. For example: jasmineReact.spyOnClass(Avatar, "getWidth").andCallFake(function(){ return 120; });

jasmineReact.classPrototype

jasmineReact.classPrototype(componentClass)

After you've spied on a component class using jasmineReact.spyOnClass, you will need to assert things on that component class. This function returns you the object you want to make your assertions against.

jasmineReact.spyOnClass(Avatar, "getWidth");

var myAvatar = jasmineReact.render(<Avatar />);
myAvatar.getWidth();

expect(jasmineReact.classPrototype(Avatar).getWidth).toHaveBeenCalled();

// NOTE: your jasmine-fu will want todo this, but you can't:
// expect(myAvatar.getWidth).toHaveBeenCalled(); <-- DON'T DO THIS

jasmineReact.createStubComponent

jasmineReact.createStubComponent(namespace, className)

React components are intended to be composable (using one component inside a render function of another component). While this is great for code reuse, it makes isolating one component for a unit test slightly more difficult.

Aside: Why do I want to isolate the component I'm testing from its subcomponents? In a unit test, when you test one component you do want to have to test the behavior of a subcomponent, because that would turn into an integration test.

What you want todo is replace any subcomponent's real definition with a "test double". By default this stub component has only the miniumum behavior to be a valid React component: a render function which returns a dom node.

If you want to add behavior to this stubComponent, so it confirms to the interface of the real component class, use the jasmineReact.addMethodToClass function.

Let's say you have an avatar class named ProfilePic which is defined on the global namespace, window. To replace window.ProfilePic with a stub component (for the life of the jasmine test), you would do:

jasmineReact.createStubComponent(window, "ProfilePic");

jasmineReact.unmountComponent

jasmineReact.unmountComponent(component);

This function makes it easy to unmount a component, given just the component instance. Unmounting a component is needed to test componentWillUnmount behavior.

var myAvatar = jasmineReact.render(<Avatar />);
jasmineReact.unmountComponent(myAvatar);

jasmineReact.getDefaultContainer

The default container for jasmineReact is document.getElementById("jasmine_content").

If your jasmine test page uses #spec-dom as its default dom node, then you'd want to define the following:

jasmineReact.getDefaultContainer = function(){
  return document.getElementById("spec-dom");
};

Installation

npm install jasmine-react-helpers --save-dev

Bower: TODO

Script Tag: TODO

Testing

Install node, npm, and grunt.

To run all of the tests (Chrome, Firefox, PhantomJS) with autoWatch:

grunt karma

To run the tests once with PhantomJS:

grunt karma:unit

TODO

  • Add the following grunt tasks: minification, linting
  • Make the project node compatible (https://blog.codecentric.de/en/2014/02/cross-platform-javascript/)
  • Create a module on npm and bower
  • Add the test suite to Travis CI