Test Empty Component using React Testing Library - javascript

I'm testing my Login component which implements Microsoft authentication for login. I'm returning empty fragment from Login.js. How can I test this using React Testing library and jest?
import { useEffect } from 'react';
import { msalInstance } from './authConfig';
export const Login = () => {
useEffect(() => {
msalInstance
.handleRedirectPromise()
.then((tokenResponse) => {
if (!tokenResponse) {
const accounts = msalInstance.getAllAccounts();
if (accounts.length === 0) {
// No user signed in
msalInstance.loginRedirect();
}
}
})
.catch((err) => {
console.error(err);
});
}, []);
return <></>;
};

Related

How to Logout (Go to Base URL) if local Storage doesn't have certain key-value pair in React

I am implementing a simple logout functionality if my local storage doesn't have a particular key-value pair and if the value is empty or if the 'token' inside the value is expired.
My current Code: TokenExpired.js
import { isExpired } from "react-jwt";
import { useNavigate } from "react-router-dom";
export const VerifyAccessToken = () => {
const navigate = useNavigate()
const Data = localStorage.getItem('Admin Credentials')
const existanceOfData = Data !== null
if (existanceOfData) {
if (Data && Data !== 'undefined') {
const tokenExpired = isExpired(JSON.parse(Data).accessToken);
if (tokenExpired) {
localStorage.removeItem("Admin Credentials");
navigate('/')
}
} else {
localStorage.removeItem("Admin Credentials");
navigate('/')
}
} else {
navigate('/')
}
}
I am using this in My Dashboard Page : Dashboard/Dashboard/js
import "./Dashboard.scss";
import { adminAuth } from "../../helpers/AdminInformation";
import { VerifyAccessToken } from "../../helpers/TokenExpired";
// components ---------------------------------
certain components
import { useEffect, useState } from "react";
const Dashboard = () => {
const [dashboard, setDashboard] = useState({ received: 0, expected: 0 })
const token = adminAuth.accessToken;
useEffect(() => {
fetch(baseURL + 'api/dashboard/', {
headers: {
token: `Bearer ${token}`
}
}).then(res => res.json()).then(json => setDashboard(json));
}, [token])
VerifyAccessToken();
return (
<div className="dashboard">
content
</div>
);
}
export default Dashboard;
Whenever I try to delete that key value after logging in, it shows error:
I Found the Answer to my Question:
I figured I need to make my Routes Strong so that there are no warning about my routes in the console
I Created Layout Component for my Dashboard Page & Update Token Expired Code with useEffect(), It worked...
My Updated Code: TokenExpired.js
import { isExpired } from "react-jwt";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
export const VerifyAccessToken = () => {
const navigate = useNavigate()
const Data = localStorage.getItem('Admin Credentials')
const existanceOfData = Data !== null
useEffect(() => {
if (existanceOfData) {
if (Data && Data !== 'undefined') {
const tokenExpired = isExpired(JSON.parse(Data).accessToken);
if (tokenExpired) {
navigate('/')
}
} else {
navigate('/')
}
} else {
navigate('/')
}
}, [Data, existanceOfData, navigate]);
}

useReactVar in apolloLink cause cannot update component while rendering a diffrend component

i use apollo client and react in project
i want appear loading indicator while network request, so i set react variables and change it inside apollo link
but it cause cannot update component(Indicator) while rendering a diffrend component(component that call useQuery hook)
loadingLink.ts
import { ApolloLink } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
export const loadingLink = new ApolloLink((operation, forward) => {
loadingVar(true);
return forward(operation).map(data => {
loadingVar(false);
return data;
});
});
loadingIndicator.ts
import React from 'react';
import { useReactiveVar } from '#apollo/client';
import { loadingVar } from 'gql/store/reactiveVariables';
import { Indicator } from './Indicator';
const LoadingIndicator: React.FC = () => {
const loading = useReactiveVar(loadingVar);
if (!loading) return null;
return <Indicator />;
};
export default LoadingIndicator;
error message
error message
BatchList.tsx
const BatchList = () => {
const {data, error} = useQuery(~~~);
if(error) return null;
if(loading || !data) return null;
return ~~~~
}
in documentation, they say should use useEffect hook but inside ApolloLink i can't use useEffect hook
How can i fix it

Next JS Redux not update server store

