I have simple component in React. I want to test method in this component when user click button. I have test for that but finally don't pass.
Component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
class TestInvokingMethod extends Component {
onClick() {
}
render() {
return (
<div>
<input id='buttonTest' type='button' value={10} onClick={this.onClick} />
</div>
);
}
}
export default TestInvokingMethod;
And test for that:
import React from 'react';
import { shallow } from 'enzyme';
import TestInvokingMethod from '../../components/TestComponent/TestInvokeMethod';
const component = shallow(
<TestInvokingMethod />
);
test('Testing invoke method', () => {
const mockFn = jest.fn();
component.instance().onClick = mockFn;
component.update();
component.find('#buttonTest').simulate('click');
expect(component.instance().onClick.mock.calls.length).toBe(1);
});
Try using Jest's SpyOn
const spy = expect.spyOn(wrapper.instance(), "onClick");
wrapper.update();
wrapper.find('#buttonTest').simulate('click');
expect(spy).toHaveBeenCalled();
In addition to Garry's answer. In scenarios where wrapper.update() does not work, try updating its instance forcefully using wrapper.instance().forceUpdate().
Related
I need a switch component to have access to route params. The switch is rendered in one of the routes but its also rendered outside of it. Is there a way to get the same params in the component rendered outside of the route? Thanks for the help in advance!
It's usually a good pattern to not directly pass params through the route and keep those simple with the view component. You can use useContext, and then have each component(route) plug into that state using the useContext hook in the component.
for example...
app.js
import { useState } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { Routes } from "./auth/routes.js";
import { GlobalContext } from './globals/GlobalContext.js';
const App = () => {
// variables
const [someState, setSomeState] = useState('hello world');
// render
return (
<div>
<GlobalContext.Provider value={{someState, setSomeState}}>
<Router children={Routes} basename={process.env.REACT_APP_PUBLIC_URL} />
</GlobalContext.Provider>
</div>
);
}
GlobalContext.js
import { createContext } from 'react';
export const GlobalContext = createContext("");
routes.js
import { Route, Switch } from "react-router-dom";
// views
import ViewOne from '../views/ViewOne.js';
import ViewTwo from '../views/ViewTwo.js';
// globals
import { frontendLinks } from '../globals/index.js';
export const Routes = (
<Switch>
<Route exact path={frontendLinks.viewOne} component={ViewOne}></Route>
<Route exact path={frontendLinks.viewTwo} component={ViewTwo}></Route>
</Switch>
);
now the views...
import { useContext } from 'react';
// globals
import { GlobalContext } from '../globals/GlobalContext.js';
const ViewOne = () => {
const { someState } = useContext(GlobalContext);
return (
<div>
<h1>{someState}<h1>
</div>
)
}
export default ViewOne;
and
import { useContext } from 'react';
// globals
import { GlobalContext } from '../globals/GlobalContext.js';
const ViewTwo = () => {
const { someState } = useContext(GlobalContext);
return (
<div>
<h1>{someState}<h1>
</div>
)
}
export default ViewTwo;
If you don't want to manage shared state in your app.js file, I suggest you check out this video for managing useContext state in different files > https://www.youtube.com/watch?v=52W__dKdNnU
Trying to start with a super simple react-native-router-flux example using 4.0.0-beta.28. I receive the dreaded red screen that the Action is undefined. I am guessing I have something semantically incorrect?
Here's my code:
import React, {Component} from 'react'
import {Scene,Router} from 'react-native-router-flux';
import LoginScreen from '../shoppinglist/screens/login'
import LandingScreen from '../shoppinglist/screens/landing'
import {
Platform,
AppRegistry
} from 'react-native';
const MyApp = () => {
return (
<Router>
<Scene key={"root"}>
<Scene key="login" component={LoginScreen} title="Login">
</Scene>
<Scene key="home" component={LandingScreen} title="Home" initial></Scene>
</Scene>
</Router>
)
}
AppRegistry.registerComponent('shoppinglist', () => MyApp)
Code that triggers Action:
<Button onPress={() => Actions.home()} title={'Navigate to Login'} />
Simulator error
You're missing Actions from your import directive:
import { Actions, Scene, Router } from 'react-native-router-flux';
Is there a way to use the ES6 extend feature with the React-Router "withRouter" component?
Something like this:
import { withRouter } from 'react-router';
export default class extends withRouter {
...
//Use react router history prop to navigate back a page.
handleSomeEvent() {
this.props.router.goBack();
}
...
}
Or am I stuck using the old composition pattern?
var MyComponent = React.createClass({
...
});
export default withRouter(MyComponent);
Yes, it's easy, see bellow (not saying you should redirect 2s after component was mounted... just an example).
BTW my version of react-router is 2.6 (2.4+ required for withRouter)
import React, { Component } from 'react';
import { withRouter } from 'react-router';
class MyComponent extends Component {
componentDidMount() {
setTimeout(() => {
this.props.router.push('my-url')
}, 2000);
}
render() {
return (
<div>My Component</div>
);
}
}
export default withRouter(MyComponent);
You could use it like this.
#withRouter
export default class extends withRouter {
...
//Use react router history prop to navigate back a page.
handleSomeEvent() {
this.props.router.goBack();
}
...
}
But you would have to include babel-plugin-transform-decorators-legacy
The use of RouteHanlder gives two errors:
VM2805 bundle.js:9597Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).
Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
The structure of my application.
src
-- components
-- -- App.jsx
-- -- LengthModule.jsx
-- index.jsx
-- routes.js
My routes.js file
var React = require('react');
var Router = require('react-router');
var DefaultRoute = Router.DefaultRoute;
var Route = Router.Route;
var routes = (
<Route name="app" path="/" handler={require('./components/app.jsx')}>
<DefaultRoute handler={require('./components/LengthModule.jsx')} />
</Route>
)
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.jsx';
ReactDOM.render(<div><App /></div>, document.getElementById('app'));
App.jsx
import React from 'react';
import { Router, RouteHandler } from 'react-router';
export class App extends React.Component {
render () {
return <div>
<RouteHandler />
</div>;
}
}
LengthModule.jsx
import React from 'react';
import Router from 'react-router';
export class LengthModule extends React.Component {
render () {
return <div>"Hello World"</div>;
}
}
Am I using RouteHandler correctly? What am I missing? Are there any alternatives?
App.jsx
import React from 'react';
import { Router, RouteHandler } from 'react-router';
export default class App extends React.Component {
render () {
return <div>
<RouteHandler />
</div>;
}
}
LengthModule.jsx
import React from 'react';
import Router from 'react-router';
export defaulf class LengthModule extends React.Component {
render () {
return <div>"Hello World"</div>;
}
}
Why es6 react component works only with "export default"?
Newer tutorials warn: Be Careful About Deprecated Syntax. This article specifically mentions "<RouteHandler /> is Deprecated."
import React from 'react';
import { Router, Link, Navigation } from 'react-router';
export default class ResourceCard extends React.Component {
render() {
return (
<div onClick={this.routeHandler.bind(this)}>
LINK
</div>
);
}
routeHandler(){
this.transitionTo('someRoute', {objectId: 'asdf'})
}
}
I can't get it, what's wrong?
I'm receiving an error:
Uncaught TypeError: this.transitionTo is not a function
I've tried everything I've find in docs or in gitHub issues:
this.transitionTo('someRoute', {objectId: 'asdf'})
this.context.transitionTo('someRoute', {objectId: 'asdf'})
this.context.route.transitionTo('someRoute', {objectId: 'asdf'})
etc.
the route and the param is correct, it works fine in this case:
<Link to="'someRoute" params={{objectId: 'asdf}}
p.s. react-router, react and other libraries is up to date
The Navigation component is a Mixin and needs to be added to the component accordingly. If you want to bypass the Mixin (which I feel is the direction React-Router is going) you need to set the contextTypes on the component like so:
var ResourceCard = React.createClass({
contextTypes: {
router: React.PropTypes.func
}, ...
then you can call this.context.router.transitionTo.
This works with react 0.14.2 and react-router 1.0.3
import React from 'react';
import { Router, Link } from 'react-router';
export default class ResourceCard extends React.Component {
constructor(props,) {
super(props);
}
render() {
return (
<div onClick={this.routeHandler.bind(this)}>
LINK
</div>
);
}
routeHandler(){
this.props.history.pushState(null, '/');
}
}
As there's no mixin support for ES6 as of now , you need to change a few things to make it work .router is an opt-in context type so you will have to explicitly define contextTypes of the class . Then in your constructor You will have to pass context and props to super class. And while calling transitionTo you'll have to use this.context.router.transitionTo . and you don't need to import Navigation.
import React from 'react';
import { Router, Link } from 'react-router';
export default class ResourceCard extends React.Component {
constructor(props, context) {
super(props, context);
}
render() {
return (
<div onClick={this.routeHandler.bind(this)}>
LINK
</div>
);
}
routeHandler(){
this.context.router.transitionTo('someRoute', {objectId: 'asdf'})
}
}
ResourceCard.contextTypes = {
router: function contextType() {
return React.PropTypes.func.isRequired;
}
};