I have a Protractor test that initiates a reloading of the same Angular app. Occasionally it fails in a Travis build, but will pass after one or two restarts of the build. Rarely it fails on my local machine. The Travis builds use Firefox, but the times it has failed on my machine have been using both Firefox and Selenium's chromedriver.
I've set rootElement: 'html' (as recommended in this GitHub issue on Protractor) because that is where the ng-app is, but I still get this error:
Error while waiting for Protractor to sync with the page: "root element (html) has no injector. this may mean it is not inside ng-app."
The error is the result of angular.element('html').injector() returning falsey, even though Angular has loaded. A previous test in the Protractor code confirms this.
The test involves being redirected to a different index.html that happens to be the same angular app. Reason for the redirect is to offer a language choice then reload the app in that language.
There are multiple locales (two shown here for simplicity). app is the document root, so app/index.html is the entry point, which detects the system language or defaults to en-gb and then redirects to app/<locale>/index.html. The former is not an Angular app (app/); the latter is (app/<locale>/) and links to ../common/ for library and Angular module files. The index.html files in the locale folders are identical.
What happens when you visit the document root?
/ -> redirect /en-gb/#/language
And when you choose the German language?
/en-gb/#/language redirect ../de/#/menu -> /de/#/menu
Directory structure:
└── app
├── common
│ ├── fonts
│ ├── images
│ ├── lib
│ ├── modules
│ └── styles
├── de
│ ├── i18n.js
│ └── index.html
├── en-gb
│ ├── i18n.js
│ └── index.html
└── index.html
Test:
(function () {
'use strict';
describe('Module: app, language screen:', function () {
beforeEach(function () {
browser.get('http://localhost:9000/en-gb/#/language/');
this.$germanChoice = // get the clickable element to select German
this.$buttonContinue = // get the clickable element to continue
});
describe('continue button', function () {
it('should go forward to the menu', function () {
this.$germanChoice.click();
this.$buttonContinue.click();
expect(browser.getCurrentUrl()).toBe('http://localhost:9000/de/#/menu/');
});
});
});
}());
Locale index.html using minified files:
<!doctype html>
<!--[if IE 8]> <html id="ng-app" ng-app="app" lang="{{ app.locale.html }}" class="no-js lt-ie10 lt-ie9"> <![endif]-->
<!--[if IE 9]> <html id="ng-app" ng-app="app" lang="{{ app.locale.html }}" class="no-js lt-ie10"> <![endif]-->
<!--[if gt IE 9]><!--> <html id="ng-app" ng-app="app" lang="{{ app.locale.html }}" class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title ng-bind="app.page.title ? app.page.title + ' - ' + app.page.titleBase : app.page.titleBase"></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../common/lib/nouislider/jquery.nouislider.css" />
<!--[if gt IE 8]><!-->
<link rel="stylesheet" href="../common/styles/b8a085fb.main.css">
<!--<![endif]-->
<!--[if lt IE 9]>
<link rel="stylesheet" href="../common/styles/f71e8123.ie.css">
<![endif]-->
<!--[if lt IE 9]>
<script src="../common/scripts/a5663f12.lt-ie9.js"></script>
<![endif]-->
</head>
<body ng-class="{ 'ios6': app.environment.ios6, 'gt-ios6': app.environment.gtIos6, 'cordova': app.environment.cordova }">
<div ng-view="" autoscroll="true"></div>
<script src="../common/scripts/cbd2241e.app.js"></script>
<script src="i18n.js"></script>
</body>
</html>