Jest tests of Promises - javascript

I need to write a Jest test for a function using Promises, which is working perfectly in the browser.
My code is as follows: a JQuery AJAX call is done, which returns a promise; when this promise is resolved, I call another function, which is also using another Promises internally!
So the code of my Jest test is globally like this:
function post(url) {
return $.ajax(url).then((result) => {
resolve(result);
});
}
function showAlert(message) {
return new Promise((resolve, reject) => {
require('anotherPackage').then(() => { // require returns a Promise
anotherPackage.showAlert(message); // will result in DOM update
}).then(resolve, reject);
});
}
function handleResponse(result) {
const promises = [];
...
promises.push(showAlert(message));
...
return Promise.all(promises);
}
test("Promises test", () => {
let response = null,
url = 'http://example.com/result.json';
return post(url).then([result, status, request] => {
response = parseResponse(result);
}).then(() => {
return handleResponse(response).then(() => {
return $('.alert').length;
}).then(value => {
expect(value).toBe(1); // doesn't work, value is 0 !!!
});
});
});
The handleResponse function is using AJAX request response, and in this use case, the function is calling another function which is using a promise internally which, when resolved, is creating an alert inside the DOM.
Actually, the test given as example doesn't work, because the expect call is done before the inner handleResponse promise is "completely resolved"!
So my question is: how can I handle this use case with Jest?
Best regards,
Thierry

Related

How to test (using Jest) a function that returns a promise that contain another promise

So, I want to test (using Jest) a function which returns a promise that contains an axios request (as another nested promise) and resolve the outer promise within then and catch blocks.
The function is similar to this:
fetchFunction() {
return new Promise((resolve, reject) => {
if (!this.valid) {
this.value = 1;
return resolve();
}
this.someModel.$get()
.then(() => {
this.value = 2;
return resolve();
})
.catch(e => {
this.value = 3;
return reject(e);
});
});
},
If you are talking about unit test, you should not test both in the same test.
Ie your test for the fetchFunction() method will have a part where you mock the this.someModel.$get() function
You can create a mock function that return a promise like e thant :
jest.fn().mockImplementation(() => return Promise.resolve('mock'))
More info on https://jestjs.io/docs/mock-functions

How to return a function as subscription from inside of a promise

I've been returning requests from my functions like this:
makeConnection(data: any) {
console.log('makeConnection');
return this.api.get(data.url, data.dataToSend);
}
So I can subscribe like this: makeConnection.subscribe();
Now I need to check if a variable named urls is set before making an API call So I thought of making a promised function like this
checkIfUrlsPresent():Promise<boolean> {
return new Promise((resolve, reject) => {
if(this.urls) {
resolve(true);
return;
}
this.api.get('/service', null).subscribe((response) => {
console.log(response);
this.urls = response.value.urls;
resolve(true);
});
});
}
And now my service functions look like this:
requestService(data) {
this.checkIfUrlsPresent().then(() => {
console.log('requestService');
let dataToSend = this.api.createFormData(data);
return this.api.post('/service/request/', dataToSend);
})
}
But now I won't be able to subscribe to my function like requestService(data).subscribe()
So my question is how to keep my present flow working while adding this promise or is there any way to check that my variable is set before making a call while keeping this flow working.
Note: The problem is that I'm making all my api calls from one files and there are too many functions that calls these APIs so I need this way so that I won't need to make changes in all my pages to check if url is present.
Almost there. need to return the promise inside the function as well
requestService(data) {
return this.checkIfUrlsPresent().then(() => { // return the promise
console.log('requestService');
let dataToSend = this.api.createFormData(data);
return this.api.post('/service/request/', dataToSend);
})
}
Have requestService return the chained promises, and call .then on it:
requestService(data) {
return this.checkIfUrlsPresent().then(() => {
console.log('requestService');
let dataToSend = this.api.createFormData(data);
return this.api.post('/service/request/', dataToSend);
})
}
// ..
instance.requestService(someData)
.then(service => service.subscribe())

How to return from a promise inside then block?

