How to use this inside a callback? - javascript

How can I use this inside a callback? Using callback, I got undefined on this.callback() (this is undefined):
constructor(collection, journey, callback, environment=null) {
this.collection = collection;
this.journey = journey;
this.callback = callback;
console.log(this.callback)
this.environment = environment;
}
run() {
newman.run({
collection: this.collection,
environment: this.environment,
insecure: true
}, this.internalCallback).on('console', function (err, args) {
console.log(args.messages.join(' '));
})
}
internalCallback(error, summary) {
if(error || summary.error) throw (error || summary.error)
console.log(this)
this.callback(this.journey, summary) // here throws an error because this is undefined
}

I assume that newman.run creates a new "this" context in your code. Have you tried binding the function: this.internalCallback.bind(this) to this of your class when passing it to newman.run?
Try this code:
run() {
newman.run({
collection: this.collection,
environment: this.environment,
insecure: true
}, this.internalCallback.bind(this)).on('console', function (err, args) {
console.log(args.messages.join(' '));
})
}

Can you try something like this:
that = this;
constructor(){
}
internalCallback(error, summary) {
if(error || summary.error) throw (error || summary.error)
console.log(this)
that.callback(that.journey, summary) // here throws an error because this is undefined
}

Related

How to test error handling functions in Javascript

I m trying to write unit test cases to handle error codes that are defined below. not sure how to achieve it.
module.exports = {
Error: Err,
BadRequest: function () {
return new Err(400, 'Bad Request');
},
NotAcceptable: function () {
return new Err(406, 'Not Acceptable');
},
NotAuthorized: function () {
return new Err(401, 'Not Authorized');
}
};
function Err(code, message) {
assert(code, 'code');
Error.call(this);
Error.captureStackTrace(this, this.constructor);
Object.defineProperty(this, 'code', {
enumerable: true,
value: code
});
this.reason = message;
}
You should have a better base error layer by extending the Error class which already provide most of the functions you would need (capturing the stack trace automatically):
class Err extends Error {
constructor(code, message) {
super(message);
this.code = code;
this.reason = message;
}
}
You can then performa your assertion using the throw expectation:
const errorFn = () => { throw new Err(401, 'Unauthorized') };
expect(errorFn).to.throw(Err).to.haveOwnProperty('code', 401).to.haveOwnProperty('reason', 'Unauthorized');
You should obviously update your sub-error accordingly to extend your generic error.

Receiving the error object in mocha afterEach

In my mocha testframework, I have an it-block that might throw an error. How do I obtain this error in the afterEach-hook that is called afterwards? Is there any workaround if it is not directly passed?
...
afterEach(async function () {
var testObject = {
title: this.currentTest.title,
state: this.currentTest.state,
duration: this.currentTest.duration,
timedOut: this.currentTest.timedOut,
error: this.currentTest.err // I want the error object here
}
});
it('my title', () => {
try {
doSomethingThatMightThrowError();
} catch (err) {
throw new Error(err)
}
});
....

Using a module for my DB, I am not retrieving my data

