How to test axios interceptors using jest? - axios

I'm trying to test the following code:
import axios from 'axios';
import { history } from './ReduxService';
axios.interceptors.response.use(response => response,
(error) => {
if ((error.response && error.response.status === 408) || error.code === 'ECONNABORTED') {
history.push('/error');
}
return Promise.reject(error);
}
);
Any advice on how to cover it?

First, modify the code so that you can pass a mocked version of axios in:
import axios, { AxiosInstance } from 'axios';
import { history } from './ReduxService';
export const addResponseInterceptor(client: AxiosInstance) => {
client.interceptors.response.use(response => response,
(error) => {
if ((error.response && error.response.status === 408) || error.code ===
'ECONNABORTED') {
history.push('/error');
}
return Promise.reject(error);
});
};
Then set up your tests like this:
import { addResponseInterceptor } from './yourInterceptorFile'
import axios from 'axios';
jest.mock('axios', () => {
return {
create: jest.fn(),
interceptors: {
request: {
use: jest.fn(),
eject: jest.fn(),
},
response: {
use: jest.fn(),
eject: jest.fn(),
},
}
};
});
describe('addResponseInterceptor tests', () => {
beforeEach(() => {
(axios.create as jest.Mock).mockReset();
(axios.interceptors.request.use as jest.Mock).mockReset();
(axios.interceptors.request.eject as jest.Mock).mockReset();
(axios.interceptors.response.use as jest.Mock).mockReset();
(axios.interceptors.response.eject as jest.Mock).mockReset();
});
it('should add a response interceptor to the axios instance', () => {
addResponseInterceptor(axios);
expect(axios.interceptors.response.use).toHaveBeenCalled();
});
it('should push to history when an error occurs', async() => {
const historySpy = jest.spyOn(history, 'push');
const axiosClient = axios;
addResponseInterceptor(axios);
const interceptorErrorHandler = (axiosClient.interceptors.response.use as jest.Mock).mock.calls[0][1];
try {
await interceptorErrorHandler({
response: {
status: 408
}
});
//this should not be called--the promise should be rejected
expect(true).toBe(false);
} catch {
expect(historySpy).toHaveBeenCalledWith('/error');
}
});
. . .
});

Related

may i use this code for finduserDetail in react using redux-thunk dispatch to calling function

