Protractor Cucumber BDD Tests Show Pass before Execution - javascript

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 :)

Related

All steps shown as pass before protractor cucumber execution

While executing my scripts, Immediately all steps shows as pass in console after that my actual scripts getting executed. Even after returning promise in each step.
Feature File:
Feature: Running Cucumber with Protractor
Scenario: To verify the Search result
Given I am on home page
When I enter search value
Then I verify the search page
Step Definition:
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
var expect = chai.expect;
chai.use(chaiAsPromised);
module.exports = function() {
this.Given(/^I am on home page$/, function () {
browser.get(browser.baseUrl);
return browser.driver.getTitle().then(function(pageTitle) {
expect(pageTitle).equal('Online Shopping Site for Mobiles, Fashion, Books, Electronics, Home Appliances and More');
});
});
this.When(/^I enter search value$/, function () {
return element(by.name('q')).sendKeys('iPhone 4s');
});
this.Then(/^I verify the search page$/, function () {
browser.sleep(1000);
return expect(element(by.className('KG9X1FUs7BSJ3tl0huXbH')).isPresent()).to.eventually.equal(true);
});
}
And the Output shows as:
There are marked as green because you don't resolve the promise.You've choosen in your setup not to return a callback but to return a promise. This means that each last line of code should return a promise.
If you transform your code into promises you will get this
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
var expect = chai.expect;
chai.use(chaiAsPromised);
module.exports = function() {
this.Given(/^I am on home page$/, function() {
browser.get(browser.baseUrl);
return expect(browser.driver.getTitle())
.to.eventually.equal('Online Shopping Site for Mobiles, Fashion, Books, Electronics, Home Appliances and More');
});
this.When(/^I enter search value$/, function() {
return element(by.name('q')).sendKeys('iPhone 4s');
});
this.Then(/^I verify the search page$/, function() {
browser.sleep(1000);
return expect(element(by.className('KG9X1FUs7BSJ3tl0huXbH')).isPresent()).to.eventually.equal(true);
});
}

Can't run cucumber tests with protractor

Every time when I am running tests, I got the error: TypeError: e.getContext is not a function
I am using examples from https://github.com/cucumber/cucumber-js with some changes in world.js (made them to fix timeout errors)
Versions of apps:
node 4.2.6
cucumber 0.9.4
protractor 3.0.0
protractor-cucumber-framework 0.3.3
zombie 4.2.1
My world.js:
// features/support/world.js
var zombie = require('zombie');
zombie.waitDuration = '30s';
function World() {
this.browser = new zombie(); // this.browser will be available in step definitions
this.visit = function (url, callback) {
this.browser.visit(url, callback);
};
}
module.exports = function() {
this.World = World;
this.setDefaultTimeout(60 * 1000);
};
My sampleSteps.js:
// features/step_definitions/my_step_definitions.js
module.exports = function () {
this.Given(/^I am on the Cucumber.js GitHub repository$/, function (callback) {
// Express the regexp above with the code you wish you had.
// `this` is set to a World instance.
// i.e. you may use this.browser to execute the step:
this.visit('https://github.com/cucumber/cucumber-js', callback);
// The callback is passed to visit() so that when the job's finished, the next step can
// be executed by Cucumber.
});
this.When(/^I go to the README file$/, function (callback) {
// Express the regexp above with the code you wish you had. Call callback() at the end
// of the step, or callback.pending() if the step is not yet implemented:
callback.pending();
});
this.Then(/^I should see "(.*)" as the page title$/, function (title, callback) {
// matching groups are passed as parameters to the step definition
var pageTitle = this.browser.text('title');
if (title === pageTitle) {
callback();
} else {
callback(new Error("Expected to be on page with title " + title));
}
});
};
My sample.feature:
# features/my_feature.feature
Feature: Example feature
As a user of Cucumber.js
I want to have documentation on Cucumber
So that I can concentrate on building awesome applications
Scenario: Reading documentation
Given I am on the Cucumber.js GitHub repository
When I go to the README file
Then I should see "Usage" as the page title
My protractor-conf.js:
exports.config = {
specs: [
'features/**/*.feature'
],
capabilities: {
'browserName': 'chrome'
},
baseUrl: 'http://127.0.0.1:8000/',
framework: 'custom',
frameworkPath: require.resolve('protractor-cucumber-framework'),
// relevant cucumber command line options
cucumberOpts: {
require: ['features/support/world.js', 'features/sampleSteps.js'],
format: "summary"
}
};
I had same issue with this example. The problem is with github.com page. What the problem, I don't know.
So, I made changes for the page which to visit and tests start run without TypeError: e.getContext is not a function.
I changed sampleSteps.js file:
module.exports = function () {
this.Given(/^I am on the Google.com$/, function (callback) {
// Express the regexp above with the code you wish you had.
// `this` is set to a World instance.
// i.e. you may use this.browser to execute the step:
this.visit('http://www.google.com/', callback);
// The callback is passed to visit() so that when the job's finished, the next step can
// be executed by Cucumber.
});
this.When(/^I go to the SEARCH page$/, function (callback) {
// Express the regexp above with the code you wish you had. Call callback() at the end
// of the step, or callback.pending() if the step is not yet implemented:
// changed to this one, otherwise next steps also are skipped...
callback();
});
this.Then(/^I should see "(.*)" as the page title$/, function (title, callback) {
// matching groups are passed as parameters to the step definition
this.browser.assert.text('title', title);
callback();
});
};
Then some changes for sample.feature:
Feature: Example feature
As a user of Google.com
I want to have search with Google
So that I can find something
Scenario: Search something
Given I am on the Google.com
When I go to the SEARCH page
Then I should see "Google" as the page title
I hope, this will help with first steps working with cucumber-js and zombie.js.
It's not the problem with protractor, because same problem is, when I run this sample in the WebStorm.

