4

I'm trying to automate my tests with Protractor and Appium for an AngularJS site with jasmine framework in iPad simulator, sendkeys() function is working for username and password, but when i click into the login button the test is passed, but the action isn't done : no redirection to home page, and no on click effect is displayed for login button, i'm sure that element is located correctly ! because when i expect the gettext() to be equal to"LOGIN" it is passed but no redirection even if i put browser.sleep(8000); Here my test script :

    "use strict";
    require("jasmine-expect");
    var wd = require("wd");
    describe('my app', function() {
    it('should make the login test',function() {
   
//    browser.ignoresynchronization=true;
    browser.get("http://10.0.22.82:8080/jws/fetablet");
    expect(browser.getCurrentUrl()).toEqual(("http://10.0.22.82:8080/jws/fetablet/#/login"));

    element(by.model('credentials.username')).sendKeys('RET02').then(function(){
    element(by.model('credentials.password')).sendKeys('RET02').then(function(){
    element(by.css('.login-button')).click().then(function(){
    browser.sleep(8000); expect(browser.getCurrentUrl()).not.toEqual("http://10.0.22.82:8080/jws/fetablet/#/login");
    });
    });
    });
});
});

Is there another method to locate the click button correctly? Here my html code :

​​<div class="lo​​gin_lang"> <md-button class="lang_button" ng-click="changeLang()">{{lang}}</md-button> </div> 
<div layout="column" flex layout-align="center center" class="md-padding splash-background"> <div class="login-logo"> <img src="{{logoSrc}}"> </div> <form class="login-form" name="loginForm" ng-submit="login()"> 
<fieldset> <md-input-container class="md-block"> 
<label translate="login.USERNAME" ng-class="{'floating-label-rtl':dir==='rtl'}" 

class="login-label">Username</label> 
<input required ng-model="credentials.username" ng-focus="onFocus()" type="text"> 

<div ng-messages="loginForm.credentials.username.$error" ng-show="loginForm.credentials.username.$dirty"> 
<div ng-message="required" trans
​​late="login.MESSAGE_REQUIRED">This is required.</div> </div> </md-input-container> <md-input-container class="md-block"> <label ​​translate="login.PASSWORD" ng-class="{'floating-label-rtl':dir==='rtl'}" 


class="login-label">Password</label> <input required ng-model="credentials.password" ng-focus="onFocus()" type="pa
​​ssword"> 
<div ng-messages="loginForm.credentials.password.$error" ng-show="loginForm.credentials.password.$dirty"> <div ng-message="required" translate="login.MESSAGE_REQUIRED">This is required.</div> </div> </md-input-container> 

<div layout-align="center center" layout="column" ng-if="oneTimePassword"> <p class="login-otp-message" translate="login.OTP_MESSAGE">Enter the code which you received by SMS</p> <md-button class="md-warn login-otp-retry" translate="login.OTP_RETRY" ng-click="retry()">Retry</md-button> </div> <md-input-container class="md-block" ng-if="oneTimePassword"> <label translate="login.SECURITY_CODE" class="login-label">Security code</label> <input required ng-model="credentials.securityCode" ng-focus="onFocus()" type="password"> <div ng-messages="loginForm.credentials.securityCode.$error" ng-show="loginForm.credentials.securityCode.$dirty"> <div ng-message="required" translate="login.MESSAGE_REQUIRED">This is required.</div> </div> </md-input-container> <div layout-align="center"> <section layout-align="center" layout="row" layout-sm="column"> <div id="login-error" md-caption class="msg-error" ng-show="error" class="label">{{error}}</div>
​​

 <md-button type="submit" class="md-raised login-button" ng-disabled="clicked" translate="login.LOGIN">Login</md-button> </section>
​​
 </div> </fieldset> </form> <md-divider></md-divider> <footer class="login-footer"> <div layout="row" layout-align="center center"> <md-button ng-click="goToCustomerCare()" class="login-footer-link" translate="login.CUSTOMER_CARE">Contact Customer Care</md-button> <div> | </div> <md-button ng-click="showDisclaimer()" class="login-footer-link" translate="login.DISCLAIMER">Disclaimer</md-button> </div> </footer> </div>

​​I put the details of Appium recorder about the login button

Appium Inspector

Community
  • 1
  • 1
Emna Ayadi
  • 2,430
  • 8
  • 37
  • 77

5 Answers5

45

