5

Here is a div with an AngularJS ng-click attribute that sets a variable when the div is clicked.

<div id="id"
     ng-click="foo.bar = true;">
     Set bar variable on foo object to true
</div>

Here is some Java code that uses Selenium to click on the div element.

By upload = By.id("id");
driver.findElement(uploadCensus).click();

When I run the Java code, the AngularJS hangs for eternity. I figure foo.bar is not set when the div is clicked so here is some code that sets the variable directly.

By upload = By.id("id");
((JavascriptExecutor) driver)
    .executeScript("foo.bar = true;",   
    driver.findElement(upload));

Stacktrace

unknown error: foo is not defined (Session info: chrome=56.0.2924.87) (Driver info: chromedriver=2.25.426923 (0390b88869384d6eb0d5d09729679f934aab9eed),platform=Windows NT 6.1.7601 SP1 x86_64) (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 51 milliseconds Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58' System info: host: 'WV-VC104-027', ip: '{ip}', os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.8.0_151' Driver info: org.openqa.selenium.chrome.ChromeDriver Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.25.426923 (0390b88869384d6eb0d5d09729679f934aab9eed), userDataDir=C:\Users{user}\AppData\Local\Temp\scoped_dir5600_4225}, takesHeapSnapshot=true, pageLoadStrategy=normal, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=56.0.2924.87, platform=XP, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true}] Session ID: a7734312eff62fe452a53895b221a58d

When I try to set foo.bar I cannot get a reference to the variable because it is not globally defined and is buried somewhere inside AngularJS source code. I have tried to unminify the index and look for the variable but I cannot seem to find it. I want to manually set the foo.bar variable through the JavascriptExecutor but cannot get a reference to the variable. How would I find then set the variable?

If that seems like the wrong way to trigger this ng-click, I am open to ideas. I am sure Protractor has a way to handle this but this AngularJS application deployed in an enterprise environment and have been trying to get the business side to approve the tech for months. I am stuck with Selenium. Help...

2
  • 1
    What is the error when you're using the JS executor? Commented Oct 20, 2018 at 17:48
  • @demouser123 editted; lol i forgot the stacktrace. Commented Oct 20, 2018 at 18:00

1 Answer 1

2

Just answer for your questions directly [ Dont use it for your case :) ]

Angular have isolated variable scope.So you need get the scope and the set the variable in it.

angular.element("#id").scope().foo.bar = true;

But dont use it for your case. As you are testing the application at system level, you have to test them in same as user does.

Recommended Answer: Wait for angular testablity in java

Whatever protractor can handle, your java test can also do. Both are wrappers over the selenium bindings. Ultimately all the selenese code executes in the same browser you test.

waitForAngularMethod is most inevitable method that your missing in your test. Good thing is you can execute same kind of javascript using JavaScriptExecutor inside expilcit wait.

If you are javascript lover, you can refer the waitForangular method implementation in protractor. It is just a simple validation check and wait using callback.

Your java code for creating expected condrion for waitforangular

String waitForAngularJs ="Your javascript goes here"; // as it is lengthy read it from file and store here.
ExpectedCondition<Boolean> waitForAngular= new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver driver) {
                return ((JavascriptExecutor) driver).executeAsyncScript(waitForAngularJs).equals(true);
            }
};

 WebDriverWait wait = new WebDriverWait(driver, 60);
 wait.until(waitForAngular);

Wait for angular Javascript (Modified for your Java execution. But I didn't check it):

The method requires two attributes, root locator hook and a call back. I have set callback as a simple funtion return true and hooks with root locator which is [ng-app].

var testCallback = function() {
  return true;
 };

    // Wait for angular1 testability first and run waitForAngular2 as a callback
    var waitForAngular1 = function(callback) {

      if (window.angular) {
        var hooks = window.angular.element('[ng-app]');
        if (!hooks){
          callback();  // not an angular1 app
        }
        else{
          if (hooks.$$testability) {
            hooks.$$testability.whenStable(callback);
          } else if (hooks.$injector) {
            hooks.$injector.get('$browser')
                .notifyWhenNoOutstandingRequests(callback);
          } else if (!rootSelector) {
            throw new Error(
                'Could not automatically find injector on page: "' +
                window.location.toString() + '".  Consider using config.rootEl');
          } else {
            throw new Error(
                'root element (' + rootSelector + ') has no injector.' +
                ' this may mean it is not inside ng-app.');
          }
        }
      }
      else {callback();}  // not an angular1 app
    };

    // Wait for Angular2 testability and then run test callback
    var waitForAngular2 = function() {
      if (window.getAngularTestability) {
        if (rootSelector) {
          var testability = null;
          var el = document.querySelector(rootSelector);
          try{
            testability = window.getAngularTestability(el);
          }
          catch(e){}
          if (testability) {
            testability.whenStable(testCallback);
            return;
          }
        }

        // Didn't specify root element or testability could not be found
        // by rootSelector. This may happen in a hybrid app, which could have
        // more than one root.
        var testabilities = window.getAllAngularTestabilities();
        var count = testabilities.length;

        // No angular2 testability, this happens when
        // going to a hybrid page and going back to a pure angular1 page
        if (count === 0) {
          testCallback();
          return;
        }

        var decrement = function() {
          count--;
          if (count === 0) {
            testCallback();
          }
        };
        testabilities.forEach(function(testability) {
          testability.whenStable(decrement);
        });

      }
      else {testCallback();}  // not an angular2 app
    };

    if (!(window.angular) && !(window.getAngularTestability)) {
      // no testability hook
      throw new Error(
          'both angularJS testability and angular testability are undefined.' +
          '  This could be either ' +
          'because this is a non-angular page or because your test involves ' +
          'client-side navigation, which can interfere with Protractor\'s ' +
          'bootstrapping.  See http://git.io/v4gXM for details');
    } else {waitForAngular1(waitForAngular2);}  // Wait for angular1 and angular2
// Testability hooks sequentially

Why we need wait for angular and logic behind

Why we need this because the angular uses html templating, two way data binding, ajax requests and routing. So after a page navigation, we have to wait all this operation (requests & promises) to complete. Else the html will not respond as expected as in your case.

Check the angular testablity

i.e. to check the testablity of angular. For this angular itselfs provide a method.

To check based on a element,

 window.getAngularTestability(el).whenStable(callback); //Here el is element to be valiated.

To check all testablities,

 window.getAllAngularTestabilities();
   testabilities.forEach(function(testability) {
        testability.whenStable(callback);
});

Wait for all http pending request to complete

Before checking the angular testablity, we can make sure there is no http request pending with the following lines.

angular.element(document).injector().get('$http').pendingRequest.length
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.