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
Related
I am trying to fetch some data from a Football API. For example, the countries that are provided by the API. I could get the data in the console.log but as soon as I try to render it, I get this error : Uncaught TypeError: (0 , axios__WEBPACK_IMPORTED_MODULE_0__.useState) is not a function or its return value is not iterable.
Here is the code :
import axios from 'axios';
import './App.css';
import { useState } from 'axios';
import React from 'react';
function Ui() {
const [country, setCountry] = useState('')
const options = {
method: 'GET',
url: 'https://api-football-v1.p.rapidapi.com/v3/countries',
headers: {
'X-RapidAPI-Key': '',
'X-RapidAPI-Host': 'api-football-v1.p.rapidapi.com'
}
};
const getCountry = () => {
axios.request(options).then(function (res) {
setCountry(res.data.response);
}).catch(function (error) {
console.error(error);
})
}
return (
<>
<button onClick={getCountry}>Get Country</button>
<p>{country}</p>
</>
);
}
export default Ui;
You're trying to import useState from Axios instead of React.Change it to this: import React, {useState} from 'react'; and for Axios: import axios from 'axios'; You're also importing axios twice.
You shouldn't add the api-key here either. You might want to look at your code again.
Example to print all the country names (put in the API key where it says API-KEY):
import axios from "axios";
import React, { useState, useEffect } from "react";
function Ui() {
const [country, setCountry] = useState([]);
useEffect(() => {
axios
.get("https://api-football-v1.p.rapidapi.com/v3/countries", {
headers: {
"X-RapidAPI-Key": "API-KEY"
}
})
.then((res) => {
setCountry(res.data.response);
})
.catch((err) => console.log(err));
}, []);
return (
<>
<button>Get Country</button>
<div>
{country.map((data, i) => (
<h1>{data.name}</h1>
))}
</div>
</>
);
}
export default Ui;
I want to develop a PayPal button, and following the API documentation, I have the following code:
import React, {useState}from 'react';
import {useDispatch} from 'react-redux';
import {useHistory} from 'react-router-dom';
import ReactDOM from 'react-dom';
import * as actionsReservations from '../../reservation/actions';
import {Errors} from '..';
const PayPalButton = paypal.Buttons.driver("react", { React, ReactDOM });
const PayPalReserve = ({deposit, menu, companyId, reservationDate, periodType, diners}) => {
const dispatch = useDispatch();
const history = useHistory();
const [backendErrors, setBackendErrors] = useState(null);
const createOrder = (data,actions) => {
return actions.order.create({
purchase_units:[
{
amount:{
value: deposit
},
},
],
});
};
const onApprove = (data, actions) => {
return actions.order.capture().then(response => {
dispatch(actionsReservations.reservation(
menu.id,
companyId,
reservationDate,
periodType,
diners,
response.id,
() => history.push('/reservation/reservation-completed'),
errors => setBackendErrors(errors)
));
console.log(response);
});
}
};
export default PayPalReserve;
But is throwing me the following error:
Line 9:22: 'paypal' is not defined no-undef
But if I import paypal from paypal-checkout with this line:
import paypal from 'paypal-checkout';
React throw me the following error:
"TypeError: paypal_checkout__WEBPACK_IMPORTED_MODULE_4___default.a.Buttons is undefined"
My index.html i have this in head tag:
<script defer src="https://www.paypal.com/sdk/js?client-id=MY_CLIENT_CODE"></script>
I dont know why is throwing me these errors when i don't import paypal-checkout or when I import it. If you knew how to solve it, I would appreciate it
Thanks.
Things will probably become easier for you if you simply use the official react-paypal-js package.
Here is the storybook .. copying its example:
import { PayPalScriptProvider, PayPalButtons } from "#paypal/react-paypal-js";
<PayPalScriptProvider options={{ "client-id": "test" }}>
<PayPalButtons
style={{ layout: "horizontal" }}
createOrder={(data, actions) => {
return actions.order.create({
purchase_units: [
{
amount: {
value: "2.00",
},
},
],
});
}}
/>;
</PayPalScriptProvider>
That’s an eslint err, just add window at the beginning as PayPal is a global variable injected into the window obj
console.log(window.paypal)
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.
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)
})
}, [])
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);
})