Async test with jest returning empty div - reactjs

So i have this axios test and Im getting an empty div, not sure why.
test
import React from 'react';
import ReactDOM from 'react-dom';
import TestAxios from '../test_axios.js';
import {act, render, fireEvent, cleanup, waitForElement} from '#testing-library/react';
import axiosMock from "axios";
afterEach(cleanup)
it('Async axios request works', async () => {
const url = 'https://jsonplaceholder.typicode.com/posts/1'
const { getByText, getByTestId } = render(<TestAxios url={url} />);
act(() => {
axiosMock.get.mockImplementation(() => Promise.resolve({ data: {title: 'some title'} })
.then(console.log('ggg')) )
})
expect(getByText(/...Loading/i).textContent).toBe("...Loading")
const resolvedSpan = await waitForElement(() => getByTestId("title"));
expect((resolvedSpan).textContent).toBe("some title");
expect(axiosMock.get).toHaveBeenCalledTimes(1);
expect(axiosMock.get).toHaveBeenCalledWith(url);
})
the component
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const TestAxios = (props) => {
const [state, setState] = useState()
useEffect(() => {
axios.get(props.url)
.then(res => setState(res.data))
}, [])
return (
<div>
<h1> Test Axios Request </h1>
{state
? <p data-testid="title">{state.title}</p>
: <p>...Loading</p>}
</div>
)
}
export default TestAxios;
the mock function
export default {
get: jest.fn().mockImplementation(() => Promise.resolve({ data: {} }) )
};
so Im supposed to get a p element with some text but I get nothing. I have tried many different things bt cant seem to get it work not sure why its not working

So I figured it out it turns out you have to call axios.mockresolved value before the rendering of the component, otherwise it will just use the value you provided as the default in your mock axios module.
import React from 'react';
import ReactDOM from 'react-dom';
import TestAxios from '../test_axios.js';
import {act, render, fireEvent, cleanup, waitForElement} from '#testing-library/react';
import axiosMock from "axios";
afterEach(cleanup)
it('Async axios request works', async () => {
axiosMock.get.mockResolvedValue({data: { title: 'some title' } })
const url = 'https://jsonplaceholder.typicode.com/posts/1'
const { getByText, getByTestId, rerender } = render(<TestAxios url={url} />);
expect(getByText(/...Loading/i).textContent).toBe("...Loading")
const resolvedEl = await waitForElement(() => getByTestId("title"));
expect((resolvedEl).textContent).toBe("some title")
expect(axiosMock.get).toHaveBeenCalledTimes(1);
expect(axiosMock.get).toHaveBeenCalledWith(url);
})

Related

React-testing-library show error because of axios

