React Enzyme Jest error jest.fn() should be called - javascript

My component is as below
import React from 'react';
import { connect } from 'react-redux';
import { Button } from 'react-bootstrap';
import UserActions from '../../../actions/sampleUserAction';
import UserForm from '../../../views/sample/userForm';
import UsersList from '../../../views/sample/usersList';
#connect(store => ({
users: store.sampleUserReducer.users,
}))
export default class UserComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
displayForm: false,
user: { id: '', fName: '', lName: '' },
isCreationMode: true,
};
this.addNewUser = this.addNewUser.bind(this);
this.handleChange = this.handleChange.bind(this);
this.submitForm = this.submitForm.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
addNewUser() {
this.setState({
displayForm: !this.state.displayForm,
isCreationMode: true,
user: { id: '', fName: '', lName: '' },
});
}
createUser(users) {
users.push({
id: users.length + 1,
fName: this.state.user.fName,
lName: this.state.user.lName,
});
return users;
}
updateUser(users) {
users.forEach((user) => {
if (user.id === this.state.user.id) {
user.fName = this.state.user.fName;
user.lName = this.state.user.lName;
}
});
return users;
}
submitForm(e) {
e.preventDefault();
let { users } = this.props;
if (this.state.isCreationMode) {
users = this.createUser(users);
} else if (!this.state.isCreationMode) {
users = this.updateUser(users);
}
this.addNewUser();
this.props.dispatch(UserActions.listUsers(users));
}
handleChange(e) {
const { id } = this.state.user;
let { fName, lName } = this.state.user;
if (e.target.name === 'fName') {
fName = e.target.value;
}
if (e.target.name === 'lName') {
lName = e.target.value;
}
this.setState({ user: { id, fName, lName } });
}
editUser(e, id) {
const { users } = this.props;
let user = users.filter(obj => obj.id === id);
user = user.length > 0 ? user[0] : null;
if (user != null) {
this.setState({
displayForm: true,
isCreationMode: false,
user: { id: user.id, fName: user.fName, lName: user.lName },
});
}
}
deleteUser(e, id) {
let { users } = this.props;
users = users.filter(user => user.id !== id);
this.props.dispatch(UserActions.listUsers(users));
}
render() {
console.log(this.state.displayForm);
return (
<div className="container-fluid">
<div className="well">
Sample Users App With Redux
</div>
<UserForm
displayForm={this.state.displayForm}
isCreationMode={this.state.isCreationMode}
submitForm={this.submitForm}
handleChange={this.handleChange}
user={this.state.user}
addNewUser={this.addNewUser}
/>
<UsersList
users={this.props.users}
editUser={this.editUser}
deleteUser={this.deleteUser}
/>
<div className="clearfix">
<Button bsStyle="primary" onClick={this.addNewUser}>Add User</Button>
</div>
</div>
);
}
}
and test file is as below
import React from 'react';
import { createMockStore } from 'redux-test-utils';
import { shallowWithStore } from 'enzyme-redux';
import { Button } from 'react-bootstrap';
import UserComponent from '../../../../src/components/containers/sample/userComponent';
import UserForm from '../../../../src/views/sample/userForm';
import UsersList from '../../../../src/views/sample/usersList';
describe('UsersComponent', () => {
let store;
let container;
const props = {
submitForm: jest.fn(),
addNewUser: jest.fn(),
};
beforeEach(() => {
const defaultState = { sampleUserReducer: { users: [] } };
store = createMockStore(defaultState);
container = shallowWithStore(<UserComponent />, store);
});
it('should work', () => {
expect(true).toEqual(true);
});
it('container should have UserForm component', () => {
expect(container.dive().find(UserForm)).toHaveLength(1);
});
it('container should have UsersList component', () => {
expect(container.dive().find(UsersList)).toHaveLength(1);
});
it('should have add new user button', () => {
expect(container.dive().find(Button)).toHaveLength(1);
expect(container.dive().find(Button).dive().text()).toEqual('Add User');
});
it('On click add user button', () => {
container.dive().find(Button).simulate('click');
expect(props.addNewUser).toHaveBeenCalled();
});
});
I'm using jest, enzyme, enzyme-redux. I'm new to react unit testing. Last test case is giving error as below. React version is 16.x. In last test case I'm trying to call mocked jest function on button click. For button using react-bootstrap inbuilt Button component
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called.

You are likely to need to add container.update(); which forces a re-render after external inputs like clicking.
http://airbnb.io/enzyme/docs/api/ShallowWrapper/update.html

Sometimes container.update() does not work and in such cases, try container.instance().forceUpdate() in your tests after the click which updates the component after the state changes.

