I would like to test that clicking on a link updates the components in my app.
Here's my app, when you click on about it renders the About component
import React, { Component } from 'react';
import './App.css';
import {
MemoryRouter as Router,
Route,
Link
} from 'react-router-dom'
const Home = () => <h1>home</h1>
const About = () => <h1>about</h1>
class App extends Component {
render() {
return (
<Router>
<ul>
<li><Link to="/about">About</Link></li>
<li><Link to="/">Home</Link></li>
</ul>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Router>
);
}
}
export default App;
And here's my test:
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme from 'enzyme';
import { mount } from "enzyme";
import { MemoryRouter} from 'react-router-dom'
import App from './App';
Enzyme.configure({ adapter: new Adapter() });
// this test passes as expected
it('renders intitial heading as home', () => {
const wrapper = mount(
<App />
);
const pageHeading = wrapper.find("h1").first().text()
expect(pageHeading).toEqual('home');
});
it('renders about heading when we navigate to about', () => {
const wrapper = mount(
<App />
);
const link = wrapper.find("Link").first();
link.simulate('click');
const pageHeading = wrapper.find("h1").first().text()
expect(pageHeading).toEqual('about');
});
The second test fails:
FAIL src/App.test.js
● renders about heading when we navigate to about
expect(received).toEqual(expected)
Expected value to equal:
"about"
Received:
"home"
I'm using react router v4, react 16 and enzyme 3.1
Is it possible to test in this way using React Router and enzyme?
Link's render function will check for event.button === 0. That's why check fails with enzyme if you call simulate without proper params.
Try link.simulate('click', { button: 0 });
Good luck.
Related
I have a create profile page and an auth page (where one enters a code sent by text). I'd like to navigate to the auth page, when a profile is created.
I have a component for the create profile page and one for the auth page.
Based on the example here.
Relevant code:
// CreateProfileComponent.js
import React from 'react';
..
import { withRouter } from "react-router";
class CreateProfile extends React.Component {
constructor(props) {
super(props);
}
handleCreateProfile(e) {
e.preventDefault();
if (condition) {
createProfile(...);
this.props.history.push('/auth');
} else {
// re-render
}
}
render() {
return (
// data-testid="create-profile" - workaround (https://kula.blog/posts/test_on_submit_in_react_testing_library/) for
// https://github.com/jsdom/jsdom/issues/1937
<form onSubmit={this.handleCreateProfile.bind(this)} data-testid="create-profile">
...
</form>
);
}
}
export default withRouter(CreateProfile);
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import CreateProfile from './CreateProfileComponent';
import { TokenEntry } from './TokenEntryComponent';
ReactDOM.render(
<Router>
<ProtectedRoute exact path="/" component={ActivityList} />
<Route path="/login" component={CreateProfile.WrappedComponent} />
<Route path="/auth" component={TokenEntry} />
</Router>,
document.getElementById('root')
);
//createprofilecomponent.test.js
import React from 'react';
import {
LocationProvider,
createMemorySource,
createHistory
} from '#reach/router';
import { render, screen, fireEvent } from '#testing-library/react';
import CreateProfile from '../CreateProfileComponent';
const source = createMemorySource('/login');
const history = createHistory(source);
let displayName = null;
let phoneNumber = null;
let legalAgreement = null;
global.window = { location: { pathname: null } };
function Wrapper({children}) {
return <LocationProvider history={history}>{children}</LocationProvider>;
}
beforeEach(() => {
render(
<CreateProfile.WrappedComponent location={'/'} />,
{ wrapper: Wrapper }
);
});
it("navigates to /auth when good data entered", () => {
// setup
fireEvent.submit(screen.getByTestId('create-profile'));
expect(global.window.location.pathname).toEqual('/auth');
});
I'm getting
TypeError: Cannot read property 'push' of undefined
during tests and in Chrome.
What am I missing?
Use the react-router-dom
import { withRouter } from "react-router-dom";
Changed
export default withRouter(CreateProfile);
To
export const CreateProfileWithRouter = withRouter(CreateProfile);
Changed
<Route path="/login" component={CreateProfileWithRouter.WrappedComponent} />
To
<Route path="/login" component={CreateProfileWithRouter} />
Pull request created to include an example in withRouter's documentation.
Example gist
I am trying to write an integration test for a React app created as part of a Udemy course. The app is using react-router-dom, and I want to simulate clicking on a Link, which should change the route, and then test something on the screen corresponding to the new route.
I was able to mount the App component using Enzyme, but I can't seem to navigate successfully in the test environment:
import React from 'react'
import { mount } from 'enzyme'
import { MemoryRouter } from 'react-router-dom'
import Root from 'src/Root'
import App from 'src/components/App'
it('navigates to the post screen when clicking the post button', () => {
const wrapped = mount(
// My Root component wraps the children with a Redux Provider
<Root initialState={{ auth: true }}>
<MemoryRouter>
<App />
</MemoryRouter>
</Root>
)
// Click on the post button
wrapped.find('a.post-link').simulate('click')
// Update the component, hoping that the navigation would take effect
wrapped.update()
// Expecting to find a CommentBox, which is what is rendered by the '/post' Route
// Unfortunately, it seems that the navigation either didn't occur or is still ongoing, as the test fails: there are 0 CommentBox components found.
expect(wrapped.find('CommentBox').length).toEqual(1)
})
This is my App component, for reference:
import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import { connect } from 'react-redux'
import CommentBox from 'src/components/CommentBox'
import CommentList from 'src/components/CommentList'
import * as actions from 'src/actions'
class App extends Component {
renderButton() {
const { auth, changeAuth } = this.props
return (
<button
onClick={() => changeAuth(!auth)}
className="auth-button"
>
Sign {auth ? 'out' : 'in'}
</button>
)
}
renderHeader() {
return (
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link className="post-link" to="/post">Post a comment</Link>
</li>
<li>
{this.renderButton()}
</li>
</ul>
)
}
render() {
return (
<div>
{this.renderHeader()}
<Route path="/post" component={CommentBox} />
<Route path="/" exact component={CommentList} />
</div>
)
}
}
function mapStateToProps(state) {
return { auth: state.auth }
}
export default connect(mapStateToProps, actions)(App)
I'm using React Router Dom V4, my path to my homepage works but my path to my about page doesn't work.
I'm not sure if it's anything to do with the fact I'm using Xampp or not.
Index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
import App from "./components/App";
import About from "./components/About";
const Root = () => {
return (
<Router>
<div>
<Route path="/" component={App} />
<Route path="/about" component={About} />
</div>
</Router>
)
}
render(<Root />, document.getElementById('main'));
Header.js
import React from "react";
const Header = () => {
return (
<div>
<ul>
<li>About</li>
</ul>
</div>
)
}
export default Header;
About.js
import React from "react";
const About = () => {
return (
<p>This is the about page</p>
)
}
export default About;
The problem is that you use plain html links, so React will not be called when the user clicks the link. You should use the Link Element from the router-module.
Try the following for the menu:
import { Link } from "react-router-dom"
....
<li><Link to='/about'>About</Link></li>
I'm putting together my first react based app. I had a working application and have since woven in the react router. I am transpiling with babel.
I get the following the the developer console in chrome -
Uncaught TypeError: Cannot read property 'render' of undefined
at Object.1../components/Login (http://localhost:8000/bundle.js:90:19)
at s (http://localhost:8000/bundle.js:1:254)
at e (http://localhost:8000/bundle.js:1:425)
at http://localhost:8000/bundle.js:1:443
I have the following component at components/Login.js
import React, { Component } from 'react';
class Login extends React.Component {
render() {
return (
<nav>
<ul>
<li>Sign Up Now</li>
<li><a className="button button-primary" href="#">Sign In</a></li>
</ul>
</nav>
);
}
}
export default Login
Within my app.js I have the following -
'use strict';
import React from 'react';
import { ReactDOM, render } from 'react-dom';
import { browserHistory, Router, Route, Link, withRouter } from 'react-router';
import {Login} from './components/Login';
const App = React.createClass({
render() {
return (
<div>
<h2>Title</h2>
<Options/>
</div>
)
}
})
const Logout = React.createClass({
render() {
return <p>You are now logged out</p>
}
})
const Profile = React.createClass({
render() {
//const token = auth.getToken()
return (
<div>
<h1>Profile</h1>
<p>You made it!</p>
//<p>{token}</p>
</div>
)
}
})
function requireAuth() {
console.log("here");
}
ReactDOM.render((
<Router>
<Route path="/" component={App}>
<Route path="login" component={Login} />
<Route path="logout" component={Logout} />
<Route path="profile" component={Profile} onEnter={requireAuth} />
</Route>
</Router>
), document.getElementById('app'));
I believe my error to be in the way in which I am export/ importing my components as the error exists with another component and when I change which is imported first in the above the error falls to that component.
I've tried various things such as adding {} to the import, adding default to the export and adding other imports into the component such as react-dom.
I'm building using the following
babel -presents react,es2015 js/source -d js/build
browserify js/build/app.js -o bundle.js
cat css/*/* css/*.css | sed 's/..\/..\/images/images/g' > bundle.css
date; echo;
Can anyone advise - I've been on this for 3 evenings now. I just don't get why render is undefined when it previously worked when using without the router.
Does changing the Login import line to import Login from './components/Login'; fix it?
Changing
import ReactDOM, { render } from 'react-dom';
to
import { ReactDOM, render } from 'react-dom';
Did the trick - I also hadn't set a browserhistory in the router which caused a secondary fault.
Change import {Login} from './components/Login'; to import Login from './components/Login';. Remember that when exporting something with default you don't need to include the {}.
I am trying to set up routing in Meteor using react-router package and have encountered the following TypeError:
Link to image: https://postimg.org/image/v0twphnc7/
The code in I use in main.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
// Importing components
import App from './components/app';
import Portfolio from './components/portfolio/portfolio';
//Creating a route
const routes = (
<Router history={browserHistory}>
<Route path='/' component={App}>
<Router path='portfolio' component={Portfolio} />
</Route>
</Router>
);
// Loading routes
Meteor.startup(() => {
ReactDOM.render(routes, document.querySelector('.universe'));
});
The problem that I've managed to identify is that when I define portfolio as a Simple Component it works.
const Portfolio = () => {
return (
<div className='red'>Portfolio page</div>
);
}
But when I extend it from the Component is where the error comes in:
class Portfolio extends Component () {
render() {
return (
<div>Portfolio page</div>
);
}
}
Can you please explain the possible difference between "normal" and class component and why the following error appears.
Assuming you are importing Component as a React.Component correctly, try removing the parenthesis after Component.
Should be:
class Portfolio extends Component {
instead of:
class Portfolio extends Component () {
If not, replace Componentwith React.Component.