angularjs-rails 1.6.8 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/lib/angularjs-rails/version.rb +1 -1
- data/vendor/assets/javascripts/angular-animate.js +242 -128
- data/vendor/assets/javascripts/angular-aria.js +35 -10
- data/vendor/assets/javascripts/angular-cookies.js +9 -82
- data/vendor/assets/javascripts/angular-loader.js +46 -17
- data/vendor/assets/javascripts/angular-message-format.js +10 -10
- data/vendor/assets/javascripts/angular-messages.js +146 -58
- data/vendor/assets/javascripts/angular-mocks.js +613 -335
- data/vendor/assets/javascripts/angular-parse-ext.js +11 -5
- data/vendor/assets/javascripts/angular-resource.js +234 -180
- data/vendor/assets/javascripts/angular-route.js +99 -57
- data/vendor/assets/javascripts/angular-sanitize.js +132 -20
- data/vendor/assets/javascripts/angular-touch.js +27 -403
- data/vendor/assets/javascripts/angular.js +3796 -1555
- metadata +2 -3
- data/vendor/assets/javascripts/angular-scenario.js +0 -46603
@@ -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,7 +23,7 @@ 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
|
@@ -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,17 +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
|
})
|
275
|
-
.info({ angularVersion: '1.
|
294
|
+
.info({ angularVersion: '1.8.0' })
|
276
295
|
|
277
296
|
/**
|
278
297
|
* @ngdoc directive
|
@@ -291,8 +310,11 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
291
310
|
* at a time and this depends on the prioritization of the messages within the template. (This can
|
292
311
|
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
293
312
|
*
|
294
|
-
* A remote template can also be used to promote message
|
295
|
-
* 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.
|
296
318
|
*
|
297
319
|
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
298
320
|
*
|
@@ -303,6 +325,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
303
325
|
* <ANY ng-message="stringValue">...</ANY>
|
304
326
|
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
305
327
|
* <ANY ng-message-exp="expressionValue">...</ANY>
|
328
|
+
* <ANY ng-message-default>...</ANY>
|
306
329
|
* </ANY>
|
307
330
|
*
|
308
331
|
* <!-- or by using element directives -->
|
@@ -310,10 +333,11 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
310
333
|
* <ng-message when="stringValue">...</ng-message>
|
311
334
|
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
312
335
|
* <ng-message when-exp="expressionValue">...</ng-message>
|
336
|
+
* <ng-message-default>...</ng-message-default>
|
313
337
|
* </ng-messages>
|
314
338
|
* ```
|
315
339
|
*
|
316
|
-
* @param {string} ngMessages an
|
340
|
+
* @param {string} ngMessages an AngularJS expression evaluating to a key/value object
|
317
341
|
* (this is typically the $error object on an ngModel instance).
|
318
342
|
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
319
343
|
*
|
@@ -338,6 +362,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
338
362
|
* <div ng-message="required">You did not enter a field</div>
|
339
363
|
* <div ng-message="minlength">Your field is too short</div>
|
340
364
|
* <div ng-message="maxlength">Your field is too long</div>
|
365
|
+
* <div ng-message-default>This field has an input error</div>
|
341
366
|
* </div>
|
342
367
|
* </form>
|
343
368
|
* </file>
|
@@ -375,6 +400,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
375
400
|
|
376
401
|
var unmatchedMessages = [];
|
377
402
|
var matchedKeys = {};
|
403
|
+
var truthyKeys = 0;
|
378
404
|
var messageItem = ctrl.head;
|
379
405
|
var messageFound = false;
|
380
406
|
var totalMessages = 0;
|
@@ -387,13 +413,17 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
387
413
|
var messageUsed = false;
|
388
414
|
if (!messageFound) {
|
389
415
|
forEach(collection, function(value, key) {
|
390
|
-
if (
|
391
|
-
|
392
|
-
if (matchedKeys[key]) return;
|
393
|
-
matchedKeys[key] = true;
|
416
|
+
if (truthy(value) && !messageUsed) {
|
417
|
+
truthyKeys++;
|
394
418
|
|
395
|
-
|
396
|
-
|
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;
|
423
|
+
|
424
|
+
messageUsed = true;
|
425
|
+
messageCtrl.attach();
|
426
|
+
}
|
397
427
|
}
|
398
428
|
});
|
399
429
|
}
|
@@ -413,7 +443,16 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
413
443
|
messageCtrl.detach();
|
414
444
|
});
|
415
445
|
|
416
|
-
|
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) {
|
417
456
|
$animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
|
418
457
|
} else {
|
419
458
|
$animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
|
@@ -422,13 +461,6 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
422
461
|
|
423
462
|
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
424
463
|
|
425
|
-
// If the element is destroyed, proactively destroy all the currently visible messages
|
426
|
-
$element.on('$destroy', function() {
|
427
|
-
forEach(messages, function(item) {
|
428
|
-
item.message.detach();
|
429
|
-
});
|
430
|
-
});
|
431
|
-
|
432
464
|
this.reRender = function() {
|
433
465
|
if (!renderLater) {
|
434
466
|
renderLater = true;
|
@@ -440,23 +472,31 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
440
472
|
}
|
441
473
|
};
|
442
474
|
|
443
|
-
this.register = function(comment, messageCtrl) {
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
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
|
+
}
|
451
487
|
|
452
488
|
ctrl.reRender();
|
453
489
|
};
|
454
490
|
|
455
|
-
this.deregister = function(comment) {
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
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
|
+
}
|
460
500
|
ctrl.reRender();
|
461
501
|
};
|
462
502
|
|
@@ -503,6 +543,9 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
503
543
|
function removeMessageNode(parent, comment, key) {
|
504
544
|
var messageNode = messages[key];
|
505
545
|
|
546
|
+
// This message node may have already been removed by a call to deregister()
|
547
|
+
if (!messageNode) return;
|
548
|
+
|
506
549
|
var match = findPreviousMessage(parent, comment);
|
507
550
|
if (match) {
|
508
551
|
match.next = messageNode.next;
|
@@ -656,9 +699,41 @@ angular.module('ngMessages', [], function initAngularHelpers() {
|
|
656
699
|
*
|
657
700
|
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
658
701
|
*/
|
659
|
-
.directive('ngMessageExp', ngMessageDirectiveFactory())
|
702
|
+
.directive('ngMessageExp', ngMessageDirectiveFactory())
|
660
703
|
|
661
|
-
|
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) {
|
662
737
|
return ['$animate', function($animate) {
|
663
738
|
return {
|
664
739
|
restrict: 'AE',
|
@@ -667,25 +742,28 @@ function ngMessageDirectiveFactory() {
|
|
667
742
|
terminal: true,
|
668
743
|
require: '^^ngMessages',
|
669
744
|
link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
|
670
|
-
var commentNode
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
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
|
+
};
|
683
760
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
761
|
+
if (dynamicExp) {
|
762
|
+
assignRecords(scope.$eval(dynamicExp));
|
763
|
+
scope.$watchCollection(dynamicExp, assignRecords);
|
764
|
+
} else {
|
765
|
+
assignRecords(staticExp);
|
766
|
+
}
|
689
767
|
}
|
690
768
|
|
691
769
|
var currentElement, messageCtrl;
|
@@ -707,8 +785,10 @@ function ngMessageDirectiveFactory() {
|
|
707
785
|
// by another structural directive then it's time
|
708
786
|
// to deregister the message from the controller
|
709
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.
|
710
790
|
if (currentElement && currentElement.$$attachId === $$attachId) {
|
711
|
-
ngMessagesCtrl.deregister(commentNode);
|
791
|
+
ngMessagesCtrl.deregister(commentNode, isDefault);
|
712
792
|
messageCtrl.detach();
|
713
793
|
}
|
714
794
|
newScope.$destroy();
|
@@ -723,6 +803,14 @@ function ngMessageDirectiveFactory() {
|
|
723
803
|
$animate.leave(elm);
|
724
804
|
}
|
725
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);
|
726
814
|
});
|
727
815
|
}
|
728
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,43 +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
96
|
self.$$url = 'http://server/';
|
44
97
|
self.$$lastUrl = self.$$url; // used by url polling fn
|
45
98
|
self.pollFns = [];
|
46
99
|
|
47
|
-
//
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
|
52
|
-
self.$$completeOutstandingRequest = function(fn) {
|
53
|
-
try {
|
54
|
-
fn();
|
55
|
-
} finally {
|
56
|
-
outstandingRequestCount--;
|
57
|
-
if (!outstandingRequestCount) {
|
58
|
-
while (outstandingRequestCallbacks.length) {
|
59
|
-
outstandingRequestCallbacks.pop()();
|
60
|
-
}
|
61
|
-
}
|
62
|
-
}
|
63
|
-
};
|
64
|
-
self.notifyWhenNoOutstandingRequests = function(callback) {
|
65
|
-
if (outstandingRequestCount) {
|
66
|
-
outstandingRequestCallbacks.push(callback);
|
67
|
-
} else {
|
68
|
-
callback();
|
69
|
-
}
|
70
|
-
};
|
100
|
+
// Task-tracking API
|
101
|
+
self.$$completeOutstandingRequest = taskTracker.completeTask;
|
102
|
+
self.$$incOutstandingRequestCount = taskTracker.incTaskCount;
|
103
|
+
self.notifyWhenNoOutstandingRequests = taskTracker.notifyWhenNoPendingTasks;
|
71
104
|
|
72
105
|
// register url polling fn
|
73
106
|
|
@@ -91,13 +124,22 @@ angular.mock.$Browser = function() {
|
|
91
124
|
self.deferredFns = [];
|
92
125
|
self.deferredNextId = 0;
|
93
126
|
|
94
|
-
self.defer = function(fn, delay) {
|
95
|
-
|
96
|
-
|
127
|
+
self.defer = function(fn, delay, taskType) {
|
128
|
+
var timeoutId = self.deferredNextId++;
|
129
|
+
|
97
130
|
delay = delay || 0;
|
98
|
-
|
99
|
-
|
100
|
-
|
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;
|
101
143
|
};
|
102
144
|
|
103
145
|
|
@@ -111,14 +153,15 @@ angular.mock.$Browser = function() {
|
|
111
153
|
|
112
154
|
|
113
155
|
self.defer.cancel = function(deferId) {
|
114
|
-
var
|
156
|
+
var taskIndex;
|
115
157
|
|
116
|
-
angular.forEach(self.deferredFns, function(
|
117
|
-
if (
|
158
|
+
angular.forEach(self.deferredFns, function(task, index) {
|
159
|
+
if (task.id === deferId) taskIndex = index;
|
118
160
|
});
|
119
161
|
|
120
|
-
if (angular.isDefined(
|
121
|
-
self.deferredFns.splice(
|
162
|
+
if (angular.isDefined(taskIndex)) {
|
163
|
+
var task = self.deferredFns.splice(taskIndex, 1)[0];
|
164
|
+
taskTracker.completeTask(angular.noop, task.type);
|
122
165
|
return true;
|
123
166
|
}
|
124
167
|
|
@@ -132,6 +175,8 @@ angular.mock.$Browser = function() {
|
|
132
175
|
* @description
|
133
176
|
* Flushes all pending requests and executes the defer callbacks.
|
134
177
|
*
|
178
|
+
* See {@link ngMock.$flushPendingsTasks} for more info.
|
179
|
+
*
|
135
180
|
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
|
136
181
|
*/
|
137
182
|
self.defer.flush = function(delay) {
|
@@ -140,26 +185,76 @@ angular.mock.$Browser = function() {
|
|
140
185
|
if (angular.isDefined(delay)) {
|
141
186
|
// A delay was passed so compute the next time
|
142
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;
|
143
191
|
} else {
|
144
|
-
|
145
|
-
|
146
|
-
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
147
|
-
} else {
|
148
|
-
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
149
|
-
throw new Error('No deferred tasks to be flushed');
|
150
|
-
}
|
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');
|
151
194
|
}
|
152
195
|
|
153
196
|
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
|
154
197
|
// Increment the time and call the next deferred function
|
155
198
|
self.defer.now = self.deferredFns[0].time;
|
156
|
-
self.deferredFns.shift()
|
199
|
+
var task = self.deferredFns.shift();
|
200
|
+
taskTracker.completeTask(task.fn, task.type);
|
157
201
|
}
|
158
202
|
|
159
203
|
// Ensure that the current time is correct
|
160
204
|
self.defer.now = nextTime;
|
161
205
|
};
|
162
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
|
+
|
163
258
|
self.$$baseHref = '/';
|
164
259
|
self.baseHref = function() {
|
165
260
|
return this.$$baseHref;
|
@@ -184,7 +279,8 @@ angular.mock.$Browser.prototype = {
|
|
184
279
|
state = null;
|
185
280
|
}
|
186
281
|
if (url) {
|
187
|
-
|
282
|
+
// The `$browser` service trims empty hashes; simulate it.
|
283
|
+
this.$$url = url.replace(/#$/, '');
|
188
284
|
// Native pushState serializes & copies the object; simulate it.
|
189
285
|
this.$$state = angular.copy(state);
|
190
286
|
return this;
|
@@ -198,6 +294,82 @@ angular.mock.$Browser.prototype = {
|
|
198
294
|
}
|
199
295
|
};
|
200
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
|
+
};
|
201
373
|
|
202
374
|
/**
|
203
375
|
* @ngdoc provider
|
@@ -466,62 +638,40 @@ angular.mock.$LogProvider = function() {
|
|
466
638
|
* @returns {promise} A promise which will be notified on each iteration.
|
467
639
|
*/
|
468
640
|
angular.mock.$IntervalProvider = function() {
|
469
|
-
this.$get = ['$browser', '
|
470
|
-
function($browser,
|
641
|
+
this.$get = ['$browser', '$$intervalFactory',
|
642
|
+
function($browser, $$intervalFactory) {
|
471
643
|
var repeatFns = [],
|
472
644
|
nextRepeatId = 0,
|
473
|
-
now = 0
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
promise.$$intervalId = nextRepeatId;
|
489
|
-
|
490
|
-
function tick() {
|
491
|
-
deferred.notify(iteration++);
|
492
|
-
|
493
|
-
if (count > 0 && iteration >= count) {
|
494
|
-
var fnIndex;
|
495
|
-
deferred.resolve(iteration);
|
496
|
-
|
497
|
-
angular.forEach(repeatFns, function(fn, index) {
|
498
|
-
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
|
499
659
|
});
|
660
|
+
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime; });
|
500
661
|
|
501
|
-
|
502
|
-
|
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
|
+
}
|
503
670
|
}
|
504
|
-
}
|
505
|
-
|
506
|
-
if (skipApply) {
|
507
|
-
$browser.defer.flush();
|
508
|
-
} else {
|
509
|
-
$rootScope.$apply();
|
510
|
-
}
|
511
|
-
}
|
671
|
+
};
|
512
672
|
|
513
|
-
|
514
|
-
nextTime: (now + (delay || 0)),
|
515
|
-
delay: delay || 1,
|
516
|
-
fn: tick,
|
517
|
-
id: nextRepeatId,
|
518
|
-
deferred: deferred
|
519
|
-
});
|
520
|
-
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
|
673
|
+
var $interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
|
521
674
|
|
522
|
-
nextRepeatId++;
|
523
|
-
return promise;
|
524
|
-
};
|
525
675
|
/**
|
526
676
|
* @ngdoc method
|
527
677
|
* @name $interval#cancel
|
@@ -534,17 +684,15 @@ angular.mock.$IntervalProvider = function() {
|
|
534
684
|
*/
|
535
685
|
$interval.cancel = function(promise) {
|
536
686
|
if (!promise) return false;
|
537
|
-
var fnIndex;
|
538
|
-
|
539
|
-
angular.forEach(repeatFns, function(fn, index) {
|
540
|
-
if (fn.id === promise.$$intervalId) fnIndex = index;
|
541
|
-
});
|
542
687
|
|
543
|
-
|
544
|
-
repeatFns[fnIndex].
|
545
|
-
|
546
|
-
|
547
|
-
|
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
|
+
}
|
548
696
|
}
|
549
697
|
|
550
698
|
return false;
|
@@ -557,7 +705,7 @@ angular.mock.$IntervalProvider = function() {
|
|
557
705
|
*
|
558
706
|
* Runs interval tasks scheduled to be run in the next `millis` milliseconds.
|
559
707
|
*
|
560
|
-
* @param {number
|
708
|
+
* @param {number} millis maximum timeout amount to flush up until.
|
561
709
|
*
|
562
710
|
* @return {number} The amount of time moved forward.
|
563
711
|
*/
|
@@ -803,7 +951,7 @@ angular.mock.TzDate.prototype = Date.prototype;
|
|
803
951
|
* You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
|
804
952
|
*/
|
805
953
|
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
806
|
-
.info({ angularVersion: '1.
|
954
|
+
.info({ angularVersion: '1.8.0' })
|
807
955
|
|
808
956
|
.config(['$provide', function($provide) {
|
809
957
|
|
@@ -969,7 +1117,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
|
969
1117
|
*
|
970
1118
|
* *NOTE*: This is not an injectable instance, just a globally available function.
|
971
1119
|
*
|
972
|
-
* Method for serializing common
|
1120
|
+
* Method for serializing common AngularJS objects (scope, elements, etc..) into strings.
|
973
1121
|
* It is useful for logging objects to the console when debugging.
|
974
1122
|
*
|
975
1123
|
* @param {*} object - any object to turn into string.
|
@@ -1051,7 +1199,7 @@ angular.mock.dump = function(object) {
|
|
1051
1199
|
* This mock implementation can be used to respond with static or dynamic responses via the
|
1052
1200
|
* `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
|
1053
1201
|
*
|
1054
|
-
* When an
|
1202
|
+
* When an AngularJS application needs some data from a server, it calls the $http service, which
|
1055
1203
|
* sends the request to a real server using $httpBackend service. With dependency injection, it is
|
1056
1204
|
* easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
|
1057
1205
|
* the requests and respond with some testing data without sending a request to a real server.
|
@@ -1289,7 +1437,7 @@ angular.mock.dump = function(object) {
|
|
1289
1437
|
* ## Matching route requests
|
1290
1438
|
*
|
1291
1439
|
* For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
|
1292
|
-
* 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
|
1293
1441
|
* similar to how application routes are configured with `$routeProvider`. Because these methods convert
|
1294
1442
|
* the definition url to regex, declaration order is important. Combined with query parameter parsing,
|
1295
1443
|
* the following is possible:
|
@@ -1349,6 +1497,7 @@ angular.mock.$httpBackendDecorator =
|
|
1349
1497
|
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
1350
1498
|
var definitions = [],
|
1351
1499
|
expectations = [],
|
1500
|
+
matchLatestDefinition = false,
|
1352
1501
|
responses = [],
|
1353
1502
|
responsesPush = angular.bind(responses, responses.push),
|
1354
1503
|
copy = angular.copy,
|
@@ -1385,9 +1534,13 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1385
1534
|
function wrapResponse(wrapped) {
|
1386
1535
|
if (!$browser && timeout) {
|
1387
1536
|
if (timeout.then) {
|
1388
|
-
timeout.then(
|
1537
|
+
timeout.then(function() {
|
1538
|
+
handlePrematureEnd(angular.isDefined(timeout.$$timeoutId) ? 'timeout' : 'abort');
|
1539
|
+
});
|
1389
1540
|
} else {
|
1390
|
-
$timeout(
|
1541
|
+
$timeout(function() {
|
1542
|
+
handlePrematureEnd('timeout');
|
1543
|
+
}, timeout);
|
1391
1544
|
}
|
1392
1545
|
}
|
1393
1546
|
|
@@ -1401,27 +1554,36 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1401
1554
|
copy(response[3] || ''), copy(response[4]));
|
1402
1555
|
}
|
1403
1556
|
|
1404
|
-
function
|
1557
|
+
function handlePrematureEnd(reason) {
|
1405
1558
|
for (var i = 0, ii = responses.length; i < ii; i++) {
|
1406
1559
|
if (responses[i] === handleResponse) {
|
1407
1560
|
responses.splice(i, 1);
|
1408
|
-
callback(-1, undefined, '', undefined,
|
1561
|
+
callback(-1, undefined, '', undefined, reason);
|
1409
1562
|
break;
|
1410
1563
|
}
|
1411
1564
|
}
|
1412
1565
|
}
|
1413
1566
|
}
|
1414
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
|
+
|
1415
1576
|
if (expectation && expectation.match(method, url)) {
|
1416
1577
|
if (!expectation.matchData(data)) {
|
1417
|
-
throw
|
1418
|
-
|
1578
|
+
throw createFatalError('Expected ' + expectation + ' with different data\n' +
|
1579
|
+
'EXPECTED: ' + prettyPrint(expectation.data) + '\n' +
|
1580
|
+
'GOT: ' + data);
|
1419
1581
|
}
|
1420
1582
|
|
1421
1583
|
if (!expectation.matchHeaders(headers)) {
|
1422
|
-
throw
|
1423
|
-
|
1424
|
-
|
1584
|
+
throw createFatalError('Expected ' + expectation + ' with different headers\n' +
|
1585
|
+
'EXPECTED: ' + prettyPrint(expectation.headers) + '\n' +
|
1586
|
+
'GOT: ' + prettyPrint(headers));
|
1425
1587
|
}
|
1426
1588
|
|
1427
1589
|
expectations.shift();
|
@@ -1433,28 +1595,26 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1433
1595
|
wasExpected = true;
|
1434
1596
|
}
|
1435
1597
|
|
1436
|
-
var i = -1, definition;
|
1437
|
-
|
1598
|
+
var i = matchLatestDefinition ? definitions.length : -1, definition;
|
1599
|
+
|
1600
|
+
while ((definition = definitions[matchLatestDefinition ? --i : ++i])) {
|
1438
1601
|
if (definition.match(method, url, data, headers || {})) {
|
1439
1602
|
if (definition.response) {
|
1440
1603
|
// if $browser specified, we do auto flush all requests
|
1441
1604
|
($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
|
1442
1605
|
} else if (definition.passThrough) {
|
1443
1606
|
originalHttpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
|
1444
|
-
} else throw
|
1607
|
+
} else throw createFatalError('No response defined !');
|
1445
1608
|
return;
|
1446
1609
|
}
|
1447
1610
|
}
|
1448
|
-
var error = wasExpected ?
|
1449
|
-
new Error('No response defined !') :
|
1450
|
-
new Error('Unexpected request: ' + method + ' ' + url + '\n' +
|
1451
|
-
(expectation ? 'Expected ' + expectation : 'No more request expected'));
|
1452
1611
|
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1612
|
+
if (wasExpected) {
|
1613
|
+
throw createFatalError('No response defined !');
|
1614
|
+
}
|
1456
1615
|
|
1457
|
-
throw
|
1616
|
+
throw createFatalError('Unexpected request: ' + method + ' ' + url + '\n' +
|
1617
|
+
(expectation ? 'Expected ' + expectation : 'No more request expected'));
|
1458
1618
|
}
|
1459
1619
|
|
1460
1620
|
/**
|
@@ -1482,8 +1642,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1482
1642
|
* ```
|
1483
1643
|
* – The respond method takes a set of static data to be returned or a function that can
|
1484
1644
|
* return an array containing response status (number), response data (Array|Object|string),
|
1485
|
-
* response headers (Object),
|
1486
|
-
*
|
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.
|
1487
1648
|
*/
|
1488
1649
|
$httpBackend.when = function(method, url, data, headers, keys) {
|
1489
1650
|
|
@@ -1510,6 +1671,47 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1510
1671
|
return chain;
|
1511
1672
|
};
|
1512
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
|
+
|
1513
1715
|
/**
|
1514
1716
|
* @ngdoc method
|
1515
1717
|
* @name $httpBackend#whenGET
|
@@ -1518,7 +1720,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1518
1720
|
*
|
1519
1721
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1520
1722
|
* and returns true if the url matches the current definition.
|
1521
|
-
* @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.
|
1522
1725
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1523
1726
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1524
1727
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1533,7 +1736,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1533
1736
|
*
|
1534
1737
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1535
1738
|
* and returns true if the url matches the current definition.
|
1536
|
-
* @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.
|
1537
1741
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1538
1742
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1539
1743
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1548,7 +1752,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1548
1752
|
*
|
1549
1753
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1550
1754
|
* and returns true if the url matches the current definition.
|
1551
|
-
* @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.
|
1552
1757
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1553
1758
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1554
1759
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1565,7 +1770,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1565
1770
|
* and returns true if the url matches the current definition.
|
1566
1771
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
1567
1772
|
* data string and returns true if the data is as expected.
|
1568
|
-
* @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.
|
1569
1775
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1570
1776
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1571
1777
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1582,7 +1788,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1582
1788
|
* and returns true if the url matches the current definition.
|
1583
1789
|
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
|
1584
1790
|
* data string and returns true if the data is as expected.
|
1585
|
-
* @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.
|
1586
1793
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1587
1794
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1588
1795
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1614,42 +1821,13 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1614
1821
|
* @param {string} url HTTP url string that supports colon param matching.
|
1615
1822
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1616
1823
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
1617
|
-
* 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.
|
1618
1826
|
*/
|
1619
1827
|
$httpBackend.whenRoute = function(method, url) {
|
1620
|
-
var
|
1621
|
-
return $httpBackend.when(method,
|
1622
|
-
};
|
1623
|
-
|
1624
|
-
function parseRoute(url) {
|
1625
|
-
var ret = {
|
1626
|
-
regexp: url
|
1627
|
-
},
|
1628
|
-
keys = ret.keys = [];
|
1629
|
-
|
1630
|
-
if (!url || !angular.isString(url)) return ret;
|
1631
|
-
|
1632
|
-
url = url
|
1633
|
-
.replace(/([().])/g, '\\$1')
|
1634
|
-
.replace(/(\/)?:(\w+)([?*])?/g, function(_, slash, key, option) {
|
1635
|
-
var optional = option === '?' ? option : null;
|
1636
|
-
var star = option === '*' ? option : null;
|
1637
|
-
keys.push({ name: key, optional: !!optional });
|
1638
|
-
slash = slash || '';
|
1639
|
-
return ''
|
1640
|
-
+ (optional ? '' : slash)
|
1641
|
-
+ '(?:'
|
1642
|
-
+ (optional ? slash : '')
|
1643
|
-
+ (star && '(.+?)' || '([^/]+)')
|
1644
|
-
+ (optional || '')
|
1645
|
-
+ ')'
|
1646
|
-
+ (optional || '');
|
1647
|
-
})
|
1648
|
-
.replace(/([/$*])/g, '\\$1');
|
1649
|
-
|
1650
|
-
ret.regexp = new RegExp('^' + url, 'i');
|
1651
|
-
return ret;
|
1652
|
-
}
|
1828
|
+
var parsed = parseRouteUrl(url);
|
1829
|
+
return $httpBackend.when(method, parsed.regexp, undefined, undefined, parsed.keys);
|
1830
|
+
};
|
1653
1831
|
|
1654
1832
|
/**
|
1655
1833
|
* @ngdoc method
|
@@ -1671,14 +1849,15 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1671
1849
|
* order to change how a matched request is handled.
|
1672
1850
|
*
|
1673
1851
|
* - respond –
|
1674
|
-
*
|
1675
|
-
*
|
1676
|
-
*
|
1677
|
-
*
|
1852
|
+
* ```js
|
1853
|
+
* {function([status,] data[, headers, statusText])
|
1854
|
+
* | function(function(method, url, data, headers, params)}
|
1855
|
+
* ```
|
1678
1856
|
* – The respond method takes a set of static data to be returned or a function that can
|
1679
1857
|
* return an array containing response status (number), response data (Array|Object|string),
|
1680
|
-
* response headers (Object),
|
1681
|
-
*
|
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.
|
1682
1861
|
*/
|
1683
1862
|
$httpBackend.expect = function(method, url, data, headers, keys) {
|
1684
1863
|
|
@@ -1703,8 +1882,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1703
1882
|
* Creates a new request expectation for GET requests. For more info see `expect()`.
|
1704
1883
|
*
|
1705
1884
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1706
|
-
* and returns true if the url matches the current
|
1707
|
-
* @param {Object=} headers HTTP headers
|
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.
|
1708
1888
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1709
1889
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1710
1890
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1718,8 +1898,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1718
1898
|
* Creates a new request expectation for HEAD requests. For more info see `expect()`.
|
1719
1899
|
*
|
1720
1900
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1721
|
-
* and returns true if the url matches the current
|
1722
|
-
* @param {Object=} headers HTTP headers
|
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.
|
1723
1904
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1724
1905
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1725
1906
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1733,8 +1914,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1733
1914
|
* Creates a new request expectation for DELETE requests. For more info see `expect()`.
|
1734
1915
|
*
|
1735
1916
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1736
|
-
* and returns true if the url matches the current
|
1737
|
-
* @param {Object=} headers HTTP headers
|
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.
|
1738
1920
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1739
1921
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1740
1922
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1748,11 +1930,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1748
1930
|
* Creates a new request expectation for POST requests. For more info see `expect()`.
|
1749
1931
|
*
|
1750
1932
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1751
|
-
* and returns true if the url matches the current
|
1933
|
+
* and returns true if the url matches the current expectation.
|
1752
1934
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1753
1935
|
* receives data string and returns true if the data is as expected, or Object if request body
|
1754
1936
|
* is in JSON format.
|
1755
|
-
* @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.
|
1756
1939
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1757
1940
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1758
1941
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1766,11 +1949,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1766
1949
|
* Creates a new request expectation for PUT requests. For more info see `expect()`.
|
1767
1950
|
*
|
1768
1951
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1769
|
-
* and returns true if the url matches the current
|
1952
|
+
* and returns true if the url matches the current expectation.
|
1770
1953
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1771
1954
|
* receives data string and returns true if the data is as expected, or Object if request body
|
1772
1955
|
* is in JSON format.
|
1773
|
-
* @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.
|
1774
1958
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1775
1959
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1776
1960
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1784,11 +1968,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1784
1968
|
* Creates a new request expectation for PATCH requests. For more info see `expect()`.
|
1785
1969
|
*
|
1786
1970
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
|
1787
|
-
* and returns true if the url matches the current
|
1971
|
+
* and returns true if the url matches the current expectation.
|
1788
1972
|
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
|
1789
1973
|
* receives data string and returns true if the data is as expected, or Object if request body
|
1790
1974
|
* is in JSON format.
|
1791
|
-
* @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.
|
1792
1977
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1793
1978
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1794
1979
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1802,7 +1987,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1802
1987
|
* Creates a new request expectation for JSONP requests. For more info see `expect()`.
|
1803
1988
|
*
|
1804
1989
|
* @param {string|RegExp|function(string)=} url HTTP url or function that receives an url
|
1805
|
-
* and returns true if the url matches the current
|
1990
|
+
* and returns true if the url matches the current expectation.
|
1806
1991
|
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
|
1807
1992
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1808
1993
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
@@ -1820,11 +2005,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1820
2005
|
* @param {string} url HTTP url string that supports colon param matching.
|
1821
2006
|
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
|
1822
2007
|
* request is handled. You can save this object for later use and invoke `respond` again in
|
1823
|
-
* 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.
|
1824
2010
|
*/
|
1825
2011
|
$httpBackend.expectRoute = function(method, url) {
|
1826
|
-
var
|
1827
|
-
return $httpBackend.expect(method,
|
2012
|
+
var parsed = parseRouteUrl(url);
|
2013
|
+
return $httpBackend.expect(method, parsed.regexp, undefined, undefined, parsed.keys);
|
1828
2014
|
};
|
1829
2015
|
|
1830
2016
|
|
@@ -1952,6 +2138,12 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
|
|
1952
2138
|
};
|
1953
2139
|
});
|
1954
2140
|
}
|
2141
|
+
|
2142
|
+
function parseRouteUrl(url) {
|
2143
|
+
var strippedUrl = stripQueryAndHash(url);
|
2144
|
+
var parseOptions = {caseInsensitiveMatch: true, ignoreTrailingSlashes: true};
|
2145
|
+
return routeToRegExp(strippedUrl, parseOptions);
|
2146
|
+
}
|
1955
2147
|
}
|
1956
2148
|
|
1957
2149
|
function assertArgDefined(args, index, name) {
|
@@ -1960,110 +2152,124 @@ function assertArgDefined(args, index, name) {
|
|
1960
2152
|
}
|
1961
2153
|
}
|
1962
2154
|
|
2155
|
+
function stripQueryAndHash(url) {
|
2156
|
+
return url.replace(/[?#].*$/, '');
|
2157
|
+
}
|
1963
2158
|
|
1964
|
-
function MockHttpExpectation(
|
1965
|
-
|
1966
|
-
function getUrlParams(u) {
|
1967
|
-
var params = u.slice(u.indexOf('?') + 1).split('&');
|
1968
|
-
return params.sort();
|
1969
|
-
}
|
1970
|
-
|
1971
|
-
function compareUrl(u) {
|
1972
|
-
return (url.slice(0, url.indexOf('?')) === u.slice(0, u.indexOf('?')) &&
|
1973
|
-
getUrlParams(url).join() === getUrlParams(u).join());
|
1974
|
-
}
|
2159
|
+
function MockHttpExpectation(expectedMethod, expectedUrl, expectedData, expectedHeaders,
|
2160
|
+
expectedKeys) {
|
1975
2161
|
|
1976
|
-
this.data =
|
1977
|
-
this.headers =
|
2162
|
+
this.data = expectedData;
|
2163
|
+
this.headers = expectedHeaders;
|
1978
2164
|
|
1979
|
-
this.match = function(
|
1980
|
-
if (
|
1981
|
-
if (!this.matchUrl(
|
1982
|
-
if (angular.isDefined(
|
1983
|
-
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;
|
1984
2170
|
return true;
|
1985
2171
|
};
|
1986
2172
|
|
1987
|
-
this.matchUrl = function(
|
1988
|
-
if (!
|
1989
|
-
if (angular.isFunction(
|
1990
|
-
if (angular.isFunction(
|
1991
|
-
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));
|
1992
2178
|
};
|
1993
2179
|
|
1994
|
-
this.matchHeaders = function(
|
1995
|
-
if (angular.isUndefined(
|
1996
|
-
if (angular.isFunction(
|
1997
|
-
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);
|
1998
2184
|
};
|
1999
2185
|
|
2000
|
-
this.matchData = function(
|
2001
|
-
if (angular.isUndefined(
|
2002
|
-
if (
|
2003
|
-
if (
|
2004
|
-
if (
|
2005
|
-
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));
|
2006
2192
|
}
|
2007
2193
|
// eslint-disable-next-line eqeqeq
|
2008
|
-
return
|
2194
|
+
return expectedData == data;
|
2009
2195
|
};
|
2010
2196
|
|
2011
2197
|
this.toString = function() {
|
2012
|
-
return
|
2198
|
+
return expectedMethod + ' ' + expectedUrl;
|
2013
2199
|
};
|
2014
2200
|
|
2015
|
-
this.params = function(
|
2016
|
-
|
2201
|
+
this.params = function(url) {
|
2202
|
+
var queryStr = url.indexOf('?') === -1 ? '' : url.substring(url.indexOf('?') + 1);
|
2203
|
+
var strippedUrl = stripQueryAndHash(url);
|
2017
2204
|
|
2018
|
-
|
2019
|
-
|
2020
|
-
if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
|
2205
|
+
return angular.extend(extractParamsFromQuery(queryStr), extractParamsFromPath(strippedUrl));
|
2206
|
+
};
|
2021
2207
|
|
2022
|
-
|
2023
|
-
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2029
|
-
|
2030
|
-
|
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
|
+
}
|
2031
2218
|
|
2032
|
-
|
2219
|
+
function normalizeQuery(queryStr) {
|
2220
|
+
return queryStr.split('&').sort().join('&');
|
2221
|
+
}
|
2222
|
+
|
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
|
+
}
|
2033
2238
|
}
|
2034
2239
|
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2051
|
-
|
2052
|
-
|
2053
|
-
|
2054
|
-
}
|
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];
|
2055
2259
|
}
|
2056
|
-
});
|
2057
|
-
return obj;
|
2058
|
-
}
|
2059
|
-
function tryDecodeURIComponent(value) {
|
2060
|
-
try {
|
2061
|
-
return decodeURIComponent(value);
|
2062
|
-
} catch (e) {
|
2063
|
-
// Ignore any invalid uri component
|
2064
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
|
2065
2271
|
}
|
2066
|
-
}
|
2272
|
+
}
|
2067
2273
|
}
|
2068
2274
|
|
2069
2275
|
function createMockXhr() {
|
@@ -2097,13 +2303,13 @@ function MockXhr() {
|
|
2097
2303
|
var header = this.$$respHeaders[name];
|
2098
2304
|
if (header) return header;
|
2099
2305
|
|
2100
|
-
name = angular
|
2306
|
+
name = angular.$$lowercase(name);
|
2101
2307
|
header = this.$$respHeaders[name];
|
2102
2308
|
if (header) return header;
|
2103
2309
|
|
2104
2310
|
header = undefined;
|
2105
2311
|
angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
|
2106
|
-
if (!header && angular
|
2312
|
+
if (!header && angular.$$lowercase(headerName) === name) header = headerVal;
|
2107
2313
|
});
|
2108
2314
|
return header;
|
2109
2315
|
};
|
@@ -2117,10 +2323,14 @@ function MockXhr() {
|
|
2117
2323
|
return lines.join('\n');
|
2118
2324
|
};
|
2119
2325
|
|
2120
|
-
this.abort =
|
2326
|
+
this.abort = function() {
|
2327
|
+
if (isFunction(this.onabort)) {
|
2328
|
+
this.onabort();
|
2329
|
+
}
|
2330
|
+
};
|
2121
2331
|
|
2122
2332
|
// This section simulates the events on a real XHR object (and the upload object)
|
2123
|
-
// When we are testing $httpBackend (inside the
|
2333
|
+
// When we are testing $httpBackend (inside the AngularJS project) we make partial use of this
|
2124
2334
|
// but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
|
2125
2335
|
this.$$events = {};
|
2126
2336
|
this.addEventListener = function(name, listener) {
|
@@ -2149,39 +2359,86 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
|
|
2149
2359
|
/**
|
2150
2360
|
* @ngdoc method
|
2151
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
|
+
*
|
2152
2369
|
* @description
|
2153
2370
|
*
|
2154
2371
|
* Flushes the queue of pending tasks.
|
2155
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
|
+
*
|
2156
2382
|
* @param {number=} delay maximum timeout amount to flush up until
|
2157
2383
|
*/
|
2158
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).
|
2159
2388
|
$browser.defer.flush(delay);
|
2160
2389
|
};
|
2161
2390
|
|
2162
2391
|
/**
|
2163
2392
|
* @ngdoc method
|
2164
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
|
+
*
|
2165
2402
|
* @description
|
2166
2403
|
*
|
2167
|
-
* 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>
|
2168
2424
|
*/
|
2169
2425
|
$delegate.verifyNoPendingTasks = function() {
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
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);
|
2173
2439
|
}
|
2174
2440
|
};
|
2175
2441
|
|
2176
|
-
function formatPendingTasksAsString(tasks) {
|
2177
|
-
var result = [];
|
2178
|
-
angular.forEach(tasks, function(task) {
|
2179
|
-
result.push('{id: ' + task.id + ', time: ' + task.time + '}');
|
2180
|
-
});
|
2181
|
-
|
2182
|
-
return result.join(', ');
|
2183
|
-
}
|
2184
|
-
|
2185
2442
|
return $delegate;
|
2186
2443
|
}];
|
2187
2444
|
|
@@ -2231,11 +2488,6 @@ angular.mock.$RootElementProvider = function() {
|
|
2231
2488
|
* A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
|
2232
2489
|
* controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
|
2233
2490
|
*
|
2234
|
-
* Depending on the value of
|
2235
|
-
* {@link ng.$compileProvider#preAssignBindingsEnabled `preAssignBindingsEnabled()`}, the properties
|
2236
|
-
* will be bound before or after invoking the constructor.
|
2237
|
-
*
|
2238
|
-
*
|
2239
2491
|
* ## Example
|
2240
2492
|
*
|
2241
2493
|
* ```js
|
@@ -2281,8 +2533,6 @@ angular.mock.$RootElementProvider = function() {
|
|
2281
2533
|
*
|
2282
2534
|
* * check if a controller with given name is registered via `$controllerProvider`
|
2283
2535
|
* * check if evaluating the string on the current scope returns a constructor
|
2284
|
-
* * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
|
2285
|
-
* `window` object (deprecated, not recommended)
|
2286
2536
|
*
|
2287
2537
|
* The string can use the `controller as property` syntax, where the controller instance is published
|
2288
2538
|
* as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
|
@@ -2293,22 +2543,13 @@ angular.mock.$RootElementProvider = function() {
|
|
2293
2543
|
* the `bindToController` feature and simplify certain kinds of tests.
|
2294
2544
|
* @return {Object} Instance of given controller.
|
2295
2545
|
*/
|
2296
|
-
function createControllerDecorator(
|
2546
|
+
function createControllerDecorator() {
|
2297
2547
|
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
2298
2548
|
return function(expression, locals, later, ident) {
|
2299
2549
|
if (later && typeof later === 'object') {
|
2300
|
-
var preAssignBindingsEnabled = compileProvider.preAssignBindingsEnabled();
|
2301
|
-
|
2302
2550
|
var instantiate = $delegate(expression, locals, true, ident);
|
2303
|
-
if (preAssignBindingsEnabled) {
|
2304
|
-
angular.extend(instantiate.instance, later);
|
2305
|
-
}
|
2306
|
-
|
2307
2551
|
var instance = instantiate();
|
2308
|
-
|
2309
|
-
angular.extend(instance, later);
|
2310
|
-
}
|
2311
|
-
|
2552
|
+
angular.extend(instance, later);
|
2312
2553
|
return instance;
|
2313
2554
|
}
|
2314
2555
|
return $delegate(expression, locals, later, ident);
|
@@ -2419,14 +2660,16 @@ angular.module('ngMock', ['ng']).provider({
|
|
2419
2660
|
$log: angular.mock.$LogProvider,
|
2420
2661
|
$interval: angular.mock.$IntervalProvider,
|
2421
2662
|
$rootElement: angular.mock.$RootElementProvider,
|
2422
|
-
$componentController: angular.mock.$ComponentControllerProvider
|
2663
|
+
$componentController: angular.mock.$ComponentControllerProvider,
|
2664
|
+
$flushPendingTasks: angular.mock.$FlushPendingTasksProvider,
|
2665
|
+
$verifyNoPendingTasks: angular.mock.$VerifyNoPendingTasksProvider
|
2423
2666
|
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
|
2424
2667
|
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
2425
2668
|
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
|
2426
2669
|
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
|
2427
2670
|
$provide.decorator('$controller', createControllerDecorator($compileProvider));
|
2428
2671
|
$provide.decorator('$httpBackend', angular.mock.$httpBackendDecorator);
|
2429
|
-
}]).info({ angularVersion: '1.
|
2672
|
+
}]).info({ angularVersion: '1.8.0' });
|
2430
2673
|
|
2431
2674
|
/**
|
2432
2675
|
* @ngdoc module
|
@@ -2435,13 +2678,13 @@ angular.module('ngMock', ['ng']).provider({
|
|
2435
2678
|
* @packageName angular-mocks
|
2436
2679
|
* @description
|
2437
2680
|
*
|
2438
|
-
* The `ngMockE2E` is an
|
2681
|
+
* The `ngMockE2E` is an AngularJS module which contains mocks suitable for end-to-end testing.
|
2439
2682
|
* Currently there is only one mock present in this module -
|
2440
2683
|
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
|
2441
2684
|
*/
|
2442
2685
|
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
2443
2686
|
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
2444
|
-
}]).info({ angularVersion: '1.
|
2687
|
+
}]).info({ angularVersion: '1.8.0' });
|
2445
2688
|
|
2446
2689
|
/**
|
2447
2690
|
* @ngdoc service
|
@@ -2728,6 +2971,39 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
|
2728
2971
|
* control how a matched request is handled. You can save this object for later use and invoke
|
2729
2972
|
* `respond` or `passThrough` again in order to change how a matched request is handled.
|
2730
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
|
+
*/
|
2731
3007
|
angular.mock.e2e = {};
|
2732
3008
|
angular.mock.e2e.$httpBackendDecorator =
|
2733
3009
|
['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
|
@@ -3249,6 +3525,9 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3249
3525
|
* - `charcode`: [charCode](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/charcode)
|
3250
3526
|
* for keyboard events (keydown, keypress, and keyup).
|
3251
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
|
+
*
|
3252
3531
|
* - `elapsedTime`: the elapsedTime for
|
3253
3532
|
* [TransitionEvent](https://developer.mozilla.org/docs/Web/API/TransitionEvent)
|
3254
3533
|
* and [AnimationEvent](https://developer.mozilla.org/docs/Web/API/AnimationEvent).
|
@@ -3353,6 +3632,24 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3353
3632
|
evnt.keyCode = eventData.keyCode;
|
3354
3633
|
evnt.charCode = eventData.charCode;
|
3355
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
|
+
|
3356
3653
|
} else {
|
3357
3654
|
evnt = window.document.createEvent('MouseEvents');
|
3358
3655
|
x = x || 0;
|
@@ -3368,30 +3665,11 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
|
3368
3665
|
|
3369
3666
|
if (!evnt) return;
|
3370
3667
|
|
3371
|
-
var originalPreventDefault = evnt.preventDefault,
|
3372
|
-
appWindow = element.ownerDocument.defaultView,
|
3373
|
-
fakeProcessDefault = true,
|
3374
|
-
finalProcessDefault,
|
3375
|
-
angular = appWindow.angular || {};
|
3376
|
-
|
3377
|
-
// igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
|
3378
|
-
angular['ff-684208-preventDefault'] = false;
|
3379
|
-
evnt.preventDefault = function() {
|
3380
|
-
fakeProcessDefault = false;
|
3381
|
-
return originalPreventDefault.apply(evnt, arguments);
|
3382
|
-
};
|
3383
|
-
|
3384
3668
|
if (!eventData.bubbles || supportsEventBubblingInDetachedTree() || isAttachedToDocument(element)) {
|
3385
|
-
element.dispatchEvent(evnt);
|
3669
|
+
return element.dispatchEvent(evnt);
|
3386
3670
|
} else {
|
3387
3671
|
triggerForPath(element, evnt);
|
3388
3672
|
}
|
3389
|
-
|
3390
|
-
finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
|
3391
|
-
|
3392
|
-
delete angular['ff-684208-preventDefault'];
|
3393
|
-
|
3394
|
-
return finalProcessDefault;
|
3395
3673
|
};
|
3396
3674
|
|
3397
3675
|
function supportsTouchEvents() {
|