Execution order of protractor tests - angularjs

Which is the best way to control the execution order of Protractor tests?
Problem case 1: protractor swipes angular pages so quickly that it cannot manipulate (fill in) input data. (The swipe logic is to transfer from being invisible to visible and by translating them into the window visible area). thus protractor cannot see those beyond the window and with opacity 0. To test i can only fill in the first page. the others are swiped too fast (or async).
Problem case 2: After i filled in the first page and submitted the form, the data is saved and alert shows confirmation message. Then protractor has to click a drop-down list and browser should navigate to the page where saved data is displayed. The problem is that protractor clicks the drop-down list before the data is saved due to alert fired later (have to wait for alert).
Question is: Is there a way to control tests to be executed in the given order in protractor? And is there a way to hold down swiping to fill in the date (otherwise protractor does not see it)? Here is the simplified code:
it('should fill in form and send data', function() {
// fill in textarea field
element.all(by.css('textarea')).first().clear().sendKeys('test');
// goes to page 2
browser.executeScript("angular.element(document.documentElement)
.injector().get('$rootScope').$broadcast('nextPage', {direction: 'next'});");
// Here is Problem 1. Though i can see this page when testing,
// the input is not visible for protractor.
// fill in input-field
element.all(by.css('input')).first().clear().sendKeys('test');
// goes to page 1
browser.executeScript("angular.element(document.documentElement)
.injector().get('$rootScope').$broadcast('prevPage', {direction: 'prev'});");
// submit form
element(by.css('button[type=submit]')).click();
// Here is problem 2. The following test is executed earlier
// than the following alert.
//browser.wait(protractor.ExpectedConditions.alertIsPresent(), 3000);
var alertDialog = browser.switchTo().alert();
expect(alertDialog.getText()).toEqual('Saved');
alertDialog.accept();
});
it('should click drop-down-list', function() {
// click drop-down list
element(by.css('.drop-down-list')).click();
});

I personally believe browser.sleep(5000) should resolve the second issue.
if not can you try with promisies
element(by.css('button[type=submit]')).click().then(function(){
var alertDialog = browser.switchTo().alert();
expect(alertDialog.getText()).toEqual('Saved');
alertDialog.accept();
});
This should wait for the promise to get resolved (i.e to click submit button) and then execute the code snippet inside

Related

AngularJS testing with Protractor - chaining promises- How to wait for animation to complete?

I am currently developing an automated test suite for an AngularJS application developed by my company. I have already designed & developed a number of test cases, which are running & passing/ failing as expected.
However, with the test that I am currently developing, I seem to have run into an issue when chaining promises, and am unable to solve it... The test script as it stands is:
it('should navigate to the browser page', function() {
console.log("Start browser page test");
browser.waitForAngularEnabled(false);
browser.wait(EC.visibilityOf(pagesMenuBtn), 10000).then(
browser.actions().mouseMove(pagesMenuBtn).perform().then(
//expect(pageBrowserBtn.isDisplayed()).toBeTruthy();
browser.wait(EC.visibilityOf(pageBrowserBtn), 12000).then(
pageBrowserBtn.click().then(
function() {
console.log("Browser menu button clicked");
//browser.pause();
}).then(
browser.wait(EC.visibilityOf(browserPageTagsLink), 20000).then(
function(){
console.log("End browser page test (then call)");
expect(browser.getCurrentUrl()).toBe(VM + '/#/pages/browser');
}
)
)
)
)
);
});
When I run my test script (I have a number of other tests that run before this one), the first few run & pass successfully, then when this test starts to execute, the console shows:
Started
....Start browser page test
Browser menu button clicked
F
Failures:
1) App should navigate to the browser page
Message:
Failed: Wait timed out after 20010ms
Stack:
TimeoutError: Wait timed out after 20010ms
at WebDriverError (C:\Users\path\selenium-webdriver\lib\error.js:27:5)
So from the output displayed in the console, it's clear that the test executes correctly as far as the console.log("Browser menu button clicked); statement, which indicates that a click has been performed on the menu button as I intend (i.e. the menu button clicked is displayed on a popup menu that is only shown when the cursor is hovering over the pagesMenuBtn element, so that indicates that the line
browser.wait(EC.visibilityOf(pageBrowserBtn), 12000).then(
is executed correctly).
Since the console.log("Browser menu button clicked"); statement is also displayed in the console, that indicates that the
pageBrowserBtn.click().then(
is also executed correctly.
But for some reason, the test is then timing out after the 20 seconds it waits for the browserPageTagsLink element to be displayed.
This browserPageTagsLink element is just a hyperlink element displayed on the 'Browser' page that my test is trying to navigate to- I am waiting for it to be visible so that I know that the page has loaded before I check that the URL is correct...
But as I watch what's happening in the browser window where these tests are run, and what's displayed in the console while my tests are running, the script seems to 'get stuck'/ pause/ 'hang' for a while after the Browser menu button clicked message is displayed in the console, and I can see from watching the browser window that this button was never actually clicked- the popup menu where this button is displayed is shown very briefly: the line browser.actions().mouseMove(pagesMenuBtn).perform() is causing the cursor to hover over the button required to show the sub-menu, but it seems that the click is performed before the sub-menu element has fully finished loading (there is some animation on the element- it appears to 'fade into view'), but I think that the click is happening before the element has fully finished loading, and so it's possibly not registering correctly?
How can I cause my test to wait for the animation to complete before trying to click the sub-menu menu item, so that the click registers with the element correctly?
I tried doing this with the line:
browser.wait(EC.visibilityOf(pageBrowserBtn), 12000).then(
It seems that the EC.visibilityOf(...) condition is met as soon as the element starts to become visible, rather than waiting until it is fully opaque, but that the
pageTagBrowserBtn.click().then(
line, which is called as soon as the condition is true can't actually be performed at this point- presumably because the element is not 'fully' visible at the point at which it's clicked?
How can I make my test wait for the animation (which has been implemented using CSS) to complete before it tries to click on the element?
I had a look on the Protractor API for anything about animations, but it only seems to provide the function allowAnimations()...
Edit
The animation for this element is set in the CSS with:
/* Animation for moving lists in configuration */
.list-fade.ng-enter,
.list-fade-leave.ng-leave {
-webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
}
i.e. it should take 0.5 seconds for the element to be displayed when the cursor hovers over its parent element.
I tried adding a call to browser.wait(), so that my test would wait for the element to be fully displayed before trying to click on it- I updated the part of my test that is sending the click to:
browser.actions().mouseMove(pagesMenuBtn).perform().then(
//expect(pageTagBrowserBtn.isDisplayed()).toBeTruthy();
browser.wait(EC.visibilityOf(pageTagBrowserBtn), 12000).then(
browser.wait(10000).then(
pageTagBrowserBtn.click().then(
function() {
console.log("Tag Browser menu button clicked");
I told it to wait for 10 seconds at this point to ensure that it definitely gave the element enough time to load (according to the CSS, it should only take 0.5 seconds to be displayed), but for some reason, my test is still failing due to the same timeout issue.
Anyone have any suggestions?
Looking at your tests I'm wondering why you have to chain your promises like that. On a fully angular page Protractor is supposed to automatically chain promises, ie when I write a test like this
driver.get(“http://www.google.com”);
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
driver.findElement(webdriver.By.name('btnG')).click();
driver.getTitle().then(function(title) {
console.log(title);
});
it actually is executed in a synchronous manner like this:
driver.get(“http://www.google.com”).
then(function() {
return driver.findElement(webdriver.By.name('q'));
}).
then(function(q) {
return q.sendKeys('webdriver');
}).
then(function() {
return driver.findElement(webdriver.By.name('btnG'));
}).
then(function(btnG) {
return btnG.click();
}).
then(function() {
return driver.getTitle();
}).
then(function(title) {
console.log(title);
});
If your page is fully Angular most of what you've written shouldn't need the promises, and that my be causing timing issues with your test. Have you tried writing the test without chaining promises?
That said, you might also try EC.elementToBeClickable(yourElement) instead of waiting for EC.visibilityOf() if you haven't already.

Protractor: verification of pop up elements randomly fails

I'm verifying and also clicking on elements of pop up window on Angular page with Protractor. The problem is that verification fails randomly.
My spec file:
describe('...
it('...
// initialize page object
var home = new homePage();
// hover over the shopping cart icon
browser.actions().mouseMove(home.shoppingCartLink).perform();
// pause browser for 4 sec
browser.sleep(4000);
// initialize page object
var shoppingCartPreview = new shoppingCartPage();
// hover over the shopping cart preview window
browser.actions().mouseMove(shoppingCartPreview.window).perform();
// verify elements are displayed
expect(shoppingCartPreview.shopName.isDisplayed()).toBeTruthy();
expect(shoppingCartPreview.price.isDisplayed()).toBeTruthy();
expect(shoppingCartPreview.delete.isDisplayed()).toBeTruthy();
// click on "Checkout" button
shoppingCartPreview.checkoutButton.click();
});
});
As wtritten, the problem is that for all the verifications I get falsy instead of truthy. What I'm doing wrong?
I even tried with the following without a success:
// waiting for elements to be visible
browser.wait(EC.presenceOf(shoppingCartPreview.popUpWindow),10000);
browser.wait(EC.presenceOf(shoppingCartPreview.shopName),10000);
browser.wait(EC.presenceOf(shoppingCartPreview.price),10000);
browser.wait(EC.presenceOf(shoppingCartPreview.delete),10000);
The problem is - you are waiting on - presenceOf() which only checks the presence of the element in DOM and hence it will always return true whether your pop-up is visible or not.
You need to wait like this leveraging visibilityOf() - browser.wait(EC.visibilityOf(shoppingCartPreview.popUpWindow), 5000)

how to click same classed element each time on page with protractor

it('it should click each of the title elements to reveal its content', function(){
element(by.css('.cardtitle')).click();
});
I have the above lines which work properly and click the first item in the series of 3 with this class. I need to click all 3 items with this class using protractor, how do I do this? (the are all on the same page, very straightforward)
for whats its worth there will be upcoming instances where there may be 2 or 5 items to click all sharing the same class.
thanks!
Loop through the elements and click them one after the other using each() function available in protractor. Here's how -
element.all(by.css('.cardtitle')).each(function(elem){
elem.click();
});
If at all you want to click the elements serially then you need to wait until click is completed resolving its promise. Here's a sample -
element.all(by.css('.cardtitle')).each(function(elem){
elem.click().then(function(){
//If you want perform some operation after click, you can do it here.
browser.sleep(1000);
});
});
Hope this helps.
You can get a collection of items and click them individually.
eg.
var cards = element.all(by.css('.cardtitle'));
expect(cards.length).toBe(3);
cards.get(0).click();
cards.get(1).click();
cards.get(2).click();

ionic + android back button + sidemenu isOpen detection - not working reliably

I have an odd situation. I am writing an angular + ionic app with a left slide menu and here is what I am trying to do:
1) Trap the Android back button
2) When you tap the back button, if the menu is not open, take you to the menu (open it)
3) If the menu is open and you hit the back button, exit the app
To trap the back button, I have this code in app.run (also tried relocating to a base controller and injecting it with $controller into other controllers, did not make a difference)
What I am noticing is that if I actually tap the 'menu icon' of the menu, the "isOpen" reliably prints true/false alternately as a I open and close the menu
But, when I tap the Android back button, it works the first time (prints true or false), but every subsequent tap does not change the isOpen state, while the menu actually toggles.
This therefore makes it impossible for me to programmatically detect if the menu is open or close within the android back button handler.
It's confusing me why this is only a problem within the android back handler, and not a problem when I tap the menu item. It's the same code that is called in both cases, which is
$ionicSideMenuDelegate.toggleLeft();
My Android handler code:
$ionicPlatform.registerBackButtonAction(function (event) {
$ionicSideMenuDelegate.toggleLeft();
$ionicSideMenuDelegate.$getByHandle('sideMenu').toggleLeft();
$timeout ( function() {
console.log ("Status of SIDE MENU IS : " + $ionicSideMenuDelegate.$getByHandle('sideMenu').isOpen());
},1000);
}, 100);
I've also set up a codepen, though not sure how one can test it on and android device, because every time I try and hit the backbutton on a codepen or jsfiddle, it makes the browser go back a page.
Any insight into what is going on? I've asked in the ionic forum, but haven't been able to find out why (yet) --> hence the post to the SO community in the hopes it reaches a wider audience.
registerBackButtonAction needs a priority to override the other actions:
The priorities for the existing back button hooks are as follows:
Return to previous view = 100 Close side menu = 150 Dismiss modal =
200 Close action sheet = 300 Dismiss popup = 400 Dismiss loading
overlay = 500
Your back button action will override each of the above actions whose
priority is less than the priority you provide. For example, an action
assigned a priority of 101 will override the 'return to previous view'
action, but not any of the other actions.
I've tested this code and it works as expected:
.run(function($ionicPlatform, $ionicSideMenuDelegate, $ionicPopup) {
$ionicPlatform.registerBackButtonAction(function(e) {
e.preventDefault();
if (!$ionicSideMenuDelegate.isOpenLeft()) {
$ionicSideMenuDelegate.toggleLeft();
} else {
navigator.app.exitApp();
}
}, 1000);
});
As you can see I've used priority 1000 to make sure that I override all the default actions.
I've also used preventDefault(). I don't think you need this but, just in case.
This bit of code only works for the left-side menu as I only check:
$ionicSideMenuDelegate.isOpenLeft()
and only open the left one:
$ionicSideMenuDelegate.toggleLeft()
but you can change it to work with the right menu as well.
UPDATE:
If someone is interested to find out more about Android and Back Button this is the best article I've read so far.

Webshims - Show invalid form fields on initial load

(Follow on questions from Placeholder Hidden)
I'd like my form to validate existing data when it is loaded. I can't seem to get that to happen
I jQuery.each of my controls and call focus() and blur(), is there a better way than this? I tried to call ctrl.checkValidity(), but it wasn't always defined yet. When it was, it still didn't mark the controls.
I seem to have a timing issue too, while the focus and blur() fire, the UI does not update. It's as if the Webshims are not fully loaded yet, even though this fires in the $.webshims.ready event.
I also tried to call $('#form').submit(), but this doesn't fire the events as I expected. The only way I could make that happen was to include an input type='submit'. How can I pragmatically case a form validation like clicking a submit button would?
Here's a jsFiddle that demonstrates the problem. When the form loads, I want the invalid email to be marked as such. If you click the add button it will be marked then, but not when initially loaded. Why?
Focus and blur in the control will cause it to be marked.
BUT, clicking ADD will too (which runs the same method that ran when it was loaded). Why does it work the 2nd time, but not when initially loaded?
updateValidation : function () {
this.$el.find('[placeholder]').each(function (index, ctrl) {
var $ctrl = $(ctrl);
if( $ctrl.val() !== "" && (ctrl.checkValidity && !ctrl.checkValidity()) ) {
// alert('Do validity check!');
$ctrl.focus();
$ctrl.blur();
}
});
}
I see this in FF 17.0.5. The problem is worse in IE9, sometimes taking 2 or 3 clicks of ADD before the fields show in error. However, I get errors on some of the js files I've liked 'due to mime type mismatch'.
This has to do with the fact, that you are trying to reuse the .user-error class, which is a "shim" for the CSS4 :user-error and shouldn't be triggered from script. The user-error scripts are loaded after onload or as soon as a user seems to interact with an invalid from.
From my point of view, you shouldn't use user-error and instead create your own class. You can simply check for validity using the ':invalid' selector:
$(this)[ $(this).is(':invalid') ? 'addClass' : 'removeClass']('invalid-value');
Simply write a function with similar code and bind them to events like change, input and so on and call it on start.
In case you still want to use user-error, you could do the following, but I would not recommend:
$.webshims.polyfill('forms');
//force webshims to load form-validation module as soon as possible
$.webshims.loader.loadList(['form-validation']);
//wait until form-validation is loaded
$.webshims.ready('DOM form-validation', function(){
$('input:invalid')
.filter(function(){
return !!$(this).val();
})
.trigger('refreshvalidityui')
;
});

Resources