redux-toolkit: Jest: TypeError: Cannot read properties of undefined (reading 'reducerPath')
Got the following error.
in test
it('should render form fields', async () => {
renderWithProviders(<FormBuilder {...props} />);
});
export const renderWithProviders = (element: React.ReactElement) => {
return renderer.create(<Provider store={store}>{element}</Provider>);
};
FormBuilder:
import React, { useEffect } from 'react';
import { View } from 'react-native';
import { Formik, useFormikContext } from 'formik';
import { Button } from '@/Components/Buttons/Button';
import { Checkbox, CheckboxProps } from '@/Components/Controls/Checkbox';
import { OwnAccountSelector, OwnAccountSelectorProps } from '@/Components/OwnAccountSelector';
import {
AccountSelectorFieldValues,
CheckboxFieldValues,
FormBuilderProps,
InputFieldValues,
FieldValues,
FieldTypes,
BoxInputFormProps,
SelectorFiledValues,
BaseInputFormProps,
BaseInputSubtypes,
BoxInputSubtypes,
MobileOperatorFieldValues,
} from './types';
import {
MobileOperatorCarousel,
MobileOperatorCarouselProps,
} from '@/Components/Carousels/MobileOperatorCarousel';
import { SelectPicker } from '@/Components/SelectPicker';
import { MobileOperator } from '@/Types/MobileOperator';
import { useInputFocus } from '@/Hooks/input';
import { BankAndAccountInput, BankAndAccountValue } from '../Inputs/BankAndAccountInput';
import { DatePicker } from '../Inputs/DatePicker';
import { BoxInputForm } from './BoxInputForm';
import { BaseInputForm } from './BaseInputForm';
import { BaseInputProps } from '../Inputs/BaseInputs';
import { SelectPickerProps } from '../SelectPicker/types';
import { useInstitutionColors } from '@/Hooks/institutionColors';
const FormValuesGrabber = ({
onValuesChange,
}: {
onValuesChange?: (values: any, submitForm: any) => void;
}) => {
const { values, submitForm } = useFormikContext<FieldValues>();
useEffect(() => {
onValuesChange && onValuesChange(values, submitForm);
}, [values, onValuesChange, submitForm]);
return null;
};
export function FormBuilder<ReturnType extends FieldValues>({
formRef,
fields,
initialValues = {} as ReturnType,
onSubmit,
onValuesChange,
validationSchema,
submitButtonText = 'Submit',
submitButtonStyle = {},
canProceed = true,
hideSubmit = false,
}: FormBuilderProps<ReturnType>) {
const { focusState, setFieldFocus, resetFieldFocus, resetFocusAll } = useInputFocus(fields);
const institutionColors = useInstitutionColors();
return (
<Formik
innerRef={formRef}
initialValues={initialValues}
validateOnMount
onSubmit={(values) => {
resetFocusAll();
onSubmit(values as ReturnType);
}}
validationSchema={validationSchema}
>
{({ handleSubmit, setFieldValue, setFieldTouched, values, errors, isValid, touched }) => (
<>
<FormValuesGrabber onValuesChange={onValuesChange} />
{fields.map((field) => {
if (!field.hidden) {
const {
name,
type,
subtype,
fieldProps = {},
additionalComponent,
additionalComponentProps = {},
} = field;
const AdditionalComponent = additionalComponent;
let fieldComponent = <></>;
const commonInputProps = {
isFocused: focusState[name],
onFocus: () => setFieldFocus(name),
onBlur: () => {
resetFieldFocus(name);
setFieldTouched(name);
},
error: touched[name] && errors[name] ? (errors[name] as string) : undefined,
};
switch (type) {
case FieldTypes.BASE_INPUT:
fieldComponent = (
<BaseInputForm
{...commonInputProps}
{...(fieldProps as BaseInputFormProps)}
subtype={subtype as BaseInputSubtypes}
value={(values as InputFieldValues)[name]}
onChangeText={(value) => setFieldValue(name, value)}
/>
);
break;
case FieldTypes.BOX_INPUT:
fieldComponent = (
<BoxInputForm
{...(fieldProps as BoxInputFormProps)}
{...commonInputProps}
subtype={subtype as BoxInputSubtypes}
value={(values as InputFieldValues)[name]}
onChangeText={(value) => setFieldValue(name, value)}
/>
);
break;
case FieldTypes.CHECKBOX:
fieldComponent = (
<Checkbox
{...(fieldProps as CheckboxProps)}
isActive={(values as CheckboxFieldValues)[name]}
onPress={() => setFieldValue(name, !values[name])}
/>
);
break;
case FieldTypes.OWN_ACCOUNT_SELECTOR:
fieldComponent = (
<OwnAccountSelector
{...(fieldProps as OwnAccountSelectorProps)}
selectedAccount={(values as AccountSelectorFieldValues)[name]}
onChangeSelected={(account) => setFieldValue(name, account)}
/>
);
break;
case FieldTypes.SELECTOR:
fieldComponent = (
<SelectPicker
{...(fieldProps as SelectPickerProps<any>)}
onPress={() => setFieldTouched(name)}
error={commonInputProps.error}
selectedValue={(values as SelectorFiledValues)[name]}
onChangeSelected={(item: any) => setFieldValue(name, item)}
/>
);
break;
case FieldTypes.DATE:
fieldComponent = (
<DatePicker
{...(fieldProps as BaseInputProps)}
error={commonInputProps.error}
onClose={() => setFieldTouched(name)}
value={(values as InputFieldValues)[name]}
onChangeDate={(date) => setFieldValue(name, date)}
/>
);
break;
case FieldTypes.MOBILE_OPERATOR_CAROUSEL:
fieldComponent = (
<MobileOperatorCarousel
{...(fieldProps as MobileOperatorCarouselProps)}
value={(values as MobileOperatorFieldValues)[name]}
onChangeSelected={(item: MobileOperator) => setFieldValue(name, item)}
/>
);
break;
case FieldTypes.BANK_AND_ACCOUNT:
fieldComponent = (
<BankAndAccountInput
isFocused={commonInputProps.isFocused}
onBlur={commonInputProps.onBlur}
onFocus={commonInputProps.onFocus}
error={commonInputProps.error as {}}
value={values[name] as BankAndAccountValue}
setFieldValue={(value) => setFieldValue(name, value)}
/>
);
break;
default:
break;
}
return (
<View key={name}>
{fieldComponent}
{AdditionalComponent && (
<AdditionalComponent value={values[name]} {...additionalComponentProps} />
)}
</View>
);
}
})}
{!hideSubmit && (
<Button
onPress={handleSubmit}
disabled={!isValid || !canProceed}
title={submitButtonText}
style={submitButtonStyle}
color={institutionColors.primary}
/>
)}
</>
)}
</Formik>
);
}
Store:
import EncryptedStorage from 'react-native-encrypted-storage';
import { combineReducers } from 'redux';
import {
persistReducer,
persistStore,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist';
import { configureStore, isRejectedWithValue, Middleware, MiddlewareAPI } from '@reduxjs/toolkit';
import { navigate } from '@/Navigators/Root';
// Services
import { rtkQueryService } from '@/Services/api';
// State
import commonSlice, { addError, clearError } from '@/Store/Common/commonSlice';
import profileSlice from '@/Store/Profile/profileSlice';
import userSlice from '@/Store/User/userSlice';
import catalogsSlice from '@/Store/Catalogs/catalogsSlice';
import startup from '@/Store/Startup';
import i18n from '@/Translations';
const rtkQueryErrorLogger: Middleware = (api: MiddlewareAPI) => (next) => (action) => {
if (isRejectedWithValue(action)) {
let errorMessage = i18n.t('errorLogger.defaultError');
if (action?.payload?.error) {
errorMessage = action?.payload?.error.replace('Error: ', '').toLowerCase();
action.payload.error = errorMessage;
}
api.dispatch(addError(errorMessage));
const { common } = api.getState() as RootState;
if (!common.pendingRequests.length && common.asyncError) {
navigate('PopUpModal', {
icon: 'error',
title: i18n.t('errorLogger.modalTitle'),
text: common.asyncError,
});
api.dispatch(clearError());
}
}
return next(action);
};
const reducers = combineReducers({
// State
// When adding new slice add clear slice to clearUserData() except persistConfig
startup,
common: commonSlice,
profile: profileSlice,
catalogs: catalogsSlice,
user: userSlice,
// Services
// When adding new service add it to services array inside clearRTKQueryCache()
[rtkQueryService.reducerPath]: rtkQueryService.reducer,
});
const persistConfig = {
key: 'root',
storage: EncryptedStorage,
whitelist: ['profile'],
};
const persistedReducer = persistReducer(persistConfig, reducers);
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) => {
const middlewares = getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat([rtkQueryErrorLogger, rtkQueryService.middleware]);
if (__DEV__ && !process.env.JEST_WORKER_ID) {
const createDebugger = require('redux-flipper').default;
middlewares.push(createDebugger());
}
return middlewares;
},
});
const persistor = persistStore(store);
export { store, persistor };
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 2
- Comments: 17 (4 by maintainers)
Having 0, 1 or one million endpoints is not your problem. Your
rtkQueryService
is getting imported asundefined
.There are two possible reasons for this:
jest
generally deals with imports an export incorrectlyjest
. This is the more likely option.Look for an import circle where the file with
rtkQueryService
imports from a file that also importsrtkQueryService
.Hello! I know what is your problem now! 😎 You need to make at least one endpoint under your
createApi()
, before injecting withinjectEndpoints()
! In your case, as you only have one endpoint, then you replace it under thecreateApi()
. I know this could be strange, but this is how it works.So try this:
And disable your
injectEndpoints()
.Thank you! The issue was resolved!
@arthedza I am having the same issue, how did you solve it ? I have no circular imports