2016-06-30 30 views
19

reagire-router devo abbastanza semplice reagiscono componente (Link involucro che aggiunge classe 'attiva' se percorso è attivo):Come deridere contesto

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; 

Come dovrei provarlo? Il mio unico tentativo è stato:

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); 
    }); 
}); 

non funziona e restituisce TypeError: Cannot read property 'isActive' of undefined. Ha sicuramente bisogno di un po 'di derisione del router, ma non ho idea di come scriverlo.

risposta

16

Grazie @Elon Szopos per la risposta, ma riesco a scrivere qualcosa di molto più semplice (segue 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); 
    }); 
}); 

devo cambiare mount-shallow al fine di non valutare Link che mi dà un errore collegato con il router di reazione TypeError: router.createHref is not a function.

Preferirei avere un router di risposta "reale" piuttosto che un semplice oggetto, ma non ho idea di come crearlo.

+1

Si noti che le parentesi intorno al "contesto" sono passate in profondità - mi mancavano inizialmente e mi ci è voluto un po 'per capire perché non funzionava! –

5

Testare i componenti che si basano sul contesto può essere un po 'complicato. Quello che ho fatto è stato scrivere un wrapper che ho usato nei miei test.

È possibile trovare l'involucro di seguito:

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> 
    ) 
    } 
} 

Qui si può vedere un esempio dell'uso:

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

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

e dovrebbe funzionare come ci si aspetterebbe.

3

È possibile utilizzare https://github.com/pshrmn/react-router-test-context a tale scopo esatto

"Creare un oggetto contesto pseudo che i duplicati Reagire struttura context.router del router. Questo è utile per poco profonde unit testing con l'enzima".

Dopo averlo installato, si sarà in grado di fare qualcosa di simile

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

Per reagire router v4 è possibile utilizzare un <MemoryRouter>. Esempio con AVA e Enzima:

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); 
}); 
+4

Funziona finché non è necessario aggiornare gli oggetti di scena del componente spostato. Chiamare 'setProps()' funziona solo sul componente root, che ora è il 'MemoryRouter' –

+0

Suppongo che sia impossibile trovare un modo universale per testare i componenti. Tuttavia, nell'esempio sopra è possibile impostare le proprietà in linea. Come 'onSubmit'. – walteronassis

0

È necessario conoscere la differenza tra mount & shallow. Il official documentation spiegato lo Shallow Render:

Durante la scrittura di unit test per Reagire, poco profondo rendering può essere utile. Il rendering superficiale consente di eseguire il rendering di un componente "a un livello profondo" e asserisce fatti su ciò che restituisce il suo metodo di rendering, senza preoccuparsi di sul comportamento dei componenti figlio, che non sono istanziati o resi . Questo non richiede un DOM.

Poi, senza la complessità, né contesti:

const renderedComponent = shallow(<NavLink to="/home" />); 

invece di

const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } }); 

che funzionerà!