Node.js - Mock result of a promise - javascript

I want to mock the result of a function within a node module so that i can run assertions.
Considering the following node module:
const doPostRequest = require('./doPostRequest.js').doPostRequest;
const normalizeSucessResult = require('./normalizer.js').normalizeSucessResult;
const normalizeErrorResult = require('./normalizer.js').normalizeErrorResult;
exports.doPost = (params, postData) => {
return doPostRequest(params, postData).then((res) => {
const normalizedSuccessResult = normalizeSucessResult(res);
return normalizedSuccessResult;
}).catch((err) => {
const normalizedErrorResult = normalizeErrorResult(err);
return normalizedErrorResult;
})
}
The function doPostRequest returns a promise. How can i fake the return value of this promise so that i can assert if normalizeSucessResult has been called?
So for i have tried:
const normalizeSucessResult = require('./normalizer.js');
const doPostRequest = require('./doPostRequests.js');
const doPost = require('./doPost.js');
it('runs a happy flow scenario', async () => {
let normalizeSucessResultStub = sinon.stub(normalizeSucessResult, 'normalizeSucessResult');
let postData = { body: 'Lorum ipsum' };
let params = { host: 'someUrl', port: 433, method: 'POST', path: '/' };
sinon.stub(doPostRequest, 'doPostRequest').resolves("some response data"); //Fake response from doPostRequest
return doPost.doPost(params, postData).then((res) => { //res should be equal to some response data
expect(normalizeSucessResultStub).to.have.been.calledOnce;
expect(normalizeSucessResultStub).to.have.been.with("some response data");
});
});
The doPostRequest module looks like this:
const https = require('https')
module.exports.doPostRequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}

You can use Promise.resolve to return a promise with any given value.
Promise.resolve(“hello world”);

For stub your func you need to do like this
sinon.stub({doPostRequest}, 'doPostRequest').resolves("some response data")

Okay, i figured it out. The function doPostRequest was loaded using require, on the top of the file using const doPostRequest = require('./doPostRequest.js').doPostRequest;
In order to mock the data that comes back from a function that is loaded using require i had to use a node module called mock-require. There are more modules that can take care of this (proxyquire is a populair one) but i picked mock-require (i did not have a specific reason for choosing mock-require).
For anyone else that is stuck with a similar problem, try mock-require to mock the respose from files that are loaded using require.

Related

Attempting to retrieve JSON data results in [ Promise { <pending> }, Promise { <pending> } ]

I am attempting to retrieve the JSON data stored in the following API:
https://api.hatchways.io/assessment/blog/posts
Using node.js and https requests, I constantly receive an array of [ Promise { }, Promise { } ]. Unfortunately, I can only search by one tag at a time, and I have to retrieve a list of posts that has at least one tag from a list of provided tags, before sorting the posts. My code is below:
const express = require('express');
const app = express();
app.get("/api/ping", (req, res) => {
res.status(200).send("{\"success\": true}");
})
app.get("/api/posts", (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
let posts_array = [];
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
console.log(posts_array);
})
app.listen(3000, () => console.log("Listening on port 3000..."));
function getPost(tag) {
const https = require('https');
return new Promise( (resolve, reject) => {
const options = {
hostname: 'api.hatchways.io',
path: `/assessment/blog/posts?tag=${tag}`
}
let body = [];
const req = https.request(options, res => {
res.on('data', data => {
body.push(data);
});
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch (error) {
reject(error);
}
resolve(body);
});
});
req.on('error', error => {
reject(error);
});
req.end();
}).then(function(data) { return data; }, function(error) { console.log(error) });
}
the getPost method is returning a promise, just do this:
app.get("/api/posts", async (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
const promises = [];
tag_array.forEach(tag => {
promises.push(getPost(tag))
});
posts_array = await Promise.all(promises)
console.log(posts_array)
})
Just wait for all promises to resolve using the await keyword.
app.get("/api/posts", async (req, res) => { // Add the async keyword
//... Skipped some code for concision
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
await Promise.all(posts_array) // Add this to await all promises to resolve
console.log(posts_array);
})

How can I "encapsulate" this code into a module so it could become reusable?

