1616/* global angular */
1717var functions = { } ;
1818
19+ ///////////////////////////////////////////////////////
20+ //// ////
21+ //// HELPERS ////
22+ //// ////
23+ ///////////////////////////////////////////////////////
24+
25+
1926/* Wraps a function up into a string with its helper functions so that it can
2027 * call those helper functions client side
2128 *
@@ -36,6 +43,84 @@ function wrapWithHelpers(fun) {
3643 ' return (' + fun . toString ( ) + ').apply(this, arguments);' ) ;
3744}
3845
46+ /* Tests if an ngRepeat matches a repeater
47+ *
48+ * @param {string } ngRepeat The ngRepeat to test
49+ * @param {string } repeater The repeater to test against
50+ * @param {boolean } exact If the ngRepeat expression needs to match the whole
51+ * repeater (not counting any `track by ...` modifier) or if it just needs to
52+ * match a substring
53+ * @return {boolean } If the ngRepeat matched the repeater
54+ */
55+ function repeaterMatch ( ngRepeat , repeater , exact ) {
56+ if ( exact ) {
57+ return ngRepeat . split ( ' track by ' ) [ 0 ] . split ( ' as ' ) [ 0 ] . split ( '|' ) [ 0 ] .
58+ split ( '=' ) [ 0 ] . trim ( ) == repeater ;
59+ } else {
60+ return ngRepeat . indexOf ( repeater ) != - 1 ;
61+ }
62+ }
63+
64+ /* Tries to find $$testability and possibly $injector for an ng1 app
65+ *
66+ * By default, doesn't care about $injector if it finds $$testability. However,
67+ * these priorities can be reversed.
68+ *
69+ * @param {string= } selector The selector for the element with the injector. If
70+ * falsy, tries a variety of methods to find an injector
71+ * @param {boolean= } injectorPlease Prioritize finding an injector
72+ * @return {$$testability?: Testability, $injector?: Injector } Returns whatever
73+ * ng1 app hooks it finds
74+ */
75+ function getNg1Hooks ( selector , injectorPlease ) {
76+ function tryEl ( el ) {
77+ try {
78+ if ( ! injectorPlease && angular . getTestability ) {
79+ var $$testability = angular . getTestability ( el ) ;
80+ if ( $$testability ) {
81+ return { $$testability : $$testability } ;
82+ }
83+ } else {
84+ var $injector = angular . element ( el ) . injector ( ) ;
85+ if ( $injector ) {
86+ return { $injector : $injector } ;
87+ }
88+ }
89+ } catch ( err ) { }
90+ }
91+ function trySelector ( selector ) {
92+ var els = document . querySelectorAll ( selector ) ;
93+ for ( var i = 0 ; i < els . length ; i ++ ) {
94+ var elHooks = tryEl ( els [ i ] ) ;
95+ if ( elHooks ) {
96+ return elHooks ;
97+ }
98+ }
99+ }
100+
101+ if ( selector ) {
102+ return trySelector ( selector ) ;
103+ } else if ( window . __TESTABILITY__NG1_APP_ROOT_INJECTOR__ ) {
104+ var $injector = window . __TESTABILITY__NG1_APP_ROOT_INJECTOR__ ;
105+ var $$testability = null ;
106+ try {
107+ $$testability = $injector . get ( '$$testability' ) ;
108+ } catch ( e ) { }
109+ return { $injector : $injector , $$testability : $$testability } ;
110+ } else {
111+ return tryEl ( document . body ) ||
112+ trySelector ( '[ng-app]' ) || trySelector ( '[ng:app]' ) ||
113+ trySelector ( '[ng-controller]' ) || trySelector ( '[ng:controller]' ) ;
114+ }
115+ }
116+
117+ ///////////////////////////////////////////////////////
118+ //// ////
119+ //// SCRIPTS ////
120+ //// ////
121+ ///////////////////////////////////////////////////////
122+
123+
39124/**
40125 * Wait until Angular has finished rendering and has
41126 * no outstanding $http calls before continuing. The specific Angular app
@@ -48,22 +133,38 @@ function wrapWithHelpers(fun) {
48133 * be passed as a parameter.
49134 */
50135functions . waitForAngular = function ( rootSelector , callback ) {
51- var el = document . querySelector ( rootSelector ) ;
52-
53136 try {
54137 if ( window . angular && ! ( window . angular . version &&
55- window . angular . version . major > 1 ) ) {
56- if ( angular . getTestability ) {
57- angular . getTestability ( el ) . whenStable ( callback ) ;
58- } else if ( angular . element ( el ) . injector ( ) ) {
59- angular . element ( el ) . injector ( ) . get ( '$browser' ) .
138+ window . angular . version . major > 1 ) ) {
139+ /* ng1 */
140+ let hooks = getNg1Hooks ( rootSelector ) ;
141+ if ( hooks . $$testability ) {
142+ hooks . $$testability . whenStable ( callback ) ;
143+ } else if ( hooks . $injector ) {
144+ hooks . $injector . get ( '$browser' ) .
60145 notifyWhenNoOutstandingRequests ( callback ) ;
146+ } else if ( ! ! rootSelector ) {
147+ throw new Error ( 'Could not automatically find injector on page: "' +
148+ window . location . toString ( ) + '". Consider using config.rootEl' ) ;
61149 } else {
62150 throw new Error ( 'root element (' + rootSelector + ') has no injector.' +
63151 ' this may mean it is not inside ng-app.' ) ;
64152 }
65- } else if ( window . getAngularTestability ) {
153+ } else if ( rootSelector && window . getAngularTestability ) {
154+ var el = document . querySelector ( rootSelector ) ;
66155 window . getAngularTestability ( el ) . whenStable ( callback ) ;
156+ } else if ( window . getAllAngularTestabilities ) {
157+ var testabilities = window . getAllAngularTestabilities ( ) ;
158+ var count = testabilities . length ;
159+ var decrement = function ( ) {
160+ count -- ;
161+ if ( count === 0 ) {
162+ callback ( ) ;
163+ }
164+ } ;
165+ testabilities . forEach ( function ( testability ) {
166+ testability . whenStable ( decrement ) ;
167+ } ) ;
67168 } else if ( ! window . angular ) {
68169 throw new Error ( 'window.angular is undefined. This could be either ' +
69170 'because this is a non-angular page or because your test involves ' +
@@ -75,39 +176,13 @@ functions.waitForAngular = function(rootSelector, callback) {
75176 'obfuscation.' ) ;
76177 } else {
77178 throw new Error ( 'Cannot get testability API for unknown angular ' +
78- 'version "' + window . angular . version + '"' ) ;
179+ 'version "' + window . angular . version + '"' ) ;
79180 }
80181 } catch ( err ) {
81182 callback ( err . message ) ;
82183 }
83184} ;
84185
85- /**
86- * Wait until all Angular2 applications on the page have become stable.
87- *
88- * Asynchronous.
89- *
90- * @param {function(string) } callback callback. If a failure occurs, it will
91- * be passed as a parameter.
92- */
93- functions . waitForAllAngular2 = function ( callback ) {
94- try {
95- var testabilities = window . getAllAngularTestabilities ( ) ;
96- var count = testabilities . length ;
97- var decrement = function ( ) {
98- count -- ;
99- if ( count === 0 ) {
100- callback ( ) ;
101- }
102- } ;
103- testabilities . forEach ( function ( testability ) {
104- testability . whenStable ( decrement ) ;
105- } ) ;
106- } catch ( err ) {
107- callback ( err . message ) ;
108- }
109- } ;
110-
111186/**
112187 * Find a list of elements in the page by their angular binding.
113188 *
@@ -119,10 +194,9 @@ functions.waitForAllAngular2 = function(callback) {
119194 * @return {Array.<Element> } The elements containing the binding.
120195 */
121196functions . findBindings = function ( binding , exactMatch , using , rootSelector ) {
122- var root = document . querySelector ( rootSelector || 'body' ) ;
123197 using = using || document ;
124198 if ( angular . getTestability ) {
125- return angular . getTestability ( root ) .
199+ return getNg1Hooks ( rootSelector ) . $$testability .
126200 findBindings ( using , binding , exactMatch ) ;
127201 }
128202 var bindings = using . getElementsByClassName ( 'ng-binding' ) ;
@@ -150,15 +224,6 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
150224 return matches ; /* Return the whole array for webdriver.findElements. */
151225} ;
152226
153- function repeaterMatch ( ngRepeat , repeater , exact ) {
154- if ( exact ) {
155- return ngRepeat . split ( ' track by ' ) [ 0 ] . split ( ' as ' ) [ 0 ] . split ( '|' ) [ 0 ] .
156- split ( '=' ) [ 0 ] . trim ( ) == repeater ;
157- } else {
158- return ngRepeat . indexOf ( repeater ) != - 1 ;
159- }
160- }
161-
162227/**
163228 * Find an array of elements matching a row within an ng-repeat.
164229 * Always returns an array of only one element for plain old ng-repeat.
@@ -273,7 +338,6 @@ functions.findAllRepeaterRows = wrapWithHelpers(findAllRepeaterRows, repeaterMat
273338 */
274339function findRepeaterElement ( repeater , exact , index , binding , using , rootSelector ) {
275340 var matches = [ ] ;
276- var root = document . querySelector ( rootSelector || 'body' ) ;
277341 using = using || document ;
278342
279343 var rows = [ ] ;
@@ -317,7 +381,7 @@ function findRepeaterElement(repeater, exact, index, binding, using, rootSelecto
317381 if ( angular . getTestability ) {
318382 matches . push . apply (
319383 matches ,
320- angular . getTestability ( root ) . findBindings ( row , binding ) ) ;
384+ getNg1Hooks ( rootSelector ) . $$testability . findBindings ( row , binding ) ) ;
321385 } else {
322386 if ( row . className . indexOf ( 'ng-binding' ) != - 1 ) {
323387 bindings . push ( row ) ;
@@ -334,7 +398,8 @@ function findRepeaterElement(repeater, exact, index, binding, using, rootSelecto
334398 if ( angular . getTestability ) {
335399 matches . push . apply (
336400 matches ,
337- angular . getTestability ( root ) . findBindings ( rowElem , binding ) ) ;
401+ getNg1Hooks ( rootSelector ) . $$testability . findBindings ( rowElem ,
402+ binding ) ) ;
338403 } else {
339404 if ( rowElem . className . indexOf ( 'ng-binding' ) != - 1 ) {
340405 bindings . push ( rowElem ) ;
@@ -357,7 +422,8 @@ function findRepeaterElement(repeater, exact, index, binding, using, rootSelecto
357422 }
358423 return matches ;
359424}
360- functions . findRepeaterElement = wrapWithHelpers ( findRepeaterElement , repeaterMatch ) ;
425+ functions . findRepeaterElement =
426+ wrapWithHelpers ( findRepeaterElement , repeaterMatch , getNg1Hooks ) ;
361427
362428/**
363429 * Find the elements in a column of an ng-repeat.
@@ -372,7 +438,6 @@ functions.findRepeaterElement = wrapWithHelpers(findRepeaterElement, repeaterMat
372438 */
373439function findRepeaterColumn ( repeater , exact , binding , using , rootSelector ) {
374440 var matches = [ ] ;
375- var root = document . querySelector ( rootSelector || 'body' ) ;
376441 using = using || document ;
377442
378443 var rows = [ ] ;
@@ -414,7 +479,8 @@ function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
414479 if ( angular . getTestability ) {
415480 matches . push . apply (
416481 matches ,
417- angular . getTestability ( root ) . findBindings ( rows [ i ] , binding ) ) ;
482+ getNg1Hooks ( rootSelector ) . $$testability . findBindings ( rows [ i ] ,
483+ binding ) ) ;
418484 } else {
419485 if ( rows [ i ] . className . indexOf ( 'ng-binding' ) != - 1 ) {
420486 bindings . push ( rows [ i ] ) ;
@@ -430,7 +496,8 @@ function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
430496 if ( angular . getTestability ) {
431497 matches . push . apply (
432498 matches ,
433- angular . getTestability ( root ) . findBindings ( multiRows [ i ] [ j ] , binding ) ) ;
499+ getNg1Hooks ( rootSelector ) . $$testability . findBindings (
500+ multiRows [ i ] [ j ] , binding ) ) ;
434501 } else {
435502 var elem = multiRows [ i ] [ j ] ;
436503 if ( elem . className . indexOf ( 'ng-binding' ) != - 1 ) {
@@ -454,7 +521,8 @@ function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
454521 }
455522 return matches ;
456523}
457- functions . findRepeaterColumn = wrapWithHelpers ( findRepeaterColumn , repeaterMatch ) ;
524+ functions . findRepeaterColumn =
525+ wrapWithHelpers ( findRepeaterColumn , repeaterMatch , getNg1Hooks ) ;
458526
459527/**
460528 * Find elements by model name.
@@ -466,11 +534,10 @@ functions.findRepeaterColumn = wrapWithHelpers(findRepeaterColumn, repeaterMatch
466534 * @return {Array.<Element> } The matching elements.
467535 */
468536functions . findByModel = function ( model , using , rootSelector ) {
469- var root = document . querySelector ( rootSelector || 'body' ) ;
470537 using = using || document ;
471538
472539 if ( angular . getTestability ) {
473- return angular . getTestability ( root ) .
540+ return getNg1Hooks ( rootSelector ) . $$testability .
474541 findModels ( using , model , true ) ;
475542 }
476543 var prefixes = [ 'ng-' , 'ng_' , 'data-ng-' , 'x-ng-' , 'ng\\:' ] ;
@@ -677,12 +744,11 @@ functions.allowAnimations = function(element, value) {
677744 * @param {string } selector The selector housing an ng-app
678745 */
679746functions . getLocationAbsUrl = function ( selector ) {
680- var el = document . querySelector ( selector ) ;
747+ var hooks = getNg1Hooks ( selector ) ;
681748 if ( angular . getTestability ) {
682- return angular . getTestability ( el ) .
683- getLocation ( ) ;
749+ return hooks . $$testability . getLocation ( ) ;
684750 }
685- return angular . element ( el ) . injector ( ) . get ( '$location' ) . absUrl ( ) ;
751+ return hooks . $ injector. get ( '$location' ) . absUrl ( ) ;
686752} ;
687753
688754/**
@@ -693,12 +759,11 @@ functions.getLocationAbsUrl = function(selector) {
693759 * /path?search=a&b=c#hash
694760 */
695761functions . setLocation = function ( selector , url ) {
696- var el = document . querySelector ( selector ) ;
762+ var hooks = getNg1Hooks ( selector ) ;
697763 if ( angular . getTestability ) {
698- return angular . getTestability ( el ) .
699- setLocation ( url ) ;
764+ return hooks . $$testability . setLocation ( url ) ;
700765 }
701- var $injector = angular . element ( el ) . injector ( ) ;
766+ var $injector = hooks . $ injector;
702767 var $location = $injector . get ( '$location' ) ;
703768 var $rootScope = $injector . get ( '$rootScope' ) ;
704769
@@ -715,12 +780,16 @@ functions.setLocation = function(selector, url) {
715780 * @return {!Array<!Object> } An array of pending http requests.
716781 */
717782functions . getPendingHttpRequests = function ( selector ) {
718- var el = document . querySelector ( selector ) ;
719- var $injector = angular . element ( el ) . injector ( ) ;
720- var $http = $injector . get ( '$http' ) ;
783+ var hooks = getNg1Hooks ( selector , true ) ;
784+ var $http = hooks . $injector . get ( '$http' ) ;
721785 return $http . pendingRequests ;
722786} ;
723787
788+ [ 'waitForAngular' , 'findBindings' , 'findByModel' , 'getLocationAbsUrl' ,
789+ 'setLocation' , 'getPendingHttpRequests' ] . forEach ( function ( funName ) {
790+ functions [ funName ] = wrapWithHelpers ( functions [ funName ] , getNg1Hooks ) ;
791+ } ) ;
792+
724793/* Publish all the functions as strings to pass to WebDriver's
725794 * exec[Async]Script. In addition, also include a script that will
726795 * install all the functions on window (for debugging.)
0 commit comments