Understanding Scopes in Spooky JS - javascript

This implementation of SpookyJS is really spooky. While using Gulp to run Mocha + SpookyJS tests, I am unable to see most console log output. I have been following the Quickstart steps on SpookyJS's github page. Why can't I see these console log outputs?
describe('test', function () {
it('test 1', function(done){
try {
var Spooky = require('spooky');
} catch (e) {
var Spooky = require('../lib/spooky');
}
var spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug',
verbose: true
}
}, function (err) {
if (err) {
e = new Error('Failed to initialize SpookyJS');
e.details = err;
throw e;
}
spooky.start(URL);
console.log('Hello 3'); //currently this is not printing anything to the console
spooky.then(function () {
this.emit('hello', 'Hello, from ' + this.evaluate(function () {
return document.title;
}));
});
spooky.run();
console.log('Hello 1'); //currently this is not printing anything to the console
});
spooky.on('hello', function (line) {
console.log(line);
});
spooky.on('console', function (line) {
console.log(line);
});
spooky.on('error', function (e, stack) {
console.error(e);
if (stack) {
console.log(stack);
}
});
console.log('Hello 2'); //this is printing to the console
done();
});
});

Simplifying your code, it roughly looks like this:
describe('test', function () {
it('test 1', function(done){
var Spooky = require('spooky');
var spooky = create a spooky object with a lot of logic in it;
console.log('Hello 2'); //this is printing to the console
done();
});
});
The spooky object is created, and after that, the program flow simply continues. Anything that the spooky object does, it does asynchronously, later on.
After creating the spooky object, you get your Hello 2 and then done() is called, which ends this test.
So, before any hello stuff can be processed, your tests ends already.
What might help is to move the done() line here:
spooky.on('hello', function (line) {
console.log(line);
done();
});
Then your test will end once the hello event has been caught.
Since I'm not familiar with Spooky or with what exactly you want to test, I'm afraid I can't be of any more help. Hopefully this gets you a step further.

Related

Promises in test cases in mocha and chai failing

I am writing a test case in mocha and chai to check if the file is not present it will create the file. Following is the test case :
context('if the valid message is supplied and file is not present in the app\'s logs folder', () => {
beforeEach((done) => {
setTimeout(() => {
fs.exists(filePath, (exists) => {
if (exists) {
fileFound = true;
} else {
fileFound = false;
}
});
done();
}, 100);
});
it('should indicate the file is not present in the app\'s log folder', () => {
expect(fileFound).to.be.false;
});
it('should create a new file in the app\'s log folder', () => {
expect(fileFound).to.be.true;
});
});
Let's day file is present in the folder, in that case first test case should fail. But the problem is, it is saying expected undefined to be false, rather than expected true to be false.
There's very little point in using promises here. Your API is callback-based, so you should use a callback test.
Like this:
it('should exist', (done) => {
fs.exists(filePath, (exists) => {
expect(exists).to.be.true;
done();
});
});
One thing to bear in mind, mostly unrelated to your issue, is that fs.exists is deprecated and you should use a different method like fs.access or fs.stat:
it('should exist', (done) => {
fs.access(filePath, (err) => {
expect(err).to.be.null;
done();
});
});
To address your post edit question, the problem here is that you are using setTimeout for no reason and calling done before fs.exists has a chance to finish.
The solution: get rid of the setTimeout and call done inside the fs.exists callback. You should also scope your fileFound variable at a place where it makes sense:
context('if the valid message is supplied and file is not present in the app\'s logs folder', () => {
let fileFound;
beforeEach((done) => {
fs.exists(filePath, (exists) => {
fileFound = exists;
done();
});
});
it('should indicate the file is not present in the app\'s log folder', () => {
expect(fileFound).to.be.false;
});
it('should create a new file in the app\'s log folder', () => {
expect(fileFound).to.be.true;
});
});

Mocha / Sinon - Unit testing / stubbing function with child_process.exec inside ES6 Promise

