Catch block not firing all statements - javascript

My goal is to test my function: fetchStats
Expected Results: It console.logs the error and a failure message. and sets _isFetching to false.
Actual Results: The error is caught but console.logs not fired and isFetching is not set.
Code below:
fetchStats:
fetchStats() {
this._isFetching = true;
// fetch stats after building url and replacing invalid characters
return new Promise(async (resolve, reject) => {
await API.fetchStats(this.rsn)
.then(jres => {
this.skills = jres.main.skills;
this._isFetching = false;
resolve('success');
})
.catch(err => {
console.log(err);
console.log('error retreiving stats');
this._isFetching = false;
reject('Failed to retreive stats');
})
.finally(() => {
this._isFetching = false;
});
});
}
The test:
it("catches thrown errors", () => {
this.apiFetchStats.throws(new Error());
const player = Player.fromJSON(
JSON.stringify({
rsn: 'rsn',
skills: {
overall: { level: 2000 },
attack: { attack: {} },
defence: { defence: {} },
},
})
);
sinon.spy(console, "log");
player.fetchStats();
expect(this.apiFetchStats).to.be.calledOnce;
expect(this.apiFetchStats).to.have.thrown();
expect(console.log).to.have.been.calledTwice;
expect(player.isFetching()).to.be.false;
console.log.restore();
});
});
The first two expects pass fine, signalling that it has been called and did throw so it should be in catch block but console.log was not called and isFetching was not set. Does anyone have an idea on what is going wrong?
Resolved by adding expectations to .then of player.fetchStats like so:
player.fetchStats().then(() => {
expect(this.apiFetchStats).to.be.calledOnce;
expect(this.apiFetchStats).to.have.thrown();
expect(console.log).to.have.been.calledTwice;
expect(player.isFetching()).to.be.false;
})
.finally(() => { console.log.restore(); });

Related

Can not return from a function

I have a function that looks like following
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
return true;
} else {
console.log("studio not available");
return false;
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
};
however the return statements don't return anything when I use the function in the following manner
useEffect(() => {
const agent = checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("studio available is: ", agent);
}, []);
the console.log massages appear but the return statement is undefined.
any help would be appreciated.
You can not return from a callback function, as it is running asynchronously and you are not waiting for it to have a result ready.
You can however make the function itself async by returning a Promise instead of the actual result and wait until the Promise has a result ready (e.g. it is resolved):
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId, // set here the topicId which you want listen for
OnError: e => {
// react to error message (optional)
console.log("error: ", e);
reject(); // reject on failure
},
OnServiceStateChange: e => {
if (e.ConnectedAdvisers > 0) {
// there are advisers online for given topicId
console.log("studio available");
resolve(true); // resolve instead of return
} else {
console.log("studio not available");
resolve(false);
}
}
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
).then((agent) => { // then callback is called when the promise resolved
console.log("studio available is: ", agent);
}).catch(error => { // catch is called when promise got rejected
console.log('An error happened');
});
}, []);
The function servceInfo.OnServiceStateChange is a function into the object (seems to be an event).
I'd suggest declaring a variable on the checkForAvailableAgent like connected and change it's value when the event is called.
Then access it using checkForAvailableAgent.connected.
A version with async/await and try/catch
export const checkForAvailableAgent = (topicId, serviceUrl, serviceId) => {
return new Promise((resolve, reject) => {
const serviceInfo = new window.adiaLive.ServiceInfo({
topicId: topicId,
OnError: reject,
OnServiceStateChange: e => resolve(e.ConnectedAdvisers > 0)
});
serviceInfo.connect(serviceUrl, serviceId);
})
};
useEffect(() => {
(async () => {
try {
const isAvailable = await checkForAvailableAgent(
`sales_${i18n.language}`,
"https://linktoserviceurl",
"serviceid"
);
// console.log("Result", isAvailable)
} catch(e) {
console.error(e)
}
})()
// console.log("studio available is: ", agent);
}, []);
There are 2 possible reasons
you are not returning anything from checkForAvailableAgent.
After returning from the checkForAvailableAgent, it might be asynchronous function. You can use async & await.

