How can I mock render the headless UI component in react testing? - reactjs

I'm writing a test to render my App component in the child there's a dashboard component where I'm using headless UI it working fine on localhost but getting undefined components on rendering on test how can I render them?
I'm trying to mock the render but no success yet it always rendering as undefined
App.test
import { render } from '#testing-library/react';
import { SnackbarProvider } from 'notistack';
import React from 'react';
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import { applyMiddleware, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import App from './App';
// import { rootReducer } from './store';
jest.mock('#headlessui/react', () => ({
...jest.requireActual('#headlessui/react'),
}));
describe('', () => {
function renderApp(
store = {},
// createStore(
// rootReducer,
// {},
// composeWithDevTools(applyMiddleware(thunk))
// ),
props = {}
) {
return render(
<SnackbarProvider>
<MemoryRouter>
{/* <Provider store={store}> */}
<App />
{/* </Provider> */}
</MemoryRouter>
</SnackbarProvider>
);
}
test('test render app component', async () => {
renderApp();
});
});
Component failing on test
import React, { Fragment, useState, useMemo, useEffect } from 'react';
import { Menu, Transition } from '#headlessui/react';
import { ChevronDownIcon } from '#heroicons/react/solid';
// import { useSelector, useDispatch } from 'react-redux';
// import types from '../../store/actions/types';
// NOTE - for ref this is happening globally now
// and it has the global styles taken out of it
// so it doesn't mess with other stuff hopefully
// import './tailwind-style.scss'
function classNames(...classes) {
return classes.filter(Boolean).join(' ');
}
const AdvertSwitch = () => {
console.log({ Menu, Transition });
// const dispatch = useDispatch()
const advertisersFromState = [
{
id: 1,
name: 'advertiser-one',
},
{
id: 2,
name: 'advertiser-two',
},
];
const advertisers = advertisersFromState;
const selectedAdvertiser = advertisersFromState[0];
const handleAdvertSwitch = (advertiser) => {
console.log('handle advertiser switch', advertiser);
};
const showMultiple = true;
const Single = () => {
return (
<div className="">
<div>{selectedAdvertiser?.name ?? 'loading...'}</div>
</div>
);
};
const Multiple = () => {
// } else {
return (
<div className="w-full bg-gray-200 flex justify-end">
<Menu as="div" className="ml-3 z-10 relative inline-block text-left">
<div>
<Menu.Button className="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-transparent ">
{selectedAdvertiser?.name ?? 'loading...'}
<ChevronDownIcon
className="-mr-1 ml-2 h-5 w-5"
aria-hidden="true"
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
<hr className="h-1 p-0 m-0" />
<div className="py-1">
{advertisers.map((advertiser) => {
return (
<Menu.Item key={advertiser.id}>
{({ active }) => (
<div
className={classNames(
active
? 'bg-gray-100 text-gray-900'
: 'text-gray-700',
'block px-4 py-2 text-sm'
)}
onClick={() => handleAdvertSwitch(advertiser)}
>
{advertiser?.name ?? 'loading...'}
</div>
)}
</Menu.Item>
);
})}
</div>
</Menu.Items>
</Transition>
</Menu>
</div>
);
};
return (
<div className="mr-2">
{showMultiple && <Multiple />}
{!showMultiple && <Single />}
</div>
);
};
export default AdvertSwitch;
Failing Test Snap

Related

how to get the value from redux store and display it using typescirpt and redux toolkit

I am working on react typescript and redux toolkit project
I have feteched api response using axios and assign the response to redux store ,but when tried to get the value the from the store it information is not display
User.tsx
import React,{useState,useEffect} from "react";
import { UserService } from "../service/UserService";
import {useAppDispatch,useAppSelector} from '../redux/hooks'
import {DeleteCard, Getall} from '../redux/ActionSlice'
import {AiOutlineStar} from 'react-icons/ai'
import {IUser} from "../model/User";
import { AddFavourite } from "../redux/FavouriteSlice";
import { toast } from 'react-toastify';
import {useNavigate} from 'react-router-dom'
interface ISTATE{
loading:boolean,
users:IUser[],
errorMsg:string,
}
const User:React.FC=()=>{
const product=useAppSelector(state=>state.app.action)
const navigate=useNavigate()
const dispatch=useAppDispatch()
const[state,setState]=useState<ISTATE>({
loading:false,
users:[] as IUser[],
errorMsg:''
})
useEffect(()=>{
UserService.getAllUsers().then(res=>dispatch(Getall(res.data.products))).catch(err=>setState({...state,loading:false,errorMsg:err}))
setState({...state,loading:false,users:product})
console.log(state.users)
},[])
const AddFav=(product:IUser)=>{
dispatch(AddFavourite(product))
}
return(
<div className="flex flex-wrap space-x-4 space-y-4 justify-center items-center">
{state.users.length >0 && state.users.map((product,i)=>(
<div key={i} className="flex flex-col items-center relative bg-white rounded-md shadow-lg shadow-zinc-400">
<img src={product.avatar} alt="image" className="h-[300px] w-[300px] p-2 mt-6 object-contain "/>
<h1 className="text-md m-2 w-full text-center font-medium">{product.name}</h1>
<h1>{product.developerEmail}</h1>
<button onClick={()=>AddFav(product)} className="absolute top-0 right-0 p-2"><AiOutlineStar className="text-xl"/></button>
<button onClick={()=>{dispatch(DeleteCard(product._id))}} className="bg-red-500 px-4 py-2 mt-2 rounded-md mb-2 text-white shadow-lg">Delete</button>
<button onClick={()=>navigate(`/details/${product._id}`)}>More details</button>
</div>
))}
</div>
)
}
export default User
Action Slice
import{createSlice,PayloadAction} from '#reduxjs/toolkit'
import { IUser } from '../model/User'
import { RootState } from './store'
const initialState:IUser[]=[]
const ActionSlice=createSlice({
name:'action',
initialState,
reducers:{
Getall:(state,action:PayloadAction<IUser>)=>{
state.push(action.payload)
},
DeleteCard:(state,action:PayloadAction<string>)=>{
const index = state.findIndex((todo) => todo._id === action.payload);
state.splice(index, 1);
},
}
})
export const {DeleteCard,Getall}=ActionSlice.actions;
export const getProductList=(state:RootState)=>state.app.action
export default ActionSlice.reducer
IUser interface
export interface IUser{
_id:string;
avatar:string;
name:string;
category:string;
description:string;
developerEmail:string;
price:number,
}
I have tried googling staff but would able to get solution to display the thing,it would be great if you guys help me out to figure out the solution
thank you in advance

NextJs router seems very slow

The problem is about that is taking long time to response another route. It seems next router's error and it can cause by this ("getServerSideProps"). How can I change with this code to another or did I need to change react router instead? This is index which used ("getServerSideProps") -
import axios from 'axios';
import VideoCard from '../components/VideoCard';
import { BASE_URL } from '../utils';
import { Video } from '../types';
import NoResults from '../components/NoResults';
interface IProps {
videos: Video[];
}
const Home = ({ videos }: IProps) => {
return (
<div className='flex flex-col gap-10 videos h-full'>
{videos.length
? videos?.map((video: Video) => (
<VideoCard post={video} isShowingOnHome key={video._id} />
))
: <NoResults text={`No Videos`} />}
</div>
);
};
export default Home;
export const getServerSideProps = async ({
query: { topic },
}: {
query: { topic: string };
}) => {
let response = await axios.get(`${BASE_URL}/api/post`);
if(topic) {
response = await axios.get(`${BASE_URL}/api/discover/${topic}`);
}
return {
props: { videos: response.data },
};
};
This is from another code with that used next router
import React, { useState } from 'react';
import { NextPage } from 'next';
import { useRouter } from 'next/router';
import Link from 'next/link';
import { AiFillHome, AiOutlineMenu } from 'react-icons/ai';
import { ImCancelCircle } from 'react-icons/im';
import SuggestedAccounts from './SuggestedAccounts';
import Discover from './Discover';
import Footer from './Footer';
import useAuthStore from '../store/authStore';
const Sidebar: NextPage = () => {
const [showSidebar, setShowSidebar] = useState<Boolean>(true);
const { pathname } = useRouter();
const { fetchAllUsers, allUsers }: any = useAuthStore();
const activeLink = 'flex items-center gap-3 hover:bg-primary p-3 justify-center xl:justify-start cursor-pointer font-semibold text-[#F51997] rounded';
const normalLink = 'flex items-center gap-3 hover:bg-primary p-3 justify-center xl:justify-start cursor-pointer font-semibold rounded';
return (
<div>
<div
className='block xl:hidden m-2 ml-4 mt-3 text-xl'
onClick={() => setShowSidebar(!showSidebar)}
>
{showSidebar ? <ImCancelCircle /> : <AiOutlineMenu />}
</div>
{showSidebar && (
<div className='xl:w-400 w-20 flex flex-col justify-start mb-10 border-r-2 border-gray-100 xl:border-0 p-3 '>
<div className='xl:border-b-2 border-gray-200 xl:pb-4'>
<Link href='/'>
<div className={pathname === '/' ? activeLink : normalLink}>
<p className='text-2xl'>
<AiFillHome />
</p>
<span className='capitalize text-xl hidden xl:block'>
For You
</span>
</div>
</Link>
</div>
<Discover />
<SuggestedAccounts
fetchAllUsers={fetchAllUsers}
allUsers={allUsers}
/>
<Footer />
</div>
)}
</div>
);
};
export default Sidebar;

Building a react project, if user has already subscribed then it should redirect to homepage but if user sign up then pop up planPage

I've used redux to maintain state if user has already subscribed but on reload, already subscribed users are also redirected to planPage instead of homePage because on reload state sets back to initial null state how should i do it then ?
This is my App.jsx
import { useEffect, useState } from "react";
import DetailPage from "./pages/DetailPage";
import ProfilePage from "./pages/ProfilePage";
import HomePage from "./pages/HomePage";
import LoginPage from "./pages/LoginPage";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { selectUser, login, logout } from "./features/userSlice";
import PlanPage from "./pages/PlanPage";
import { getDocs } from "firebase/firestore";
import { auth, users } from "./firebase";
import { selectSubscription } from "./features/subscriptionSlice";
function App() {
const user = useSelector(selectUser);
const isSubscribed = useSelector(selectSubscription);
const dispatch = useDispatch(); //hook to access redux dispatch function
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((userAuth) => {
// observe the state change in user's sign in activity
if (userAuth) {
//logged in
dispatch(login({ uid: userAuth.uid, email: userAuth.email }));
} else {
//logged out
dispatch(logout());
}
});
return unsubscribe; // for cleanup the previous state
}, [dispatch]);
console.log(isSubscribed);
return (
<div>
<BrowserRouter>
{!user ? (
<LoginPage />
) : (
<>
{!isSubscribed ? (
<PlanPage />
) : (
<>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="detailScreen" element={<DetailPage />} />
<Route path="profileScreen" element={<ProfilePage />} />
<Route path="planScreen" element={<PlanPage />} />
</Routes>
</>
)}
</>
)}
</BrowserRouter>
</div>
);
}
export default App;
planPage.jsx file
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { auth } from "../firebase";
import { logout } from "../features/userSlice";
import { AiOutlineCheck } from "react-icons/ai";
import { products, users } from "../firebase";
import { getDocs, setDoc, doc } from "firebase/firestore";
import Table from "../components/Table";
//import { BiLoaderAlt } from "react-icons/bi";
import {
showSubscriptionDetail,
selectSubscription,
} from "../features/subscriptionSlice";
import { useNavigate } from "react-router-dom";
export default function PlanPage() {
const navigate = useNavigate();
const subscription = useSelector(selectSubscription);
const [prod, setProducts] = useState([]); //useState() hook, sets initial state to an empty array
const [selectedPlan, setSelectedPlan] = useState(null);
useEffect(() => {
async function unsubscribe() {
const item = await getDocs(products);
const productItem = item.docs.map((doc) => ({
id: doc.id, //id and data pushed into productItems array
...doc.data(),
}));
setProducts(productItem);
setSelectedPlan(productItem[2]);
}
unsubscribe();
}, []);
const dispatch = useDispatch();
const handleClick = () => {
auth.onAuthStateChanged(() => {
dispatch(logout());
});
};
const manageSubscription = () => {
if (subscription) navigate("/");
navigate(-1);
};
async function setData(data) {
await setDoc(doc(users, `${auth.currentUser.email}`), {
productType: data,
email: auth.currentUser.email,
subscribed: true,
activateTime: new Date().toLocaleString(),
planEndTime: `${
new Date().getMonth() + 2
}/${new Date().getDate()}/${new Date().getFullYear()}`,
});
}
const subscribeToPlan = () => {
if (!auth) return;
dispatch(showSubscriptionDetail({ subscription: true }));
setData(selectedPlan?.name);
};
return (
<div>
<header className="border-b border-white/10 bg-[#141414] ">
<img
alt=""
src="https://rb.gy/ulxxee"
width={150}
height={90}
className="cursor-pointer object-contain"
onClick={manageSubscription}
/>
<button
className="text-lg font-medium hover:underline"
onClick={handleClick}
>
Sign Out
</button>
</header>
<main className="mx-auto max-w-5xl px-5 pt-28 pb-12 transition-all md:px-10">
<h1 className="mb-3 text-lg md:text-3xl font-medium">
Choose the plan that's right for you
</h1>
<ul>
<li className="flex items-center gap-x-2 text-sm md:text-lg">
<AiOutlineCheck className=" h-5 w-5 md:h-7 md:w-7 text-[#E50914]" />{" "}
Watch all you want. Ad-free.
</li>
<li className="flex items-center gap-x-2 text-sm md:text-lg">
<AiOutlineCheck className=" h-5 w-5 md:h-7 md:w-7 text-[#E50914]" />{" "}
Recommendations just for you.
</li>
<li className="flex items-center gap-x-2 text-sm md:text-lg">
<AiOutlineCheck className=" h-5 w-5 md:h-7 md:w-7 text-[#E50914]" />{" "}
Change or cancel your plan anytime.
</li>
</ul>
<div className="mt-4 flex flex-col space-y-4">
<div className="flex w-full items-center self-end md:w-3/5">
{prod.map((product) => (
<div
className={`planBox ${
selectedPlan?.id === product.id ? "opacity-100" : "opacity-60"
}`}
key={product.id}
onClick={() => setSelectedPlan(product)} //here if i have directly called the stateSetter i.e setSelectedPlan then it is getting called soon after the component mount stage, and keeps on rerender the state and getting stuck into loops. hence ()=> setSelected()
>
{product.name}
</div>
))}
</div>
<Table products={prod} selectedPlan={selectedPlan} />
<button
disabled={!selectedPlan}
className={`mx-auto w-11/12 rounded bg-[#E50914] py-4 text-xl shadow hover:bg-[#f6121d] md:w-[420px] `}
onClick={subscribeToPlan}
>
Subscribe
</button>
</div>
</main>
</div>
);
}
subscriptionSlice.jsx (redux-reducer code)
import { createSlice } from "#reduxjs/toolkit";
export const subscriptionSlice = createSlice({
name: "subscription",
initialState: {
subscription: null,
},
reducers: {
showSubscriptionDetail: (state, action) => {
state.subscription = action.payload;
},
},
});
export const { showSubscriptionDetail } = subscriptionSlice.actions;
export const selectSubscription = (state) =>
state.subscription.subscription?.subscription;
export const subscriptionReducer = subscriptionSlice.reducer;
I implemented this features just by defining a state(issubscribed) in App.js and have access to db. if subscription exist in firebase sent back a data if not it will stay null and you can have the simple logic in your routing.

