Protractor: Protractor and angular manual bootstrap

Created on 30 Aug 2013  ·  49Comments  ·  Source: angular/protractor

I'm having a problem when running protractor 0.8 and having troubles running protractor on our app that is manually bootstrapped.

Here are some snippets revealing the relevant parts of the application and setup:
index.html:

 <script type="text/javascript" data-main="js/main" src="js/libraries/require.js"></script>

js/main.js:

require({
    shim: {
        .... snip ..
            }
           }, 
  ['require', ..... ], function(require) {
    return require(['bootstrap']);

bootstrap.js:

var appElm = $('html');
appElm.addClass('ng-app'); // this
angular.bootstrap(document, ['app']);

This works in practice and Angular is started in a correct way. When I view the html source I cannot see the ng-app attribute appearing on the html element. May be Protractor also fails to do so?

I use the protractor sample configuration and changed the relevant values to suit my app:

specs: [
    '../src/test/javascript/protractorSpec/*_spec.js',
  ],
  baseUrl: 'http://localhost:8080/Viewer/',
  rootElement: 'html',

Sample test:

describe('List SMC Charts', function() {
        var ptor= protractor.getInstance();

        beforeEach(function() {
          ptor.get('#/smcChart');
        });

        it('list, no filter', function() {
          var selectOption = ptor.findElement(ptor.By.input("selectOption"));
        });
});

When I run this test two errors appear:

 1) List SMC Charts   list, no filter
   Message:
     Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
   Stacktrace:
     Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
    at /usr/local/lib/node_modules/protractor/lib/protractor.js:392:15

and

2) List SMC Charts list, no filter
   Message:
     TypeError: Cannot call method 'input' of undefined
   Stacktrace:
     TypeError: Cannot call method 'input' of undefined
    at null.<anonymous> (/Users/........//Viewer/src/test/javascript/protractorSpec/some_spec.js:9:55)

The first error seems to be the most important one, but the last error is also quite interesting. It appears to me that the line: ptor.By.input is not working.

Any ideas?

untriaged discussion

Most helpful comment

@sjelin What's the documented way to support manual bootstrapping? I can't find the relevant information for protractor 5.0.

All 49 comments

Is the Angular code being included on the page? It looks like you're requiring shim, bootstrap and others but angular.min.js isn't shown.

'app': {
            deps: ['libraries/angular', 'libraries/angular-resource', 'libraries/angular-strap']
        },

The snippet above resides inside the shim{} element, I left it out of the snippets because it did not seem very relevant to me. But this is the way how angular.js is being loaded.

Hi,

Yes, Protractor currently expects an ng-app, so manually bootstrapped sites will have problems. Hoping to get a workaround for this documented soon.

Ok thx for the update. When you've figured out a workaround can you post it in this issue?

Sorry for the slow response - you should be able to fix by doing

describe('List SMC Charts', function() {
        var ptor= protractor.getInstance();

        beforeEach(function() {
          ptor.driver.get('#/smcChart'); // Just use the plain old webdriver here, it won't complain if Angular isn't loaded yet.
        });

        it('list, no filter', function() {
          var selectOption = ptor.findElement(ptor.By.input("selectOption"));
        });
});

Did you set up the ptor variable somewhere? e.g. var ptor = protractor.getInstance()?

Any update on this? Your suggested fix isn't working for me ([email protected], [email protected])

I have an awkward combination of a non-angular login screen followed by a requirejs, manually bootstrapped app:

describe('User levels', function() {
    var ptor, driver;

    var findByName = function (name) {
        return driver.findElement(protractor.By.name(name));
    };

    describe('Editors', function() {
        ptor = protractor.getInstance();
        driver = ptor.driver;
        it('Can log in', function() {
            driver.get('http://localhost:8000/');
            findByName('login').sendKeys('test_editor');
            findByName('pass').sendKeys('rubbish');
            driver.findElement(protractor.By.css('button[type="submit"]')).click();
        }, 20000);

        it('Shows the homepage', function() {
            var el = ptor.findElement(protractor.By.css('h1[field="pageTitle"]'));
            expect(el.getText()).toEqual('Welcome to Home');
        }, 30000);

    });
});

And my main.js:

require([
    'angular',
    'app'
], function (angular, app) {
    'use strict';
    var body = document.body, name = app.name;
    body.setAttribute('ng-app', name);
    body.setAttribute('id', 'ng-app');
    angular.bootstrap(body, [name]);
});

The login part works and I can see a flash of the protected page loading before protractor errors out with UnknownError: javascript error: angular is not defined. I've tried re-loading the page with driver.get('...') & even ptor.get('...') but nothing works.

Any hints/advice/fixes gratefully received.

@magnetised you'll need some way of telling your test to wait until the login is done before trying other tests. I think what's happening is that you're trying to use ptor.findElement before Angular has loaded on the page.

Your 'Can log in' spec should end with a wait statement that checks some condition for your app. Check out the example here - https://github.com/angular/protractor/blob/master/spec/login/viaConfigConf.js

@juliemr Brilliant, that works perfectly.

I had understood that protractor would do the "wait until angular has loaded" but I guess you can't cover all the cases.

Thanks again.

I have exactly the same setup. @juliemr do you mind sharing your solution. What is your waiting condition? Thanks!

@jorgenfb I wonder if you meant that comment to be directed at me?

My solution just involved waiting for the angular router to boot and change the url from the bare '/' that the login system redirects you to, to the default route -- in our case '/#view'.

I made it into a function that I could reuse it across all the tests:

var waitUntilLoaded = function() {
    ptor.wait(function() {
      return ptor.driver.getCurrentUrl().then(function(url) {
        return /view/.test(url);
      });
    });
};

I don't know if this will work for you, it depends on your routing config.

@juliemr Thank you, works like a charm :)

