Using asynchronous commands in nightwatch e2e tests - javascript

I have a following e2e scenario written using Nightwatch:
var Q = require('q');
module.exports = {
afterEach: function (browser, done) {
browser.end(function() {
done();
});
},
'should display same data on second page as on first page': function (browser) {
//Given
var firstPage = bowser.pages.first()
//When
Q.all([
firstPage.getTextPromise('#element1'),
firstPage.getTextPromise('#element2'),
firstPage.getTextPromise('#element3')]
).then( function(values) {
users.click('#linkToSecondPage');
//Then
var secondPage = browser.page.secondPage();
secondPage.expect.element('#dataElement1').text.to.equal(values[0]).before(5000);
secondPage.expect.element('#dataElemnet2').contains.text(values[1]);
secondPage.expect.element('#dataElement3').contains.text(values[2]);
});
} }
The getTextPromise command is defined by me in following way:
commands: [{
getTextPromise: function(selector) {
var self = this;
return Q.Promise(function (resolve, reject) {
self.getText(selector, function(result) {resolve(result.value); });
});
} }]
The rationale behind this scenarion is to remember some values on one page before clicking on link to second page
and then checking that on second page the same content is displayed (for example, you click on one item in a table
and go to page displaying details of this particular item).
Unfortunately, I observed that this test sometimes does not check things inside the then callback.
I think this is caused by the test finishing (calling done in afterEach()) before he callback returns.
I thought there was a done() callback passed to the test (much like in nightwatch async unit tests) that I could use but apparently there is not.
Is there a proper way to do this in Nightwatch? Perhaps I am using commands in wrong way?

Nightwatch will keep track of command run order itself if the command runs a method on 'this', and returns 'this'.
Try a command like this, adapted as a page command if you prefer:
exports.command = function() {
var self = this;
var globals = self.globals;
if (!globals.values) { globals.values = []; }
var link = 'some_xpath';
self.getText('selector', function(result) {
if(result.status !== -1){
self.globals.values.push = result.value;
}
});
return self;
};
Because the command returns this. It can be chained and you could be sure the commands run in order without manually writing promises.
example:
var firstPage = bowser.pages.first()
var secondPage = browser.page.secondPage();
firstPage.getTextPromise('#element1')
.getTextPromise('#element2')
.getTextPromise('#element3');
secondPage.expect.element('#dataElement1').text.to.equal(global.values[0]).before(5000)
.expect.element('#dataElemnet2').contains.text(global.values[1])
.expect.element('#dataElement3').contains.text(global.values[2]);
I haven't tested this out so it may need a slight tweak. Hopefully it gives a rough idea of how to chain your commands the nightwatch way. If you run into a problem let me know.

Related

Testing javascript that updates DOM

I am new to web testing and would like to know how can I test javascript that reads and updates the DOM.
function otherFunction(string) {
// do some processing and return processed data
}
function domUpdater() {
var string = document.getElementById("input").value;
document.getElementById("output").innerHTML = otherFunction(string);
}
I can test easily the otherFunction which accepts and input and returns an output.
expect(otherFunction("input1")).to.be("expected1");
expect(otherFunction("input2")).to.be("expected2");
I am not clear on how can I test the domUpdater function which modifies the DOM?
I usually use jsdom, and in my case since I'm using mocha it would look like this (using jsdom-mocha):
var jsdom = require('mocha-jsdom')
var expect = require('chai').expect
describe('mocha tests', function () {
jsdom()
it('has document', function () {
var div = document.createElement('div')
expect(div.nodeName).eql('DIV')
})
})
However if you can afford it (the tests tend to take a bit longer to run) i would recommend looking into running your tests in a headless browser, using something like Puppeteer.
A quick google search yielded this for how to run jest with puppeteer:
First install jest-puppeteer
yarn add --dev jest-puppeteer
Specify preset in your Jest configuration:
{
"preset": "jest-puppeteer"
}
Write your test
describe('Google', () => {
beforeAll(async () => {
await page.goto('https://google.com');
});
it('should be titled "Google"', async () => {
await expect(page.title()).resolves.toMatch('Google');
});
});

Externally determine which test cases fail - Javascript

