No output from jasmine-node on FilesizeWatcherSpec - Newbie Alert - javascript

I'm new to Node.js and jasmine, and my JavaScript experience is old and rusty, so I'm a newbie there too. I finished Manuel Kiessling's book, The Node Beginner Book, and I am working my way through his second book, The Node Craftsman Book. I'm stuck on the FilesizeWatcher tutorial. I've been able to run earlier tests but this one is not working. There is a similar question on SO: No output from jasmine-node but the answer isn't working for me.
I'll post my code here and hopefully somebody can tell me what I'm doing wrong.
FilesizeWatcherSpec.js:
'use strict';
var FilesizeWatcher = require('./FilesizeWatcher');
var exec = require('child_process').exec;
describe('FilesizeWatcher', function() {
var watcher;
afterEach(function() {
watcher.stop();
});
it('should fire a "grew" event when the file grew in size', function(done) {
var path = './var/tmp/filesizewatcher.test';
exec('rm -f ' + path + ' ; touch ' + path, function() {
watcher = new FilesizeWatcher(path);
watcher.on('grew', function(gain) {
expect(gain).toBe(5);
done();
});
exec('echo "test" > ' + path, function(){});
});
});
it('should fire a "shrank" event when the file shrank in size', function(done) {
var path = './var/tmp/filesizewatcher.test';
exec('rm -f ' + path + ' ; echo "test" > ' + path, function() {
watcher = new FilesizeWather(path);
watcher.on('shrank', function(loss) {
expect(loss).toBe(3);
done();
});
exec('echo "a" > ' + path, function(){});
});
});
it('should fire an "error" if path does not start', function(done) {
var path = 'var/tmp/filesizewatcher.test';
watcher = new FilesizeWather(path);
watcher.on('error', function(err) {
expect(err).toBe('Path does not start with a slash');
done();
});
});
});
FilesizeWatcher.js:
'use strict';
var fs = require('fs');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var FilesizeWatcher = function (path) {
var self = this;
if (/^\//.test(path) === false) {
process.nextTick(function() {
self.emit('error', 'Path does not start with a slash');
});
return;
}
fs.stat(path, function (err, stats) {
console.log('stats= ' + stats);
self.lastfilesize = stats.size;
});
self.interval = setInterval(
function () {
console.log('We are in function()');
fs.stat(path, function (err, stats) {
if (stats.size > self.lastfilesize) {
self.emit('grew', stats.size - self.lastfilesize);
self.lastfilesize = stats.size;
}
if (stats.size < self.lastfilesize) {
self.emit('shrank', self.lastfilesize - stats.size);
self.lastfilesize = stats.size;
}
}, 1000);
});
};
util.inherits(FilesizeWatcher, EventEmitter);
FilesizeWatcher.prototype.stop = function () {
clearInterval(this.interval);
};
module.exports = FilesizeWatcher;
Console output:
C:\Users\pdl\Projects\NodeCraftsman>jasmine-node ./FilesizeWatcherSpec.js
C:\Users\pdl\Projects\NodeCraftsman>
Other tests run fine:
C:\Users\pdl\Projects\NodeCraftsmanTestDrivenDevelopment>jasmine-node spec\greetSpec.js
..
Finished in 0.006 seconds
2 tests, 2 assertions, 0 failures, 0 skipped
C:\Users\pdl\Projects\NodeCraftsmanTestDrivenDevelopment>
I added --captureExceptions to see if I could get any information and I got the TypeError: self.callbacks.error is not a function.
My first problem was as Eppilo suggested below, that I needed to use process.nextTick on self.callbacks'error'. Mixing async code with sync code causes the error event to be fired before the error handler is registered. So I made the changes and am now using the EventEmitter but I'm still getting the following errors:
If I include the "." in the path: var path = './var/tmp/filesizewatcher.test'; then the file gets written. Otherwise, it does not.
If the file does NOT get written, stats= undefined and I receive this error:
TypeError: Cannot read property 'size' of undefined
at C:\Users\pdl\Projects\NodeCraftsman\FilesizeWatcher.js:19:34
at FSReqWrap.oncomplete (fs.js:82:15)
If the file DOES get written, then I receive this error:
Error: Uncaught, unspecified "error" event. (Path does not start with a slash)
at emit (events.js:144:17)
at C:\Users\pdl\Projects\NodeCraftsman\FilesizeWatcher.js:12:18
at nextTickCallbackWith0Args (node.js:419:9)
at process._tickCallback (node.js:348:13)
Of course, it's not supposed to start with a slash. That is the test. But when I remove the --captureExceptions from the command, I still get no output.