I'm having a similar problem (found on SO at: http://stackoverflow.com/questions/19391813/protractor-fails-to-find-angular) with Protractor not finding Angular even though Angular is very plainly loaded and running. Is the easy solution (testing without trying to find Angular) the way that we should be going? It seems to defeat the purpose of Protractor being specifically built to test Angular apps.
edit: I should also note that the proposed solution in this issue does not work for me.

+1 for having better angular manual bootstrap documentation

is this still relevant?

I honestly don't know as I've since given up trying and don't really have the time to go back and see if something has changed. Considering the version changes since then, if you're looking to close this I wouldn't argue. It can always be re-opened later.

I just started using protractor today and our app uses require.js... if there have been any updates with respect to testing an application that is manually bootstrapped, I , for one, am interested. :-)

Same problem here. Got application with the login page outside the angular app. Workaround in previous comments seems to be outdated. Would very much like updated document on how to be able test that kind of scenario.

@hankduan Yes, I think so.

I'm struggling with this issue right now. Background is similar: I have a bigger application which dependencies are managed via RequireJS.


I've already updated my app using window.name='NG_DEFER_BOOTSTRAP!'; & angular.resumeBootstrap(), however it still won't work.

Said this, I do not know what to do next. For further investigations I've set up a test case at https://github.com/knalli/protractor-with-deferred-bootstrap

  • git clone https://github.com/knalli/protractor-with-deferred-bootstrap.git
  • cd protractor-with-deferred-bootstrap
  • npm install
  • npm run-script webdriver (Selenium Server)
  • npm run-script server (Demo App Server serving a mini AJS app)
  • npm run-script e2e-test (runs protractor...)

The result is

Using the selenium server at http://localhost:4444/wd/hub
Login: Click
Login: Waiting...
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
The last active task was: 
unknown
F

Failures:

followed by

UnknownError: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element '

The last error is a little bit weird and perhaps even misleading: The test has been performed (successful as far as expectations were be okay and what we can see in the browser).

Crosscheck: If I switch the demo app to non deferred bootstrapping with manual includes (this variant is available in the branch no-deferred), then it will work successful.


