180 lines
6.1 KiB
JavaScript
Executable file
180 lines
6.1 KiB
JavaScript
Executable file
/**
|
|
* Copied from Protractor 5.2.0
|
|
*
|
|
* Wait until Angular has finished rendering and has
|
|
* no outstanding $http calls before continuing. The specific Angular app
|
|
* is determined by the rootSelector.
|
|
*
|
|
* Asynchronous.
|
|
*
|
|
* @param {string} rootSelector The selector housing an ng-app
|
|
* @param {function(string)} callback callback. If a failure occurs, it will
|
|
* be passed as a parameter.
|
|
*/
|
|
function waitForAngular(rootSelector, callback) {
|
|
|
|
try {
|
|
// Wait for both angular1 testability and angular2 testability.
|
|
|
|
var testCallback = callback;
|
|
|
|
// Wait for angular1 testability first and run waitForAngular2 as a callback
|
|
var waitForAngular1 = function(callback) {
|
|
|
|
if (window.angular) {
|
|
var hooks = getNg1Hooks(rootSelector);
|
|
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) {
|
|
return testability.whenStable(testCallback);
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
return testCallback();
|
|
}
|
|
|
|
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
|
|
|
|
} catch (err) {
|
|
callback(err.message);
|
|
}
|
|
|
|
};
|
|
|
|
/* Tries to find $$testability and possibly $injector for an ng1 app
|
|
*
|
|
* By default, doesn't care about $injector if it finds $$testability. However,
|
|
* these priorities can be reversed.
|
|
*
|
|
* @param {string=} selector The selector for the element with the injector. If
|
|
* falsy, tries a variety of methods to find an injector
|
|
* @param {boolean=} injectorPlease Prioritize finding an injector
|
|
* @return {$$testability?: Testability, $injector?: Injector} Returns whatever
|
|
* ng1 app hooks it finds
|
|
*/
|
|
function getNg1Hooks(selector, injectorPlease) {
|
|
function tryEl(el) {
|
|
try {
|
|
if (!injectorPlease && angular.getTestability) {
|
|
var $$testability = angular.getTestability(el);
|
|
if ($$testability) {
|
|
return {$$testability: $$testability};
|
|
}
|
|
} else {
|
|
var $injector = angular.element(el).injector();
|
|
if ($injector) {
|
|
return {$injector: $injector};
|
|
}
|
|
}
|
|
} catch(err) {}
|
|
}
|
|
function trySelector(selector) {
|
|
var els = document.querySelectorAll(selector);
|
|
for (var i = 0; i < els.length; i++) {
|
|
var elHooks = tryEl(els[i]);
|
|
if (elHooks) {
|
|
return elHooks;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selector) {
|
|
return trySelector(selector);
|
|
} else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {
|
|
var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;
|
|
var $$testability = null;
|
|
try {
|
|
$$testability = $injector.get('$$testability');
|
|
} catch (e) {}
|
|
return {$injector: $injector, $$testability: $$testability};
|
|
} else {
|
|
return tryEl(document.body) ||
|
|
trySelector('[ng-app]') || trySelector('[ng\\:app]') ||
|
|
trySelector('[ng-controller]') || trySelector('[ng\\:controller]');
|
|
}
|
|
}
|
|
|
|
/* Wraps a function up into a string with its helper functions so that it can
|
|
* call those helper functions client side
|
|
*
|
|
* @param {function} fun The function to wrap up with its helpers
|
|
* @param {...function} The helper functions. Each function must be named
|
|
*
|
|
* @return {string} The string which, when executed, will invoke fun in such a
|
|
* way that it has access to its helper functions
|
|
*/
|
|
function wrapWithHelpers(fun) {
|
|
var helpers = Array.prototype.slice.call(arguments, 1);
|
|
if (!helpers.length) {
|
|
return fun;
|
|
}
|
|
var FunClass = Function; // Get the linter to allow this eval
|
|
return new FunClass(
|
|
helpers.join(';') + String.fromCharCode(59) +
|
|
' return (' + fun.toString() + ').apply(this, arguments);');
|
|
}
|
|
|
|
exports.NG_WAIT_FN = wrapWithHelpers(waitForAngular, getNg1Hooks);
|