angularjs-rails 1.6.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/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() {
|