The Redux Update operations I made on the client-side in Next JS are not updated in the server store.
Hello. I have a problem. I'm developing an SSR application with Next JS. I have provided the next js link with the next-redux-wrapper. State update operations can be provided. State updates I made on the server-side can be viewed on the client-side. The redux updates I made on the client-side also appear on the client-side, but when I refresh the page, it returns to the previous position. Sample scenario:
Users have addresses. Their addresses can be taken from the DB and printed on the screen. DB updates when I add a new address or delete the old address. Along with it, it is updated in the store on the client-side. So far there is no problem. However, when I refresh the page, for example, if there are 4 addresses before updating and I deleted one, after the refresh, it is printed as 4 addresses again. It continues like this until I get data from the server again.
How can I move the client-side store updates to the server-side without having to make requests to the server over and over again?
store.js
// store.js
import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from "next-redux-wrapper";
import thunkMiddleware from 'redux-thunk'
// ROOT REDUCERS
import rootReducer from "../reducers";
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
const store_ = (initialState) => {
return createStore(rootReducer, initialState, bindMiddleware([thunkMiddleware]));
}
const wrapper = createWrapper(store_/*, { debug: true }*/);
export {
wrapper
}
_app.js
// _app.js
const MyApp = ({props, Component, pageProps }) => {
const store = useStore();
if (!store.getState().R_PageSettings.initStore)
{
store.dispatch({
type: HYDRATE,
payload: {
...props.initialState
}
})
}
return (
<>
<Head>
<title>{ variables.meta.title }</title>
</Head>
<Component {...pageProps} />
</>
)
}
const wrappedApp = wrapper.withRedux(MyApp);
export default wrappedApp;
wrappedApp.getInitialProps = async ctx => {
const data = await wrapper.getServerSideProps(
async (req) => {
const { store, ctx } = req;
const reduxStates = store.getState();
let user = reduxStates.R_User.user;
if (!user)
{
const cookies = parseCookies(ctx);
if (cookies.usr && user !== undefined)
{
const getUser = await CustomersController.tokenLoginControl(cookies.usr);
if (getUser && getUser.status)
{
store.dispatch(setUserSSR(getUser.user))
user = getUser.user;
}
else
destroyCookie(ctx, 'usr');
}
}
return {
user
}
}
)(ctx)
return data;
}
action.js
// CONSTANTS
import {
C_User
} from "../constants";
export const setUserSSR = user => {
return {
type: C_User.SET_USER,
payload: {
user
}
}
}
export const setUser = user => dispatch => {
return dispatch({
type: C_User.SET_USER,
payload: {
user
}
})
}
addresspage.js
// addresspage.js
import { connect } from 'react-redux';
import { bindActionCreators } from "redux";
// COMPONENTS
import UserPageLayout from "../UserPagesLayout";
import {
CustomerAddressForm
} from "../../../components";
// CONTROLLERS
import {
CustomersController
} from "../../../controllers";
// ACTIONS
import {
setUser
} from "../../../actions";
const MyAddressPage = connect(({ R_User }) => {
return {
R_User
}
}, dispatch => {
return {
setUser: bindActionCreators(setUser, dispatch)
}
})((props) => {
const addAddressHandle = () => {
props.fullBarOpen(
<CustomerAddressForm confirmHandle={async (address, setLoading) => {
const execute = await CustomersController.addAddress(address);
if (execute.status)
{
await props.setUser(execute.user);
}
else
{
setLoading(false);
}
}}
/>
);
}
return (
<UserPageLayout>
</UserPageLayout>
);
})
export default MyAddressPage;

React context api update state instantly