I have got this Node.JS snippet and would like to write it as a module, so I can use recaptcha in different parts of my system.
This is how it currently looks like:
app.post('/register_user', (req, res) => {
const secret_key = process.env.RECAPTCHA_SECRET;
const token = req.body.recaptcha;
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
fetch(url, { method: "post",})
.then((response) => response.json())
.then((google_response) => {
if (google_response.success == true) {
res.format({'text/html': () => res.redirect(303, '/register'),})
} else {
return res.send({ response: "Failed" });
}
})
.catch((error) => {
return res.json({ error });
});
})
I have tried to write the following module which works absolutely great, but I have absolute no idea about how to call it from the app.post, since I always get undefined as return:
import fetch from 'node-fetch';
export function fetch_out(url, timeout = 7000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
export async function checkRecaptcha(token, secret_key){
const url = "https://www.google.com/recaptcha/api/siteverify?secret=" + secret_key + "&response=" + token;
try{
const response = await fetch_out(url, 1000);
const google_response = await response.json();
}catch(error){
return error;
}
return google_response;
}
Any help would be appreciated! Thanks!
You could make this method reusable by removing the framework actions that need to happen and only return if the validation was successful or not. This way, it will be reusable in another project that doesn't use a specific framework.
Example module;
export async function checkRecaptcha(token, secret_key) {
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
const response = await fetch(url, { method: "post",});
if (!response.ok) return false;
const json = await response.json();
if (!json.success) return false;
return true;
}
Usage:
import { checkRecaptcha } from "./some-file-name";
app.post('/register_user', async (req, res) => {
const isHuman = await checkRecaptcha(req.body.recaptcha, process.env.RECAPTCHA_SECRET);
if (!isHuman) {
return res.send({ response: "Failed" });
}
return res.format({'text/html': () => res.redirect(303, '/register'),});
});
If you specifically want to call an action after the validation, you can also use successful and error callbacks.

How to add a new then after fetch has run

I have a method that runs a fetch request and then saves the result or error like this:
saveTema() {
this.gateway.editTema(this.state.tema)
.then(tema => {
this.setState({
tema,
error: null,
isDirty: false,
});
})
.catch(httpOrOtherError => {
if (httpOrOtherError.status) {
if (httpOrOtherError.status === 400) {
httpOrOtherError.json().then(result => {
const serverValidationfailures =
this.transformValideringsfeil(result.valideringsfeil);
this.setState({
error: {
valideringsfeil: {...serverValidationfailures},
},
showActivationDialog: false,
})
});
} else {
this.setState({
error: {httpError: {status: httpOrOtherError.status, statusText: httpOrOtherError.statusText}},
showActivationDialog: false,
});
}
} else {
this.setState({
error: {fetchReject: {message: httpOrOtherError.message}},
showActivationDialog: false,
})
}
})
}
And this is the fetch request itself:
editTema(tema) {
return fetch(
this.temaUrl(tema.id),
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tema)
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
}
);
}
I would like to run this method from another one, and check if everything went ok with this method and based on that do further actions. Something like this:
this.saveTema().then(() => {
this.props.history.push({
pathname: '/tema',
state: {
successMessage: `Tema ${this.state.tema.id} ble oppdatert`,
}
}}));
But, this is of course wrong, I am not sure how can I do this, to run some code after the fetch handling of the fetch request has finished. What is the right way to do it?
saveTema() {
return this.gateway.editTema(this.state.tema)
...
Return the promise and then you'll be able to do exactly what you are trying to do.
Return the editThema result after setting up the handlers:
saveTema() {
let prom = this.gateway.editTema(this.state.tema)
prom.then(tema => {
// .. success handling code
})
.catch(httpOrOtherError => {
// .. error handling code
})
return prom;
}
Now you can call your function exactly like you wanted to.
You can achieve that by two approaches
Using async/await
Using native Promise
1. async/await way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = async (req, res) => {
try {
const { userName } = req.query;
const result = await userUtils.searchUser(userName);
return res.status(200).json(result);
} catch (err) {
return res.status(err.code).json({ error: err.error });
}
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = async (userName) => {
try {
if (userName) {
// ...Do some cool stuff
const result = [];
return result;
}
const errorObj = { code: 400, error: 'ERR_VALID_PARAM' };
throw errorObj;
} catch (err) {
console.error(err);
throw err;
}
};
module.exports = userUtils;
2. Promise way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = (req, res) => {
const { userName } = req.query;
userUtils.searchUser(userName)
.then((result) => {
return res.status(200).json(result);
})
.catch((err) => {
return res.status(err.code).json({ error: err.error });
});
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = (userName) => {
return new Promise((resolve, reject) => {
if (userName) {
// ...Do some cool stuff
const result = [];
return resolve(result);
} else {
const error = { code: 400, error: 'Please provide valid data!' }
return reject(error);
}
});
};
module.exports = userUtils;
In both approaches you can hold further execution (in both approach Promise are used directly or indirectly), In a second approach you can achieve by .then().catch() whereas in the first approach just you need to put a keyword await and put async on your function, I suggest you to use async/await. Because when you need to wait for the completion of more than 3 promises and yo go with Native Promise then your code will be so messy like .then().then().then() Whereas in a first approach you just need to put a keyword await on starting of your function, Using async/await approach your code will neat and clean and easily understandable and easy to debug.

How to write an async function that resolves when `data` event emitter fires

I am using node-serialport to communicate with a piece of hardware. It just writes a command and receives a response.
https://serialport.io/docs/en/api-parsers-overview
The following code works:
const port = new SerialPort(path);
const parser = port.pipe(new Readline({ delimiter: '\r', encoding: 'ascii' }));
const requestArray = [];
parser.on('data', (data) => {
// get first item in array
const request = requestArray[0];
// remove first item
requestArray.shift();
// resolve promise
request.promise.resolve(data);
});
export const getFirmwareVersion = async () => {
let resolvePromise;
let rejectPromise;
const promise = new Promise((resolve, reject) => {
resolvePromise = resolve;
rejectPromise = reject;
});
const title = 'getFirmwareVersion';
const cmd = 'V\r';
requestArray.push({
title,
cmd,
promise: {
resolve: resolvePromise,
reject: rejectPromise
}
});
await v2Port.write(cmd);
return promise;
};
Then from my app (which is written in electron/react) I can call the function:
<Button onClick={() => {
let data = await _api.getFirmwareVersion();
console.log('done waiting...');
console.log(data);
}>
Click Me
</Button>
Is there anyway I can refactor this code to make it more succinct?
Is there a way to get the Promise from the async function, rather than having to make a new Promise?
Is there a way to tap into the Transform Stream that already exists and pipe the Promise in there somehow?
I'm also new to async/await, and wanted to avoid using callbacks, especially in the React/Redux side of things.
I aim to have a lot of these endpoints for the api (i.e. getFirmwareVersion, getTemperature, etc...). So I want to make the code as concise as possible. I don't want the UI to have any underlying knowledge of how the API is getting the data. It just needs to request it like any other API and wait for a response.
Oh, I think I get it. The parser is receiving data constantly. So when a request comes, you wait for the next data and send it when it arrives. I suggest you to write an intermediate class.
Like this:
const SerialPort = require('serialport')
const Readline = require('#serialport/parser-readline')
const { EventEmitter } = require('events');
class SerialPortListener extends EventEmitter {
constructor(path) {
super();
this.serialPortPath = path;
}
init() {
this.serialPort = new SerialPort(this.serialPortPath);
const parser = this.serialPort.pipe(new Readline({ delimiter: '\r', encoding: 'ascii' }));
parser.on('data', data => this.emit('data', data));
}
}
Then you could modify the getFirmwareVersion like this:
const serialPortListener = new SerialPortListener(path);
serialPortListener.init();
export const getFirmwareVersion = () => {
return new Promise((resolve, reject) => {
serialPortListener.once('data', async (data) => {
try {
const cmd = 'V\r';
await v2Port.write(cmd);
resolve(data);
} catch (ex) {
reject(ex);
}
});
});
};
Based on help from Mehmet, here is what I ended up with:
const _port = new SerialPort(path);
const _parser = _port.pipe(new Readline({ delimiter: '\r', encoding: 'ascii' }));
const waitForData = async () => {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => reject('Write Timeout'), 500);
_parser.once('data', (data) => {
clearTimeout(timeoutId);
resolve(data);
});
});
};
const createAPIFunction = (cmdTemplate, validationString) => {
return async (config) => {
try {
// replace {key} in template with config[key] props
const cmd = cmdTemplate.replace(/{(\w+)}/g, (_, key) => {
return config[key];
});
_port.write(cmd + '\r');
const data = await waitForData();
// validate data
if (data.startsWith(validationString)) {
// is valid
return data;
} else {
// invalid data
throw new Error('Invalid Data Returned');
}
} catch (err) {
throw err;
}
};
};
export const getFirmwareVersion = createAPIFunction('V', 'V1');
export const enableSampling = createAPIFunction('G1{scope}', 'G11');

Promise returns wrong value

In my code I try to assign a value to json variable to return it after (because I can't return it from the anon. function).
As my function is async, because it sends requests (maybe someone knows how to make it sync? I didn't plan to make it asynchronous), I've added await before the request (https.get).
I've been trying to get value from the Promise, but it's always undefined, even though I've awaited the async function.
Here's a code:
async function get_users() {
const https = require('https');
var token = '...';
var json = undefined;
await https.get('...', (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
json = JSON.parse(data)['response']['items'];
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
return json;
}
get_users().then(function(result) {
console.log(result);
});
Return a Promise and resolve it, when the end event is called, otherwise reject it in case of an error occurred:
async function get_users() {
const https = require('https');
const token = '...';
return new Promise((resolve, reject) => {
https.get('...', resp => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
let json;
try {
json = JSON.parse(data)['response']['items'];
} catch (e) {
reject(e);
};
resolve(json);
});
}).on("error", err => reject(err));
});
}
get_users().then(result => console.log(result));
Please refer my below code.I had issues with getting responses from Promises too.But i finally got it to work.Here's the code:
var output;
var rp = require('request-promise-native');
var myJSONObject = {
"inputs": [{
"name": "<name>",
"value": < value >
}]
};
var orchName = 'TEST05';
postData = JSON.stringify(myJSONObject);
return networkCall(postData, orchName).then((response) => {
console.log('response is' + response)
}).catch((response) => {
console.log(`ERROR: ` + response);
});
function networkCall(postData, orchName) {
return new Promise((resolve, reject) => {
var options = {
method: 'post',
uri: '<URL>',
body: postData,
auth: {
'user': 'usr',
'pass': 'pwd'
},
json: true
};
return rp(options)
.then(body => {
var response = body;
resolve(response);
})
.catch(err => {
console.log('FAILED' + err);
reject(err);
});
});
}
This way your code can run in Synchronous Flow.If the return value is undefined,then,what might have probably happened is that the calling function would have finished executing even before the called function returns its response.But the above approach would work just fine.

Categories