Why is this error not ending up in my Redux Action's Catch?

In this app, I'm using async await to try handle an API call through a couple of functions. As I'm testing the sad path error routing, I'm noticing that the error does not end up in the catch.
The Action which starts the whole process
export const submitBasicDetails = form => async dispatch => {
try {
const response = await interstitialHandlerPromise(Actions.SUBMIT_FORM, form); // <- we end up here
console.log('submitBasicDetails response', response); // <-- we see the error here
const {id} = response.data;
dispatch({
type: Actions.SUBMIT_FORM,
id
});
return response;
} catch (error) {
console.log('ACTION', error); // <-- instead of here :()
dispatch({
type: Actions.SUBMIT_FORM_FAIL,
id: null,
error: 'There was a server error, please try again later.'
});
}
};
The form data then hits this interstitialHandlerPromise where we decide which API method to use:
export const interstitialHandlerPromise = (type, data) => {
const {requestMethod, config} = determineRequestMethod(type);
const requests = requestMethod(data);
return new Promise((resolve, reject) =>
interstitialHandler(requests, config)
.then(response => resolve(response))
.catch(error => {
console.log('intersitial error', error);
reject(error);
})
);
};
Finally the postForm function which is requests that is inside of the function above:
// /test will cause a 500 error since it doesn't exist
export const postForm = async (form, END_POINT = '/api/test') => {
const resPayload = form.data;
const resUrl = `${BASE_URL}${V1}${END_POINT}`;
try {
const res = await axios.post(resUrl, {...resPayload});
return {
res
};
} catch (e) {
console.log('postForm e', e); // <-- This hits because `/test` returns 500
return new Error(e.message);
}
};
Got it! Instead of returning the error, I needed to throw the error.
In the postForm function:
} catch (e) {
console.log('postInitialLoss e', e);
throw new Error(e.message);
}
Then in the intersitialHandlerPromise:
export const interstitialHandlerPromise = async (type, data) => {
const {requestMethod, config} = determineRequestMethod(type);
const requests = requestMethod(data);
try {
const response = await interstitialHandler(requests, config);
return response;
} catch(e) {
throw new Error(e); // <- this gets hit
}
};
Now finally back in the action we will end up in the catch:
} catch (error) {
console.log('ACTION (SHOULD GET HERE)!', error); // <-- WE are here! :D
dispatch({
type: Actions.SUBMIT_BASIC_DETAILS_FAIL,
initialLossCreated: false,
error: 'There was a server error, please try again later.'
});
}

How to avoid automatic reload after nested AXIOS call?

