I'm trying to send the parametter countryCode to spookyjs function.
My question is how to do this , because when I wanna use the
countryCode inside spooky.then(function) the countryCode is empty
thank you a lot
This is the call code
var greetings = require("./get_country.js");
var countryCode = "ES";
greetings.getOne("ES");
This is the function code:
var Spooky = require('spooky');
module.exports = {
getOne: function(countryCode) {
var init = function(error) {
if (error) {
e = new Error('Failed to initialize SpookyJS');
e.details = error;
throw e;
}
spooky.start('http://www.domain.com/');
spooky.then(function() {
this.emit('return', this.evaluate(function() {
var raw_countries = document.querySelectorAll('#country-selector li.' + countryCode);
return raw_countries;
}));
});
spooky.run();
},
spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug',
verbose: true
}
}, init);
spooky.on('error', function(error, stack) {
console.error(error);
if (stack) {
console.log(stack);
}
});
spooky.on('return', function(data) {
console.log(data);
});
}};
You need to pass the required global variable to both spooky's then() as well as evaluate() functions to be able to access it as below
spooky.start('example.com/');
spooky.then([{ countryCode: countryCode }, function () {
this.emit('return', this.evaluate(function (countryCode) {
var raw_countries = document.querySelectorAll('#country-selector li.' + countryCode);
return raw_countries;
}), countryCode);
}]);
spooky.run();
Reference example
Related
I am new to node/express js, and trying to execute the following. The control executes the lines after function call "var nextVersion =getNextContractVersion(cid)", even before the function returns a response. As a result the value for newVersion is not updated to Contract object. Also, function getNextContractVersion(cid) returns undefined, unlike the updated nextVersion.
How do i fix this behavior, please suggest. Also, is the right way of invoking function?
// Package imports
const express = require('express');
var router = express.Router();
const mongoose = require('mongoose');
//Local imports
var { Customer } = require('../models/customer');
var { Contract } = require('../models/contract');
router.put('/:cid/contracts', (req, res) => {
var cid = req.params.cid;
var nextVersion =getNextContractVersion(cid);
var contract;
if (validateCustomerId(cid)) {
req.body.contract.forEach((item) => {
contract = new Contract({
customerID: cid,
startDate: item.startDate,
endDate: item.endDate,
conditions: item.conditions,
price: item.price,
author: item.author,
version: nextVersion
});
});
contract.save((err, docs) => {
if (!err) {
Customer.findOneAndUpdate({ customerID: cid }, { $push: { contract: contract } },
{ safe: true, upsert: true, new: true }).populate({ path: 'contract' }).exec((err1, docs1) => {
if (!err1) {
res.send(docs1).status(200);
} else {
console.log('Error is adding a new contract:' + JSON.stringify(err1, undefined, 2));
}
});
} else {
console.log('Error is updating a new customer:' + JSON.stringify(err, undefined, 2));
}
});
} else {
res.status(400).send('Bad Request - Invalid input!')
}
});
function getNextContractVersion(cid) {
var nextVersion=1;
Contract.findOne({ customerID: cid }).sort({version: 'descending'}).exec((err, doc) => {
if (!err && doc != null) {
var currentVersion = parseInt(doc.version);
nextVersion = currentVersion + 1;
}
});
return nextVersion;
}
You are mixing synchronous and asynchronous code.
Contract.findOne({ customerID: cid }).sort({version: 'descending'}).exec((err, doc) => {
if (!err && doc != null) {
var currentVersion = parseInt(doc.version);
nextVersion = currentVersion + 1;
}
});
The above code effectively says "Go to the database, find one of these objects and whenever in the future that is done, run this code that's in the exec block."
One of the ways to reason about asynchronous code from a synchronous mindset is that of promises.
Here's a semi pseudo implementation:
router.put('/:cid/contracts', (req, res) => {
var cid = req.params.cid;
return getTheMostRecentContract(cid)
.then(function(oldContract){
var nextVersion = oldContract.version +1;
if(!validateCustomerId(cid)){
return res.status(400).send('Bad Request - Invalid input!');
}
var contract;
var savePromises = [];
req.body.contract.forEach((item) => {
contract = new Contract({
customerID: cid,
startDate: item.startDate,
endDate: item.endDate,
conditions: item.conditions,
price: item.price,
author: item.author,
version: nextVersion
});
savePromises.push(contract.save());
});
return Promise.all(savePromises);
})
.then(function(resultOfAllSavePromises){
//rest of code here
}).catch(function(error){
console.log('Error is updating a new customer:' + JSON.stringify(err, undefined, 2));
return res.status(400);
})
});
function getTheMostRecentContract(cid) {
return Contract.findOne({ customerID: cid }).sort({version: 'descending'});
}
As a matter of practice though, have the database control your auto-increment values. This code won't work in a high traffic environment.
I have an action where I need to update MongoDB entry including _id field, which requires deleting old entry and making a new one, here is server side:
exports.update = function(req, res, next){
var outcome = [];
outcome.previousId = req.params.id;
outcome.newId = req.body.name;
var getPreviousRecord = function(callback) {
req.app.db.models.AccountGroup
.findOne({ _id: req.params.id })
.lean()
.exec(function(err, accountGroups) {
if (err) {
return callback(err, null);
}
outcome.accountGroups = accountGroups;
return callback(null, 'done');
});
};
var makeNewRecord = function(callback) {
var permissions = outcome.accountGroups.permissions;
var fieldsToSet = {
_id: outcome.newId.toLowerCase(),
name: outcome.newId,
permissions: permissions
};
req.app.db.models.AccountGroup
.create(fieldsToSet, function(err, record) {
if (err) {
return callback(err, null);
}
outcome.record = record;
return callback(null, 'done');
});
};
var deletePreviousRecord = function() {
req.app.db.models.AccountGroup
.findByIdAndRemove(outcome.previousId)
.exec(function(err) {
if (err) {
return next(err);
}
res.redirect('admin/account-groups/' + outcome.newId + '/');
});
};
var asyncFinally = function(err) {
if (err) {
return next(err);
}
};
require('async').series([getPreviousRecord, makeNewRecord, deletePreviousRecord], asyncFinally);
};
It works fine, but I can't make this work normally on the front-end, it returns me both old route and a new route, for example:
PUT /admin/account-groups/customers22/admin/account-groups/Customers2233/ 404 213.749 ms - 31
where customers22 is old _id and customers2233 is new _id. If I navigate from another page to new entry it gets route normally.
On client side:
(function() {
'use strict';
app = app || {};
app.Details = Backbone.Model.extend({
idAttribute: '_id',
defaults: {
success: false,
errors: [],
errfor: {},
name: ''
},
url: function() {
return '/admin/account-groups/'+ app.mainView.model.id +'/';
},
parse: function(response) {
if (response.accountGroup) {
app.mainView.model.set(response.accountGroup);
delete response.accountGroup;
}
return response;
}
});
app.DetailsView = Backbone.View.extend({
el: '#details',
events: {
'click .btn-update': 'update'
},
template: Handlebars.compile( $('#tmpl-details').html() ),
initialize: function() {
this.model = new app.Details();
this.syncUp();
this.listenTo(app.mainView.model, 'change', this.syncUp);
this.listenTo(this.model, 'sync', this.render);
this.render();
},
syncUp: function() {
this.model.set({
_id: app.mainView.model.id,
name: app.mainView.model.get('name')
});
},
render: function() {
this.$el.html(this.template( this.model.attributes ));
for (var key in this.model.attributes) {
if (this.model.attributes.hasOwnProperty(key)) {
this.$el.find('[name="'+ key +'"]').val(this.model.attributes[key]);
}
}
},
update: function() {
this.model.save({
name: this.$el.find('[name="name"]').val()
});
}
});
app.MainView = Backbone.View.extend({
el: '.page .container',
initialize: function() {
app.mainView = this;
this.model = new app.AccountGroup( JSON.parse( unescape($('#data-record').html()) ) );
// ...
app.detailsView = new app.DetailsView();
}
});
$(document).ready(function() {
app.mainView = new app.MainView();
});
}());
It probably requires to trigger both model.save and model.destroy or prevent URL being used. Any advice on how to do it is appreciated, thank you.
Edit
Just a typo mistake here that is not related to the question, recklessly checking routes, see as cancelled
I believe the problem is here:
res.redirect('admin/account-groups/' + outcome.newId + '/');
That's a relative path so it'll be appended onto the current URL. I suspect you want something like this:
res.redirect('/admin/account-groups/' + outcome.newId + '/');
I have a module which I export and which has a method editHeroImage which I am trying to test using mocha, chai and sinon. The modules has two objects that are passed as arguments, connection and queries. These are mySql objects, one containing the connection to the database and the other the query strings which are defined in their separate modules. The expObj which I am exporting and trying to test is a "helper" module.
I have successfully tested other methods of this module in the same way I am trying to test this method, but, however when I run into methods which use the async module for some reason, my tests no longer behave as expected. I wonder if I am missing something in this particular case, because I have tested other modules and methods which also use async and have not come across this behaviour.
When I run the tests, it logs "HELLO!" as expected but the assertion that the callbackSpy has been called, fails.
I am losing my mind here! Please help! What is going on? Could there be contamination between test suits?
Method under test:
expObj.editHeroImage = function(connection, queries, postId, postData, callback) {
async.waterfall([
function(next) {
var qString = queries.getSinglePostById();
connection.query(qString, [postId], function(err, results) {
if (err) {
return next(err);
}
if (!results.length) {
console.log('NO POST FOUND WITH ID ' + postId);
return callback();
}
next(null, results[0].hero_image);
});
},
function(heroImageId, next) {
if (!heroImageId) {
console.log('HERO IMAGE IS NEW - NEXT TICK!');
return next();
}
// Delete resized images of hero image
var queryStr = queries.deleteResizedImages();
var resizedVals = [heroImageId];
connection.query(queryStr, resizedVals, function(err) {
if (err) {
return callback(err);
}
console.log('DELETED RESIZED IMAGES OF HERO IMAGE ' + heroImageId);
var qString = queries.updateHeroImagePath();
var values = [postData.hero_image, heroImageId];
return connection.query(qString, values, function(err, results) {
if (err) {
return next(err);
}
console.log('UPDATED HERO IMAGE ' + heroImageId + ' WITH PATH ' + postData.hero_image);
next('break');
});
});
},
function addHeroImage(next) {
var qString = queries.insertImage();
var values = [postData.hero_image, postId];
connection.query(qString, values, function(err, results) {
if (err) {
return next(err);
}
next(null, results.insertId);
});
},
function addHeroImagePathToPost(heroImageId, next) {
var qString = queries.saveHeroImageId();
var values = [heroImageId, postId];
connection.query(qString, values, function(err) {
if (err) {
return next(err);
}
next();
});
}
], function(err) {
if (err && err !== 'break') {
return callback(err);
}
console.log('HELLO!');
callback(null);
});
};
Test, with set-up:
'use strict';
var chai = require('chai');
var sinonChai = require("sinon-chai");
var proxyquire = require('proxyquire');
var sinon = require('sinon');
chai.use(sinonChai);
var expect = chai.expect;
describe('HELPERS', function() {
var testedModule,
callbackSpy,
fakeConnectionObj,
fakeQueriesObj,
fakePost,
fakeSnakeCaseObj,
queryStub,
connectionStub,
manageStub,
fakeCamelCaseObj;
beforeEach(function() {
fakePost = {};
fakeConnectionObj = {};
fakeQueriesObj = {
getPostIdFromImage: function() {},
insertResizedImages: function() {},
createPost: function() {},
getPostImages: function() {},
getPostsAlternativesImages: function() {},
getSinglePostById: function() {},
getAllImages: function() {},
insertImage: function() {},
deleteMainImage: function() {},
deleteResizedImages: function() {},
updateHeroImagePath: function() {},
saveHeroImageId: function() {}
};
afterEach(function() {
queryStub.resetBehavior();
});
fakeSnakeCaseObj = {
sub_title: '123',
hero_image: '456'
};
fakeCamelCaseObj = {
subTitle: '123',
heroImage: '456'
};
callbackSpy = sinon.spy();
queryStub = sinon.stub();
manageStub = sinon.stub();
connectionStub = {query: queryStub};
testedModule = proxyquire('./../../../../lib/modules/mySql/workers/helpers', {
'./../../../factories/notification-service': {
select: function() {
return {manageSns: manageStub};
}
}
});
});
it('edits hero image', function() {
var _post = {
id: '123',
title: 'vf',
sub_title: 'vf',
slug: 'vf',
reading_time: 4,
created_at: '123',
published_at: '123',
deleted_on: false,
hero_image: 'hero_image_path'
};
var _postId = '123';
queryStub.onCall(0).callsArgWith(2, null, [{hero_image: '55'}]);
queryStub.onCall(1).callsArgWith(2, null);
queryStub.onCall(2).callsArgWith(2, null);
testedModule.editHeroImage(connectionStub, fakeQueriesObj, _postId, _post, function() {
console.log(arguments); // --> {'0': null} as expected
callbackSpy.apply(null, arguments);
});
expect(callbackSpy).has.been.calledWith(null);
});
});
Your assertion is probably executing before your async function has returned.
There are a number of ways to ensure your async functions have finished executing. The cleanest is to format your mocha test differently.
describe('...', function () {
var callbackSpy;
before(function () {
var _post = {
id: '123',
title: 'vf',
sub_title: 'vf',
slug: 'vf',
reading_time: 4,
created_at: '123',
published_at: '123',
deleted_on: false,
hero_image: 'hero_image_path'
};
var _postId = '123';
queryStub.onCall(0).callsArgWith(2, null, [{
hero_image: '55'
}]);
queryStub.onCall(1).callsArgWith(2, null);
queryStub.onCall(2).callsArgWith(2, null);
return testedModule.editHeroImage(connectionStub, fakeQueriesObj, _postId, _post, function () {
console.log(arguments); // --> {'0': null} as expected
callbackSpy.apply(null, arguments);
});
});
it('edits hero image', function () {
expect(callbackSpy).has.been.calledWith(null);
});
});
Notice that I have wrapped your assertion in a describe block so we can use before. Your actual logic for setting up stubs and executing the class has been moved to the before block and a return added, this ensures the async function is complete before moving on to your assertions.
Your other tests may have passed, but they will also be susceptible to this and it is purely a timing issue.
Indeed #Varedis was right about it being a timing issue. However using your suggestion of wrapping the assertion in a describe bloack and using the before function to set-up the test resulted in my stubs no longer working correctly. However taking your suggestion about timing into account I managed to solve the issue by using the done callback within my test suit. By keeping the set-up I made a slight change and my tests suddenly passed:
it('edits hero image', function(done) {
var _post = {
id: '123',
title: 'vf',
sub_title: 'vf',
slug: 'vf',
reading_time: 4,
created_at: '123',
published_at: '123',
deleted_on: false,
hero_image: 'hero_image_path'
};
var _postId = '123';
queryStub.onCall(0).callsArgWith(2, null, [{hero_image: '55'}]);
queryStub.onCall(1).callsArgWith(2, null);
queryStub.onCall(2).callsArgWith(2, null);
testedModule.editHeroImage(connectionStub, fakeQueriesObj, _postId, _post, function() {
callbackSpy.apply(null, arguments);
expect(callbackSpy).has.been.calledWith(null);
expect(callbackSpy).has.not.been.calledWith('FDgdjghg');
done();
});
});
I am using this module tedious to connect. I am having issues when I try to populate a collection with the data from MSSQL.
My code thus far:
http://pastebin.com/q4ByRCbW
Meteor.startup(function () {
var Request = Meteor.require('tedious').Request;
var Connection = Meteor.require('tedious').Connection;
var config = {
userName: 'xxxxx',
password: 'xxxx',
server: '197.xxx.xxx.xxx',
// If you're on Windows Azure, you will need this:
options: {
encrypt: true,
debug: {
packet: true,
data: true,
payload: true,
token: false,
log: true
}
}
};
var connection = new Connection(config);
var asnycWrapFunc = Async.wrap(connection.execSql);
var rettarr = [];
function executeStatement() {
Fiber(function(){
request = new Request("select * from AccountSummary", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
});
request.on('row', function(columns) {
aaary = []; cnting = 0;
columns.forEach(function(column) {
console.log(column.value);
aaary.push(column.value);
});
if (AccountSummary.find().count() === 0){
AccountSummary.insert({ID:aaary[0], ClientNo:aaary[1], ClientName:aaary[2]});
}
});
//rettarr.push(aaary);
}).run();
asnycWrapFunc(request);
//return rettarr;
}
connection.on('connect', function(err) {
// If no error, then good to go...
var res = executeStatement();
// aaary = res[0];
console.log(res);
errr = err;
});
});
I have found that you have to use Future if you want to you a package like Tedious.
This mini tutorial has the answer
I've tried (with success) to do an http request. I have some REST Api's, like graph.facebook.com as target. The functionality is the same. When i make an HTTP request with node.js as a simple program i can do it.
Really i want to make a little module, and i have this code:
// file: facebook.js
var http = require('http');
var Facebook = (function() {
function Facebook(access_token) {
this.access_token = access_token;
}
Facebook.prototype.getObject = function(id) {
var options;
this.id = id;
options = {
host: 'graph.facebook.com',
port: 80,
path: '/' + id + '?access_token=' + this.access_token
};
return http.request(options, function(response) {
response.on('data', function(d) {
return JSON.parse(d);
});
request.end();
return request.on('error', function(err) {
return {
error: 'An error ocurred.'
};
});
});
};
return Facebook;
})();
module.exports = Facebook;
After, when i write a program i can do this:
var facebook = require('./facebook.js');
var fb = facebook('my_Access_token')
// Here's the problem:
var response = fb.getObject('My_facebook_profile_ID')
I get a response like
{ domain: null,
_events:
{ response: { [Function: g] listener: [Function] },
socket: { [Function: g] listener: [Function] } },
....
When i must have something like
{
"id": "MyIDNumer",
"first_name": "Orlando",
"gender": "male",
"last_name": "Sanchez",
"link": "https://www.facebook.com/MyFacebookName",
"locale": "es_LA",
"name": "Orlando S\u00e1nchez",
"username": "MyUsername"
}
What can i do?
The first thing you should do is rewrite the module to not use the same function name twice ('Facebook').
Secondly, there's no need for the closure, just export the constructor.
Thirdly, you are trying to return a value from an asynchronous callback. This particular issue is common with people coming from the synchronous world.
Assuming you wanted to do a GET request, here's your module refactored after the above and other things are fixed:
// file: facebook.js
var http = require('http');
function Facebook(access_token) {
if (!(this instanceof Facebook))
return new Facebook(access_token);
this.access_token = access_token;
}
Facebook.prototype.getObject = function(id, cb) {
var options;
this.id = id;
options = {
host: 'graph.facebook.com',
port: 80,
path: '/' + id + '?access_token=' + this.access_token
};
http.get(options, function(res) {
var buf = '',
hadError = false;
res.on('data', function(d) {
buf += d;
}).on('error', function(err) {
hadError = true;
cb(err);
}).on('end', function() {
if (hadError)
return;
var val;
try {
val = JSON.parse(buf);
} catch (err) {
return cb(err);
}
cb(null, val);
});
});
};
module.exports = Facebook;
Then use it like so:
var facebook = require('./facebook.js');
var fb = facebook('my_Access_token');
fb.getObject('My_facebook_profile_ID', function(err, response) {
if (err) {
// include better error handling here
return console.log(err);
}
// use response here
console.dir(response);
});