I'm trying to understand how promise's cascading properly works. For this, I created a function which returns a new Promise but has some callback functions in their scope:
exports.function1 = (params) => {
return new Promise((resolve, reject) => {
// do something sync
someFunctionAsyncWithCallback(params, (err, data) => { //async func
err ? reject(err) : resolve(data);
})
}).then(data => {
// do something sync again
anotherAsyncFunctionWithCallback(data, function (err, response) {
return err ? Promise.reject(err) : Promise.resolve(response);
// return err ? reject(err) : resolve(response); ?
});
})
}
Inside then block, how can I made a properly return in order to continue cascading process? In executor there are resolve/reject functions which I can call in order to continue chaining. But, once we are in then execution, these function aren't there - correct me if I'm wrong - and I don't know how to move on.
Any comment will be appreciated.
Avoid combining promise chains with callback-style APIs. Instead, wrap the callback-style API with a promise wrapper, which then lets you compose things reasonably.
The examples you've quoted look like NodeJS APIs. If you're using Node, v8 and higher have utils.promisify which can be used to quickly and easily wrap standard NodeJS-callback-style functions to functions returning promises.
// Get promise-enabled versions:
const promiseSomeFunctionAsyncWithCallback = utils.promisify(someFunctionAsyncWithCallback);
const promiseAnotherAsyncFunctionWithCallback = utils.promisify(anotherAsyncFunctionWithCallback);
// Use them:
exports.function1 = (params) => {
return promiseSomeFunctionAsyncWithCallback(params)
.then(promiseAnotherAsyncFunctionWithCallback);
})
};
If you're not using Node, or you're using an old version, there's nothing magic about utils.promisify, you can easily roll your own:
const promisify = f => return function(..args) {
return new Promise((resolve, reject) => {
f.call(this, ...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
Re your comment:
I have some sync code between these callback functions.. How would you handle it in your first example?
There are two styles for that:
1. Put the sync code in the then callback and chain only when you reach your next async bit:
exports.function1 = (params) => {
// Code here will run synchronously when `function1` is called
return promiseSomeFunctionAsyncWithCallback(params)
.then(result => {
// You culd have synchronous code here, which runs when
// this `then` handler is called and before we wait for the following:
return promiseAnotherAsyncFunctionWithCallback(result);
});
})
};
2. Put the sync code in its own then callback:
exports.function1 = (params) => {
// Code here will run synchronously when `function1` is called
return promiseSomeFunctionAsyncWithCallback(params)
.then(result => {
// You culd have synchronous code here, which runs when
// this `then` handler is called.
// Pass on the result:
return result;
})
.then(promiseAnotherAsyncFunctionWithCallback);
})
};
One advantage to #2 is that each distinct logical step is its own block. It does mean one additional yield back to the microtask loop at the end of this iteration of the main event loop, but that's not likely to be an issue.
You need to return another Promise:
return new Promise((res, rej) => anotherAsyncFunctionWithCallback(data, (err, data) => err ? rej(err) : res(data));
However then it would make sense to promisify the function:
const promisify = f => (...args) => new Promise((res, rej) => f(...args, (err, data) => err? rej(err) : res(data)));
const asyncF = promisify(AsyncFunctionWithCallback);
So one can do:
asyncF(1).then(asyncF).then(console.log);
you can use a flag variable for returning something.
example;
async function test(){
let flag=0;
await fetch(request).then(()=> flag=1}
if(flag==1) return;
}

Response terminates before the result could be collected from the promise

In the following snippet, lineReader listens to an event line. When a line event is received, it calls da on Parser which returns a Promise
lineReader.on('line',(line) => {
Parser.da(line).then((info) => {
});
});
lineReader.on('close',() => {
req.end();
});
Parser.da = function(line) {
return new Promise((resolve,reject) => {
geo.getLocation().then((r) => {
console.log(r); return resolve(r);
});
});
}
da function in return calls a function which also operates on Promise. What happens is that I can never see the output from geo.getLocation and readLine.on('close') gets called.
What should be the way to handle this situation?
You are not resolving the promise. When you get the result from geo service you need to resolve the data.
Take a look this
Parser.da = function(line) {
return new Promise((resolve,reject) => {
geo.getLocation().then((r) => {
console.log(r);
resolve(r);
});
});
}
Why do you not just return the Promise from geo.geoLocation() instead of wrapping it into another promise? Like this:
Parser.da = function(line) {
return geo.geoLocation();
}
Or you might want to chain "then"'s instead. Although this is synchronous
Parser.da = function(line) {
return geo.geoLocation().then(function (r) {
console.log(r);
return r; // return r to chain it to the next ".then"
});
}
One possible problem might be that a promise is asynchronous so the lineReader.on("line"); event is closed before the promise can be executed because the event is synchronous. And then the lineReader.on("close"); is executed before the promise is resolved.
Also you should always have a "catch" in your promise so you can see if there are any errors. Like this:
lineReader.on('line',(line) => {
Parser.da(line).then((info) => {
// ...do code here
}, console.error); // errors will be outputted to console
});

Node.js Can't fulfill promise (Promise { <pending> })

I m new in asynchronous coding
I m using csvtojson library and I'm trying to convert a csv file and pass the result in an other module .
The convert() function looks like this:
convert: function (csvFilePath) {
return new Promise((resolve, reject) => {
const options = { delimiter: ["|",","],
noHeader: true ,
headers: ["header1", "header2"]
}
csv(options)
.fromFile(csvFilePath)
.on('end_parsed',(convertedJson) => {
resolve(convertedJson);
})
.on("done",(error) => {
reject(error);
})
});
}
My call:
const converter = require ("./converter")();
let json;
json = converter.convert("./importSample.csv");
console.log(json);
When I execute the code I can see that the promise is still on pending state:
Promise { <pending> }
I think that I have to use the .then() function but I don't know where or how.
From converter function you are getting promise, and that object has method then. You should do something like this.
const converter = require ("./converter")();
converter.convert("./importSample.csv").then(json => {
console.log(json);
}).catch(error => {
console.log(error);
});
Here you can find nice tutorial about Promises, and here is documentation for Promises.
Promise has a fixed syntactical architecture. I'll explain it with a simple code.
var x = new Promise((resolve,reject)=>{
//here you perform an asynchronous call
resolve(value); //receive it in 'then' part of promise
reject(error): //if your operation fails due to any error, you call reject, which is handled by 'catch' part of the promise.
});
x.then((value)=>{
//this is the part which was called using resolve, and the value it receives is the value you passed as argument in resolve.
});
x.catch((error)=>{
//this part is called by reject. the error received is the value you passed inside the reject.
});
So, your function should go something like-
converter.convert("./importSample.csv").then((json)=>{
//here is your part of code which is to be handled synchronously after the promise is called.
}).catch((error)=>{
//in case any error occurs and you want your program to exit gracefully.
});

Categories

Resources