Learning unit-testing on the react with typescript, encountered an error when tests fall when importing axios.
screenshot error in the terminal](https://i.stack.imgur.com/dFxJU.png)
Code component
import axios from "axios";
import React, { FC, useEffect, useState } from "react";
import { IUser } from "../../types/IUsers";
const Users: FC = () => {
const [users, setUsers] = useState<IUser[]>([]);
useEffect(() => {
getUsers();
}, [users]);
const getUsers = async () => {
try {
const response = await axios.get(
"https://jsonplaceholder.typicode.com/users/"
);
const res = response.data;
setUsers(res);
} catch (error) {
console.log(error);
}
};
return (
<div data-testid="users-wrapper">
{users.map((user) => (
<div>{user.name}</div>
))}
</div>
);
};
export default Users;
Code test
import React from "react";
import { render, screen } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import Users from "./Users";
import axios from "axios";
jest.mock("axios");
describe("Testing user component", () => {
test("Show title", () => {
render(<Users />);
const usersWrapper = screen.getByTestId("users-wrapper");
expect(usersWrapper).toBeInTheDocument();
});
});
Tried install types for axios, create babel-config, create .babelrc, add `
--transformIgnorePatterns \"node_modules/(?!axios)/\""
` on the package-json. Help me please.
Add the following in package.json and try:
"jest": {
"transformIgnorePatterns": [
"node_modules/(?!axios)/"
]
},
This fixed the issue for me.
I use this pattern, and it's works for me too

lazy load for dynamic component test coverage

I have appRoutes.js file
import projectConfig from './projectConfig'
import React, {lazy} from 'react'
const Busin = lazy(() => import('./busine'))
const own = lazy(() => import(()=>import('./own'))
export const appRoutes = [{
path: projectConfig.route, component: Busin},
{path: projectConfig.route, component: own}]
own.js
import React from 'react'
const own = () => {
return <div>
<form>some child component</form>
</div>
}
export default own
appRoute.test.js
import {render, waitFor} from '#testing-library/react'
describe('test', () => {
it('lazy', () => {
const {getByText} = render(<appRoutes />)
await waitfor(() => {
expect(getByText('').tobeinthedocument()
})
})
})
How can I cover the lazy load component here in the test coverage
Looks like you are re-assigning the container returned by render, I think your test should be:
import React from 'react'
import {render, waitFor, getByText } from 'react-testing-library'
import AppRoutes from 'AppRoutes'
test('renders lazy component', async () => {
const { container } = render(<appRoutes />)
await waitFor(() => expect(getByText(container, 'I am lazy !' )).toBeInTheDocument())
})

jest mock axios doesn't provide proper mock for axios

I'm trying to provide a mock request for this class and then expect that history.push is called with some path.
Start.js
import React from 'react'
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import { ReactComponent as Arrow } from '../../arrow.svg';
export default function Start() {
let history = useHistory();
const doInitializeApp = () => {
axios.get('http://localhost:8080/api/v1/asap/start')
.then(res => {
if (res.data == true) {
history.push('/login')
} else {
alert('something went wrong. Could not start the application')
}
}).catch(err => {
alert('something went wrong. Could not contact the server!')
});
}
return (
<div>
<div className="container">
<div className="content">
<div id="box">
<h1>Welcome</h1>
<Arrow id="next" onClick={doInitializeApp} />
</div>
</div>
</div>
</div>
);
}
And this is my approach for the test
Start.test.js
import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Start from '../components/startscreen/Start';
import { ReactComponent as Arrow } from '../arrow.svg';
import axios from "axios";
Enzyme.configure({ adapter: new Adapter() });
describe('Start', () => {
it('test axios get reroute the application to path /login', () => {
const mProps = { history: { push: jest.fn() } };
const wrapper = shallow(<Start {...mProps} />);
const arrow = wrapper.find(Arrow);
const axiosSpy = jest.spyOn(axios, 'get');
//mock axios
jest.mock("axios");
//mock axios response
axios.get.mockResolvedValue({ data: true });
//simulate onclick
arrow.simulate('click');
expect(axiosSpy).toHaveBeenCalled(); --> this pass
expect(mProps.history.push).toBeCalledWith('/login'); --> this doesn't pass
})
});
However, the test did not pass because the actual axios.get(url) doesn't take the response which I mocked and it always come to the .catch(err => ... "Could not contact the server!")
What did I do wrong in here ? Because that the code didn't come to the if (res.data===true) so that I also couldn't test whether the history.push is actually called or not.
Your mocking code is fine. The code in the catch block is getting executed since useHistory() returns undefined (You can confirm this by console.logging the error inside the catch block).
One way to fix it would be to mock useHistory and pass a mock function for history.push. You can then spy on useHistory() to confirm the history.push got called with /login.
import { useHistory } from 'react-router-dom'
// other import statements omitted for brevity
jest.mock('axios')
jest.mock('react-router-dom', () => {
const fakeHistory = {
push: jest.fn()
}
return {
...jest.requireActual('react-router-dom'),
useHistory: () => fakeHistory
}
})
const flushPromises = () => new Promise(setImmediate)
describe('Start component', () => {
test('redirects to /login', async () => {
const pushSpy = jest.spyOn(useHistory(), 'push')
axios.get.mockResolvedValue({ data: true })
const wrapper = shallow(<App />)
const button = wrapper.find(Arrow)
button.simulate('click')
await flushPromises()
expect(pushSpy).toBeCalledWith('/login')
})
})
I'm using setImmediate to wait for the async action to complete as suggested here.

TypeError: patients.map is not a function React Js

I am requesting some basic info from the back end using axios but for some reason unable to render the data on screen. Below is my basic App component using hooks and a map function to return a surname
import React, { useState, useEffect } from 'react';
import FullCalendar from '#fullcalendar/react'
import dayGridPlugin from '#fullcalendar/daygrid'
import interactionPlugin from '#fullcalendar/interaction';
import { Router } from '#reach/router'
import 'bootstrap/dist/css/bootstrap.css'
import axios from 'axios'
import './custom.css'
const App = () => {
const [patients, setPatient] = useState([])
useEffect(() => {
axios.get('http://localhost:5000/api/patient').then(response => {
console.log(response.data)
setPatient(response.data)
})
}, [])
return (
<>
<div>
<ul>
{patients.map(p => (
<li>{p.surname}</li>
))}
</ul>
</div>
</>
)
}
export default App
When I check the dev tools I am bringing back all of data
I cannot see how my map function 'is not a function' can anyone help me out here please I get the below error message which is annoying
Try to use Async and Await for API call.
useEffect(function() {
async function fetchPatients() {
const response = await
fetch('http://localhost:5000/api/patient');
const json = await response.json();
setPatient(json.data);
}
fetchPatients();
}, []);
try this fixes:-
import React, { useState, useEffect } from 'react';
import FullCalendar from '#fullcalendar/react'
import dayGridPlugin from '#fullcalendar/daygrid'
import interactionPlugin from '#fullcalendar/interaction';
import { Router } from '#reach/router'
import 'bootstrap/dist/css/bootstrap.css'
import axios from 'axios'
import './custom.css'
const App = () => {
const [patients, setPatient] = useState([])
useEffect(() => {
(async () => {
try {
// fetching all patirnts
let res = await axios.get("http://localhost:5000/api/patient");
setPatient(res.data);
} catch (err) {
console.log(err);
}
})();
}, []);
return (
<>
<div>
<ul>
{patients?.map(p => (
<li>{p.surname}</li>
))}
</ul>
</div>
</>
)
}
export default App
A working code, for anyone who stupidly wastes too much time on this problem in the future. The data was nested meaning I had to setPatient to response.data.data. I was then able to pull all required info using Axios
useEffect(() => {
axios.get('http://localhost:5000/api/patient').then(response => {
setPatient(response.data.data)
})
}, [])

Mocking useHistory and expect toBeCalledWith

I wan't to check if history.push() has been called with the correct parameters in my test.
I'm not sure what's the correct way to mock useHistory()
I tried this solution. But it seems that I can't check if push() has been called.
App.tsx
import React from 'react';
import {useHistory} from 'react-router-dom';
const App: React.FC = () => {
const history = useHistory();
const onClick = () => {
history.push('/anotherPath');
};
return (
<div>
<button onClick={onClick}>click</button>
</div>
);
};
export default App;
App.test.tsx
import React from 'react';
import {render, fireEvent} from '#testing-library/react';
import App from './App';
import {useHistory} from 'react-router-dom'
jest.mock('react-router-dom', () => ({
useHistory: () => ({
push: jest.fn(),
}),
}));
test('renders learn react link', async () => {
const app = render(<App/>);
fireEvent.click(app.getByText('click'));
expect(useHistory().push).toBeCalledWith('/anotherPath');
});
Is there any way to make sure that history.push() has been called with the correct parameters?
Try this, assign the mocked push method into a variable and use that to assert if it is called with the right parameters.
import React from "react";
import { render, fireEvent } from "#testing-library/react";
import { useHistory } from "react-router-dom";
const mockHistoryPush = jest.fn();
const App: React.FC = () => {
const history = useHistory();
const onClick = () => {
history.push("/anotherPath");
};
return (
<div>
<button onClick={onClick}>click</button>
</div>
);
};
jest.mock("react-router-dom", () => ({
useHistory: () => ({
push: mockHistoryPush
})
}));
test("renders learn react link", async () => {
const { getByText } = render(<App />);
fireEvent.click(getByText("click"));
expect(mockHistoryPush).toBeCalledWith("/anotherPath");
});

Resources