I've already tried some so called "workrounds":

  1. http://www.sebdangerfield.me.uk/2014/01/angularjs-protractor-app-already-bootstrapped-error/
    Irrelevant or misleading --> no difference.
  2. https://github.com/angular/protractor/issues/325#issuecomment-36738048 using protractor.getInstance() and ignoreSynchronization --> no difference
  3. https://github.com/angular/protractor/issues/325#issuecomment-30701683 patching clientSideScripts.js/waitForAngular --> no difference

I wrote my first protractor tests yesterday and had the same issue as knalli.

Thanks for writing up the example @knalli. I'll take a look in a few days

So my protractor tests all work (except for one failure but that's my fault :-P ).
Let me know if this does/n't help.
Here's my setup:

protractor version:
Version 1.0.0-rc4
selenium standalone:
is up to date
chromedriver:
is up to date

Definitely relevant files:

Config

/*jshint expr: true*/
/*global browser: true*/
var TEST_DIR =  '../tests/';
exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub'
,  specs : [     '../tests/e2e/_protractor.js'       ]
,  capabilities: {'browserName': 'chrome'    }
,  chromeDriver: './chromedriver'  // <---- I remember not being able to get chromedriver working 
                                                    // for some reason, so I just downloaded the executable and put it
                                                    // in my config directory
,  onPrepare: function() {
    "use strict";
    browser.driver.get('http://localhost:5000/register');
    browser.driver.wait(function() {
      return browser.driver.getCurrentUrl().then(function(url) {
        return /\#\/registration/.test(url);
      });
    });
  }
    //  chromeOnly: true,
    //  baseUrl: 'http://localhost:5000/register'
,  jasmineNodeOpts: {
    showColors: true
  }
};

_protractor.js

/*jshint expr: true, undef: true */
/* global element: true, by: true, browser: true, model:true */
(function(){
  "use strict";

var RegistrationPage = function(){
  browser.driver.wait(function() {
    return browser.driver.getCurrentUrl().then(function(url) {
      return /\#\/registration/.test(url);
    });
  });
};
  var account = new RegistrationPage();

  account.elem = element.all(by.name('account_exists'));
  account.accountNumber = element(by.name('account_number'));
  account.submitAccountButton = element(by.css('#account_input button'));

  account.selectHasAccount = function(){
    account.elem.get(0).click();
    return browser.getCurrentUrl();
  };

  account.enterAccountNumber = function(keys){

    account.selectHasAccount().then(function(){
      account.accountNumber.sendKeys(keys);
      return account.submitAccountButton.click()
        .then(function(){
          return browser.getCurrentUrl();
      });
      //this wont log because already returned ^
//      console.log('need to return again');
    });
  };
  account.noAccount = function(){
    account.elem.get(1).click();
  };

describe('registration page', function() {
  it("will survive", function(){
    var registrationPage = new RegistrationPage()
    , title = browser.getTitle();
    expect(title).toBe('Web Shop | Homepage');
  });
  it('will change route on account_exists radiobox click', function(){
    var elem = element.all(by.name('account_exists'))
    , _url;
    elem.get(0).click()
    .then(function(){
      _url = browser.getCurrentUrl().then(function(_url){
        expect(_url).toBe('http://localhost:5000/register#/registration/has');
      });
    })
    .then(function(){
        elem.get(1).click().then(function(){
          _url = browser.getCurrentUrl();
          expect(_url).toBe('http://localhost:5000/register#/registration/no');
        });
      });
  });
  describe('navigate the hasAccount path:', function(){
    it('will click hasAccount which will cause a route change.', function () {
      expect(account.selectHasAccount()).toBe('http://localhost:5000/register#/registration/has');
    });
    it("will submit a valid account number", function(){
      expect(account.enterAccountNumber('1231231231')).toBe('http://localhost:5000/register#/user');
    });
  });
  it('will click noAccount which will cause a route change.', function () {
    var  _url;
    account.noAccount();
    _url = browser.getCurrentUrl();
    expect(_url).toBe('http://localhost:5000/register#/registration/no');
  });
});
})();

Require/main.js:

var pathToJQuery
if('querySelector' in document
  && 'localStorage' in window
  && 'addEventListener' in window) {
  //ie9+ and all browsers
  pathToJQuery = 'lib/jquery/dist/jquery'
} else {
  //ie8 and ie7
  pathToJQuery = 'lib/jquery-1.7.2.min'
}
requirejs.config({

      paths : {

//        Paths

          "reg": 'js/registration'
        , "com": 'js/common'
        , "init": 'js/init'
        , "regD": 'js/registration/directives'
        , "regC": 'js/registration/controllers'
        , "regS": 'js/registration/services'
        , "regM": 'js/registration/modules'
//
//        Frameworks
        ,  "angular": "lib/angular/angular"
        ,  "angular.animate" : "lib/angular-animate/angular-animate"
        ,  "jquery": pathToJQuery   //< = == v    long story; don't judge me.
        ,  "jqueryMigrate" : "lib/jquery-migrate/jquery-migrate"
        ,  "mocks": "lib/angular-mocks/angular-mocks"
        ,  "ui_router": "lib/angular-ui-router/release/angular-ui-router"
        ,  "Q": "lib/q/q"

//        Init
        ,  "apps_init" : 'js/init/apps_init'
        ,  "apps_bootstrap" : 'js/init/apps_bootstrap'
//        ,  "jquery_bootstrap" : 'js/init/jquery_bootstrap'
        ,  "registration": "js/init/registration"

//       Common

//         Registration
        ,  "registration": "js/registration/registration"
//         Controllers
        ,  "account.controller" : 'js/registration/controllers/account.controller'
        ,  "user.controller" : 'js/registration/controllers/user.controller'
//         Directives
        ,  "account.directive" : 'js/registration/directives/account.directive'
//    Services
        , "user.service" : "js/registration/services/user.service"
    }
,     shim: {
        'angular': {
            exports : 'angular'
        }
    ,   'angular.animate' : {deps: ['angular']}
    ,   'jqueryMigrate' : {deps: ['jquery']}
    ,   'ui_router' : ['angular']
    ,   'mocks': { deps: ['angular'], 'exports': 'angular.mock' }
}
});