First of all try and run Jasmine on verbose mode and capture exceptions:
jasmine-node ./FilesizeWatcherSpec.js --verbose --captureExceptions
Link: https://github.com/mhevery/jasmine-node/wiki/Command-Line-Usage
Also try to make the error checking asynchronous:
if (/^\//.test(path) === false) {
process.nextTick(function() {
self.callbacks['error']('Path does not start with a slash');
});
return;
}

Newbie as well, with not enough reputation to comment.
I got the same no output on my Mac too, and was able to get the test to work with this.
There is an error in FilesizeWatcher.js.
Currently:
self.interval = setInterval(
function (){
...
fs.stat(path, function (err, stats) {
...
}, 1000);
});
It should instead be:
self.interval = setInterval(
function (){
...
fs.stat(path, function (err, stats) {
...
});
},1000);
Just sharing my findings, cheers.

Related

Create unit test for events

I'm using the following open source for unzip the files which is working OK.
Now I want to create some integration test for it to see that the file was saved,the problem is that the process is trowing events and I don't know for sure when the process end,any idea how to solve this?
ex
var DecompressZip = require('decompress-zip');
var unzipper = new DecompressZip(filename)
unzipper.on('error', function (err) {
console.log('Caught an error');
});
unzipper.on('extract', function (log) {
console.log('Finished extracting');
});
unzipper.on('progress', function (fileIndex, fileCount) {
console.log('Extracted file ' + (fileIndex + 1) + ' of ' + fileCount);
});
unzipper.extract({
path: 'some/path',
filter: function (file) {
return file.type !== "SymbolicLink";
}
});
I use mocha & chai as test framework.
and this is the open source
https://github.com/bower/decompress-zip
Mocha support asynchronous code you need to call test case with done argument and then once all checks is completed call done function.
Example:
var DecompressZip = require('decompress-zip');
describe("Unzipper test", function() {
it("should extract folder without error", function(done) {
var unzipper = new DecompressZip(filename);
unzipper.on('error', done); // Pass error to done callback
unzipper.on('extract', function (log) {
// Add your checks
done();
});
});
unzipper.extract({
path: 'some/path',
filter: function (file) {
return file.type !== "SymbolicLink";
}
});
});

Mocha calling callback after done()

I am aiming for ease of use in my unit tests, I have created a master_test.js file that I will include below. In it, I simply require other test.js files as a function and run them. An issue I had is that Mocha does not detect any unit tests to be run unless I encase my startup in a describe() block, so I am forced to have a master describe encase my application to resolve that. My problem now is that in order to run the other tests, I have to call a callback() after a done(). Is there any easy way to fix my problem?
function app_setup() {
options.database = 'testing';
it('app launched successfully', function(done) {
require('../app.js').initialize(0, options, function(err, app) {
remove_all_entities(function() {
app.set('port', port);
var server = app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + server.address().port);
//ISSUE LOCATION, NEED TO CALL run_tests() callback after done()//
done();
run_tests();
});
});
});
});
}
function run_tests() {
var database = require('../database.js');
var entity_controller_test = require('./controllers/entity_controller_test.js').entity_controller_test;
var login_test = require('./controllers/login_test.js').login_test;
var token_access_test = require('./controllers/token_access_test.js').token_access_test;
var token_auth_test = require('./controllers/auth_token_test.js').token_auth_test;
var business_rules_insert = require('./business_rules/basic_database_rules.js').business_rules_insert_test;
var logout_test = require('./controllers/logout_test.js').logout_test;
var schema_override = require('./business_rules/schema_overrides').schema_overrides;
var aggregation_test = require('./entity_specific/aggregation').aggregation_test;
var tests = [login_test, aggregation_test, logout_test];
async.series(tests, function() {
test_teardown(done);
});
}
function test_teardown(done) {
remove_all_entities(done);
};

