Material UI React Test cases failing - JEST, ENZYME - javascript

I have a connected component and have integrated MaterialUI in my component. For some reason, the tests keep on failing and I am not able to find some article online to resolve this.
Please advice.
Below is my code.
import React, {Component} from 'react';
import {connect} from 'react-redux';
import SourceCurrencyInput from './components/SourceCurrencyInput';
import TargetCurrencyInput from './components/TargetCurrencyInput';
import {fetchCurrencyConverterRates} from './actions/currencyConverterActions';
import CurrencyConverterValue from './components/CurrencyConverterValue';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import {withStyles} from '#material-ui/core/styles';
import './App.css';
import {
changeSourceCurrencyValue,
changeTargetCurrencyValue
} from './actions/actions';
const styles = {
root: {
flexGrow: 1,
},
grow: {
flexGrow: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
};
class App extends Component {
componentDidMount() {
this.props.fetchCurrencyConverterRates(
this.props.srcCurrencyType,
this.props.tgtCurrencyType
);
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (prevProps.srcCurrencyType !== this.props.srcCurrencyType
|| prevProps.tgtCurrencyType !== this.props.tgtCurrencyType) {
this.props.fetchCurrencyConverterRates(
this.props.srcCurrencyType,
this.props.tgtCurrencyType);
}
}
clearAll = () => {
this.props.sourceValue('');
this.props.targetValue('');
};
render() {
const {srcCurrencyType, tgtCurrencyType, srcCurrencyValue, tgtCurrencyValue, currencyConversionRate, classes} = this.props;
return (
<div className="App">
<AppBar position="static">
<Toolbar>
<Typography variant="h6" color="inherit" className={classes.grow}>
Currency Converter by Arun Manohar
</Typography>
</Toolbar>
</AppBar>
<header className="App-header">
<SourceCurrencyInput/>
<TargetCurrencyInput/>
<Button variant="contained" color="primary" className={classes.button}
onClick={() => this.clearAll()}>Clear</Button>
</header>
<CurrencyConverterValue srcCurrencyType={srcCurrencyType}
tgtCurrencyType={tgtCurrencyType}
srcCurrencyValue={srcCurrencyValue}
tgtCurrencyValue={tgtCurrencyValue}
currencyConversionRate={currencyConversionRate}
/>
<footer><a href={'https://currencyconverterapi.com'} target={'_blank'}>API from currencyconverterapi.com</a></footer>
</div>
);
}
}
const mapStateToProps = state => {
return {
srcCurrencyType: state.currencyConverterReducer.sourceCurrencyType,
tgtCurrencyType: state.currencyConverterReducer.targetCurrencyType,
srcCurrencyValue: state.currencyConverterReducer.sourceCurrencyValue,
tgtCurrencyValue: state.currencyConverterReducer.targetCurrencyValue,
currencyConversionRate: state.getConvertedRates.data[0]
};
};
const mapDispatchToProps = (dispatch) => ({
fetchCurrencyConverterRates: (src, tgt) => dispatch(fetchCurrencyConverterRates(src, tgt)),
sourceValue: (val) => dispatch(changeSourceCurrencyValue(val)),
targetValue: (val) => dispatch(changeTargetCurrencyValue(val)),
});
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(App));
Below is my test case.
import React from 'react';
import {Provider} from 'react-redux';
import configureStore from 'redux-mock-store';
import App from './App';
import {createMount} from '#material-ui/core/test-utils';
const mockStore = configureStore();
const initialState = {sourceCurrencyType: 'USD'};
const store = mockStore(initialState);
describe('<App />', () => {
let mount;
beforeEach(() => {
mount = createMount();
});
it('should work', () => {
const wrapper = mount(<Provider store={store}><App/></Provider>);
console.log(wrapper.debug());
});
});
This is the error I get.
TypeError: Cannot read property 'sourceCurrencyType' of undefined
I just need a way to access this component in my tests. Please help me out.

Your initial state must keep the same structure with the reducer object, such:
const initialState = {
currencyConverterReducer: {
sourceCurrencyType: 'USD',
// rest of attributes of currencyConverterReducer
}
};

Related

How to configure redux with _app.tsx?

