angular / protractor

E2E test framework for Angular apps
http://www.protractortest.org
MIT License
8.75k stars 2.31k forks source link

Protractor compatablity problem with Angular 1.2+ #325

Closed ghost closed 10 years ago

ghost commented 10 years ago

Hi,

At this moment the latest version of protractor (with grunt) is not working against an Angular application based on version 1.2 and above. The problem is stacktrace that is given:

Stacktrace: Error: Error while waiting for Protractor to sync with the page: {} at Error () at ....\node_modules\protractor\node_modules\selenium-webdriver\lib\webdriver\promise.js:1311:15 at ....\node_modules\protractor\node_modules\selenium-webdriver\lib\webdriver\promise.js:1460:7 at ....\node_modules\protractor\nodemodules\selenium-webdriver\lib\goog\base.js:1178:15 at webdriver.promise.ControlFlow.runInNewFrame (....\node_modules\protractor\node_modules\selenium-webdriver\lib\webdriver\promise.js:1438:20) at notify (....\node_modules\protractor\node_modules\selenium-webdriver\lib\webdriver\promise.js:328:12) at notifyAll (....\node_modules\protractor\node_modules\selenium-webdriver\lib\webdriver\promise.js:297:7) at reject as errback at reject (....\node_modules\protractor\nodemodules\selenium-webdriver\lib\webdriver\promise.js:1624:5) at webdriver.promise.ControlFlow.abortFrame (....\node_modules\protractor\node_modules\selenium-webdriver\lib\webdriver\promise.js:1397:9)

A 'solution' is to add the snippet below to your test but this is not always desired. beforeEach(function() { var ptor = protractor.getInstance(); ptor.ignoreSynchronization = true; });

juliemr commented 10 years ago

The current tests run against Angular 1.2, so that's not the only problem - there must be something about your application which isn't synchronizing. This is usually because of an $http request or $timeout. Could you provide more information about your app? Does this happen on every test, or just some?

ghost commented 10 years ago

This happens on every test that I wrote. The strange thing about this is that when I downgrade Angular back to 1.1.5 then all my tests succeed. Also there are no code changes in the code and tests after the upgrade to, in this case, Angular 1.2.3.

The application is using $resource but I can't imagine that this causes the problem.

romankolpak commented 10 years ago

I'm having the same issue, and the ptor.ignoreSynchronization = true; seems to be resolving it. Though I'm running into this in just one scenario - I'm trying to test a Facebook login in my app:

element(by.xpath('//div[@id="login-form"]/a')).click();
var handlesPromise = ptor.getAllWindowHandles();

handlesPromise
  .then(function(handles) {
     return ptor.switchTo().window(handles[1]);
  }).then(function(handle) {
     // here's where I'm getting the `Error: Error while waiting for Protractor to sync with the page: {}`
    // It seems that scheduling any commands at this point will result in this error
    element(by.id('email')).sendKeys('wut'); 
  });
juliemr commented 10 years ago

I just updated the test application to angular 1.2.4 and everything still works fine for me.

Are you sure your page has no $http requests or persistent $timeouts?

EdwinKorsten commented 10 years ago

We have the same problem.

We have a lot of protractor test on our Angular 1.0.7 application and all tests worked ok. After we upgraded to Angular 1.2.3 we noticed that most of the protractor tests failed.

This happens when we run protractor on an applicationserver with a mock backend that responds with real JSON http responses. When we run protractor on a development server which mocks all http-request with httpBackend then the protractor tests are valid.

So using $http seems to work differently in the combination protractor/Angular 1.2.3

On the server with real http JSON responses we get the error-message: Error: Error while waiting for Protractor to sync with the page: {}

To fix this we need to add: beforeEach(function () { browser.ignoreSynchronization = true; });

But then we need to add a lot of browser.sleep(1000) calls to get the existing tests working because protractor then does not wait for $http to finish.

With Angular 1.0.7 we did not have any of these problems. Is this something that can be fixed in protractor, or do we need to rewrite our protractor tests when we use $http?

ghost commented 10 years ago

The issue is solved when I changed the following snippet:

<body>
    <div  ng-app="myApp" ng-cloak ng-include src="'myFile.html'"></div>
</body>

to:

<body ng-app="myApp">
    <div ng-cloak ng-include src="'myFile.html'"></div>
</body>

The Protractor tests are now running against Angular 1.2.3 without 'Error while waiting for Protractor to sync with the page: {}' errors. EdwinKorsten and Rawry, can you verify if this solves also the problem in your applications?

EdwinKorsten commented 10 years ago

Thanks Stefan, this is going in the right direction.