Rookie error while writing test with Chai, Mocha, Express, johnny-five and node

Hi there I'm trying to learn a bit of test driven development using express, mocha, chai and johnny-five. So I wrote this little application that can turn an LED on and off. The application works but my test fails. Can somebody tell me what I am doing wrong in my test?
Thank you
The output of npm test is
> blink#1.0.0 test /Users/me/Documents/johnny-five/blink
> mocha --reporter spec
1435257445439 Looking for connected device
j5
.on()
1) Should turn a led on
.off()
✓ Should turn a led off
1 passing (13ms)
1 failing
1) j5 .on() Should turn a led on:
AssertionError: expected undefined to equal 1
at Context.<anonymous> (test/j5.js:9:14)
npm ERR! Test failed. See above for more details.
This is test/j5.js
require('mocha');
var assert = require('chai').assert;
var j5 = require("../j5");
describe('j5', function () {
describe('.on()', function () {
it('Should turn a led on',function(){
var result = j5.on();
assert.equal(result, 1);
});
});
describe('.off()', function () {
it('Should turn a led off', function () {
// var res = j5.on();
// expect(res).to.equal(0);
});
});
});
This is server.js
var express = require("express");
var app = express();
var j5 = require("./j5");
var port = 3000;
app.get('/', function(req, res){
res.send('hello j5');
});
app.get("/on", function(req, res) {
j5.on();
res.send("on");
});
app.get("/off", function(req, res) {
j5.off();
res.send("off");
});
console.log("listening on port http://localhost:" + port);
app.listen(3000);
This is j5.js
var exports = module.exports = {};
var five = require("johnny-five");
var board = new five.Board();
var board_ready = false;
var led = null;
board.on("ready", function() {
board_ready = true;
led = new five.Led(13);
});
exports.on = function() {
if (led !== null && board_ready === true) {
led.on();
return 1;
}
};
exports.off = function() {
if (led !== null && board_ready === true) {
led.off();
return 0;
}
};
EDIT: The path to my j5.js in test/j5.js was wrong. but now I have a new error. AssertionError: expected undefined to equal 1 at Context. (test/j5.js:9:14).
After some playing around I found my error.
johnny-five needs some time to connect to the board via the serial. As soon as the build in REPL is available I can use the functions on() and off(). So I made my test wait for 5 seconds before making the call of j5.on(). The standard max timeout for the done() function is 2000ms. To make this longer I used this.timeout(10000);
This is my new test/j5.js
require('mocha');
var assert = require('chai').assert;
var j5 = require("../j5");
var result = null;
describe('j5', function() {
describe('.on()', function() {
it('Should turn a led on', function(done) {
this.timeout(10000);
setTimeout(function() {
result = j5.on();
assert.equal(result, 1);
done();
}, 5000);
});
});
});
Result of npm test:
> blink#1.0.0 test /Users/icke/Documents/johnny-five/blink
> mocha --reporter spec
1435305595110 Device(s) /dev/cu.usbmodem1421
1435305595124 Connected /dev/cu.usbmodem1421
j5
.on()
1435305598694 Repl Initialized
✓ Should turn a led on (5003ms)
.off()
✓ Should turn a led off
2 passing (5s)

exec 'node app' hangs inside gulp task

