Cannot destructure property trouble in enzyme? - reactjs

While I'm trying to make a test
import React from "react";
import {EventsLoadingContext} from "../../../src/components/data-admin/context/EventsLoadingContext";
import DataAdmin from "../../../src/components/data-admin/DataAdmin";
import { configure, mount, shallow } from "enzyme";
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
it("should render DataAdmin component", () => {
const component = shallow(<DataAdmin />, {context: EventsLoadingContext});
const dataTable = component.find(".table-container");
expect(dataTable.length).toBe(1);
});
and have a trouble like that:
In the context component I have:
export const EventsLoadingContext = createContext();
export const WithEventsLoadingContext = ({children}) => {
const [backendEvents, setBackendEvents] = useState([]);
const [integrationEvents, setIntegrationEvents] = useState([]);
const [allEvents, setAllEvents] = useState([]);
// ...
const value = {
preparedEvents,
dropdownItems,
dropdownClicked, setDropdownClicked,
loading,
updatedChanges, setUpdatedChanges
};
return (
<EventsLoadingContext.Provider value = {value}>
{children}
</EventsLoadingContext.Provider>
)
};
And in the DataAdmin component I have:
const DataAdmin = () => {
const {
preparedEvents,
dropdownItems,
dropdownClicked, setDropdownClicked,
loading,
updatedChanges, setUpdatedChanges
} = useContext(EventsLoadingContext);
and so on.
So how could I fix that?
So how could I fix that?

Seems like you should pass the EventsLoadingContext context in options parameter for shallow method. See shallow documentation.

Related

How to mock useRef using playwright component testing

hello I'm writing test for a component with ref. I'd like to mock the ref element and change some properties but have no idea how to. Any suggestions?
import { test, expect } from "#playwright/experimental-ct-react";
import { useState } from "react";
import { useRef } from "react";
import Main from "./index";
test.use({ viewport: { width: 500, height: 500 } });
test("should work", async ({ mount }) => {
const refMain = useRef();
const refFooter = useRef();
const refMenuButton = useRef();
const [mainPostion, setMainPostion] = useState(startMainPostion);
const refContainerForMainFooter = useRef({
refMain,
refFooter,
refMenuButton,
});
const component = await mount(
<Main setMainPostion={setMainPostion} theme='light' ref={ref} />
);
// await expect(component).toContainText('DAMAN');
});

Using context in react component tests

I am writing tests for components of my projects and one of components is changing context value. How can I check if click actually changes context value? Code I have right now gives error "Invalid hook call. Hooks can only be called inside of the body of a function component." What is the way to actually use hooks in tests?
import { useState, useContext } from "react";
import { IntlProvider } from "react-intl";
import { BrowserRouter } from "react-router-dom";
import { render, screen, fireEvent } from "#testing-library/react";
import { messages } from "../../App/App.const";
import Navbar from "./Navbar";
import { DarkModeContext } from "../../contexts/DarkModeContext";
function NavbarMock() {
const [search, setSearch] = useState("");
const [language, setLanguage] = useState("en");
return (
<IntlProvider
messages={messages[language as keyof typeof messages]}
locale={language}
defaultLocale="en"
>
<BrowserRouter>
<Navbar
setLanguage={setLanguage}
language={language}
setSearch={setSearch}
search={search}
/>
</BrowserRouter>
</IntlProvider>
);
}
describe("testing navbar component", () => {
test("renders logo correctly", async () => {
render(<NavbarMock />);
const logo = screen.getByText(/Todoly/i);
expect(logo).toBeInTheDocument();
});
test("renders mode icon correctly", async () => {
render(<NavbarMock />);
const svgEl = screen.getByTitle("mode icon");
expect(svgEl).toBeInTheDocument();
});
test("mode changes", async () => {
render(<NavbarMock />);
const svgEl = screen.getByTitle("mode icon");
const { isDarkMode } = useContext(DarkModeContext);
fireEvent.click(svgEl);
expect(isDarkMode).toBe(true);
});
test("renders language icon correctly", async () => {
render(<NavbarMock />);
const flagEl = screen.getByAltText("en");
expect(flagEl).toBeInTheDocument();
});
});
Ok, the error isn't caused by any of the provider, instead it's caused by the way you write useContext.
A hook can't be used unless it's referenced inside a Component, this is the RULE by React hooks. Because states defined by hooks are meaningless outside.
const AnotherNavbarMock = () => {
const { isDarkMode } = useContext(DarkModeContext);
return <NavbarMock />
}
The above code is the working example. Basically a test isn't a component, you have to define a component and then use it via <AnotherNavbarMock />, otherwise all the hook code would fail.

How to pass array of objects as value in Context Provider

I want to send the array of objects in ContextProvider value attribute, so that i can use it in another component using useContext() hook. So that in the component i can destructure values from the array and map the array and display it on the browser.
Here is the ContextProvider file code:-
import React, { useState, useEffect, createContext } from "react";
import axios from "axios";
const APIContext = createContext();
export const APIContextProvider = (props) => {
const [orders, setOrders] = useState([]);
useEffect(() => {
const fetchData = async () => {
const res = await axios.get("/api/orders");
setOrders(res.data);
//res.data returns ARRAY OF OBJECTS
console.log(res.data);
};
fetchData();
}, []);
return (
<APIContext.Provider value={?}>{props.children}</APIContext.Provider>
);
};
Why you dont pass objects as value={{objects}} and then later access it as const {objects} = useContext(APIContext)

How to store and share one single Pusher instance

I've read that using multiple instances of Pusher is not recommended and therefore I am using React's context feature.
import React, { useContext, useEffect, useRef } from "react";
import Pusher from "pusher-js";
const PusherContext = React.createContext<Pusher | undefined>(undefined);
export const usePusher = () => useContext(PusherContext)
export const PusherProvider: React.FC = (props) => {
const clientRef = useRef<Pusher>();
useEffect(() => {
if (!clientRef.current) {
clientRef.current = new Pusher('pusher_key', {
cluster: 'pusher_cluster',
authEndpoint: 'auth_endpoint',
})
}
}, [clientRef]);
return (
<PusherContext.Provider value={clientRef?.current}>
{props.children}
</PusherContext.Provider>
)
}
Our pusher instance is of type Pusher or undefined. When we call usePusher it is always undefined.
import { usePusher } from "./services/pusher";
const Chat: React.FC = (props) => {
const pusher = usePusher()
useEffect(() => {
if (pusher) {
console.log("pusher is initialized/defined")
} else {
// it is always undefined
console.log("pusher is not initialized/defined")
}
}, [pusher])
return (
<div>
Test component
</div>
)
}
Where is the problem?
It seems to work. I was just doing something wrong when wrapping the provider. Hope it helps in case someone have similar problem!

React is telling me that I cannot use hooks inside my "Provider" function for contextAPI

I am following an article: https://dev.to/mongopark/learn-react-hooks-and-context-api-by-building-a-recipe-search-app-2-1g3o. In it, he uses useState and useEffect inside his created Context. When I try to do the same. I get errors sayin, " Line 10:28: React Hook "useState" is called in function "dataProvider" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks" for all the hooks. I would like to just know what I am doing wrong here.
import React, {useState, useEffect, createContext} from 'react';
import { headerFooter } from '../../api/Api';
import {setHeaderFooter} from '../../functions/generalFunctions'
import {grabUserInventory} from '../../api/Api'
import {getAllOrgs} from '../../api/Api'
const dataContext = createContext()
const dataProvider = (props) => {
const [data, setData] =useState("")
const [combined, setCombined] =useState(0)
const [inventory, setInventory] =useState([])
const [notes, setNotes] =useState([])
const [orgs, setOrgs] =useState([])
const [renderedData, setRenderedData]=useState([])
const [progress,setProgress] = useState(true)
useEffect(()=>{
console.log(props.match.params.token)
headerFooter(props.match.params.token)
.then(res=>{
setData(res)
setHeaderFooter(res.header,res.footer)
return grabUserInventory(res.user_id)
.then(data=>{
setInventory(data)
setRenderedData(data)
setProgress(false)
return getAllOrgs()
.then(data=>{
var outputData = data.map( Object.values );
setOrgs(outputData)
})
})
})
.catch(err=>console.error(err))
}, []);
return (
<dataContext.Provider value={{
data,
setData,
combined,
setCombined,
inventory,
setInventory,
orgs,
setOrgs,
renderedData,
setRenderedData,
progress,
setProgress
}}>
{props.children}
</dataContext.Provider>
);
}
export { dataProvider, dataContext }

Resources