This code for finduserdetail by hitting function with help of dispatch method and passing (match.params.id) not working, even function not called as I know on dispatch method it should be called how may I call this so that our stat could be update in root.js file then I can show detail on my ui by using useselector
in productDetail.js
import { Fragment } from "react";
import Carousel from "react-material-ui-carousel";
import {useParams} from "react-router-dom";
import './ProductDetails.css';
import {useDispatch} from "react-redux";
import { useEffect } from "react";
import { getProductDetail } from "../../actions/productAction";
const ProductDetails = () =>{
console.log("hello this is detail")
const dispatch= useDispatch();
let params = useParams()
const id= params.id;
// const {product,error} = useSelector(
// (state) =>state.product)
useEffect(()=>{
dispatch(getProductDetail(id))
}, [dispatch, id])
return(
<Fragment>
<div className="ProductDetails">
<div>
<Carousel>
{/* {product.images &&
product.images.map((item,i)=>{
<img
className="CarouselImage"
key={item.url}
src={item.url}
alt={`${i} Slide`}
/>
})} */}
</Carousel>
</div>
</div>
</Fragment>
);
};
export default ProductDetails
and using for calling api for findproductDetail using getProductDetail in productAction.js
export const getProductDetail =(id) => async (dispatch) => {
try {
dispatch({ type: PRODUCT_DETAILES_REQUEST });
const { data } = await axios.get(`/api/v1/product/${id}`);
dispatch({
type: PRODUCT_DETAILES_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: PRODUCT_DETAILES_FAIL,
payload: error.response.data.message,
});
}
};
and one other file productReducer.js
export const productDetailsReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_DETAILES_REQUEST:
return {
loading: true,
...state,
};
case PRODUCT_DETAILES_SUCCESS:
return {
loading: false,
product: action.payload.product,
};
case PRODUCT_DETAILES_FAIL:
return {
loading: false,
error: action.payload,
};
and another file store.js
import { legacy_createStore as createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { productDetailsReducer, productReducer } from './reducers/productReducer';
const reducer = combineReducers({
products: productReducer,
product: productDetailsReducer,
});
let initialState ={};
const middleware = [thunk];
const store = createStore(reducer, initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
These stuff not working. I am quite beginner in React programming
import { Fragment } from "react";
import Carousel from "react-material-ui-carousel";
import {useParams} from "react-router-dom";
import './ProductDetails.css';
import {useDispatch} from "react-redux";
import { useEffect } from "react";
import { getProductDetail } from "../../actions/productAction";
const ProductDetails = () =>{
console.log("hello this is detail")
const dispatch= useDispatch();
let params = useParams()
const id= params.id;
// const {product,error} = useSelector(
// (state) =>state.product)
useEffect(()=>{
dispatch(getProductDetail(id))
}, [dispatch, id])
return(
<Fragment>
<div className="ProductDetails">
<div>
<Carousel>
{/* {product.images &&
product.images.map((item,i)=>{
<img
className="CarouselImage"
key={item.url}
src={item.url}
alt={`${i} Slide`}
/>
})} */}
</Carousel>
</div>
</div>
</Fragment>
);
};
export default ProductDetails
and using for calling api for findproductDetail using getProductDetail in productAction.js
export const getProductDetail =(id) => async (dispatch) => {
try {
dispatch({ type: PRODUCT_DETAILES_REQUEST });
const { data } = await axios.get(`/api/v1/product/${id}`);
dispatch({
type: PRODUCT_DETAILES_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: PRODUCT_DETAILES_FAIL,
payload: error.response.data.message,
});
}
};
and one other file productReducer.js
export const productDetailsReducer = (state = { product: {} }, action) => {
switch (action.type) {
case PRODUCT_DETAILES_REQUEST:
return {
loading: true,
...state,
};
case PRODUCT_DETAILES_SUCCESS:
return {
loading: false,
product: action.payload.product,
};
case PRODUCT_DETAILES_FAIL:
return {
loading: false,
error: action.payload,
};
and another file store.js
import { legacy_createStore as createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { productDetailsReducer, productReducer } from './reducers/productReducer';
const reducer = combineReducers({
products: productReducer,
product: productDetailsReducer,
});
let initialState ={};
const middleware = [thunk];
const store = createStore(reducer, initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
The most important stuff my redux state is not updating because of id matching condition once if
state will get update I am checking using redux tool

Testing tanstack-react-qiery - (wait for is not a function in renderHook)

I want to test this customHook but it's showing me an error of
waitFor is not a function.
import { QueryClient, QueryClientProvider } from "#tanstack/react-query";
import { renderHook } from "#testing-library/react";
import { useCustomHookForTestCase } from "../useQueryCustomHook";
const queryClient = new QueryClient();
// jest.mock('../../../Utilies/Apicall')
describe('Testing custom hooks of react query', () => {
const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
it('Should get data of todos from api',async () => {
const { result, waitFor } = await renderHook(() => useCustomHookForTestCase(), { wrapper });
await waitFor(() => result.current.isSuccess );
expect(result.current.data).toEqual("Hello");
})
})
export function useCustomHookForTestCase() {
return useQuery(['customHook'], () => 'Hello');
}
only renderHook from react-hook-testing-library returns a waitFor method. It is not available if you use the one from #testing-library/react-hooks. For that to work, you just need to import waitFor directly. You also can't return a boolean directly, but it needs to be a promise:
import { renderHook, waitFor } from '#testing-library/react'
await waitFor(() => expect(result.current.isSuccess).toBe(true))

react-admin authentication flow not working

Working on authentication react-admin using JWT token and storing in memory as closure variable.
Creating AuthToken from Django works fine, it's emitting the Token with user info as JSON.
Here is inMemoryJWT Manager
File: inMemoryJwt.js
// https://github.com/marmelab/ra-in-memory-jwt
const inMemoryJWTManager = () => {
let inMemoryJWT = null;
let isRefreshing = null;
// Token code goes here
const getToken = () => inMemoryJWT;
const setToken = (token) => {
inMemoryJWT = token;
return true;
};
const ereaseToken = () => {
inMemoryJWT = null;
return true;
}
return {
ereaseToken,
getToken,
setToken,
}
};
export default inMemoryJWTManager();
File: AuthProvider.js
oryJWTManager';
const authProvider = {
login: ({ username, password }) => {
const request = new Request('http://127.0.0.1:8000/api/token-auth/', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request)
.then(response => {
console.log('Response status ' + response.status )
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
console.log('response.json is ');
console.log(response.json());
return response.json();
})
.then(({ token }) => {
console.log(' about to set token in storage .......... ');
JwtManager.setToken(token);
const decodedToken = decodeJwt(token);
# Its not setting in localstorage
localStorage.setItem('token', token);
console.log('Token from lS : -------------',localStorage.getIdentity('token'));
localStorage.setItem('permissions', decodedToken.permissions);
})
.then(auth => {
localStorage.setItem('auth', JSON.stringify(auth));
})
.catch(response => {
console.log('Catch : ' );
console.log(response.json());
throw new Error('Network errorkkkkkkkkkkkkk')
});
},
logout: () => {
localStorage.setItem('not_authenticated', true);
localStorage.removeItem('auth');
localStorage.removeItem('role');
localStorage.removeItem('login');
localStorage.removeItem('user');
localStorage.removeItem('avatar');
inMemoryJWT.ereaseToken();
return Promise.resolve();
},
checkError: ({ status }) => {
if (status === 401 || status === 403) {
inMemoryJWT.ereaseToken();
return Promise.reject();
}
return Promise.resolve();
// return status === 401 || status === 403
// ? Promise.reject( { redirectTo: '/login' , logoutUser: false} )
// : Promise.resolve();
},
checkAuth: () => {
return inMemoryJWT.getToken() ? Promise.resolve() : Promise.reject({ redirectTo: '/no-access', message: 'login.required' });
// localStorage.getItem('auth')
// ? Promise.resolve()
// : Promise.reject({ redirectTo: '/no-access', message: 'login.required' }),
},
getPermissions: () => {
return inMemoryJWT.getToken() ? Promise.resolve() : Promise.reject();
// const role = localStorage.getItem('permissions');
// return role ? Promise.resolve(role) : Promise.reject();
},
getIdentity: () => {
try {
const { id, fullName, avatar } = JSON.parse(localStorage.getItem('auth'));
return Promise.resolve({ id, fullName, avatar });
} catch (error) {
return Promise.reject(error);
}
// return {
// id: localStorage.getItem('login'),
// fullName: localStorage.getItem('user'),
// avatar: localStorage.getItem('avatar'),
// };
},
}
export default authProvider;
App.js
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const { token } = JSON.parse(localStorage.getItem('auth'));
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
};
const dataProvider = drfProvider('http://127.0.0.1:8000/api/token-auth/', httpClient);
const App = () => (
<Admin title=""
loginPage={Login}
catchAll={NotFound}
logoutButton={PfsLogoutButton}
authProvider={authProvider}
dataProvider={dataProvider}
catchAll={NotFound}
dashboard={Dashboard} disableTelemetry>
The problem
The problem I am facing is to, It's not setting in-memory or local storage, also I get the message in the footer on the login button clicked. I am breaking my head with it for quite some time. Let me know if you need any other info.
Login required error for every page
HTTP Response goes here

How can I render fetched information from a REST API in React Native?

I'm developing a screen which fetches Spotify playlists of a particular category, and prints the playlist information (and then allows the user to play them). I'm currently just trying to print the names of the playlists, but I'm getting an error. The playlists are being fetched just fine, as I can see in my console, though I'm struggling to render those details onto the screen.
This is my code:
import React, { Component } from 'react'
import { StyleSheet, SafeAreaView, ActivityIndicator, View, Text, Image} from 'react-native';
import * as Font from 'expo-font';
import styled from 'styled-components';
import { Ionicons } from '#expo/vector-icons';
import dimensions from '../components/ScreenSize';
import colours from '../components/Colours';
import { Audio } from 'expo-av';
import { FlatList, TouchableHighlight } from 'react-native-gesture-handler';
const client_id = {Client_ID}
const client_secret = {Client_Secret}
const item = ({item}) => (
<View>
<TouchableHighlight onPress={() => this.fetchTracks(item.id)}>
<View>
<Text {{uri:item.name}}/>
</View>
</TouchableHighlight>
</View>
)
export default class HomeScreen extends React.Component {
state={
fontsLoaded:false,
}
async componentDidMount() {
await Font.loadAsync({
'montserrat-regular': require('../assets/fonts/Montserrat/Montserrat-Regular.ttf'),
'montserrat-light': require('../assets/fonts/Montserrat/Montserrat-Light.ttf'),
'montserrat-semibold': require('../assets/fonts/Montserrat/Montserrat-SemiBold.ttf'),
'montserrat-bold': require('../assets/fonts/Montserrat/Montserrat-Bold.ttf'),
}
).then(() => this.setState({ fontsLoaded:true}))
this.getToken();
this.setAudio();
}
constructor (props) {
super(props)
this.playbackInstance=null;
this.state = {
playing:false,
token: '',
DATA:[],
};
}
setAudio=() => {
Audio.setAudioModeAsync({
allowsRecordingIOS:false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false,
});
}
componentDidCatch(error, info)
{
console.log(error, info.componentStack);
}
getToken = async() =>
{
try
{
const getspotifytoken = await fetch("https://accounts.spotify.com/api/token",
{
method:'POST',
body: `grant_type=client_credentials&client_id=${client_id}&client_secret=${client_secret}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const spotifytoken = await getspotifytoken.json();
this.setState({
token: spotifytoken.access_token
});
console.log(this.state.token);
}
catch(err)
{
console.log("Error fetching data ", err);
}
}
search = async () => {
try
{
const query = "mood"
console.log('Searching: ', query)
const spotifyApiCall = await fetch(`https://api.spotify.com/v1/browse/categories/${query}/playlists?`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${this.state.token}`,
"Content-Type":'application/json'
}
})
const spotify = await spotifyApiCall.json();
console.log("Items ", spotify);
this.setState({
DATA: spotify.playlists.items,
})
}
catch (err)
{
console.log("Error fetching data ", err);
}
}
fetchTracks = async (playlistId) => {
console.log('Playlist ', playlistId)
try
{
const getplaylist = await fetch(`https://api.spotify.com/v1.playlist/${playlistId}`,
{
method:'GET',
headers: {
Accept:"application/json",
Authorization:`Bearer ${this.state.token}`,
"Content-Type":"application/json"
}
});
const playlist = await getplaylist.json();
console.log('music ', playlist.tracks.items[0].preview_url);
}
catch (err)
{
console.log("Error fetching data ", err);
}
}
async _loadNewPlaybackInstance(playing, track) {
if(this.playbackInstance != null)
{
await this.playbackInstance.unloadAsync();
this.playbackInstance.setOnPlaybackStatusUpdate(null);
this.playbackInstance = null;
}
const source = {uri: track};
const initialStatus = {
shouldPlay: true,
rate: 1.0,
shouldCorrectPitch: true,
volume: 1.0,
isMuted: false
};
const {sound, status} = await Audio.Sound.createAsync(
source.initialStatus);
this.playbackInstance=sound;
this.playbackInstance.setIsLoopingAsync(false);
this.playbackInstance.playAsync();
if (this.state.selected === playlistId) {
console.log("Playing, so stop");
this.setState({selected:null});
this.playbackInstance.pauseAsync();
return;
}
this.setState({ selected:playlistId});
this._loadNewPlaybackInstance(true, playlist.tracks.items[0].preview_url);
}
render() {
if(!this.state.fontsLoaded ) {
return null
}
return (
<Container>
<Titlebar>
<Title>Music</Title>
</Titlebar>
<HeaderBar2>
<TouchableHighlight onPress={() => this.search()}>
<Header2>Playlists for your Mood</Header2>
</TouchableHighlight>
</HeaderBar2>
<View>
<FlatList
data = {this.state.DATA}
renderItem={item}
keyExtractor = {item.id}
numColumns={2}
extraData = {this.state}
/>
</View>
</Container>
);
}
}
And the error is: Unexpected token, expected '...' (18:23)
which is in reference to the line which reads <Text {{uri:item.name}}/>
I've also tried <Text {{item.name}}/>, as well as a few other alternatives, but I can't quite figure it out.
Thank you for reading, I'd really appreciate any help!
The thing you are doing wrong is you can not use Text like you are using above, check with this:
replace
<Text {{uri:item.name}}/>
to
<Text>{item.name}</Text>
Hope this helps!

Ionic 2 native Http plugin not returning response

I am using Ionic 2 HTTP native plugin and running http.post from a provider. The data is received from the API in the provider but does not seem to be being sent back to the page component. I get error:
TypeError: undefined is not an object (evaluating
'this.authProvider.login(formData).then')
PAGE: login.ts
doLogin() {
this.spinner = 'true';
this.authProvider.login(formData).then((result:any) => {
if(result.status == 'isTrue') {
this.storage.set('userId', result.userId);
this.storage.set('userToken', result.token);
this.storage.set('profilePic', result.profilepic);
this.storage.set('userUsername', result.username);
this.navCtrl.setRoot(TabsPage);
}
else {
this.presentToast('Incorrect email or password, try again');
console.log('not a user');
}
this.spinner = 'false';
}, (err) => {
});
}
PROVIDER: authProvider
login(data) {
if (this.platform.is('ios'))
{
this.http2.post(this.apiUrl+'/api/login', data, {})
.then((dataresult) => {
return dataresult; // this outputs ok in console.log, but doesnt
return back to page
//console.log(dataresult);
})
.catch(error => {
});
}
}
You should have to return promise from authProvider,
return new Promise(resolve => {
this.http2.post(this.apiUrl+'/api/login', data, {})
.subscribe(dataresult => {
resolve(dataresult);
});
});