When I use this:

<div class="ib-app" ng-app="appName" ng-include="'/partials/startup.html'">
</div>

I get the "Error while waiting for Protractor to sync with te page" error.

When I use this:

   <div ng-app="appName">
    <div class="ib-app" ng-include="'/partials/startup.html'">
    </div>
    </div>

This error is gone.

Unfortunately our app is bootstrapped from a content-management system with something like:

<div id="appId"></div>
<script type="text/javascript">

  var appName = angular.module('appName', ['moduleName']);
    appName.config(function (someProvider) {
        someProvider.add('module', {'field':'cms-value'});
    });
    angular.bootstrap(document.getElementById("appId"),['appName']);        
</script>

And that still gives the "Error while waiting for Protractor to sync with the page" error on Angular 1.2.3

ghost commented 10 years ago

Seems like there is an issue with manually bootstrapping the Angular application together with Protractor. Juliemr, how are you bootstrapping your test application? With ng-app or with angular.bootstrap()?

EdwinKorsten commented 10 years ago

I now can reproduce this problem using the test from the protractor project (and I also have a fix). For this I use protractor conf/basicConf.js against the application testapp/scripts/web-server.js

Running the test: everything is ok.

Then In the directory of index.html I create a file named ngviewpartial.html with contents:

  <div ng-view></div>

And in index.html replace

  <div ng-view></div>

with

 <div id="test123" ng-include="'ngviewpartial.html'" >
 </div>

And add manual bootstrapping to the bottom of the page:

    <script type="text/javascript">
     var newModule = angular.module('newModule', ['myApp']);
     newModule.run(['$route', function($route)  {
        $route.reload();
     }]); 
     angular.bootstrap(document.getElementById("test123"),['newModule']);   
    </script>  

And add the new ng-app root element to the basicConf.js file:

 rootElement: '#test123',

Now the app works ok when you access it in the browser, but all protractor tests fail.

This can be fixed in the protractor code in function clientSideScripts.waitForAngular

Replace this:

 angular.element(el).injector().get('$browser').
        notifyWhenNoOutstandingRequests(callback);

With:

    if (angular.element(el).injector()){
       angular.element(el).injector().get('$browser').
          notifyWhenNoOutstandingRequests(callback);
    } else {
    if (angular.bootstrap().get('$browser')){
            // app is manually bootstrapped
            angular.bootstrap().get('$browser').
                notifyWhenNoOutstandingRequests(callback);
        } else {
            // work around. wait 200 ms
            setTimeout(callback(),200);
        }
    }               

With this fix most of the protractor test will run ok.

Reason: with this manual bootstrapping angular.element(el).injector() does not give back an object, so the wait will fail. But with manual bootstrapping angular.bootstrap().get('$browser') will return the $browser instance.

I will try to make a pull request for this.

juliemr commented 10 years ago

Looks like this is a dupe of https://github.com/angular/protractor/issues/66 in that case.

EdwinKorsten commented 10 years ago

I just created pull request #356 to fix this issue.

EdwinKorsten commented 10 years ago

Finally found out the root cause of the problem.

When an app is manually bootstrapped on an element that has an ng-include and in the ng-include there is an ng-view, then this angular application works ok on both Angular 0.7 and Angular 1.2. But the protractor-test work on an Angular 0.7 application, but not on an Angular 1.2 application.

Cause of this is that in Angular 1.2 in the above situation, on an already bootstrapped application and el is the element that Angular is bootstrapped on, then angular.element(el).injector() does not exists, but angular.element(el).scope() does exists.

The waitForAngular function is calling angular.element(el).injector().get('$browser'), so calling waitForAngular on an already bootstrapped application with the situation above, will return an error instead of a wait. When using the software in the pull request, this error does not occur.

Instead of using the pull request we now use a work-around and modified the Angular application. When we move the ng-include to another element as the element where the app is bootstrapped, the protractor test work again. So we now use this:

   <div id="test123">
     <div ng-include="'ngviewpartial.html'">
     </div>
   </div>

<script type="text/javascript">
     var newModule = angular.module('newModule', ['myApp']);
     newModule.run(['$route', function($route)  {
        $route.reload();
     }]); 
     angular.bootstrap(document.getElementById("test123"),['newModule']);   
    </script>  
laurelnaiad commented 10 years ago

I don't know much about ngInclude, but if injector () is not returning the injector could that be a core bug?

igorzg commented 10 years ago

Sync is not working with 1.2.9

juliemr commented 10 years ago

@igorzg is this a new or separate issue? If so, could you please file a new bug with a concrete example?

igorzg commented 10 years ago