I have a function that returns an ES6 promise for use in another external promise chain. The function scans for wifi networks using child_process.exec. The output from exec is sent via the callback to a synchronous parsing function that will return the output in a formatted object, and then resolve the outer promise:
var exec = require('child_process').exec;
function scan() {
return new Promise((resolve, reject) => {
// getCurrent returns a promise
getCurrent().then(network => {
exec('iwlist wlan0 scan', function(err, stdout, stderr) {
(err) ? reject(stderr) : resolve(parseOutput(stdout, network));
});
})
.catch(err => {
reject(err);
});
});
}
The problem is, I can't seem to get sinon stubs working properly to get the stubbed version of exec to be called. Currently I have something along the lines of:
var wifi = require('./wifi');
describe('#scan', function() {
it('Should scan and return networks object', function() {
var fakeNetwork = '(fake iwlist output goes here)';
var exec = sinon.stub(child_process, 'exec').yields(false, fakeNetwork);
var getCurrent = sinon.stub(wifi, 'getCurrent').returns(Promise.resolve('current-ssid'));
wifi.scan().then(function(networks) {
console.log(networks);
sinon.assert.calledOnce(getCurrent);
sinon.assert.calledOnce(exec);
});
getCurrent.restore();
exec.restore();
});
});
I have also tried stubbing like:
var getCurrent = sinon.stub(wifi, 'getCurrent', function() {
return Promise.resolve('current-ssid').then(network => {
exec('iwlist wlan0 scan', function(err, data) {
return(wifi.parseOutput(data, network));
});
});
});
The behavior seems to be that either exec never gets called, or more strangely, that the stubbed exec does get called, but the 'real' functions get called anyway.
With all of the stubs, all I am really "testing" is the parseOutput method, so should I just ditch testing the 'scan' function completely and only focus on the parse function (which would be simple to test), or is there another way I should be looking at this?
I believe I am just doing something obviously wrong, and have been hitting a wall here for several hours. I'm hoping someone has run into this before and can suggest an alternative/better approach.
you can use stub.yields([arg1, arg2, ...]) sinon stubs guide
here is my code
function getVersioniList() {
let versionList = new Promise((resolve, reject) => {
child_process.exec(cmd, function(error, stdout, stderr) {
if (error) {
reject(error);
}
resolve(stdout);
});
});
return versionList;
}
and unit test code as below
// i like the sandbox, or you can use sinon itself
sandbox = sinon.sandbox.create();
sandbox.stub(child_process, 'exec').yields(undefined, [1, 2, 3]);
assert.equal(await getVersioniList(), [1, 2, 3]);
finally, you can get versionList [1, 2, 3]

Promises in Mocha Unit Tests

This question is related to Using Promises to test Meteor - Mocha
Like Louis suggested, I have replicated the same issue in a smaller program, so that you can reproduce this. And in this one too Mocha doesn't care about the assert statement. The catch block of the promises gets this error.
/server/main.js
import { Meteor } from 'meteor/meteor';
export const myCollection = new Mongo.Collection('mycollection');
export const addObject = function (id) {
myCollection.insert({
name: 'test ' + id
});
}
Meteor.publish('mycollection', function() {
return myCollection.find({});
});
/server/main.test.js
/**
* Created by enigma on 6/9/16.
*/
import { Meteor } from 'meteor/meteor';
import { PublicationCollector } from 'meteor/johanbrook:publication-collector';
import { Promise } from 'meteor/promise';
import { assert } from 'meteor/practicalmeteor:chai';
import { Random } from 'meteor/random';
import { addObject } from '../server/main.js';
if (Meteor.isServer) {
describe('test mocha promise', function() {
before(function() {
addObject(Random.id());
});
it('collects myCollection test', function() {
const collector = new PublicationCollector({ userId: Random.id()});
return new Promise(function(resolve) {
collector.collect('mycollection', function (collections) {
resolve(collections);
});
}).then(function(coll) {
chai.assert.notEqual(coll, null);
chai.assert.equal(coll, null);
}).catch(function(err) {
console.log('error:', err.stack);
});
});
});
}
console output
=> Meteor server restarted
I20160609-18:31:14.546(-5)? MochaRunner.runServerTests: Starting server side tests with run id GK3WqWY4Ln9u6vmsg
I20160609-18:31:14.598(-5)? error: AssertionError: expected { Object (mycollection) } to equal null
I20160609-18:31:14.598(-5)? at Function.assert.equal (packages/practicalmeteor_chai.js:2635:10)
I20160609-18:31:14.598(-5)? at test/main.test.js:25:29
I20160609-18:31:14.598(-5)? at /Users/enigma/.meteor/packages/promise/.0.6.7.1d67q83++os+web.browser+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:33:40
W20160609-18:31:14.607(-5)? (STDERR) MochaRunner.runServerTests: failures: 0
you need to either throw in the catch or remove the catch, so that mocha gets the error too. currently since you catch the error, the promise mocha gets is resolved.
Below was my old answer, before the question change
ps: it looks like I misunderstood what was a publish for meteor so the bellow answer is not really correct
The error you're encountering is because "mycollection" is not published
I is probably because Meteor.publish('mycollection'); is an async function, to the collection is not published yet when you test it.
you should do the publish in a before() before your test
Here is an example of how you can wait the publish to finish in a before
I read this in a discussion, this works for me, although some are discouraging use of 'done' callback with promises.
it('collects myCollection test', function(done) {
const collector = new PublicationCollector({ userId: Random.id()});
return new Promise(function(resolve) {
collector.collect('mycollection', function (collections) {
resolve(collections);
});
}).then(function(coll) {
chai.assert.notEqual(coll, null);
chai.assert.equal(coll, null);
done();
}).catch(function(err) {
done(err);
});
});
I use PublicationCollector like this:
it('should publish 2 documents', async () => {
const collector = new PublicationCollector({ 'userId': Random.id() });
const testPromise = new Promise((resolve, reject) => {
collector.collect('myDocuments',
(collections) => {
resolve(collections.myDocuments.length);
});
});
const result = await testPromise;
assert.equal(result, 1);
});
Adapted from here.
This test fails relatively gracefully.