if( pathToJQuery ===  'lib/jquery/dist/jquery' ){
  requirejs([
        'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
      , function(angular, jquery, jquery_bootstrap, apps_bootstrap) {
      }
  );
} else {
  requirejs([
      'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
    , function(angular, jquery, jqueryMigrate, jquery_bootstrap, apps_bootstrap) {
    }
  );
}
console.log("working");

Potentially helpful files:

default view (handlebars)

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org"
            xmlns:fab="http://angularjs.org"
            xmlns:ui ="http://angularjs.org"
            lang = "en" class = "no-js" >
<head >
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/x-icon" media="all" href="/images/favicon.ico" />
<script type="text/javascript" src="lib/modernizr.js"></script>

<!-- Default ie theme files:-->
<!--[if lte IE 9]><script type="text/javascript">console = {}; console.log = function(){};</script><![endif]-->
<!--[if IE 8]><link type="text/css" rel="stylesheet" href="build/css/ie8/theme.css" /><![endif]-->

<!--[if IE 7]><link type="text/css" rel="stylesheet" href="build/css/ie7/theme.css" /><![endif]-->

<!-- Normal styles -->
<link type="text/css" href="css/style.css" rel="stylesheet">


<!--[if IE 8]> <link type="text/css" rel="stylesheet" href="build/css/ie8/ie_8.css"  /> <![endif]-->
<!--[if IE 7]> <link type="text/css" rel="stylesheet" href="build/css/ie7/lte_ie7.css"/> <link type="text/css" rel="stylesheet" href="build/css/ie7/ie_7.css" /><![endif]-->


<script data-main="main" src="lib/requirejs/require.js"></script>
</head >

<body class = "page-homepage language-en" >
<div id = "wrapper" >
    <div id = "page" data-currency-iso-code = "USD" >
        {{> _header}}
        <a href = "#skip-to-content" class = "skiptocontent" >Skip to content</a >
        <a href = "#skiptonavigation" class = "skiptonavigation" >Skip to navigation menu</a >
    <!--[if lte IE 10]> <p class="browsehappy" style="margin-top: 10px; ">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p><![endif]-->
    <main ng-cloak id = "content" role = "main" class = "page_main <!--[if IE7]>ng-cloak<![endif]-->" >

        </main>
    </div >
</div >
</body >

</html >

controller.js (server config)

(function(){
    "use strict";

    //  node requires
    //==============================
    // express-hbs
    // express
    //
    const express = require('express')
        , portN = 5000
        , hbs = require('express-hbs')
        , app = express()
        , path = require('path')
        , Server = require('./server')
        , bodyParser = require('body-parser')
        , serveStatic = require('serve-static')
        , session = require('express-session')

    , router = express.Router()
    , logger  = require('morgan')
        , methodOverride = require('method-override')
        , errorhandler = require('errorhandler')


    //  view paths
    //==============================
    //
    //
    //
        , viewsD = __dirname + '/views/'
        , partialsD = viewsD + 'partials/'
        , layoutsD = viewsD + 'layouts/'
        , testsD =  __dirname + '/tests/'
    , publicD = __dirname + '/public/'
    , styleguideD = publicD+ '/styleguide/'
        , defaultF = layoutsD + 'default.hbs';
    //  Express setup.
    //==============================
    //
    //
    //
    app.use(express.static(publicD))
            .use(bodyParser())
            .use(logger('dev'))
            .use(methodOverride())
            .use(serveStatic(path.join(__dirname, 'public')))
            .use(errorhandler());

    app.set('view engine', 'hbs')
            .set('port', process.env.PORT || portN)
            .set('cache', false)
            .set('views', viewsD);

    app.engine('hbs', hbs.express3({
        partialsDir: partialsD,
        defaultLayout: defaultF,
        layoutsDir: layoutsD
    }));

    var indx = {};

    app.get( '/' ,function (req, res) {
        res.render(partialsD + '_home.hbs', indx);
    });

    app.get( '/register' ,function (req, res) {
        res.render(partialsD + '_register.hbs', indx);
    });

    new Server(app);

})();

server.js

module.exports = function(app){
  "use strict";
    var http = require('http');
    http.createServer(app).listen(app.get('port'), function(){
        console.log('Express server listening on port ' + app.get('port'));
    });
};


module.exports = function (app) {
    "use strict";
    var http = require ('http');
    http.createServer (app).listen (app.get ('port'),function () {
        console.log ('Express server listening on port ' + app.get ('port'));
    }).on ('error', function (e) {
        console.log ("got error: " + e.message);
    });
};

package.json (relevant excerpt)

"dependencies": {
    "body-parser": "^1.2.1",
    "bower": "^1.3.2",
    "errorhandler": "^1.0.1",
    "express": "^4.3.1",
    "express-hbs": "^0.7.9",
    "express-session": "^1.2.0",
    "node-dev": "^2.3.0"
  },

  "scripts": {
    "start": "node --harmony $* ./node_modules/.bin/node-dev controller.js",
  },
"devDependencies": {
  "browserstack-webdriver": "^2.41.1",
  "grunt-protractor-runner": "^1.0.0",
  "method-override": "^1.0.2",
    "morgan": "^1.1.1",
    "protractor": "^0.23.1",
    "requirejs": "^2.1.11",
    "serve-static": "^1.1.0",
},
  "author": "Andrew Luhring"

file structure:

config/
   protractor.config.js
    chromedriver

server.js
controller.js

public/
   main.js
   js/
   lib/

tests/
   e2e/
      _protractor.js    <=== test file.

views/
  layouts/
      default.hbs

  partials/
      _head.hbs
      _register.hbs   <=== file being tested.

Pretty sure that's my whole setup. Figured I'd go for verbose. Hope that helps.

What I needed to do was to:

  1. add a test-specific part to the URL, so that my bootstrap code knows whether it's running 'normally' or as a protractor test, then just for the tests
  2. call angular.bootstrap from document.ready
  3. set window name to begin with 'NG_DEFER_BOOTSTRAP!' before calling angular.bootstrap
  4. set the 'ng-app' attribute on my toplevel element
  5. set window.angular to point to angular

See https://github.com/mikaraento/protractor-reuse/blob/master/index.js

May I know why the protractor scripts which are executing perfectly well in chrome and firefox browsers cannot execute the always in Internet Explorer?

Though the same scripts are executing perfectly in other browsers, cannot execute always the same in Internet Explorer. I am getting frequent errors like time out exception as
message: 'timeout: timed out after 30000 msec waiting for spec to complete',
trace: { stack: undefined } } ]
and also elements are sometimes not getting detected in Internet Explorer.

