Protractor failure and browser handling on data driven approach - javascript

We are developing data driven protractor framework (jasmine), I need help in handling certain failure scenario.
I will be iterating same test with different data set, my Page module will handle the all verification.
If any it blocks fails, I want to run the certain function to clear cookies, capture session details and re-start the browser (I do have all the functions )
but ,
I am not sure how to get the it block failure and trigger the specific function, also I want to make sure next loop iteration is triggered.
browser.restart() - never worked in data driven in before or after all .....
If am running this data driven in parallel (we can run same test in parallel browser, but we can't distribute each data in to multiple browser), is there any way to distribute?
var dData = requireFile('testData/data.json');
using(dData,async function(data, description) {
describe( scenario 1++ , function() {
it('Load URL' , async function() { })
it('validate Page1' , async function() { xxxxx })
it('validate Page2' , async function() { xxxxx })
it('validate Page3' , async function() { xxxxx })
}) }

If I understood everything right, you have like 3 questions. I'll answer only the first, general one - how to handle results of each it block
It sounds like for what you are trying to implement you should use advantage of custom reporter in jasmine
More precisely, what you want to do is to:
create a module with custom reporter
register it in your config. This would be a good place to think ahead of time if there are any parameters that you want to pass to the reporter
there are different hooks: jasmine-started, suite-started (describe), spec-started (it), suite-done, jasmine-done. Not sure if you all of them, but one particular for sure: spec-done. This should be a function that will be called after each it block. It will be taking spec object as a parameter. You can explore it on your own, but what you'll need from it is status property (spec.status). It's value can be 'passed', 'failed' and I believe others. So your logic will be like
if (spec.status === 'passed') {
// ...
} else if (spec.status === 'failed') {
// ...
} else {
// ...
}

Related

Is there a way to run particular Protractor test depending on the result of the other test?

This kind of question has been asked before but most of this question has pretty complicated background.
The scenario is simple. Let's say we are testing our favorite TODO app.
Test cases are next:
TC00 - 'User should be able to add a TODO item to the TODO list'
TC01 - 'User should be able to rename TODO item'
TC02 - 'User should be able to remove TODO item'
I don't want to run the TC01 and TC02 if TC00 fails (the TODO item is not added so I have nothing to remove or rename)
So I've been researching on this question for the past 3 days and the most common answers fro this question are:
• Your tests should not depend on each other
• Protractor/Jasmine does not have feature to dynamically turn on/off tests ('it' blocks)
There reason why I'm asking this question here is because it looks like a very widespread case and still no clear suggestion to handle this (I mean I could not find any)
My javascript skills are poor but I understand that I need to play around, let's say' passing 'done' or adding the if with the test inside...
it('should add a todo' ()=> {
todoInput.sendKeys('test')
addButton.click();
let item = element(by.cssContainingText('.list-item','test')
expect(item.isPresent()).toBe(true)
}
In my case there are like 15 tests ('it' blocks) after adding the item to the list. And I want to skip SOME OF THE tests if the 'parent' test failed.
PLEASE NOTE:
There is a solution out there which allows to skip ALL remaining test if one fails. This does not suit my needs
Man, I spent good couple of weeks researching this, and yes there was NO clear answers, until I realized how protractor works in details. If you understand this too you'll figure out the best option for you.
SOLUTION IS BELOW AFTER SHORT THEORY
1) If you try to pass async function to describe you see it'll fail, because it only accepts synchronous function
What it means for you, is that whatever condition you want to pass to it block, it can't be Promise based (Promise == resolves somewhen, but not immediately). What you're trying to do essentially IS a Promise (open page, do something and wait to see if the condition satisfies your criteria)
if (conditionIsTrue) { // can't be Promise
it('name', () => {
})
}
Thats first thing to consider...
2) When you run protractor, it picks up spec files specified in config and builds the queue of describe/it AND beforeAll/afterAll blocks. IMPORTANT DETAIL HERE IS THAT IT HAPPENS BEFORE THE BROWSER EVEN STARTED.
Look at this example
let conditionIsTrue; // undefined
it('name', () => {
conditionIsTrue = true;
})
if (conditionIsTrue) { // still undefined
it('name', () => {
})
}
By the time Protractor reaches if() statement, the value of conditionIsTrue is still undefined. And it maybe overwritten inside of it block, when browser starts, later on, but not when it builds the queue. So it skips it.
In other words, protractor knows which describe blocks it'll run before it even opens the browser, and this queue can NOT be modified during execution
POSSIBLE SOLUTION
1.1 Define a global variable outside of describe
let conditionIsTrue; // undefined
describe("describe", () => {
it('name1', async () => {
conditionIsTrue = await element.isPresent(); // NOW IT'S TRUE if element is present
})
it('name2', async () => {
if (conditionIsTrue) {
//do whatever you want if the element is present
} else {
console.log("Skipping 'name2' test")
}
})
})
So you won't skip the it block itself, however you can skip anything inside of it
1.2 The same approach can be used for skipping it blocks across different specs, using environment variable. Example:
spec_1.js
describe(`Suite: 1`, () => {
it("element is present", async () => {
if (await element.isPresent()) {
process.env.FLAG = true
} else {
process.env.FLAG = false
}
});
});
spec_2.js
describe(`Suite: 2`, () => {
it("element is present", async () => {
if (process.env.FLAG) {
// do element specific actions
}
});
});
Another possibility I found out, but never had a chance to check is to use Grunt task runner, which may help you implement the following scenario
Run protractor to execute one spec
Check a desired condition
Export this condition to environment variable
Exit protractor
In your Grunt task implement a conditional logic for executing the rests of conditional specs, by starting protractor again
But honestly, I don't see why you'd want to go this time consuming route, which requires a lot of code... But just as an FYI
There is one way provided by Protractor which might achieve what you want to achieve.
In protractor config file you can have onPrepare function. It is actually a callback function called once protractor is ready and available, and before the specs are executed. If multiple capabilities are being run, this will run once per capability.
Now as i understand you need to do a test or we can say execute a parent function and then based on its output you want to run some tests and do not want to run other tests.
onPrepare function in protractor config file will look like this :
onPrepare: async () => {
await browser.manage().window().maximize();
await browser.driver.get('url')
// continue your parent test steps for adding an item and at the last of function you can assign a global variable say global.itemAdded = true/false based on the result of above test steps. Note that you need to use 'global.' here to make it a global variable which will then be available in all specs
}
Now in you specs file you can run tests (it()) based on global.itemAdded variable value
if(global.itemAdded === true) {
it('This test should be running' () => {
})
}
if(global.itemAdded === false) {
it('This test should not be running' () => {
})
}

How do I access information about the currently running test case from the beforeEach function?

Using Protractor 5.1.2 and Jasmine2 for describing test cases, how does one get the current testcase/spec being run in the beforeEach method?
I would like to do some different setup based on which test case I'm running. I do not want to put these tests in different spec files with repeating code except for the little bit I want to change in the setup.
Example of what I'm looking for:
...
beforeEach(() => {
if(currentSpec/TestCase.name == "thisName") {
// Do a particular login specific to testcase.name
} else {
// Do a default login
}
});
...
My research into this brought up much older solutions (2+ years) that are very out of date and seem to keep saying that accessing the currently running testcase/spec is something they (protractor) try to keep hidden. I feel like wanting to do particular setup for a particular test case in a suite of test cases is not a unique thing. I could just be using the wrong search terms.
I am not sure how to do what you want with beforeEach(). But, I think you can get the same effect by using a helper file. This will allow you to setup a common file that any spec can reference so you can use a common set of functions. To set this up, you will:
Create a central file (I call mine util.js)
const helper = function(){
this.exampleFunction = function(num){
return num; //insert function here
}
this.exampleFunction2 = function(elem){
elem.click() //insert function here
}
}
Inside your spec.js file you will do:
const help = require('path/to/util.js');
const util = new help();
describe('Example with util',function(){
it('Should use util to click an element',function(){
let elem = $('div.yourItem');
util.exampleFunction2(elem);
});
});
You can then call these functions from any spec file. You would then be able to seperate your tests into seperate spec files, but have a common set of functions for the parts that are the same.
Another way to do this, without creating separate files is to just use a local function.
Example spec.js file:
describe('Should use functions',function(){
afterEach(function(){
$('button.logout').click();
)};
it('Should run test as user 1',function(){
$('#Username').sendKeys('User1');
$('#Password').sendKeys('Password1');
$('button.login).click();
doStuff();
)};
it('Should run test as user 2',function(){
$('#Username').sendKeys('User2');
$('#Password').sendKeys('Password2');
$('button.login').click();
doStuff();
)};
function doStuff(){
$('div.thing1').click();
$('div.thing2').click();
)};
)};
As per comments for multiple describes:
describe('Test with user 1',function(){
beforeEach(function(){
//login as user 1
});
it('Should do a thing',function(){
//does the thing as user 1
});
});
describe('Test with user 2',function(){
beforeEach(function(){
//login as user 2
});
it('Should do another thing',function(){
//does the other thing as user 2
});
});
The whole point of beforeEach is that it is the same for each test.
If you want to do different things, then they belong in the specific test.
Write a helper function and call it from the specific test if you want to have common functionality that does slightly different things depending on an argument.

Disable Jasmine expectation, like xdescribe or xit?

On the Jasmine website I see that we can disable suites by xdescribe or individual specs by xit. Is there a way to disable only an expectation (like xexpect)?
The reason why I'm asking this is because I'm writing e2e tests with Protractor and in our continuous integration we don't yet (if ever) have access to the database, though locally we can run real end to end tests with access to the database, for example.
I would like to mark individual expectations as optional, depending on a configuration or environment variable. It would be nice to make a switch once, and then create a wrapper around expect, that only fails if we are running the tests locally (with access to the database).
So for example I can create a new spec family:
if (process.env.DB_AVAILABLE) {
dbit = it;
} else {
dbit = xit;
}
and write specs that depend on database connection as following:
dbit('creates new user', function () {});
Is there a way to do the same with expect (e.g. dbexpect)?
If there is something fundamentally wrong with my approach, don't hold it back and let me know.
You could create your own xexpect by implementing all the methods/properties with an empty function:
var xexpect = function() {
return xexpect;
};
Object.getOwnPropertyNames(jasmine.Expectation.prototype).forEach(function(name){
xexpect[name] = xexpect;
});
Object.defineProperty(xexpect, 'not', {get: xexpect});
Usage :
xexpect(1).toBeGreaterThan(2);
xexpect(true).not.toEqual(true);

Yeoman - Delaying logging until after task completion

I'm getting frustrated with part of a Yeoman Generator I'm building. As it's my first, I have no doubt I'm missing something obvious, but here it goes.
Simply put, I'm trying to log a message, Do Things™ and then log another message only when those things have been done.
Here's the method:
repos: function () {
var self = this;
this.log(highlightColour('Pulling down the repositories'));
// Skeleton
this.remote('user', 'skeleton', 'master', function(err, remote) {
if (!err) {
remote.bulkDirectory('.', self.destinationRoot());
} else {
self.log('\n');
self.log(alertColour('Failed to pull down Skeleton'));
repoErr = true;
}
});
//
// Three more near identical remote() tasks
//
if (!repoErr) {
self.log(successColour('Success!'));
self.log('\n');
} else {
self.log(alertColour('One or more repositories failed to download!'));
}
},
Each of the individual remote() tasks are working fine, but I get both the first and last self.log() messages before the file copying happens. It seems trivial, but I simply want the success message to come after everything has been completed.
For example, in the terminal I see:
Pulling down the repositories
Success!
file copying results
It should be:
Pulling down the repositories
file copying results
Success!
I thought it could be something to do with using this.async() with done() at the end of each remote() task, and I tried that, but whenever I do, none of the code fires at all.
I've even tried breaking everything (including the messages) into separate methods, but still no luck.
Such a simple goal, but I'm out of ideas! I'd be grateful for your help!
EDIT: In case you're wondering, I know the messages are coming first because any alerts regarding file conflicts are coming after the messages :)
This is not an issue related to Yeoman. You have asynchronous code, but you're handling it as if it was synchronous.
In the example you posted here, just do the logging as part of this.remote callback:
repos: function () {
var self = this;
this.log(highlightColour('Pulling down the repositories'));
// Skeleton
this.remote('user', 'skeleton', 'master', function(err, remote) {
if (!err) {
remote.bulkDirectory('.', self.destinationRoot());
self.log(successColour('Success!'));
self.log('\n');
} else {
self.log('\n');
self.log(alertColour('Failed to pull down Skeleton'));
self.log(alertColour('One or more repositories failed to download!'));
}
});
},
Maybe your actual use case is more complex; in this case you can use a module like async (or any other alternative) to handle more complex async flow. Either way, Yeoman doesn't provide helpers to handle asynchronous code as this is the bread and butter of Node.js.

Deps autorun in Meteor JS

Decided to test out Meteor JS today to see if I would be interested in building my next project with it and decided to start out with the Deps library.
To get something up extremely quick to test this feature out, I am using the 500px API to simulate changes. After reading through the docs quickly, I thought I would have a working example of it on my local box.
The function seems to only autorun once which is not how it is suppose to be working based on my initial understanding of this feature in Meteor.
Any advice would be greatly appreciated. Thanks in advance.
if (Meteor.isClient) {
var Api500px = {
dep: new Deps.Dependency,
get: function () {
this.dep.depend();
return Session.get('photos');
},
set: function (res) {
Session.set('photos', res.data.photos);
this.dep.changed();
}
};
Deps.autorun(function () {
Api500px.get();
Meteor.call('fetchPhotos', function (err, res) {
if (!err) Api500px.set(res);
else console.log(err);
});
});
Template.photos.photos = function () {
return Api500px.get();
};
}
if (Meteor.isServer) {
Meteor.methods({
fetchPhotos: function () {
var url = 'https://api.500px.com/v1/photos';
return HTTP.call('GET', url, {
params: {
consumer_key: 'my_consumer_key_here',
feature: 'fresh_today',
image_size: 2,
rpp: 24
}
});
}
});
}
Welcome to Meteor! A couple of things to point out before the actual answer...
Session variables have reactivity built in, so you don't need to use the Deps package to add Deps.Dependency properties when you're using them. This isn't to suggest you shouldn't roll your own reactive objects like this, but if you do so then its get and set functions should return and update a normal javascript property of the object (like value, for example), rather than a Session variable, with the reactivity being provided by the depend and changed methods of the dep property. The alternative would be to just use the Session variables directly and not bother with the Api500px object at all.
It's not clear to me what you're trying to achieve reactively here - apologies if it should be. Are you intending to repeatedly run fetchPhotos in an infinite loop, such that every time a result is returned the function gets called again? If so, it's really not the best way to do things - it would be much better to subscribe to a server publication (using Meteor.subscribe and Meteor.publish), get this publication function to run the API call with whatever the required regularity, and then publish the results to the client. That would dramatically reduce client-server communication with the same net result.
Having said all that, why would it only be running once? The two possible explanations that spring to mind would be that an error is being returned (and thus Api500px.set is never called), or the fact that a Session.set call doesn't actually fire a dependency changed event if the new value is the same as the existing value. However, in the latter case I would still expect your function to run repeatedly as you have your own depend and changed structure surrounding the Session variable, which does not implement that self-limiting logic, so having Api500px.get in the autorun should mean that it reruns when Api500px.set returns even if the Session.set inside it isn't actually doing anything. If it's not the former diagnosis then I'd just log everything in sight and the answer should present itself.

Categories

Resources