Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing React Router with Link

I am having a nightmare finding a good solution for testing a React Router link. It is passing on the 'renders Categories properly' however zero links are being passed through to the test, I have tried so many different things and have still got nowhere.

Below is what i am trying to test:

Component

import React from 'react';
import { Link } from 'react-router';

class Categories extends React.Component {

constructor(props, context){
    super(props);
    context.router
}

render() {
    return (
        <nav className="categories">
          <ul>
              <li><Link to="devices">Devices</Link></li>
              <li><Link to="cases">Cases</Link></li>
              <li><Link to="layouts">Layouts</Link></li>
              <li><Link to="designs">Designs</Link></li>
          </ul>
        </nav>
    );
  }
}

Categories.contextTypes = {
 router: React.PropTypes.func.isRequired
};

export default Categories;

StubRouterContext

import React from 'react';
import objectAssign from 'object-assign';

var stubRouterContext = (Component, props, stubs) => {
function RouterStub() { }

objectAssign(RouterStub, {
  makePath () {},
  makeHref () {},
  transitionTo () {},
  replaceWith () {},
  goBack () {},
  getCurrentPath () {},
  getCurrentRoutes () {},
  getCurrentPathname () {},
  getCurrentParams () {},
  getCurrentQuery () {},
  isActive () {},
  getRouteAtDepth() {},
  setRouteComponentAtDepth() {}
 }, stubs)

return React.createClass({
childContextTypes: {
    router: React.PropTypes.func,
    routeDepth: React.PropTypes.number
},

getChildContext () {
    console.log('blah');
  return {
    router: RouterStub,
    routeDepth: 0
  };
},

render () {
  return <Component {...props} />
}
});
};

export default stubRouterContext;

Component Test

var expect = require('chai').expect;

var React = require('react/addons');
var Categories = require('../app/src/js/components/Categories.React.js');
var stubRouterContext = require('../test-utils/stubRouterContext.js');
var TestUtils = React.addons.TestUtils;

describe('Categories', function() {
  var categoriesWithContext = stubRouterContext(Categories);

  it('renders Categories properly', function() {
  var categories = TestUtils.renderIntoDocument(<categoriesWithContext />, {});
});

it('renders 4 links', function() {
  var catLinks = TestUtils.scryRenderedDOMComponentsWithTag(categoriesWithContext, 'a');
  expect(catLinks).to.have.length(4);
});
});
like image 823
Dan Beck Avatar asked Apr 14 '15 10:04

Dan Beck


People also ask

How do I test a link in React?

Link.test.js import React from 'react'; import renderer from 'react-test-renderer'; import { Link } from 'react-router-dom'; test('Link matches snapshot', () => { const component = renderer. create( <Link to="#" /> ); let tree = component. toJSON(); expect(tree). toMatchSnapshot(); });

How do I use router link in React?

To add the link in the menu, use the <NavLink /> component by react-router-dom . The NavLink component provides a declarative way to navigate around the application. It is similar to the Link component, except it can apply an active style to the link if it is active.

How do you test an anchor's href With React testing library?

const linkToTest = screen. getByRole("link", { name: /link to test/i }) // I used regex here as a value of name property which ignores casing expect(linkToTest. getAttribute("href")). toMatchInlineSnapshot();


2 Answers

I had exactly the same problem. In the latest versions of react-router, you do not need context to render link elements so this isn't a problem. However, if you are stuck on pre API 1.0 versions as I am, the stubRouterContext approach works well.

The only reason OP and I found that our wrapper was rendering empty is the use of a camelCase component name.

var categoriesWithContext = stubRouterContext(Categories); becomes var CategoriesWithContext = stubRouterContext(Categories);.

Therfore var categories = TestUtils.renderIntoDocument(<categoriesWithContext />,{}); becomes var categories = TestUtils.renderIntoDocument(<CategoriesWithContext />,{});.

Explanation of this approach is here - https://gist.github.com/sebmarkbage/f1f4ba40816e7d7848ad.

like image 164
Rich Avatar answered Oct 20 '22 19:10

Rich


The first thing I notice is that you're not re-rendering "categoriesWithContext" in the second test.

it('renders 4 links', function() {
  var categories = TestUtils.renderIntoDocument(<categoriesWithContext />, {});
  var catLinks = TestUtils.scryRenderedDOMComponentsWithTag(categories, 'a');
  expect(catLinks).to.have.length(4);
});

Though I haven't run your code myself, the next thing I notice is the way you're fetching the links. In a test I have, I must dig through the hierarchy manually.

Try this.

it('renders 4 links', function() {
  var categories = TestUtils.renderIntoDocument(<categoriesWithContext />, {});
  var ul = TestUtils.findRenderedDOMComponentWithTag(categories, 'ul');
  var lis = TestUtils.scryRenderedDOMComponentsWithTag(ul, 'li');
  lis.forEach(function(li) {
    // this should throw if <a/> is not found
    var a = TestUtils.findRenderedDOMComponentWithTag(li, 'a');
    // but write an explicit expectation anyway
    expect(a);
  });
});
like image 28
Jeff Fairley Avatar answered Oct 20 '22 18:10

Jeff Fairley