Using asynchronous commands in nightwatch e2e tests

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.

How to call Q promise notify within the promise chain

I need helps on notify() within the promise chain.
I have 3 promise base functions connect(), send(cmd), disconnect(). Now I would like to write another function to wrap those call in following manner with progress notification.
function bombard() {
return connect()
.then(function () {
var cmds = [/*many commands in string*/];
var promises = _.map(cmds, function (cmd) {
var deferred = Q.defer();
deferred.notify(cmd);
send(cmd).then(function (result) {
deferred.resovle(result);
});
return deferred.promise;
});
return Q.all(promises);
})
.finally(function () { return disconnect() })
}
Run the function like that
bombard.then(onResolve, onReject, function (obj) {
console.log(ob);
});
I supposed I will get notification for every command I have sent. However, it does not work as I expected. I get nothing actually.
Although I believe this is due to those notifications havn't propagated to outside promise, I have no idea on how to propagated those notifications on Q or wrapping that promise chain: connect, send, disconnect in a one deferred object.
Thanks
I have some good news and some bad news.
Very good! You have found out the problem with the notifications API and why it is being removed in Q in the v2 branch, being deprecated in newer libraries like Bluebird, and never included in ECMAScript 6. It really boils down to the fact promises are not event emitters.
The notifications API does not compose or aggregate very well. In fact, notifications being on promises does not make too much sense imo to begin with,.
Instead, I suggest using a progress notification even, kind of like IProgress in C#. I'm going to simulate all the actions with Q.delay() for isolation, your code will obviously make real calls
function connect(iProgress){
return Q.delay(1000).then(function(res){
iProgress(0.5,"Connecting to Database");
}).delay(1000).then(function(res){
iProgress(0.5,"Done Connecting");
});
}
function send(data,iProgress){
return Q.delay(200*Math.random() + 200).then(function(res){
iProgress(0.33, "Sent First Element");
}).delay(200*Math.random() + 400).then(function(){
iProgress(0.33, "Sent second Element");
}).delay(200*Math.random() + 500).then(function(){
iProgress(0.33, "Done sending!");
});
}
// disconnect is similar
Now, we can easily decide how our promises compose, for example:
function aggregateProgress(num){
var total = 0;
return function(progression,message){
total += progression;
console.log("Progressed ", ((total/num)*100).toFixed(2)+"%" );
console.log("Got message",message);
}
}
Which would let you do:
// bombard can accept iProgress itself if it needs to propagate it
function bombard() {
var notify = aggregateProgress(cmds.length+1);
return connect(notify)
.then(function () {
var cmds = [/*many commands in string*/];
return Q.all(cmds.map(function(command){ return send(command,notify); }));
});
}
Here is a complete and working fiddle to play with

Testing ajax requests using jasmine returns TypeError

The description of the task. I want to test the code that loads a list of resources using $.get.
So, the source code:
fetchTemplates: function(list, cb){
var promises = [],
$container = $('#templates');
Object.keys(list).forEach(function(tplSelector){
if($(tplSelector).length > 0){ return; }
var promise = $.get(list[tplSelector]);
promise
.done(function(tplHtml){
$container.append(tplHtml);
})
.fail(function(){
console.warn('Template "' + tplSelector + " not found by url:" + list[tplSelector]);
});
promises.push( promise );
});
return $.when.apply($,promises).done(cb);
}
The test suite:
it("Correct template fetching", function (done) {
var fetchResult = viewManager.fetchTemplates({
'#helpTpl': 'somecorrectaddress'
});
fetchResult.done(function () {
expect(true).toBeTruthy();
done();
});
fetchResult.fail(function () {
expect(false).toBeTruthy();
done();
});
});
What it generates. Test passes, but generates an error:
TypeError: 'null' is not an object (evaluating 'this.results_.addResult')
at jasmine.js?2348
So, the test case marks as passed. But whole test suite still generates the error above (and this method is the only one async, other parts are trivial to test). My thought was that since the tested method contains async operations and promises - results were not properly handled and thus TypeError. So I added jasmine async "done()" to handle the issue - unfortunately nothing changed. Also worth noting that if I leave only one test in the suite using "iit" - no error is generated. Search didn't find similar cases. Any ideas?
You need to wrap your async calls in 'runs()' with 'waitsFor()' after, read the documentation here. I've never worked with jquery, but try something like the following inside your it function:
var done = false;
var that = this;
runs( function() {
//your async test goes here
that.done = true;
});
waitsFor( function() {
return that.done;
}, "async code failed", 2000);

Categories

Resources