enzyme mocha AssertionError: expected 0 to equal 21 - node.js

Writing some unit tests for an app and hitting a wall in the describe block.
/* eslint-env mocha */
const React = require('react')
const chai = require('chai')
const { expect } = chai
const Search = require('../js/Search')
const ShowCard = require('../js/ShowCard')
const enzyme = require('enzyme')
const { shallow } = enzyme
const data = require('../public/data')
describe('<Search />', () => {
it('should render as many shows as there are data for', () => {
const wrapper = shallow(<Search />)
expect(wrapper.find(ShowCard).length).to.equal(data.shows.length)
console.log(wrapper.debug())
})
})
The code in the Search component is rendering ShowCard like this:
<div className='shows'>
{data.shows
.filter((show) => `${show.title} ${show.description}`.toUpperCase().indexOf(this.state.searchTerm.toUpperCase()) >= 0)
.map((show, index) => (
<ShowCard {...show} key={index} id={index} />
))}
</div>
(wrapper.find(ShowCard).length) should equal (data.shows.length), but it's giving this error:
<Search /> should render as many shows as there are data for:
AssertionError: expected 0 to equal 21
+ expected - actual
-0
+21
at Context.<anonymous> (App.spec.js:19:45)
According to the above error, the problem starts at the expectation equal(data.shows.length) but I see nothing wrong with that. Can anyone point me in the right direction?

wow, how embarrassing. i had the state of the Search constructor's input value set to "default search term" - thus preventing any search results from appearing until that string was manually removed from the input.
replacing with an empty string solved the problem. all tests now pass.

Related

Jest/React Testing Library test failing - react router link click not working as expected

I have a failing test but can't work out why. I have react-router links which link to the URL structure: /classes/${weekday}.
Classes component then sets the activeWeekday in context by React Router location which is displayed by the Classes as {activeWeekday} Classes
Functionality works i nthe browser, but for some reason in my tests it's not updating the header so the test is failing.
TestingLibraryElementError: Unable to find an element with the text: /friday classes/i
Can anyone see why? I can't figure it out.
Thks so much in advance.
Update - Here is a codepen replicating the issue.
// Snapshot of the state passed to Classes via context
export const ClassesProvider = ({ children }: ClassesProviderProps) => {
const [activeWeekdayNumber, setActiveWeekdayNumber] = useState<number>(
new Date().getDay()
);
// Classes component
const Classes = () => {
const { activeWeekdayNumber, setActiveWeekdayNumber } = useContext(ClassesContext);
const location = useLocation();
useEffect(() => {
const day = location.pathname.replace("/classes/", "");
const dayIndex = daysOfWeekArray.indexOf(capitaliseFirstLetter(day));
if (dayIndex !== -1) {
setActiveWeekdayNumber(
daysOfWeekArray.indexOf(capitaliseFirstLetter(day))
);
}
}, [location, setActiveWeekdayNumber]);
return (
<>
<h2>{daysOfWeekArray[activeWeekdayNumber]} Classes</h2>
</>
);
};
// failing test - TestingLibraryElementError: Unable to find an element with the text: /friday classes/i
const setup = (value: ClassesContextType) =>
render(
<ClassesContext.Provider value={value}>
<MemoryRouter>
<Classes />
</MemoryRouter>
</ClassesContext.Provider>
);
test("displays the relevant heading when a day of the week link is clicked", () => {
const value = {
activeWeekdayNumber: 3, // wednesday
};
setup(value);
const link = screen.getByRole("link", { name: "Friday" });
fireEvent.click(link);
expect(screen.getByText(/friday classes/i)).toBeInTheDocument();
});
});
The menu list is a styled link:
<li>
<HorizontalMenuLink $active={weekdayNumber === 1} to="/classes/monday">
Monday
</HorizontalMenuLink>
</li>

Using JSON.stringify on [object Object] gives "[object Object]" on React

I am trying to pass an object data from one page to another.
I have a line of code that can pass id and I tried to use it to pass object rather than an integer id but I can't get it right.
The code for passing id from source page:
const navigate = useNavigate();
id && navigate(generatePath("/employeelistedit/:id", { id })); //sample code which works fine when used
The code for passing the object data from source page copying the format of the code above:
function sourcePage(){
const navigate = useNavigate();
var values = {id: "someData", startd: "someData", endd: "someData"}
values && navigate(generatePath("/harvestcalendarmonitoring/:values", { values }));
    } //with useNavigate and generatePath