Testing HOC wrapped React Component

I'm trying to test hoc wrapped component.
The component calls an apollo query inside.
const Header: React.FC<IProps> = ({}) => {
const { data, loading } = useMeQuery();
return (
<>
{!data?.me.verified && (
<div className="bg-red-500 p-3 text-center text-sm text-white font-bold">
<span>Please verify your email</span>
</div>
)}
<header className="py-4">
<div className="w-full px-5 xl:px-0 max-w-screen-xl bg-yellow-500 mx-auto">
<Image
src="/pineapple.png"
alt="pineapple-logo"
width="64"
height="64"
/>
{data ? (
<NextLink href="edit-profile">
<Link>
<span className="text-sm">
<FontAwesomeIcon className="text-xl" icon={faUser} />
{data?.me.email}
</span>
</Link>
</NextLink>
) : (
<div>Login</div>
)}
</div>
</header>
</>
);
};
export { Header as PureHeader };
export default withApollo()(Header);
import { render, waitFor } from "#testing-library/react";
import React from "react";
import Header, { PureHeader } from "../../src/components/Header";
import { ApolloProvider } from "#apollo/client";
import { createMockClient } from "mock-apollo-client";
import { MockedProvider } from "#apollo/client/testing";
import withApolloMocked from "../../src/apollo/__mock__/withApolloMocked";
describe("<Header />", () => {
it("renders ok", async () => {
const mockedClient = createMockClient();
render(
<ApolloProvider client={mockedClient}>
<PureHeader />
</ApolloProvider>
);
});
});
Even if I imported Pure Component without hoc, I get the same error when running the test.
However, if remove the hoc and export default Header (which is a pure component), it then passes the test... ;;;
import React from "react";
import Image from "next/image";
import { useMeQuery } from "../generated/graphql";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faUser } from "#fortawesome/free-solid-svg-icons";
import NextLink from "next/link";
import { Link } from "#chakra-ui/react";
import withApollo from "../apollo/withApollo";
interface IProps {}
const Header: React.FC<IProps> = ({}) => {
const { data, loading } = useMeQuery();
return (
<>
{!data?.me.verified && (
<div className="bg-red-500 p-3 text-center text-sm text-white font-bold">
<span>Please verify your email</span>
</div>
)}
<header className="py-4">
<div className="w-full px-5 xl:px-0 max-w-screen-xl bg-yellow-500 mx-auto">
<Image
src="/pineapple.png"
alt="pineapple-logo"
width="64"
height="64"
/>
{data ? (
<NextLink href="edit-profile">
<Link>
<span className="text-sm">
<FontAwesomeIcon className="text-xl" icon={faUser} />
{data?.me.email}
</span>
</Link>
</NextLink>
) : (
<div>Login</div>
)}
</div>
</header>
</>
);
};
// export { Header as PureHeader };
// export default withApollo()(Header);
export default Header;
import { render, waitFor } from "#testing-library/react";
import React from "react";
// import Header, { PureHeader } from "../../src/components/Header";
import Header from "../../src/components/Header";
import { ApolloProvider } from "#apollo/client";
import { createMockClient } from "mock-apollo-client";
import { MockedProvider } from "#apollo/client/testing";
import withApolloMocked from "../../src/apollo/__mock__/withApolloMocked";
describe("<Header />", () => {
it("renders ok", async () => {
const mockedClient = createMockClient();
render(
<ApolloProvider client={mockedClient}>
<Header />
</ApolloProvider>
);
});
});
Did I miss something? OR Is it just a bug?
I'm not quite sure how to deal with this problem...

React router useHistory

I want when I press on the button it redirects me to the payment page.
import React,{useHistory, useState} from 'react'
import data from './data.json'
function GLC() {
let history = useHistory();
function handleClick() {
history.push("/payment");
}
return (
<div >
{data.map((postData) =>{
console.log(postData)
return(
<div key=
{postData.id}>
<div className='absolute '>
<img className='w-screen object-contain'src="./ModImages/Gclass.jpg"></img>
<h1 className='absolute ml-24 md:text-5xl sm:text-5xl top-8'>Class G</h1>
<h1 className='absolute text-base font-mono ml-24 top-24'>$43,600</h1>
<button onClick={handleClick} className=' absolute text-black-600 h-10 top-24 ml-24 mt-32 bg-
white w-36 rounded-full focus:outline-none focus:ring-2 focus:ring-gray-600'>BuyNow</button>
</div>
</div>
)
})
}
</div>
)
}
export default GLC
// Make sure you alreadly installed the react-router-dom package.
import { useHistory } from 'react-router-dom';
function GLC() {
const history = useHistory();
function handleClick() {
history.push("/payment");
}
// ...
}
Then you can work .

Resources