I am working on an problem for which i need to detect which test cases fail for any javascript/node.js application, when that application's test suite is run. I need to determine this in a programmatic manner.
Mocha testsuite output result
Consider an example of test output above, for this example I would like to write an external javascript script that can tell me which particular test case failed.
Currently the only solution in my mind is; executing npm test in a javascript child process and read its output from the stdout stream, parse the output and extract necessary information, something like this.
const { spawn } = require('child_process');
const chalk = require('chalk');
const child = spawn('npm.cmd',['test']);
line = 0
child.stdout.on('data', (data) => {
console.log(`${chalk.bgBlue('line = ' + line)} , data = ${data}`);
line++;
});
However, this would be a very strict approach. I would like a more generic way of going about it, that can work for a variety of test modules(not just mocha).
Help would be appreciated !
You can get the state for every test after the execution into the code. So you can know if the test has been passed or not.
The code you need is quite simple. Something like this:
afterEach(function () {
const state = this.currentTest.state;
if (state !== "passed") {
//Do whatever you want with this value
}
});
For example, if you want to store into code, which test has been failed, then you can code this:
var testFailed = []
describe('test', function () {
afterEach(function () {
const state = this.currentTest.state;
if (state !== "passed") {
testFailed.push(this.currentTest.title)
}
});
after(function(){
console.log(testFailed)
})
it('test1', () => {
assert.equal(1, 1)
});
it('test2', () => {
assert.equal(1, 2)
});
})
And the output will be:
test
√ test1
1) test2
[ 'test2' ]
1 passing (15ms)
1 failing
Now you can play with this. You can use the variable to do your work, or even you can create a file or whatever you want to store the info.

This code doesn't seem to fire in order?

My problem is that the code does not seem to be running in order, as seen below.
This code is for my discord.js bot that I am creating.
var Discord = require("discord.js");
var bot = new Discord.Client();
var yt = require("C:/Users/username/Documents/Coding/Discord/youtubetest.js");
var youtubetest = new yt();
var fs = require('fs');
var youtubedl = require('youtube-dl');
var prefix = "!";
var vidid;
var commands = {
play: {
name: "!play ",
fnc: "Gets a Youtube video matching given tags.",
process: function(msg, query) {
youtubetest.respond(query, msg);
var vidid = youtubetest.vidid;
console.log(typeof(vidid) + " + " + vidid);
console.log("3");
}
}
};
bot.on('ready', () => {
console.log('I am ready!');
});
bot.on("message", msg => {
if(!msg.content.startsWith(prefix) || msg.author.bot || (msg.author.id === bot.user.id)) return;
var cmdraw = msg.content.split(" ")[0].substring(1).toLowerCase();
var query = msg.content.split("!")[1];
var cmd = commands[cmdraw];
if (cmd) {
var res = cmd.process(msg, query, bot);
if (res) {
msg.channel.sendMessage(res);
}
} else {
let msgs = [];
msgs.push(msg.content + " is not a valid command.");
msgs.push(" ");
msgs.push("Available commands:");
msgs.push(" ");
msg.channel.sendMessage(msgs);
msg.channel.sendMessage(commands.help.process(msg));
}
});
bot.on('error', e => { console.error(e); });
bot.login("mytoken");
The youtubetest.js file:
var youtube_node = require('youtube-node');
var ConfigFile = require("C:/Users/username/Documents/Coding/Discord/json_config.json");
var mybot = require("C:/Users/username/Documents/Coding/Discord/mybot.js");
function myyt () {
this.youtube = new youtube_node();
this.youtube.setKey(ConfigFile.youtube_api_key);
this.vidid = "";
}
myyt.prototype.respond = function(query, msg) {
this.youtube.search(query, 1, function(error, result) {
if (error) {
msg.channel.sendMessage("There was an error finding requested video.");
} else {
vidid = 'http://www.youtube.com/watch?v=' + result.items[0].id.videoId;
myyt.vidid = vidid;
console.log("1");
}
});
console.log("2");
};
module.exports = myyt;
As the code shows, i have an object for the commands that the bot will be able to process, and I have a function to run said commands when a message is received.
Throughout the code you can see that I have put three console.logs with 1, 2 and 3 showing in which order I expect the parts of the code to run. When the code is run and a query is found the output is this:
I am ready!
string +
2
3
1
This shows that the code is running in the wrong order that I expect it to.
All help is very highly appreciated :)
*Update! Thank you all very much to understand why it isn't working. I found a solution where in the main file at vidid = youtubetest.respond(query, msg) when it does that the variable is not assigned until the function is done so it goes onto the rest of my code without the variable. To fix I simply put an if statement checking if the variable if undefined and waiting until it is defined.*
Like is mentioned before, a lot of stuff in javascript runs in async, hence the callback handlers. The reason it runs in async, is to avoid the rest of your code being "blocked" by remote calls. To avoid ending up in callback hell, most of us Javascript developers are moving more and more over to Promises. So your code could then look more like this:
myyt.prototype.respond = function(query, msg) {
return new Promise(function(resolve, reject) {
this.youtube.search(query, 1, function(error, result) {
if (error) {
reject("There was an error finding requested video."); // passed down to the ".catch" statement below
} else {
vidid = 'http://www.youtube.com/watch?v=' + result.items[0].id.videoId;
myyt.vidid = vidid;
console.log("1");
resolve(2); // Resolve marks the promises as successfully completed, and passes along to the ".then" method
}
});
}).then(function(two) {
// video is now the same as myyt.vidid as above.
console.log(two);
}).catch(function(err) {
// err contains the error object from above
msg.channel.sendMessage(err);
})
};
This would naturally require a change in anything that uses this process, but creating your own prototypes seems.. odd.
This promise returns the vidid, so you'd then set vidid = youtubetest.response(query, msg);, and whenever that function gets called, you do:
vidid.then(function(id) {
// id is now the vidid.
});
Javascript runs async by design, and trying to hack your way around that leads you to dark places fast. As far as I can tell, you're also targetting nodeJS, which means that once you start running something synchronously, you'll kill off performance for other users, as everyone has to wait for that sync call to finish.
Some suggested reading:
http://callbackhell.com/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://stackoverflow.com/a/11233849/3646975
I'd also suggest looking up ES6 syntax, as it shortens your code and makes life a hellofalot easier (native promises were only introduced in ES6, which NodeJS 4 and above supports (more or less))
In javascript, please remember that any callback function you pass to some other function is called asynchronously. I.e. the calls to callback function may not happen "in order". "In order" in this case means the order they appear on the source file.
The callback function is simply called on certain event:
When there is data to be processed
on error
in your case for example when the youtube search results are ready,
'ready' event is received or 'message' is received.
etc.

