I'm trying to write a plugin for HomeBridge and have run in to a problem. I started out by just writing some code, start up Homebridge and test it. That worked out find in the beginning but after a while as I added more functionality every time I start HomeBridge there's a lot of testing just to ensure that I haven't broken anything. I mainly work with Java and have just started out with JavaScript. I have copied the patten on how the plugin should be designed. Theres very little documentation about how to write a plugin so I'm kind of in the dark here when it comes to best practice and so on. I have simplified the code so it won't take that much space but the structure is intact. So my question is: How do I test this code?
index.js
let Service, Characteristic;
let request = require('request');
module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerAccessory("homebridge-myplugin", "MyPlugin", MyPluginDevice);
};
function MyPluginDevice(log, config) {
this.log = log;
this.url = config['url'];
this.id = config['id'];
}
MyPluginDevice.prototype = {
getSwitchOnCharacteristic: function(callback) {
this.httpRequest(this.url, null, 'GET', function(error, response, body) {
if (error) {
this.log("Fail: %s", error.message);
callback(error);
} else {
const newState = body['state'];
return callback(null, newState);
}
}.bind(this)
);
},
setSwitchOnCharacteristic: function(state, callback) {
this.httpRequest(this.url, requestBody, 'POST', function(error, response, body) {
if (error) {
this.log("Fail: %s", error.message);
callback(error);
} else {
callback();
}
}.bind(this));
},
httpRequest: function(url, body, theMethod, callback) {
request(
{
url: url,
body: body,
method: theMethod,
auth: {
user: 'nexa',
pass: 'nexa',
sendImmediately: false
},
json: true
},
function(error, response, body) {
callback(error, response, body)
})
},
getServices: function() {
let informationService = new Service.AccessoryInformation();
informationService.setCharacteristic(Characteristic.Manufacturer, "My Nexa plugin").setCharacteristic(Characteristic.Model, "My Nexa Plugin Model").setCharacteristic(Characteristic.SerialNumber, "A very special number");
this.switchService = new Service.Switch(this.name);
this.switchService
.getCharacteristic(Characteristic.On)
.on("get", this.getSwitchOnCharacteristic.bind(this))
.on("set", this.setSwitchOnCharacteristic.bind(this));
return [this.switchService];
}
};
After having been at it for a while I can't write a test for this code. I'm having problem with variables being null and however I try to get around it I always end up with some part of the code not initiated. I have tried:
let MyPluginDevice = require('./index');
let myDevice = new MyPluginDevice(homebridgeMock);
but that leaves me with a problem with getSwitchOnCharacteristic and setSwitchOnCharacteristic. My other approach is to access the MyPluginDevice through my homebridgeMock. But that leaves me with getSwitchOnCharacteristic and setSwitchOnCharacteristicas null or not a function.
I'm kind of out of ideas and my skills is not that good so I can spot the problem or of I have implemented the code in a way that it can't be tested. I got no idea how other developers have done when writing a plugin but I would feel much safer if I could have a few tests running.
Help me Stackoverflow, you are my only hope!
Related
I am working on a project that I have to authenticate e get a token from API body content.
How can I Get only the access_token and save it on a variable to use in another API?
I tried too many things to access it. I think it´s something very easy to do, but I don´t know how!
I am new in node/typescript.
Thank you!
Result:
{
"access_token": "eyJhbGciOiJSUzUxMiIAU6l9z2zMQiI9g",
"refresh_token": "AFGpstweytwtewetwetwetwetwetwet2Fg",
"scope": "default",
"token_type": "Bearer",
"expires_in": 3600
}
export const chamada = (event, context, callback) => {
var request = require('request');
console.log('fora');
request.post('http://MY_API',
{ json: { key: 'value' } },
function (error, response, body) {
if (!error && response.statusCode == 201) {
console.log('dentro');
console.log(body);
}
else {
console.log(response.statusCode);
}
}
);
const p = new Promise((resolve) => {
resolve('success');
});
p.then(() =>
callback(null, {
message: 'Go Serverless Webpack (Ecma Script) v1.0! First module!',
event,
})
).catch((e) => callback(e));
};
You have to read the property from the response. Inside of the first if in the callback, you can just access body.access_token and do whatever you like with it.
Not sure I understand what you mean by "use it in another api". Maybe you can add more description on how you want to use it?
By the way, I see that the library that you are using (request) is marked as deprecated, so if possible, you might want to replace it.
I'm new to node.js and I'm having a hard time trying to understand the concept of event-based async programming.
I'm implementing a restful API web service, so consider the following simple (synchronous!) API method addStuff(), which inserts stuff to an elasticsearch db:
var client = new elasticsearch.Client({ host: 'localhost:9200' });
function indexStuff(stuff) {
return client.index({
index: 'test_idx',
type: 'test',
id: stuff.id,
body: stuff
});
}
function addStuff(req, res, next) {
let stuff = processRequest(req);
indexStuff(stuff).then(
function (body) {
return true;
},
function (error) {
res.status(error.status).send({ message: error.message });
}
);
}
So far, so good.
Now during testing I wanted to avoid inserting already existing stuff to the db.
So I'd like to add something like:
function stuffAlreadyInDB(id) {
... // returns true/false
}
function addStuff(req, res, next) {
if (stuffAlreadyInDB(req.id))
{
res.status(409).send({ message: 'stuff with id ' + req.id + ' already in DB' });
return;
}
var stuff = processRequest(req);
...
}
Unfortunately, the call to the elasticsearch db is asyncronous, which means, I can't just return a boolean in a sync function. Instead, I have to refactor the whole shabang to something (argueably less easy to read) like this:
function getStuffByID(id) {
return client.get({
id: id,
index: 'test_idx',
type: 'test',
ignore: 404
});
}
function addStuff(req, res, next) {
getStuffByID(req.id).then(
function(resp) {
if (resp.found) {
res.status(409).send({ message: 'stuff with id ' + req.id + ' already in DB' });
return;
}
else {
var stuff = processRequest(req);
indexStuff(stuff).then(
function (body) {
res.writeHead(200, {'Content-Type': 'application/json' });
res.end();
},
function (error) {
res.status(error.status).send({ message: error.message });
}
);
}
},
function(error) {
res.status(error.status).send({ message: error.message });
}
);
}
At least, I haven't found any better solution. I tried to find out how to make the async call to the db a sync call, but basically everybody was saying: just don't do it.
So how am I supposed to do it right if I don't want to refactor everything and back-factor it when I finished testing and don't need this extra db check anymore?
Oh... and if you downvote my question: leave a comment why you do so.
Because I have the feeling that many people struggle with this issue, but I haven't found a satisfying answer yet.
you could use async\await syntax to make your code readable.
for example you could do this:
async function getStuffById(){
//return true or false; }
and in the "add stuff" function you could write:
if ( await getStuffById() ){
//do some more stuff }
please notice that you have to make "add stuff" async as well in order to use await syntax.
more on async \ await can be found here
I have a question I have this function
authAnonymous: function (callback) {
rest
.post(wso2config.token.host + "/" + wso2config.token.path, {
username: wso2config.clientId,
password: wso2config.clientSecret,
data: {
username: wso2config.anonymousUser.username,
password: wso2config.anonymousUser.password,
grant_type: "password",
redirect_uri: wso2config.redirect_uri,
scope: "somescope:thisisit"
}
})
.on("complete", function (data) {
if (data.error) callback(data, null);
else {
data.anonym = true;
callback(null, {
openid_data: data
});
}
});
},
so i want to code coverage this function and i make some unit test using Jest here's the unit test code
test("do login using authAnonymous", done => {
openID.authAnonymous(function (error, data) {
if (!error) {
expect(data.anonym).toBeTruthy();
} else {
//expect(data.anonym).toBe(false);
}
});
done();
});
somehow this unit test is working as I expected but code coverage says the statement is not covered
this says statement is not covered
as i understand my code works fine and unit test is working as expected why jest code coverage says this statement not covered can anyone explain how this statement covered works ? and why mine is not covered, i believe this is my fault but i don't know what to investigate here
You are calling done() before everything is done. You need it to be called after all changes and checks are finished:
test("do login using authAnonymous", done => {
openID.authAnonymous(function (error, data) {
if (!error) {
expect(data.anonym).toBeTruthy();
} else {
//expect(data.anonym).toBe(false);
}
done(); // <-- at the end of callback
});
});
So I'm having trouble getting one javascript function to finish before the next one starting. I've spent quite a lot of time trying to use callback methods described on other stackoverflow posts. I could get simple examples that used timeouts to work but couldn't get it to work with my API request. I stumbled upon async.js and thought that perhaps using async.series would be a good idea to get my two functions to perform one after another. So I tried this approach, however I still seem to be having the problem where the first function takes a bit longer to execute (which is fine) but the execution process moves past this function instead of waiting for it to end. I feel I have a misconception of some sort since I have tried several methods but to no avail.
What is strange is, is that that when running server.js, it goes into the first function but then it leaves the async.series() function even before the request is finished. When I print inside of tokenReq(), I can see that the request was successful as a token code is returned successfully however this happens to late as execution has moved on. The output is shown below.
server.js:
var access_code;
async.series([
function() {
access_code = queries.data.tokenReq(code);
console.log("Finished inside function 1");
},
function() {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
}
],
function(err, access_code) {
});
console.log("Outside");
queries.js:
tokenReq: function(code) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return access_token;
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return "error";
}
});
},
Output:
Finished inside function 1
Outside
INSIDE FUNCTION REQUEST, Token: 8Swhd.......
You missed a major point here. Since node.js is asynchronous there should not be a way to know when a function completes its execution. That is why we specify callbacks so that the invoking function knows whom to call when it finishes its execution. Once you have functions with callbacks, you can enforce series/parallel/waterfall behavior with async module.
tokenReq: function(code, cb) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return cb(null, access_token);
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return cb(new Error("whatever"));
}
});
},
Now, you can use the callback inside server.js
var access_code;
async.series([
function(cb) {
return queries.data.tokenReq(code, cb);
},
function(access_code, cb) {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
// do whatever you want after this
return cb();
}
],
function(err, access_code) {
if (err) {
console.log(err);
}
// wrap your logic around a function and call the correspoding callback here
});
I'm trying to put together a quick node.js/edge.js/C# bridge for a demo.
I have to use the ".Net calling Node.js" style, as the existing C# code uses a number of config values, which I can't add to node.exe.config as I will need to run several versions concurrently.
So I have this code:
private static async Task Start() {
Func<object, Task<object>> edge = EdgeJs.Edge.Func(#"
var login = require('login.js');
var edge = require('edge')
login({ email: 'user#example.com', password: 'shh' }, function callback(err, api) {
if (err) return console.error(err);
// This will keep listening until terminated
api.listen(function callback(err, message) {
if (err) return console.error(err);
// At this point I need to send the message back to this class so it can be processed..
console.log(message); // send the message to C#
// ... and then return the response via the api
api.send('response goes here');
});
});
return function (data, callback) {
callback(null, er...);
}
");
}
So, the code is waiting for messages in an event loop and responding. This all works with hardcoded values. But I need to submit the message back to the C# for processing, and I cannot work out how to communicate back and forth between edge.js and the C# app.
It must surely be via the callback, but I can't seem to start figure out how to structure it, and time is getting short. And I'm by no means a JavaScript expert.
How can I communicate between the edge code and the C# code from within the event loop using the callback?
You're right, it's via the callback. Since you're using async code, you have to wrap all your code inside the returned (edge) function, like this:
private static async Task Start() {
Func<object, Task<object>> edge = EdgeJs.Edge.Func(#"
// edge_callback is used to return values to the C# code
return function(data, edge_callback) {
var login = require('login.js');
var edge = require('edge')
login({
email: 'user#example.com',
password: 'shh'
}, function callback(err, api) {
if (err) return console.error(err);
// possible enhancement here by letting C# know there is an error
// edge_callback(err);
// This will keep listening until terminated
api.listen(function callback(err, message) {
if (err) return console.error(err);
// same thing here: edge_callback(err);
// At this point I need to send the message back to this class so it can be processed..
console.log(message); // send the message to C#
// use the callback, first param is error if there is any, second is the data
edge_callback(null, message);
// ... and then return the response via the api
api.send('response goes here');
});
});
}
");
}
I have ended up with something like this: there's a function defined on the data passed to edge, which edge then calls when a new message is received. That function then waits for the response, and passes it back to edge, which receives the result in (of course) another callback.
private static async Task Start() {
dynamic payload = new ExpandoObject();
payload.msgHook = NewMessage;
payload.login = new {
email,
password
};
var receive = Edge.Func(#"
return function(payload, edge_callback) {
var login = require('index.js');
login({
email: payload.login.email,
password: payload.login.password
}, function callback(err, api) {
if (err) {
edge_callback(err);
}
api.listen(function callback(err, message) {
if (err) { edge_callback(err); }
payload.msgHook(message,
function callback(err, result) {
if (err) {
edge_callback(err);
}
var msg = {
body: result.body,
url: result.url
}
api.sendMessage(msg, result.threadId);
});
});
});
}
");
var _ = await receive(payload) as IDictionary<string, object>;
}
private static Func<object, Task<object>> NewMessage {
get {
Func<object, Task<object>> hook = async m => {
string body, threadId;
if (!ProcessMessage(m as IDictionary<string, object>, out body, out threadId)) {
log.Error("Failed to process message: " + m.ToString());
}
api.SendMessage(body, threadId, phone);
var reply = await waitForReply(threadId);
var result = new {
body = reply
};
// Return the _result_ of the task.
return Task.FromResult<object>(result).Result;
};
return hook;
}
}