This gulp task hangs on exec('node config/app') line. first exec works fine but the second just hangs.
gulp.task('test', function(cb) {
var exec = require('child_process').exec;
exec('echo 3', function(err, stdout) {
console.log(stdout);
});
exec('node config/app', function(err, stdout, stderr) {
console.log(stdout);
var testemOptions = {
file: 'testem.json'
};
var t = new testem();
return t.startCI(testemOptions, function() {
cb();
});
});
});
I can see the output 3 but no output is shown for the second console.log.
I am trying to run my server before running the tests with testem.
I've tried this similar solution but it doesn't work: Exec not returning anything when trying to run git shortlog with nodejs.
Also I've recently asked a hanging testem gulp task question: Testem gulp task hangs after finished.
Edit:
My current solution is:
gulp.task('test', /*['build'],*/ function(cb) {
var spawn = require('child_process').spawn;
var proc = spawn('node', ['config/app']);
proc.stdout.on('readable', function() {
var output = proc.stdout.read();
if (output && output.toString().match('express listening')) {
var testemOptions = {
file: 'testem.json'
};
var t = new testem();
t.startCI(testemOptions, function() {
proc.kill();
cb();
});
}
});
});
If you want to use testem to test the "node config/app" server, you cannot use exec.
Exec is supposed to callback when the command is finished so in your case it will never callback.
try with
gulp.task('test', function(cb) {
var spawn = require('child_process').spawn;
var proc = spawn('node', ['config/app']);
var testStarted = false;
proc.stdout.on('readable', function() {
if (testStarted) return;
testStarted = true;
var testemOptions = {
file: 'testem.json'
};
var t = new testem();
t.startCI(testemOptions, function() {
proc.kill()
cb();
});
}
});
Note that I did not test this code and that it probably does not handle all the corner cases you might encounter (if the server stops unexpectedly for example)
you may also want to check the plugin https://github.com/sargentsurg/gulp-testem
There is ŧestem plugin on github.

Mocha test is failing but implementation works

I am trying to write a newer watch module that uses the fs.watch method instead of the watchFile approach.
So far, it works beautifully, but only when I run it outside of mocha. I can't figure out why my unit test is throwing a tantrum, maybe someone here can?
Here is my class code:
/**
* requirements
*/
var fs, path, events;
fs = require('fs');
path = require('path');
events = require('events');
/**
* private
*/
var Monitor = function(directory, options) {
this.directory = directory;
this.options = options || {};
(this.options.lazy && this.empty()) || this.walk(this.directory);
this.watch(this.directory);
};
Monitor.prototype = new events.EventEmitter();
Monitor.prototype.watch = function(directory, stats) {
var stats = stats || {};
if (!this.directories[directory]) {
var w = fs.watch(directory, this.options, this.justlookatit.bind(this));
}
this.directories[directory] = { 'stats': stats, 'w': w };
};
Monitor.prototype.directories = function() {
if (!Object.keys(this.directories).length) {
this.walk(this.directory);
}
return this.directories;
};
Monitor.prototype.files = function() {
if (!Object.keys(this.files).length) {
this.walk(this.directory);
}
return this.files;
};
Monitor.prototype.unwatch = function() {
if (!Object.keys(this.directories).length) {
for (var dir in this.directories) {
dir.w.close();
}
}
};
Monitor.prototype.empty = function() {
this.unwatch();
this.files = {};
this.directories = {};
};
Monitor.prototype.walk = function(directory) {
var monitor = this;
this.empty();
fs.readdir(directory, function(err, files) {
if (err) return;
for (var file in files) {
var fullname = path.resolve(files[file]);
if (!monitor.options.filter || monitor.options.filter(fullname)) {
fs.stat(fullname, function(err, stats) {
if (err) return;
if (stats.isDirectory()) {
monitor.walk(fullname);
monitor.watch(fullname, stats);
} else {
monitor.files[fullname] = stats;
}
});
}
}
});
};
Monitor.prototype.justlookatit = function(action, file) {
var monitor = this;
var fullname = path.resolve(file);
if (this.options.filter && !this.options.filer(fullname)) return;
fs.exists(fullname, function(exists) {
if (exists) {
fs.stat(fullname, function(err, stats) {
if (stats.isDirectory()) {
monitor.watch(fullname, stats);
} else {
if (monitor.files[fullname]) {
if (stats.mtime.getTime() > monitor.files[fullname].mtime.getTime()) {
monitor.emit('modified', fullname, stats);
}
} else {
monitor.emit('added', fullname, stats);
}
monitor.files[fullname] = stats;
}
});
} else {
if (monitor.files[fullname]) {
delete monitor.files[fullname];
monitor.emit('deleted', fullname);
} else if (monitor.directories[fullname]) {
monitor.directories[fullname].w.close();
delete monitor.directories[fullname];
}
}
});
};
/**
* exports
*/
exports.start = function(directory, options) {
return new Monitor(directory, options);
};
Here is my Working external test code:
var watch = require("./watch.js");
var fs = require('fs');
monitor = watch.start(__dirname);
monitor.on('added', function(file, stats) {
console.log("Caught Added: " + file);
});
monitor.on('modified', function(file, stats) {
console.log("Caught Modified: " + file);
});
monitor.on('deleted', function(file) {
console.log("Caught deleted: " + file);
});
// try creating a file immediately
fs.openSync('v.md', 'w');
The first test file runs perfectly fine, and I've tried both openSync and open. Finally, here is a version of the same test code, wrapped in a mocha unit test which is timing out:
/**
* requirements
*/
var watch, Q, fs, path, mocha, chai, assert;
watch = require('../lib/watch.js');
Q = require('q');
fs = require('fs');
path = require('path');
mocha = require('mocha');
chai = require('chai');
assert = chai.assert;
/**
* variables
*/
var watch_directory = path.join(__dirname, './watch');
/**
* tests
*/
describe('test watch', function() {
it('should create a monitor and run callbacks after fs changes', function(done) {
// I had planned to implement promises that chained the three callbacks
// but couldn't get one of them working in general
var added = function(file, stats) {
console.log("added");
done();
};
var modified = function(file, stats) {
console.log("modified");
};
var deleted = function(file, stats) {
console.log("deleted");
};
// create our service
var monitor = watch.start(watch_directory);
// assert it is defined
assert.isDefined(monitor);
// establish a listener
monitor.on('added', added);
monitor.on('modified', modified);
monitor.on('deleted', deleted);
// here is a file name using the current date to prevent duplication during tests
var file = path.join(watch_directory, (new Date()).getTime() + '.md');
// let's create the file, then delete it
fs.open(file, 'w+', function(err, fileDescriptor) {
// this prints before console output from the watch.js's `justlookatit` method
console.log(err);
console.log("writing to file");
// we probably don't want to try closing the fileDescriptor if the open failed
if (err) return;
// close the file descriptor
fs.close(fileDescriptor, function() {
// delete the file we just created
// fs.unlink(file, function() { /* not a big deal */ });
});
});
// modify a known-existing test file
fs.open('test.md', 'w+', function() {/* we don't care about this */});
})
});
I checked with console.log(fullname) inside the justlookatit method on the watch code, and it spits out the correct file name, matching the one generated by the unit test.
However, it then proceeds to return false when I run fs.exists. As I undestand it, that means the file system is notifying me that a file exists before it exists, which doesn't make sense really. So I tried adding an additional delay by wrapping my fs.exists method in a setTimeout, and that didn't change the results. I have also tried using both openSync and existsSync, and that made no difference.
I'm stumped, does anyone have any ideas why the mocha code isn't working?
So, the solution was to go for a walk. I came back, looked at the code again and figured out the cause of the problem with mocha, and also identified many other bugs.
The problem was the lack of context. The justlookatit method does not have a context, and in the test.js scenario it is watching the current directory, while the mocha test is watching a sub-directory.
The path.resolve was receiving only the file name, not the directory, and therefore merged it with the default (executables) directory, so the level of test.js, or watch_test.js for mocha. It proceeded to fail to locate any of the files in the mocha test case because they were all one level below the executable.
I won't go into detail about all the other bugs, but I may come back and post the repository link when I get to a point that I want to push it online.
You're missing the callback return(done); at the end of your test. Unless you call that callback, Mocha will time out every time.

Categories