I need help figuring out how to mock an axios post request. The docs have failed me. I've tried just about every combination I could find on stack overflow. All end up with either typescript errors or the undesired results shown below...
The rendered component
import React, { useState } from 'react';
import { Image, StyleSheet, Text, TextInput, View } from 'react-native';
import { Formik } from 'formik';
import * as Yup from 'yup';
import { MaterialCommunityIcons as Icon } from '@expo/vector-icons';
import { BtnMain, MainView } from '@app/components';
import { useAuthStore } from '@app/stores';
import { apiGetCurrentUser, apiLogin } from '@app/apis';
const validationSchema = Yup.object({
username: Yup.string().required('Username required'),
password: Yup.string().required('Password required')
});
export default function ScreenLogin(): JSX.Element {
const [isLoggingIn, setIsLoggingIn] = useState(false);
const [hidePassword, setHidePassword] = useState(true);
const { setIsViewerAuthenticated, setViewerInfo } = useAuthStore(store => store);
const loginHander = async (values: { username: string; password: string }): Promise<void> => {
try {
setIsLoggingIn(true);
const responseToken = await apiLogin(values.username, values.password);
if (!responseToken) {
throw new Error('Access Denied');
}
await setIsViewerAuthenticated(responseToken);
const responseViewerInfo = await apiGetCurrentUser();
await setViewerInfo(responseViewerInfo);
} catch (error: any) {
throw error;
} finally {
setIsLoggingIn(false);
}
};
return (
<MainView>
<Formik
initialValues={{
username: '',
password: '',
submitError: null
}}
validationSchema={validationSchema}
onSubmit={(values, { setErrors }) =>
loginHander(values).catch(error => setErrors({ submitError: error.message }))
}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
errors
// isValid, dirty
}) => (
<View style={styles.container}>
<Image source={require('@/assets/images/vlogo.png')} style={styles.image} />
<View style={styles.form}>
<View>
<TextInput
style={styles.inputMain}
placeholder="Username"
onBlur={handleBlur('username')}
onChangeText={handleChange('username')}
value={values.username}
/>
{errors.username && <Text style={styles.error}>{errors.username}</Text>}
</View>
<View>
<View style={styles.inputContainer}>
<TextInput
style={styles.inputPswd}
placeholder="Password"
secureTextEntry={hidePassword}
onBlur={handleBlur('password')}
onChangeText={handleChange('password')}
value={values.password}
/>
<Icon
style={styles.eyeIcon}
onPress={() => setHidePassword(!hidePassword)}
name={hidePassword ? 'eye-off' : 'eye'}
size={20}
/>
</View>
{errors.password && <Text style={styles.error}>{errors.password}</Text>}
</View>
<View>
<BtnMain
btnName="Login"
// isDisabled={isLoggingIn || !dirty || !isValid}
isLoading={isLoggingIn}
btnStyles={styles.btn}
btnTextStyles={styles.txtLogin}
onPress={handleSubmit}
/>
{errors.submitError && <Text style={styles.submitError}>{errors.submitError}</Text>}
</View>
</View>
</View>
)}
</Formik>
</MainView>
);
}
The spyon function
export async function apiLogin(username: string, password: string): Promise<string> {
try {
const result = await axiosLoginRequest([mapApiEndpoints.login, { username: username, password: password }]);
return result.data.Response;
} catch (error: any) {
throw new Error(error);
}
}
test trial #1 (axios mock)
import React from 'react';
import { render, fireEvent, waitFor, cleanup, screen } from '@testing-library/react-native';
import ScreenLogin from './ScreenLogin';
jest.mock('expo-asset');
jest.mock('expo-font');
const mockAxios = {
post: jest.fn(() => Promise.resolve({ data: {} }))
};
describe('Login screen...', () => {
afterAll(() => {
cleanup();
});
it('renders inputs and button', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
expect(userInput).toBeTruthy();
expect(passwordInput).toBeTruthy();
expect(loginButton).toBeDefined();
});
it('enter username/password and click login', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
await waitFor(() => fireEvent.changeText(userInput as never, 'test1'));
expect(userInput.props.value).toEqual('test1');
await waitFor(() => fireEvent.changeText(passwordInput as never, 'pass1'));
expect(passwordInput.props.value).toEqual('pass1');
expect(loginButton).toBeDefined();
mockAxios.post.mockImplementationOnce(() =>
Promise.resolve({
data: {
results: 'token'
}
})
);
await waitFor(() => fireEvent(loginButton, 'click'));
expect(mockAxios.post).toHaveBeenCalledTimes(1);
});
});
test trial #2 (spyon)
import React from 'react';
import { render, fireEvent, waitFor, cleanup, screen } from '@testing-library/react-native';
import ScreenLogin from './ScreenLogin';
import { apiLogin } from '@app/apis/index';
const api = { apiLogin };
jest.mock('expo-asset');
jest.mock('expo-font');
jest.spyOn(api, 'apiLogin').mockResolvedValue('token');
describe('Login screen...', () => {
afterAll(() => {
cleanup();
jest.resetAllMocks();
});
it('renders inputs and button', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
expect(userInput).toBeTruthy();
expect(passwordInput).toBeTruthy();
expect(loginButton).toBeDefined();
});
it('enter username/password and click login', async () => {
// setup
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
// enter credentials
await waitFor(() => fireEvent.changeText(userInput as never, 'test1'));
expect(userInput.props.value).toEqual('test1');
await waitFor(() => fireEvent.changeText(passwordInput as never, 'pass1'));
expect(passwordInput.props.value).toEqual('pass1');
// login
await waitFor(() => fireEvent.press(loginButton));
// results
expect(api.apiLogin).toHaveBeenCalledTimes(1);
});
});
Any help would be appreciated.