I am getting this error /!\ You are using legacy implementaion. Please update your code: use createWrapper() and wrapper.useWrappedStore(). when I compile my application. The application works fine, but I do not know why I get the error. I tried configuring similar to other projects on google and nothing seems to work.
I tried using this solution, but had no luck. Not sure how to fix this.
Here is my _app.tsx
import type { AppProps } from 'next/app'
import { Toaster } from 'react-hot-toast';
import { useRouter } from 'next/router';
import { wrapper } from '../features/store';
import useAuth from '../hooks/useAuth';
import useDarkMode from '../hooks/useDarkMode';
import '../styles/global.css';
const App = ({ Component, pageProps }: AppProps) => {
useAuth();
useDarkMode();
const router = useRouter();
return (
<main>
<Component {...pageProps} key={router.asPath} />
<Toaster
position="bottom-center"
toastOptions={{
duration: 3000,
}}
/>
</main>
)
};
export default wrapper.withRedux(App);
here is index.tsx
import { useEffect } from "react";
import { useRouter } from "next/router";
import { useSelector } from "react-redux";
import { User } from "../domain/accounts/user";
import Loading from "../icons/Loading";
import useUser from "../hooks/useUser";
const Home = () => {
const user = useUser();
const router = useRouter();
useEffect(() => {
if (user.isAuthenticated !== null) {
if (user.isAuthenticated) router.push('/home');
if (!user.isAuthenticated) router.push('/explore')
}
}, [user.isAuthenticated]);
return (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '95vh',
}}>
<Loading height={80} width={80} />
</div>
);
}
export default Home;
here is store.js
import { configureStore } from "#reduxjs/toolkit";
import { userSlice } from "./user";
import { userPreferenceSlice } from "./userPreference";
import { createWrapper } from "next-redux-wrapper";
const makeStore = () => {
return configureStore({
reducer: {
user: userSlice.reducer,
userPreference: userPreferenceSlice.reducer,
},
});
}
export const wrapper = createWrapper(makeStore);
Did you try npm install next-redux-wrapper
Then, in _app.tsx try replaceing the wrapper.withRedux import with import { useWrappedStore } from "next-redux-wrapper";
And instead of using the wrapper.withRedux function, try using the useWrappedStore method:
const MyAppWithRedux = useWrappedStore(MyApp);
export default MyAppWithRedux;

How to export or use component state as hook