angular.js Assert error throw async

I'm testing an angular.js app using karma with sinon-chai. I need to test a code like this:
var sync = function() {
async.then(function() {
// success
}, function() {
throw Error('foo');
});
}
sync is the function I want to test. async is a function returning a $q promise I want to test it fails throwing an error.
My ideal test suit:
describe('unit', function() {
it('should throw an error', function() {
expect(function() {
return sync();
}).to.throw(Error, 'foo');
})
});
But, it fails at afterEach() because the error is thrown on $digest.
How can I achieve this test?
Thank you very much!

Callbacks in Laika tests are not called

Meteor.collection.insert() accepts callback as an argument. As an example, one can create a brand new Meteor project and run the following code in the browser's console.
my_collection = new Meteor.Collection("myCollection");
my_collection.insert(
{some: "object"},
function() {
console.log("finished insertion");
})
When I take this same code and put it in a Laika test, the callback argument never gets called. Here is my test code:
suite('testing Laika out', function() {
test('inserting into collection', function(done, server, client) {
client.eval(function() {
my_collection = new Meteor.Collection("myCollection");
my_collection.insert(
{some: "object"},
function() {
console.log("finished insertion");
done();
})
})
})
})
Anyone know why the callback function isn't called in this Laika test? This seems to be an issue for more than just Meteor.collection.insert().
(I'm running Ubuntu 13.04, Meteor 0.7.0.1, Laika 0.3.1, PhantomJS 1.9.2-6)
The problem is that you're trying to call done(); inside of your insert callback, when it doesn't exist in that function scope. You actually need to listen for the insertion into my_collection and emit a signal which is picked up by either the client or server (the client in your case). Also, you obviously won't be initializing your collection in your test; that should be done in your production code.
Try this instead:
var assert = require("assert");
suite('testing Laika out', function() {
test('inserting into collection', function(done, server, client) {
client.eval(function() {
addedNew = function(newItem) {
console.log("finished insertion");
emit("done", newItem)
};
my_collection = new Meteor.Collection("myCollection");
my_collection.find().observe({
added: addedNew
});
my_collection.insert(
{some: "object"}
)
}).once("done", function(item) {
assert.equal(item.some, "object");
done();
});
});
})
Check out https://github.com/arunoda/hello-laika for the basic examples for testing.
Well, Mr. jonS90, if you were to run Laika with the --verbose flag, you would notice that an exception is quietly being thrown:
[client log] Exception in delivering result of invoking '/myCollection/insert': ReferenceError: Can't find variable: done
You see, you don't have access to done() in that context. Here's how you should revise your code:
test('inserting into collection', function(done, server, client) {
client.eval(function() {
my_collection = new Meteor.Collection("myCollection");
finishedInsertion = function () {
console.log("finished insertion");
emit('done')
}
my_collection.insert(
{some: "object"},
finishedInsertion)
})
client.once('done', function() {
done();
})
})

Categories