angularjs-rails 1.5.8 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/lib/angularjs-rails/engine.rb +1 -1
- data/lib/angularjs-rails/version.rb +1 -1
- data/vendor/assets/javascripts/angular-animate.js +426 -293
- data/vendor/assets/javascripts/angular-aria.js +64 -43
- data/vendor/assets/javascripts/angular-cookies.js +24 -93
- data/vendor/assets/javascripts/angular-loader.js +190 -36
- data/vendor/assets/javascripts/angular-message-format.js +72 -84
- data/vendor/assets/javascripts/angular-messages.js +158 -68
- data/vendor/assets/javascripts/angular-mocks.js +1033 -402
- data/vendor/assets/javascripts/angular-parse-ext.js +14 -10
- data/vendor/assets/javascripts/angular-resource.js +317 -269
- data/vendor/assets/javascripts/angular-route.js +329 -132
- data/vendor/assets/javascripts/angular-sanitize.js +268 -93
- data/vendor/assets/javascripts/angular-touch.js +46 -413
- data/vendor/assets/javascripts/angular.js +9213 -4485
- metadata +2 -3
- data/vendor/assets/javascripts/angular-scenario.js +0 -44134
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.
|
3
|
-
* (c) 2010-
|
2
|
+
* @license AngularJS v1.8.0
|
3
|
+
* (c) 2010-2020 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
6
6
|
(function(window, angular) {'use strict';
|
@@ -23,9 +23,9 @@ var jqLite;
|
|
23
23
|
* sequencing based on the order of how the messages are defined in the template.
|
24
24
|
*
|
25
25
|
* Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
|
26
|
-
* `ngMessage` and `
|
26
|
+
* `ngMessage`, `ngMessageExp` and `ngMessageDefault` directives.
|
27
27
|
*
|
28
|
-
*
|
28
|
+
* ## Usage
|
29
29
|
* The `ngMessages` directive allows keys in a key/value collection to be associated with a child element
|
30
30
|
* (or 'message') that will show or hide based on the truthiness of that key's value in the collection. A common use
|
31
31
|
* case for `ngMessages` is to display error messages for inputs using the `$error` object exposed by the
|
@@ -69,7 +69,7 @@ var jqLite;
|
|
69
69
|
* By default, `ngMessages` will only display one message for a particular key/value collection at any time. If more
|
70
70
|
* than one message (or error) key is currently true, then which message is shown is determined by the order of messages
|
71
71
|
* in the HTML template code (messages declared first are prioritised). This mechanism means the developer does not have
|
72
|
-
* to
|
72
|
+
* to prioritize messages using custom JavaScript code.
|
73
73
|
*
|
74
74
|
* Given the following error object for our example (which informs us that the field `myField` currently has both the
|
75
75
|
* `required` and `email` errors):
|
@@ -200,7 +200,7 @@ var jqLite;
|
|
200
200
|
*
|
201
201
|
* Feel free to use other structural directives such as ng-if and ng-switch to further control
|
202
202
|
* what messages are active and when. Be careful, if you place ng-message on the same element
|
203
|
-
* as these structural directives,
|
203
|
+
* as these structural directives, AngularJS may not be able to determine if a message is active
|
204
204
|
* or not. Therefore it is best to place the ng-message on a child element of the structural
|
205
205
|
* directive.
|
206
206
|
*
|
@@ -262,16 +262,36 @@ var jqLite;
|
|
262
262
|
* .some-message.ng-leave.ng-leave-active {}
|
263
263
|
* ```
|
264
264
|
*
|
265
|
-
* {@link ngAnimate
|
265
|
+
* {@link ngAnimate See the ngAnimate docs} to learn how to use JavaScript animations or to learn
|
266
|
+
* more about ngAnimate.
|
267
|
+
*
|
268
|
+
* ## Displaying a default message
|
269
|
+
* If the ngMessages renders no inner ngMessage directive (i.e. when none of the truthy
|
270
|
+
* keys are matched by a defined message), then it will render a default message
|
271
|
+
* using the {@link ngMessageDefault} directive.
|
272
|
+
* Note that matched messages will always take precedence over unmatched messages. That means
|
273
|
+
* the default message will not be displayed when another message is matched. This is also
|
274
|
+
* true for `ng-messages-multiple`.
|
275
|
+
*
|
276
|
+
* ```html
|
277
|
+
* <div ng-messages="myForm.myField.$error" role="alert">
|
278
|
+
* <div ng-message="required">This field is required</div>
|
279
|
+
* <div ng-message="minlength">This field is too short</div>
|
280
|
+
* <div ng-message-default>This field has an input error</div>
|
281
|
+
* </div>
|
282
|
+
* ```
|
283
|
+
*
|
284
|
+
|
266
285
|
*/
|
267
286
|
angular.module('ngMessages', [], function initAngularHelpers() {
|
268
|
-
// Access helpers from
|
287
|
+
// Access helpers from AngularJS core.
|
269
288
|
// Do it inside a `config` block to ensure `window.angular` is available.
|
270
289
|
forEach = angular.forEach;
|
271
290
|
isArray = angular.isArray;
|
272
291
|
isString = angular.isString;
|
273
292
|
jqLite = angular.element;
|
274
293
|
})
|
294
|
+
.info({ angularVersion: '1.8.0' })
|
275
295
|
|
276
296
|
/**
|
277
297
|
* @ngdoc directive
|
@@ -290,8 +310,11 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
290
310
|
* at a time and this depends on the prioritization of the messages within the template. (This can
|
291
311
|
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
292
312
|
*
|
293
|
-
* A remote template can also be used to promote message
|
294
|
-
* overridden.
|
313
|
+
* A remote template can also be used (With {@link ngMessagesInclude}) to promote message
|
314
|
+
* reusability and messages can also be overridden.
|
315
|
+
*
|
316
|
+
* A default message can also be displayed when no `ngMessage` directive is inserted, using the
|
317
|
+
* {@link ngMessageDefault} directive.
|
295
318
|
*
|
296
319
|
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
297
320
|
*
|
@@ -302,6 +325,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
302
325
|
* <ANY ng-message="stringValue">...</ANY>
|
303
326
|
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
304
327
|
* <ANY ng-message-exp="expressionValue">...</ANY>
|
328
|
+
* <ANY ng-message-default>...</ANY>
|
305
329
|
* </ANY>
|
306
330
|
*
|
307
331
|
* <!-- or by using element directives -->
|
@@ -309,10 +333,11 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
309
333
|
* <ng-message when="stringValue">...</ng-message>
|
310
334
|
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
311
335
|
* <ng-message when-exp="expressionValue">...</ng-message>
|
336
|
+
* <ng-message-default>...</ng-message-default>
|
312
337
|
* </ng-messages>
|
313
338
|
* ```
|
314
339
|
*
|
315
|
-
* @param {string} ngMessages an
|
340
|
+
* @param {string} ngMessages an AngularJS expression evaluating to a key/value object
|
316
341
|
* (this is typically the $error object on an ngModel instance).
|
317
342
|
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
318
343
|
*
|
@@ -337,6 +362,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
337
362
|
* <div ng-message="required">You did not enter a field</div>
|
338
363
|
* <div ng-message="minlength">Your field is too short</div>
|
339
364
|
* <div ng-message="maxlength">Your field is too long</div>
|
365
|
+
* <div ng-message-default>This field has an input error</div>
|
340
366
|
* </div>
|
341
367
|
* </form>
|
342
368
|
* </file>
|
@@ -352,7 +378,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
352
378
|
return {
|
353
379
|
require: 'ngMessages',
|
354
380
|
restrict: 'AE',
|
355
|
-
controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
381
|
+
controller: ['$element', '$scope', '$attrs', function NgMessagesCtrl($element, $scope, $attrs) {
|
356
382
|
var ctrl = this;
|
357
383
|
var latestKey = 0;
|
358
384
|
var nextAttachId = 0;
|
@@ -374,6 +400,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
374
400
|
|
375
401
|
var unmatchedMessages = [];
|
376
402
|
var matchedKeys = {};
|
403
|
+
var truthyKeys = 0;
|
377
404
|
var messageItem = ctrl.head;
|
378
405
|
var messageFound = false;
|
379
406
|
var totalMessages = 0;
|
@@ -386,13 +413,17 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
386
413
|
var messageUsed = false;
|
387
414
|
if (!messageFound) {
|
388
415
|
forEach(collection, function(value, key) {
|
389
|
-
if (
|
390
|
-
|
391
|
-
|
392
|
-
|
416
|
+
if (truthy(value) && !messageUsed) {
|
417
|
+
truthyKeys++;
|
418
|
+
|
419
|
+
if (messageCtrl.test(key)) {
|
420
|
+
// this is to prevent the same error name from showing up twice
|
421
|
+
if (matchedKeys[key]) return;
|
422
|
+
matchedKeys[key] = true;
|
393
423
|
|
394
|
-
|
395
|
-
|
424
|
+
messageUsed = true;
|
425
|
+
messageCtrl.attach();
|
426
|
+
}
|
396
427
|
}
|
397
428
|
});
|
398
429
|
}
|
@@ -412,48 +443,60 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
412
443
|
messageCtrl.detach();
|
413
444
|
});
|
414
445
|
|
415
|
-
unmatchedMessages.length !== totalMessages
|
416
|
-
|
417
|
-
|
446
|
+
var messageMatched = unmatchedMessages.length !== totalMessages;
|
447
|
+
var attachDefault = ctrl.default && !messageMatched && truthyKeys > 0;
|
448
|
+
|
449
|
+
if (attachDefault) {
|
450
|
+
ctrl.default.attach();
|
451
|
+
} else if (ctrl.default) {
|
452
|
+
ctrl.default.detach();
|
453
|
+
}
|
454
|
+
|
455
|
+
if (messageMatched || attachDefault) {
|
456
|
+
$animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
|
457
|
+
} else {
|
458
|
+
$animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
|
459
|
+
}
|
418
460
|
};
|
419
461
|
|
420
462
|
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
421
463
|
|
422
|
-
// If the element is destroyed, proactively destroy all the currently visible messages
|
423
|
-
$element.on('$destroy', function() {
|
424
|
-
forEach(messages, function(item) {
|
425
|
-
item.message.detach();
|
426
|
-
});
|
427
|
-
});
|
428
|
-
|
429
464
|
this.reRender = function() {
|
430
465
|
if (!renderLater) {
|
431
466
|
renderLater = true;
|
432
467
|
$scope.$evalAsync(function() {
|
433
|
-
if (renderLater) {
|
434
|
-
|
468
|
+
if (renderLater && cachedCollection) {
|
469
|
+
ctrl.render(cachedCollection);
|
435
470
|
}
|
436
471
|
});
|
437
472
|
}
|
438
473
|
};
|
439
474
|
|
440
|
-
this.register = function(comment, messageCtrl) {
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
475
|
+
this.register = function(comment, messageCtrl, isDefault) {
|
476
|
+
if (isDefault) {
|
477
|
+
ctrl.default = messageCtrl;
|
478
|
+
} else {
|
479
|
+
var nextKey = latestKey.toString();
|
480
|
+
messages[nextKey] = {
|
481
|
+
message: messageCtrl
|
482
|
+
};
|
483
|
+
insertMessageNode($element[0], comment, nextKey);
|
484
|
+
comment.$$ngMessageNode = nextKey;
|
485
|
+
latestKey++;
|
486
|
+
}
|
448
487
|
|
449
488
|
ctrl.reRender();
|
450
489
|
};
|
451
490
|
|
452
|
-
this.deregister = function(comment) {
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
491
|
+
this.deregister = function(comment, isDefault) {
|
492
|
+
if (isDefault) {
|
493
|
+
delete ctrl.default;
|
494
|
+
} else {
|
495
|
+
var key = comment.$$ngMessageNode;
|
496
|
+
delete comment.$$ngMessageNode;
|
497
|
+
removeMessageNode($element[0], comment, key);
|
498
|
+
delete messages[key];
|
499
|
+
}
|
457
500
|
ctrl.reRender();
|
458
501
|
};
|
459
502
|
|
@@ -500,6 +543,9 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
500
543
|
function removeMessageNode(parent, comment, key) {
|
501
544
|
var messageNode = messages[key];
|
502
545
|
|
546
|
+
// This message node may have already been removed by a call to deregister()
|
547
|
+
if (!messageNode) return;
|
548
|
+
|
503
549
|
var match = findPreviousMessage(parent, comment);
|
504
550
|
if (match) {
|
505
551
|
match.next = messageNode.next;
|
@@ -594,6 +640,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
594
640
|
* @name ngMessage
|
595
641
|
* @restrict AE
|
596
642
|
* @scope
|
643
|
+
* @priority 1
|
597
644
|
*
|
598
645
|
* @description
|
599
646
|
* `ngMessage` is a directive with the purpose to show and hide a particular message.
|
@@ -632,10 +679,8 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
632
679
|
* @scope
|
633
680
|
*
|
634
681
|
* @description
|
635
|
-
* `ngMessageExp` is
|
636
|
-
*
|
637
|
-
* must be situated since it determines which messages are visible based on the state
|
638
|
-
* of the provided key/value map that `ngMessages` listens on.
|
682
|
+
* `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
|
683
|
+
* value, it accepts an expression to be evaluated for the message key.
|
639
684
|
*
|
640
685
|
* @usage
|
641
686
|
* ```html
|
@@ -654,9 +699,41 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
654
699
|
*
|
655
700
|
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
656
701
|
*/
|
657
|
-
.directive('ngMessageExp', ngMessageDirectiveFactory())
|
702
|
+
.directive('ngMessageExp', ngMessageDirectiveFactory())
|
658
703
|
|
659
|
-
|
704
|
+
/**
|
705
|
+
* @ngdoc directive
|
706
|
+
* @name ngMessageDefault
|
707
|
+
* @restrict AE
|
708
|
+
* @scope
|
709
|
+
*
|
710
|
+
* @description
|
711
|
+
* `ngMessageDefault` is a directive with the purpose to show and hide a default message for
|
712
|
+
* {@link directive:ngMessages}, when none of provided messages matches.
|
713
|
+
*
|
714
|
+
* More information about using `ngMessageDefault` can be found in the
|
715
|
+
* {@link module:ngMessages `ngMessages` module documentation}.
|
716
|
+
*
|
717
|
+
* @usage
|
718
|
+
* ```html
|
719
|
+
* <!-- using attribute directives -->
|
720
|
+
* <ANY ng-messages="expression" role="alert">
|
721
|
+
* <ANY ng-message="stringValue">...</ANY>
|
722
|
+
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
723
|
+
* <ANY ng-message-default>...</ANY>
|
724
|
+
* </ANY>
|
725
|
+
*
|
726
|
+
* <!-- or by using element directives -->
|
727
|
+
* <ng-messages for="expression" role="alert">
|
728
|
+
* <ng-message when="stringValue">...</ng-message>
|
729
|
+
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
730
|
+
* <ng-message-default>...</ng-message-default>
|
731
|
+
* </ng-messages>
|
732
|
+
*
|
733
|
+
*/
|
734
|
+
.directive('ngMessageDefault', ngMessageDirectiveFactory(true));
|
735
|
+
|
736
|
+
function ngMessageDirectiveFactory(isDefault) {
|
660
737
|
return ['$animate', function($animate) {
|
661
738
|
return {
|
662
739
|
restrict: 'AE',
|
@@ -665,25 +742,28 @@ function ngMessageDirectiveFactory() {
|
|
665
742
|
terminal: true,
|
666
743
|
require: '^^ngMessages',
|
667
744
|
link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
|
668
|
-
var commentNode
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
745
|
+
var commentNode, records, staticExp, dynamicExp;
|
746
|
+
|
747
|
+
if (!isDefault) {
|
748
|
+
commentNode = element[0];
|
749
|
+
staticExp = attrs.ngMessage || attrs.when;
|
750
|
+
dynamicExp = attrs.ngMessageExp || attrs.whenExp;
|
751
|
+
|
752
|
+
var assignRecords = function(items) {
|
753
|
+
records = items
|
754
|
+
? (isArray(items)
|
755
|
+
? items
|
756
|
+
: items.split(/[\s,]+/))
|
757
|
+
: null;
|
758
|
+
ngMessagesCtrl.reRender();
|
759
|
+
};
|
681
760
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
761
|
+
if (dynamicExp) {
|
762
|
+
assignRecords(scope.$eval(dynamicExp));
|
763
|
+
scope.$watchCollection(dynamicExp, assignRecords);
|
764
|
+
} else {
|
765
|
+
assignRecords(staticExp);
|
766
|
+
}
|
687
767
|
}
|
688
768
|
|
689
769
|
var currentElement, messageCtrl;
|
@@ -705,8 +785,10 @@ function ngMessageDirectiveFactory() {
|
|
705
785
|
// by another structural directive then it's time
|
706
786
|
// to deregister the message from the controller
|
707
787
|
currentElement.on('$destroy', function() {
|
788
|
+
// If the message element was removed via a call to `detach` then `currentElement` will be null
|
789
|
+
// So this handler only handles cases where something else removed the message element.
|
708
790
|
if (currentElement && currentElement.$$attachId === $$attachId) {
|
709
|
-
ngMessagesCtrl.deregister(commentNode);
|
791
|
+
ngMessagesCtrl.deregister(commentNode, isDefault);
|
710
792
|
messageCtrl.detach();
|
711
793
|
}
|
712
794
|
newScope.$destroy();
|
@@ -721,6 +803,14 @@ function ngMessageDirectiveFactory() {
|
|
721
803
|
$animate.leave(elm);
|
722
804
|
}
|
723
805
|
}
|
806
|
+
}, isDefault);
|
807
|
+
|
808
|
+
// We need to ensure that this directive deregisters itself when it no longer exists
|
809
|
+
// Normally this is done when the attached element is destroyed; but if this directive
|
810
|
+
// gets removed before we attach the message to the DOM there is nothing to watch
|
811
|
+
// in which case we must deregister when the containing scope is destroyed.
|
812
|
+
scope.$on('$destroy', function() {
|
813
|
+
ngMessagesCtrl.deregister(commentNode, isDefault);
|
724
814
|
});
|
725
815
|
}
|
726
816
|
};
|
@@ -1,12 +1,61 @@
|
|
1
1
|
/**
|
2
|
-
* @license AngularJS v1.
|
3
|
-
* (c) 2010-
|
2
|
+
* @license AngularJS v1.8.0
|
3
|
+
* (c) 2010-2020 Google, Inc. http://angularjs.org
|
4
4
|
* License: MIT
|
5
5
|
*/
|
6
6
|
(function(window, angular) {
|
7
7
|
|
8
8
|
'use strict';
|
9
9
|
|
10
|
+
/* global routeToRegExp: true */
|
11
|
+
|
12
|
+
/**
|
13
|
+
* @param {string} path - The path to parse. (It is assumed to have query and hash stripped off.)
|
14
|
+
* @param {Object} opts - Options.
|
15
|
+
* @return {Object} - An object containing an array of path parameter names (`keys`) and a regular
|
16
|
+
* expression (`regexp`) that can be used to identify a matching URL and extract the path
|
17
|
+
* parameter values.
|
18
|
+
*
|
19
|
+
* @description
|
20
|
+
* Parses the given path, extracting path parameter names and a regular expression to match URLs.
|
21
|
+
*
|
22
|
+
* Originally inspired by `pathRexp` in `visionmedia/express/lib/utils.js`.
|
23
|
+
*/
|
24
|
+
function routeToRegExp(path, opts) {
|
25
|
+
var keys = [];
|
26
|
+
|
27
|
+
var pattern = path
|
28
|
+
.replace(/([().])/g, '\\$1')
|
29
|
+
.replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) {
|
30
|
+
var optional = option === '?' || option === '*?';
|
31
|
+
var star = option === '*' || option === '*?';
|
32
|
+
keys.push({name: key, optional: optional});
|
33
|
+
slash = slash || '';
|
34
|
+
return (
|
35
|
+
(optional ? '(?:' + slash : slash + '(?:') +
|
36
|
+
(star ? '(.+?)' : '([^/]+)') +
|
37
|
+
(optional ? '?)?' : ')')
|
38
|
+
);
|
39
|
+
})
|
40
|
+
.replace(/([/$*])/g, '\\$1');
|
41
|
+
|
42
|
+
if (opts.ignoreTrailingSlashes) {
|
43
|
+
pattern = pattern.replace(/\/+$/, '') + '/*';
|
44
|
+
}
|
45
|
+
|
46
|
+
return {
|
47
|
+
keys: keys,
|
48
|
+
regexp: new RegExp(
|
49
|
+
'^' + pattern + '(?:[?#]|$)',
|
50
|
+
opts.caseInsensitiveMatch ? 'i' : ''
|
51
|
+
)
|
52
|
+
};
|
53
|
+
}
|
54
|
+
|
55
|
+
'use strict';
|
56
|
+
|
57
|
+
/* global routeToRegExp: false */
|
58
|
+
|
10
59
|
/**
|
11
60
|
* @ngdoc object
|
12
61
|
* @name angular.mock
|
@@ -31,23 +80,27 @@ angular.mock = {};
|
|
31
80
|
* that there are several helper methods available which can be used in tests.
|
32
81
|
*/
|
33
82
|
angular.mock.$BrowserProvider = function() {
|
34
|
-
this.$get =
|
35
|
-
|
36
|
-
|
83
|
+
this.$get = [
|
84
|
+
'$log', '$$taskTrackerFactory',
|
85
|
+
function($log, $$taskTrackerFactory) {
|
86
|
+
return new angular.mock.$Browser($log, $$taskTrackerFactory);
|
87
|
+
}
|
88
|
+
];
|
37
89
|
};
|
38
90
|
|
39
|
-
angular.mock.$Browser = function() {
|
91
|
+
angular.mock.$Browser = function($log, $$taskTrackerFactory) {
|
40
92
|
var self = this;
|
93
|
+
var taskTracker = $$taskTrackerFactory($log);
|
41
94
|
|
42
95
|
this.isMock = true;
|
43
|
-
self.$$url =
|
96
|
+
self.$$url = 'http://server/';
|
44
97
|
self.$$lastUrl = self.$$url; // used by url polling fn
|
45
98
|
self.pollFns = [];
|
46
99
|
|
47
|
-
//
|
48
|
-
self.$$completeOutstandingRequest =
|
49
|
-
self.$$incOutstandingRequestCount =
|
50
|
-
|
100
|
+
// Task-tracking API
|
101
|
+
self.$$completeOutstandingRequest = taskTracker.completeTask;
|
102
|
+
self.$$incOutstandingRequestCount = taskTracker.incTaskCount;
|
103
|
+
self.notifyWhenNoOutstandingRequests = taskTracker.notifyWhenNoPendingTasks;
|
51
104
|
|
52
105
|
// register url polling fn
|
53
106
|
|
@@ -71,11 +124,22 @@ angular.mock.$Browser = function() {
|
|
71
124
|
self.deferredFns = [];
|
72
125
|
self.deferredNextId = 0;
|
73
126
|
|
74
|
-
self.defer = function(fn, delay) {
|
127
|
+
self.defer = function(fn, delay, taskType) {
|
128
|
+
var timeoutId = self.deferredNextId++;
|
129
|
+
|
75
130
|
delay = delay || 0;
|
76
|
-
|
77
|
-
|
78
|
-
|
131
|
+
taskType = taskType || taskTracker.DEFAULT_TASK_TYPE;
|
132
|
+
|
133
|
+
taskTracker.incTaskCount(taskType);
|
134
|
+
self.deferredFns.push({
|
135
|
+
id: timeoutId,
|
136
|
+
type: taskType,
|
137
|
+
time: (self.defer.now + delay),
|
138
|
+
fn: fn
|
139
|
+
});
|
140
|
+
self.deferredFns.sort(function(a, b) { return a.time - b.time; });
|
141
|
+
|
142
|
+
return timeoutId;
|
79
143
|
};
|
80
144
|
|
81
145
|
|
@@ -89,14 +153,15 @@ angular.mock.$Browser = function() {
|
|
89
153
|
|
90
154
|
|
91
155
|
self.defer.cancel = function(deferId) {
|
92
|
-
var
|
156
|
+
var taskIndex;
|
93
157
|
|
94
|
-
angular.forEach(self.deferredFns, function(
|
95
|
-
if (
|
158
|
+
angular.forEach(self.deferredFns, function(task, index) {
|
159
|
+
if (task.id === deferId) taskIndex = index;
|
96
160
|
});
|
97
161
|
|
98
|
-
if (angular.isDefined(
|
99
|
-
self.deferredFns.splice(
|
162
|
+
if (angular.isDefined(taskIndex)) {
|
163
|
+
var task = self.deferredFns.splice(taskIndex, 1)[0];
|
164
|
+
taskTracker.completeTask(angular.noop, task.type);
|
100
165
|
return true;
|
101
166
|
}
|
102
167
|
|
@@ -110,6 +175,8 @@ angular.mock.$Browser = function() {
|
|
110
175
|
* @description
|
111
176
|
* Flushes all pending requests and executes the defer callbacks.
|
112
177
|
*
|
178
|
+
* See {@link ngMock.$flushPendingsTasks} for more info.
|
179
|
+
*
|
113
180
|
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
|
114
181
|
*/
|
115
182
|
self.defer.flush = function(delay) {
|
@@ -118,26 +185,76 @@ angular.mock.$Browser = function() {
|
|
118
185
|
if (angular.isDefined(delay)) {
|
119
186
|
// A delay was passed so compute the next time
|
120
187
|
nextTime = self.defer.now + delay;
|
188
|
+
} else if (self.deferredFns.length) {
|
189
|
+
// No delay was passed so set the next time so that it clears the deferred queue
|
190
|
+
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
121
191
|
} else {
|
122
|
-
|
123
|
-
|
124
|
-
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
125
|
-
} else {
|
126
|
-
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
127
|
-
throw new Error('No deferred tasks to be flushed');
|
128
|
-
}
|
192
|
+
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
193
|
+
throw new Error('No deferred tasks to be flushed');
|
129
194
|
}
|
130
195
|
|
131
196
|
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
|
132
197
|
// Increment the time and call the next deferred function
|
133
198
|
self.defer.now = self.deferredFns[0].time;
|
134
|
-
self.deferredFns.shift()
|
199
|
+
var task = self.deferredFns.shift();
|
200
|
+
taskTracker.completeTask(task.fn, task.type);
|
135
201
|
}
|
136
202
|
|
137
203
|
// Ensure that the current time is correct
|
138
204
|
self.defer.now = nextTime;
|
139
205
|
};
|
140
206
|
|
207
|
+
/**
|
208
|
+
* @name $browser#defer.getPendingTasks
|
209
|
+
*
|
210
|
+
* @description
|
211
|
+
* Returns the currently pending tasks that need to be flushed.
|
212
|
+
* You can request a specific type of tasks only, by specifying a `taskType`.
|
213
|
+
*
|
214
|
+
* @param {string=} taskType - The type tasks to return.
|
215
|
+
*/
|
216
|
+
self.defer.getPendingTasks = function(taskType) {
|
217
|
+
return !taskType
|
218
|
+
? self.deferredFns
|
219
|
+
: self.deferredFns.filter(function(task) { return task.type === taskType; });
|
220
|
+
};
|
221
|
+
|
222
|
+
/**
|
223
|
+
* @name $browser#defer.formatPendingTasks
|
224
|
+
*
|
225
|
+
* @description
|
226
|
+
* Formats each task in a list of pending tasks as a string, suitable for use in error messages.
|
227
|
+
*
|
228
|
+
* @param {Array<Object>} pendingTasks - A list of task objects.
|
229
|
+
* @return {Array<string>} A list of stringified tasks.
|
230
|
+
*/
|
231
|
+
self.defer.formatPendingTasks = function(pendingTasks) {
|
232
|
+
return pendingTasks.map(function(task) {
|
233
|
+
return '{id: ' + task.id + ', type: ' + task.type + ', time: ' + task.time + '}';
|
234
|
+
});
|
235
|
+
};
|
236
|
+
|
237
|
+
/**
|
238
|
+
* @name $browser#defer.verifyNoPendingTasks
|
239
|
+
*
|
240
|
+
* @description
|
241
|
+
* Verifies that there are no pending tasks that need to be flushed.
|
242
|
+
* You can check for a specific type of tasks only, by specifying a `taskType`.
|
243
|
+
*
|
244
|
+
* See {@link $verifyNoPendingTasks} for more info.
|
245
|
+
*
|
246
|
+
* @param {string=} taskType - The type tasks to check for.
|
247
|
+
*/
|
248
|
+
self.defer.verifyNoPendingTasks = function(taskType) {
|
249
|
+
var pendingTasks = self.defer.getPendingTasks(taskType);
|
250
|
+
|
251
|
+
if (pendingTasks.length) {
|
252
|
+
var formattedTasks = self.defer.formatPendingTasks(pendingTasks).join('\n ');
|
253
|
+
throw new Error('Deferred tasks to flush (' + pendingTasks.length + '):\n ' +
|
254
|
+
formattedTasks);
|
255
|
+
}
|
256
|
+
};
|
257
|
+
|
141
258
|
self.$$baseHref = '/';
|
142
259
|
self.baseHref = function() {
|
143
260
|
return this.$$baseHref;
|
@@ -162,7 +279,8 @@ angular.mock.$Browser.prototype = {
|
|
162
279
|
state = null;
|
163
280
|
}
|
164
281
|
if (url) {
|
165
|
-
|
282
|
+
// The `$browser` service trims empty hashes; simulate it.
|
283
|
+
this.$$url = url.replace(/#$/, '');
|
166
284
|
// Native pushState serializes & copies the object; simulate it.
|
167
285
|
this.$$state = angular.copy(state);
|
168
286
|
return this;
|
@@ -173,13 +291,85 @@ angular.mock.$Browser.prototype = {
|
|
173
291
|
|
174
292
|
state: function() {
|
175
293
|
return this.$$state;
|
176
|
-
},
|
177
|
-
|
178
|
-
notifyWhenNoOutstandingRequests: function(fn) {
|
179
|
-
fn();
|
180
294
|
}
|
181
295
|
};
|
182
296
|
|
297
|
+
/**
|
298
|
+
* @ngdoc service
|
299
|
+
* @name $flushPendingTasks
|
300
|
+
*
|
301
|
+
* @description
|
302
|
+
* Flushes all currently pending tasks and executes the corresponding callbacks.
|
303
|
+
*
|
304
|
+
* Optionally, you can also pass a `delay` argument to only flush tasks that are scheduled to be
|
305
|
+
* executed within `delay` milliseconds. Currently, `delay` only applies to timeouts, since all
|
306
|
+
* other tasks have a delay of 0 (i.e. they are scheduled to be executed as soon as possible, but
|
307
|
+
* still asynchronously).
|
308
|
+
*
|
309
|
+
* If no delay is specified, it uses a delay such that all currently pending tasks are flushed.
|
310
|
+
*
|
311
|
+
* The types of tasks that are flushed include:
|
312
|
+
*
|
313
|
+
* - Pending timeouts (via {@link $timeout}).
|
314
|
+
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync $applyAsync}.
|
315
|
+
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
316
|
+
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
|
317
|
+
*
|
318
|
+
* <div class="alert alert-info">
|
319
|
+
* Periodic tasks scheduled via {@link $interval} use a different queue and are not flushed by
|
320
|
+
* `$flushPendingTasks()`. Use {@link ngMock.$interval#flush $interval.flush(millis)} instead.
|
321
|
+
* </div>
|
322
|
+
*
|
323
|
+
* @param {number=} delay - The number of milliseconds to flush.
|
324
|
+
*/
|
325
|
+
angular.mock.$FlushPendingTasksProvider = function() {
|
326
|
+
this.$get = [
|
327
|
+
'$browser',
|
328
|
+
function($browser) {
|
329
|
+
return function $flushPendingTasks(delay) {
|
330
|
+
return $browser.defer.flush(delay);
|
331
|
+
};
|
332
|
+
}
|
333
|
+
];
|
334
|
+
};
|
335
|
+
|
336
|
+
/**
|
337
|
+
* @ngdoc service
|
338
|
+
* @name $verifyNoPendingTasks
|
339
|
+
*
|
340
|
+
* @description
|
341
|
+
* Verifies that there are no pending tasks that need to be flushed. It throws an error if there are
|
342
|
+
* still pending tasks.
|
343
|
+
*
|
344
|
+
* You can check for a specific type of tasks only, by specifying a `taskType`.
|
345
|
+
*
|
346
|
+
* Available task types:
|
347
|
+
*
|
348
|
+
* - `$timeout`: Pending timeouts (via {@link $timeout}).
|
349
|
+
* - `$http`: Pending HTTP requests (via {@link $http}).
|
350
|
+
* - `$route`: In-progress route transitions (via {@link $route}).
|
351
|
+
* - `$applyAsync`: Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync $applyAsync}.
|
352
|
+
* - `$evalAsync`: Pending tasks scheduled via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
353
|
+
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
|
354
|
+
*
|
355
|
+
* <div class="alert alert-info">
|
356
|
+
* Periodic tasks scheduled via {@link $interval} use a different queue and are not taken into
|
357
|
+
* account by `$verifyNoPendingTasks()`. There is currently no way to verify that there are no
|
358
|
+
* pending {@link $interval} tasks.
|
359
|
+
* </div>
|
360
|
+
*
|
361
|
+
* @param {string=} taskType - The type of tasks to check for.
|
362
|
+
*/
|
363
|
+
angular.mock.$VerifyNoPendingTasksProvider = function() {
|
364
|
+
this.$get = [
|
365
|
+
'$browser',
|
366
|
+
function($browser) {
|
367
|
+
return function $verifyNoPendingTasks(taskType) {
|
368
|
+
return $browser.defer.verifyNoPendingTasks(taskType);
|
369
|
+
};
|
370
|
+
}
|
371
|
+
];
|
372
|
+
};
|
183
373
|
|
184
374
|
/**
|
185
375
|
* @ngdoc provider
|
@@ -252,19 +442,19 @@ angular.mock.$ExceptionHandlerProvider = function() {
|
|
252
442
|
case 'rethrow':
|
253
443
|
var errors = [];
|
254
444
|
handler = function(e) {
|
255
|
-
if (arguments.length
|
445
|
+
if (arguments.length === 1) {
|
256
446
|
errors.push(e);
|
257
447
|
} else {
|
258
448
|
errors.push([].slice.call(arguments, 0));
|
259
449
|
}
|
260
|
-
if (mode ===
|
450
|
+
if (mode === 'rethrow') {
|
261
451
|
throw e;
|
262
452
|
}
|
263
453
|
};
|
264
454
|
handler.errors = errors;
|
265
455
|
break;
|
266
456
|
default:
|
267
|
-
throw new Error(
|
457
|
+
throw new Error('Unknown mode \'' + mode + '\', only \'log\'/\'rethrow\' modes are allowed!');
|
268
458
|
}
|
269
459
|
};
|
270
460
|
|
@@ -414,8 +604,8 @@ angular.mock.$LogProvider = function() {
|
|
414
604
|
});
|
415
605
|
});
|
416
606
|
if (errors.length) {
|
417
|
-
errors.unshift(
|
418
|
-
|
607
|
+
errors.unshift('Expected $log to be empty! Either a message was logged unexpectedly, or ' +
|
608
|
+
'an expected log message was not checked and removed:');
|
419
609
|
errors.push('');
|
420
610
|
throw new Error(errors.join('\n---------\n'));
|
421
611
|
}
|
@@ -448,62 +638,40 @@ angular.mock.$LogProvider = function() {
|
|
448
638
|
* @returns {promise} A promise which will be notified on each iteration.
|
449
639
|
*/
|
450
640
|
angular.mock.$IntervalProvider = function() {
|
451
|
-
this.$get = ['$browser', '
|
452
|
-
function($browser,
|
641
|
+
this.$get = ['$browser', '$$intervalFactory',
|
642
|
+
function($browser, $$intervalFactory) {
|
453
643
|
var repeatFns = [],
|
454
644
|
nextRepeatId = 0,
|
455
|
-
now = 0
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
promise.$$intervalId = nextRepeatId;
|
471
|
-
|
472
|
-
function tick() {
|
473
|
-
deferred.notify(iteration++);
|
474
|
-
|
475
|
-
if (count > 0 && iteration >= count) {
|
476
|
-
var fnIndex;
|
477
|
-
deferred.resolve(iteration);
|
478
|
-
|
479
|
-
angular.forEach(repeatFns, function(fn, index) {
|
480
|
-
if (fn.id === promise.$$intervalId) fnIndex = index;
|
645
|
+
now = 0,
|
646
|
+
setIntervalFn = function(tick, delay, deferred, skipApply) {
|
647
|
+
var id = nextRepeatId++;
|
648
|
+
var fn = !skipApply ? tick : function() {
|
649
|
+
tick();
|
650
|
+
$browser.defer.flush();
|
651
|
+
};
|
652
|
+
|
653
|
+
repeatFns.push({
|
654
|
+
nextTime: (now + (delay || 0)),
|
655
|
+
delay: delay || 1,
|
656
|
+
fn: fn,
|
657
|
+
id: id,
|
658
|
+
deferred: deferred
|
481
659
|
});
|
660
|
+
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime; });
|
482
661
|
|
483
|
-
|
484
|
-
|
662
|
+
return id;
|
663
|
+
},
|
664
|
+
clearIntervalFn = function(id) {
|
665
|
+
for (var fnIndex = repeatFns.length - 1; fnIndex >= 0; fnIndex--) {
|
666
|
+
if (repeatFns[fnIndex].id === id) {
|
667
|
+
repeatFns.splice(fnIndex, 1);
|
668
|
+
break;
|
669
|
+
}
|
485
670
|
}
|
486
|
-
}
|
487
|
-
|
488
|
-
if (skipApply) {
|
489
|
-
$browser.defer.flush();
|
490
|
-
} else {
|
491
|
-
$rootScope.$apply();
|
492
|
-
}
|
493
|
-
}
|
671
|
+
};
|
494
672
|
|
495
|
-
|
496
|
-
nextTime:(now + delay),
|
497
|
-
delay: delay,
|
498
|
-
fn: tick,
|
499
|
-
id: nextRepeatId,
|
500
|
-
deferred: deferred
|
501
|
-
});
|
502
|
-
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
|
673
|
+
var $interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
|
503
674
|
|
504
|
-
nextRepeatId++;
|
505
|
-
return promise;
|
506
|
-
};
|
507
675
|
/**
|
508
676
|
* @ngdoc method
|
509
677
|
* @name $interval#cancel
|
@@ -516,16 +684,15 @@ angular.mock.$IntervalProvider = function() {
|
|
516
684
|
*/
|
517
685
|
$interval.cancel = function(promise) {
|
518
686
|
if (!promise) return false;
|
519
|
-
var fnIndex;
|
520
|
-
|
521
|
-
angular.forEach(repeatFns, function(fn, index) {
|
522
|
-
if (fn.id === promise.$$intervalId) fnIndex = index;
|
523
|
-
});
|
524
687
|
|
525
|
-
|
526
|
-
repeatFns[fnIndex].
|
527
|
-
|
528
|
-
|
688
|
+
for (var fnIndex = repeatFns.length - 1; fnIndex >= 0; fnIndex--) {
|
689
|
+
if (repeatFns[fnIndex].id === promise.$$intervalId) {
|
690
|
+
var deferred = repeatFns[fnIndex].deferred;
|
691
|
+
deferred.promise.then(undefined, function() {});
|
692
|
+
deferred.reject('canceled');
|
693
|
+
repeatFns.splice(fnIndex, 1);
|
694
|
+
return true;
|
695
|
+
}
|
529
696
|
}
|
530
697
|
|
531
698
|
return false;
|
@@ -538,15 +705,21 @@ angular.mock.$IntervalProvider = function() {
|
|
538
705
|
*
|
539
706
|
* Runs interval tasks scheduled to be run in the next `millis` milliseconds.
|
540
707
|
*
|
541
|
-
* @param {number
|
708
|
+
* @param {number} millis maximum timeout amount to flush up until.
|
542
709
|
*
|
543
710
|
* @return {number} The amount of time moved forward.
|
544
711
|
*/
|
545
712
|
$interval.flush = function(millis) {
|
713
|
+
var before = now;
|
546
714
|
now += millis;
|
547
715
|
while (repeatFns.length && repeatFns[0].nextTime <= now) {
|
548
716
|
var task = repeatFns[0];
|
549
717
|
task.fn();
|
718
|
+
if (task.nextTime === before) {
|
719
|
+
// this can only happen the first time
|
720
|
+
// a zero-delay interval gets triggered
|
721
|
+
task.nextTime++;
|
722
|
+
}
|
550
723
|
task.nextTime += task.delay;
|
551
724
|
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
|
552
725
|
}
|
@@ -558,16 +731,13 @@ angular.mock.$IntervalProvider = function() {
|
|
558
731
|
};
|
559
732
|
|
560
733
|
|
561
|
-
/* jshint -W101 */
|
562
|
-
/* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
|
563
|
-
* This directive should go inside the anonymous function but a bug in JSHint means that it would
|
564
|
-
* not be enacted early enough to prevent the warning.
|
565
|
-
*/
|
566
|
-
var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
567
|
-
|
568
734
|
function jsonStringToDate(string) {
|
735
|
+
// The R_ISO8061_STR regex is never going to fit into the 100 char limit!
|
736
|
+
// eslit-disable-next-line max-len
|
737
|
+
var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
738
|
+
|
569
739
|
var match;
|
570
|
-
if (match = string.match(R_ISO8061_STR)) {
|
740
|
+
if ((match = string.match(R_ISO8061_STR))) {
|
571
741
|
var date = new Date(0),
|
572
742
|
tzHour = 0,
|
573
743
|
tzMin = 0;
|
@@ -650,9 +820,10 @@ angular.mock.TzDate = function(offset, timestamp) {
|
|
650
820
|
|
651
821
|
timestamp = self.origDate.getTime();
|
652
822
|
if (isNaN(timestamp)) {
|
823
|
+
// eslint-disable-next-line no-throw-literal
|
653
824
|
throw {
|
654
|
-
name:
|
655
|
-
message:
|
825
|
+
name: 'Illegal Argument',
|
826
|
+
message: 'Arg \'' + tsStr + '\' passed into TzDate constructor is not a valid date string'
|
656
827
|
};
|
657
828
|
}
|
658
829
|
} else {
|
@@ -758,7 +929,7 @@ angular.mock.TzDate = function(offset, timestamp) {
|
|
758
929
|
|
759
930
|
angular.forEach(unimplementedMethods, function(methodName) {
|
760
931
|
self[methodName] = function() {
|
761
|
-
throw new Error(
|
932
|
+
throw new Error('Method \'' + methodName + '\' is not implemented in the TzDate mock');
|
762
933
|
};
|
763
934
|
});
|
764
935
|
|
@@ -767,7 +938,6 @@ angular.mock.TzDate = function(offset, timestamp) {
|
|
767
938
|
|
768
939
|
//make "tzDateInstance instanceof Date" return true
|
769
940
|
angular.mock.TzDate.prototype = Date.prototype;
|
770
|
-
/* jshint +W101 */
|
771
941
|
|
772
942
|
|
773
943
|
/**
|
@@ -781,6 +951,7 @@ angular.mock.TzDate.prototype = Date.prototype;
|
|
781
951
|
* You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
|
782
952
|
*/
|
783
953
|
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
954
|
+
.info({ angularVersion: '1.8.0' })
|
784
955
|
|
785
956
|
.config(['$provide', function($provide) {
|
786
957
|
|
@@ -946,7 +1117,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
|
946
1117
|
*
|
947
1118
|
* *NOTE*: This is not an injectable instance, just a globally available function.
|
948
1119
|
*
|
949
|
-
* Method for serializing common
|
1120
|
+
* Method for serializing common AngularJS objects (scope, elements, etc..) into strings.
|
950
1121
|
* It is useful for logging objects to the console when debugging.
|
951
1122
|
*
|
952
1123
|
* @param {*} object - any object to turn into string.
|
@@ -1028,7 +1199,7 @@ angular.mock.dump = function(object) {
|
|
1028
1199
|
* This mock implementation can be used to respond with static or dynamic responses via the
|
1029
1200
|
* `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
|
1030
1201
|
*
|
1031
|
-
* When an
|
1202
|
+
* When an AngularJS application needs some data from a server, it calls the $http service, which
|
1032
1203
|
* sends the request to a real server using $httpBackend service. With dependency injection, it is
|
1033
1204
|
* easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
|
1034
1205
|
* the requests and respond with some testing data without sending a request to a real server.
|
@@ -1123,6 +1294,8 @@ angular.mock.dump = function(object) {
|
|
1123
1294
|
$http.get('/auth.py').then(function(response) {
|
1124
1295
|
authToken = response.headers('A-Token');
|
1125
1296
|
$scope.user = response.data;
|
1297
|
+
}).catch(function() {
|
1298
|
+
$scope.status = 'Failed...';
|
1126
1299
|
});
|
1127
1300
|
|
1128
1301
|
$scope.saveMessage = function(message) {
|
@@ -1215,7 +1388,7 @@ angular.mock.dump = function(object) {
|
|
1215
1388
|
$httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
|
1216
1389
|
// check if the header was sent, if it wasn't the expectation won't
|
1217
1390
|
// match the request and the test will fail
|
1218
|
-
return headers['Authorization']
|
1391
|
+
return headers['Authorization'] === 'xxx';
|
1219
1392
|
}).respond(201, '');
|
1220
1393
|
|
1221
1394
|
$rootScope.saveMessage('whatever');
|
@@ -1264,7 +1437,7 @@ angular.mock.dump = function(object) {
|
|
1264
1437
|
* ## Matching route requests
|
1265
1438
|
*
|
1266
1439
|
* For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
|
1267
|
-
* delimited matching of the url path, ignoring the query string. This allows declarations
|
1440
|
+
* delimited matching of the url path, ignoring the query string and trailing slashes. This allows declarations
|
1268
1441
|
* similar to how application routes are configured with `$routeProvider`. Because these methods convert
|
1269
1442
|
* the definition url to regex, declaration order is important. Combined with query parameter parsing,
|
1270
1443
|
* the following is possible:
|
@@ -1304,9 +1477,8 @@ angular.mock.dump = function(object) {
|
|
1304
1477
|
});
|
1305
1478
|
```
|
1306
1479
|
*/
|
1307
|
-
angular.mock.$
|
1308
|
-
|
1309
|
-
};
|
1480
|
+
angular.mock.$httpBackendDecorator =
|
1481
|
+
['$rootScope', '$timeout', '$delegate', createHttpBackendMock];
|
1310
1482
|
|
1311
1483
|
/**
|
1312
1484
|
* General factory function for $httpBackend mock.
|
@@ -1325,17 +1497,21 @@ angular.mock.$HttpBackendProvider = function() {
|
|
1325
1497
|
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
1326
1498
|
var definitions = [],
|
1327
1499
|
expectations = [],
|
1500
|
+
matchLatestDefinition = false,
|
1328
1501
|
responses = [],
|
1329
1502
|
responsesPush = angular.bind(responses, responses.push),
|
1330
|
-
copy = angular.copy
|
1503
|
+
copy = angular.copy,
|
1504
|
+
// We cache the original backend so that if both ngMock and ngMockE2E override the
|
1505
|
+
// service the ngMockE2E version can pass through to the real backend
|
1506
|
+
originalHttpBackend = $delegate.$$originalHttpBackend || $delegate;
|
1331
1507
|
|
1332
1508
|
function createResponse(status, data, headers, statusText) {
|
1333
1509
|
if (angular.isFunction(status)) return status;
|
1334
1510
|
|
1335
1511
|
return function() {
|
1336
1512
|
return angular.isNumber(status)
|
1337
|
-
? [status, data, headers, statusText]
|
1338
|
-
: [200, status, data, headers];
|
1513
|
+
? [status, data, headers, statusText, 'complete']
|
1514
|
+
: [200, status, data, headers, 'complete'];
|
1339
1515
|
};
|
1340
1516
|
}
|
1341
1517
|
|
@@ -1357,39 +1533,57 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1357
1533
|
|
1358
1534
|
function wrapResponse(wrapped) {
|
1359
1535
|
if (!$browser && timeout) {
|
1360
|
-
|
1536
|
+
if (timeout.then) {
|
1537
|
+
timeout.then(function() {
|
1538
|
+
handlePrematureEnd(angular.isDefined(timeout.$$timeoutId) ? 'timeout' : 'abort');
|
1539
|
+
});
|
1540
|
+
} else {
|
1541
|
+
$timeout(function() {
|
1542
|
+
handlePrematureEnd('timeout');
|
1543
|
+
}, timeout);
|
1544
|
+
}
|
1361
1545
|
}
|
1362
1546
|
|
1547
|
+
handleResponse.description = method + ' ' + url;
|
1363
1548
|
return handleResponse;
|
1364
1549
|
|
1365
1550
|
function handleResponse() {
|
1366
1551
|
var response = wrapped.response(method, url, data, headers, wrapped.params(url));
|
1367
1552
|
xhr.$$respHeaders = response[2];
|
1368
1553
|
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
|
1369
|
-
copy(response[3] || ''));
|
1554
|
+
copy(response[3] || ''), copy(response[4]));
|
1370
1555
|
}
|
1371
1556
|
|
1372
|
-
function
|
1557
|
+
function handlePrematureEnd(reason) {
|
1373
1558
|
for (var i = 0, ii = responses.length; i < ii; i++) {
|
1374
1559
|
if (responses[i] === handleResponse) {
|
1375
1560
|
responses.splice(i, 1);
|
1376
|
-
callback(-1, undefined, '');
|
1561
|
+
callback(-1, undefined, '', undefined, reason);
|
1377
1562
|
break;
|
1378
1563
|
}
|
1379
1564
|
}
|
1380
1565
|
}
|
1381
1566
|
}
|
1382
1567
|
|
1568
|
+
function createFatalError(message) {
|
1569
|
+
var error = new Error(message);
|
1570
|
+
// In addition to being converted to a rejection, these errors also need to be passed to
|
1571
|
+
// the $exceptionHandler and be rethrown (so that the test fails).
|
1572
|
+
error.$$passToExceptionHandler = true;
|
1573
|
+
return error;
|
1574
|
+
}
|
1575
|
+
|
1383
1576
|
if (expectation && expectation.match(method, url)) {
|
1384
1577
|
if (!expectation.matchData(data)) {
|
1385
|
-
throw
|
1386
|
-
|
1578
|
+
throw createFatalError('Expected ' + expectation + ' with different data\n' +
|
1579
|
+
'EXPECTED: ' + prettyPrint(expectation.data) + '\n' +
|
1580
|
+
'GOT: ' + data);
|
1387
1581
|
}
|
1388
1582
|
|
1389
1583
|
if (!expectation.matchHeaders(headers)) {
|
1390
|
-
throw
|
1391
|
-
|
1392
|
-
|
1584
|
+
throw createFatalError('Expected ' + expectation + ' with different headers\n' +
|
1585
|
+
'EXPECTED: ' + prettyPrint(expectation.headers) + '\n' +
|
1586
|
+
'GOT: ' + prettyPrint(headers));
|
1393
1587
|
}
|
1394
1588
|
|
1395
1589
|
expectations.shift();
|
@@ -1401,22 +1595,26 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1401
1595
|
wasExpected = true;
|
1402
1596
|
}
|
1403
1597
|
|
1404
|
-
var i = -1, definition;
|
1405
|
-
|
1598
|
+
var i = matchLatestDefinition ? definitions.length : -1, definition;
|
1599
|
+
|
1600
|
+
while ((definition = definitions[matchLatestDefinition ? --i : ++i])) {
|
1406
1601
|
if (definition.match(method, url, data, headers || {})) {
|
1407
1602
|
if (definition.response) {
|
1408
1603
|
// if $browser specified, we do auto flush all requests
|
1409
1604
|
($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
|
1410
1605
|
} else if (definition.passThrough) {
|
1411
|
-
|
1412
|
-
} else throw
|
1606
|
+
originalHttpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
|
1607
|
+
} else throw createFatalError('No response defined !');
|
1413
1608
|
return;
|
1414
1609
|
}
|
1415
1610
|
}
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1611
|
+
|
1612
|
+
if (wasExpected) {
|
1613
|
+
throw createFatalError('No response defined !');
|
1614
|
+
}
|
1615
|
+
|
1616
|
+
throw createFatalError('Unexpected request: ' + method + ' ' + url + '\n' +
|
1617
|
+
(expectation ? 'Expected ' + expectation : 'No more request expected'));
|
1420
1618
|
}
|
1421
1619
|
|
1422
1620
|
/**
|
@@ -1426,7 +1624,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1426
1624
|
* Creates a new backend definition.
|
1427
1625
|
*
|
1428
1626
|
* @param {string} method HTTP method.
|
1429
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1627
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1430
1628
|
* and returns true if the url matches the current definition.
|
1431
1629
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
1432
1630
|
* data string and returns true if the data is as expected.
|
@@ -1444,10 +1642,14 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1444
1642
|
* ```
|
1445
1643
|
* – The respond method takes a set of static data to be returned or a function that can
|
1446
1644
|
* return an array containing response status (number), response data (Array|Object|string),
|
1447
|
-
* response headers (Object),
|
1448
|
-
*
|
1645
|
+
* response headers (Object), HTTP status text (string), and XMLHttpRequest status (string:
|
1646
|
+
* `complete`, `error`, `timeout` or `abort`). The respond method returns the `requestHandler`
|
1647
|
+
* object for possible overrides.
|
1449
1648
|
*/
|
1450
1649
|
$httpBackend.when = function(method, url, data, headers, keys) {
|
1650
|
+
|
1651
|
+
assertArgDefined(arguments, 1, 'url');
|
1652
|
+
|
1451
1653
|
var definition = new MockHttpExpectation(method, url, data, headers, keys),
|
1452
1654
|
chain = {
|
1453
1655
|
respond: function(status, data, headers, statusText) {
|
@@ -1469,15 +1671,57 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1469
1671
|
return chain;
|
1470
1672
|
};
|
1471
1673
|
|
1674
|
+
/**
|
1675
|
+
* @ngdoc method
|
1676
|
+
* @name $httpBackend#matchLatestDefinitionEnabled
|
1677
|
+
* @description
|
1678
|
+
* This method can be used to change which mocked responses `$httpBackend` returns, when defining
|
1679
|
+
* them with {@link ngMock.$httpBackend#when $httpBackend.when()} (and shortcut methods).
|
1680
|
+
* By default, `$httpBackend` returns the first definition that matches. When setting
|
1681
|
+
* `$http.matchLatestDefinitionEnabled(true)`, it will use the last response that matches, i.e. the
|
1682
|
+
* one that was added last.
|
1683
|
+
*
|
1684
|
+
* ```js
|
1685
|
+
* hb.when('GET', '/url1').respond(200, 'content', {});
|
1686
|
+
* hb.when('GET', '/url1').respond(201, 'another', {});
|
1687
|
+
* hb('GET', '/url1'); // receives "content"
|
1688
|
+
*
|
1689
|
+
* $http.matchLatestDefinitionEnabled(true)
|
1690
|
+
* hb('GET', '/url1'); // receives "another"
|
1691
|
+
*
|
1692
|
+
* hb.when('GET', '/url1').respond(201, 'onemore', {});
|
1693
|
+
* hb('GET', '/url1'); // receives "onemore"
|
1694
|
+
* ```
|
1695
|
+
*
|
1696
|
+
* This is useful if a you have a default response that is overriden inside specific tests.
|
1697
|
+
*
|
1698
|
+
* Note that different from config methods on providers, `matchLatestDefinitionEnabled()` can be changed
|
1699
|
+
* even when the application is already running.
|
1700
|
+
*
|
1701
|
+
* @param {Boolean=} value value to set, either `true` or `false`. Default is `false`.
|
1702
|
+
* If omitted, it will return the current value.
|
1703
|
+
* @return {$httpBackend|Boolean} self when used as a setter, and the current value when used
|
1704
|
+
* as a getter
|
1705
|
+
*/
|
1706
|
+
$httpBackend.matchLatestDefinitionEnabled = function(value) {
|
1707
|
+
if (angular.isDefined(value)) {
|
1708
|
+
matchLatestDefinition = value;
|
1709
|
+
return this;
|
1710
|
+
} else {
|
1711
|
+
return matchLatestDefinition;
|
1712
|
+
}
|
1713
|
+
};
|
1714
|
+
|
1472
1715
|
/**
|
1473
1716
|
* @ngdoc method
|
1474
1717
|
* @name $httpBackend#whenGET
|
1475
1718
|
* @description
|
1476
1719
|
* Creates a new backend definition for GET requests. For more info see `when()`.
|
1477
1720
|
*
|
1478
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1721
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1479
1722
|
* and returns true if the url matches the current definition.
|
1480
|
-
* @param {(Object|function(Object))=} headers HTTP headers
|
1723
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1724
|
+
* object and returns true if the headers match the current definition.
|
1481
1725
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1482
1726
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1483
1727
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1490,9 +1734,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1490
1734
|
* @description
|
1491
1735
|
* Creates a new backend definition for HEAD requests. For more info see `when()`.
|
1492
1736
|
*
|
1493
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1737
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1494
1738
|
* and returns true if the url matches the current definition.
|
1495
|
-
* @param {(Object|function(Object))=} headers HTTP headers
|
1739
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1740
|
+
* object and returns true if the headers match the current definition.
|
1496
1741
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1497
1742
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1498
1743
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1505,9 +1750,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1505
1750
|
* @description
|
1506
1751
|
* Creates a new backend definition for DELETE requests. For more info see `when()`.
|
1507
1752
|
*
|
1508
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1753
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1509
1754
|
* and returns true if the url matches the current definition.
|
1510
|
-
* @param {(Object|function(Object))=} headers HTTP headers
|
1755
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1756
|
+
* object and returns true if the headers match the current definition.
|
1511
1757
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1512
1758
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1513
1759
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1520,11 +1766,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1520
1766
|
* @description
|
1521
1767
|
* Creates a new backend definition for POST requests. For more info see `when()`.
|
1522
1768
|
*
|
1523
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1769
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1524
1770
|
* and returns true if the url matches the current definition.
|
1525
1771
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
1526
1772
|
* data string and returns true if the data is as expected.
|
1527
|
-
* @param {(Object|function(Object))=} headers HTTP headers
|
1773
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1774
|
+
* object and returns true if the headers match the current definition.
|
1528
1775
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1529
1776
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1530
1777
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1537,11 +1784,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1537
1784
|
* @description
|
1538
1785
|
* Creates a new backend definition for PUT requests. For more info see `when()`.
|
1539
1786
|
*
|
1540
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1787
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1541
1788
|
* and returns true if the url matches the current definition.
|
1542
1789
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
1543
1790
|
* data string and returns true if the data is as expected.
|
1544
|
-
* @param {(Object|function(Object))=} headers HTTP headers
|
1791
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1792
|
+
* object and returns true if the headers match the current definition.
|
1545
1793
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1546
1794
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1547
1795
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1554,7 +1802,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1554
1802
|
* @description
|
1555
1803
|
* Creates a new backend definition for JSONP requests. For more info see `when()`.
|
1556
1804
|
*
|
1557
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1805
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1558
1806
|
* and returns true if the url matches the current definition.
|
1559
1807
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1560
1808
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
@@ -1573,42 +1821,13 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1573
1821
|
* @param {string} url HTTP url string that supports colon param matching.
|
1574
1822
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1575
1823
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
1576
|
-
* order to change how a matched request is handled.
|
1824
|
+
* order to change how a matched request is handled.
|
1825
|
+
* See {@link ngMock.$httpBackend#when `when`} for more info.
|
1577
1826
|
*/
|
1578
1827
|
$httpBackend.whenRoute = function(method, url) {
|
1579
|
-
var
|
1580
|
-
return $httpBackend.when(method,
|
1581
|
-
};
|
1582
|
-
|
1583
|
-
function parseRoute(url) {
|
1584
|
-
var ret = {
|
1585
|
-
regexp: url
|
1586
|
-
},
|
1587
|
-
keys = ret.keys = [];
|
1588
|
-
|
1589
|
-
if (!url || !angular.isString(url)) return ret;
|
1590
|
-
|
1591
|
-
url = url
|
1592
|
-
.replace(/([().])/g, '\\$1')
|
1593
|
-
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
|
1594
|
-
var optional = option === '?' ? option : null;
|
1595
|
-
var star = option === '*' ? option : null;
|
1596
|
-
keys.push({ name: key, optional: !!optional });
|
1597
|
-
slash = slash || '';
|
1598
|
-
return ''
|
1599
|
-
+ (optional ? '' : slash)
|
1600
|
-
+ '(?:'
|
1601
|
-
+ (optional ? slash : '')
|
1602
|
-
+ (star && '(.+?)' || '([^/]+)')
|
1603
|
-
+ (optional || '')
|
1604
|
-
+ ')'
|
1605
|
-
+ (optional || '');
|
1606
|
-
})
|
1607
|
-
.replace(/([\/$\*])/g, '\\$1');
|
1608
|
-
|
1609
|
-
ret.regexp = new RegExp('^' + url, 'i');
|
1610
|
-
return ret;
|
1611
|
-
}
|
1828
|
+
var parsed = parseRouteUrl(url);
|
1829
|
+
return $httpBackend.when(method, parsed.regexp, undefined, undefined, parsed.keys);
|
1830
|
+
};
|
1612
1831
|
|
1613
1832
|
/**
|
1614
1833
|
* @ngdoc method
|
@@ -1617,7 +1836,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1617
1836
|
* Creates a new request expectation.
|
1618
1837
|
*
|
1619
1838
|
* @param {string} method HTTP method.
|
1620
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1839
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1621
1840
|
* and returns true if the url matches the current definition.
|
1622
1841
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1623
1842
|
* receives data string and returns true if the data is as expected, or Object if request body
|
@@ -1630,16 +1849,20 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1630
1849
|
* order to change how a matched request is handled.
|
1631
1850
|
*
|
1632
1851
|
* - respond –
|
1633
|
-
*
|
1634
|
-
*
|
1635
|
-
*
|
1636
|
-
*
|
1852
|
+
* ```js
|
1853
|
+
* {function([status,] data[, headers, statusText])
|
1854
|
+
* | function(function(method, url, data, headers, params)}
|
1855
|
+
* ```
|
1637
1856
|
* – The respond method takes a set of static data to be returned or a function that can
|
1638
1857
|
* return an array containing response status (number), response data (Array|Object|string),
|
1639
|
-
* response headers (Object),
|
1640
|
-
*
|
1858
|
+
* response headers (Object), HTTP status text (string), and XMLHttpRequest status (string:
|
1859
|
+
* `complete`, `error`, `timeout` or `abort`). The respond method returns the `requestHandler`
|
1860
|
+
* object for possible overrides.
|
1641
1861
|
*/
|
1642
1862
|
$httpBackend.expect = function(method, url, data, headers, keys) {
|
1863
|
+
|
1864
|
+
assertArgDefined(arguments, 1, 'url');
|
1865
|
+
|
1643
1866
|
var expectation = new MockHttpExpectation(method, url, data, headers, keys),
|
1644
1867
|
chain = {
|
1645
1868
|
respond: function(status, data, headers, statusText) {
|
@@ -1658,9 +1881,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1658
1881
|
* @description
|
1659
1882
|
* Creates a new request expectation for GET requests. For more info see `expect()`.
|
1660
1883
|
*
|
1661
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1662
|
-
* and returns true if the url matches the current
|
1663
|
-
* @param {Object=} headers HTTP headers
|
1884
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1885
|
+
* and returns true if the url matches the current expectation.
|
1886
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1887
|
+
* object and returns true if the headers match the current expectation.
|
1664
1888
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1665
1889
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1666
1890
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1673,9 +1897,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1673
1897
|
* @description
|
1674
1898
|
* Creates a new request expectation for HEAD requests. For more info see `expect()`.
|
1675
1899
|
*
|
1676
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1677
|
-
* and returns true if the url matches the current
|
1678
|
-
* @param {Object=} headers HTTP headers
|
1900
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1901
|
+
* and returns true if the url matches the current expectation.
|
1902
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1903
|
+
* object and returns true if the headers match the current expectation.
|
1679
1904
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1680
1905
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1681
1906
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1688,9 +1913,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1688
1913
|
* @description
|
1689
1914
|
* Creates a new request expectation for DELETE requests. For more info see `expect()`.
|
1690
1915
|
*
|
1691
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1692
|
-
* and returns true if the url matches the current
|
1693
|
-
* @param {Object=} headers HTTP headers
|
1916
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1917
|
+
* and returns true if the url matches the current expectation.
|
1918
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1919
|
+
* object and returns true if the headers match the current expectation.
|
1694
1920
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1695
1921
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1696
1922
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1703,12 +1929,13 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1703
1929
|
* @description
|
1704
1930
|
* Creates a new request expectation for POST requests. For more info see `expect()`.
|
1705
1931
|
*
|
1706
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1707
|
-
* and returns true if the url matches the current
|
1932
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1933
|
+
* and returns true if the url matches the current expectation.
|
1708
1934
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1709
1935
|
* receives data string and returns true if the data is as expected, or Object if request body
|
1710
1936
|
* is in JSON format.
|
1711
|
-
* @param {Object=} headers HTTP headers
|
1937
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1938
|
+
* object and returns true if the headers match the current expectation.
|
1712
1939
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1713
1940
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1714
1941
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1721,12 +1948,13 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1721
1948
|
* @description
|
1722
1949
|
* Creates a new request expectation for PUT requests. For more info see `expect()`.
|
1723
1950
|
*
|
1724
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1725
|
-
* and returns true if the url matches the current
|
1951
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1952
|
+
* and returns true if the url matches the current expectation.
|
1726
1953
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1727
1954
|
* receives data string and returns true if the data is as expected, or Object if request body
|
1728
1955
|
* is in JSON format.
|
1729
|
-
* @param {Object=} headers HTTP headers
|
1956
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1957
|
+
* object and returns true if the headers match the current expectation.
|
1730
1958
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1731
1959
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1732
1960
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1739,12 +1967,13 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1739
1967
|
* @description
|
1740
1968
|
* Creates a new request expectation for PATCH requests. For more info see `expect()`.
|
1741
1969
|
*
|
1742
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
1743
|
-
* and returns true if the url matches the current
|
1970
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1971
|
+
* and returns true if the url matches the current expectation.
|
1744
1972
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1745
1973
|
* receives data string and returns true if the data is as expected, or Object if request body
|
1746
1974
|
* is in JSON format.
|
1747
|
-
* @param {Object=} headers HTTP headers
|
1975
|
+
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
1976
|
+
* object and returns true if the headers match the current expectation.
|
1748
1977
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1749
1978
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1750
1979
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1757,8 +1986,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1757
1986
|
* @description
|
1758
1987
|
* Creates a new request expectation for JSONP requests. For more info see `expect()`.
|
1759
1988
|
*
|
1760
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives an url
|
1761
|
-
* and returns true if the url matches the current
|
1989
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives an url
|
1990
|
+
* and returns true if the url matches the current expectation.
|
1762
1991
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1763
1992
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1764
1993
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1776,11 +2005,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1776
2005
|
* @param {string} url HTTP url string that supports colon param matching.
|
1777
2006
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1778
2007
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
1779
|
-
* order to change how a matched request is handled.
|
2008
|
+
* order to change how a matched request is handled.
|
2009
|
+
* See {@link ngMock.$httpBackend#expect `expect`} for more info.
|
1780
2010
|
*/
|
1781
2011
|
$httpBackend.expectRoute = function(method, url) {
|
1782
|
-
var
|
1783
|
-
return $httpBackend.expect(method,
|
2012
|
+
var parsed = parseRouteUrl(url);
|
2013
|
+
return $httpBackend.expect(method, parsed.regexp, undefined, undefined, parsed.keys);
|
1784
2014
|
};
|
1785
2015
|
|
1786
2016
|
|
@@ -1788,24 +2018,34 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1788
2018
|
* @ngdoc method
|
1789
2019
|
* @name $httpBackend#flush
|
1790
2020
|
* @description
|
1791
|
-
* Flushes
|
1792
|
-
*
|
1793
|
-
*
|
1794
|
-
*
|
1795
|
-
*
|
2021
|
+
* Flushes pending requests using the trained responses. Requests are flushed in the order they
|
2022
|
+
* were made, but it is also possible to skip one or more requests (for example to have them
|
2023
|
+
* flushed later). This is useful for simulating scenarios where responses arrive from the server
|
2024
|
+
* in any order.
|
2025
|
+
*
|
2026
|
+
* If there are no pending requests to flush when the method is called, an exception is thrown (as
|
2027
|
+
* this is typically a sign of programming error).
|
2028
|
+
*
|
2029
|
+
* @param {number=} count - Number of responses to flush. If undefined/null, all pending requests
|
2030
|
+
* (starting after `skip`) will be flushed.
|
2031
|
+
* @param {number=} [skip=0] - Number of pending requests to skip. For example, a value of `5`
|
2032
|
+
* would skip the first 5 pending requests and start flushing from the 6th onwards.
|
1796
2033
|
*/
|
1797
|
-
$httpBackend.flush = function(count, digest) {
|
2034
|
+
$httpBackend.flush = function(count, skip, digest) {
|
1798
2035
|
if (digest !== false) $rootScope.$digest();
|
1799
|
-
|
2036
|
+
|
2037
|
+
skip = skip || 0;
|
2038
|
+
if (skip >= responses.length) throw new Error('No pending request to flush !');
|
1800
2039
|
|
1801
2040
|
if (angular.isDefined(count) && count !== null) {
|
1802
2041
|
while (count--) {
|
1803
|
-
|
1804
|
-
|
2042
|
+
var part = responses.splice(skip, 1);
|
2043
|
+
if (!part.length) throw new Error('No more pending request to flush !');
|
2044
|
+
part[0]();
|
1805
2045
|
}
|
1806
2046
|
} else {
|
1807
|
-
while (responses.length) {
|
1808
|
-
responses.
|
2047
|
+
while (responses.length > skip) {
|
2048
|
+
responses.splice(skip, 1)[0]();
|
1809
2049
|
}
|
1810
2050
|
}
|
1811
2051
|
$httpBackend.verifyNoOutstandingExpectation(digest);
|
@@ -1847,9 +2087,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1847
2087
|
* afterEach($httpBackend.verifyNoOutstandingRequest);
|
1848
2088
|
* ```
|
1849
2089
|
*/
|
1850
|
-
$httpBackend.verifyNoOutstandingRequest = function() {
|
2090
|
+
$httpBackend.verifyNoOutstandingRequest = function(digest) {
|
2091
|
+
if (digest !== false) $rootScope.$digest();
|
1851
2092
|
if (responses.length) {
|
1852
|
-
|
2093
|
+
var unflushedDescriptions = responses.map(function(res) { return res.description; });
|
2094
|
+
throw new Error('Unflushed requests: ' + responses.length + '\n ' +
|
2095
|
+
unflushedDescriptions.join('\n '));
|
1853
2096
|
}
|
1854
2097
|
};
|
1855
2098
|
|
@@ -1867,125 +2110,166 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1867
2110
|
responses.length = 0;
|
1868
2111
|
};
|
1869
2112
|
|
2113
|
+
$httpBackend.$$originalHttpBackend = originalHttpBackend;
|
2114
|
+
|
1870
2115
|
return $httpBackend;
|
1871
2116
|
|
1872
2117
|
|
1873
2118
|
function createShortMethods(prefix) {
|
1874
2119
|
angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
|
1875
2120
|
$httpBackend[prefix + method] = function(url, headers, keys) {
|
2121
|
+
assertArgDefined(arguments, 0, 'url');
|
2122
|
+
|
2123
|
+
// Change url to `null` if `undefined` to stop it throwing an exception further down
|
2124
|
+
if (angular.isUndefined(url)) url = null;
|
2125
|
+
|
1876
2126
|
return $httpBackend[prefix](method, url, undefined, headers, keys);
|
1877
2127
|
};
|
1878
2128
|
});
|
1879
2129
|
|
1880
2130
|
angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
|
1881
2131
|
$httpBackend[prefix + method] = function(url, data, headers, keys) {
|
2132
|
+
assertArgDefined(arguments, 0, 'url');
|
2133
|
+
|
2134
|
+
// Change url to `null` if `undefined` to stop it throwing an exception further down
|
2135
|
+
if (angular.isUndefined(url)) url = null;
|
2136
|
+
|
1882
2137
|
return $httpBackend[prefix](method, url, data, headers, keys);
|
1883
2138
|
};
|
1884
2139
|
});
|
1885
2140
|
}
|
1886
|
-
}
|
1887
|
-
|
1888
|
-
function MockHttpExpectation(method, url, data, headers, keys) {
|
1889
2141
|
|
1890
|
-
function
|
1891
|
-
var
|
1892
|
-
|
2142
|
+
function parseRouteUrl(url) {
|
2143
|
+
var strippedUrl = stripQueryAndHash(url);
|
2144
|
+
var parseOptions = {caseInsensitiveMatch: true, ignoreTrailingSlashes: true};
|
2145
|
+
return routeToRegExp(strippedUrl, parseOptions);
|
1893
2146
|
}
|
2147
|
+
}
|
1894
2148
|
|
1895
|
-
|
1896
|
-
|
2149
|
+
function assertArgDefined(args, index, name) {
|
2150
|
+
if (args.length > index && angular.isUndefined(args[index])) {
|
2151
|
+
throw new Error('Undefined argument `' + name + '`; the argument is provided but not defined');
|
1897
2152
|
}
|
2153
|
+
}
|
1898
2154
|
|
1899
|
-
|
1900
|
-
|
2155
|
+
function stripQueryAndHash(url) {
|
2156
|
+
return url.replace(/[?#].*$/, '');
|
2157
|
+
}
|
2158
|
+
|
2159
|
+
function MockHttpExpectation(expectedMethod, expectedUrl, expectedData, expectedHeaders,
|
2160
|
+
expectedKeys) {
|
2161
|
+
|
2162
|
+
this.data = expectedData;
|
2163
|
+
this.headers = expectedHeaders;
|
1901
2164
|
|
1902
|
-
this.match = function(
|
1903
|
-
if (
|
1904
|
-
if (!this.matchUrl(
|
1905
|
-
if (angular.isDefined(
|
1906
|
-
if (angular.isDefined(
|
2165
|
+
this.match = function(method, url, data, headers) {
|
2166
|
+
if (expectedMethod !== method) return false;
|
2167
|
+
if (!this.matchUrl(url)) return false;
|
2168
|
+
if (angular.isDefined(data) && !this.matchData(data)) return false;
|
2169
|
+
if (angular.isDefined(headers) && !this.matchHeaders(headers)) return false;
|
1907
2170
|
return true;
|
1908
2171
|
};
|
1909
2172
|
|
1910
|
-
this.matchUrl = function(
|
1911
|
-
if (!
|
1912
|
-
if (angular.isFunction(
|
1913
|
-
if (angular.isFunction(
|
1914
|
-
return (
|
2173
|
+
this.matchUrl = function(url) {
|
2174
|
+
if (!expectedUrl) return true;
|
2175
|
+
if (angular.isFunction(expectedUrl.test)) return expectedUrl.test(url);
|
2176
|
+
if (angular.isFunction(expectedUrl)) return expectedUrl(url);
|
2177
|
+
return (expectedUrl === url || compareUrlWithQuery(url));
|
1915
2178
|
};
|
1916
2179
|
|
1917
|
-
this.matchHeaders = function(
|
1918
|
-
if (angular.isUndefined(
|
1919
|
-
if (angular.isFunction(
|
1920
|
-
return angular.equals(
|
2180
|
+
this.matchHeaders = function(headers) {
|
2181
|
+
if (angular.isUndefined(expectedHeaders)) return true;
|
2182
|
+
if (angular.isFunction(expectedHeaders)) return expectedHeaders(headers);
|
2183
|
+
return angular.equals(expectedHeaders, headers);
|
1921
2184
|
};
|
1922
2185
|
|
1923
|
-
this.matchData = function(
|
1924
|
-
if (angular.isUndefined(
|
1925
|
-
if (
|
1926
|
-
if (
|
1927
|
-
if (
|
1928
|
-
return angular.equals(angular.fromJson(angular.toJson(
|
2186
|
+
this.matchData = function(data) {
|
2187
|
+
if (angular.isUndefined(expectedData)) return true;
|
2188
|
+
if (expectedData && angular.isFunction(expectedData.test)) return expectedData.test(data);
|
2189
|
+
if (expectedData && angular.isFunction(expectedData)) return expectedData(data);
|
2190
|
+
if (expectedData && !angular.isString(expectedData)) {
|
2191
|
+
return angular.equals(angular.fromJson(angular.toJson(expectedData)), angular.fromJson(data));
|
1929
2192
|
}
|
1930
|
-
|
2193
|
+
// eslint-disable-next-line eqeqeq
|
2194
|
+
return expectedData == data;
|
1931
2195
|
};
|
1932
2196
|
|
1933
2197
|
this.toString = function() {
|
1934
|
-
return
|
2198
|
+
return expectedMethod + ' ' + expectedUrl;
|
1935
2199
|
};
|
1936
2200
|
|
1937
|
-
this.params = function(
|
1938
|
-
|
2201
|
+
this.params = function(url) {
|
2202
|
+
var queryStr = url.indexOf('?') === -1 ? '' : url.substring(url.indexOf('?') + 1);
|
2203
|
+
var strippedUrl = stripQueryAndHash(url);
|
1939
2204
|
|
1940
|
-
|
1941
|
-
|
1942
|
-
if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
|
2205
|
+
return angular.extend(extractParamsFromQuery(queryStr), extractParamsFromPath(strippedUrl));
|
2206
|
+
};
|
1943
2207
|
|
1944
|
-
|
1945
|
-
|
1946
|
-
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
1950
|
-
|
1951
|
-
|
1952
|
-
|
2208
|
+
function compareUrlWithQuery(url) {
|
2209
|
+
var urlWithQueryRe = /^([^?]*)\?(.*)$/;
|
2210
|
+
|
2211
|
+
var expectedMatch = urlWithQueryRe.exec(expectedUrl);
|
2212
|
+
var actualMatch = urlWithQueryRe.exec(url);
|
2213
|
+
|
2214
|
+
return !!(expectedMatch && actualMatch) &&
|
2215
|
+
(expectedMatch[1] === actualMatch[1]) &&
|
2216
|
+
(normalizeQuery(expectedMatch[2]) === normalizeQuery(actualMatch[2]));
|
2217
|
+
}
|
2218
|
+
|
2219
|
+
function normalizeQuery(queryStr) {
|
2220
|
+
return queryStr.split('&').sort().join('&');
|
2221
|
+
}
|
1953
2222
|
|
1954
|
-
|
2223
|
+
function extractParamsFromPath(strippedUrl) {
|
2224
|
+
var keyObj = {};
|
2225
|
+
|
2226
|
+
if (!expectedUrl || !angular.isFunction(expectedUrl.test) ||
|
2227
|
+
!expectedKeys || !expectedKeys.length) return keyObj;
|
2228
|
+
|
2229
|
+
var match = expectedUrl.exec(strippedUrl);
|
2230
|
+
if (!match) return keyObj;
|
2231
|
+
|
2232
|
+
for (var i = 1, len = match.length; i < len; ++i) {
|
2233
|
+
var key = expectedKeys[i - 1];
|
2234
|
+
var val = match[i];
|
2235
|
+
if (key && val) {
|
2236
|
+
keyObj[key.name || key] = val;
|
2237
|
+
}
|
1955
2238
|
}
|
1956
2239
|
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
1968
|
-
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1975
|
-
|
1976
|
-
}
|
2240
|
+
return keyObj;
|
2241
|
+
}
|
2242
|
+
|
2243
|
+
function extractParamsFromQuery(queryStr) {
|
2244
|
+
var obj = {},
|
2245
|
+
keyValuePairs = queryStr.split('&').
|
2246
|
+
filter(angular.identity). // Ignore empty segments.
|
2247
|
+
map(function(keyValue) { return keyValue.replace(/\+/g, '%20').split('='); });
|
2248
|
+
|
2249
|
+
angular.forEach(keyValuePairs, function(pair) {
|
2250
|
+
var key = tryDecodeURIComponent(pair[0]);
|
2251
|
+
if (angular.isDefined(key)) {
|
2252
|
+
var val = angular.isDefined(pair[1]) ? tryDecodeURIComponent(pair[1]) : true;
|
2253
|
+
if (!hasOwnProperty.call(obj, key)) {
|
2254
|
+
obj[key] = val;
|
2255
|
+
} else if (angular.isArray(obj[key])) {
|
2256
|
+
obj[key].push(val);
|
2257
|
+
} else {
|
2258
|
+
obj[key] = [obj[key], val];
|
1977
2259
|
}
|
1978
|
-
});
|
1979
|
-
return obj;
|
1980
|
-
}
|
1981
|
-
function tryDecodeURIComponent(value) {
|
1982
|
-
try {
|
1983
|
-
return decodeURIComponent(value);
|
1984
|
-
} catch (e) {
|
1985
|
-
// Ignore any invalid uri component
|
1986
2260
|
}
|
2261
|
+
});
|
2262
|
+
|
2263
|
+
return obj;
|
2264
|
+
}
|
2265
|
+
|
2266
|
+
function tryDecodeURIComponent(value) {
|
2267
|
+
try {
|
2268
|
+
return decodeURIComponent(value);
|
2269
|
+
} catch (e) {
|
2270
|
+
// Ignore any invalid uri component
|
1987
2271
|
}
|
1988
|
-
}
|
2272
|
+
}
|
1989
2273
|
}
|
1990
2274
|
|
1991
2275
|
function createMockXhr() {
|
@@ -2019,13 +2303,13 @@ function MockXhr() {
|
|
2019
2303
|
var header = this.$$respHeaders[name];
|
2020
2304
|
if (header) return header;
|
2021
2305
|
|
2022
|
-
name = angular
|
2306
|
+
name = angular.$$lowercase(name);
|
2023
2307
|
header = this.$$respHeaders[name];
|
2024
2308
|
if (header) return header;
|
2025
2309
|
|
2026
2310
|
header = undefined;
|
2027
2311
|
angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
|
2028
|
-
if (!header && angular
|
2312
|
+
if (!header && angular.$$lowercase(headerName) === name) header = headerVal;
|
2029
2313
|
});
|
2030
2314
|
return header;
|
2031
2315
|
};
|
@@ -2039,10 +2323,14 @@ function MockXhr() {
|
|
2039
2323
|
return lines.join('\n');
|
2040
2324
|
};
|
2041
2325
|
|
2042
|
-
this.abort =
|
2326
|
+
this.abort = function() {
|
2327
|
+
if (isFunction(this.onabort)) {
|
2328
|
+
this.onabort();
|
2329
|
+
}
|
2330
|
+
};
|
2043
2331
|
|
2044
2332
|
// This section simulates the events on a real XHR object (and the upload object)
|
2045
|
-
// When we are testing $httpBackend (inside the
|
2333
|
+
// When we are testing $httpBackend (inside the AngularJS project) we make partial use of this
|
2046
2334
|
// but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
|
2047
2335
|
this.$$events = {};
|
2048
2336
|
this.addEventListener = function(name, listener) {
|
@@ -2071,39 +2359,86 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
|
|
2071
2359
|
/**
|
2072
2360
|
* @ngdoc method
|
2073
2361
|
* @name $timeout#flush
|
2362
|
+
*
|
2363
|
+
* @deprecated
|
2364
|
+
* sinceVersion="1.7.3"
|
2365
|
+
*
|
2366
|
+
* This method flushes all types of tasks (not only timeouts), which is unintuitive.
|
2367
|
+
* It is recommended to use {@link ngMock.$flushPendingTasks} instead.
|
2368
|
+
*
|
2074
2369
|
* @description
|
2075
2370
|
*
|
2076
2371
|
* Flushes the queue of pending tasks.
|
2077
2372
|
*
|
2373
|
+
* _This method is essentially an alias of {@link ngMock.$flushPendingTasks}._
|
2374
|
+
*
|
2375
|
+
* <div class="alert alert-warning">
|
2376
|
+
* For historical reasons, this method will also flush non-`$timeout` pending tasks, such as
|
2377
|
+
* {@link $q} promises and tasks scheduled via
|
2378
|
+
* {@link ng.$rootScope.Scope#$applyAsync $applyAsync} and
|
2379
|
+
* {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
2380
|
+
* </div>
|
2381
|
+
*
|
2078
2382
|
* @param {number=} delay maximum timeout amount to flush up until
|
2079
2383
|
*/
|
2080
2384
|
$delegate.flush = function(delay) {
|
2385
|
+
// For historical reasons, `$timeout.flush()` flushes all types of pending tasks.
|
2386
|
+
// Keep the same behavior for backwards compatibility (and because it doesn't make sense to
|
2387
|
+
// selectively flush scheduled events out of order).
|
2081
2388
|
$browser.defer.flush(delay);
|
2082
2389
|
};
|
2083
2390
|
|
2084
2391
|
/**
|
2085
2392
|
* @ngdoc method
|
2086
2393
|
* @name $timeout#verifyNoPendingTasks
|
2394
|
+
*
|
2395
|
+
* @deprecated
|
2396
|
+
* sinceVersion="1.7.3"
|
2397
|
+
*
|
2398
|
+
* This method takes all types of tasks (not only timeouts) into account, which is unintuitive.
|
2399
|
+
* It is recommended to use {@link ngMock.$verifyNoPendingTasks} instead, which additionally
|
2400
|
+
* allows checking for timeouts only (with `$verifyNoPendingTasks('$timeout')`).
|
2401
|
+
*
|
2087
2402
|
* @description
|
2088
2403
|
*
|
2089
|
-
* Verifies that there are no pending tasks that need to be flushed.
|
2404
|
+
* Verifies that there are no pending tasks that need to be flushed. It throws an error if there
|
2405
|
+
* are still pending tasks.
|
2406
|
+
*
|
2407
|
+
* _This method is essentially an alias of {@link ngMock.$verifyNoPendingTasks} (called with no
|
2408
|
+
* arguments)._
|
2409
|
+
*
|
2410
|
+
* <div class="alert alert-warning">
|
2411
|
+
* <p>
|
2412
|
+
* For historical reasons, this method will also verify non-`$timeout` pending tasks, such as
|
2413
|
+
* pending {@link $http} requests, in-progress {@link $route} transitions, unresolved
|
2414
|
+
* {@link $q} promises and tasks scheduled via
|
2415
|
+
* {@link ng.$rootScope.Scope#$applyAsync $applyAsync} and
|
2416
|
+
* {@link ng.$rootScope.Scope#$evalAsync $evalAsync}.
|
2417
|
+
* </p>
|
2418
|
+
* <p>
|
2419
|
+
* It is recommended to use {@link ngMock.$verifyNoPendingTasks} instead, which additionally
|
2420
|
+
* supports verifying a specific type of tasks. For example, you can verify there are no
|
2421
|
+
* pending timeouts with `$verifyNoPendingTasks('$timeout')`.
|
2422
|
+
* </p>
|
2423
|
+
* </div>
|
2090
2424
|
*/
|
2091
2425
|
$delegate.verifyNoPendingTasks = function() {
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2426
|
+
// For historical reasons, `$timeout.verifyNoPendingTasks()` takes all types of pending tasks
|
2427
|
+
// into account. Keep the same behavior for backwards compatibility.
|
2428
|
+
var pendingTasks = $browser.defer.getPendingTasks();
|
2429
|
+
|
2430
|
+
if (pendingTasks.length) {
|
2431
|
+
var formattedTasks = $browser.defer.formatPendingTasks(pendingTasks).join('\n ');
|
2432
|
+
var hasPendingTimeout = pendingTasks.some(function(task) { return task.type === '$timeout'; });
|
2433
|
+
var extraMessage = hasPendingTimeout ? '' : '\n\nNone of the pending tasks are timeouts. ' +
|
2434
|
+
'If you only want to verify pending timeouts, use ' +
|
2435
|
+
'`$verifyNoPendingTasks(\'$timeout\')` instead.';
|
2436
|
+
|
2437
|
+
throw new Error('Deferred tasks to flush (' + pendingTasks.length + '):\n ' +
|
2438
|
+
formattedTasks + extraMessage);
|
2095
2439
|
}
|
2096
2440
|
};
|
2097
2441
|
|
2098
|
-
function formatPendingTasksAsString(tasks) {
|
2099
|
-
var result = [];
|
2100
|
-
angular.forEach(tasks, function(task) {
|
2101
|
-
result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
|
2102
|
-
});
|
2103
|
-
|
2104
|
-
return result.join(', ');
|
2105
|
-
}
|
2106
|
-
|
2107
2442
|
return $delegate;
|
2108
2443
|
}];
|
2109
2444
|
|
@@ -2153,7 +2488,6 @@ angular.mock.$RootElementProvider = function() {
|
|
2153
2488
|
* A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
|
2154
2489
|
* controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
|
2155
2490
|
*
|
2156
|
-
*
|
2157
2491
|
* ## Example
|
2158
2492
|
*
|
2159
2493
|
* ```js
|
@@ -2171,18 +2505,24 @@ angular.mock.$RootElementProvider = function() {
|
|
2171
2505
|
* // Controller definition ...
|
2172
2506
|
*
|
2173
2507
|
* myMod.controller('MyDirectiveController', ['$log', function($log) {
|
2174
|
-
*
|
2508
|
+
* this.log = function() {
|
2509
|
+
* $log.info(this.name);
|
2510
|
+
* };
|
2175
2511
|
* }]);
|
2176
2512
|
*
|
2177
2513
|
*
|
2178
2514
|
* // In a test ...
|
2179
2515
|
*
|
2180
2516
|
* describe('myDirectiveController', function() {
|
2181
|
-
*
|
2182
|
-
*
|
2183
|
-
*
|
2184
|
-
*
|
2185
|
-
*
|
2517
|
+
* describe('log()', function() {
|
2518
|
+
* it('should write the bound name to the log', inject(function($controller, $log) {
|
2519
|
+
* var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
|
2520
|
+
* ctrl.log();
|
2521
|
+
*
|
2522
|
+
* expect(ctrl.name).toEqual('Clark Kent');
|
2523
|
+
* expect($log.info.logs).toEqual(['Clark Kent']);
|
2524
|
+
* }));
|
2525
|
+
* });
|
2186
2526
|
* });
|
2187
2527
|
*
|
2188
2528
|
* ```
|
@@ -2193,45 +2533,51 @@ angular.mock.$RootElementProvider = function() {
|
|
2193
2533
|
*
|
2194
2534
|
* * check if a controller with given name is registered via `$controllerProvider`
|
2195
2535
|
* * check if evaluating the string on the current scope returns a constructor
|
2196
|
-
* * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
|
2197
|
-
* `window` object (not recommended)
|
2198
2536
|
*
|
2199
2537
|
* The string can use the `controller as property` syntax, where the controller instance is published
|
2200
2538
|
* as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
|
2201
2539
|
* to work correctly.
|
2202
2540
|
*
|
2203
2541
|
* @param {Object} locals Injection locals for Controller.
|
2204
|
-
* @param {Object=} bindings Properties to add to the controller
|
2205
|
-
*
|
2542
|
+
* @param {Object=} bindings Properties to add to the controller instance. This is used to simulate
|
2543
|
+
* the `bindToController` feature and simplify certain kinds of tests.
|
2206
2544
|
* @return {Object} Instance of given controller.
|
2207
2545
|
*/
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
|
2212
|
-
|
2213
|
-
|
2214
|
-
var instance = instantiate();
|
2215
|
-
if (instance !== instantiate.instance) {
|
2546
|
+
function createControllerDecorator() {
|
2547
|
+
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
2548
|
+
return function(expression, locals, later, ident) {
|
2549
|
+
if (later && typeof later === 'object') {
|
2550
|
+
var instantiate = $delegate(expression, locals, true, ident);
|
2551
|
+
var instance = instantiate();
|
2216
2552
|
angular.extend(instance, later);
|
2553
|
+
return instance;
|
2217
2554
|
}
|
2555
|
+
return $delegate(expression, locals, later, ident);
|
2556
|
+
};
|
2557
|
+
}];
|
2218
2558
|
|
2219
|
-
|
2220
|
-
|
2221
|
-
return $delegate(expression, locals, later, ident);
|
2222
|
-
};
|
2223
|
-
}];
|
2559
|
+
return angular.mock.$ControllerDecorator;
|
2560
|
+
}
|
2224
2561
|
|
2225
2562
|
/**
|
2226
2563
|
* @ngdoc service
|
2227
2564
|
* @name $componentController
|
2228
2565
|
* @description
|
2229
|
-
* A service that can be used to create instances of component controllers.
|
2230
|
-
*
|
2566
|
+
* A service that can be used to create instances of component controllers. Useful for unit-testing.
|
2567
|
+
*
|
2231
2568
|
* Be aware that the controller will be instantiated and attached to the scope as specified in
|
2232
2569
|
* the component definition object. If you do not provide a `$scope` object in the `locals` param
|
2233
2570
|
* then the helper will create a new isolated scope as a child of `$rootScope`.
|
2234
|
-
*
|
2571
|
+
*
|
2572
|
+
* If you are using `$element` or `$attrs` in the controller, make sure to provide them as `locals`.
|
2573
|
+
* The `$element` must be a jqLite-wrapped DOM element, and `$attrs` should be an object that
|
2574
|
+
* has all properties / functions that you are using in the controller. If this is getting too complex,
|
2575
|
+
* you should compile the component instead and access the component's controller via the
|
2576
|
+
* {@link angular.element#methods `controller`} function.
|
2577
|
+
*
|
2578
|
+
* See also the section on {@link guide/component#unit-testing-component-controllers unit-testing component controllers}
|
2579
|
+
* in the guide.
|
2580
|
+
*
|
2235
2581
|
* @param {string} componentName the name of the component whose controller we want to instantiate
|
2236
2582
|
* @param {Object} locals Injection locals for Controller.
|
2237
2583
|
* @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
|
@@ -2239,7 +2585,8 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
|
2239
2585
|
* @param {string=} ident Override the property name to use when attaching the controller to the scope.
|
2240
2586
|
* @return {Object} Instance of requested controller.
|
2241
2587
|
*/
|
2242
|
-
angular.mock.$ComponentControllerProvider = ['$compileProvider',
|
2588
|
+
angular.mock.$ComponentControllerProvider = ['$compileProvider',
|
2589
|
+
function ComponentControllerProvider($compileProvider) {
|
2243
2590
|
this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
|
2244
2591
|
return function $componentController(componentName, locals, bindings, ident) {
|
2245
2592
|
// get all directives associated to the component name
|
@@ -2273,21 +2620,17 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
|
|
2273
2620
|
* @packageName angular-mocks
|
2274
2621
|
* @description
|
2275
2622
|
*
|
2276
|
-
*
|
2277
|
-
*
|
2278
|
-
* The `ngMock` module provides support to inject and mock Angular services into unit tests.
|
2279
|
-
* In addition, ngMock also extends various core ng services such that they can be
|
2623
|
+
* The `ngMock` module provides support to inject and mock AngularJS services into unit tests.
|
2624
|
+
* In addition, ngMock also extends various core AngularJS services such that they can be
|
2280
2625
|
* inspected and controlled in a synchronous manner within test code.
|
2281
2626
|
*
|
2282
|
-
*
|
2283
|
-
* <div doc-module-components="ngMock"></div>
|
2284
|
-
*
|
2285
2627
|
* @installation
|
2286
2628
|
*
|
2287
2629
|
* First, download the file:
|
2288
2630
|
* * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
|
2289
2631
|
* `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
|
2290
2632
|
* * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
|
2633
|
+
* * [Yarn](https://yarnpkg.com) e.g. `yarn add angular-mocks@X.Y.Z`
|
2291
2634
|
* * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
|
2292
2635
|
* * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
|
2293
2636
|
* `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
|
@@ -2316,15 +2659,17 @@ angular.module('ngMock', ['ng']).provider({
|
|
2316
2659
|
$exceptionHandler: angular.mock.$ExceptionHandlerProvider,
|
2317
2660
|
$log: angular.mock.$LogProvider,
|
2318
2661
|
$interval: angular.mock.$IntervalProvider,
|
2319
|
-
$httpBackend: angular.mock.$HttpBackendProvider,
|
2320
2662
|
$rootElement: angular.mock.$RootElementProvider,
|
2321
|
-
$componentController: angular.mock.$ComponentControllerProvider
|
2322
|
-
|
2663
|
+
$componentController: angular.mock.$ComponentControllerProvider,
|
2664
|
+
$flushPendingTasks: angular.mock.$FlushPendingTasksProvider,
|
2665
|
+
$verifyNoPendingTasks: angular.mock.$VerifyNoPendingTasksProvider
|
2666
|
+
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
|
2323
2667
|
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
2324
2668
|
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
2325
2669
|
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
|
2326
|
-
$provide.decorator('$controller',
|
2327
|
-
|
2670
|
+
$provide.decorator('$controller', createControllerDecorator($compileProvider));
|
2671
|
+
$provide.decorator('$httpBackend', angular.mock.$httpBackendDecorator);
|
2672
|
+
}]).info({ angularVersion: '1.8.0' });
|
2328
2673
|
|
2329
2674
|
/**
|
2330
2675
|
* @ngdoc module
|
@@ -2333,14 +2678,13 @@ angular.module('ngMock', ['ng']).provider({
|
|
2333
2678
|
* @packageName angular-mocks
|
2334
2679
|
* @description
|
2335
2680
|
*
|
2336
|
-
* The `ngMockE2E` is an
|
2681
|
+
* The `ngMockE2E` is an AngularJS module which contains mocks suitable for end-to-end testing.
|
2337
2682
|
* Currently there is only one mock present in this module -
|
2338
2683
|
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
|
2339
2684
|
*/
|
2340
2685
|
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
2341
|
-
$provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
|
2342
2686
|
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
2343
|
-
}]);
|
2687
|
+
}]).info({ angularVersion: '1.8.0' });
|
2344
2688
|
|
2345
2689
|
/**
|
2346
2690
|
* @ngdoc service
|
@@ -2387,19 +2731,19 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2387
2731
|
* phones.push(phone);
|
2388
2732
|
* return [200, phone, {}];
|
2389
2733
|
* });
|
2390
|
-
* $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for
|
2734
|
+
* $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templates are handled by the real server
|
2391
2735
|
* //...
|
2392
2736
|
* });
|
2393
2737
|
* ```
|
2394
2738
|
*
|
2395
2739
|
* Afterwards, bootstrap your app with this new module.
|
2396
2740
|
*
|
2397
|
-
*
|
2741
|
+
* @example
|
2398
2742
|
* <example name="httpbackend-e2e-testing" module="myAppE2E" deps="angular-mocks.js">
|
2399
2743
|
* <file name="app.js">
|
2400
2744
|
* var myApp = angular.module('myApp', []);
|
2401
2745
|
*
|
2402
|
-
* myApp.controller('
|
2746
|
+
* myApp.controller('MainCtrl', function MainCtrl($http) {
|
2403
2747
|
* var ctrl = this;
|
2404
2748
|
*
|
2405
2749
|
* ctrl.phones = [];
|
@@ -2441,7 +2785,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2441
2785
|
* });
|
2442
2786
|
* </file>
|
2443
2787
|
* <file name="index.html">
|
2444
|
-
* <div ng-controller="
|
2788
|
+
* <div ng-controller="MainCtrl as $ctrl">
|
2445
2789
|
* <form name="newPhoneForm" ng-submit="$ctrl.addPhone($ctrl.newPhone)">
|
2446
2790
|
* <input type="text" ng-model="$ctrl.newPhone.name">
|
2447
2791
|
* <input type="submit" value="Add Phone">
|
@@ -2465,9 +2809,10 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2465
2809
|
* Creates a new backend definition.
|
2466
2810
|
*
|
2467
2811
|
* @param {string} method HTTP method.
|
2468
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2812
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2469
2813
|
* and returns true if the url matches the current definition.
|
2470
|
-
* @param {(string|RegExp)=} data HTTP request body
|
2814
|
+
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
2815
|
+
* data string and returns true if the data is as expected.
|
2471
2816
|
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
|
2472
2817
|
* object and returns true if the headers match the current definition.
|
2473
2818
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
@@ -2497,7 +2842,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2497
2842
|
* @description
|
2498
2843
|
* Creates a new backend definition for GET requests. For more info see `when()`.
|
2499
2844
|
*
|
2500
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2845
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2501
2846
|
* and returns true if the url matches the current definition.
|
2502
2847
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
2503
2848
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
@@ -2514,7 +2859,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2514
2859
|
* @description
|
2515
2860
|
* Creates a new backend definition for HEAD requests. For more info see `when()`.
|
2516
2861
|
*
|
2517
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2862
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2518
2863
|
* and returns true if the url matches the current definition.
|
2519
2864
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
2520
2865
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
@@ -2531,7 +2876,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2531
2876
|
* @description
|
2532
2877
|
* Creates a new backend definition for DELETE requests. For more info see `when()`.
|
2533
2878
|
*
|
2534
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2879
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2535
2880
|
* and returns true if the url matches the current definition.
|
2536
2881
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
2537
2882
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
@@ -2548,9 +2893,10 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2548
2893
|
* @description
|
2549
2894
|
* Creates a new backend definition for POST requests. For more info see `when()`.
|
2550
2895
|
*
|
2551
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2896
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2552
2897
|
* and returns true if the url matches the current definition.
|
2553
|
-
* @param {(string|RegExp)=} data HTTP request body
|
2898
|
+
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
2899
|
+
* data string and returns true if the data is as expected.
|
2554
2900
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
2555
2901
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
2556
2902
|
* {@link ngMock.$httpBackend $httpBackend mock}.
|
@@ -2566,9 +2912,10 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2566
2912
|
* @description
|
2567
2913
|
* Creates a new backend definition for PUT requests. For more info see `when()`.
|
2568
2914
|
*
|
2569
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2915
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2570
2916
|
* and returns true if the url matches the current definition.
|
2571
|
-
* @param {(string|RegExp)=} data HTTP request body
|
2917
|
+
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
2918
|
+
* data string and returns true if the data is as expected.
|
2572
2919
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
2573
2920
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
2574
2921
|
* {@link ngMock.$httpBackend $httpBackend mock}.
|
@@ -2584,9 +2931,10 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2584
2931
|
* @description
|
2585
2932
|
* Creates a new backend definition for PATCH requests. For more info see `when()`.
|
2586
2933
|
*
|
2587
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2934
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2588
2935
|
* and returns true if the url matches the current definition.
|
2589
|
-
* @param {(string|RegExp)=} data HTTP request body
|
2936
|
+
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
2937
|
+
* data string and returns true if the data is as expected.
|
2590
2938
|
* @param {(Object|function(Object))=} headers HTTP headers.
|
2591
2939
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
2592
2940
|
* {@link ngMock.$httpBackend $httpBackend mock}.
|
@@ -2602,7 +2950,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2602
2950
|
* @description
|
2603
2951
|
* Creates a new backend definition for JSONP requests. For more info see `when()`.
|
2604
2952
|
*
|
2605
|
-
* @param {string|RegExp|function(string)} url HTTP url or function that receives a url
|
2953
|
+
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
2606
2954
|
* and returns true if the url matches the current definition.
|
2607
2955
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
|
2608
2956
|
* {@link ngMock.$httpBackend $httpBackend mock}.
|
@@ -2623,6 +2971,39 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2623
2971
|
* control how a matched request is handled. You can save this object for later use and invoke
|
2624
2972
|
* `respond` or `passThrough` again in order to change how a matched request is handled.
|
2625
2973
|
*/
|
2974
|
+
/**
|
2975
|
+
* @ngdoc method
|
2976
|
+
* @name $httpBackend#matchLatestDefinitionEnabled
|
2977
|
+
* @module ngMockE2E
|
2978
|
+
* @description
|
2979
|
+
* This method can be used to change which mocked responses `$httpBackend` returns, when defining
|
2980
|
+
* them with {@link ngMock.$httpBackend#when $httpBackend.when()} (and shortcut methods).
|
2981
|
+
* By default, `$httpBackend` returns the first definition that matches. When setting
|
2982
|
+
* `$http.matchLatestDefinitionEnabled(true)`, it will use the last response that matches, i.e. the
|
2983
|
+
* one that was added last.
|
2984
|
+
*
|
2985
|
+
* ```js
|
2986
|
+
* hb.when('GET', '/url1').respond(200, 'content', {});
|
2987
|
+
* hb.when('GET', '/url1').respond(201, 'another', {});
|
2988
|
+
* hb('GET', '/url1'); // receives "content"
|
2989
|
+
*
|
2990
|
+
* $http.matchLatestDefinitionEnabled(true)
|
2991
|
+
* hb('GET', '/url1'); // receives "another"
|
2992
|
+
*
|
2993
|
+
* hb.when('GET', '/url1').respond(201, 'onemore', {});
|
2994
|
+
* hb('GET', '/url1'); // receives "onemore"
|
2995
|
+
* ```
|
2996
|
+
*
|
2997
|
+
* This is useful if a you have a default response that is overriden inside specific tests.
|
2998
|
+
*
|
2999
|
+
* Note that different from config methods on providers, `matchLatestDefinitionEnabled()` can be changed
|
3000
|
+
* even when the application is already running.
|
3001
|
+
*
|
3002
|
+
* @param {Boolean=} value value to set, either `true` or `false`. Default is `false`.
|
3003
|
+
* If omitted, it will return the current value.
|
3004
|
+
* @return {$httpBackend|Boolean} self when used as a setter, and the current value when used
|
3005
|
+
* as a getter
|
3006
|
+
*/
|
2626
3007
|
angular.mock.e2e = {};
|
2627
3008
|
angular.mock.e2e.$httpBackendDecorator =
|
2628
3009
|
['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
|
@@ -2654,6 +3035,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2654
3035
|
* @ngdoc method
|
2655
3036
|
* @name $rootScope.Scope#$countChildScopes
|
2656
3037
|
* @module ngMock
|
3038
|
+
* @this $rootScope.Scope
|
2657
3039
|
* @description
|
2658
3040
|
* Counts all the direct and indirect child scopes of the current scope.
|
2659
3041
|
*
|
@@ -2662,7 +3044,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2662
3044
|
* @returns {number} Total number of child scopes.
|
2663
3045
|
*/
|
2664
3046
|
function countChildScopes() {
|
2665
|
-
// jshint validthis: true
|
2666
3047
|
var count = 0; // exclude the current scope
|
2667
3048
|
var pendingChildHeads = [this.$$childHead];
|
2668
3049
|
var currentScope;
|
@@ -2684,6 +3065,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2684
3065
|
/**
|
2685
3066
|
* @ngdoc method
|
2686
3067
|
* @name $rootScope.Scope#$countWatchers
|
3068
|
+
* @this $rootScope.Scope
|
2687
3069
|
* @module ngMock
|
2688
3070
|
* @description
|
2689
3071
|
* Counts all the watchers of direct and indirect child scopes of the current scope.
|
@@ -2694,7 +3076,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2694
3076
|
* @returns {number} Total number of watchers.
|
2695
3077
|
*/
|
2696
3078
|
function countWatchers() {
|
2697
|
-
// jshint validthis: true
|
2698
3079
|
var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
|
2699
3080
|
var pendingChildHeads = [this.$$childHead];
|
2700
3081
|
var currentScope;
|
@@ -2714,7 +3095,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2714
3095
|
}];
|
2715
3096
|
|
2716
3097
|
|
2717
|
-
|
3098
|
+
(function(jasmineOrMocha) {
|
2718
3099
|
|
2719
3100
|
if (!jasmineOrMocha) {
|
2720
3101
|
return;
|
@@ -2809,7 +3190,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2809
3190
|
*
|
2810
3191
|
* You cannot call `sharedInjector()` from within a context already using `sharedInjector()`.
|
2811
3192
|
*
|
2812
|
-
* ##
|
3193
|
+
* ## Example
|
2813
3194
|
*
|
2814
3195
|
* Typically beforeAll is used to make many assertions about a single operation. This can
|
2815
3196
|
* cut down test run-time as the test setup doesn't need to be re-run, and enabling focussed
|
@@ -2847,14 +3228,14 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2847
3228
|
*/
|
2848
3229
|
module.sharedInjector = function() {
|
2849
3230
|
if (!(module.$$beforeAllHook && module.$$afterAllHook)) {
|
2850
|
-
throw Error(
|
3231
|
+
throw Error('sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll');
|
2851
3232
|
}
|
2852
3233
|
|
2853
3234
|
var initialized = false;
|
2854
3235
|
|
2855
|
-
module.$$beforeAllHook(function() {
|
3236
|
+
module.$$beforeAllHook(/** @this */ function() {
|
2856
3237
|
if (injectorState.shared) {
|
2857
|
-
injectorState.sharedError = Error(
|
3238
|
+
injectorState.sharedError = Error('sharedInjector() cannot be called inside a context that has already called sharedInjector()');
|
2858
3239
|
throw injectorState.sharedError;
|
2859
3240
|
}
|
2860
3241
|
initialized = true;
|
@@ -2873,10 +3254,10 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2873
3254
|
};
|
2874
3255
|
|
2875
3256
|
module.$$beforeEach = function() {
|
2876
|
-
if (injectorState.shared && currentSpec && currentSpec
|
3257
|
+
if (injectorState.shared && currentSpec && currentSpec !== this) {
|
2877
3258
|
var state = currentSpec;
|
2878
3259
|
currentSpec = this;
|
2879
|
-
angular.forEach([
|
3260
|
+
angular.forEach(['$injector','$modules','$providerInjector', '$injectorStrict'], function(k) {
|
2880
3261
|
currentSpec[k] = state[k];
|
2881
3262
|
state[k] = null;
|
2882
3263
|
});
|
@@ -2900,12 +3281,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2900
3281
|
delete fn.$inject;
|
2901
3282
|
});
|
2902
3283
|
|
2903
|
-
angular.forEach(currentSpec.$modules, function(module) {
|
2904
|
-
if (module && module.$$hashKey) {
|
2905
|
-
module.$$hashKey = undefined;
|
2906
|
-
}
|
2907
|
-
});
|
2908
|
-
|
2909
3284
|
currentSpec.$injector = null;
|
2910
3285
|
currentSpec.$modules = null;
|
2911
3286
|
currentSpec.$providerInjector = null;
|
@@ -2967,7 +3342,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
2967
3342
|
* These are ignored by the injector when the reference name is resolved.
|
2968
3343
|
*
|
2969
3344
|
* For example, the parameter `_myService_` would be resolved as the reference `myService`.
|
2970
|
-
* Since it is available in the function body as _myService_
|
3345
|
+
* Since it is available in the function body as `_myService_`, we can then assign it to a variable
|
2971
3346
|
* defined in an outer scope.
|
2972
3347
|
*
|
2973
3348
|
* ```
|
@@ -3031,7 +3406,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3031
3406
|
|
3032
3407
|
|
3033
3408
|
|
3034
|
-
var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
|
3409
|
+
var ErrorAddingDeclarationLocationStack = function ErrorAddingDeclarationLocationStack(e, errorForStack) {
|
3035
3410
|
this.message = e.message;
|
3036
3411
|
this.name = e.name;
|
3037
3412
|
if (e.line) this.line = e.line;
|
@@ -3049,11 +3424,11 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3049
3424
|
if (!errorForStack.stack) {
|
3050
3425
|
try {
|
3051
3426
|
throw errorForStack;
|
3052
|
-
} catch (e) {}
|
3427
|
+
} catch (e) { /* empty */ }
|
3053
3428
|
}
|
3054
|
-
return wasInjectorCreated() ?
|
3429
|
+
return wasInjectorCreated() ? WorkFn.call(currentSpec) : WorkFn;
|
3055
3430
|
/////////////////////
|
3056
|
-
function
|
3431
|
+
function WorkFn() {
|
3057
3432
|
var modules = currentSpec.$modules || [];
|
3058
3433
|
var strictDi = !!currentSpec.$injectorStrict;
|
3059
3434
|
modules.unshift(['$injector', function($injector) {
|
@@ -3066,7 +3441,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3066
3441
|
if (strictDi) {
|
3067
3442
|
// If strictDi is enabled, annotate the providerInjector blocks
|
3068
3443
|
angular.forEach(modules, function(moduleFn) {
|
3069
|
-
if (typeof moduleFn ===
|
3444
|
+
if (typeof moduleFn === 'function') {
|
3070
3445
|
angular.injector.$$annotate(moduleFn);
|
3071
3446
|
}
|
3072
3447
|
});
|
@@ -3081,9 +3456,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3081
3456
|
injector.annotate(blockFns[i]);
|
3082
3457
|
}
|
3083
3458
|
try {
|
3084
|
-
/* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
|
3085
3459
|
injector.invoke(blockFns[i] || angular.noop, this);
|
3086
|
-
/* jshint +W040 */
|
3087
3460
|
} catch (e) {
|
3088
3461
|
if (e.stack && errorForStack) {
|
3089
3462
|
throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
|
@@ -3122,5 +3495,263 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3122
3495
|
}
|
3123
3496
|
})(window.jasmine || window.mocha);
|
3124
3497
|
|
3498
|
+
'use strict';
|
3499
|
+
|
3500
|
+
(function() {
|
3501
|
+
/**
|
3502
|
+
* @ngdoc function
|
3503
|
+
* @name browserTrigger
|
3504
|
+
* @description
|
3505
|
+
*
|
3506
|
+
* This is a global (window) function that is only available when the {@link ngMock} module is
|
3507
|
+
* included.
|
3508
|
+
*
|
3509
|
+
* It can be used to trigger a native browser event on an element, which is useful for unit testing.
|
3510
|
+
*
|
3511
|
+
*
|
3512
|
+
* @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
|
3513
|
+
* @param {string=} eventType Optional event type. If none is specified, the function tries
|
3514
|
+
* to determine the right event type for the element, e.g. `change` for
|
3515
|
+
* `input[text]`.
|
3516
|
+
* @param {Object=} eventData An optional object which contains additional event data that is used
|
3517
|
+
* when creating the event:
|
3518
|
+
*
|
3519
|
+
* - `bubbles`: [Event.bubbles](https://developer.mozilla.org/docs/Web/API/Event/bubbles).
|
3520
|
+
* Not applicable to all events.
|
3521
|
+
*
|
3522
|
+
* - `cancelable`: [Event.cancelable](https://developer.mozilla.org/docs/Web/API/Event/cancelable).
|
3523
|
+
* Not applicable to all events.
|
3524
|
+
*
|
3525
|
+
* - `charcode`: [charCode](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/charcode)
|
3526
|
+
* for keyboard events (keydown, keypress, and keyup).
|
3527
|
+
*
|
3528
|
+
* - `data`: [data](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent/data) for
|
3529
|
+
* [CompositionEvents](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent).
|
3530
|
+
*
|
3531
|
+
* - `elapsedTime`: the elapsedTime for
|
3532
|
+
* [TransitionEvent](https://developer.mozilla.org/docs/Web/API/TransitionEvent)
|
3533
|
+
* and [AnimationEvent](https://developer.mozilla.org/docs/Web/API/AnimationEvent).
|
3534
|
+
*
|
3535
|
+
* - `keycode`: [keyCode](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/keycode)
|
3536
|
+
* for keyboard events (keydown, keypress, and keyup).
|
3537
|
+
*
|
3538
|
+
* - `keys`: an array of possible modifier keys (ctrl, alt, shift, meta) for
|
3539
|
+
* [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent) and
|
3540
|
+
* keyboard events (keydown, keypress, and keyup).
|
3541
|
+
*
|
3542
|
+
* - `relatedTarget`: the
|
3543
|
+
* [relatedTarget](https://developer.mozilla.org/docs/Web/API/MouseEvent/relatedTarget)
|
3544
|
+
* for [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent).
|
3545
|
+
*
|
3546
|
+
* - `which`: [which](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/which)
|
3547
|
+
* for keyboard events (keydown, keypress, and keyup).
|
3548
|
+
*
|
3549
|
+
* - `x`: x-coordinates for [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent)
|
3550
|
+
* and [TouchEvent](https://developer.mozilla.org/docs/Web/API/TouchEvent).
|
3551
|
+
*
|
3552
|
+
* - `y`: y-coordinates for [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent)
|
3553
|
+
* and [TouchEvent](https://developer.mozilla.org/docs/Web/API/TouchEvent).
|
3554
|
+
*
|
3555
|
+
*/
|
3556
|
+
window.browserTrigger = function browserTrigger(element, eventType, eventData) {
|
3557
|
+
if (element && !element.nodeName) element = element[0];
|
3558
|
+
if (!element) return;
|
3559
|
+
|
3560
|
+
eventData = eventData || {};
|
3561
|
+
var relatedTarget = eventData.relatedTarget || element;
|
3562
|
+
var keys = eventData.keys;
|
3563
|
+
var x = eventData.x;
|
3564
|
+
var y = eventData.y;
|
3565
|
+
|
3566
|
+
var inputType = (element.type) ? element.type.toLowerCase() : null,
|
3567
|
+
nodeName = element.nodeName.toLowerCase();
|
3568
|
+
if (!eventType) {
|
3569
|
+
eventType = {
|
3570
|
+
'text': 'change',
|
3571
|
+
'textarea': 'change',
|
3572
|
+
'hidden': 'change',
|
3573
|
+
'password': 'change',
|
3574
|
+
'button': 'click',
|
3575
|
+
'submit': 'click',
|
3576
|
+
'reset': 'click',
|
3577
|
+
'image': 'click',
|
3578
|
+
'checkbox': 'click',
|
3579
|
+
'radio': 'click',
|
3580
|
+
'select-one': 'change',
|
3581
|
+
'select-multiple': 'change',
|
3582
|
+
'_default_': 'click'
|
3583
|
+
}[inputType || '_default_'];
|
3584
|
+
}
|
3585
|
+
|
3586
|
+
if (nodeName === 'option') {
|
3587
|
+
element.parentNode.value = element.value;
|
3588
|
+
element = element.parentNode;
|
3589
|
+
eventType = 'change';
|
3590
|
+
}
|
3591
|
+
|
3592
|
+
keys = keys || [];
|
3593
|
+
function pressed(key) {
|
3594
|
+
return keys.indexOf(key) !== -1;
|
3595
|
+
}
|
3596
|
+
|
3597
|
+
var evnt;
|
3598
|
+
if (/transitionend/.test(eventType)) {
|
3599
|
+
if (window.WebKitTransitionEvent) {
|
3600
|
+
evnt = new window.WebKitTransitionEvent(eventType, eventData);
|
3601
|
+
evnt.initEvent(eventType, eventData.bubbles, true);
|
3602
|
+
} else {
|
3603
|
+
try {
|
3604
|
+
evnt = new window.TransitionEvent(eventType, eventData);
|
3605
|
+
} catch (e) {
|
3606
|
+
evnt = window.document.createEvent('TransitionEvent');
|
3607
|
+
evnt.initTransitionEvent(eventType, eventData.bubbles, null, null, eventData.elapsedTime || 0);
|
3608
|
+
}
|
3609
|
+
}
|
3610
|
+
} else if (/animationend/.test(eventType)) {
|
3611
|
+
if (window.WebKitAnimationEvent) {
|
3612
|
+
evnt = new window.WebKitAnimationEvent(eventType, eventData);
|
3613
|
+
evnt.initEvent(eventType, eventData.bubbles, true);
|
3614
|
+
} else {
|
3615
|
+
try {
|
3616
|
+
evnt = new window.AnimationEvent(eventType, eventData);
|
3617
|
+
} catch (e) {
|
3618
|
+
evnt = window.document.createEvent('AnimationEvent');
|
3619
|
+
evnt.initAnimationEvent(eventType, eventData.bubbles, null, null, eventData.elapsedTime || 0);
|
3620
|
+
}
|
3621
|
+
}
|
3622
|
+
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
|
3623
|
+
evnt = createTouchEvent(element, eventType, x, y);
|
3624
|
+
} else if (/key/.test(eventType)) {
|
3625
|
+
evnt = window.document.createEvent('Events');
|
3626
|
+
evnt.initEvent(eventType, eventData.bubbles, eventData.cancelable);
|
3627
|
+
evnt.view = window;
|
3628
|
+
evnt.ctrlKey = pressed('ctrl');
|
3629
|
+
evnt.altKey = pressed('alt');
|
3630
|
+
evnt.shiftKey = pressed('shift');
|
3631
|
+
evnt.metaKey = pressed('meta');
|
3632
|
+
evnt.keyCode = eventData.keyCode;
|
3633
|
+
evnt.charCode = eventData.charCode;
|
3634
|
+
evnt.which = eventData.which;
|
3635
|
+
} else if (/composition/.test(eventType)) {
|
3636
|
+
try {
|
3637
|
+
evnt = new window.CompositionEvent(eventType, {
|
3638
|
+
data: eventData.data
|
3639
|
+
});
|
3640
|
+
} catch (e) {
|
3641
|
+
// Support: IE9+
|
3642
|
+
evnt = window.document.createEvent('CompositionEvent', {});
|
3643
|
+
evnt.initCompositionEvent(
|
3644
|
+
eventType,
|
3645
|
+
eventData.bubbles,
|
3646
|
+
eventData.cancelable,
|
3647
|
+
window,
|
3648
|
+
eventData.data,
|
3649
|
+
null
|
3650
|
+
);
|
3651
|
+
}
|
3652
|
+
|
3653
|
+
} else {
|
3654
|
+
evnt = window.document.createEvent('MouseEvents');
|
3655
|
+
x = x || 0;
|
3656
|
+
y = y || 0;
|
3657
|
+
evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
|
3658
|
+
pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
|
3659
|
+
}
|
3660
|
+
|
3661
|
+
/* we're unable to change the timeStamp value directly so this
|
3662
|
+
* is only here to allow for testing where the timeStamp value is
|
3663
|
+
* read */
|
3664
|
+
evnt.$manualTimeStamp = eventData.timeStamp;
|
3665
|
+
|
3666
|
+
if (!evnt) return;
|
3667
|
+
|
3668
|
+
if (!eventData.bubbles || supportsEventBubblingInDetachedTree() || isAttachedToDocument(element)) {
|
3669
|
+
return element.dispatchEvent(evnt);
|
3670
|
+
} else {
|
3671
|
+
triggerForPath(element, evnt);
|
3672
|
+
}
|
3673
|
+
};
|
3674
|
+
|
3675
|
+
function supportsTouchEvents() {
|
3676
|
+
if ('_cached' in supportsTouchEvents) {
|
3677
|
+
return supportsTouchEvents._cached;
|
3678
|
+
}
|
3679
|
+
if (!window.document.createTouch || !window.document.createTouchList) {
|
3680
|
+
supportsTouchEvents._cached = false;
|
3681
|
+
return false;
|
3682
|
+
}
|
3683
|
+
try {
|
3684
|
+
window.document.createEvent('TouchEvent');
|
3685
|
+
} catch (e) {
|
3686
|
+
supportsTouchEvents._cached = false;
|
3687
|
+
return false;
|
3688
|
+
}
|
3689
|
+
supportsTouchEvents._cached = true;
|
3690
|
+
return true;
|
3691
|
+
}
|
3692
|
+
|
3693
|
+
function createTouchEvent(element, eventType, x, y) {
|
3694
|
+
var evnt = new window.Event(eventType);
|
3695
|
+
x = x || 0;
|
3696
|
+
y = y || 0;
|
3697
|
+
|
3698
|
+
var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
|
3699
|
+
var touches = window.document.createTouchList(touch);
|
3700
|
+
|
3701
|
+
evnt.touches = touches;
|
3702
|
+
|
3703
|
+
return evnt;
|
3704
|
+
}
|
3705
|
+
|
3706
|
+
function supportsEventBubblingInDetachedTree() {
|
3707
|
+
if ('_cached' in supportsEventBubblingInDetachedTree) {
|
3708
|
+
return supportsEventBubblingInDetachedTree._cached;
|
3709
|
+
}
|
3710
|
+
supportsEventBubblingInDetachedTree._cached = false;
|
3711
|
+
var doc = window.document;
|
3712
|
+
if (doc) {
|
3713
|
+
var parent = doc.createElement('div'),
|
3714
|
+
child = parent.cloneNode();
|
3715
|
+
parent.appendChild(child);
|
3716
|
+
parent.addEventListener('e', function() {
|
3717
|
+
supportsEventBubblingInDetachedTree._cached = true;
|
3718
|
+
});
|
3719
|
+
var evnt = window.document.createEvent('Events');
|
3720
|
+
evnt.initEvent('e', true, true);
|
3721
|
+
child.dispatchEvent(evnt);
|
3722
|
+
}
|
3723
|
+
return supportsEventBubblingInDetachedTree._cached;
|
3724
|
+
}
|
3725
|
+
|
3726
|
+
function triggerForPath(element, evnt) {
|
3727
|
+
var stop = false;
|
3728
|
+
|
3729
|
+
var _stopPropagation = evnt.stopPropagation;
|
3730
|
+
evnt.stopPropagation = function() {
|
3731
|
+
stop = true;
|
3732
|
+
_stopPropagation.apply(evnt, arguments);
|
3733
|
+
};
|
3734
|
+
patchEventTargetForBubbling(evnt, element);
|
3735
|
+
do {
|
3736
|
+
element.dispatchEvent(evnt);
|
3737
|
+
// eslint-disable-next-line no-unmodified-loop-condition
|
3738
|
+
} while (!stop && (element = element.parentNode));
|
3739
|
+
}
|
3740
|
+
|
3741
|
+
function patchEventTargetForBubbling(event, target) {
|
3742
|
+
event._target = target;
|
3743
|
+
Object.defineProperty(event, 'target', {get: function() { return this._target;}});
|
3744
|
+
}
|
3745
|
+
|
3746
|
+
function isAttachedToDocument(element) {
|
3747
|
+
while ((element = element.parentNode)) {
|
3748
|
+
if (element === window) {
|
3749
|
+
return true;
|
3750
|
+
}
|
3751
|
+
}
|
3752
|
+
return false;
|
3753
|
+
}
|
3754
|
+
})();
|
3755
|
+
|
3125
3756
|
|
3126
3757
|
})(window, window.angular);
|