I want to use hook to get and set the value of my component WITHOUT PROPS. I import the component and it lists the data returned from the request, ok, but when I use the hook to see the data it returns empty, as if it were another instance.
Initially I used the state of the parent component, but when I needed to change some value of my component, everything would re-render as it affected the state of the parent component, so I want to isolate the state in the child component and use it freely as a hook elsewhere .
How I would like to use:
import MyComp, { useMyHook } from '../../../components/MyComp';
const OtherComp = () => {
const { data } = useMyHook();
return(
<div>
<button type="button" onClick={() => console.log(data)}>
click test
</button>
<MyComp />
</div>
);
};
export default OtherComp;
Component render
Example1
Example2
But the click button log: [ ]
Without using external components/libs like redux and etc.
My custom hook:
src/useMyHook.ts
import { useState } from 'react';
export const useMyHook = () => {
const [data, setData] = useState<any[]>([]);
const addItem = (item: unknown) => {
setData([...date, item]);
};
return { data, setData, addItem};
};
export default useMyHook;
My main component:
src/MyComp.tsx
import {useEffect} from 'react';
import useMyHook from './useMyHook';
const MyComp = () => {
const { data, setData } = useMyHook();
const req = async() => {
const {values} = await anyRequest(); // to do any request
setData(values);
};
useEffect(() => { req() },[]);
return(
<div>
{data.map((item) => <p>{item.name}</p>)}
</div>
);
};
export { useMyHook };
export default MyComp;
src/index.tsx
import MyComp, { useMyHook } from './MyComp';
export default MyComp;
export { useMyHook };
To demonstrate my comment:
import React,{useState, createContext} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
import useMyHook from './useMyHook'
import Example1 from './Example1'
import Example2 from './Example2'
import OtherComp from './OtherComp'
import {Data, DataSetter} from './types'
type DContext = {
data:Data,
setData:DataSetter
}
export const DataContext = createContext({
data:[],
setData:()=>{}
} as DContext)
export default function App() {
const [data, setData] = useState<Data>([])
return (
<View style={styles.container}>
<DataContext.Provider value={{data,setData}}>
<Example1/>
<Example2/>
<OtherComp/>
</DataContext.Provider>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
import { useContext, useCallback } from 'react';
import { DataContext } from './App';
export const useMyHook = () => {
const { data, setData } = useContext(DataContext);
// wrapped in useCallback to prevent function from
// being recreated
const addItem = useCallback((item: unknown) => {
setData(prev=>[...prev, item]);
},[]);
return { data, setData, addItem };
};
export default useMyHook;
useMyHook usage:
import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import useMyHook from './useMyHook';
export default function Example1() {
const { data, addItem } = useMyHook();
return (
<View style={{ width: '100%' }}>
<Button
title="Add Item From Example 1"
onPress={() => {
addItem(Math.floor(Math.random() * 25) + ' From Example1');
}}
/>
</View>
);
}
A demo

React Native Authentication Navigation - REDUX

I can't change the value of the state of the store using the reducer. I'm making an app which has a login-functionality. When a person opens up the app, depending on the fact if he is logged in or not, it should show the right screen. The problem I'm having right now is it doesn't seem to be able to change the store state out of another screen. Anybody who can help me?
import {createStore} from "redux";
const initialState = {
value: false
}
function reducer(state= initialState, action) {
const newState = {...state};
if(action.type === 'login') {
console.log("hahaha you logged in");
newState.value = true;
}
else if(action.type ==='logout') {
console.log("hahaha you logged out")
newState.value = false;
}
return newState;
}
const store = createStore(reducer);
export default store;
This is the store, this should change the value accordingly.
When the login button is pressed on loginscreen it should call the reducer function.
import React, { useRef, useState } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, Image, Dimensions, AsyncStorage } from 'react-native';
import axios from "axios";
import store from "../routes/store"
function LoginScreen({navigation}, props) {
const win = Dimensions.get('window');
const [email,setEmail] = useState('');
const [password, setPassword] = useState('');
const { auth, setAuth } = useAuth();
const [errMsg, setErrMsg] = useState('');
const logInCheck = async (e) => {
console.log("Ingelogd");
store.dispatch({type: 'login'})
}
return(
<Root>
<View style={styles.container}>
<TouchableOpacity style={styles.loginBtn} onPress{logInCheck}>
<Text style={styles.loginText}>LOGIN</Text>
</TouchableOpacity>
</View>
</Root>
)
}
This is the code which should render the right screen depending on the fact if the person is logged in!
import React, { useState, useReducer } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState()? (
<AuthStack.Screen name="Tabs" component={Tabs} options={{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
store.subscribe(rendFunc);
};
export default AuthStack;
The problem with your code is in react re-render rather than the redux store not updating. The store is updating properly but your react component is not aware of any change that has occurred in the store so no re-render is happening.
Firstly you need to add subscriptions to the redux store listener in useEffect i.e. when the component is mounted and later unsubscribe to prevent memory leakages. The redux subscribe function takes in a function to handle whenever state change has occurred in the redux store.
In this function, you can create a state using useState to create a re-render of the component.
use the below code in the authstack and it should work fine.
if any more queries you can contact me # wizcoderzcorp#gmail.com
import React, { useState, useReducer, useEffect } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-
stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
const [loginState, setLoginState] = useState(false)
const handleReduxStateChange = () =>{
setLoginState(store.getState().value)
}
useEffect(()=>{
const unsubscribe = store.subscribe(handleReduxStateChange);
return unsubscribe()
},[])
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState().value? (
<AuthStack.Screen name="Tabs" component={Tabs} options=
{{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
};
export default AuthStack;

TypeError: Object(...) is not a function problem with event handler

I am trying to change the value of a state defined in the store using redux. I defined Slice, store and component according to react redux doc, however, when I trigger the event I obtain the error:
"object(...) is not a function"
This is the slice definition
import { createSlice } from "#reduxjs/toolkit";
export const sliceCambiadorDeVista = createSlice({
name:"cambiadorDeVista",
initialState:{
value:30,
},
reducers:{
cambiarVista:(state, action)=>{
state.value = action.payload
}
}
})
export const cambiarVista = sliceCambiadorDeVista.actions;
export const vistaActual = (state) => state.cambiadorDeVista.value;
export default sliceCambiadorDeVista.reducer;
This is the store definition
import {configureStore} from "#reduxjs/toolkit"
import cambiadorDeVistaReducer from "../redux/actions/SwitchSections/switchSections"
export default configureStore({
reducer:{
cambiadorDeVista:cambiadorDeVistaReducer,
}
})
and this is a part of the component definition.
import * as React from 'react';
import Box from '#mui/material/Box';
import { ThemeProvider } from '#emotion/react';
import theme from "../themeConfig"
import { ListItem, ListItemText, ListSubheader } from '#mui/material';
import AddCircleIcon from "#mui/icons-material/AddCircle"
import WbSunnyIcon from '#mui/icons-material/WbSunny';
import OtherHousesIcon from '#mui/icons-material/OtherHouses';
import ElectricalServicesIcon from '#mui/icons-material/ElectricalServices';
import { ListItemIcon , List} from '#mui/material';
import { makeStyles } from '#mui/styles';
import { useDispatch, useSelector } from 'react-redux';
import { cambiarVista, vistaActual } from '../redux/actions/SwitchSections/switchSections';
const LeftAside = () => {
const dispatch = useDispatch()
const vistaActualState = useSelector(vistaActual)
const changeVistaHandler = ()=>{
dispatch(()=>cambiarVista(3))
}
return (
<ThemeProvider theme = {theme}>
<Box sx={{display:{xs:"none",md:"flex"}}}>
<List
sx={{display:{xs:"none",md:"flex"}, width:"100%", bgcolor:"secondary",flexDirection:"column", borderRight:1}}
subheader ={<ListSubheader>Etapas {vistaActualState}</ListSubheader>}
>
<ListItem>
<ListItemIcon>
<AddCircleIcon />
</ListItemIcon>
<ListItemText id="section-basic-data-label"
primary = "Datos Basicos"
disableTypography ="true"
className={classes.textAsideItems}
onClick={changeVistaHandler}
/>
</ListItem>
Error is generated inside of event handler changeVistaHandler. Problem seens to be related to reducer parameter.

React function component not rendering an app component

I'm a newbie to React. My array of messages is not rendering. I've verified that my array, which is part of the Messages state, is getting made properly, so I'm really confused as to why this is happening. Do I need to make all the components functional components?
ChatApp.js uses Messages.js which is a list that is generated from Message.js
ChatApp.js
import React, {useState} from 'react';
import config from '../config';
import Messages from './Messages';
import ChatInput from './ChatInput';
import Container from 'react-bootstrap/Container';
import Image from 'react-bootstrap/Image';
import Card from 'react-bootstrap/Card';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Button from 'react-bootstrap/Button';
import Media from 'react-bootstrap/Media';
import {useParams} from 'react-router-dom';
import {useSelector} from 'react-redux';
import { connect } from 'react-redux';
require('../styles/ChatApp.css');
const ChatApp = () => {
const [messages, setMessages] = React.useState([]);
const userId = useSelector(state => state.profile.profile._id);
const role = useSelector(state => state.profile.profile.role);
const addMessage = (message) => {
const messagess = [...messages];
messagess.push(message);
setMessages(messagess);
console.log(messagess);
}
const sendHandler = (message) => {
const messageObject = {
username: userId,
message
};
addMessage(messageObject);
}
return (
<div className="landing">
<Container>
<Row className="mt-5">
<Col md={{ span: 8, offset: 2 }}>
<Card style={{ height: "36rem" }} border="dark">
<Messages msgs={messages} />
<Card.Footer>
<ChatInput onSend={sendHandler}>
</ChatInput>
</Card.Footer>
</Card>
</Col>
</Row>
</Container>
</div>
)
};
ChatApp.defaultProps = {
username: 'anonymous'
};
const mapStateToProps = (state) => {
return {
authUser: state.auth.user,
profile: state.profile.profile
};
};
export default connect(
mapStateToProps
)(ChatApp);
Messages.js
import React from 'react';
import Message from './Message';
class Messages extends React.Component {
componentDidUpdate() {
// There is a new message in the state, scroll to bottom of list
const objDiv = document.getElementById('messageList');
objDiv.scrollTop = objDiv.scrollHeight;
}
render() {
// Loop through all the messages in the state and create a Message component
const messages = this.props.messages.map((message, i) => {
return (
<Message
key={i}
username={message.username}
message={message.message}
fromMe={message.fromMe} />
);
});
return (
<div className='messages' id='messageList'>
{ messages }
</div>
);
}
}
Messages.defaultProps = {
messages: []
};
export default Messages;
Message.js
import React from 'react';
import Image from 'react-bootstrap/Image'
import Media from 'react-bootstrap/Media';
class Message extends React.Component {
render() {
return (
<ul className="list-unstyled">
<Media as="li">
<Media.Body>
<h6 className="font-weight-bold">{ this.props.username }</h6>
<p>
{ this.props.message }
</p>
<p className="small text-muted">
3 hrs ago
</p>
</Media.Body>
</Media>
</ul>
);
}
}
Message.defaultProps = {
message: '',
username: '',
to: '',
fromMe: false
};
export default Message;

Categories