AngularJS: Accessing $scope objects in e2e tests

I am building a Math tutoring application and would like to test my UI using angular's e2e testing suite.

Currently I am working on a Fraction page that generates a random fraction, displays a series of shaded and unshaded boxes and asks the user to input the fraction formed by the shading.

Using an e2e test, I would like to test how the UI responds to both correct and incorrect input; however, since the fraction is randomized on page load, I do not know what 'correct' input is from inside the test.

The easiest way for me to get the correct answers to input would be to gain access to the Fraction object, located at $scope.problemObject for the controller, and call its API functions .getNumerator() and .getDenominator(). However, I have not found a way to get to this object from within my tests.

Relevant lines from my controller are:

$scope.problemObject = Fraction.random();
// This produces an object with two relevant 
// functions (getNumerator() & getDenominator())

What I Have Tried

  • binding()

Initially I thought binding() would do what I needed, however all calls to binding('problemObject') or binding('problemObject.getNumerator()' and the like issue an error saying that the binding cannot be found. I suspect that this is because $scope.problemObject and the return value of $scope.problemObject.getNumerator() are not directly bound to the UI.

  • angular.element().scope()

Executing angular.element('#problem').scope().problemObject from the console on the page that I am testing works perfectly; however, trying the same line from within my test issues the following error: 'selectors not implemented'.

I have also tried a few variations:

element('#problem').scope().problemObject: Error: 'Object # has no method 'scope''

angular.element(element('#problem')).scope().problemObject: Error: 'Cannot read property 'problemObject' of undefined'

I guess 'element' in e2e test and 'angular.element' are different objects. You may want to try reading the value from the view.

if it is input field.

var value = element('#problem').val();

Otherwise, something like:

var value = element('#problem').text();

(Looking into scope object from e2e is kind of cheating in my opinion.)

Edit

I totally misunderstood the question and construct of the web page. Sorry for the confusion. What it has to validate is the input fields against numbers of the shaded and non-shaded boxes ('td' elems in this example).

var total = element('td').count()
  , fraction = element('td.shaded').count();

Idea is same, it is trying to get the numbers from the view, not from $scope.

Turns out the problem lies in the scope being stored in jQuery's data. Since jQuery stores the data in a hashtable as $.cache global, once we are outside of the frame that the test webpage is running in, we no longer have access to them. The way I solved it is by accessing the jQuery inside the iframe's window (conveniently given in the $window parameter).

Below is what I come up with to access the scope. You can do scope('#myElement', 'foo.bar') to query $scope.foo.bar.

angular.scenario.dsl('scope', function() {
return function(selector, entry) {
    return this.addFutureAction('find scope variable for \'' + selector + '\'', 
        function($window, $document, done) {
            var $ = $window.$; // jQuery inside the iframe
            var elem = $(selector);
            if (!elem.length) {
                return done('No element matched \'' + selector + '\'.');
            }
            var entries = entry.split('.');
            var prop = elem.scope();
            for (var i in entries) {
                prop = prop[entries[i]];
            }
            done(null, prop);
        });
    };
});