This is the code in another page which receives the data:
const { values } = useParams(); //values gives [object Object]
const x = JSON.stringify(JSON.stringify(values)) //gives "[object Object]"
const y = Object.prototype.toString.call(values) //gives [object String]
For my routing, this is how I wrote it:
<Route path="/harvestcalendarmonitoring/:values" element={< Harvestcalendarmonitoring />} /> //refers to the receiving page
I know I'm not doing it right cause I know that "[object Object]" is showing that something is wrong somewhere in my codes.
Any help and suggestions would be really much appreciated. Thank you in advance.
It looks like you missed the stringify step:
function sourcePage(){
const navigate = useNavigate();
var values = JSON.stringify({id: "someData", startd: "someData", endd: "someData"});
values && navigate(generatePath("/harvestcalendarmonitoring/:values", { values }));
} //with useNavigate and generatePath
However, make sure generatePath is also URL encoding this string values or else you will likely have an invalid URL.
When it comes time to parsing the string back into an object, be sure to call JSON.parse
With the help of Steve through his comment above/below this comment,
use JSON.stringify() for the object before passing and receive it using JSON.parse().
Code in source page:
values = JSON.stringify(values);
values && navigate(generatePath("/harvestcalendarmonitoring/:values", { values }));
Code in receiving page:
const {values} = useParams();
const w = JSON.parse(values) //the w variable gives the desired and/or expected object data

Having trouble grabbing data from MongoDB and using in new var (Next.js project)

Error I'm getting is this:
TypeError: Cannot read property 'latitude' of undefined
Here's the piece that's messing me up
const [user] = useCurrentUser();
var location = [user.latitude, user.longitude];
useCurrentUser() is here:
export function useCurrentUser() {
const { data, mutate } = useSWR('/api/user', fetcher);
const user = data?.user;
return [user, { mutate }];
}
I'm assuming I'm just calling it early, because it hasn't had a chance to go through the useCurrentUser() function yet. However, I can't for the life of me figure out how to call it when that's done?
edit:
It's also not usable later on in a component:
This works:
<span className="bold">{user ? user.latitude : ''}</span>
This doesn't:
<Map location={[user.longitude, user.latitude]}/>
What am I not understanding, hahaha

React array[0] is undefined despite the array existing when I console log it

I have the code below
import React from 'react';
import axios from 'axios'
//let termExample = "https://en.wikipedia.org/w/api.php?action=query&origin=*&format=json&generator=search&gsrnamespace=0&gsrlimit=5&gsrsearch='New_England_Patriots'";
function APIThing({ term }) {
let wikioutput = []
axios.get(term).then((response) => {for (let i in response.data.query.pages) if(wikioutput) wikioutput.push(response.data.query.pages[i])})
function safe(x){
console.log("test", x[0], x)
if (x[0]){
console.log("test2", x[0], x[0].title)
return x[0].title
}
}
return (
<>
<p>TEST {safe(wikioutput)} TEST</p>
<p>EXAMPLE {safe([{title: "Test"}])}</p>
</>
)
}
export default APIThing;
When term is a wikipedia api url, I cannot return the value of wikioutput[0]. When I log wikioutput (or x in this case) I can see the entire array of objects, but x[0] returns undefined
I came across this post when I looked for an explanation for my very similar problem. I also have an asynchronous API call that gives me an array with several objects.
`useEffect(() => {
async function fetchMailContent() {
const response = await fetch(insertapiwebsitehere.com;
if (open) {
try {
const res = await response.json();
setMailContent(res);
setLoading(false);
} catch (err) {
console.error(err);
setError(true);
setLoading(false);
}
}
}
fetchMailContent();
}, [open]);`
The array I get from this is then given to another function where I attempted to place a value of one of the array's objects into a textfield component.
`export function MainTab(){
[shortened]
return(
<TextField variant={'outlined'} type="text" value={array[0].field_entry} />
)
}`
This would then result in the error of thejonymyster: array[0] is undefined. But a console.log(array[0]) would work fine and gave me the right object.
The fix is indeed rooted in the asynchronicity. You need to ask first if the object value exists before rendering it. This can be done with a simple ? after the array[0] and fixed the issue for me. So the Textfield component looks like this:
<TextField variant={'outlined'} type="text" value={array[0]?.field_entry} />
You should really be using hooks + useEffect in ReactJS that will most likely fix your problem, you can find examples here
what i ended up doing was json stringifying the array and regexing for the first element. json stringify array didnt give empty, miraculously. if anyone knows why array existed but array[0] didnt, id love to know someday. link to this post

How to test a function that accepts a JSX.Element?

I have a type:
type button = JSX.Element | null;
and a function:
const getFirstButton = (buttonArray: button[], first: boolean) => {
if (first) {
return buttonArray[1];
}
return buttonArray.find(b => b !== null);
};
here is my test
test('getFirstButton', () => {
const buttons = // what goes here?
expect(getFirstButton(buttons, false)).toContain('button_1');
});
I need help with the second line on the test. How do i handle this?
Is this even possible?
Note: my test file is test.ts and I don't want to change it .tsx
JSX.Element is an object created by JSX syntax, i.e. React.createElement.
It can be:
const buttons = [null, null, <p/>, <div/>];
expect(getFirstButton(buttons, false)).toBe(buttons[2]);
Notice that JavaScript arrays are zero-based, so buttonArray[1] is possibly a mistake that will be detected when covering if (first) condition.

Resources