web-dev-qa-db-fra.com

Comment se moquer du contexte Reactor-Routeur

J'ai un composant react assez simple (Link wrapper qui ajoute une classe 'active' si la route est active):

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

const NavLink = (props, context) => {
  const isActive = context.router.isActive(props.to, true);
  const activeClass = isActive ? 'active' : '';

  return (
    <li className={activeClass}>
      <Link {...props}>{props.children}</Link>
    </li>
  );
}

NavLink.contextTypes = {
  router: PropTypes.object,
};

NavLink.propTypes = {
  children: PropTypes.node,
  to: PropTypes.string,
};

export default NavLink;

Comment suis-je censé le tester? Ma seule tentative a été:

import NavLink from '../index';

import expect from 'expect';
import { mount } from 'enzyme';
import React from 'react';

describe('<NavLink />', () => {
  it('should add active class', () => {
    const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } });
    expect(renderedComponent.hasClass('active')).toEqual(true);
  });
});

Cela ne fonctionne pas et renvoie TypeError: Cannot read property 'isActive' of undefined. Il a certainement besoin de se moquer du routeur, mais je ne sais pas comment l'écrire.

27
jmarceli

Merci @Elon Szopos pour votre réponse mais je parviens à écrire quelque chose de beaucoup plus simple (suivant https://github.com/airbnb/enzyme/pull/62 ):

import NavLink from '../index';

import expect from 'expect';
import { shallow } from 'enzyme';
import React from 'react';

describe('<NavLink />', () => {
  it('should add active class', () => {
    const context = { router: { isActive: (a, b) => true } };
    const renderedComponent = shallow(<NavLink to="/home" />, { context });
    expect(renderedComponent.hasClass('active')).toEqual(true);
  });
});

Je dois changer mount en shallow afin de ne pas évaluer Link ce qui me donne une erreur liée au react-router TypeError: router.createHref is not a function.

Je préférerais avoir un "vrai" routeur réactif plutôt qu'un simple objet mais je ne sais pas comment le créer.

21
jmarceli

Pour react router v4, vous pouvez utiliser un <MemoryRouter>. Exemple avec AVA et Enzyme:

import React from 'react';
import PropTypes from 'prop-types';
import test from 'ava';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { MemoryRouter as Router } from 'react-router-dom';

const mountWithRouter = node => mount(<Router>{node}</Router>);

test('submits form directly', t => {
  const onSubmit = sinon.spy();
  const wrapper = mountWithRouter(<LogInForm onSubmit={onSubmit} />);
  const form = wrapper.find('form');
  form.simulate('submit');

  t.true(onSubmit.calledOnce);
});
17
asdf

Tester des composants qui dépendent du contexte peut être un peu délicat. Ce que j'ai fait, c'est d'écrire un wrapper que j'ai utilisé dans mes tests.

Vous pouvez trouver le wrapper ci-dessous:

import React, { PropTypes } from 'react'

export default class WithContext extends React.Component {
  static propTypes = {
    children: PropTypes.any,
    context: PropTypes.object
  }

  validateChildren () {
    if (this.props.children === undefined) {
      throw new Error('No child components were passed into WithContext')
    }
    if (this.props.children.length > 1) {
      throw new Error('You can only pass one child component into WithContext')
    }
  }

  render () {
    class WithContext extends React.Component {
      getChildContext () {
        return this.props.context
      }

      render () {
        return this.props.children
      }
    }

    const context = this.props.context

    WithContext.childContextTypes = {}

    for (let propertyName in context) {
      WithContext.childContextTypes[propertyName] = PropTypes.any
    }

    this.validateChildren()

    return (
      <WithContext context={this.props.context}>
        {this.props.children}
      </WithContext>
    )
  }
}

Ici vous pouvez voir un exemple d'utilisation:

  <WithContext context={{ location: {pathname: '/Michael/Jackson/lives' }}}>
    <MoonwalkComponent />
  </WithContext>

  <WithContext context={{ router: { isActive: true }}}>
    <YourTestComponent />
  </WithContext>

Et cela devrait fonctionner comme prévu.

6
Elod Szopos

Vous pouvez utiliser https://github.com/pshrmn/react-router-test-context dans ce but précis

"Créer un pseudo-objet contextuel qui duplique React La structure context.router du routeur. Ceci est utile pour les tests unitaires peu profonds avec Enzyme."

Après l'avoir installé, vous pourrez faire quelque chose comme

describe('my test', () => {
  it('renders', () => {
    const context = createRouterContext()
    const wrapper = shallow(<MyComponent />, { context })
  })
})
3
katzmopolitan