There might be multiple reasons for that and it is going to be a guessing game anyway.

  • it could be that there is an another element matching the .login-button locator and you are clicking a different element. Let's improve the locator:

    element(by.css(".login-form .login-button")).click();
    
  • wait for the element to be clickable:

    var EC = protractor.ExpectedConditions;
    element(by.model('credentials.username')).sendKeys('RET02');
    element(by.model('credentials.password')).sendKeys('RET02');
    
    var loginButton = element(by.css('.login-form .login-button'));
    browser.wait(EC.elementToBeClickable(loginButton), 5000);
    loginButton.click();
    
  • add a small delay before clicking the element (silly, but I see that helped sometimes):

    element(by.model('credentials.username')).sendKeys('RET02');
    element(by.model('credentials.password')).sendKeys('RET02');
    browser.sleep(500);
    element(by.css('.login-form .login-button')).click();
    
  • another silly try, click 2 times (I cannot believe I actually advise that):

    var loginButton = element(by.css('.login-form .login-button'));
    loginButton.click();
    loginButton.click();
    
  • disable angular animations

  • click the button via browser.actions() moving to the element before the click:

    var loginButton = element(by.css('.login-form .login-button'));
    browser.actions().mouseMove(loginButton).click().perform();
    
  • sort of an extension to the previous approach. Move to element, sleep for half a second and then click:

    browser.actions.mouseMove(loginButton).perform();
    browser.sleep(500);
    loginButton.click();
    

    Or, if you would introduce a custom sleep() action, you can do:

    browser.actions.mouseMove(loginButton).sleep(500).click().perform();
    
  • click the element via javascript:

    var loginButton = element(by.css('.login-form .login-button'));
    browser.executeScript("arguments[0].click();", loginButton);
    

And, after the form is submitted, instead of browser.sleep(), you can wait for URL to change explicitly, please see:


As a side note, in Protractor, you use the $ and $$ shortcuts for the CSS locators:

