SpookyJS: Extra Then Required After Filling Forms - node.js

The CasperJS Quickstart Guide has this example on filling out a form:
casper.start('http://google.fr/', function() {
// search for 'casperjs' from google form
this.fill('form[action="/search"]', { q: 'casperjs' }, true);
});
casper.then(function() {
// aggregate results for the 'casperjs' search
links = this.evaluate(getLinks);
// now search for 'phantomjs' by filling the form again
this.fill('form[action="/search"]', { q: 'phantomjs' }, true);
});
The form is submitted automatically because of the true parameter being passed to this.fill, and the next casper.then function is called as soon as the new page is loaded.
But when writing Cucumber tests with SpookyJS like so:
spooky.start("http://www.somepage.com");
spooky.then(function () {
this.fill("form", {username: "foo", password: "bar"}, true);
});
spooky.then(function () {
this.echo(this.getPageContent());
this.echo(this.getCurrentUrl());
});
By looking at the page content and the URL, I can tell that Spooky is saying it's still on the form page when the spooky.then function is called. But if you add one more spooky.then like so:
//Empty Spooky.then call so we can do what we want on the correct page
this.spooky.then(function () {});
this.spooky.then(function () {
this.echo(this.getPageContent());
this.echo(this.getCurrentUrl());
});
Suddenly, this works. Why is an extra spooky.then call needed here, when it isn't needed while using Casper? I have tested this in Casper and it works as expected, but with Spooky, an extra then call is needed.

Related

How to Get Window Variable Using WebdriverIO

I am trying to run webdriverio with PhantomJS/Chrome to load a page and then grab the window object for use with other scripts. For some reason I am unable to get the window object. Everytime I get, I end up seeing output like this:
Title is: XXXXX
{ state: 'pending' }
Using the following script:
var webdriverio = require('webdriverio');
var options = {
desiredCapabilities: {
browserName: 'chrome',
logLevel: 'verbose'
}
};
var client = webdriverio.remote(options);
client
.init()
.url('https://xxxx.com')
.waitUntil(function () {
return client.execute(function () {
return Date.now() - window.performance.timing.loadEventEnd > 40000;
}).then(function (result) {
console.log(window);
return window;
});
})
.end();
Does anyone know how I can fix my code so that the window object is returned to my NodeJS console app after the page is completely loaded?
Thanks!
Window is an object in the browser's DOM, so it's only available inside of the 'execute' function. If you wanted access to it, you could return it from your 'execute' function:
return client.execute(function () {
return window;
}).then(function (result) {
console.log(result);
});
This work as well:
browser.execute('return window');

Object state leaking between Mocha tests

I'm new to Mocha and I'm hoping someone can help me. I'm curious how I can ensure that actions performed in one test do not effect another. For example, assume that I have a function microbe that returns an object that looks like:
{
_state : {
views : 'views'
}
set : function(key, val) { this._state[key] = val }
}
This is a majorly reduced version of what I'm working with, but it demonstrates what I'm testing, using a test suite such as:
var expect = require('chai').expect;
describe('State', function() {
var microbe = require('../microbe/index'), app;
beforeEach(function(done) {
app = microbe();
});
it('should update the view location with app.set', function() {
app.set('views', 'test');
expect(app._state.views).to.equal('test');
});
it('should return "views" as the defualt view location', function() {
expect(app._state.views).to.equal('views');
});
});
The second test fails because the first test sets app._state.views to 'test' so it overrides the default of 'views'. I thought Mocha would isolate each test, but apparently not. I've tried to just re-instantiate the app for each test as shown usingbeforeEach, but it still seems to be leaky.
Whats the best way to do this? I know I could just reverse the order of the tests and it would technically pass, but I'd rather fix the issue itself than just avoid it
It looks as if your function microbe() is behaving as a singleton.
The easiest way of having a fresh instance of your microbe is to use the new keyword within the beforeEach():
beforeEach(function(done) {
app = new microbe();
});
Doing this, each execution of a test will start freshly with a 'default' instance of the object you are returning.
I just tested the code you posted and the tests run correctly:
test.js:
var expect = require('chai').expect;
describe('State', function() {
var microbe = require('./microbe.js'), app;
beforeEach(function() {
app = microbe();
});
it('should update the view location with app.set', function() {
app.set('views', 'test');
expect(app._state.views).to.equal('test');
});
it('should return "views" as the defualt view location', function() {
expect(app._state.views).to.equal('views');
});
});
microbe.js:
module.exports = function () {
return {
_state : {
views : 'views'
},
set : function(key, val) { this._state[key] = val; }
};
};
Executing mocha outputs:
/tmp mocha -R spec test.js
State
✓ should update the view location with app.set
✓ should return "views" as the defualt view location
2 passing (5ms)
You have something strange in your implementation (maybe a singleton or a global variable somewhere) but it's not Mocha's fault.

Can't update the mongodb document on the fly

