16
16
/* global angular */
17
17
var functions = { } ;
18
18
19
+ ///////////////////////////////////////////////////////
20
+ //// ////
21
+ //// HELPERS ////
22
+ //// ////
23
+ ///////////////////////////////////////////////////////
24
+
25
+
19
26
/* Wraps a function up into a string with its helper functions so that it can
20
27
* call those helper functions client side
21
28
*
@@ -36,6 +43,84 @@ function wrapWithHelpers(fun) {
36
43
' return (' + fun . toString ( ) + ').apply(this, arguments);' ) ;
37
44
}
38
45
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
+
39
124
/**
40
125
* Wait until Angular has finished rendering and has
41
126
* no outstanding $http calls before continuing. The specific Angular app
@@ -48,22 +133,38 @@ function wrapWithHelpers(fun) {
48
133
* be passed as a parameter.
49
134
*/
50
135
functions . waitForAngular = function ( rootSelector , callback ) {
51
- var el = document . querySelector ( rootSelector ) ;
52
-
53
136
try {
54
137
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' ) .
60
145
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' ) ;
61
149
} else {
62
150
throw new Error ( 'root element (' + rootSelector + ') has no injector.' +
63
151
' this may mean it is not inside ng-app.' ) ;
64
152
}
65
- } else if ( window . getAngularTestability ) {
153
+ } else if ( rootSelector && window . getAngularTestability ) {
154
+ var el = document . querySelector ( rootSelector ) ;
66
155
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
+ } ) ;
67
168
} else if ( ! window . angular ) {
68
169
throw new Error ( 'window.angular is undefined. This could be either ' +
69
170
'because this is a non-angular page or because your test involves ' +
@@ -75,39 +176,13 @@ functions.waitForAngular = function(rootSelector, callback) {
75
176
'obfuscation.' ) ;
76
177
} else {
77
178
throw new Error ( 'Cannot get testability API for unknown angular ' +
78
- 'version "' + window . angular . version + '"' ) ;
179
+ 'version "' + window . angular . version + '"' ) ;
79
180
}
80
181
} catch ( err ) {
81
182
callback ( err . message ) ;
82
183
}
83
184
} ;
84
185
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
-
111
186
/**
112
187
* Find a list of elements in the page by their angular binding.
113
188
*
@@ -119,10 +194,9 @@ functions.waitForAllAngular2 = function(callback) {
119
194
* @return {Array.<Element> } The elements containing the binding.
120
195
*/
121
196
functions . findBindings = function ( binding , exactMatch , using , rootSelector ) {
122
- var root = document . querySelector ( rootSelector || 'body' ) ;
123
197
using = using || document ;
124
198
if ( angular . getTestability ) {
125
- return angular . getTestability ( root ) .
199
+ return getNg1Hooks ( rootSelector ) . $$testability .
126
200
findBindings ( using , binding , exactMatch ) ;
127
201
}
128
202
var bindings = using . getElementsByClassName ( 'ng-binding' ) ;
@@ -150,15 +224,6 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
150
224
return matches ; /* Return the whole array for webdriver.findElements. */
151
225
} ;
152
226
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
-
162
227
/**
163
228
* Find an array of elements matching a row within an ng-repeat.
164
229
* Always returns an array of only one element for plain old ng-repeat.
@@ -273,7 +338,6 @@ functions.findAllRepeaterRows = wrapWithHelpers(findAllRepeaterRows, repeaterMat
273
338
*/
274
339
function findRepeaterElement ( repeater , exact , index , binding , using , rootSelector ) {
275
340
var matches = [ ] ;
276
- var root = document . querySelector ( rootSelector || 'body' ) ;
277
341
using = using || document ;
278
342
279
343
var rows = [ ] ;
@@ -317,7 +381,7 @@ function findRepeaterElement(repeater, exact, index, binding, using, rootSelecto
317
381
if ( angular . getTestability ) {
318
382
matches . push . apply (
319
383
matches ,
320
- angular . getTestability ( root ) . findBindings ( row , binding ) ) ;
384
+ getNg1Hooks ( rootSelector ) . $$testability . findBindings ( row , binding ) ) ;
321
385
} else {
322
386
if ( row . className . indexOf ( 'ng-binding' ) != - 1 ) {
323
387
bindings . push ( row ) ;
@@ -334,7 +398,8 @@ function findRepeaterElement(repeater, exact, index, binding, using, rootSelecto
334
398
if ( angular . getTestability ) {
335
399
matches . push . apply (
336
400
matches ,
337
- angular . getTestability ( root ) . findBindings ( rowElem , binding ) ) ;
401
+ getNg1Hooks ( rootSelector ) . $$testability . findBindings ( rowElem ,
402
+ binding ) ) ;
338
403
} else {
339
404
if ( rowElem . className . indexOf ( 'ng-binding' ) != - 1 ) {
340
405
bindings . push ( rowElem ) ;
@@ -357,7 +422,8 @@ function findRepeaterElement(repeater, exact, index, binding, using, rootSelecto
357
422
}
358
423
return matches ;
359
424
}
360
- functions . findRepeaterElement = wrapWithHelpers ( findRepeaterElement , repeaterMatch ) ;
425
+ functions . findRepeaterElement =
426
+ wrapWithHelpers ( findRepeaterElement , repeaterMatch , getNg1Hooks ) ;
361
427
362
428
/**
363
429
* Find the elements in a column of an ng-repeat.
@@ -372,7 +438,6 @@ functions.findRepeaterElement = wrapWithHelpers(findRepeaterElement, repeaterMat
372
438
*/
373
439
function findRepeaterColumn ( repeater , exact , binding , using , rootSelector ) {
374
440
var matches = [ ] ;
375
- var root = document . querySelector ( rootSelector || 'body' ) ;
376
441
using = using || document ;
377
442
378
443
var rows = [ ] ;
@@ -414,7 +479,8 @@ function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
414
479
if ( angular . getTestability ) {
415
480
matches . push . apply (
416
481
matches ,
417
- angular . getTestability ( root ) . findBindings ( rows [ i ] , binding ) ) ;
482
+ getNg1Hooks ( rootSelector ) . $$testability . findBindings ( rows [ i ] ,
483
+ binding ) ) ;
418
484
} else {
419
485
if ( rows [ i ] . className . indexOf ( 'ng-binding' ) != - 1 ) {
420
486
bindings . push ( rows [ i ] ) ;
@@ -430,7 +496,8 @@ function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
430
496
if ( angular . getTestability ) {
431
497
matches . push . apply (
432
498
matches ,
433
- angular . getTestability ( root ) . findBindings ( multiRows [ i ] [ j ] , binding ) ) ;
499
+ getNg1Hooks ( rootSelector ) . $$testability . findBindings (
500
+ multiRows [ i ] [ j ] , binding ) ) ;
434
501
} else {
435
502
var elem = multiRows [ i ] [ j ] ;
436
503
if ( elem . className . indexOf ( 'ng-binding' ) != - 1 ) {
@@ -454,7 +521,8 @@ function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
454
521
}
455
522
return matches ;
456
523
}
457
- functions . findRepeaterColumn = wrapWithHelpers ( findRepeaterColumn , repeaterMatch ) ;
524
+ functions . findRepeaterColumn =
525
+ wrapWithHelpers ( findRepeaterColumn , repeaterMatch , getNg1Hooks ) ;
458
526
459
527
/**
460
528
* Find elements by model name.
@@ -466,11 +534,10 @@ functions.findRepeaterColumn = wrapWithHelpers(findRepeaterColumn, repeaterMatch
466
534
* @return {Array.<Element> } The matching elements.
467
535
*/
468
536
functions . findByModel = function ( model , using , rootSelector ) {
469
- var root = document . querySelector ( rootSelector || 'body' ) ;
470
537
using = using || document ;
471
538
472
539
if ( angular . getTestability ) {
473
- return angular . getTestability ( root ) .
540
+ return getNg1Hooks ( rootSelector ) . $$testability .
474
541
findModels ( using , model , true ) ;
475
542
}
476
543
var prefixes = [ 'ng-' , 'ng_' , 'data-ng-' , 'x-ng-' , 'ng\\:' ] ;
@@ -677,12 +744,11 @@ functions.allowAnimations = function(element, value) {
677
744
* @param {string } selector The selector housing an ng-app
678
745
*/
679
746
functions . getLocationAbsUrl = function ( selector ) {
680
- var el = document . querySelector ( selector ) ;
747
+ var hooks = getNg1Hooks ( selector ) ;
681
748
if ( angular . getTestability ) {
682
- return angular . getTestability ( el ) .
683
- getLocation ( ) ;
749
+ return hooks . $$testability . getLocation ( ) ;
684
750
}
685
- return angular . element ( el ) . injector ( ) . get ( '$location' ) . absUrl ( ) ;
751
+ return hooks . $ injector. get ( '$location' ) . absUrl ( ) ;
686
752
} ;
687
753
688
754
/**
@@ -693,12 +759,11 @@ functions.getLocationAbsUrl = function(selector) {
693
759
* /path?search=a&b=c#hash
694
760
*/
695
761
functions . setLocation = function ( selector , url ) {
696
- var el = document . querySelector ( selector ) ;
762
+ var hooks = getNg1Hooks ( selector ) ;
697
763
if ( angular . getTestability ) {
698
- return angular . getTestability ( el ) .
699
- setLocation ( url ) ;
764
+ return hooks . $$testability . setLocation ( url ) ;
700
765
}
701
- var $injector = angular . element ( el ) . injector ( ) ;
766
+ var $injector = hooks . $ injector;
702
767
var $location = $injector . get ( '$location' ) ;
703
768
var $rootScope = $injector . get ( '$rootScope' ) ;
704
769
@@ -715,12 +780,16 @@ functions.setLocation = function(selector, url) {
715
780
* @return {!Array<!Object> } An array of pending http requests.
716
781
*/
717
782
functions . 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' ) ;
721
785
return $http . pendingRequests ;
722
786
} ;
723
787
788
+ [ 'waitForAngular' , 'findBindings' , 'findByModel' , 'getLocationAbsUrl' ,
789
+ 'setLocation' , 'getPendingHttpRequests' ] . forEach ( function ( funName ) {
790
+ functions [ funName ] = wrapWithHelpers ( functions [ funName ] , getNg1Hooks ) ;
791
+ } ) ;
792
+
724
793
/* Publish all the functions as strings to pass to WebDriver's
725
794
* exec[Async]Script. In addition, also include a script that will
726
795
* install all the functions on window (for debugging.)
0 commit comments