Well, I have the same problem. When I create a petition with axios it works properly, but when I have nested axios petitions the page refreshes automatically. And I can't figure out what is the problem.
I am using evt.preventDefault() and return false; but none of them seem work.
When I call this single function everything works fine:
addSport(payload) {
const path = 'http://localhost:5000/sports';
axios.post(path, payload)
.then(() => {
this.getSports();
this.message = 'Sport Added!';
this.showMessage = true;
return false;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
return false;
}
and I call it:
this.uploadImage(formData, this.addSports, payload);
But, when I try to use this one, the page is reloading after two calls are executed:
uploadImage(formData, callback, payload) {
const path = 'http://localhost:5000/upload';
axios.post(path, formData)
.then((response) => {
callback({
sport_id: payload.sport_id,
name: payload.name,
logo_url: response.data.logo_url,
});
return false;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
return false;
},
addSport(payload) {
const path = 'http://localhost:5000/sports';
axios.post(path, payload)
.then(() => {
this.getSports();
this.message = 'Sport Added!';
this.showMessage = true;
return false;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
return false;
}
The way I call it is:
this.uploadImage(formData, this.updateDeporte, payload);
Can anyone help me to figure out how can I fix that?

Why is this promise not resolving back to the caller?

I have a Vue-App which runs with Vuex and Axios. In this app I have vuex-store which handles API-calls, but a problem is that when I call the store-actions I cant chain the response in the caller.Any ideas what Im doing wrong?
Calling code:
import { FETCH_PRODUCTS, ADD_PRODUCT } from './actions.type'
methods: {
sendNewProduct () {
this.$store
.dispatch(ADD_PRODUCT, this.newProductForm)
.then(() => {
console.log('This never gets called')
})
}
}
Vuex-store:
const actions = {
[ADD_PRODUCT] (context, credentials) {
return new Promise((resolve) => {
ApiService
.post('/Products/', {
Name: credentials.Name,
Description: credentials.Description,
Price: credentials.Price
})
.then(({ data }) => {
this.$store
.dispatch(FETCH_PRODUCTS)
resolve(data)
})
.catch(({ response }) => {
console.log(response)
context.commit(SET_ERROR, 'Error adding product')
})
})
}
}
const actions = {
[ADD_PRODUCT](context, credentials) {
return ApiService.post("/Products/", {
Name: credentials.Name,
Description: credentials.Description,
Price: credentials.Price
})
.then(({ data }) => {
this.$store.dispatch(FETCH_PRODUCTS);
return data;
})
.catch(({ response }) => {
console.log(response);
context.commit(SET_ERROR, "Error adding product");
throw new Error("Error adding product");
});
}
};
I've removed the new Promise(...) because axios already creates a promise.
If added a return data in the then callback and a throw in the catch callback to let the calling api receive the data/error.
Note that the promise resolves before the FETCH_PRODUCTS completes, to make sure that action is also completed, you'd write:
.then(({ data }) => {
return this.$store.dispatch(FETCH_PRODUCTS)
.then(() => data);
})

How to remove the following promise catches without making the code hang?

The following code loops through some form fields. If the field is a file that has to be uploaded it runs an api.uploadPhotofunction (setting the payload once the photos has been uploaded). If the field is a normal input when the payload is set directly:
formFields.forEach(field => {
if (hasUploadFiles(field)) {
uploadPhotoPromise = new Promise((resolve, reject) => {
uploads.queued.push(file)
api.uploadPhoto(file, field).then(uploadedPhoto => {
uploads.finished.push(field)
if (uploads.queued.length === uploads.finished.length) {
payload[field.name] = uploadedPhoto
resolve()
} else {
reject()
}
}).catch(error => {
console.log('error:', error)
reject()
})
}).catch(error => {
console.log('error:', error)
})
} else {
payload[field.name] = field.value
}
})
Promise.all([uploadPhotoPromise]).then(values => {
// update action
}
The code works. However, all those catch make it look a bit messy.
I tried removed them but the code hangs if I remove any of them (the code inside Promise.all never runs). Why is this? And how to refactor this code without all those catch statements without making the it hang?
Original code (plus Bergi's suggested modification):
const buildingFormPromise = utils.mapDeep(this.buildingForm.schema, field => {
if (!field.name) return // fields not in the database
else if (utils.hasUploadFiles(field)) {
utils.eachCall(field.value, (file, index) => {
field.userId = this.user.id
this.uploads.queued.push(file)
this.$set(this.uploads.queued, index, { progress: 30 })
return api.uploadPhoto(file, field).then(uploadedPhoto => {
this.$set(this.uploads.queued, index, { progress: 100 })
return loadImage(uploadedPhoto, () => {
this.uploads.finished.push(field)
if (this.uploads.queued.length === this.uploads.finished.length) {
console.log('This runs after the code inside Promise.all')
buildingPayload[field.name] = uploadedPhoto
}
})
})
})
} else {
return Promise.resolve(buildingPayload[field.name] = field.value)
}
})
Promise.all([buildingFormPromise]).then(values => {
console.log('This runs before the files are uploaded')
})
You need to pass an array of all the promises into Promise.all, and you should avoid the Promise constructor antipattern. You can move the .catch to the very end if you don't want to handle individual upload failures.
var fieldValuePromises = formFields.map(field => {
if (hasUploadFiles(field)) {
return api.uploadPhoto(file, field).then(uploadedPhoto => {
return payload[field.name] = uploadedPhoto;
});
} else {
return Promise.resolve(payload[field.name] = field.value);
}
});
Promise.all(fieldValuePromises).then(values => {
// update action
}).catch(error => {
// at least one upload failed
console.log('error:', error)
});

Categories