angularjs-rails 1.5.8 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|