@juliemr I have to ignore synchronization in order to execute tests correctly, but then i need to handle async calls by myself in tests. When synchronization is not ignored tests are executed but tests don't work, and i get always an timeout error. I will provide later an example.

damienklinnert commented 10 years ago

@igorzg +1 same issue here

DanielaValero commented 10 years ago

I have the same issue @igorzg has, same angular 1.2.9.

this is an example:

describe('login', function () {
    beforeEach(function () {
        browser.get('/app/login');
//        browser.ignoreSynchronization = true; (If this is present, it goes ok)
    });

    it('should render login when user navigates to /app/login', function () {
        var name = element(by.tagName('form')).getAttribute('name');
        expect(name).toBe('loginForm');
    });
});

Config file:

exports.config = {
    seleniumServerJar: null,
    seleniumPort: null,

    chromeDriver: '../node_modules/protractor/selenium/chromedriver',

    chromeOnly: false,
    seleniumArgs: [],
    sauceUser: null,
    sauceKey: null,
    seleniumAddress: 'http://localhost:4444/wd/hub',
    allScriptsTimeout: 11000,
    specs: [
        '../test/e2e/*.js'
    ],

    // https://code.google.com/p/selenium/wiki/DesiredCapabilities
    // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js
    capabilities: {
        'browserName': 'chrome'
    },

    // A base URL for your application under test. Calls to protractor.get()
    // with relative paths will be prepended with this.
    baseUrl: 'http://localhost:8080/app',

    rootElement: 'html',

    onPrepare: function () {
        //     jasmine.getEnv().addReporter(new jasmine.JUnitXmlReporter(
        //         'outputdir/', true, true));
    },

    params: {
        login: {
            user: 'jane',
            password: '1234'
        }
    },

    framework: 'jasmine',

    // See the full list at https://github.com/juliemr/minijasminenode
    jasmineNodeOpts: {
        // onComplete will be called just before the driver quits.
        onComplete: null,
        // If true, display spec names.
        isVerbose: true,
        // If true, print colors to the terminal.
        showColors: true,
        // If true, include stack traces in failures.
        includeStackTrace: true,
        // Default time to wait in ms before a test fails.
        defaultTimeoutInterval: 30000
    }
};

and partial

<form class="login-form" novalidate name="loginForm" ng-submit="login()">
<span>some content</span>
</html>

Update:

And the stacktrace:

Stacktrace:
     Error: Timed out waiting for Protractor to synchronize with the page after 11 seconds
    at Error (<anonymous>)
==== async task ====
WebDriver.executeScript()
    at Protractor.waitForAngular (/Users/danielavalero/workspace/dashboard/node_modules/protractor/lib/protractor.js:278:22)
    at Protractor.findElement (/Users/danielavalero/workspace/dashboard/node_modules/protractor/lib/protractor.js:427:8)
    at Object.elementFinder.(anonymous function) [as getAttribute] (/Users/danielavalero/workspace/dashboard/node_modules/protractor/lib/protractor.js:62:21)
    at null.<anonymous> (/Users/danielavalero/workspace/dashboard/test/e2e/scenario-login.js:10:48)
    at /Users/danielavalero/workspace/dashboard/node_modules/protractor/jasminewd/index.js:54:12
==== async task ====
    at null.<anonymous> (/Users/danielavalero/workspace/dashboard/node_modules/protractor/jasminewd/index.js:53:12)
    at null.<anonymous> (/Users/danielavalero/workspace/dashboard/node_modules/protractor/node_modules/minijasminenode/lib/async-callback.js:45:37)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

Finished in 14.027 seconds
1 test, 1 assertion, 1 failure
juliemr commented 10 years ago

I've added some notes about timeouts to the error messages - I don't think this issue points to one individual problem anymore, so I'm closing. If you think you've found a bug with Protractor, please open a new issue!

softvar commented 10 years ago

describe('Testing Protractor', function() { var draftList; var ptor;

beforeEach(function() { ptor = protractor.getInstance(); ptor.ignoreSynchronization = true; });

it('should count the number of drafts', function() { ptor.get('#/'); draftList = element.all(by.repeater('newsletter in drafts')); expect(draftList.count()).toEqual(2); }); });

ahmednuaman commented 10 years ago

So this is an interesting issue. I only get this "Error while waiting for Protractor to sync with the page" when I'm running my tests on Travis: https://s3.amazonaws.com/archive.travis-ci.org/jobs/20410566/log.txt

I'm trying to do some investigation work now to try and run the tests locally on my machine through Sauce Connect. When I run the tests locally on my machine's webdriver they're all good.