I'm using the context api in a Gatsby setup to keep track of a state called userIsLoggedIn. I'm using Firebase for authentication.
This is my context file:
import { createContext } from "react"
export const AppContext = createContext(null)
This is my AppWrapper component:
import React, { useState, useEffect } from "react"
import firebase from "../../config/firebase"
import { AppContext } from "../../context/AppContext"
const AppWrapper = ({ children }: any) => {
const [userIsLoggedIn, setUserIsLoggedIn] = useState(false)
const authListener = () => {
firebase.auth().onAuthStateChanged(user => {
if (user && user.emailVerified) {
setUserIsLoggedIn(true)
} else {
setUserIsLoggedIn(false)
}
})
}
useEffect(() => {
authListener()
}, [])
return (
<>
<AppContext.Provider
value={{
userIsLoggedIn,
}}
>
<main>{children}</main>
</AppContext.Provider>
</>
)
}
export default AppWrapper
This is my index page where I want to keep track if the user is logged in so I can show/hide certain content:
import React, { useContext } from "react"
import { AppContext } from "../context/AppContext"
const IndexPage = () => {
const app = useContext(AppContext)
console.log("app", app)
return (
<>
{app && app.userIsLoggedIn && (
<>
<h1>Hello dearest user</h1>
<p>Welcome to your page.</p>
</>
)}
</>
)
}
export default IndexPage
The outcome of my console.log inside the my IndexPage component is the following when I first load the page or whenever the page is reloaded:
app {userIsLoggedIn: false}
app {userIsLoggedIn: true}
This means my page is re-rendering and my content is flickering between content which is hidden/shown when a user is logged in. Is there a way to avoid this and make the state more instant? I'm open for any suggestions :)
Okay so I found out what helps my specific case. Instead of using the context api to keep an app state (which will be reset to it's default value when reloaded, hence the flickering between states) I use localStorage to save if a user is logged in in combination with my authListener function.
This is the auth util I added:
// Firebase
import firebase from "../config/firebase"
export const isBrowser = () => typeof window !== "undefined"
export const getUser = () => {
if (isBrowser()) {
const user = window.localStorage.getItem("user")
if (user !== null) {
return JSON.parse(user)
} else {
return ""
}
}
}
export const setUser = (email: string | null) =>
isBrowser() && window.localStorage.setItem("user", JSON.stringify(email))
export const isLoggedIn = () => {
const user = getUser()
return !!user
}
export const logout = () => {
return new Promise(resolve => {
firebase
.auth()
.signOut()
.then(() => {
setUser("")
resolve()
})
})
}
and inside my AppWrapper my authListener function now looks like this:
import { setUser, logout } from "../../utils/auth"
const authListener = () => {
firebase.auth().onAuthStateChanged(user => {
if (user && user.emailVerified) {
setUser(user.email)
} else {
logout()
}
})
}
useEffect(() => {
authListener()
})
Anywhere in my app I can use the isLoggedIn() util to check if the user is actually logged in without having the flickering content.
If I manually delete or alter the localStorage user this will instantly be refreshed by the authListener function when anything changes in the app.

Jest wait until typemoq function called

So I have a mock (typemoq) http call that I'm passing into my react component (mounted with enzyme):
const mockhttp: TypeMoq.IMock<IHttpRequest> = TypeMoq.Mock.ofType<IHttpRequest>();
mockhttp
.setup(x => x.get('/get-all-goal-questions'))
.returns(() => {
return Promise.resolve(mockResponse.object.data);
});
const wrapper = mount(<Goals history={Object} http={mockhttp.object} />);
expect(wrapper.find('#foo')).to.have.lengthOf(1);
However, the mock "Get" isn't being called until after the expected, how can I get the expect to wait until the mock is called to test?
// Edit here is the code under test
let httpCall = this.props.pageHoc.httpRequest -- the call im mocking
import React, { Component } from 'react';
import { Row, Col } from 'react-bootstrap';
import { Animated } from "react-animated-css";
import { Answer, IPageHOC } from '../../interfaces/pageObjects';
// Fonts
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faCheck } from '#fortawesome/free-solid-svg-icons'
// Cookies
import cookie from 'react-cookies';
// Google analytics
import ReactGA from 'react-ga';
type GoalsComponent = {
answers: Answer[],
showError:boolean,
showAnimation:boolean,
question:string,
questionId:number
};
type Props = {
history:any,
pageHoc?: IPageHOC
}
export default class Goals extends Component<Props, GoalsComponent>
{
constructor(props: any) {
super(props);
this.state = {
answers : [],
showError: false,
showAnimation:false,
question: "",
questionId: 0
}
}
componentDidMount(){
// Hide nav
this.props.pageHoc.hideRightNav();
this.loadQuestions();
}
loadQuestions(){
// Setup auth
let auth = this.props.pageHoc.externalAuth;
auth.setToken(cookie.load('Email'), cookie.load('Password')).then((x) => {
let httpCall = this.props.pageHoc.httpRequest;
// Headers
httpCall.setHeaders({
Organization: cookie.load('Organization')
});
httpCall.get(`/thrive/goal/get-all-goal-questions`)
.then((x) => {
this.setState({
answers:x.data.goalQuestions[0].answers,
question: x.data.goalQuestions[0].question,
questionId: x.data.goalQuestions[0].id
});
})
.catch((x) => {
console.log(x, "error");
});
});
}
render() {
return (
<ul className="list-group list-group-goals">
{this.state.answers.map((x:Answer) =>
<li className={("list-group-item ") + (x.selected ? "selected" : "")} key={x.id} onClick={() => this.toggleGoal(x.id)}>
{x.answer}
<FontAwesomeIcon icon={faCheck} className={("goal-tick ") + (x.selected ? "goal-tick-red" : "")} />
</li>
)}
</ul>
);
}
}
hmm if you are trying to test async request you should follow what is written here:
https://jestjs.io/docs/en/tutorial-async
for the short version your test should look something like this:
it('works with async/await', async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual('Mark');
});
You can do something like this:
fun test = async () => {
const mockhttp: TypeMoq.IMock<IHttpRequest> = TypeMoq.Mock.ofType<IHttpRequest>();
mockhttp
.setup(x => x.get('/get-all-goal-questions'))
.returns(() => {
return Promise.resolve(mockResponse.object.data);
});
const wrapper = await mount(<Goals history={Object} http={mockhttp.object} />);
expect(wrapper.find('#foo')).to.have.lengthOf(1);
}
This will wait for the promise returned by the mocked get function to resolve and the component to render with the latest data.

Categories