Another option would be to use jest's spy to assert that addNewUser was called.
const spy = jest.spyOn(container.instance(), 'addNewUser');
container.dive().find(Button).simulate('click');
expect(spy).toBeCalled();

Related

Saving state to localStorage [duplicate]

I have no idea How to store the react js state into localstorage.
import React, { Component } from 'react'
import './App.css';
import { auth,createUserProfileDocument } from './firebase/firebase.utils'
import { TodoForm } from './components/TodoForm/TodoForm.component'
import {TodoList} from './components/TodoList/TodoList.component'
import {Footer} from './components/footer/footer.component'
import Header from '../src/components/header/header.component'
import {Redirect} from 'react-router-dom'
import {connect} from 'react-redux'
import {setCurrentUser} from './redux/user/user.actions'
export class App extends Component {
constructor(props) {
super(props)
this.input=React.createRef()
this.state = {
todos:[
{id:0, content:'Welcome Sir!',isCompleted:null},
]
}
}
todoDelete = (id) =>{
const todos = this.state.todos.filter(todo => {
return todo.id !== id
})
this.setState({
todos
})
}
toDoComplete = (id,isCompleted) =>{
console.log(isCompleted)
var todos = [...this.state.todos];
var index = todos.findIndex(obj => obj.id === id);
todos[index].isCompleted = !isCompleted;
this.setState({todos});
console.log(isCompleted)
}
addTODO = (todo) =>{
todo.id = Math.random()
todo.isCompleted = true
let todos = [...this.state.todos, todo]
this.setState({
todos
})
}
unsubscribeFromAuth = null;
componentDidMount() {
const { setCurrentUser } = this.props;
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
setCurrentUser({
id: snapShot.id,
...snapShot.data()
});
});
}
setCurrentUser(userAuth);
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return (
<div className='App'>
<Header />
<TodoForm addTODO={this.addTODO} />
<TodoList
todos={this.state.todos}
todoDelete={ this.todoDelete}
toDoComplete={ this.toDoComplete}
/>
<Footer/>
</div>
)
}
}
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser
});
const mapDispatchToProps = dispatch => ({
setCurrentUser: user => dispatch(setCurrentUser(user))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
in my input Form
import './TodoForm.style.css'
export class TodoForm extends Component {
constructor(props) {
super(props)
this.state = {
content : ''
}
}
handleChange = (e) =>{
this.setState({
content: e.target.value
})
}
handleSubmit =(e) =>{
e.preventDefault();
this.props.addTODO(this.state);
this.setState({
content: ''
})
}
render() {
return (
<div className='inputTask'>
<form onSubmit={ this.handleSubmit}>
<input
className="textBox"
type='text'
onChange={ this.handleChange}
value={this.state.content}
placeholder='what you want to do ...'
/>
</form>
</div>
)
}
}
export default TodoForm
I have no idea How to store the react js state into localstorage.
i searched on internet but unable to find the exact solution all the codes that i think is necessary post.
You can use reactLocalStorage to save any data in local storage
import {reactLocalStorage} from 'reactjs-localstorage';
reactLocalStorage.set('var', true);
reactLocalStorage.get('var', true);
reactLocalStorage.setObject('var', {'test': 'test'});
reactLocalStorage.getObject('var');
reactLocalStorage.remove('var');
reactLocalStorage.clear();
Read out the localStorage item in the componentDidMount callback. Simply read the item you want to get, check if it exists and parse it to a usable object, array or datatype that need. Then set the state with the results gotten from the storage.
And to store it, simply handle it in an event handler or helper method to update both the state and the localStorage item.
class ExampleComponent extends Component {
constructor() {
super();
this.state = {
something: {
foo: 'bar'
}
}
}
componentDidMount() {
const storedState = localStorage.getItem('state');
if (storedState !== null) {
const parsedState = JSON.parse(storedState);
this.setState({ something: parsedState });
}
}
clickHandler = (event) => {
const value = event.target.value;
const stringifiedValue = JSON.stringify(value);
localStorage.setItem('state', stringifiedValue);
this.setState({ something: value });
}
render() {
return (
<button onClick={clickHandler} value={this.state.something}>Click me</button>
);
}
}
Set data in localStorage
key-value pair :
localStorage.setItem('key_name',"value");
object
localStorage.setItem('key_name', JSON.stringify(object));
Remove data from localStorage
localStorage.removeItem('key_name');
Get data from localStorage
let data = localStorage.getItem('key_name');
object :
let data = JSON.parse(localStorage.getItem('key_name'));
clear localStorage (delete all data)
localStorage.clear();

Got an error while setting the 'state' (REACT)

I was doing a small project just to learn more about React when I got this error:
" Line 19.5: 'state' is not defined no-undef ".
I don't know what is wrong...
I used the same structure in another project and I didn't get an error.
import React, { Component } from 'react'
import axios from 'axios'
import Main from '../template/Main'
const headerProps = {
icon: 'users',
title: 'Usuários',
subtitle: 'Cadastro de usuários: Incluir, Listar, Alterar e Excluir'
}
const baseUrl = 'http://localhost:3001/users'
const initalState = {
user: { name: '', email: '' },
list: []
}
export default class UserCrud extends Component {
state = { ...initalState }
clear() {
this.setState({ user: initalState.user })
}
save() {
const user = this.state.user
const method = user.id ? 'put' : 'post'
const url = user.id ? `${baseUrl}/${user.id}` : baseUrl
axios[method](url, user)
.then(resp => {
const list = this.getUpdatedList(resp.data)
this.setState({ user: initalState.user, list })
})
}
getUpdatedList(user) {
const list = this.state.list.filter(u => u.id !== user.id)
list.unshift(user)
return list
}
render() {
return (
<Main {...headerProps}>
Cadastro de Usuários
</Main>
)
}
}
Error message
Declare state inside constructor method using this.
export default class UserCrud extends Component {
constructor(props) {
super(props);
this.state = { ...initalState };
}
clear() {
this.setState({ user: initalState.user })
}
save() {
const user = this.state.user
const method = user.id ? 'put' : 'post'
const url = user.id ? `${baseUrl}/${user.id}` : baseUrl
axios[method](url, user)
.then(resp => {
const list = this.getUpdatedList(resp.data)
this.setState({ user: initalState.user, list })
})
}
getUpdatedList(user) {
const list = this.state.list.filter(u => u.id !== user.id)
list.unshift(user)
return list
}
render() {
return (
<Main {...headerProps}>
Cadastro de Usuários
</Main>
)
}
}

React native : How can I turn the class component code into a function component with hooks?

How can I turn the following code into a function component with hooks?
In my example I use a class component
And I want to change the code to a function component form
export default class Modal2 extends Component {
state = {
placeName: "",
errorMsg: null
};
placeNameChangedHandler = val => {
this.setState({
placeName: val,
errorMsg: null
});
};
onConfirm = () => {
const { placeName } = this.state;
const { onConfirm, onHideModal } = this.props;
if (placeName.trim().length > 5) {
onConfirm("Cancel", placeName);
onHideModal();
this.setState({ placeName: "", errorMsg: null })
} else {
this.setState({ errorMsg: "must 5 letters" });
}
};
}
That's how it should look after converting it.
import React, { useState } from 'react';
function Modal2(props) {
const [desiredStateName, setDesiredStateName] = useState({
placeName: "",
errorMsg: null
});
placeNameChangedHandler = val => {
setDesiredStateName({
placeName: val,
errorMsg: null
});
};
onConfirm = () => {
const { placeName } = desiredStateName.placeName;
const { onConfirm, onHideModal } = props;
if (placeName.trim().length > 5) {
onConfirm("Cancel", placeName);
onHideModal();
setDesiredStateName({ placeName: "", errorMsg: null })
} else {
setDesiredStateName((prevState)=>{
return{ ...prevState,errorMsg: "must 5 letters" }
})
}
};
}
export default Modal2;
Also, a quick guide of how you are able to do it by yourself
Have a try by replacing your code with the below code:
import React, { useState } from 'react';
export default Modal2 = props => {
const [placeName, setPlaceName] = useState("")
const [errorMsg, setErrorMsg] = useState(null)
placeNameChangedHandler = val => {
setPlaceName(val)
setErrorMsg(null)
};
onConfirm = () => {
const { onConfirm, onHideModal } = props;
if (placeName.trim().length > 5) {
onConfirm("Cancel", placeName);
onHideModal();
setPlaceName("")
setErrorMsg(null)
} else {
setErrorMsg("must 5 letters")
}
};
}

How to make live search on this name react js?

I am trying to make live search for name in table but i can't make live search i don't know how to do this i wrote my code like this as i mentioned please help me how to make live search on name field foe table and in Search Page i used onSubmit={this.props.loaddata like this thanks
import React, { Component } from "react";
import Search from "../../views/Cars/Search";
class Search1 extends Component {
constructor(props) {
super(props);
this.state = {
query: []
};
}
// Get Data from filter date
getData = async e => {
try {
const search = e.target.elements.search.value;
e.preventDefault();
const res = await fetch(`https://swapi.co/api/people/?search=${search}`);
const query = await res.json();
console.log(query);
this.setState({
query: query.results
});
} catch (e) {
console.log(e);
}
};
async componentDidMount() {
// let authToken = localStorage.getItem("Token");
try {
const res = await fetch(`https://swapi.co/api/people/`);
const query = await res.json();
// console.log(movie);
this.setState({
query: query.results
});
} catch (e) {
console.log(e);
}
}
render() {
const options = this.state.query.map(r => <li key={r.id}>{r.name}</li>);
return (
<div>
<Search loaddata={this.getData} />
{options}
</div>
);
}
}
export default Search1;
Genrally You can try React-Search
import Search from 'react-search'
import ReactDOM from 'react-dom'
import React, { Component, PropTypes } from 'react'
class TestComponent extends Component {
HiItems(items) {
console.log(items)
}
render () {
let items = [
{ id: 0, value: 'ruby' },
{ id: 1, value: 'javascript' },
{ id: 2, value: 'lua' },
{ id: 3, value: 'go' },
{ id: 4, value: 'julia' }
]
return (
<div>
<Search items={items} />
<Search items={items}
placeholder='Pick your language'
maxSelected={3}
multiple={true}
onItemsChanged={this.HiItems.bind(this)} />
</div>
)
}
}
Made few changes to your component. Send e.target.value from your child component
class Search1 extends Component {
constructor(props) {
super(props);
this.state = {
query: []
};
}
// Get Data from filter date
getData = search => {
const url = `https://swapi.co/api/people${search ? `/?search=${search}` : ``}`;
// e.preventDefault();
fetch(url)
.then(res => res.json())
.then(data =>
this.setState({
query: data.results || []
})).catch(e => console.log(e));
};
async componentDidMount() {
// let authToken = localStorage.getItem("Token");
this.getData();
}
render() {
const options = this.state.query.map(r => <li key={r.id}>{r.name}</li>);
return (
<div>
<Search loaddata={this.getData} />
{options}
</div>
);
}
}
export default Search1;
For Gettind Data from Api you can follow this code of react-search
import Search from 'react-search'
import ReactDOM from 'react-dom'
import React, { Component, PropTypes } from 'react'
class TestComponent extends Component {
constructor (props) {
super(props)
this.state = { repos: [] }
}
getItemsAsync(searchValue, cb) {
let url = `https://api.github.com/search/repositories?q=${searchValue}&language=javascript`
fetch(url).then( (response) => {
return response.json();
}).then((results) => {
if(results.items != undefined){
let items = results.items.map( (res, i) => { return { id: i, value: res.full_name } })
this.setState({ repos: items })
cb(searchValue)
}
});
}
render () {
return (
<div>
<Search items={this.state.repos}
multiple={true}
getItemsAsync={this.getItemsAsync.bind(this)}
onItemsChanged={this.HiItems.bind(this)} />
</div>
)
}

React Native: null is not an object (evaluating '_this.state.displayErrors')

I had a fully functioning Android application and now I am getting this error:
null is not an object (evaluating '_this.state.displayErrors') which is referencing this line of code here:
_getErrors = () => {
if (this.state.displayErrors) {
return {
...this.state.validationErrors,
...this.props.validationErrors
};
}
return {};
};
This is the full file:
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import DetailsConfirmationForm from "auth/components/DetailsConfirmationForm";
import {
firstNameChanged,
lastNameChanged,
prefixChanged,
suffixChanged,
stateChanged
} from "auth/registrationActions";
import regex from "utils/helpers/regex";
import { prefixes, suffixes } from "enums/dropdownOptions";
export class DetailsConfirmation extends Component {
static propTypes = {
firstName: PropTypes.string,
firstNameChanged: PropTypes.func.isRequired,
lastName: PropTypes.string,
lastNameChanged: PropTypes.func.isRequired,
navigation: PropTypes.object,
prefix: PropTypes.string,
prefixChanged: PropTypes.func.isRequired,
registeredUser: PropTypes.object,
state: PropTypes.string,
stateChanged: PropTypes.func.isRequired,
suffix: PropTypes.string,
suffixChanged: PropTypes.func.isRequired,
validationErrors: PropTypes.object
};
constructor(props) {
super(props);
}
componentDidMount() {
const { personalDetails, personalAddress } = this.props.registeredUser;
console.log(this.props.registeredUser);
if (personalDetails) {
this.props.firstNameChanged(personalDetails.firstName);
this.props.lastNameChanged(personalDetails.lastName);
this.props.suffixChanged(personalDetails.suffix);
this.props.prefixChanged(personalDetails.prefix);
}
if (personalAddress && personalAddress.stateCode) {
this.props.stateChanged(personalAddress.stateCode);
}
const { params = {} } = this.props.navigation.state;
const { displayAlert = true } = params;
this.state = {
validationErrors: {},
displayErrors: false,
titleName: personalDetails && personalDetails.firstName,
displayAlert
};
}
componentWillReceiveProps(nextProps) {
if (this.state.displayErrors) {
this._validate(nextProps);
}
}
_validate = props => {
const { prefix, state, firstName, lastName, suffix } = props;
const validPrefixes = prefixes.map(p => p.value);
const validSuffixes = suffixes.map(p => p.value);
const validationErrors = {
prefix:
prefix && prefix.trim() && validPrefixes.includes(prefix)
? ""
: "Is Required",
state: state && state.trim() ? "" : "Is Required",
firstName: firstName && firstName.trim() ? "" : "Is Required",
lastName: lastName && lastName.trim() ? "" : "Is Required",
suffix:
!suffix || validSuffixes.includes(suffix) ? "" : "Select an option"
};
const nameRegexErrorMessage =
"Only letters, hyphens and periods are allowed.";
if (validationErrors.firstName === "" && !regex.userName.test(firstName)) {
validationErrors.firstName = nameRegexErrorMessage;
}
if (validationErrors.lastName === "" && !regex.userName.test(lastName)) {
validationErrors.lastName = nameRegexErrorMessage;
}
const fullErrors = {
...validationErrors,
...this.props.validationErrors
};
const isValid = Object.keys(fullErrors).reduce((acc, curr) => {
if (fullErrors[curr] !== "") {
return false;
}
return acc;
}, true);
if (isValid) {
this.setState({ validationErrors: {} });
//register
} else {
this.setState({ validationErrors, displayErrors: true });
}
return isValid;
};
_navigate = () => {
const isValid = this._validate(this.props);
if (isValid) {
if (this.props.registeredUser.organization) {
this.props.navigation.navigate("CompleteAccount");
} else {
this.props.navigation.navigate("AskForMembership");
}
}
};
_getErrors = () => {
if (this.state.displayErrors) {
return {
...this.state.validationErrors,
...this.props.validationErrors
};
}
return {};
};
render() {
return (
<DetailsConfirmationForm
{...this.state}
{...this.props}
navigate={this._navigate}
validationErrors={this._getErrors()}
/>
);
}
}
const mapsStateToProps = ({ registrations }) => {
return {
...registrations.accountData,
validationErrors: registrations.validationErrors,
registeredUser: registrations.registeredUser
};
};
export default connect(
mapsStateToProps,
{
firstNameChanged,
lastNameChanged,
prefixChanged,
suffixChanged,
stateChanged
}
)(DetailsConfirmation);
Is this a scoping issue? The if statement does not have access to displayErrors outside of _getErrors function? If so, how in the world did this work for several weeks before?
I tried placing:
this.state = {
validationErrors: {},
displayErrors: false,
titleName: personalDetails && personalDetails.firstName,
displayAlert
};
inside the constructor(props) function where I believe this belongs, but then I get a ton of other problems regarding the variables in it such as personalDetails and displayAlert not being defined as variables. The biggest pain being that displayAlert.
For setting this.state in your constructor you will need to destructure props to get the values you need similar to what is happening in componentDidMount. I would remove the setting of initial state values from componentDidMount altogether.
constructor(props) {
super(props);
const { personalDetails, personalAddress } = props.registeredUser;
const { params = {} } = props.navigation.state;
const { displayAlert = true } = params;
this.state = {
validationErrors: {},
displayErrors: false,
titleName: personalDetails && personalDetails.firstName,
displayAlert
};
}
componentDidMount() {
const { personalDetails, personalAddress } = this.props.registeredUser;
console.log(this.props.registeredUser);
if (personalDetails) {
this.props.firstNameChanged(personalDetails.firstName);
this.props.lastNameChanged(personalDetails.lastName);
this.props.suffixChanged(personalDetails.suffix);
this.props.prefixChanged(personalDetails.prefix);
}
if (personalAddress && personalAddress.stateCode) {
this.props.stateChanged(personalAddress.stateCode);
}
}
I'm not sure if this will fix your problem or how this.state is getting set to null.
I think the problem is with this line in DetailsConfirmationForm component
validationErrors={this._getErrors()}
You are calling function instead of passing .
Try this
validationErrors={this._getErrors}

Categories