Protractor Cucumber BDD Tests Show Pass before Execution

I have a sample BDD test using Protractor with Cucumber. On executing the code, the console immediately shows the result as passed and the code actually begins executing only after that.
I wish execution status display to be in sync with actual execution.(e.g Console displays - 'Given I launch the protractor demo page' and the code underneath is executed, then console displays next step and so on) I know it has got something to do with Async coding and callbacks, not able to figure out the exact problem though.
Feature file:
Feature: Test
Scenario: Test Scenario
Given I launch the protractor demo page
When I enter two in the first field
And I enter three in the second field
And I click Go button
Then Result should be displayed as Five
Step File:
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;
module.exports = function () {
this.Given(/^I launch the protractor demo page$/, function (callback) {
browser.driver.manage().window().maximize();
browser.get('http://juliemr.github.io/protractor-demo/');
browser.getTitle().then(function(text){
console.log('title is - ' + text);
expect(text).to.equal('Super Calculator');
});
callback();
});
this.When(/^I enter two in the first field$/, function (callback) {
element(by.model('first')).sendKeys('2');
callback();
});
this.When(/^I enter three in the second field$/, function (callback) {
element(by.model('second')).sendKeys('3');
callback();
});
this.When(/^I click Go button$/, function (callback) {
element(by.id('gobutton')).click();
callback();
});
this.Then(/^Result should be displayed as Five$/, function (callback) {
element(by.repeater('result in memory')).all(by.tagName('td')).get(2).getText().then(function(text){
expect(text).to.equal('5');
});
callback();
});
};
You need to either return a promise or use the done callback in your step definitions. Otherwise cucumber doesn't know when your asynchronous
actions are complete.
I had the same question and above statement was the response from one of the core members of the protractor-cucumber github forum.
I prefer to return promises when I am performing some actions on the results with .then function and use .done callback function when I am not, Also you don't need callbacks now CucumberJS supports promises. So your step file should look like -
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;
module.exports = function () {
this.Given(/^I launch the protractor demo page$/, function () {
browser.driver.manage().window().maximize();
browser.get('http://juliemr.github.io/protractor-demo/');
return browser.getTitle().then(function(text){
console.log('title is - ' + text);
expect(text).to.equal('Super Calculator');
});
});
this.When(/^I enter two in the first field$/, function () {
return element(by.model('first')).sendKeys('2');
});
this.When(/^I enter three in the second field$/, function () {
return element(by.model('second')).sendKeys('3'); // you can use return also
});
this.When(/^I click Go button$/, function () {
return element(by.id('gobutton')).click();
});
this.Then(/^Result should be displayed as Five$/, function () {
return element(by.repeater('result in memory')).all(by.tagName('td')).get(2).getText().then(function(text){
expect(text).to.equal('5');
});
});
};
I would recommend you to read about Promises http://www.html5rocks.com/en/tutorials/es6/promises/ as it requires some understanding how they behave.They can be sometimes tricky, it took me a while to get an idea still I have lot to learn :)

