wait.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /**
  2. * Copied from Protractor 5.2.0
  3. *
  4. * Wait until Angular has finished rendering and has
  5. * no outstanding $http calls before continuing. The specific Angular app
  6. * is determined by the rootSelector.
  7. *
  8. * Asynchronous.
  9. *
  10. * @param {string} rootSelector The selector housing an ng-app
  11. * @param {function(string)} callback callback. If a failure occurs, it will
  12. * be passed as a parameter.
  13. */
  14. function waitForAngular(rootSelector, callback) {
  15. try {
  16. // Wait for both angular1 testability and angular2 testability.
  17. var testCallback = callback;
  18. // Wait for angular1 testability first and run waitForAngular2 as a callback
  19. var waitForAngular1 = function(callback) {
  20. if (window.angular) {
  21. var hooks = getNg1Hooks(rootSelector);
  22. if (!hooks){
  23. callback(); // not an angular1 app
  24. }
  25. else{
  26. if (hooks.$$testability) {
  27. hooks.$$testability.whenStable(callback);
  28. } else if (hooks.$injector) {
  29. hooks.$injector.get('$browser')
  30. .notifyWhenNoOutstandingRequests(callback);
  31. } else if (!!rootSelector) {
  32. throw new Error(
  33. 'Could not automatically find injector on page: "' +
  34. window.location.toString() + '". Consider using config.rootEl');
  35. } else {
  36. throw new Error(
  37. 'root element (' + rootSelector + ') has no injector.' +
  38. ' this may mean it is not inside ng-app.');
  39. }
  40. }
  41. }
  42. else {callback();} // not an angular1 app
  43. };
  44. // Wait for Angular2 testability and then run test callback
  45. var waitForAngular2 = function() {
  46. if (window.getAngularTestability) {
  47. if (rootSelector) {
  48. var testability = null;
  49. var el = document.querySelector(rootSelector);
  50. try{
  51. testability = window.getAngularTestability(el);
  52. }
  53. catch(e){}
  54. if (testability) {
  55. return testability.whenStable(testCallback);
  56. }
  57. }
  58. // Didn't specify root element or testability could not be found
  59. // by rootSelector. This may happen in a hybrid app, which could have
  60. // more than one root.
  61. var testabilities = window.getAllAngularTestabilities();
  62. var count = testabilities.length;
  63. // No angular2 testability, this happens when
  64. // going to a hybrid page and going back to a pure angular1 page
  65. if (count === 0) {
  66. return testCallback();
  67. }
  68. var decrement = function() {
  69. count--;
  70. if (count === 0) {
  71. testCallback();
  72. }
  73. };
  74. testabilities.forEach(function(testability) {
  75. testability.whenStable(decrement);
  76. });
  77. }
  78. else {testCallback();} // not an angular2 app
  79. };
  80. if (!(window.angular) && !(window.getAngularTestability)) {
  81. // no testability hook
  82. throw new Error(
  83. 'both angularJS testability and angular testability are undefined.' +
  84. ' This could be either ' +
  85. 'because this is a non-angular page or because your test involves ' +
  86. 'client-side navigation, which can interfere with Protractor\'s ' +
  87. 'bootstrapping. See http://git.io/v4gXM for details');
  88. } else {waitForAngular1(waitForAngular2);} // Wait for angular1 and angular2
  89. // Testability hooks sequentially
  90. } catch (err) {
  91. callback(err.message);
  92. }
  93. };
  94. /* Tries to find $$testability and possibly $injector for an ng1 app
  95. *
  96. * By default, doesn't care about $injector if it finds $$testability. However,
  97. * these priorities can be reversed.
  98. *
  99. * @param {string=} selector The selector for the element with the injector. If
  100. * falsy, tries a variety of methods to find an injector
  101. * @param {boolean=} injectorPlease Prioritize finding an injector
  102. * @return {$$testability?: Testability, $injector?: Injector} Returns whatever
  103. * ng1 app hooks it finds
  104. */
  105. function getNg1Hooks(selector, injectorPlease) {
  106. function tryEl(el) {
  107. try {
  108. if (!injectorPlease && angular.getTestability) {
  109. var $$testability = angular.getTestability(el);
  110. if ($$testability) {
  111. return {$$testability: $$testability};
  112. }
  113. } else {
  114. var $injector = angular.element(el).injector();
  115. if ($injector) {
  116. return {$injector: $injector};
  117. }
  118. }
  119. } catch(err) {}
  120. }
  121. function trySelector(selector) {
  122. var els = document.querySelectorAll(selector);
  123. for (var i = 0; i < els.length; i++) {
  124. var elHooks = tryEl(els[i]);
  125. if (elHooks) {
  126. return elHooks;
  127. }
  128. }
  129. }
  130. if (selector) {
  131. return trySelector(selector);
  132. } else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {
  133. var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;
  134. var $$testability = null;
  135. try {
  136. $$testability = $injector.get('$$testability');
  137. } catch (e) {}
  138. return {$injector: $injector, $$testability: $$testability};
  139. } else {
  140. return tryEl(document.body) ||
  141. trySelector('[ng-app]') || trySelector('[ng\\:app]') ||
  142. trySelector('[ng-controller]') || trySelector('[ng\\:controller]');
  143. }
  144. }
  145. /* Wraps a function up into a string with its helper functions so that it can
  146. * call those helper functions client side
  147. *
  148. * @param {function} fun The function to wrap up with its helpers
  149. * @param {...function} The helper functions. Each function must be named
  150. *
  151. * @return {string} The string which, when executed, will invoke fun in such a
  152. * way that it has access to its helper functions
  153. */
  154. function wrapWithHelpers(fun) {
  155. var helpers = Array.prototype.slice.call(arguments, 1);
  156. if (!helpers.length) {
  157. return fun;
  158. }
  159. var FunClass = Function; // Get the linter to allow this eval
  160. return new FunClass(
  161. helpers.join(';') + String.fromCharCode(59) +
  162. ' return (' + fun.toString() + ').apply(this, arguments);');
  163. }
  164. exports.NG_WAIT_FN = wrapWithHelpers(waitForAngular, getNg1Hooks);