I'm trying to use jest to write tests for when i use fetch to get and post data from a react page.
the current test is something like this - I'm confused about how to make sure the correct data is passed through the specific component.
The test itself is looking like this at the moment,
import React from 'react';
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import getSettings from 'components/onboarding/stages/useGetSettingsData.js'
import updateSettings from 'components/onboarding/stages/useUpdateSettings.js'
import Settings from 'components/onboarding/stages/useUpdateSettings.js'
describe('get settings', () => {
it('should load settings with no issues', () => {
getSettings().mockResolvedValueOnce({
power: 'magical',
potion: 'magilin',
potion_times: '[]',
potion_activity_times: '',
gender: 'Male',
age: '30',
});
const {getByText, getByLabelText,debug} = render( <Settings/> )
expect(heading).toBeInTheDocument();
expect(GetSettings)
});
});
Testing this get user data function,
import { useEffect, useState } from 'react';
export default function getSettings(auth) {
const [settings, setSettings] = useState(false);
useEffect(() => {
try {
const idToken = auth.getIdToken();
const response = fetch('/api/dashboard/get-settings', {
method: 'GET',
headers: new Headers({
Authorization: `Bearer ${idToken}`,
'Content-Type': 'application/x-www-form-urlencoded',
}),
});
const jsonReponse = response.json();
if (!jsonReponse.error) {
setSettings(jsonReponse.settings);
} else {
setSettings({
power: 'magical',
potion: 'magilin',
potion_times: '[]',
potion_activity_times: '',
gender: 'Male',
age: '30',
});
}
} catch (error) {
console.log('Error happened in decrypting token', error);
}
}, [settings]);
return settings;
};
and this for setting settings,
import { loadingReducer } from 'components/onboarding/stages/loadingReducer';
export default function updateSettings({ newSettings, auth, initialState }) {
const [state, dispatch] = loadingReducer(loadingReducer, initialState);
dispatch({ type: 'LOADING' });
try {
const idToken = auth.getIdToken();
const response = fetch('/api/dashboard/set-settings', {
method: 'POST',
headers: new Headers({
Authorization: `Bearer ${idToken}`,
'Content-Type': 'application/json',
}),
body: JSON.stringify(newSettings),
});
// const jsonReponse = response.json();
// TODO: Create Notification Toast
} catch (error) {
console.log('Error happened in decrypting token', error);
}
dispatch({ type: 'FINISHED' });
return { isLoading: state.isLoading };
}
Into a component like this,
function SettingsDetails() {
const auth = useAuth();
const { register, handleSubmit, errors } = useForm();
const initialState = {
isLoading: false,
};
// const settings = false
const settings = getSettings(auth);
const onSubmit = (data) => {
console.log('Submitting..');
const isLoading =updateSettings(data,initialState);
return isLoading
};
return (
<div className="md:grid md:grid-cols-3 md:gap-6">
<div className="md:col-span-1">
<h3 className="text-lg font-medium leading-6 text-gray-900">
Preferences
</h3>
<p className="mt-1 text-sm leading-5 text-gray-500">
Set your Personal Preferences
</p>
</div>
<div className="mt-5 md:mt-0 md:col-span-2">
<form onSubmit={handleSubmit(onSubmit)}>
<Dropdown
data-testid = "power"
registerInput={register}
optionLabel="power"
selectedOption={settings.power}
optionName="power"
allOptions={['ice', 'fire', 'Both']}
/>
.....
<button
className="inline-flex items-center justify-center px-5 py-2 border border-transparent text-base leading-6 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:text-indigo-600 hover:bg-indigo-50 focus:outline-none focus:shadow-outline focus:border-indigo-300 transition duration-150 ease-in-out"
// variant={props.buttonColor}
// size={props.inputSize}
type="submit"
disabled={initialState.isLoading}
>
{initialState.isLoading ? (
<>
<span>Saving</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className="animate-spin h-4 w-4 ml-3 fill-current"
>
<path d="M0 11c.511-6.158 5.685-11 12-11s11.489 4.842 12 11h-2.009c-.506-5.046-4.793-9-9.991-9s-9.485 3.954-9.991 9h-2.009zm21.991 2c-.506 5.046-4.793 9-9.991 9s-9.485-3.954-9.991-9h-2.009c.511 6.158 5.685 11 12 11s11.489-4.842 12-11h-2.009z" />
</svg>
</>
) : (
<span>Save</span>
)}
</button>
I'm getting a bit lost on the best approach to try and mock this get data functionality - any best practices?
I just want to make sure that the page renders, that it renders the data from the getdata function, and that it saves data on submission to the form properly. Really hard to find any resources online that sort of go through this.
Currently I get this error when running the test,
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Thanks in advance,
update to current test with new error (thanks for help so far!). The test for mocking those functions work now! I would like to be able to mock the functions themselves to ensure they are working as well though.
the loading reducer for updating settings is this btw,
export default function loadingReducer(state, action) {
switch (action.type) {
case 'LOADING':
return { isLoading: true };
case 'FINISHED':
return { isLoading: false };
default:
return state;
}
}
getSettingshook properly (callinggetSettings()is what's causing theInvalid hook callerror). I suggest having a look at Mocking React custom hook with Jest.