I am new to the continuation passing style of asynchronous computation used in Node.js applications, and I'm struggling to get a grip on some fairly basic code.
I am trying to write a library which will create an imap interface to imap.gmail.com, and I am trying to follow BDD with 'vows'.js' (with varying levels of success. I'm definitely not following the full red->code->green cycle that I should be, but it's hard to get started in a language that way).
The relevant test case looks like this:
var gmail = require('../lib/gmail.js'),
vows = require('vows'),
assert = require('assert'),
fs = require('fs');
vows.describe('Basic interface tests').addBatch({
'A GMailInterface object can': {
topic: function() {
var gm = Object.create(gmail.GMailInterface);
var settings_file = 'test/test_settings.json';
var settings = JSON.parse(fs.readFileSync(settings_file));
var that = this;
gm.connect(settings.email,settings.password,function() {
that.callback(gm); // ERROR BEING GENERATED HERE
});
},
// ACTUAL VOWS OMITTED - the following is just a test of the callback
'find an email' : {
topic: function(gm) {
console.log(">>>",gm);
},
}
}
}).export(module)
If I write a console.log message right above the line with "ERROR BEING GENERATED HERE", it will print. It will not if I put a message below it. The output of the test gives the following error:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Uncaught, unspecified 'error' event.
at EventEmitter.<anonymous> (events.js:50:15)
at EventEmitter.emit (/Users/eblume/Repositories/my_stuff/gmail/node_modules/vows/lib/vows.js:236:24)
at /Users/eblume/Repositories/my_stuff/gmail/node_modules/vows/lib/vows/context.js:31:52
at Object.callback (/Users/eblume/Repositories/my_stuff/gmail/node_modules/vows/lib/vows/context.js:46:29)
at Array.0 (/Users/eblume/Repositories/my_stuff/gmail/test/gmail_test.js:17:14)
at EventEmitter._tickCallback (node.js:192:40)
The code in gmail.js is a bit too much to post here, but here is what I think is the relevant section - I can post more if you ask a question below.
gm.connect = function(username,password,cb) {
var self = this;
self.conn = new ImapConnection({
username: username,
password: password,
host: 'imap.gmail.com',
port: 993,
secure: true
});
async.series([
function(callback){self.conn.connect(callback); },
function(callback){self.conn.openBox('[Gmail]/All Mail',true,callback);}
],
function(err,results) {
if (err) {
die(err);
}
process.nextTick(cb);
});
};
Where might I be going wrong? Thanks for any help!
I recommend reading up on how 'this' works. If nobody's been messing with it the 'that' of that.callback refers to the parent object which is labeled with a literal string as 'A GMailInterface object can' .
I suspect it's the this factor tripping you up. 'callback' should be defined as a method of the same object as the 'topic' method the way you have things set up and that doesn't strike me as the way it's meant to work.
'this' typically refers to nearest ancestor/parent object by default. It ignores wrapping functions unless they're used as constructors using the 'new' keyword in which case it indicates the object instance. In the case of event callbacks in the DOM (browser JS - not node.js which I don't know in-depth as far as events), it typically refers to the object that's had an event triggered on it.
There's no real kludge being fixed with that and self. We just tend to use those to be certain we're addressing the top object in cases where an object has aggregate objects.
Related
I am using a custom javascript modulue which has it's own Error objects. I would like to intercept those custom Error objects and take the appropriate path in my try{} catch{} block, distinguishing them from Javascript's built in Error objects such as ReferenceError, TypeError etc.
So a bit like this.
try {
// Some code that might produce a traditional javascript error
// or one of the errors raised by the module I am using.
}catch (error){
if(error instanceof ExchangeError){
// Handle this in a way.
}else{
// Probably one of the built in Javascript errors,
// So do this other thing.
}
}
So, in the example above, ExchangeError is a custom error belonging to that specific module, however, I am not able to run the instanceof on my error, despite the fact that when I do error.constructor.name I get ExchangeError.
My javascript scope simply does not know about that ExchangeError. So the question is, how can I intercept those kind of Error objects? I'm sure I can do it with string matching, but just wanted to check if there is a more elegant way.
One thing I tried, I have my own errors module, that has some custom errors in there, I tried to mimic the module's Error object:
class ExchangeError extends Error {
constructor (message) {
super (message);
this.constructor = ExchangeError;
this.__proto__ = ExchangeError.prototype;
this.message = message;
}
}
and import that through my errors module, but that did not work obviously.
By actually implementing my own ExchangeError I actually was doing something really really bad, I was blinding the instanceof check with my own ExchangeError, whereas the ExchangeError instance that was coming from the module, was NOT an instance of my own ExchangeError. That is why my if check was falling silent.
The solution is simply doing this:
const { ExchangeError } = require ('ccxt/js/base/errors');
Importing the error from within the module. Now the instanceof look up is working. I did not know that one can import bits and pieces from a module like that.
Thanks to #FrankerZ for pointing that out.
I have a Node.js script that used to work, but after I switched to another VM it does not work anymore. Can anyone see what the problem is? Here is the function, db is a database:
this.start = function() {
logger.debug('Starting up.');
db.serialize(() => {
db.run("DELETE FROM jobs WHERE status = 'failed'")
.run("UPDATE jobs SET status = 'queued'", (err) => {
if (err) {
logger.error(err.message);
} else {
logger.info('done');
}
});
});
}
Now I get the following error:
TypeError: Cannot read property 'run' of undefined
at Database.db.serialize ()
at TransactionDatabase.serialize
at module.exports.start
at Object.<anonymous>
...
The error is pointing at the second ".run".
My Node.js version is 10.4.1, sqlite3 version 3.8.2.
What am I missing? Some module?
I think I found the answer. Chaining run() runs the queries nearly at the same time. According to this answer, the function run() starts the query, but returns immediately.
However, if you serialize and chain, those two methods cannot be used at the same time. You are trying run queries sequentialy, but also at the same time.
Although, depending on your needs, you can nest serialize, parallelize or callbacks, as shown in the "control flow" doc.
I guess the method serialize() "locks" chaining by changing the return value of run() to undefined.
JavaScript, when throw-ing a built-in error as such:
throw new Error("Something was wrong");
displays the text nicely - you can't tell you threw an object
However, when creating a custom error by subclassing the Error object (or other error object for that matter), the thrown error is not displayed the same way in the console.
So, by using this code:
var ImproperlyConfigured = function(message){
this.name ="ImproperlyConfigured";
this.message = message || "The object you tried to construct was improperly configured due to an unknown error";
}
ImproperlyConfigured.prototype = new Error();
The following is the output
I don't like the fact that the object properties (name and message) are shown. In fact, I don't like that I don't understand why the properties are shown.
I've googled a bit and I got the feeling that by implementing a toString method will help but, by using this method, the fact that the name of the error is no longer in red puzzles me even more.
Code
var ImproperlyConfigured = function(message){
this.name ="ImproperlyConfigured";
this.message = message || "The object you tried to construct was improperly configured due to an unknown error";
this.toString = function(){
return this.message;
}
}
ImproperlyConfigured.prototype = new Error();
Output:
What I would like to achieve is a standard looking error, by the use of custom error and, of course, by not using the console.error method manually in a try...catch block.
Is this possible?
As Pointy correctly pointed out (pun intended), the issue here is not with JavaScript, but rather with the environment JavaScript is running (in this case, Google Chrome).
In another environment (like Chromium, Firefox, NodeJS, etc.) the behavior will likely be different, using the same code, depending on how those JavaScript hosts are handling these cases.
I'd like to monitor access to certain methods of the browser's built-in objects from a Firefox add-on. The sample code included below largely works, except when one of the method arguments is a function. Then, I receive the following XrayWrapper error:
XrayWrapper denied access to property 0 (reason: value is callable).
See https://developer.mozilla.org/en-US/docs/Xray_vision for more
information. Note that only the first denied property access from a
given global object will be reported.
I don't understand why an XrayWrapper error would throw, as the prototype instrumented is an object in the unsafeWindow (page-script) scope. The content-script used for instrumentation is:
function logCalls(object, objectName, methodName) {
var originalMethod = object[methodName];
object[methodName] = function () {
console.log(objectName + "." + methodName, "was called");
originalMethod.apply(this, arguments);
};
}
// 1. Works (expected)
logCalls(unsafeWindow.RTCPeerConnection.prototype,
"unsafeWindow.RTCPeerConnection", "createDataChannel");
// 2. Throws XrayWrapper Error (unexpected)
logCalls(unsafeWindow.RTCPeerConnection.prototype,
"unsafeWindow.RTCPeerConnection", "createOffer");
A sample page-script:
var PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection;
var connection = new PeerConnection({iceServers: []}, {optional: [{RtpDataChannels: !0}]});
// 1. Method call recorded
connection.createDataChannel("", {reliable: !1});
// 2. Method call recorded but causes XrayWrapper error
connection.createOffer(function(a) {
connection.setLocalDescription(a)
}, function(err) {})
For (1), the instrumentation works as expected. The call to createDataChannel logs to the console and succeeds in creating a dataChannel.
For (2), the instrumentation successfully logs to console. However, line 5 of the content-script (originalMethod.apply(this, arguments);) causes the XrayWrapper error above due to the argument list containing a function.
First of all, you should give any method assigned into a different security context the exportFunction treatment.
That may not be sufficient though, since the method body itself is still executed in the privileged context which means any arguments it receives will also be xray wrappers. Passing them back to the less privileged scope, especially through arcane magic like apply and the arguments object may not do what you would expect it to. Additional xray unwrapping or using rest parameters and .call instead may be necessary.
In cases like these it may be easier to eval() the call-interception logic into the target scope and only export the the logging function called by the interception-wrapper. That way there will not be any unprivileged -> privileged -> unprivileged transition for the arguments, the this or return values and only the log call will cross a security boundary.
Since eval is discouraged by addon review guidelines you also may want to consult the reviewers on that approach.
I want to test the case where an error is thrown in my search.coffee class:
Let's assume that for my test, I cn fo
search.coffee
{ EventEmitter } = require 'events'
connectors = require './connectors'
class Search extends EventEmitter
constructor: (#query, #credentials, dispatch = true) ->
#connectors = #_connectors()
if #connectors.length == 0
# want to test this
#emit 'error', new Error 'Search could not be dispatched to any connectors'
#_dispatch(query, connector) for connector in #connectors if dispatch
I have tried the following, however since an event is being emitted with the error, the return of the new Search() itself does not throw an error. How can I catch this thrown error?
search_spec.coffee
Search = require '../../src/search'
describe "search.coffee", ->
describe "constructor", ->
it 'should return an error if no credentials match loaded connectors', ->
new Search("foo", { }, false).should.throw()
Yeah, nothing is thrown here. The default behavior if no event listener is installed is for Node to print a stack trace and exit. It looks like a thrown exception but you can't catch it. Systems like should.throw, assert.Throw, etc. rely on being able to catch the exception. Not possible here.
Now, there's a problem. You are emitting the 'error' event in your constructor. Who has the opportunity to call the on method to install a listener so that when this emit happens, someone will receive it? The object is not constructed yet. The object could call on on itself but no one outside can.
Options:
Don't emit an error, but instead throw an error. For the reason given above, this is the option I'd prefer if this were my code.
Add an additional parameter to the constructor. This would be a listener that Search installs on itself right away to listen for error events. So in testing, a listener could be passed to the constructor and then you just test that it is called.
If it does not make sense to allow callers to install a listener like mentioned in the 1st option, Search could install its own listener, which could record that that the object is dead (for instance, this.dead = true). Then test for this flag.
Or if the current design and behavior is the desired one (the constructor emits 'error', which causes Node.js to exit), modify the previous option. Devise a way to indicate to Search that it is running in a test environment. When it detects that it is in testing, it adds a listener like in the previous option.