var loginButton = $('.login-form .login-button');
Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • Thanks a lot @alecxe for your detailled answer, i try with the first one :element(by.css(".login-form md-button.login-button")).click(); But it tells me No element found using locator: By(css selector, .login-form md-button.login-button) – Emna Ayadi Apr 29 '16 at 14:14
  • @Emna okay, how about `element(by.css(".login-form .login-button"))`? Also, how about the option #2 - the wait - does it play around with wait nicely? Thanks. – alecxe Apr 29 '16 at 14:20
  • with element(by.css(".login-form .login-button")) it's ok, the element is found i even get the text and print it it is equal to "LOGIN" but the problem of click() isn't solved yet with all those smart options you propose to me, the action is executed but we don't see it ! and with this option : browser.actions.mouseMove(loginButton).click().perform(); i got this error browser.actions.mouseMove is not defined. Otherwise i got this error after the end of all script execution : Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL – Emna Ayadi May 02 '16 at 08:48
  • with option "click the element via javascript:" JS stack trace Security context: 0x20dff2ce3ac1 1: new constructor(aka Deferred) [/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1380] [pc=0x2d32fda1634] (this=0x2d15945f3231 ,opt_flow=0x2485f6447811 ) 3: new Task(aka Task) [/usr/local/lib/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2509] [pc=0x... FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory Abort trap: 6 – Emna Ayadi May 02 '16 at 09:03
  • 2
    @Emna the "browser actions" option had a problem - just fixed it: `browser.actions().mouseMove(loginButton).click().perform()`. – alecxe May 02 '16 at 12:12
  • @Emna also, remember I've asked about whether you actually have any errors on the console when you click the button? Have you checked that? That "Allocation failed - process out of memory" issue is one of the possible symptoms. I suspect it's the application itself or the local application build you are running the tests agains that causes problems. – alecxe May 02 '16 at 12:16
  • Thanks @alecxe :D browser.actions().mouseMove(loginButton).click().perform() is working fine :D – Emna Ayadi May 02 '16 at 15:07
  • i 'm always having problem in click(); the click into login button is ok. But in Home page i have an ajax tutorial that appear. I want to click into Don't show again button but the action is never performed : i tried with this one alsobrowser.actions().mouseMove(tutorialButton).click().perform() but not working, here the new problem : http://stackoverflow.com/questions/37070680/timed-out-waiting-for-asynchronous-script-result-while-executing-protractor-scri – Emna Ayadi May 10 '16 at 10:27
  • 1
    @Emna yeah, I've seen you've started a bounty on that question. I'll take a closer look later if I have a chance. Crazy days.., thanks. – alecxe May 10 '16 at 16:18
  • Thanks a lot, i'll try to update it if i change some thing :) – Emna Ayadi May 10 '16 at 16:21
1

Try executing those commands without using the promise chain. It can be a problem in the previous chain.

"use strict";
require("jasmine-expect");
var wd = require("wd");
describe('my app', function() {
it('should make the login test',function() {
  browser.get("http://10.0.22.82:8080/jws/fetablet");
  expect(browser.getCurrentUrl()).toEqual(("http://10.0.22.82:8080/jws/fetablet/#/login"));
  element(by.model('credentials.username')).sendKeys('RET02');
  element(by.model('credentials.password')).sendKeys('RET02');
  element(by.css('.login-button')).click();
  browser.sleep(8000);   
  expect(browser.getCurrentUrl()).not.toEqual("http://10.0.22.82:8080/jws/fetablet/#/login");
});

PS: You can avoid using the 'then' function when you do not need to use the result of the method. It is controlled by the control flow

flaviomeira10
  • 566
  • 11
  • 24
  • Yes i have already tried that but no, so that i make then to see if it is executed or not i found that all is executed ! But i have a small doubt may be there is another way to locate element ? – Emna Ayadi Apr 29 '16 at 13:40
  • Yes, you could use xpath locator (not recommended). Is your button enabled by default or you must fill in some field in order to enable it? Can you insert a `browser.sleep(10000);` before the click command and check the button clicking on it? Does it work? PS: try using '.md-raised login-button' to find the element. – flaviomeira10 Apr 29 '16 at 14:05
  • yes i have tried with the xpath generated by appium but it couldn't be found !! I put the details of appium recorder in the question because i'm testign using appium and protractor – Emna Ayadi May 02 '16 at 09:30
  • @Emna: Is your button enabled by default or it is enabled by an event? Try triggering the mouseUp event manually after filling the password field: `element(by.model('credentials.password')).sendKeys('RET02'); browser.actions().mouseDown().mouseUp().perform(); element(by.css('.login-button')).click();`. – flaviomeira10 May 02 '16 at 15:18
  • Thanks for your comment, yes it is enabled by default. now it works with some thing like this one browser.actions().mouseMove(loginButton).click().perform(); But i don't know if this is the best solution for end to end testing or i should find another one? – Emna Ayadi May 02 '16 at 15:34
1

Just adding browser.sleep after the click worked for me:

it('Some test', function () {

    element(by.css("button[type='submit']")).click();
    browser.sleep(1000);
});
evgpisarchik
  • 452
  • 5
  • 9
0

I have one more suggestion, you can expect particular element need to be displayed and clear the text-box. You can actually write in promise, that is the best way.

  var loginButton = element(by.css('.md-raised.login-button'));
  var userName = element(by.model('credentials.username'));
  var password = element(by.model('credentials.password'));

  this.username = function(sendUserName) {
      expect(userName.isDisplayed()).toBeTruthy();
      userName.clear().then(function(){
          userName.sendKeys(sendUserName).then(function(){
              expect(password.isDisplayed()).toBeTruthy();
          });
      });
  };

  this.password = function(password) {
      expect(password.isDisplayed()).toBeTruthy();
      password.clear().then(function(){
          password.sendKeys(password).then(function(){
              browser.wait(EC.elementToBeClickable(loginButton), 10000);
          });
      });
  };

  this.clickloginbutton = function() {
    expect(loginButton.isDisplayed()).toBeTruthy();
    loginButton.click().then(function(){
      expect('something').not.toBeNull();
    });
 }
Nick
  • 484
  • 5
  • 18
  • The one who down-voted answer, can i know the reason please. So that next time i can correct. – Nick Apr 30 '16 at 20:37
  • this is unique answer among other answer, please can some on tell me why this is down-voted. – Nick Apr 30 '16 at 21:44
  • 2
    As I mentioned before, you can avoid the promise chaining when you don't need the result of the driver command. Your code is not very readable. Take a look at the control flow explanation (https://spin.atomicobject.com/2014/12/17/asynchronous-testing-protractor-angular/). – flaviomeira10 May 01 '16 at 15:09
  • Hi @Nick, thanks for trying to answer to my question Nick, but the method wait (EC.elementToBeClickable () .. has already been proposed by Alecxe and it didn't work for me. – Emna Ayadi May 02 '16 at 08:58
  • @Nick, i'm not talking about fields of username or password but about login button. Now issue is solved ! ;) – Emna Ayadi May 03 '16 at 07:45
0

Tried to click the button twice and functionality wise it worked but throwed NoSuchElementError for the second click.

Made below adjustment and it worked for me

    await browser.wait(EC.elementToBeClickable(element(by.css('selector')), 5000);
    await $('selector').click();
         if(await $(selector).isDisplayed())
           await $(selector).click();

55s25
  • 11
  • 2