Trying to export function without success with JavaScript, Node.js - javascript

I'm trying to learn Node.js and I have a pretty good app now, but I wanted to implement a library (venom-bot) to send some text messages. My problem is that I'm having trouble trying to use functions outside the primary file. Here is my code:
// Dependencies
const server = require("./lib/server")
const venom = require("venom-bot")
// Declare the app
const app = {}
// Init function
app.init = () => {
// Start the server
server.init()
}
// Init venom-bot
venom.create().then((client) => start(client))
// A function to test if I can send the message
async function testing(msg) {
await msg.sendText(...some code here...)
}
function start(client) {
app.msg = client
// Here, if I pass app.msg as an argument, works
// My problem is that I can't use app.msg outside of here,
// even with the module.exports down there
// (I'm trying to use it on a helpers.js file).
testing(app.msg)
// Execute the application
app.init()
}
// Export the app
module.exports = app
On the helpers.js file I'm requiring it this way:
// Dependencies
const app = require("./index.js")
// A function to test
async function sendMsg(msg) {
await msg.sendText(...some code here...)
}
helpers.send = () => {
sendMsg(app.msg)
}
module.exports = helpers
Whenever helpers.send gets invoked, it should correctly use the async function right above the helpers.send passing the app.msg as argument, at least I think it should. What I'm missing here?
The error I got is:
(node:18148) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'sendText' of undefined
On testing purpose, I'm trying to simple call that helper when I receive a get request to a specified route.

Related

Testing a file that calls its own function

I have a file that is the entry-point to my application;
const main = async () => {
// Do stuff
}
main().then();
I understand that I can export the main function, delete the call in the first file and in a separate file import and call the function there;
const entry = require('./rewritten-entry.js');
entry.main().then();
But for arguments sake lets say I really want to test this first file's main function. As soon as I import the file in my test, the main function will execute before I even reach the test code;
const entry = require('./original-entry.js') // <-- main function executes here
const test = {
// Do test stuff
}
For a test, I don't want this to happen. Likewise I don't want to mock the function since it is exactly what I want to test.
I see this common 'init' logic all over the place so I imagine there is a way to test this.

Need to hook to function of the Web API, is it possible?

I'm trying to hook into a function that comes with the File System Access API.
For example when the web site used the File System Access API as shown in below.
// store a reference to our file handle
let fileHandle;
async function getFile() {
// open file picker
[fileHandle] = await window.showOpenFilePicker();
if (fileHandle.type === 'file') {
// run file code
} else if (fileHandle.type === 'directory')
// run directory code
}
}
It is possible to hook Web APIs with JavaScript? I mean how can I stop process of the web site when it used this API?
If by "hook" you mean intercept when the function was called, then you could override the built-in function like so:
const originalShowOpenFilePicker = window.showOpenFilePicker;
window.showOpenFilePicker = (...args) => {
console.log('Modified `showOpenFilePicker` called with these arguments:', args);
// Block the page by calling `while (true) {}`,
// but nor sure if this is what you mean. The
// `return` statement below would be never reached
// in this case.
return originalShowOpenFilePicker(...args);
};

In Node.js, how do you store callback data from a module's function in the main app?

I made a node web app and it works. However, I built it monolithic, and I'm attempting to break it out into modules for practice.
Long question short, in a module, how do I define a variable from callback results and store it in a way that it is available to the main app? is it as simple as storing the results in the module to a global variable and exporting that as well?
In my monolthic version:
app.js
var resultsArray = [];
function getDatafromHTTP(page){
callback(data){
resultsArray.push(data);//push json elements to array
if(page < 50){page++;getDatafromHTTP(page);}
}
}
getDatafromHTTP(0);
app.get(some function that displays the resultsArray)
The getDatafromHTTP function runs 50 times via the page variable.
Now that I tried to break it down:
app.js
var resultsArray =[];
var getDatafromHTTP = require(module.js).getDatafromHTTP
getDatafromHTTP(0);
app.get(some function that displays the resultsArray)
module.js
exports.getDatafromHTTP = function(page){
callback(data){
resultsArray.push(data);//push json elements to array
if(page < 50){page++;getDatafromHTTP(page);}
}
}
//error resultsArray not Defined.
I get why resultsArray is not defined in the module, and understand I can make resultsArray a variable in the module itself. If it was a return, i would simply define a variable in the main app based on the return of the function. But since the function gets data via a callback and not a return, whats the "right" way to get that data back into the main app and available for the app.get function? either as it builds, or after the 50 function runs completes?
There are a number of different ways to do this, but the most common pattern I've seen is to just add the value as a new property of app (or similar). So for example:
module.exports = function(app) {
return function getHttpData(callback) {
request.get('http://example.com/', (err, results, body) => {
if (err) return cb(err);
app.httpData = body;
return cb(null, body);
});
}
}
Then in your calling code you'd do something like:
var app = express();
var getHttpData = require('./get-http-data.js')(app); // <-- note I'm passing the app object in here
// sometime later
getHttpData((err, data) => {
console.log(data);
});
Obviously, I'm leaving off a few steps like some of the require statements and such, but hopefully you get the idea.
All that said, often you want to avoid global variables like that. In particular, storing things that potentially change like that in memory will lead to bugs later if your app has to scale to more than one process (each instance of the app would fetch separately, and possibly wind up with different states).
module.js
function getDataFromHTTP() {
return callback(data) {
return data;
}
}
module.exports = getDataFromHTTP;
app.js
var getDataFromHTTP = require('module.js');
var resultsArray = [];
for (var page=0; page<50; page++) {
resultsArray.push(getDataFromHTTP);
}