node-mysql timing

i have a recursive query like this (note: this is just an example):
var user = function(data)
{
this.minions = [];
this.loadMinions = function()
{
_user = this;
database.query('select * from users where owner='+data.id,function(err,result,fields)
{
for(var m in result)
{
_user.minions[result[m].id] = new user(result[m]);
_user.minions[result[m].id].loadMinions();
}
}
console.log("loaded all minions");
}
}
currentUser = new user(ID);
for (var m in currentUser.minions)
{
console.log("minion found!");
}
this don't work because the timmings are all wrong, the code don't wait for the query.
i've tried to do this:
var MyQuery = function(QueryString){
var Data;
var Done = false;
database.query(QueryString, function(err, result, fields) {
Data = result;
Done = true;
});
while(Done != true){};
return Data;
}
var user = function(data)
{
this.minions = [];
this.loadMinions = function()
{
_user = this;
result= MyQuery('select * from users where owner='+data.id);
for(var m in result)
{
_user.minions[result[m].id] = new user(result[m]);
_user.minions[result[m].id].loadMinions();
}
console.log("loaded all minions");
}
}
currentUser = new user(ID);
for (var m in currentUser.minions)
{
console.log("minion found!");
}
but he just freezes on the while, am i missing something?
The first hurdle to solving your problem is understanding that I/O in Node.js is asynchronous. Once you know how this applies to your problem the recursive part will be much easier (especially if you use a flow control library like Async or Step).
Here is an example that does some of what you're trying to do (minus the recursion). Personally, I would avoid recursively loading a possibly unknown number/depth of records like that; Instead load them on demand, like in this example:
var User = function(data) {
this.data = data
this.minions;
};
User.prototype.getMinions = function(primaryCallback) {
var that = this; // scope handle
if(this.minions) { // bypass the db query if results cached
return primaryCallback(null, this.minions);
}
// Callback invoked by database.query when it has the records
var aCallback = function(error, results, fields) {
if(error) {
return primaryCallback(error);
}
// This is where you would put your recursive minion initialization
// The problem you are going to have is callback counting, using a library
// like async or step would make this party much much easier
that.minions = results; // bypass the db query after this
primaryCallback(null, results);
}
database.query('SELECT * FROM users WHERE owner = ' + data.id, aCallback);
};
var user = new User(someData);
user.getMinions(function(error, minions) {
if(error) {
throw error;
}
// Inside the function invoked by primaryCallback(...)
minions.forEach(function(minion) {
console.log('found this minion:', minion);
});
});
The biggest thing to note in this example are the callbacks. The database.query(...) is asynchronous and you don't want to tie up the event loop waiting for it to finish. This is solved by providing a callback, aCallback, to the query, which is executed when the results are ready. Once that callback fires and after you perform whatever processing you want to do on the records you can fire the primaryCallback with the final results.
Each Node.js process is single-threaded, so the line
while(Done != true){};
takes over the thread, and the callback that would have set Done to true never gets run because the thead is blocked on an infinite loop.
You need to refactor your program so that code that depends on the results of the query is included within the callback itself. For example, make MyQuery take a callback argument:
MyQuery = function(QueryString, callback){
Then call the callback at the end of your database.query callback -- or even supply it as the database.query callback.
The freezing is unfortunately correct behaviour, as Node is single-threaded.
You need a scheduler package to fix this. Personally, I have been using Fibers-promise for this kind of issue. You might want to look at this or another promise library or at async

Categories

Resources