I am quite new to Node.js and MongoDB. I am trying to have this code in which I have a boolean document called test which is initially "false" in Mongodb and I want to alert that when a page /hello is loaded. then go to another page submit a form and update test to true and load /hello again. so this time it should alert true (when I check the database it has been updated to true) but it doesn't alert anything when I test it. Would be great if you let me know when am doing wrong!
here are the relevant codes in my app.js
app.post("/Submitted", function(req,res){
var conditions = mongoose.model('users').findOne({username: req.session.user.username}, function (err, doc){
doc.test = true;
doc.save();
res.redirect('hello');
});
app.get('/hello', function (req, res) {
var umbrella = req.session.user.test;
if (req.session.user) {
res.render('5points', {test: test});
} else {
res.redirect('/');
}
});
and here is my jade file:
script.
function check() {
var test= !{JSON.stringify(test)};
alert(test);
body(onload= "check()")
The res.redirect('hello') is getting triggered before the doc.save gets completed.Remember node.js is non blocking I/O.So you need to call the res.redirect inside the callback of save.
app.post("/Submitted", function(req, res) {
var conditions = mongoose.model('users').findOne({
username: req.session.user.username
}, function(err, doc) {
doc.test = true;
doc.save(function(err) {
if (err) return res.json(message: "error");
res.redirect('hello');
});
}
});
});

Mock function return in node.js test

I'm new to testing in node.js and I would like to mock the return of a specific function call in a process that looks like the following.
doSomething(function(err, res){
callAnotherOne(res, function(err, result){
getDataFromDB(result, function(err, docs){
//some logic over the docs here
})
})
})
The function that I want to mock is the getDataFromDB() and specifically the documents (using MongoDB) that it returns.
How could I do something like this with mocha?
Part of the code, strip from the logic in between, is the following:
filterTweets(item, input, function(err, item) {
//Some filtering and logging here
db.getTwitterReplies(item, function(err, result) {
if(err) {
return callback('Failed to retrieve tweet replies');
}
//Do some work here on the item using the result (tweet replies)
/***** Here I want to test that the result is the expected ****/
db.storeTweets(item function (err, result){
//error checks, logging
callback();
});
});
});
Based on the amount of twitter replies (function call "getTwitterReplies"), I will modify my object accordingly (didn't include that code). I want to see if based on different replies result, my object is constructed as expected.
p.s. I also checked into sinon.js after some searching and I managed to mock the return of a callback (by writing some testing code outside my project) but not the return of a callback of a nested function call.
Here's how I would approach this category of problem:
First create a "config.js" that wraps the dependencies that you'd like to inject. This will become your container.
var db = {
doSomeDbWork : function(callback){
callback("db data");
}
};
module.exports = {
db: db
};
From there, you can call config dependencies like so:
var config = require('./index/config');
config.db.doSomeDbWork(function(data){
res.render('index', { title: 'Express' , data:data});
});
And in your tests, inject a mock/spy easily:
var config = require('../routes/index/config');
config.db = {
doSomeDbWork : function(callback){
callback("fake db data");
}
};
var indexRouter = require('../routes/index');
indexRouter.get('/');
Because the require call refers to the same config module exports, the changes made to the config in the spec will be reflected where ever they are imported via require()

Scraping with phantomJS and NodeJS

I'm following the tutorial listed here :
http://code.tutsplus.com/tutorials/screen-scraping-with-nodejs--net-25560
When I run the code:
var host = 'http://www.shoutcast.com/?action=sub&cat=Hindi#134';
var phantom = require('phantom');
phantom.create(function(ph) {
return ph.createPage(function(page) {
return page.open(host, function(status) {
console.log("opened site? ", status);
page.injectJs('http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js', function() {
//jQuery Loaded.
//Wait for a bit for AJAX content to load on the page. Here, we are waiting 5 seconds.
setTimeout(function() {
return page.evaluate(function() {
//Get what you want from the page using jQuery. A good way is to populate an object with all the jQuery commands that you need and then return the object.
console.log(document.getElementsByClassName('transition')[0]);
return document.getElementsByClassName('transition')[0];
}, function(result) {
console.log(result);
ph.exit();
});
}, 5000);
});
});
});
});
I get the following error :
phantom stdout: ReferenceError: Can't find variable: $
phantom stdout: phantomjs://webpage.evaluate():7
phantomjs://webpage.evaluate():10
phantomjs://webpage.evaluate():10
I have no idea what this means and there's no help on how to resolve it ...
How can this be solved ?
Basically I want all the 'a' tags with class transition from the site I'm scraping. All these tags are loaded asynchronously on the site.
The $ is due to jQuery and possible conflicts. You hardly require to inject jQuery just to scrape 'a' tags with class transition. You always have document.querySelector or document.querySelectorAll.
var host = 'http://www.shoutcast.com/?action=sub&cat=Hindi#134';
var phantom = require('phantom');
phantom.create(function(ph) {
ph.createPage(function(page) {
page.open(host, function(status) {
console.log("opened site? ", status);
//Wait for a bit for AJAX content to load on the page. Here, we are waiting 5 seconds.
setTimeout(function() {
page.evaluate(function() {
// here you need to add more code to get the html/text
// more code incase you use querySelectorAll
return document.document.querySelector('a.transition');
//return document.document.querySelectorAll('a.transition');
},
function(result) {
console.log(result);
ph.exit();
});
}, 5000);
});
});
});
However, I am not able to understand the way function (result) { console.log(result); ...} is coded. I am not aware if page.evaluate takes callback function as second parameter. Please check that with the documentation.

Resources