Sorry if the title is not quite descriptive.
I am using Node and trying to use export.module to have clean code.
app.js
// ...
require('./router')(app);
module.exports = app;
router.js
cloudant = require("./helpers/cloudant")
// ...
module.exports = (app) => {
// ...
app.post("/statsPage", function(req, res) {
// ...
var a = cloudant.listUsers();
console.log("from post ", a) // --> it shows ("undefined")
if(a == false || a == undefined ) {
res.render("error");
} else {
res.render("statsPage", {
results: a
});
}
cloudant.js
exports = module.exports = {}
exports.listUsers = function() {
db.find({selector: {_id:{ "$gt": 0}}}, function(err, body) {
if(err) {
console.log(err);
return false;
} else {
console.log(body.docs) // --> it shows results correctly
return body.docs;
}
});
}
I've made the same way others "export" methods, like "insert", so I'm convinced that this issue is not related neither to my db connection or export "config".
The db.find method is asynchronous, so the data you get from the database is only available in the callback function. If you look carefully at the function you're exporting in cloudant.js, you'll see that there is no return statement returning any data, only in the callback function, that doesn't help anything.
There are many ways to solve this (and many, many posts on SO dealing with it).
Simplest solution for you would be to pass your own callback to your listUsers function:
exports.listUsers = function (callback) {
db.find({ selector: { _id: { "$gt": 0 } } }, function (err, body) {
if (err) {
console.log(err);
callback(err);
} else {
callback(body.docs);
}
});
}
router.js
app.post("/statsPage", function(req, res) {
cloudant.listUsers(function (a) {
console.log("from post ", a);
});
});

Sinon js check stub called with exact arguments

Sinon js check stub called with exact arguments
Requirement: I want to test ejs.renderFile called with right arguments.
My function file:
html_to_pdf_converter.js
var ejsToPdfConvert = function (template, data, callback) {
var row = data.voucher;
html = ejs.renderFile(
path.join(__dirname+'/../../views/', template),
{
data: data
},
function (error, success) {
if (error) {
callback(error, null);
} else {
var pdfPath = getPdfUploadPath(row);
htmlToPdf.convertHTMLString(success, pdfPath, function (error, success) {
if (error) {
if (typeof callback === 'function') {
callback(error, null);
}
} else {
if (typeof callback === 'function') {
callback(null, success, pdfPath);
}
}
});
}
});
};
Mt test is: html_to_pdf_converter.test.js
describe("ejs to html converter", function () {
it('ejs to html generation error', function() {
var data = {
voucher: {},
image_path: 'tmp/1.jpg',
date_format: '',
parameters: ''
};
var cb_1 = sinon.spy();
var cb_2 = sinon.spy();
var ejsStub = sinon.stub(ejs, 'renderFile');
var pathStub = sinon.stub(path, 'join');
ejsStub.callsArgWith(2, 'path not found', null);
htmlToPdfConverter.ejsToPdfConvert('voucher', data, cb_1);
sinon.assert.calledOnce(ejs.renderFile);
sinon.assert.calledOnce(path.join);
sinon.assert.calledOnce(cb_1);
sinon.assert.calledWith(ejsStub, path.join('views/', templateName), data, cb_2); //Error in this line
ejsStub.restore();
pathStub.restore();
});
});
Here are 2 problems with this line:
sinon.assert.calledWith(ejsStub, path.join('views/', templateName), data, cb_2);
First, you want ejsStub to be called with argument 'data' but when you actually call renderFile you wrap it like this: {data: data}.
The second is that cb_2 is not equal function (error, success) { if (error) ... } that you are actually passing to renderFile.
To make it working run it like this:
sinon.assert.calledWith(ejsStub, path.join('views/', templateName), {data: data});
There is no need to pass cb_2 or anything else because the actual callback is defined in the function and cannot be changed.

This scope lost with inheritance

Here is the example code, two files and "classes".
CRUD class with defined methods, the problem occurs with this.modelName, as I set the routes the this context changes with this code:
The question is how, to get the same scope under the CRUD where you have defined the modelName ?
server.get('/users/:id', UserRoutes.find);
Code:
var db = require('../models');
function CRUD(modelName) {
this.modelName = modelName;
this.db = db;
}
CRUD.prototype = {
ping: function (req, res, next) {
res.json(200, { works: 1 });
},
list: function (req, res, next) {
// FAILS BECAUSE the modelName is undefined
console.log(this);
db[this.modelName].findAll()
.success(function (object) {
res.json(200, object);
})
.fail(function (error) {
res.json(500, { msg: error });
});
}
};
module.exports = CRUD;
UserRoutes class:
var CRUD = require('../utils/CRUD'),
util = require('util');
var UserModel = function() {
UserModel.super_.apply(this, arguments);
};
util.inherits(UserModel, CRUD);
var userRoutes = new UserModel('User');
module.exports = userRoutes;
I assume that you are using userRoutes.list as a handler somewhere else, i.e. the context changes. In that case this should be a simple solution:
function CRUD(modelName) {
this.modelName = modelName;
this.db = db;
this.list = CRUD.prototype.list.bind(this);
}
Note that you won't be able to access "the other this" with that solution (this will be permamently bound to CRUD instance, no matter how .list is called).
The other option is to turn list into a function generator (which is pretty much the same what .bind does, except you can still use this from the other context):
CRUD.prototype = {
// some code
list: function() {
var that = this;
return function (req, res, next) {
console.log(that);
db[that.modelName].findAll()
.success(function (object) {
res.json(200, object);
})
.fail(function (error) {
res.json(500, { msg: error });
});
}
}
};
and then use userRoutes.list() as a handler.
This sort of thing is generally fixed by stowing the right this into _this. In your list function this is the function object, which doesn't have a modelName object.
var _this;
function CRUD(modelName) {
this.modelName = modelName;
this.db = db;
_this = this // <---------------
}
....
// call the _this in the outer scope
db[_this.modelName]

Categories