exporting a function in node.js

I am using nodejs and webdriver for automation tests. I am trying to export a function from one js file to another. e.g there is a function called abc under file abc.js and i want to use that function with other file called xyz.js. I tried using export and require but it exports and runs entire test (abc.js) instead of just a function.
//abc.js
console.log('print this');
client= function client() {
driver.get(baseUrl + '/#/login');
};
exports.client = client;
//xyz.js
var client1 = require('abc.js').client();
Requiring a module for the first time causes the module to be cached and it's code to be executed, that's why you're seeing your "print this" log. Next time you call your client function you shouldn't see it.
This is not relevant to the question, but still, in your xyz.js file, since your function isn't returning anything you can use:
require('abc.js').client();
Instead of:
var client1 = require('abc.js').client();
In your abc.js, there's no need for a named function, you can just use:
var client = function() {
...
};
give it a try
function abc(){
console.log('print this');
this.client= function client() {
driver.get(baseUrl + '/#/login');
};
return this;
}
module.exports = abc;
//xyz.js
var abc = require('abc.js')();
abc.client();
its a good practice when capsulating objects in nodejs

Stub save Instance Method of Mongoose Model With Sinon

I am trying to test a service function I use to save a widget using a Mongoose model. I want to stub out the save instance method on my model, but I cannot figure out a good solution. I have seen other suggestions, but none seem to be complete.
See... this, and this.
Here is my model...
// widget.js
var mongoose = require('mongoose');
var widgetSchema = mongoose.Schema({
title: {type: String, default: ''}
});
var Widget = mongoose.model('Widget', widgetSchema);
module.exports = Widget;
Here is my service...
// widgetservice.js
var Widget = require('./widget.js');
var createWidget = function(data, callback) {
var widget = new Widget(data);
widget.save(function(err, doc) {
callback(err, doc);
});
};
My service is very simple. It accepts some JSON data, creates a new widget, and then saves the widget using the "save" instance method. It then calls back passing an err and doc based on the outcome of the save call.
I only want to test that when I call createWidget({title: 'Widget A'})...
The Widget constructor is called once with the data I passed to the service function
The save instance method on the newly created widget object is called once
EXTRA CREDIT: That the save instance method calls back with null for the err and with {title: 'Widget A'} for the doc.
In order to test this in isolation, I would probably need to...
Mock or stub the Widget constructor so that it would return a mock widget object that I create as part of my test.
Stub the mock widget object's save function so I can control what occurs.
I am having trouble figuring out how to do this with Sinon. I have tried several variations found on the pages of SO with no luck.
NOTES:
I don't want to pass in an already constructed model object to the service because I want the service to be the only thing that "knows" about mongoose.
I know this is not the biggest deal (to just test this with more of an integration or end-to-end test, but it would be nice to figure out a solution.
Thanks for any help you can provide.
If were to test that, this is how I would approach it, first have a way to inject my mocked widget to the widget-service. I know there's node-hijack, mockery or something like node-di, they all have different styles, I'm sure there's more. Choose one and use it.
Once I get that right, then I create my widget-service with my mock widget module. Then I do something like this(this is using mocha btw):
// Either do this:
saveStub = sinon.stub();
function WidgetMock(data) {
// some mocking stuff
// ...
// Now add my mocked stub.
this.save = saveStub;
}
// or do this:
WidgetMock = require('./mocked-widget');
var saveStub = sinon.stub(WidgetMock.prototype, 'save');
diInject('widget', WidgetMock); // This function doesn't really exists, but it should
// inject your mocked module instead of real one.
beforeEach(function () {
saveStub.reset(); // we do this, so everytime, when we can set the stub only for
// that test, and wouldn't clash with other tests. Don't do it, if you want to set
// the stub only one time for all.
});
after(function () {
saveStub.restore();// Generally you don't need this, but I've seen at times, mocked
// objects clashing with other mocked objects. Make sure you do it when your mock
// object maybe mocked somewhere other than this test case.
});
it('createWidget()', function (done) {
saveStub.yields(null, { someProperty : true }); // Tell your stub to do what you want it to do.
createWidget({}, function (err, result) {
assert(!err);
assert(result.someProperty);
sinon.assert.called(saveStub); // Maybe do something more complicated. You can
// also use sinon.mock instead of stubs if you wanna assert it.
done();
});
});
it('createWidget(badInput)', function (done) {
saveStub.yields(new Error('shhoo'));
createWidget({}, function (err, result) {
assert(err);
done();
});
});
This is just a sample, my tests sometimes get more complicated. It happens that most of the time, the backend calling function(here it is, widget.save) that I want to mock, is the one that I want it's behavior to change with every different test, so that's why I reset the stub everytime.
Here's also another example for doing similar thing: https://github.com/mozilla-b2g/gaia/blob/16b7f7c8d313917517ec834dbda05db117ec141c/apps/sms/test/unit/thread_ui_test.js#L1614
Here is how I would do it. I'm using Mockery to manipulate the module loading. The code of widgetservice.js must changed so that it calls require('./widget');, without the .js extension. Without the modification, the following code won't work because I use the general recommended practice of avoiding extensions in require calls. Mockery is states clearly that the names passed to the require call must match exactly so.
The test runner is Mocha.
The code follows. I've put copious comments in the code itself.
var mockery = require("mockery");
var sinon = require("sinon");
// We grab a reference to the pristine Widget, to be used later.
var Widget = require("./widget");
// Convenience object to group the options we use for mockery.
var mockery_options = {
// `useCleanCache` ensures that "./widget", which we've
// previously loaded is forgotten when we enable mockery.
useCleanCache: true,
// Please look at the documentation on these two options. I've
// turned them off but by default they are on and they may help
// with creating a test suite.
warnOnReplace: false,
warnOnUnregistered: false
};
describe("widgetservice", function () {
describe("createWidget", function () {
var test_doc = {title: "foo"};
it("creates a widget with the correct data", function () {
// Create a mock that provides the bare minimum. We
// expect it to be called with the value of `test_doc`.
// And it returns an object which has a fake `save` method
// that does nothing. This is *just enough* for *this*
// test.
var mock = sinon.mock().withArgs(test_doc)
.returns({"save": function () {}});
// Register our mock with mockery.
mockery.registerMock('./widget', mock);
// Then tell mockery to intercept module loading.
mockery.enable(mockery_options);
// Now we load the service and mockery will give it our mock
// Widget.
var service = require("./widgetservice");
service.createWidget(test_doc, function () {});
mock.verify(); // Always remember to verify!
});
it("saves a widget with the correct data", function () {
var mock;
// This will intercept object creation requests and return an
// object on which we can check method invocations.
function Intercept() {
// Do the usual thing...
var ret = Widget.apply(this, arguments);
// Mock only on the `save` method. When it is called,
// it should call its first argument with the
// parameters passed to `yields`. This effectively
// simulates what mongoose would do when there is no
// error.
mock = sinon.mock(ret, "save").expects("save")
.yields(null, arguments[0]);
return ret;
}
// See the first test.
mockery.registerMock('./widget', Intercept);
mockery.enable(mockery_options);
var service = require("./widgetservice");
// We use sinon to create a callback for our test. We could
// just as well have passed an anonymous function that contains
// assertions to check the parameters. We expect it to be called
// with `null, test_doc`.
var callback = sinon.mock().withArgs(null, test_doc);
service.createWidget(test_doc, callback);
mock.verify();
callback.verify();
});
afterEach(function () {
// General cleanup after each test.
mockery.disable();
mockery.deregisterAll();
// Make sure we leave nothing behind in the cache.
mockery.resetCache();
});
});
});
Unless I've missed something, this covers all the tests that were mentioned in the question.
With current version of Mongoose you can use create method
// widgetservice.js
var Widget = require('./widget.js');
var createWidget = function(data, callback) {
Widget.create(data, callback);
};
Then to test the method (using Mocha)
// test.js
var sinon = require('sinon');
var mongoose = require('mongoose');
var Widget = mongoose.model('Widget');
var WidgetMock = sinon.mock(Widget);
var widgetService = require('...');
describe('widgetservice', function () {
describe('createWidget', function () {
it('should create a widget', function () {
var doc = { title: 'foo' };
WidgetMock
.expects('create').withArgs(doc)
.yields(null, 'RESULT');
widgetService.createWidget(doc, function (err, res) {
assert.equal(res, 'RESULT');
WidgetMock.verify();
WidgetMock.restore();
});
});
});
});
Also, if you want to mock chained methods use sinon-mongoose.

Categories