For some reason, I can't seem to get vows.js sub-topics working in my real test-suite, but they work fine in a example file... can you spot my problem?
This works:
vows.describe('An Education in Vows').addBatch({
'when I write an asynchronous topic': {
topic: function() {
var that = this;
setTimeout(function() {
that.callback(true);
}, 100);
},
'it passes': function (topic) {
assert.equal(topic, true);
},
'and it has an asynchronous sub-topic': {
topic: function() {
var that = this;
setTimeout(function() {
that.callback(true);
}, 100);
},
'it also passes': function (topic) {
assert.equal(topic, true);
}
}
}
}).run();
When I run this via:
node tests/learning-vows.js
I get:
·
·
✓ OK » 2 honored (0.207s)
This Doesn't work:
I have a file ./tests/smoke.js
vows.describe('Registration & Authentication').addBatch({
'when a user registers with a valid username and password': {
topic: function () {
client.register({
username: validusername,
password: validpassword
}, this.callback);
},
'we return status 200 OK': function (data, response) {
assert.equal(200, response.statusCode);
},
'simple sub-topic': {
topic: true,
'should work': function(topic) {
assert.equal(true, topic);
}
},
}
}).run()
When I execute this via:
node tests/smoke.js
I get:
·
✗ Errored » 1 honored ∙ 1 errored
Note that in the second example, without the sub topic I get:
·
✓ OK » 1 honored (0.100s)
Vows is using node's convention for callbacks (see : http://nodemanual.org/latest/nodejs_dev_guide/working_with_callbacks.html), it assumes that first parameter on callback is an error object.
So when you send data as first parameter your telling vows that an error happened in client.register. It prevents vows from evaluating sub-topic. Sub-topic is marked errored but assertion succeeded and current topic is marked as honored.
It is really not trivial to guess that from output. Moreover vows behavior is not consistent, try replacing true to 0 and then '0' as callback parameter in your first test and you will see two other results.
Here is a working exemple :
var vows = require('vows'), assert = require('assert');
var client = {
register: function(obj,callback){
callback(null, obj, {statusCode:200});
}
};
vows.describe('Registration & Authentication').addBatch({
'when a user registers with a valid username and password': {
topic: function () {
client.register({
username: 'validusername',
password: 'validpassword'
}, this.callback);
},
'we return status 200 OK': function (err, data, response) {
assert.equal(response.statusCode, 200);
},
'simple sub-topic': {
topic: true,
'should work': function(topic) {
assert.equal(true, topic);
}
}
}
}).export(module)
Related
There is a nice example how Rollup function could be called via MS CRM WebApi here.
But it covers general access to CRM WebApi. Although in most recent versions new JS namespace Xrm.WebApi was introduced. Which provides more straightforward way to access that endpoint.
Method Xrm.WebApi.execute should be able to execute Rollup request, as it is able to execute WhoAmI. But I'm struggling to figure out correct values of parameters to make this execution happen.
Here is my code:
var RollupRequest = function(entityType, id, query) {
this.Target = { entityType: entityType, id: id };
this.RollupType = "Related";
this.Query = {
Query: query
};
};
RollupRequest.prototype.getMetadata = function() {
return {
boundParameter: null,
parameterTypes: {
Target: {
typeName: "Microsoft.Xrm.Sdk.EntityReference",
structuralProperty: 5
},
RollupType: {
typeName: "Microsoft.Dynamics.CRM.RollupType",
structuralProperty: 3
},
Query: {
typeName: "Microsoft.Xrm.Sdk.Query.FetchExpression",
structuralProperty: 5
}
},
operationType: 1, // This is a function. Use '0' for actions and '2' for CRUD
operationName: "Rollup"
};
};
var request = new RollupRequest(
"contact",
"0473FD41-C744-E911-A822-000D3A2AA2C5",
"<fetch><entity name='activitypointer'></entity></fetch>"
);
Xrm.WebApi.execute(request).then(
function(data) {
console.log("Success: ", data);
},
function(error) {
console.log("Failure: ", error);
}
);
The code generates following URL:
/api/data/v9.0/Rollup(Target=#Target,RollupType=#RollupType,Query=#Query)?#Target={"#odata.id":"contacts(0473FD41-C744-E911-A822-000D3A2AA2C5)"}&#RollupType=&#Query={"Query":"<fetch><entity name='activitypointer'></entity></fetch>"}
and the error: "Expression expected at position 0 in ''."
Which, seems to be, indicates that RollupType was not set correctly, because indeed in URL RollupType is missing.
I assume there are more than one potential error, because I'm using FetchXML as query expression. But meanwhile is it possible indicate what should be changed to generate proper URL at least for RollupType property?
I am new to node and the async way of doing things.
I want to create and run a test suite using nightwatch.js, I have read all the docs and I'm baffled at how to do what I want (been working on it for 3 days already).
Am I thinking about this the wrong way?
module.exports = {
before: function(browser) {
/*
Here I just want to make a web call to an api and get a result and then
store that result in a variable which we will use later in test1 and other test cases
*/
browser.globals.myVariable = resultofsomeapicalll;
//wait here until proceeding
},
after: function(browser) {
browser.end();
},
beforeEach: function(browser) {
},
afterEach: function() {
},
'test1': function(browser) {
browser.url(browser.launchUrl + browser.globals.myVariable, function(result) {
browser.waitForElementPresent('body', 1000);
browser.expect.element("#something").to.be.present;
browser.saveScreenshot('./screenshots/' + browser.currentTest.module + '/' + browser.currentTest.name + '.png');
});
},
};
To perform asynchronous task in the Nightwatch.JS before[Each] or after[Each] hooks, you need to pass an callback argument to the function, which you trigger once the job is done.
In below example, it would be an API Call using Axios library;
module.exports = {
before: function(browser, done) {
axios.get('https://example.com/api?ID=12345')
.then(function (response) {
browser.globals.myVariable = response;
done();
})
.catch(function (error) {
done(error);
});
},
after: function(browser) {
browser.end();
},
beforeEach: function(browser) {
},
afterEach: function() {
},
'test1': function(browser) {
console.log()
},
};
Controlling the done invocation timeout
By default the done invocation timeout is set to 10 seconds (2 seconds
for unit tests). In some cases this might not be sufficient and to
avoid a timeout error, you can increase this timeout by defining an
asyncHookTimeout property (in milliseconds) in your external globals
file (see below for details on external globals).
http://nightwatchjs.org/guide/#asynchronous-before-each-and-after-each-
Best regards,
Riku
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 8 years ago.
BACKGROUND
I have made a small site to run a tipping competition. I am using chartjs to draw garphs. I have made a function (createCjsDonutDataWinnerPickData) to format the data for the graph in to a format suitable for chartjs.
THE PROBLEM
For some reason the function (createCjsDonutDataWinnerPickData) is returning undefined. I think it is due to the "return" returning before the function has finished processing.
I am calling the function in two places. The first is a test before the page is rendered and the second time is when rendering the page.
Extract from page rendering function
Point.findOne({user:req.user.id, competition:comp.id, fixture:fixture.id}).exec(function (err, points){
if (err) {console.log('ERR: fixtures pick page on COMP lookup')}
else {
console.log('TEST FUNCTION');
console.log(createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams)));
res.render('fixturePick.ejs', {
user : req.user, // get the user out of session and pass to template
fixture: fixture,
pick: pick,
teams: createIdLookup(teams),
draw: draw,
round: round,
competition: comp,
points: points,
gWinnerPickGraph: JSON.stringify( createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams)) ),
successMsg: req.flash('successMsg'),
dangerMsg: req.flash('dangerMsg'),
warningMsg: req.flash('warningMsg')
});
}
});
The function createCjsDonutDataWinnerPickData
function createCjsDonutDataWinnerPickData(fixtureID,competitionID,teamLookup){
var Statistic = require('../app/models/statistic');
var Fixture = require('../app/models/fixture');
var async = require('async');
try {
async.waterfall([
function(cb_ReturnData){
var chartData =[];
Statistic.findOne({fixture:fixtureID, competition:competitionID,type:'winnerPickNumber'}).populate('fixture').exec(function (err,statData){
if (err) {console.log('ERROR in preparing data for graph');throw (err)}
else {
async.each(statData.data, function(dataPoint,cb_PrepData){
var sliceData = {};
if (statData.fixture.homeTeam._id == dataPoint.teamID){
//console.log('Found data for home team')
sliceData = {value: dataPoint.number, color:"rgba(151,187,205,0.5)", highlight: "rgba(151,187,205,0.75)", label:teamLookup[dataPoint.teamID].name };
}
else
{
//console.log('Found data for away team')
sliceData = {value: dataPoint.number, color:"rgba(220,220,220,0.5)", highlight: "rgba(220,220,220,0.75)", label:teamLookup[dataPoint.teamID].name };
}
//console.log('Pusihgin slice data to data')
chartData.push(sliceData);
cb_PrepData();
}, function(err){
if (err) {
console.log('ERROR in creating data for WinnerPickGraph');
cb_ReturnData(err);
}
else {
//console.log('CHART DATA IN INNER ASYNC');
//console.log(chartData);
cb_ReturnData(null, chartData);
}
});
}
});
}
],function(err,chartData){
if (err) {console.log('ERROR in preparing return data')}
else {
console.log('HERE IS THE FINAL RETURN');
console.log(chartData);
return (chartData);
}
});
}
catch (err){
//probably end up here because there is no points History Data
console.log('threw erro returning undefined');
return undefined;
}
}
Console output
GET /fixturePick?competition=542a5ffa736e3e35532f2d24&fixture=542c9ae12367c9209a739150 302 5.532 ms - 58
PATH: /fixturePick?competition=542a5ffa736e3e35532f2d24&fixture=542c9ae12367c9209a739150
GET / 200 11.291 ms - 1515
GET /images/grassBackground.jpg 200 5.533 ms - 145369
POST /login 302 459.431 ms - 228
TEST FUNCTION
undefined
GET /fixturePick?competition=542a5ffa736e3e35532f2d24&fixture=542c9ae12367c9209a739150 200 1609.303 ms - 4984
GET /images/team/logo/sm/53fc6399b918a6b661d423b4.png 200 2.720 ms - 15747
HERE IS THE FINAL RETURN
[ { value: 1,
color: 'rgba(220,220,220,0.5)',
highlight: 'rgba(220,220,220,0.75)',
label: 'Melbourne Victory' },
{ value: 2,
color: 'rgba(220,220,220,0.5)',
highlight: 'rgba(220,220,220,0.75)',
label: 'Western Sydney Wanderers' } ]
HERE IS THE FINAL RETURN
[ { value: 1,
color: 'rgba(220,220,220,0.5)',
highlight: 'rgba(220,220,220,0.75)',
label: 'Melbourne Victory' },
{ value: 2,
color: 'rgba(220,220,220,0.5)',
highlight: 'rgba(220,220,220,0.75)',
label: 'Western Sydney Wanderers' } ]
GET /images/team/logo/sm/53fc6399b918a6b661d423b5.png 200 2.368 ms - 13144
GET /images/none.png 200 1.857 ms - 195
I may have overcomplicated this with async.foreach and async.waterfall but I was running out of ideas as to why the function keeps returning undefined.
createCjsDonutDataWinnerPickData will return undefined always because it's asynchronous. To get a handle on results, you should either pass it a callback or return a promise.
Here are some examples of the approaches:
// callback
createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams), function(err, data) {
// do something with err and data now
});
// promise (es6-promise)
createCjsDonutDataWinnerPickData(fixture._id,comp._id,createIdLookup(teams)).then(function(data) {
// got some data, do something
}).catch(function(err) {
// deal with the error
});
Latter approach is possible with es6-promise. bluebird works well too.
I was attempting to teach myself to use a Testing framework for automating tests instead of having to do them by hand. After a bit of trial and error, I finally got the unit tests to start passing ... but now, my problem is everything is passing regardless of if it should or not.
Currently I have the following code:
describe('create {authName, authPW}', function() {
it('no name', function() {
init({
path: ':memory:',
callback: function() {
var arg;
arg = {};
//arg['authName'] = 'Name';
arg['authPW'] = 'Pass';
arg['callback'] = function(r) {
// r.should.equal('create error');
r.should.equal('foobar');
done();
};
create(arg);
}
});
});
});
as you can guess ... r should NOT equal 'foobar'
What am I doing wrong here?
When creating async tests with mocha you need to let him know when it is done
describe('an asynch piece of code', function() {
var foo = new bar();
it('should call the callback with a result', function( done ) {
foo.doAsynchStuff( function( result ) {
result.should.be.ok;
done();
});
});
});
If done is present as an argument on the it then mocha will wait for the done to be called. It has a timeout of 2 seconds, that if exceeded fails the test. You can increase this timeout:
it('should resolve in less than 10 seconds', function( done ) {
this.timeout( 10000 );
foo.doAsynchStuff( function( result ) {
result.should.be.ok;
done();
});
}
it('no name', function(done) {
done has to be an argument of the function passed to it()
I am trying to combine the examples here, here to write a vows test for my node.js / express app that:
Creates a new user object
Checks the response was sane
Uses the returned _id to test looking up the newly created user
Again uses the _id to test updating the user
Item 1 and 2 work fine, but there is something wrong with my sub-context 'GET /users/:id'. It errors and I cannot figure out why. Tried Googling and using the debugger, but I still can't see what it is, I am probably just overlooking something obvious.
···✗ Errored » 3 honored ∙ 1 errored
Can anyone tell me why the 4th vow errors?
Here's my vows code:
var vows = require('vows')
, assert = require('assert')
, tobi = require('tobi')
var suite = vows.describe('Users API')
, now = new Date().getTime()
, newUser = { name: now + '_test_user', email: now + '#test.com' }
, browser = tobi.createBrowser(3000, 'localhost')
, defaultHeaders = { 'Content-Type': 'application/json' }
function assertStatus(code) {
return function (res, $) {
res.should.have.status(code)
}
}
var client = {
get: function(path) {
return function() {
browser.get(path, { headers: defaultHeaders }, this.callback)
}
},
post: function(path, data) {
return function() {
browser.post(path, { body: JSON.stringify(data), headers: defaultHeaders }, this.callback)
}
}
}
suite.addBatch({
'GET /users': {
topic: client.get('/users'),
'should respond with a 200 ok': assertStatus(200)
},
'POST /users': {
topic: client.post('/users', newUser),
'should respond with a 200 ok': assertStatus(200),
'should return the new user': function(res, $){
assert.isNotNull(res.body._id)
assert.isNotNull(res.body.created_at)
assert.isTrue(res.body._id.length > 0)
assert.equal(newUser.name, res.body.name)
assert.equal(newUser.email, res.body.email)
},
'GET /users/:id': { // Sub-context of POST /users
topic: function(res) { return client.get('/users/' + res.body._id) },
'should respond with a 200 ok': assertStatus(200)
}
}
})
suite.export(module)
EDIT
I tried simplifying the code as follows to help see if this.callback was the problem, but the error is still there:
'GET /users/:id': { // Sub-context of POST /users
topic: function(res) {
console.log('About to request /users/' + res.body._id)
browser.get('/users/' + res.body._id, { headers: defaultHeaders }, this.callback)
},
'should respond with a 200 ok': assertStatus(200)
}
How are you populating res for the fourth tes?? It wouldn't be visible outside the line
'should return the new user'
Try creating the id variable outside the addBatch call, and set it in the third test. then call
client.get('/users/' + id)
EDIT:
Better yet, put it back into newUser in the third test:
'should return the new user': function(res, $){
newUser.id = res.body._id
....
and then do:
client.get('/users/' + newUser.id)