Though I used default time interval as defaultTimeoutInterval:1000000, in my scripts I am getting the above mentioned errors while executing in Internet Explorer...

Can anyone help me out on this?

Regards,
Santhosh Chunchu

I do not see that's a problem with manual bootstrapping only? Or do you able to run manual bootstrapped apps already in Chrome and Firefox?

@hankduan Did you already have a look into the test case? Perhaps anything we can help out?

I have a CL out that changes the way bootstrapping works for protractor (https://github.com/angular/protractor/pull/1155). It's main purpose is to fix a number of other issues, but I think it should fix (or at least help) with manual bootstrap depending on how you are bootstrapping. However, I've never set up manual bootstrap to test, so this is just theoretical.

This PR requires a change in protractor and a change in Angular, so it might not be released in a short period of time.

This issue seems to be the top search result when looking for ways to manually bootstrap Angular for use with Protractor, so I figured this is as good as place as any to suggest another solution. This works for the case where your entire interface is an Angular app (rather than doing something like bootstrapping on a div after login, like above examples).

While it may be possible to delay Protractor's tests until after Angular is loaded, the official Angular docs state you can't do manual bootstrapping with Protractor. I tend to shy away from workarounds that the official documentation seems to forbid.

My Solution

I'm using Grunt to Concat/Uglify my code down to a single JS file, which is the only script referenced in index.html, with a script tag. Let's call it script.js. In Development, I run a different Grunt task, which concats together only the files I need in order to load the rest of the JS files, separately, asynchronously, and with timestamps to prevent caching. This concat'ed set of scripts replaces script.js while I'm developing. In either scenario, I'm only loading script.js from index.html, and with a script tag.

Development

So, in Development, I have to do a manual bootstrap. That's easy because I can include a single file that I don't concat into the Production JS, and I make sure to load this script after all the other JS has loaded.

bootstrap_dev.js

        .element(document.body)
        .ready(function() {
            angular.bootstrap(document.body, ['App']);
        });

Production / Protractor

In Production, I can't manually bootstrap because Protractor doesn't like it. And I'm running Protractor against my Production code. I also removed the "ng-app" attribute from my HTML, so the application doesn't bootstrap prematurely in Development, so the normal automatic bootstrap isn't occurring. I make up for this in production by putting a script before all other JS in script.js.

bootstrap_prod.js

    window.document.body.setAttribute("ng-app", "myApp");

Just replace "myApp" with the name of your main module.

Conclusion

In Development, if you do the above, you can load your scripte asynchronously and do a manual bootstrap. But if you're ok with only running your Protractor tests against production code (which can be loaded from a single file via a script tag, just add the ng-app directive before Angular is loaded so it does its automatic bootstrapping. Now you have asynchronous script loading of your various scripts in Development, and running Protractor tests for your production code.

What ended up working for me was adding, in my protractor.conf.js, the following function:

onPrepare: function() 
    {
        // implicit and page load timeouts
        browser.manage().timeouts().pageLoadTimeout(40000);
        browser.manage().timeouts().implicitlyWait(25000);

        // for non-angular page
        browser.ignoreSynchronization = true;

        // sign in before all tests
     }

If there's a better way of doing it, please let me know =]

Had similar issue. Found this useful info:

Boot process

There is no difference, how the app is boostrapped in production, but in tests protractor is doing that:

  1. Changes window.name so angular is paused before start: NG_DEFER_BOOTSTRAP! (this is angular feature!)
  2. Does mock stuff, then calls angular.resumeBoostrap() and the app actually starts.

So make sure your production code is not conflicting with this process, because angular can be boostrapped only once, second call to angular.boostrap() or angular.resumeBoostrap() will break the protractor or the app. So, ng-app in body is not needed at all, and you can call angular.boostrap() by yourself and it will be handled by protractor using NG_DEFER_BOOTSTRAP! properly.

window.name

Make sure window.name is not changed by any app code during loading & booting. In my case jquery was doing that, can't figure out why. Fully removed jquery from my project and tests worked. This is the root of the problem!

For now, I'm searching, how to bring jquery back without breaking tests, because it is needed by my project!

Finally,

our problem was in two versions of jQuery on one page (second version was loaded by external library). Removed it and left only our jQuery on page and everything works.

Some code, may be helpfull:

index.html

<body>
...
<script data-main="require.conf" type="text/javascript" src="bower_components/requirejs/require.js"></script>
</body>

requirejs config

...
 deps: ["proto/main"],
...

proto.js

// We just load deps and register the module:
 define(["deps"],function(){
   angular.module('proto'...
 }

proto/main.js

define(["require", "exports", "proto", "angular"], 
    function (require, exports) {

    angular.bootstrap(document, ['proto']);
});

and, actually, the test spec

var protractor = require('protractor');
var ptor;
function prepareBrowser() {

    ptor = protractor.getInstance();

    return ptor.get(ptor["baseUrl"] + '/login').then(function () {
        console.log("got");       
        return ptor.waitForAngular().then(function () {
            console.log("ngwaited");
        });
    });
}
describe('container', function () {
    beforeEach(function () {
        return prepareBrowser();
    });
    describe('form tests', function () {
        it('form is shown', function () {
            expect($('[ng-controller=LoginContr]').isDisplayed()).toBeTruthy();
        });

    });
});

Output

Using the selenium server at http://localhost:4444/wd/hub
got
ngwaited
container
  form tests
    form is shown - pass


Finished in 3.112 seconds
1 test, 1 assertion, 0 failures

Horay!

When we switched from using ng-app to angular.bootstrap(), adding rootElement: 'div' to projotractor.conf was the only change we needed to get protractor working again. We are using angular 1.3.0 and protractor 1.3.1.

@jongunnip Yep that worked for me too, thanks!

@mikaraento It worked for me, thanks~

Hi guys, strange thing relevant for this thread.

I have a regular angular app - no requirejs or anything, I am using protractor 2.0.0.

When I run the system tests on staging environment it works.
When I run it locally it gives me :
org.openqa.selenium.WebDriverException: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element ... message

When I configure an Nginx server to serve the static resource instead of grunt:server everything works.

Does it really matter if I run it with livereload in development?

Seems like static servers behave differently, and grunt serves some 404s or redirects, so webdriver goes crazy.

Hi all,

after a lot of time reading and reading, I found one solution that works well to my project (fix protractor execution flow)

  1. when open the app to protractor add ?protractor-test to URL (project code)
  2. change the angular bootstrap code to be like the bellow (project code)
if(location.href.indexOf("protractor-test") < 0){
  // start angular app
  angular.bootstrap(document, [module.name]);
} else {
  // start angular app to protractor tests
  window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
  angular.bootstrap(document, [module.name]);
}

and finally all back to work again \o/

with copy @juliemr

I recently(just 1-2 weeks) started using Protractor to test my Angular application. My application has ng-App defined in body tag on Login page. My Test keep complaining saying "Angular could not be found on the page httP://(WebSite Address) : angular never provided resumeBootstrap" But test execution is fine. Not sure about the issue ( if i turn on browser ignoresync parameter then it is working fine , but this is not correct way ), I can try what Juliemr suggesting ( use legacy driver to navigate ), but just trying to understand why my app complaining even though it has ng-App in body tag. App Functional Behavior is like this.. Login Page..Once Log In click on some link on Home Page and it opens another Window/Tab. Could someone help me in finding steps what all need to verify to make sure bootstraping is done automatic way ( one is ng-app parameter in body tag ) ? Do i need to check something in bootstrap.js file ( I do not see any ng-app parameter in bootstrap file hence checking. Thank you

Is there any updates on this issue? What are the best practices or methods for configuring protractor to work on with an angular app that doesnt have an ng-app? @erkobridee could you explain your example further? And can someone explain how to properly handle window.name in regards to how protractor should wait for when angular is done loading?

@juliemr With Angular 1.5, manually bootstrapping is the preferred way to bootstrap. Is there blessed protractor support on the way?

+1 I think this issue should be higher priority. If Protractor does not work with manual bootstrap, then there's no benefit of using Protractor. The whole point of using Protractor over Webdriver is that Protractor handles all the angular sync for you.

I have the same issue. Any update?

hi, is there any plan or process to improve this ?

Strange there is no feedback. I have apps missing ng-app and I can not test
them with protractor

On 16 Jul 2016 5:01 am, "millerwx" [email protected] wrote:

hi, is there any plan or process to improve this ?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/angular/protractor/issues/66#issuecomment-233104701,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AOghFs2ff0e-88DLafNGD3iCH8uCOgVaks5qWEmIgaJpZM4A82d7
.

To this date in 2016, this was still a problem for me. I followed Magnetized answer to solve the issue. I just did a few changes. What is nice and out of scope with the conversation is to track also redirects and land back inside your app. *I did it with Babel, but you can write it in ES5 instead.

`describe('login screen', ()=>{

var urlTimeout = 9000;

const waitForUrlRegex = (regex, urlMatcher )=>{

    return driver.wait(()=>{
        return driver.getCurrentUrl().then( (url)=>{
            return regex.test(url);
        });
    }, urlTimeout, "Expectation error: Timeout for url not matching " + urlMatcher );
};

const waitForRoute = (route)=>{
    return waitForUrlRegex( new RegExp( route + "$" ), route  );
}

const waitForUrl = (url)=>{
    return waitForUrlRegex( new RegExp( "^https?:\/\/" + url ), url );
}

it('should go to homepage and click login button', ()=>{

    driver.get( app_url, urlTimeout);

    //there is a redirect to welcome route
    expect( waitForRoute("welcome") ).toBe(true);

    const loginButton = element.all(by.className("login_button")).get(0);
    loginButton.click();
});

it( 'should enter credentials', ()=>{
    expect( waitForUrl( non_angular_login_url ) ).toBe(true);
    driver.findElement( by.name("user")).sendKeys( "username");
    driver.findElement( by.name("password")).sendKeys( "userpassword");
    driver.findElement( by.tagName("button")).click();
});

it( 'should go back to app, and continue testing', ()=>{
    expect( waitForRoute( "user-screen") ).toBe(true);
});

});`

config.onPrepare

onPrepare: ()=>{ require("babel-register"); global.driver = browser.driver; }

In case you don't want to look into redirects, simply have the driver sleep and then start working with protractor.

`driver.get( app_url+ "/#/welcome", urlTimeout);

    driver.sleep( 1000 );
    const loginButton = element.all(by.className("login_button")).get(0);
    loginButton.click();`

We do actually support manual bootstrapping, this information is old. Bootstrapping will become even more reliable once we handle https://github.com/angular/protractor/issues/3857 as well.

@sjelin What's the documented way to support manual bootstrapping? I can't find the relevant information for protractor 5.0.

I was facing the same issue while manual bootstrapping. and found the solution to run with grunt.
used protractor version 2.5.1.

  1. Mention this in your grunt file.

grunt.initConfig({ protractor: { options: { configFile: "protractor.conf.js", // Default protractor config file keepAlive: true, // If false, the grunt process stops when the test fails. noColor: false, // If true, protractor will not use colors in its output. args: { // Arguments passed to the command } }, e2e: { options: { // Stops Grunt process if a test fails keepAlive: true } } } })

  1. Install npm install grunt-protractor-runner --save-dev and add for load task grunt.loadNpmTasks('grunt-protractor-runner')

  2. Run protractor with following command :
    grunt protractor
    or with suite name
    grunt protractor --suite suite_name

Please let me know if it work for you.

This worked for me.
browser.ignoreSynchronization = true;
browser.get(url);
browser.ignoreSynchronization = false;

Was this page helpful?
0 / 5 - 0 ratings