Compare commits
299 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5745734991 | |||
| 0551aa95f0 | |||
| b62327ec2d | |||
| 07a58dd766 | |||
| ffe5e01584 | |||
| 3c2e1c5e4d | |||
| cdf7781878 | |||
| 7e8d3c1736 | |||
| d30845a757 | |||
| 06ed8ef012 | |||
| 2f437e8978 | |||
| faf02f0c4d | |||
| b8bd4d5460 | |||
| b2f46251ac | |||
| a26234f718 | |||
| 61315211da | |||
| af89daf464 | |||
| 224d7d6e90 | |||
| a179a9a96e | |||
| 610a5a0c14 | |||
| 4f5583465a | |||
| 75487ec933 | |||
| dddb1221fa | |||
| 69e4d40a76 | |||
| d521619c58 | |||
| 5ae63fd385 | |||
| 8b44324814 | |||
| 9e991ddb1d | |||
| 93070f1488 | |||
| 3c8583e5dd | |||
| 78699c62ea | |||
| 49f9e4cef1 | |||
| 7e746015ea | |||
| 8c269883fd | |||
| a69674b36d | |||
| 8b9e6c3501 | |||
| c97c53dbd4 | |||
| c6392616ea | |||
| 74dd2f7980 | |||
| c0cb9f8c14 | |||
| 7dff7bb696 | |||
| cdf6fb19c8 | |||
| c909f49112 | |||
| cc821502bc | |||
| 037aefae47 | |||
| febb4c1c35 | |||
| 76a6047af6 | |||
| b6b7c5a1d6 | |||
| 5b5f35d5e4 | |||
| 14948cf5d9 | |||
| 2b0978b07c | |||
| 1122dc7a5b | |||
| a3a9d4af05 | |||
| 36089931a5 | |||
| 74db36ee94 | |||
| 8d42ce8563 | |||
| f0c6ebc076 | |||
| 59d9b89852 | |||
| 6aac69039e | |||
| 9e96d98345 | |||
| 1e13544da8 | |||
| e03182f018 | |||
| f2d526190a | |||
| 039b138042 | |||
| 30a9da5dc1 | |||
| 25e1ad9a94 | |||
| 37cced6296 | |||
| a66c968df2 | |||
| c398d7d370 | |||
| d93533812b | |||
| ff11061a8f | |||
| c52bfd37ee | |||
| f8b755982a | |||
| bcc7089b3c | |||
| d43cc3f893 | |||
| c9d937082c | |||
| 26adeb119b | |||
| 15183f3e1f | |||
| 2c405f4171 | |||
| 8991680d8a | |||
| ec801ac137 | |||
| d6da505f4e | |||
| 5f7054bf5d | |||
| cf4ed8a145 | |||
| d1e48fcbf3 | |||
| cc42c99bec | |||
| 3c7bfa77aa | |||
| 2ee0f56c54 | |||
| 9b18644f30 | |||
| fce100a46c | |||
| 3a75b1124d | |||
| a32bc40fd7 | |||
| cfe13b5dac | |||
| d859dcecea | |||
| d3b32a7c94 | |||
| 1b17dfa693 | |||
| e6d9bea4f3 | |||
| c8e9105fe6 | |||
| d644dcfa52 | |||
| e7d37ee45a | |||
| 733a97adf8 | |||
| 96ed9ff59a | |||
| b9a9f91fbf | |||
| 650fd933df | |||
| e249502880 | |||
| ca3e0e7374 | |||
| e6966e05f5 | |||
| 682418f029 | |||
| c8fd7fd0e2 | |||
| 168db33985 | |||
| 79af2badcb | |||
| 610927d77b | |||
| 55d15806fb | |||
| 94e1c0391c | |||
| b21f4a376d | |||
| f28f283fcf | |||
| e362a510e3 | |||
| 8a7f752a80 | |||
| af7e0bd0a7 | |||
| bd524fc4e5 | |||
| 19a324ce11 | |||
| cd8b78ebfd | |||
| 8891757891 | |||
| 5c5193946d | |||
| ffa6c5195f | |||
| a758799c7f | |||
| e9253a88b9 | |||
| f3e053cb6f | |||
| 04450c48df | |||
| 8650843603 | |||
| e034fa08a8 | |||
| c6b4ab3548 | |||
| b429f538a3 | |||
| b3cae4f457 | |||
| 7b52a976e1 | |||
| 3a624a7ff5 | |||
| b32adb7dea | |||
| 271d2bed3a | |||
| 249a1d84e7 | |||
| fdf85bfd86 | |||
| 090e5426ac | |||
| 7c67b2fb6a | |||
| 3c9b39ff52 | |||
| 54b3875ba5 | |||
| 008a782bc8 | |||
| 524c5c8b5d | |||
| b936236fbc | |||
| fc115bfd0d | |||
| bca1604c12 | |||
| f4517b500c | |||
| f54edbbdd4 | |||
| be50e0769a | |||
| cf78fb5661 | |||
| 5c9eb75867 | |||
| f43cf3b816 | |||
| 175e727f05 | |||
| d938983c06 | |||
| 8d69f4b93a | |||
| ca96ec32f9 | |||
| 4f59022582 | |||
| 3bd95dbb1a | |||
| c959fa4fe8 | |||
| a5d434d857 | |||
| 0ae0591f42 | |||
| 43ac783d35 | |||
| c96dc60594 | |||
| b440ad36f3 | |||
| 8b2532cec7 | |||
| 8db47ca7d4 | |||
| bcaa3bb373 | |||
| 6fc4fdb438 | |||
| 6a5f8c0483 | |||
| 20c116d9d5 | |||
| 4a04c2ec0c | |||
| 89dd566277 | |||
| 9d168f058f | |||
| 5418564f04 | |||
| b0a05a7531 | |||
| 209b67df6a | |||
| 2e1539356a | |||
| 331cd5a8cb | |||
| f2ebfa16b0 | |||
| 95276a7e10 | |||
| 5dbd942bac | |||
| 84c13d96ff | |||
| 79941d2527 | |||
| 03ebecd5eb | |||
| 62bb728d07 | |||
| b8eb843b25 | |||
| 053247e412 | |||
| 7fa391c979 | |||
| b01c28c900 | |||
| c0b9e94dec | |||
| 83fbdd1097 | |||
| 03042c52b9 | |||
| 2a4a8226d1 | |||
| c81d8176cc | |||
| 04329151d2 | |||
| a9be003fce | |||
| ca30fce28c | |||
| b6e4a71166 | |||
| d9eff86ef7 | |||
| 8cb9c99ec0 | |||
| 9473780e77 | |||
| eb5fd400d3 | |||
| 0472c5f07e | |||
| 92558fe411 | |||
| d519953a4b | |||
| 4909d1d39d | |||
| 7079ff5eb6 | |||
| 10e1c759f4 | |||
| 6c67719dfa | |||
| cebd015f78 | |||
| fbdab513dd | |||
| f2b7fffdc0 | |||
| 42c38b29f7 | |||
| f299fd5122 | |||
| 05c88b866b | |||
| 9b08bfa251 | |||
| 99a000bac2 | |||
| f353fea042 | |||
| b1f50307b3 | |||
| d0c0eadedd | |||
| b8fac353f0 | |||
| b22308152f | |||
| 5e9041818b | |||
| db861db1f2 | |||
| b12d1b6813 | |||
| acb499f820 | |||
| 9a710c788d | |||
| 1b34c6d558 | |||
| a62c7b8b4e | |||
| 62cfedbe0c | |||
| 5cb7297a08 | |||
| 0f05516d14 | |||
| f5f1200f25 | |||
| c023c850c3 | |||
| 5318588d6e | |||
| 14c8f6a7ca | |||
| 351deb555f | |||
| 847d2da0f8 | |||
| dbefd671e4 | |||
| aff68a9ddf | |||
| 0a71753ce3 | |||
| 1a8642aac2 | |||
| 8114d55a15 | |||
| 9398040a41 | |||
| d804bbcd51 | |||
| d3fa7a2e9e | |||
| 8693eac417 | |||
| e0184d4aef | |||
| 1702e49548 | |||
| d6706efe7f | |||
| b08d4b22d2 | |||
| e8ded01cf5 | |||
| 7a5f25f667 | |||
| 96697f464f | |||
| 7e18724dfa | |||
| c269eb3d26 | |||
| fa62ea810f | |||
| bf8ed8a532 | |||
| d05a2809a1 | |||
| fa6c8c3131 | |||
| f7ac8ef97a | |||
| 4a4b28dbf3 | |||
| 3e12bc481d | |||
| 32137cab82 | |||
| f7b4296c38 | |||
| cab5e1d9b3 | |||
| dfe99836cd | |||
| 0f37194fb7 | |||
| e85774f709 | |||
| 44345c74de | |||
| 58f121a5c2 | |||
| cf6023ef22 | |||
| 2034871764 | |||
| 15d283b114 | |||
| 9be169365c | |||
| 00683a8bbb | |||
| f00b6cca02 | |||
| e05a97c6f5 | |||
| 2e3651686c | |||
| 536de14821 | |||
| e0a54f6b20 | |||
| 9767f7bdd3 | |||
| 167aa0c29c | |||
| 4ccd9eb883 | |||
| c0d638a94b | |||
| 054d40f338 | |||
| 5cec32492c | |||
| c25cb7d488 | |||
| 54e4a6ffbf | |||
| eee9a51fad | |||
| 77e6d833f6 | |||
| 33ad2b4126 | |||
| b84eaffd39 | |||
| a1107e81eb | |||
| e3e8813e3c | |||
| 6e2d9711e8 |
@@ -57,25 +57,6 @@ task :concat_scenario => :init do
|
||||
end
|
||||
|
||||
|
||||
desc 'Concat JSTD Scenario Adapter'
|
||||
task :concat_jstd_scenario_adapter => :init do
|
||||
|
||||
concat_file('jstd-scenario-adapter.js', [
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
|
||||
])
|
||||
|
||||
# TODO(vojta) use jstd configuration when implemented
|
||||
# (instead of including jstd-adapter-config.js)
|
||||
File.open(path_to('jstd-scenario-adapter-config.js'), 'w') do |f|
|
||||
f.write("/**\r\n" +
|
||||
" * Configuration for jstd scenario adapter \n */\n" +
|
||||
"var jstdScenarioAdapter = {\n relativeUrlPrefix: '/build/docs/'\n};\n")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
desc 'Concat AngularJS files'
|
||||
task :concat => :init do
|
||||
@@ -115,7 +96,7 @@ end
|
||||
|
||||
|
||||
desc 'Minify JavaScript'
|
||||
task :minify => [:init, :concat, :concat_scenario, :concat_jstd_scenario_adapter] do
|
||||
task :minify => [:init, :concat, :concat_scenario] do
|
||||
[ 'angular.js',
|
||||
'angular-cookies.js',
|
||||
'angular-loader.js',
|
||||
|
||||
Vendored
+3704
-3668
File diff suppressed because it is too large
Load Diff
Vendored
+2
-4
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
1.7.2
|
||||
1.8.2
|
||||
|
||||
@@ -410,9 +410,10 @@ function createInjector(modulesToLoad) {
|
||||
decorator: decorator
|
||||
}
|
||||
},
|
||||
providerInjector = createInternalInjector(providerCache, function() {
|
||||
throw Error("Unknown provider: " + path.join(' <- '));
|
||||
}),
|
||||
providerInjector = (providerCache.$injector =
|
||||
createInternalInjector(providerCache, function() {
|
||||
throw Error("Unknown provider: " + path.join(' <- '));
|
||||
})),
|
||||
instanceCache = {},
|
||||
instanceInjector = (instanceCache.$injector =
|
||||
createInternalInjector(instanceCache, function(servicename) {
|
||||
@@ -489,9 +490,7 @@ function createInjector(modulesToLoad) {
|
||||
try {
|
||||
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
|
||||
var invokeArgs = invokeQueue[i],
|
||||
provider = invokeArgs[0] == '$injector'
|
||||
? providerInjector
|
||||
: providerInjector.get(invokeArgs[0]);
|
||||
provider = providerInjector.get(invokeArgs[0]);
|
||||
|
||||
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
|
||||
}
|
||||
|
||||
+4
-3
@@ -353,11 +353,11 @@ var JQLitePrototype = JQLite.prototype = {
|
||||
// value on get.
|
||||
//////////////////////////////////////////
|
||||
var BOOLEAN_ATTR = {};
|
||||
forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) {
|
||||
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
|
||||
BOOLEAN_ATTR[lowercase(value)] = value;
|
||||
});
|
||||
var BOOLEAN_ELEMENTS = {};
|
||||
forEach('input,select,option,textarea,button,form'.split(','), function(value) {
|
||||
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
|
||||
BOOLEAN_ELEMENTS[uppercase(value)] = true;
|
||||
});
|
||||
|
||||
@@ -659,8 +659,9 @@ forEach({
|
||||
|
||||
append: function(element, node) {
|
||||
forEach(new JQLite(node), function(child){
|
||||
if (element.nodeType === 1)
|
||||
if (element.nodeType === 1 || element.nodeType === 11) {
|
||||
element.appendChild(child);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* @returns {object} Newly created cache object with the following set of methods:
|
||||
*
|
||||
* - `{object}` `info()` — Returns id, size, and options of cache.
|
||||
* - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache.
|
||||
* - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it.
|
||||
* - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
|
||||
* - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
|
||||
* - `{void}` `removeAll()` — Removes all cached values.
|
||||
@@ -53,6 +53,8 @@ function $CacheFactoryProvider() {
|
||||
if (size > capacity) {
|
||||
this.remove(staleEnd.key);
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -272,6 +272,37 @@
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngOpen
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as open.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce the `ngOpen` directive.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Check me check multiple: <input type="checkbox" ng-model="open"><br/>
|
||||
<details id="details" ng-open="open">
|
||||
<summary>Show/Hide me</summary>
|
||||
</details>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should toggle open', function() {
|
||||
expect(element('#details').prop('open')).toBeFalsy();
|
||||
input('open').check();
|
||||
expect(element('#details').prop('open')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element DETAILS
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
*/
|
||||
|
||||
var ngAttributeAliasDirectives = {};
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ var nullFormCtrl = {
|
||||
$addControl: noop,
|
||||
$removeControl: noop,
|
||||
$setValidity: noop,
|
||||
$setDirty: noop
|
||||
$setDirty: noop,
|
||||
$setPristine: noop
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -37,7 +38,8 @@ function FormController(element, attrs) {
|
||||
var form = this,
|
||||
parentForm = element.parent().controller('form') || nullFormCtrl,
|
||||
invalidCount = 0, // used to easily determine if we are valid
|
||||
errors = form.$error = {};
|
||||
errors = form.$error = {},
|
||||
controls = [];
|
||||
|
||||
// init state
|
||||
form.$name = attrs.name;
|
||||
@@ -61,6 +63,8 @@ function FormController(element, attrs) {
|
||||
}
|
||||
|
||||
form.$addControl = function(control) {
|
||||
controls.push(control);
|
||||
|
||||
if (control.$name && !form.hasOwnProperty(control.$name)) {
|
||||
form[control.$name] = control;
|
||||
}
|
||||
@@ -73,6 +77,8 @@ function FormController(element, attrs) {
|
||||
forEach(errors, function(queue, validationToken) {
|
||||
form.$setValidity(validationToken, true, control);
|
||||
});
|
||||
|
||||
arrayRemove(controls, control);
|
||||
};
|
||||
|
||||
form.$setValidity = function(validationToken, isValid, control) {
|
||||
@@ -120,6 +126,29 @@ function FormController(element, attrs) {
|
||||
parentForm.$setDirty();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.directive:form.FormController#$setPristine
|
||||
* @methodOf ng.directive:form.FormController
|
||||
*
|
||||
* @description
|
||||
* Sets the form to its pristine state.
|
||||
*
|
||||
* This method can be called to remove the 'ng-dirty' class and set the form to its pristine
|
||||
* state (ng-pristine class). This method will also propagate to all the controls contained
|
||||
* in this form.
|
||||
*
|
||||
* Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
|
||||
* saving or resetting it.
|
||||
*/
|
||||
form.$setPristine = function () {
|
||||
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
|
||||
form.$dirty = false;
|
||||
form.$pristine = true;
|
||||
forEach(controls, function(control) {
|
||||
control.$setPristine();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ var inputType = {
|
||||
* patterns defined as scope expressions.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the
|
||||
* input.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -35,12 +37,12 @@ var inputType = {
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.text = 'guest';
|
||||
$scope.word = /^\w*$/;
|
||||
$scope.word = /^\s*\w*\s*$/;
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl">
|
||||
Single word: <input type="text" name="input" ng-model="text"
|
||||
ng-pattern="word" required>
|
||||
ng-pattern="word" required ng-trim="false">
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.input.$error.pattern">
|
||||
@@ -69,6 +71,12 @@ var inputType = {
|
||||
input('text').enter('hello world');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should not be trimmed', function() {
|
||||
input('text').enter('untrimmed ');
|
||||
expect(binding('text')).toEqual('untrimmed ');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
@@ -382,7 +390,14 @@ function isEmpty(value) {
|
||||
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
|
||||
var listener = function() {
|
||||
var value = trim(element.val());
|
||||
var value = element.val();
|
||||
|
||||
// By default we will trim the value
|
||||
// If the attribute ng-trim exists we will avoid trimming
|
||||
// e.g. <input ng-model="foo" ng-trim="false">
|
||||
if (toBoolean(attr.ngTrim || 'T')) {
|
||||
value = trim(value);
|
||||
}
|
||||
|
||||
if (ctrl.$viewValue !== value) {
|
||||
scope.$apply(function() {
|
||||
@@ -963,6 +978,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
parentForm.$setValidity(validationErrorKey, isValid, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.directive:ngModel.NgModelController#$setPristine
|
||||
* @methodOf ng.directive:ngModel.NgModelController
|
||||
*
|
||||
* @description
|
||||
* Sets the control to its pristine state.
|
||||
*
|
||||
* This method can be called to remove the 'ng-dirty' class and set the control to its pristine
|
||||
* state (ng-pristine class).
|
||||
*/
|
||||
this.$setPristine = function () {
|
||||
this.$dirty = false;
|
||||
this.$pristine = true;
|
||||
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
*/
|
||||
var ngEventDirectives = {};
|
||||
forEach(
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup'.split(' '),
|
||||
function(name) {
|
||||
var directiveName = directiveNormalize('ng-' + name);
|
||||
ngEventDirectives[directiveName] = ['$parse', function($parse) {
|
||||
@@ -164,6 +164,38 @@ forEach(
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngKeydown
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior on keydown event.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
|
||||
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngKeyup
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior on keyup event.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
|
||||
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngSubmit
|
||||
|
||||
@@ -330,18 +330,22 @@ function dateFilter($locale) {
|
||||
|
||||
|
||||
var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
||||
function jsonStringToDate(string){
|
||||
// 1 2 3 4 5 6 7 8 9 10 11
|
||||
function jsonStringToDate(string) {
|
||||
var match;
|
||||
if (match = string.match(R_ISO8601_STR)) {
|
||||
var date = new Date(0),
|
||||
tzHour = 0,
|
||||
tzMin = 0;
|
||||
tzMin = 0,
|
||||
dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
|
||||
timeSetter = match[8] ? date.setUTCHours : date.setHours;
|
||||
|
||||
if (match[9]) {
|
||||
tzHour = int(match[9] + match[10]);
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||
date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
|
||||
dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
|
||||
timeSetter.call(date, int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
|
||||
return date;
|
||||
}
|
||||
return string;
|
||||
|
||||
+50
-33
@@ -6,20 +6,20 @@
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Creates a new array containing only a specified number of elements in an array. The elements
|
||||
* are taken from either the beginning or the end of the source array, as specified by the
|
||||
* value and sign (positive or negative) of `limit`.
|
||||
* Creates a new array or string containing only a specified number of elements. The elements
|
||||
* are taken from either the beginning or the end of the source array or string, as specified by
|
||||
* the value and sign (positive or negative) of `limit`.
|
||||
*
|
||||
* Note: This function is used to augment the `Array` type in Angular expressions. See
|
||||
* {@link ng.$filter} for more information about Angular arrays.
|
||||
*
|
||||
* @param {Array} array Source array to be limited.
|
||||
* @param {string|Number} limit The length of the returned array. If the `limit` number is
|
||||
* positive, `limit` number of items from the beginning of the source array are copied.
|
||||
* If the number is negative, `limit` number of items from the end of the source array are
|
||||
* copied. The `limit` will be trimmed if it exceeds `array.length`
|
||||
* @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
|
||||
* elements.
|
||||
* @param {Array|string} input Source array or string to be limited.
|
||||
* @param {string|number} limit The length of the returned array or string. If the `limit` number
|
||||
* is positive, `limit` number of items from the beginning of the source array/string are copied.
|
||||
* If the number is negative, `limit` number of items from the end of the source array/string
|
||||
* are copied. The `limit` will be trimmed if it exceeds `array.length`
|
||||
* @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
|
||||
* had less than `limit` elements.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -27,59 +27,76 @@
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.numbers = [1,2,3,4,5,6,7,8,9];
|
||||
$scope.limit = 3;
|
||||
$scope.letters = "abcdefghi";
|
||||
$scope.numLimit = 3;
|
||||
$scope.letterLimit = 3;
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Limit {{numbers}} to: <input type="integer" ng-model="limit">
|
||||
<p>Output: {{ numbers | limitTo:limit }}</p>
|
||||
Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
|
||||
<p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
|
||||
Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
|
||||
<p>Output letters: {{ letters | limitTo:letterLimit }}</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should limit the numer array to first three items', function() {
|
||||
expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3');
|
||||
expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]');
|
||||
it('should limit the number array to first three items', function() {
|
||||
expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3');
|
||||
expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3');
|
||||
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]');
|
||||
expect(binding('letters | limitTo:letterLimit')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should update the output when -3 is entered', function() {
|
||||
input('limit').enter(-3);
|
||||
expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]');
|
||||
input('numLimit').enter(-3);
|
||||
input('letterLimit').enter(-3);
|
||||
expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]');
|
||||
expect(binding('letters | limitTo:letterLimit')).toEqual('ghi');
|
||||
});
|
||||
|
||||
it('should not exceed the maximum size of input array', function() {
|
||||
input('limit').enter(100);
|
||||
expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]');
|
||||
input('numLimit').enter(100);
|
||||
input('letterLimit').enter(100);
|
||||
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]');
|
||||
expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
function limitToFilter(){
|
||||
return function(array, limit) {
|
||||
if (!(array instanceof Array)) return array;
|
||||
return function(input, limit) {
|
||||
if (!isArray(input) && !isString(input)) return input;
|
||||
|
||||
limit = int(limit);
|
||||
|
||||
if (isString(input)) {
|
||||
//NaN check on limit
|
||||
if (limit) {
|
||||
return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
var out = [],
|
||||
i, n;
|
||||
|
||||
// check that array is iterable
|
||||
if (!array || !(array instanceof Array))
|
||||
return out;
|
||||
|
||||
// if abs(limit) exceeds maximum length, trim it
|
||||
if (limit > array.length)
|
||||
limit = array.length;
|
||||
else if (limit < -array.length)
|
||||
limit = -array.length;
|
||||
if (limit > input.length)
|
||||
limit = input.length;
|
||||
else if (limit < -input.length)
|
||||
limit = -input.length;
|
||||
|
||||
if (limit > 0) {
|
||||
i = 0;
|
||||
n = limit;
|
||||
} else {
|
||||
i = array.length + limit;
|
||||
n = array.length;
|
||||
i = input.length + limit;
|
||||
n = input.length;
|
||||
}
|
||||
|
||||
for (; i<n; i++) {
|
||||
out.push(array[i]);
|
||||
out.push(input[i]);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
+65
-17
@@ -29,6 +29,43 @@ function parseHeaders(headers) {
|
||||
}
|
||||
|
||||
|
||||
var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/;
|
||||
|
||||
|
||||
/**
|
||||
* Parse a request and location URL and determine whether this is a same-domain request.
|
||||
*
|
||||
* @param {string} requestUrl The url of the request.
|
||||
* @param {string} locationUrl The current browser location url.
|
||||
* @returns {boolean} Whether the request is for the same domain.
|
||||
*/
|
||||
function isSameDomain(requestUrl, locationUrl) {
|
||||
var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl);
|
||||
// if requestUrl is relative, the regex does not match.
|
||||
if (match == null) return true;
|
||||
|
||||
var domain1 = {
|
||||
protocol: match[2],
|
||||
host: match[4],
|
||||
port: int(match[6]) || DEFAULT_PORTS[match[2]] || null,
|
||||
// IE8 sets unmatched groups to '' instead of undefined.
|
||||
relativeProtocol: match[2] === undefined || match[2] === ''
|
||||
};
|
||||
|
||||
match = URL_MATCH.exec(locationUrl);
|
||||
var domain2 = {
|
||||
protocol: match[1],
|
||||
host: match[3],
|
||||
port: int(match[5]) || DEFAULT_PORTS[match[1]] || null
|
||||
};
|
||||
|
||||
return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) &&
|
||||
domain1.host == domain2.host &&
|
||||
(domain1.port == domain2.port || (domain1.relativeProtocol &&
|
||||
domain2.port == DEFAULT_PORTS[domain2.protocol]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a function that provides access to parsed headers.
|
||||
*
|
||||
@@ -88,7 +125,7 @@ function $HttpProvider() {
|
||||
JSON_END = /[\}\]]\s*$/,
|
||||
PROTECTION_PREFIX = /^\)\]\}',?\n/;
|
||||
|
||||
var $config = this.defaults = {
|
||||
var defaults = this.defaults = {
|
||||
// transform incoming response data
|
||||
transformResponse: [function(data) {
|
||||
if (isString(data)) {
|
||||
@@ -108,8 +145,7 @@ function $HttpProvider() {
|
||||
// default headers
|
||||
headers: {
|
||||
common: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
post: {'Content-Type': 'application/json;charset=utf-8'},
|
||||
put: {'Content-Type': 'application/json;charset=utf-8'}
|
||||
@@ -215,7 +251,6 @@ function $HttpProvider() {
|
||||
*
|
||||
* - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
|
||||
* - `Accept: application/json, text/plain, * / *`
|
||||
* - `X-Requested-With: XMLHttpRequest`
|
||||
* - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
|
||||
* - `Content-Type: application/json`
|
||||
* - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
|
||||
@@ -350,7 +385,7 @@ function $HttpProvider() {
|
||||
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
|
||||
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
|
||||
* runs on your domain could read the cookie, your server can be assured that the XHR came from
|
||||
* JavaScript running on your domain.
|
||||
* JavaScript running on your domain. The header will not be set for cross-domain requests.
|
||||
*
|
||||
* To take advantage of this, your server needs to set a token in a JavaScript readable session
|
||||
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
|
||||
@@ -384,6 +419,8 @@ function $HttpProvider() {
|
||||
* - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
|
||||
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
|
||||
* requests with credentials} for more information.
|
||||
* - **responseType** - `{string}` - see {@link
|
||||
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
|
||||
*
|
||||
* @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
|
||||
* standard `then` method and two http specific methods: `success` and `error`. The `then`
|
||||
@@ -476,10 +513,12 @@ function $HttpProvider() {
|
||||
function $http(config) {
|
||||
config.method = uppercase(config.method);
|
||||
|
||||
var reqTransformFn = config.transformRequest || $config.transformRequest,
|
||||
respTransformFn = config.transformResponse || $config.transformResponse,
|
||||
defHeaders = $config.headers,
|
||||
reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
|
||||
var reqTransformFn = config.transformRequest || defaults.transformRequest,
|
||||
respTransformFn = config.transformResponse || defaults.transformResponse,
|
||||
defHeaders = defaults.headers,
|
||||
xsrfToken = isSameDomain(config.url, $browser.url()) ?
|
||||
$browser.cookies()['XSRF-TOKEN'] : undefined,
|
||||
reqHeaders = extend({'X-XSRF-TOKEN': xsrfToken},
|
||||
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
|
||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
||||
promise;
|
||||
@@ -489,6 +528,10 @@ function $HttpProvider() {
|
||||
delete reqHeaders['Content-Type'];
|
||||
}
|
||||
|
||||
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
||||
config.withCredentials = defaults.withCredentials;
|
||||
}
|
||||
|
||||
// send request
|
||||
promise = sendReq(config, reqData, reqHeaders);
|
||||
|
||||
@@ -620,11 +663,11 @@ function $HttpProvider() {
|
||||
*
|
||||
* @description
|
||||
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
|
||||
* default headers as well as request and response transformations.
|
||||
* default headers, withCredentials as well as request and response transformations.
|
||||
*
|
||||
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
|
||||
*/
|
||||
$http.defaults = $config;
|
||||
$http.defaults = defaults;
|
||||
|
||||
|
||||
return $http;
|
||||
@@ -659,7 +702,7 @@ function $HttpProvider() {
|
||||
* Makes the request
|
||||
*
|
||||
* !!! ACCESSES CLOSURE VARS:
|
||||
* $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
|
||||
* $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
|
||||
*/
|
||||
function sendReq(config, reqData, reqHeaders) {
|
||||
var deferred = $q.defer(),
|
||||
@@ -700,7 +743,7 @@ function $HttpProvider() {
|
||||
// if we won't have the response in cache, send the request to the backend
|
||||
if (!cachedResp) {
|
||||
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
|
||||
config.withCredentials);
|
||||
config.withCredentials, config.responseType);
|
||||
}
|
||||
|
||||
return promise;
|
||||
@@ -755,10 +798,15 @@ function $HttpProvider() {
|
||||
var parts = [];
|
||||
forEachSorted(params, function(value, key) {
|
||||
if (value == null || value == undefined) return;
|
||||
if (isObject(value)) {
|
||||
value = toJson(value);
|
||||
}
|
||||
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
|
||||
if (!isArray(value)) value = [value];
|
||||
|
||||
forEach(value, function(v) {
|
||||
if (isObject(v)) {
|
||||
v = toJson(v);
|
||||
}
|
||||
parts.push(encodeURIComponent(key) + '=' +
|
||||
encodeURIComponent(v));
|
||||
});
|
||||
});
|
||||
return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ function $HttpBackendProvider() {
|
||||
|
||||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
|
||||
// TODO(vojta): fix the signature
|
||||
return function(method, url, post, callback, headers, timeout, withCredentials) {
|
||||
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
||||
$browser.$$incOutstandingRequestCount();
|
||||
url = url || $browser.url();
|
||||
|
||||
@@ -65,8 +65,8 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
// always async
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
completeRequest(
|
||||
callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
|
||||
completeRequest(callback, status || xhr.status, xhr.response || xhr.responseText,
|
||||
xhr.getAllResponseHeaders());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,6 +74,10 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
xhr.withCredentials = true;
|
||||
}
|
||||
|
||||
if (responseType) {
|
||||
xhr.responseType = responseType;
|
||||
}
|
||||
|
||||
xhr.send(post || '');
|
||||
|
||||
if (timeout > 0) {
|
||||
|
||||
+16
-10
@@ -52,7 +52,7 @@ function $InterpolateProvider() {
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$parse', function($parse) {
|
||||
this.$get = ['$parse', '$exceptionHandler', function($parse, $exceptionHandler) {
|
||||
var startSymbolLength = startSymbol.length,
|
||||
endSymbolLength = endSymbol.length;
|
||||
|
||||
@@ -124,18 +124,24 @@ function $InterpolateProvider() {
|
||||
if (!mustHaveExpression || hasInterpolation) {
|
||||
concat.length = length;
|
||||
fn = function(context) {
|
||||
for(var i = 0, ii = length, part; i<ii; i++) {
|
||||
if (typeof (part = parts[i]) == 'function') {
|
||||
part = part(context);
|
||||
if (part == null || part == undefined) {
|
||||
part = '';
|
||||
} else if (typeof part != 'string') {
|
||||
part = toJson(part);
|
||||
try {
|
||||
for(var i = 0, ii = length, part; i<ii; i++) {
|
||||
if (typeof (part = parts[i]) == 'function') {
|
||||
part = part(context);
|
||||
if (part == null || part == undefined) {
|
||||
part = '';
|
||||
} else if (typeof part != 'string') {
|
||||
part = toJson(part);
|
||||
}
|
||||
}
|
||||
concat[i] = part;
|
||||
}
|
||||
concat[i] = part;
|
||||
return concat.join('');
|
||||
}
|
||||
catch(err) {
|
||||
var newErr = new Error('Error while interpolating: ' + text + '\n' + err.toString());
|
||||
$exceptionHandler(newErr);
|
||||
}
|
||||
return concat.join('');
|
||||
};
|
||||
fn.exp = text;
|
||||
fn.parts = parts;
|
||||
|
||||
+45
-1
@@ -33,7 +33,33 @@
|
||||
</example>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ng.$logProvider
|
||||
* @description
|
||||
* Use the `$logProvider` to configure how the application logs messages
|
||||
*/
|
||||
function $LogProvider(){
|
||||
var debug = true,
|
||||
self = this;
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name ng.$logProvider#debugEnabled
|
||||
* @methodOf ng.$logProvider
|
||||
* @description
|
||||
* @param {string=} flag enable or disable debug level messages
|
||||
* @returns {*} current value if used as getter or itself (chaining) if used as setter
|
||||
*/
|
||||
this.debugEnabled = function(flag) {
|
||||
if (isDefined(flag)) {
|
||||
debug = flag;
|
||||
return this;
|
||||
} else {
|
||||
return debug;
|
||||
}
|
||||
};
|
||||
|
||||
this.$get = ['$window', function($window){
|
||||
return {
|
||||
/**
|
||||
@@ -74,7 +100,25 @@ function $LogProvider(){
|
||||
* @description
|
||||
* Write an error message
|
||||
*/
|
||||
error: consoleLog('error')
|
||||
error: consoleLog('error'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ng.$log#debug
|
||||
* @methodOf ng.$log
|
||||
*
|
||||
* @description
|
||||
* Write a debug message
|
||||
*/
|
||||
debug: (function () {
|
||||
var fn = consoleLog('debug');
|
||||
|
||||
return function() {
|
||||
if (debug) {
|
||||
fn.apply(self, arguments);
|
||||
}
|
||||
}
|
||||
}())
|
||||
};
|
||||
|
||||
function formatError(arg) {
|
||||
|
||||
+13
-5
@@ -20,6 +20,8 @@ var OPERATORS = {
|
||||
'%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
|
||||
'^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
|
||||
'=':noop,
|
||||
'===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
|
||||
'!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
|
||||
'==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
|
||||
'!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
|
||||
'<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
|
||||
@@ -70,9 +72,14 @@ function lex(text, csp){
|
||||
continue;
|
||||
} else {
|
||||
var ch2 = ch + peek(),
|
||||
ch3 = ch2 + peek(2),
|
||||
fn = OPERATORS[ch],
|
||||
fn2 = OPERATORS[ch2];
|
||||
if (fn2) {
|
||||
fn2 = OPERATORS[ch2],
|
||||
fn3 = OPERATORS[ch3];
|
||||
if (fn3) {
|
||||
tokens.push({index:index, text:ch3, fn:fn3});
|
||||
index += 3;
|
||||
} else if (fn2) {
|
||||
tokens.push({index:index, text:ch2, fn:fn2});
|
||||
index += 2;
|
||||
} else if (fn) {
|
||||
@@ -94,8 +101,9 @@ function lex(text, csp){
|
||||
return chars.indexOf(lastCh) != -1;
|
||||
}
|
||||
|
||||
function peek() {
|
||||
return index + 1 < text.length ? text.charAt(index + 1) : false;
|
||||
function peek(i) {
|
||||
var num = i || 1;
|
||||
return index + num < text.length ? text.charAt(index + num) : false;
|
||||
}
|
||||
function isNumber(ch) {
|
||||
return '0' <= ch && ch <= '9';
|
||||
@@ -456,7 +464,7 @@ function parser(text, json, $filter, csp){
|
||||
function equality() {
|
||||
var left = relational();
|
||||
var token;
|
||||
if ((token = expect('==','!='))) {
|
||||
if ((token = expect('==','!=','===','!=='))) {
|
||||
left = binaryFn(left, token.fn, equality());
|
||||
}
|
||||
return left;
|
||||
|
||||
+11
-11
@@ -196,7 +196,6 @@ function $RootScopeProvider(){
|
||||
child['this'] = child;
|
||||
child.$$listeners = {};
|
||||
child.$parent = this;
|
||||
child.$$asyncQueue = [];
|
||||
child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
|
||||
child.$$prevSibling = this.$$childTail;
|
||||
if (this.$$childHead) {
|
||||
@@ -363,7 +362,7 @@ function $RootScopeProvider(){
|
||||
$digest: function() {
|
||||
var watch, value, last,
|
||||
watchers,
|
||||
asyncQueue,
|
||||
asyncQueue = this.$$asyncQueue,
|
||||
length,
|
||||
dirty, ttl = TTL,
|
||||
next, current, target = this,
|
||||
@@ -372,18 +371,19 @@ function $RootScopeProvider(){
|
||||
|
||||
beginPhase('$digest');
|
||||
|
||||
do {
|
||||
do { // "while dirty" loop
|
||||
dirty = false;
|
||||
current = target;
|
||||
do {
|
||||
asyncQueue = current.$$asyncQueue;
|
||||
while(asyncQueue.length) {
|
||||
try {
|
||||
current.$eval(asyncQueue.shift());
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
|
||||
while(asyncQueue.length) {
|
||||
try {
|
||||
current.$eval(asyncQueue.shift());
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
|
||||
do { // "traverse the scopes" loop
|
||||
if ((watchers = current.$$watchers)) {
|
||||
// process our watches
|
||||
length = watchers.length;
|
||||
|
||||
+28
-7
@@ -35,12 +35,24 @@ function $RouteProvider(){
|
||||
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
|
||||
* created scope or the name of a {@link angular.Module#controller registered controller}
|
||||
* if passed as a string.
|
||||
* - `template` – `{string=}` – html template as a string that should be used by
|
||||
* {@link ng.directive:ngView ngView} or
|
||||
* - `template` – `{string=|function()=}` – html template as a string or function that returns
|
||||
* an html template as a string which should be used by {@link ng.directive:ngView ngView} or
|
||||
* {@link ng.directive:ngInclude ngInclude} directives.
|
||||
* this property takes precedence over `templateUrl`.
|
||||
* - `templateUrl` – `{string=}` – path to an html template that should be used by
|
||||
* {@link ng.directive:ngView ngView}.
|
||||
* This property takes precedence over `templateUrl`.
|
||||
*
|
||||
* If `template` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
||||
* template that should be used by {@link ng.directive:ngView ngView}.
|
||||
*
|
||||
* If `templateUrl` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
|
||||
* be injected into the controller. If any of these dependencies are promises, they will be
|
||||
* resolved and converted to a value before the controller is instantiated and the
|
||||
@@ -395,9 +407,18 @@ function $RouteProvider(){
|
||||
values.push(isString(value) ? $injector.get(value) : $injector.invoke(value));
|
||||
});
|
||||
if (isDefined(template = next.template)) {
|
||||
if (isFunction(template)) {
|
||||
template = template(next.params);
|
||||
}
|
||||
} else if (isDefined(template = next.templateUrl)) {
|
||||
template = $http.get(template, {cache: $templateCache}).
|
||||
then(function(response) { return response.data; });
|
||||
if (isFunction(template)) {
|
||||
template = template(next.params);
|
||||
}
|
||||
if (isDefined(template)) {
|
||||
next.loadedTemplateUrl = template;
|
||||
template = $http.get(template, {cache: $templateCache}).
|
||||
then(function(response) { return response.data; });
|
||||
}
|
||||
}
|
||||
if (isDefined(template)) {
|
||||
keys.push('$template');
|
||||
|
||||
+7
-6
@@ -5,6 +5,7 @@
|
||||
*
|
||||
* @name ng.$sniffer
|
||||
* @requires $window
|
||||
* @requires $document
|
||||
*
|
||||
* @property {boolean} history Does the browser support html5 history api ?
|
||||
* @property {boolean} hashchange Does the browser support hashchange event ?
|
||||
@@ -13,9 +14,10 @@
|
||||
* This is very simple implementation of testing browser's features.
|
||||
*/
|
||||
function $SnifferProvider() {
|
||||
this.$get = ['$window', function($window) {
|
||||
this.$get = ['$window', '$document', function($window, $document) {
|
||||
var eventSupport = {},
|
||||
android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]);
|
||||
android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]),
|
||||
document = $document[0];
|
||||
|
||||
return {
|
||||
// Android has history.pushState, but it does not update location correctly
|
||||
@@ -25,7 +27,7 @@ function $SnifferProvider() {
|
||||
history: !!($window.history && $window.history.pushState && !(android < 4)),
|
||||
hashchange: 'onhashchange' in $window &&
|
||||
// IE8 compatible mode lies
|
||||
(!$window.document.documentMode || $window.document.documentMode > 7),
|
||||
(!document.documentMode || document.documentMode > 7),
|
||||
hasEvent: function(event) {
|
||||
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
|
||||
// it. In particular the event is not fired when backspace or delete key are pressed or
|
||||
@@ -33,14 +35,13 @@ function $SnifferProvider() {
|
||||
if (event == 'input' && msie == 9) return false;
|
||||
|
||||
if (isUndefined(eventSupport[event])) {
|
||||
var divElm = $window.document.createElement('div');
|
||||
var divElm = document.createElement('div');
|
||||
eventSupport[event] = 'on' + event in divElm;
|
||||
}
|
||||
|
||||
return eventSupport[event];
|
||||
},
|
||||
// TODO(i): currently there is no way to feature detect CSP without triggering alerts
|
||||
csp: false
|
||||
csp: document.securityPolicy ? document.securityPolicy.isActive : false
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
Vendored
+61
-35
@@ -1352,17 +1352,49 @@ function MockXhr() {
|
||||
* @description
|
||||
*
|
||||
* This service is just a simple decorator for {@link ng.$timeout $timeout} service
|
||||
* that adds a "flush" method.
|
||||
*/
|
||||
* that adds a "flush" and "verifyNoPendingTasks" methods.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngMock.$timeout#flush
|
||||
* @methodOf ngMock.$timeout
|
||||
* @description
|
||||
*
|
||||
* Flushes the queue of pending tasks.
|
||||
*/
|
||||
angular.mock.$TimeoutDecorator = function($delegate, $browser) {
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngMock.$timeout#flush
|
||||
* @methodOf ngMock.$timeout
|
||||
* @description
|
||||
*
|
||||
* Flushes the queue of pending tasks.
|
||||
*/
|
||||
$delegate.flush = function() {
|
||||
$browser.defer.flush();
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngMock.$timeout#verifyNoPendingTasks
|
||||
* @methodOf ngMock.$timeout
|
||||
* @description
|
||||
*
|
||||
* Verifies that there are no pending tasks that need to be flushed.
|
||||
*/
|
||||
$delegate.verifyNoPendingTasks = function() {
|
||||
if ($browser.deferredFns.length) {
|
||||
throw Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
|
||||
formatPendingTasksAsString($browser.deferredFns));
|
||||
}
|
||||
};
|
||||
|
||||
function formatPendingTasksAsString(tasks) {
|
||||
var result = [];
|
||||
angular.forEach(tasks, function(task) {
|
||||
result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
|
||||
});
|
||||
|
||||
return result.join(', ');
|
||||
}
|
||||
|
||||
return $delegate;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -1388,15 +1420,9 @@ angular.module('ngMock', ['ng']).provider({
|
||||
$httpBackend: angular.mock.$HttpBackendProvider,
|
||||
$rootElement: angular.mock.$RootElementProvider
|
||||
}).config(function($provide) {
|
||||
$provide.decorator('$timeout', function($delegate, $browser) {
|
||||
$delegate.flush = function() {
|
||||
$browser.defer.flush();
|
||||
};
|
||||
return $delegate;
|
||||
});
|
||||
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngMockE2E
|
||||
@@ -1610,14 +1636,20 @@ window.jstestdriver && (function(window) {
|
||||
})(window);
|
||||
|
||||
|
||||
window.jasmine && (function(window) {
|
||||
(window.jasmine || window.mocha) && (function(window) {
|
||||
|
||||
var currentSpec = null;
|
||||
|
||||
beforeEach(function() {
|
||||
currentSpec = this;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
var spec = getCurrentSpec();
|
||||
var injector = spec.$injector;
|
||||
var injector = currentSpec.$injector;
|
||||
|
||||
spec.$injector = null;
|
||||
spec.$modules = null;
|
||||
currentSpec.$injector = null;
|
||||
currentSpec.$modules = null;
|
||||
currentSpec = null;
|
||||
|
||||
if (injector) {
|
||||
injector.get('$rootElement').unbind();
|
||||
@@ -1639,13 +1671,8 @@ window.jasmine && (function(window) {
|
||||
angular.callbacks.counter = 0;
|
||||
});
|
||||
|
||||
function getCurrentSpec() {
|
||||
return jasmine.getEnv().currentSpec;
|
||||
}
|
||||
|
||||
function isSpecRunning() {
|
||||
var spec = getCurrentSpec();
|
||||
return spec && spec.queue.running;
|
||||
return currentSpec && currentSpec.queue.running;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1670,11 +1697,10 @@ window.jasmine && (function(window) {
|
||||
return isSpecRunning() ? workFn() : workFn;
|
||||
/////////////////////
|
||||
function workFn() {
|
||||
var spec = getCurrentSpec();
|
||||
if (spec.$injector) {
|
||||
if (currentSpec.$injector) {
|
||||
throw Error('Injector already created, can not register a module!');
|
||||
} else {
|
||||
var modules = spec.$modules || (spec.$modules = []);
|
||||
var modules = currentSpec.$modules || (currentSpec.$modules = []);
|
||||
angular.forEach(moduleFns, function(module) {
|
||||
modules.push(module);
|
||||
});
|
||||
@@ -1741,13 +1767,13 @@ window.jasmine && (function(window) {
|
||||
return isSpecRunning() ? workFn() : workFn;
|
||||
/////////////////////
|
||||
function workFn() {
|
||||
var spec = getCurrentSpec();
|
||||
var modules = spec.$modules || [];
|
||||
var modules = currentSpec.$modules || [];
|
||||
|
||||
modules.unshift('ngMock');
|
||||
modules.unshift('ng');
|
||||
var injector = spec.$injector;
|
||||
var injector = currentSpec.$injector;
|
||||
if (!injector) {
|
||||
injector = spec.$injector = angular.injector(modules);
|
||||
injector = currentSpec.$injector = angular.injector(modules);
|
||||
}
|
||||
for(var i = 0, ii = blockFns.length; i < ii; i++) {
|
||||
try {
|
||||
|
||||
+42
-15
@@ -24,7 +24,8 @@
|
||||
* number, like this: `$resource('http://example.com\\:8080/api')`.
|
||||
*
|
||||
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||
* `actions` methods.
|
||||
* `actions` methods. If any of the parameter value is a function, it will be executed every time
|
||||
* when a param value needs to be obtained for a request (unless the param was overriden).
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
@@ -36,21 +37,40 @@
|
||||
* the data object (useful for non-GET operations).
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
|
||||
* default set of resource actions. The declaration should be created in the following format:
|
||||
* default set of resource actions. The declaration should be created in the format of {@link
|
||||
* ng.$http#Parameters $http.config}:
|
||||
*
|
||||
* {action1: {method:?, params:?, isArray:?},
|
||||
* action2: {method:?, params:?, isArray:?},
|
||||
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* action2: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* ...}
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* - `action` – {string} – The name of action. This name becomes the name of the method on your
|
||||
* - **`action`** – {string} – The name of action. This name becomes the name of the method on your
|
||||
* resource object.
|
||||
* - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
|
||||
* and `JSONP`
|
||||
* - `params` – {object=} – Optional set of pre-bound parameters for this action.
|
||||
* - isArray – {boolean=} – If true then the returned object for this action is an array, see
|
||||
* - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
|
||||
* and `JSONP`.
|
||||
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of the
|
||||
* parameter value is a function, it will be executed every time when a param value needs to be
|
||||
* obtained for a request (unless the param was overriden).
|
||||
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, see
|
||||
* `returns` section.
|
||||
* - **`transformRequest`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* request body and headers and returns its transformed (typically serialized) version.
|
||||
* - **`transformResponse`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body and headers and returns its transformed (typically deserialized) version.
|
||||
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with
|
||||
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||
* caching.
|
||||
* - **`timeout`** – `{number}` – timeout in milliseconds.
|
||||
* - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the
|
||||
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
|
||||
* requests with credentials} for more information.
|
||||
* - **`responseType`** - `{string}` - see {@link
|
||||
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
|
||||
*
|
||||
* @returns {Object} A resource "class" object with methods for the default set of resource actions
|
||||
* optionally extended with custom `actions`. The default set contains these actions:
|
||||
@@ -132,7 +152,7 @@
|
||||
* The object returned from this function execution is a resource "class" which has "static" method
|
||||
* for each action in the definition.
|
||||
*
|
||||
* Calling these methods invoke `$http` on the `url` template with the given `method` and `params`.
|
||||
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and `headers`.
|
||||
* When the data is returned from the server then the object is an instance of the resource type and
|
||||
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
|
||||
* operations (create, read, update, delete) on server-side data.
|
||||
@@ -324,6 +344,7 @@ angular.module('ngResource', ['ng']).
|
||||
var ids = {};
|
||||
actionParams = extend({}, paramDefaults, actionParams);
|
||||
forEach(actionParams, function(value, key){
|
||||
if (isFunction(value)) { value = value(); }
|
||||
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
@@ -376,11 +397,17 @@ angular.module('ngResource', ['ng']).
|
||||
}
|
||||
|
||||
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
|
||||
$http({
|
||||
method: action.method,
|
||||
url: route.url(extend({}, extractParams(data, action.params || {}), params)),
|
||||
data: data
|
||||
}).then(function(response) {
|
||||
var httpConfig = {};
|
||||
|
||||
forEach(action, function(value, key) {
|
||||
if (key != 'params' && key != 'isArray' ) {
|
||||
httpConfig[key] = copy(value);
|
||||
}
|
||||
});
|
||||
httpConfig.data = data;
|
||||
httpConfig.url = route.url(extend({}, extractParams(data, action.params || {}), params))
|
||||
|
||||
$http(httpConfig).then(function(response) {
|
||||
var data = response.data;
|
||||
|
||||
if (data) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* plain email address links.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
|
||||
* @returns {string} Html-linkified text.
|
||||
*
|
||||
* @usage
|
||||
@@ -24,6 +25,7 @@
|
||||
'mailto:us@somewhere.org,\n'+
|
||||
'another@somewhere.org,\n'+
|
||||
'and one more: ftp://127.0.0.1/.';
|
||||
$scope.snippetWithTarget = 'http://angularjs.org/';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
@@ -43,6 +45,15 @@
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="linky-target">
|
||||
<td>linky target</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippetWithTarget | linky:'_blank'"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
@@ -75,6 +86,11 @@
|
||||
toBe('new <a href="http://link">http://link</a>.');
|
||||
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
|
||||
});
|
||||
|
||||
it('should work with the target property', function() {
|
||||
expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
|
||||
toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
@@ -82,7 +98,7 @@ angular.module('ngSanitize').filter('linky', function() {
|
||||
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text) {
|
||||
return function(text, target) {
|
||||
if (!text) return text;
|
||||
var match;
|
||||
var raw = text;
|
||||
@@ -91,6 +107,10 @@ angular.module('ngSanitize').filter('linky', function() {
|
||||
var writer = htmlSanitizeWriter(html);
|
||||
var url;
|
||||
var i;
|
||||
var properties = {};
|
||||
if (angular.isDefined(target)) {
|
||||
properties.target = target;
|
||||
}
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
@@ -98,7 +118,8 @@ angular.module('ngSanitize').filter('linky', function() {
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
writer.chars(raw.substr(0, i));
|
||||
writer.start('a', {href:url});
|
||||
properties.href = url;
|
||||
writer.start('a', properties);
|
||||
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
|
||||
writer.end('a');
|
||||
raw = raw.substring(i + match[0].length);
|
||||
|
||||
@@ -123,7 +123,7 @@ var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:
|
||||
BEGING_END_TAGE_REGEXP = /^<\s*\//,
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
|
||||
|
||||
|
||||
|
||||
@@ -298,6 +298,8 @@ angular.scenario.dsl('select', function() {
|
||||
option = select.find('option:contains("' + value + '")');
|
||||
if (option.length) {
|
||||
select.val(option.val());
|
||||
} else {
|
||||
return done("option '" + value + "' not found");
|
||||
}
|
||||
}
|
||||
select.trigger('change');
|
||||
@@ -325,6 +327,7 @@ angular.scenario.dsl('select', function() {
|
||||
* Usage:
|
||||
* element(selector, label).count() get the number of elements that match selector
|
||||
* element(selector, label).click() clicks an element
|
||||
* element(selector, label).mouseover() mouseover an element
|
||||
* element(selector, label).query(fn) executes fn(selectedElements, done)
|
||||
* element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
|
||||
* element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
|
||||
@@ -365,6 +368,30 @@ angular.scenario.dsl('element', function() {
|
||||
});
|
||||
};
|
||||
|
||||
chain.dblclick = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' dblclick", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
var href = elements.attr('href');
|
||||
var eventProcessDefault = elements.trigger('dblclick')[0];
|
||||
|
||||
if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) {
|
||||
this.application.navigateTo(href, function() {
|
||||
done();
|
||||
}, done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
chain.mouseover = function() {
|
||||
return this.addFutureAction("element '" + this.label + "' mouseover", function($window, $document, done) {
|
||||
var elements = $document.elements();
|
||||
elements.trigger('mouseover');
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
chain.query = function(fn) {
|
||||
return this.addFutureAction('element ' + this.label + ' custom query', function($window, $document, done) {
|
||||
fn.call(this, $document.elements(), done);
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* JSTestDriver adapter for angular scenario tests
|
||||
*
|
||||
* Example of jsTestDriver.conf for running scenario tests with JSTD:
|
||||
<pre>
|
||||
server: http://localhost:9877
|
||||
|
||||
load:
|
||||
- lib/angular-scenario.js
|
||||
- lib/jstd-scenario-adapter-config.js
|
||||
- lib/jstd-scenario-adapter.js
|
||||
# your test files go here #
|
||||
|
||||
proxy:
|
||||
- {matcher: "/your-prefix/*", server: "http://localhost:8000/"}
|
||||
</pre>
|
||||
*
|
||||
* For more information on how to configure jstd proxy, see {@link http://code.google.com/p/js-test-driver/wiki/Proxy}
|
||||
* Note the order of files - it's important !
|
||||
*
|
||||
* Example of jstd-scenario-adapter-config.js
|
||||
<pre>
|
||||
var jstdScenarioAdapter = {
|
||||
relativeUrlPrefix: '/your-prefix/'
|
||||
};
|
||||
</pre>
|
||||
*
|
||||
* Whenever you use <code>browser().navigateTo('relativeUrl')</code> in your scenario test, the relativeUrlPrefix will be prepended.
|
||||
* You have to configure this to work together with JSTD proxy.
|
||||
*
|
||||
* Let's assume you are using the above configuration (jsTestDriver.conf and jstd-scenario-adapter-config.js):
|
||||
* Now, when you call <code>browser().navigateTo('index.html')</code> in your scenario test, the browser will open /your-prefix/index.html.
|
||||
* That matches the proxy, so JSTD will proxy this request to http://localhost:8000/index.html.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Custom type of test case
|
||||
*
|
||||
* @const
|
||||
* @see jstestdriver.TestCaseInfo
|
||||
*/
|
||||
var SCENARIO_TYPE = 'scenario';
|
||||
|
||||
/**
|
||||
* Plugin for JSTestDriver
|
||||
* Connection point between scenario's jstd output and jstestdriver.
|
||||
*
|
||||
* @see jstestdriver.PluginRegistrar
|
||||
*/
|
||||
function JstdPlugin() {
|
||||
var nop = function() {};
|
||||
|
||||
this.reportResult = nop;
|
||||
this.reportEnd = nop;
|
||||
this.runScenario = nop;
|
||||
|
||||
this.name = 'Angular Scenario Adapter';
|
||||
|
||||
/**
|
||||
* Called for each JSTD TestCase
|
||||
*
|
||||
* Handles only SCENARIO_TYPE test cases. There should be only one fake TestCase.
|
||||
* Runs all scenario tests (under one fake TestCase) and report all results to JSTD.
|
||||
*
|
||||
* @param {jstestdriver.TestRunConfiguration} configuration
|
||||
* @param {Function} onTestDone
|
||||
* @param {Function} onAllTestsComplete
|
||||
* @returns {boolean} True if this type of test is handled by this plugin, false otherwise
|
||||
*/
|
||||
this.runTestConfiguration = function(configuration, onTestDone, onAllTestsComplete) {
|
||||
if (configuration.getTestCaseInfo().getType() != SCENARIO_TYPE) return false;
|
||||
|
||||
this.reportResult = onTestDone;
|
||||
this.reportEnd = onAllTestsComplete;
|
||||
this.runScenario();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.getTestRunsConfigurationFor = function(testCaseInfos, expressions, testRunsConfiguration) {
|
||||
testRunsConfiguration.push(
|
||||
new jstestdriver.TestRunConfiguration(
|
||||
new jstestdriver.TestCaseInfo(
|
||||
'Angular Scenario Tests', function() {}, SCENARIO_TYPE), []));
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton instance of the plugin
|
||||
* Accessed using closure by:
|
||||
* - jstd output (reports to this plugin)
|
||||
* - initScenarioAdapter (register the plugin to jstd)
|
||||
*/
|
||||
var plugin = new JstdPlugin();
|
||||
|
||||
/**
|
||||
* Initialise scenario jstd-adapter
|
||||
* (only if jstestdriver is defined)
|
||||
*
|
||||
* @param {Object} jstestdriver Undefined when run from browser (without jstd)
|
||||
* @param {Function} initScenarioAndRun Function that inits scenario and runs all the tests
|
||||
* @param {Object=} config Configuration object, supported properties:
|
||||
* - relativeUrlPrefix: prefix for all relative links when navigateTo()
|
||||
*/
|
||||
function initScenarioAdapter(jstestdriver, initScenarioAndRun, config) {
|
||||
if (jstestdriver) {
|
||||
// create and register ScenarioPlugin
|
||||
jstestdriver.pluginRegistrar.register(plugin);
|
||||
plugin.runScenario = initScenarioAndRun;
|
||||
|
||||
/**
|
||||
* HACK (angular.scenario.Application.navigateTo)
|
||||
*
|
||||
* We need to navigate to relative urls when running from browser (without JSTD),
|
||||
* because we want to allow running scenario tests without creating its own virtual host.
|
||||
* For example: http://angular.local/build/docs/docs-scenario.html
|
||||
*
|
||||
* On the other hand, when running with JSTD, we need to navigate to absolute urls,
|
||||
* because of JSTD proxy. (proxy, because of same domain policy)
|
||||
*
|
||||
* So this hack is applied only if running with JSTD and change all relative urls to absolute.
|
||||
*/
|
||||
var appProto = angular.scenario.Application.prototype,
|
||||
navigateTo = appProto.navigateTo,
|
||||
relativeUrlPrefix = config && config.relativeUrlPrefix || '/';
|
||||
|
||||
appProto.navigateTo = function(url, loadFn, errorFn) {
|
||||
if (url.charAt(0) != '/' && url.charAt(0) != '#' &&
|
||||
url != 'about:blank' && !url.match(/^https?/)) {
|
||||
url = relativeUrlPrefix + url;
|
||||
}
|
||||
|
||||
return navigateTo.call(this, url, loadFn, errorFn);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds proper TestResult object from given model spec
|
||||
*
|
||||
* TODO(vojta) report error details
|
||||
*
|
||||
* @param {angular.scenario.ObjectModel.Spec} spec
|
||||
* @returns {jstestdriver.TestResult}
|
||||
*/
|
||||
function createTestResultFromSpec(spec) {
|
||||
var map = {
|
||||
success: 'PASSED',
|
||||
error: 'ERROR',
|
||||
failure: 'FAILED'
|
||||
};
|
||||
|
||||
return new jstestdriver.TestResult(
|
||||
spec.fullDefinitionName,
|
||||
spec.name,
|
||||
jstestdriver.TestResult.RESULT[map[spec.status]],
|
||||
spec.error || '',
|
||||
spec.line || '',
|
||||
spec.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates JSTD output (jstestdriver.TestResult)
|
||||
*/
|
||||
angular.scenario.output('jstd', function(context, runner, model) {
|
||||
model.on('SpecEnd', function(spec) {
|
||||
plugin.reportResult(createTestResultFromSpec(spec));
|
||||
});
|
||||
|
||||
model.on('RunnerEnd', function() {
|
||||
plugin.reportEnd();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window) {
|
||||
@@ -1,2 +0,0 @@
|
||||
initScenarioAdapter(window.jstestdriver, angular.scenario.setUpAndRun, window.jstdScenarioAdapter);
|
||||
})(window);
|
||||
+1
-1
@@ -175,7 +175,7 @@ describe('Binder', function() {
|
||||
$rootScope.error['throw'] = function() {throw 'MyError';};
|
||||
errorLogs.length = 0;
|
||||
$rootScope.$apply();
|
||||
expect(errorLogs.shift()).toBe('MyError');
|
||||
expect(errorLogs.shift().message).toBe('Error while interpolating: {{error.throw()}}\nMyError');
|
||||
|
||||
$rootScope.error['throw'] = function() {return 'ok';};
|
||||
$rootScope.$apply();
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
describe('injector', function() {
|
||||
var providers;
|
||||
var injector;
|
||||
var providerInjector;
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
beforeEach(module(function($provide, $injector) {
|
||||
providers = function(name, factory, annotations) {
|
||||
$provide.factory(name, extend(factory, annotations||{}));
|
||||
};
|
||||
providerInjector = $injector;
|
||||
}));
|
||||
beforeEach(inject(function($injector){
|
||||
injector = $injector;
|
||||
@@ -72,6 +74,11 @@ describe('injector', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should create a new $injector for the run phase', inject(function($injector) {
|
||||
expect($injector).not.toBe(providerInjector);
|
||||
}));
|
||||
|
||||
|
||||
describe('invoke', function() {
|
||||
var args;
|
||||
|
||||
@@ -549,26 +556,26 @@ describe('injector', function() {
|
||||
|
||||
|
||||
it('should decorate the missing service error with module name', function() {
|
||||
angular.module('TestModule', [], function($injector) {});
|
||||
angular.module('TestModule', [], function(xyzzy) {});
|
||||
expect(function() {
|
||||
createInjector(['TestModule']);
|
||||
}).toThrow('Unknown provider: $injector from TestModule');
|
||||
}).toThrow('Unknown provider: xyzzy from TestModule');
|
||||
});
|
||||
|
||||
|
||||
it('should decorate the missing service error with module function', function() {
|
||||
function myModule($injector){}
|
||||
function myModule(xyzzy){}
|
||||
expect(function() {
|
||||
createInjector([myModule]);
|
||||
}).toThrow('Unknown provider: $injector from ' + myModule);
|
||||
}).toThrow('Unknown provider: xyzzy from ' + myModule);
|
||||
});
|
||||
|
||||
|
||||
it('should decorate the missing service error with module array function', function() {
|
||||
function myModule($injector){}
|
||||
function myModule(xyzzy){}
|
||||
expect(function() {
|
||||
createInjector([['$injector', myModule]]);
|
||||
}).toThrow('Unknown provider: $injector from ' + myModule);
|
||||
createInjector([['xyzzy', myModule]]);
|
||||
}).toThrow('Unknown provider: xyzzy from ' + myModule);
|
||||
});
|
||||
|
||||
|
||||
|
||||
+6
-1
@@ -955,9 +955,14 @@ describe('jqLite', function() {
|
||||
expect(root.append('text')).toEqual(root);
|
||||
expect(root.html()).toEqual('text');
|
||||
});
|
||||
it('should not append anything if parent node is not of type element', function() {
|
||||
it('should append to document fragment', function() {
|
||||
var root = jqLite(document.createDocumentFragment());
|
||||
expect(root.append('<p>foo</p>')).toBe(root);
|
||||
expect(root.children().length).toBe(1);
|
||||
});
|
||||
it('should not append anything if parent node is not of type element or docfrag', function() {
|
||||
var root = jqLite('<p>some text node</p>').contents();
|
||||
expect(root.append('<p>foo</p>')).toBe(root);
|
||||
expect(root.children().length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,6 +104,12 @@ describe('$cacheFactory', function() {
|
||||
cache.remove(123);
|
||||
expect(cache.info().size).toBe(0);
|
||||
}));
|
||||
|
||||
|
||||
it("should return value from put", inject(function($cacheFactory) {
|
||||
var obj = {};
|
||||
expect(cache.put('k1', obj)).toBe(obj);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -74,6 +74,16 @@ describe('boolean attr directives', function() {
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('multiple')).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should bind open', inject(function($rootScope, $compile) {
|
||||
element = $compile('<details ng-open="isOpen"></details>')($rootScope)
|
||||
$rootScope.isOpen=false;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('open')).toBeFalsy();
|
||||
$rootScope.isOpen=true;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('open')).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -430,4 +430,110 @@ describe('form', function() {
|
||||
expect(doc).toBeDirty();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('$setPristine', function() {
|
||||
|
||||
it('should reset pristine state of form and controls', function() {
|
||||
|
||||
doc = $compile(
|
||||
'<form name="testForm">' +
|
||||
'<input ng-model="named1" name="foo">' +
|
||||
'<input ng-model="named2" name="bar">' +
|
||||
'</form>')(scope);
|
||||
|
||||
scope.$digest();
|
||||
|
||||
var form = doc,
|
||||
formCtrl = scope.testForm,
|
||||
input1 = form.find('input').eq(0),
|
||||
input1Ctrl = input1.controller('ngModel'),
|
||||
input2 = form.find('input').eq(1),
|
||||
input2Ctrl = input2.controller('ngModel');
|
||||
|
||||
input1Ctrl.$setViewValue('xx');
|
||||
input2Ctrl.$setViewValue('yy');
|
||||
scope.$apply();
|
||||
expect(form).toBeDirty();
|
||||
expect(input1).toBeDirty();
|
||||
expect(input2).toBeDirty();
|
||||
|
||||
formCtrl.$setPristine();
|
||||
expect(form).toBePristine();
|
||||
expect(formCtrl.$pristine).toBe(true);
|
||||
expect(formCtrl.$dirty).toBe(false);
|
||||
expect(input1).toBePristine();
|
||||
expect(input1Ctrl.$pristine).toBe(true);
|
||||
expect(input1Ctrl.$dirty).toBe(false);
|
||||
expect(input2).toBePristine();
|
||||
expect(input2Ctrl.$pristine).toBe(true);
|
||||
expect(input2Ctrl.$dirty).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
it('should reset pristine state of anonymous form controls', function() {
|
||||
|
||||
doc = $compile(
|
||||
'<form name="testForm">' +
|
||||
'<input ng-model="anonymous">' +
|
||||
'</form>')(scope);
|
||||
|
||||
scope.$digest();
|
||||
|
||||
var form = doc,
|
||||
formCtrl = scope.testForm,
|
||||
input = form.find('input').eq(0),
|
||||
inputCtrl = input.controller('ngModel');
|
||||
|
||||
inputCtrl.$setViewValue('xx');
|
||||
scope.$apply();
|
||||
expect(form).toBeDirty();
|
||||
expect(input).toBeDirty();
|
||||
|
||||
formCtrl.$setPristine();
|
||||
expect(form).toBePristine();
|
||||
expect(formCtrl.$pristine).toBe(true);
|
||||
expect(formCtrl.$dirty).toBe(false);
|
||||
expect(input).toBePristine();
|
||||
expect(inputCtrl.$pristine).toBe(true);
|
||||
expect(inputCtrl.$dirty).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
it('should reset pristine state of nested forms', function() {
|
||||
|
||||
doc = $compile(
|
||||
'<form name="testForm">' +
|
||||
'<div ng-form>' +
|
||||
'<input ng-model="named" name="foo">' +
|
||||
'</div>' +
|
||||
'</form>')(scope);
|
||||
|
||||
scope.$digest();
|
||||
|
||||
var form = doc,
|
||||
formCtrl = scope.testForm,
|
||||
nestedForm = form.find('div'),
|
||||
nestedFormCtrl = nestedForm.controller('form'),
|
||||
nestedInput = form.find('input').eq(0),
|
||||
nestedInputCtrl = nestedInput.controller('ngModel');
|
||||
|
||||
nestedInputCtrl.$setViewValue('xx');
|
||||
scope.$apply();
|
||||
expect(form).toBeDirty();
|
||||
expect(nestedForm).toBeDirty();
|
||||
expect(nestedInput).toBeDirty();
|
||||
|
||||
formCtrl.$setPristine();
|
||||
expect(form).toBePristine();
|
||||
expect(formCtrl.$pristine).toBe(true);
|
||||
expect(formCtrl.$dirty).toBe(false);
|
||||
expect(nestedForm).toBePristine();
|
||||
expect(nestedFormCtrl.$pristine).toBe(true);
|
||||
expect(nestedFormCtrl.$dirty).toBe(false);
|
||||
expect(nestedInput).toBePristine();
|
||||
expect(nestedInputCtrl.$pristine).toBe(true);
|
||||
expect(nestedInputCtrl.$dirty).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -117,6 +117,18 @@ describe('NgModelController', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setPristine', function() {
|
||||
|
||||
it('should set control to its pristine state', function() {
|
||||
ctrl.$setViewValue('edit');
|
||||
expect(ctrl.$dirty).toBe(true);
|
||||
expect(ctrl.$pristine).toBe(false);
|
||||
|
||||
ctrl.$setPristine();
|
||||
expect(ctrl.$dirty).toBe(false);
|
||||
expect(ctrl.$pristine).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('view -> model', function() {
|
||||
|
||||
@@ -382,6 +394,14 @@ describe('input', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should update the model and not trim the value', function() {
|
||||
compileInput('<input type="text" ng-model="name" name="alias" ng-trim="false" />');
|
||||
|
||||
changeInputValueTo(' a ');
|
||||
expect(scope.name).toEqual(' a ');
|
||||
});
|
||||
|
||||
|
||||
it('should allow complex reference binding', function() {
|
||||
compileInput('<input type="text" ng-model="obj[\'abc\'].name"/>');
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
describe('ngKeyup and ngKeydown directives', function() {
|
||||
var element;
|
||||
|
||||
afterEach(function() {
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
it('should get called on a keyup', inject(function($rootScope, $compile) {
|
||||
element = $compile('<input ng-keyup="touched = true">')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.touched).toBeFalsy();
|
||||
|
||||
browserTrigger(element, 'keyup');
|
||||
expect($rootScope.touched).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should get called on a keydown', inject(function($rootScope, $compile) {
|
||||
element = $compile('<input ng-keydown="touched = true">')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.touched).toBeFalsy();
|
||||
|
||||
browserTrigger(element, 'keydown');
|
||||
expect($rootScope.touched).toEqual(true);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
@@ -253,32 +253,37 @@ describe('filters', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should support various iso8061 date strings as input', function() {
|
||||
var format = 'yyyy-MM ss';
|
||||
it('should support various iso8061 date strings with timezone as input', function() {
|
||||
var format = 'yyyy-MM-dd ss';
|
||||
|
||||
//full ISO8061
|
||||
expect(date('2003-09-10T13:02:03.000Z', format)).toEqual('2003-09 03');
|
||||
expect(date('2003-09-10T13:02:03.000Z', format)).toEqual('2003-09-10 03');
|
||||
|
||||
expect(date('2003-09-10T13:02:03.000+00:00', format)).toEqual('2003-09 03');
|
||||
expect(date('2003-09-10T13:02:03.000+00:00', format)).toEqual('2003-09-10 03');
|
||||
|
||||
expect(date('2003-09-10T13:02:03-08:00', format)).toEqual('2003-09 03');
|
||||
|
||||
expect(date('20030910T033203-0930', format)).toEqual('2003-09 03');
|
||||
|
||||
//no timezone
|
||||
expect(date('2003-09-10T13:02:03.000', format)).toEqual('2003-09 03');
|
||||
expect(date('20030910T033203-0930', format)).toEqual('2003-09-10 03');
|
||||
|
||||
//no millis
|
||||
expect(date('2003-09-10T13:02:03Z', format)).toEqual('2003-09 03');
|
||||
expect(date('2003-09-10T13:02:03Z', format)).toEqual('2003-09-10 03');
|
||||
|
||||
//no seconds
|
||||
expect(date('2003-09-10T13:02Z', format)).toEqual('2003-09 00');
|
||||
expect(date('2003-09-10T13:02Z', format)).toEqual('2003-09-10 00');
|
||||
|
||||
//no minutes
|
||||
expect(date('2003-09-10T13Z', format)).toEqual('2003-09 00');
|
||||
expect(date('2003-09-10T13Z', format)).toEqual('2003-09-10 00');
|
||||
});
|
||||
|
||||
|
||||
it('should parse iso8061 date strings without timezone as local time', function() {
|
||||
var format = 'yyyy-MM-dd HH-mm-ss';
|
||||
|
||||
//full ISO8061 without timezone
|
||||
expect(date('2003-09-10T03:02:04.000', format)).toEqual('2003-09-10 03-02-04');
|
||||
|
||||
expect(date('20030910T030204', format)).toEqual('2003-09-10 03-02-04');
|
||||
|
||||
//no time
|
||||
expect(date('2003-09-10', format)).toEqual('2003-09 00');
|
||||
expect(date('2003-09-10', format)).toEqual('2003-09-10 00-00-00');
|
||||
});
|
||||
|
||||
it('should support different degrees of subsecond precision', function () {
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
describe('Filter: limitTo', function() {
|
||||
var items;
|
||||
var str
|
||||
var limitTo;
|
||||
|
||||
beforeEach(inject(function($filter) {
|
||||
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
|
||||
str = "tuvwxyz";
|
||||
limitTo = $filter('limitTo');
|
||||
}));
|
||||
|
||||
@@ -13,12 +15,16 @@ describe('Filter: limitTo', function() {
|
||||
it('should return the first X items when X is positive', function() {
|
||||
expect(limitTo(items, 3)).toEqual(['a', 'b', 'c']);
|
||||
expect(limitTo(items, '3')).toEqual(['a', 'b', 'c']);
|
||||
expect(limitTo(str, 3)).toEqual("tuv");
|
||||
expect(limitTo(str, '3')).toEqual("tuv");
|
||||
});
|
||||
|
||||
|
||||
it('should return the last X items when X is negative', function() {
|
||||
expect(limitTo(items, -3)).toEqual(['f', 'g', 'h']);
|
||||
expect(limitTo(items, '-3')).toEqual(['f', 'g', 'h']);
|
||||
expect(limitTo(str, -3)).toEqual("xyz");
|
||||
expect(limitTo(str, '-3')).toEqual("xyz");
|
||||
});
|
||||
|
||||
|
||||
@@ -30,11 +36,17 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, undefined)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty string when X cannot be parsed', function() {
|
||||
expect(limitTo(str, 'bogus')).toEqual("");
|
||||
expect(limitTo(str, 'null')).toEqual("");
|
||||
expect(limitTo(str, 'undefined')).toEqual("");
|
||||
expect(limitTo(str, null)).toEqual("");
|
||||
expect(limitTo(str, undefined)).toEqual("");
|
||||
});
|
||||
|
||||
it('should return an empty array when input is not Array type', function() {
|
||||
expect(limitTo('bogus', 1)).toEqual('bogus');
|
||||
expect(limitTo(null, 1)).toEqual(null);
|
||||
expect(limitTo(undefined, 1)).toEqual(undefined);
|
||||
|
||||
it('should return input if not String or Array', function() {
|
||||
expect(limitTo(1,1)).toEqual(1);
|
||||
expect(limitTo(null, 1)).toEqual(null);
|
||||
expect(limitTo(undefined, 1)).toEqual(undefined);
|
||||
expect(limitTo({}, 1)).toEqual({});
|
||||
@@ -42,11 +54,18 @@ describe('Filter: limitTo', function() {
|
||||
|
||||
|
||||
it('should return a copy of input array if X is exceeds array length', function () {
|
||||
expect(limitTo(items, 19)).toEqual(items);
|
||||
expect(limitTo(items, 9)).toEqual(items);
|
||||
expect(limitTo(items, '9')).toEqual(items);
|
||||
expect(limitTo(items, -9)).toEqual(items);
|
||||
expect(limitTo(items, '-9')).toEqual(items);
|
||||
|
||||
expect(limitTo(items, 9)).not.toBe(items);
|
||||
});
|
||||
|
||||
it('should return the entire string if X exceeds input length', function() {
|
||||
expect(limitTo(str, 9)).toEqual(str);
|
||||
expect(limitTo(str, '9')).toEqual(str);
|
||||
expect(limitTo(str, -9)).toEqual(str);
|
||||
expect(limitTo(str, '-9')).toEqual(str);
|
||||
})
|
||||
});
|
||||
|
||||
@@ -135,6 +135,24 @@ describe('$httpBackend', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should set responseType and return xhr.response', function() {
|
||||
$backend('GET', '/whatever', null, callback, {}, null, null, 'blob');
|
||||
|
||||
var xhrInstance = MockXhr.$$lastInstance;
|
||||
expect(xhrInstance.responseType).toBe('blob');
|
||||
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(response).toBe(xhrInstance.response);
|
||||
});
|
||||
|
||||
xhrInstance.response = {some: 'object'};
|
||||
xhrInstance.readyState = 4;
|
||||
xhrInstance.onreadystatechange();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
describe('JSONP', function() {
|
||||
|
||||
var SCRIPT_URL = /([^\?]*)\?cb=angular\.callbacks\.(.*)/;
|
||||
|
||||
+69
-10
@@ -147,6 +147,12 @@ describe('$http', function() {
|
||||
$httpBackend.expect('GET', '/url?a=1&b=%7B%22c%22%3A3%7D').respond('');
|
||||
$http({url: '/url', params: {a:1, b:{c:3}}, method: 'GET'});
|
||||
}));
|
||||
|
||||
|
||||
it('should expand arrays in params map', inject(function($httpBackend, $http) {
|
||||
$httpBackend.expect('GET', '/url?a=1&a=2&a=3').respond('');
|
||||
$http({url: '/url', params: {a: [1,2,3]}, method: 'GET'});
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -371,8 +377,7 @@ describe('$http', function() {
|
||||
|
||||
it('should set default headers for GET request', function() {
|
||||
$httpBackend.expect('GET', '/url', undefined, function(headers) {
|
||||
return headers['Accept'] == 'application/json, text/plain, */*' &&
|
||||
headers['X-Requested-With'] == 'XMLHttpRequest';
|
||||
return headers['Accept'] == 'application/json, text/plain, */*';
|
||||
}).respond('');
|
||||
|
||||
$http({url: '/url', method: 'GET', headers: {}});
|
||||
@@ -383,7 +388,6 @@ describe('$http', function() {
|
||||
it('should set default headers for POST request', function() {
|
||||
$httpBackend.expect('POST', '/url', 'messageBody', function(headers) {
|
||||
return headers['Accept'] == 'application/json, text/plain, */*' &&
|
||||
headers['X-Requested-With'] == 'XMLHttpRequest' &&
|
||||
headers['Content-Type'] == 'application/json;charset=utf-8';
|
||||
}).respond('');
|
||||
|
||||
@@ -395,7 +399,6 @@ describe('$http', function() {
|
||||
it('should set default headers for PUT request', function() {
|
||||
$httpBackend.expect('PUT', '/url', 'messageBody', function(headers) {
|
||||
return headers['Accept'] == 'application/json, text/plain, */*' &&
|
||||
headers['X-Requested-With'] == 'XMLHttpRequest' &&
|
||||
headers['Content-Type'] == 'application/json;charset=utf-8';
|
||||
}).respond('');
|
||||
|
||||
@@ -406,8 +409,7 @@ describe('$http', function() {
|
||||
|
||||
it('should set default headers for custom HTTP method', function() {
|
||||
$httpBackend.expect('FOO', '/url', undefined, function(headers) {
|
||||
return headers['Accept'] == 'application/json, text/plain, */*' &&
|
||||
headers['X-Requested-With'] == 'XMLHttpRequest';
|
||||
return headers['Accept'] == 'application/json, text/plain, */*';
|
||||
}).respond('');
|
||||
|
||||
$http({url: '/url', method: 'FOO', headers: {}});
|
||||
@@ -418,7 +420,6 @@ describe('$http', function() {
|
||||
it('should override default headers with custom', function() {
|
||||
$httpBackend.expect('POST', '/url', 'messageBody', function(headers) {
|
||||
return headers['Accept'] == 'Rewritten' &&
|
||||
headers['X-Requested-With'] == 'XMLHttpRequest' &&
|
||||
headers['Content-Type'] == 'Rewritten';
|
||||
}).respond('');
|
||||
|
||||
@@ -429,6 +430,17 @@ describe('$http', function() {
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should not set XSRF cookie for cross-domain requests', inject(function($browser) {
|
||||
$browser.cookies('XSRF-TOKEN', 'secret');
|
||||
$browser.url('http://host.com/base');
|
||||
$httpBackend.expect('GET', 'http://www.test.com/url', undefined, function(headers) {
|
||||
return headers['X-XSRF-TOKEN'] === undefined;
|
||||
}).respond('');
|
||||
|
||||
$http({url: 'http://www.test.com/url', method: 'GET', headers: {}});
|
||||
$httpBackend.flush();
|
||||
}));
|
||||
|
||||
|
||||
it('should not send Content-Type header if request data/body is undefined', function() {
|
||||
$httpBackend.expect('POST', '/url', undefined, function(headers) {
|
||||
@@ -959,12 +971,35 @@ describe('$http', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should pass timeout and withCredentials', function() {
|
||||
it('should pass timeout, withCredentials and responseType', function() {
|
||||
var $httpBackend = jasmine.createSpy('$httpBackend');
|
||||
|
||||
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials) {
|
||||
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
|
||||
expect(timeout).toBe(12345);
|
||||
expect(withCredentials).toBe(true);
|
||||
expect(responseType).toBe('json');
|
||||
});
|
||||
|
||||
module(function($provide) {
|
||||
$provide.value('$httpBackend', $httpBackend);
|
||||
});
|
||||
|
||||
inject(function($http) {
|
||||
$http({
|
||||
method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true, responseType: 'json'
|
||||
});
|
||||
expect($httpBackend).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
$httpBackend.verifyNoOutstandingExpectation = noop;
|
||||
});
|
||||
|
||||
|
||||
it('should use withCredentials from default', function() {
|
||||
var $httpBackend = jasmine.createSpy('$httpBackend');
|
||||
|
||||
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
|
||||
expect(withCredentials).toBe(true);
|
||||
});
|
||||
|
||||
module(function($provide) {
|
||||
@@ -972,10 +1007,34 @@ describe('$http', function() {
|
||||
});
|
||||
|
||||
inject(function($http) {
|
||||
$http({method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true});
|
||||
$http.defaults.withCredentials = true;
|
||||
$http({
|
||||
method: 'GET', url: 'some.html', timeout: 12345, responseType: 'json'
|
||||
});
|
||||
expect($httpBackend).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
$httpBackend.verifyNoOutstandingExpectation = noop;
|
||||
});
|
||||
|
||||
describe('isSameDomain', function() {
|
||||
it('should support various combinations of urls', function() {
|
||||
expect(isSameDomain('path/morepath',
|
||||
'http://www.adomain.com')).toBe(true);
|
||||
expect(isSameDomain('http://www.adomain.com/path',
|
||||
'http://www.adomain.com')).toBe(true);
|
||||
expect(isSameDomain('//www.adomain.com/path',
|
||||
'http://www.adomain.com')).toBe(true);
|
||||
expect(isSameDomain('//www.adomain.com/path',
|
||||
'https://www.adomain.com')).toBe(true);
|
||||
expect(isSameDomain('//www.adomain.com/path',
|
||||
'http://www.adomain.com:1234')).toBe(false);
|
||||
expect(isSameDomain('https://www.adomain.com/path',
|
||||
'http://www.adomain.com')).toBe(false);
|
||||
expect(isSameDomain('http://www.adomain.com:1234/path',
|
||||
'http://www.adomain.com')).toBe(false);
|
||||
expect(isSameDomain('http://www.anotherdomain.com/path',
|
||||
'http://www.adomain.com')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,29 @@ describe('$interpolate', function() {
|
||||
expect($interpolate('{{ false }}')()).toEqual('false');
|
||||
}));
|
||||
|
||||
it('should rethrow exceptions', inject(function($interpolate, $rootScope) {
|
||||
$rootScope.err = function () {
|
||||
throw new Error('oops');
|
||||
};
|
||||
expect(function () {
|
||||
$interpolate('{{err()}}')($rootScope);
|
||||
}).toThrow('Error while interpolating: {{err()}}\nError: oops');
|
||||
}));
|
||||
|
||||
it('should stop interpolation when encountering an exception', inject(function($interpolate, $compile, $rootScope) {
|
||||
$rootScope.err = function () {
|
||||
throw new Error('oops');
|
||||
};
|
||||
var dom = jqLite('<div>{{1 + 1}}</div><div>{{err()}}</div><div>{{1 + 2}}</div>');
|
||||
$compile(dom)($rootScope);
|
||||
expect(function () {
|
||||
$rootScope.$apply();
|
||||
}).toThrow('Error while interpolating: {{err()}}\nError: oops');
|
||||
expect(dom[0].innerHTML).toEqual('2');
|
||||
expect(dom[1].innerHTML).toEqual('{{err()}}');
|
||||
expect(dom[2].innerHTML).toEqual('{{1 + 2}}');
|
||||
}));
|
||||
|
||||
|
||||
it('should return interpolation function', inject(function($interpolate, $rootScope) {
|
||||
$rootScope.name = 'Misko';
|
||||
|
||||
+45
-8
@@ -1,17 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
function initService(debugEnabled) {
|
||||
return module(function($logProvider){
|
||||
$logProvider.debugEnabled(debugEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
describe('$log', function() {
|
||||
var $window, logger, log, warn, info, error;
|
||||
var $window, logger, log, warn, info, error, debug;
|
||||
|
||||
|
||||
|
||||
beforeEach(module(function($provide){
|
||||
$window = {navigator: {}};
|
||||
$window = {navigator: {}, document: {}};
|
||||
logger = '';
|
||||
log = function() { logger+= 'log;'; };
|
||||
warn = function() { logger+= 'warn;'; };
|
||||
info = function() { logger+= 'info;'; };
|
||||
error = function() { logger+= 'error;'; };
|
||||
debug = function() { logger+= 'debug;'; };
|
||||
|
||||
$provide.provider('$log', $LogProvider);
|
||||
$provide.value('$exceptionHandler', angular.mock.rethrow);
|
||||
@@ -23,14 +30,16 @@ describe('$log', function() {
|
||||
$window.console = {log: log,
|
||||
warn: warn,
|
||||
info: info,
|
||||
error: error};
|
||||
error: error,
|
||||
debug: debug};
|
||||
},
|
||||
function($log) {
|
||||
$log.log();
|
||||
$log.warn();
|
||||
$log.info();
|
||||
$log.error();
|
||||
expect(logger).toEqual('log;warn;info;error;');
|
||||
$log.debug();
|
||||
expect(logger).toEqual('log;warn;info;error;debug;');
|
||||
}
|
||||
));
|
||||
|
||||
@@ -44,7 +53,8 @@ describe('$log', function() {
|
||||
$log.warn();
|
||||
$log.info();
|
||||
$log.error();
|
||||
expect(logger).toEqual('log;log;log;log;');
|
||||
$log.debug();
|
||||
expect(logger).toEqual('log;log;log;log;log;');
|
||||
}
|
||||
));
|
||||
|
||||
@@ -55,6 +65,7 @@ describe('$log', function() {
|
||||
$log.warn();
|
||||
$log.info();
|
||||
$log.error();
|
||||
$log.debug();
|
||||
}
|
||||
));
|
||||
|
||||
@@ -64,22 +75,48 @@ describe('$log', function() {
|
||||
log.apply = log.call =
|
||||
warn.apply = warn.call =
|
||||
info.apply = info.call =
|
||||
error.apply = error.call = null;
|
||||
error.apply = error.call =
|
||||
debug.apply = debug.call = null;
|
||||
|
||||
$window.console = {log: log,
|
||||
warn: warn,
|
||||
info: info,
|
||||
error: error};
|
||||
error: error,
|
||||
debug: debug};
|
||||
},
|
||||
function($log) {
|
||||
$log.log.apply($log);
|
||||
$log.warn.apply($log);
|
||||
$log.info.apply($log);
|
||||
$log.error.apply($log);
|
||||
expect(logger).toEqual('log;warn;info;error;');
|
||||
$log.debug.apply($log);
|
||||
expect(logger).toEqual('log;warn;info;error;debug;');
|
||||
})
|
||||
);
|
||||
|
||||
describe("$log.debug", function () {
|
||||
|
||||
beforeEach(initService(false));
|
||||
|
||||
it("should skip debugging output if disabled", inject(
|
||||
function(){
|
||||
$window.console = {log: log,
|
||||
warn: warn,
|
||||
info: info,
|
||||
error: error,
|
||||
debug: debug};
|
||||
},
|
||||
function($log) {
|
||||
$log.log();
|
||||
$log.warn();
|
||||
$log.info();
|
||||
$log.error();
|
||||
$log.debug();
|
||||
expect(logger).toEqual('log;warn;info;error;');
|
||||
}
|
||||
));
|
||||
|
||||
});
|
||||
|
||||
describe('$log.error', function() {
|
||||
var e, $log, errorArgs;
|
||||
|
||||
+13
-3
@@ -91,8 +91,8 @@ describe('parser', function() {
|
||||
expect(tokens[1].text).toEqual('b');
|
||||
});
|
||||
|
||||
it('should tokenize relation', function() {
|
||||
var tokens = lex("! == != < > <= >=");
|
||||
it('should tokenize relation and equality', function() {
|
||||
var tokens = lex("! == != < > <= >= === !==");
|
||||
expect(tokens[0].text).toEqual('!');
|
||||
expect(tokens[1].text).toEqual('==');
|
||||
expect(tokens[2].text).toEqual('!=');
|
||||
@@ -100,6 +100,8 @@ describe('parser', function() {
|
||||
expect(tokens[4].text).toEqual('>');
|
||||
expect(tokens[5].text).toEqual('<=');
|
||||
expect(tokens[6].text).toEqual('>=');
|
||||
expect(tokens[7].text).toEqual('===');
|
||||
expect(tokens[8].text).toEqual('!==');
|
||||
});
|
||||
|
||||
it('should tokenize statements', function() {
|
||||
@@ -197,12 +199,20 @@ describe('parser', function() {
|
||||
expect(scope.$eval("false")).toBeFalsy();
|
||||
expect(scope.$eval("!true")).toBeFalsy();
|
||||
expect(scope.$eval("1==1")).toBeTruthy();
|
||||
expect(scope.$eval("1==true")).toBeTruthy();
|
||||
expect(scope.$eval("1===1")).toBeTruthy();
|
||||
expect(scope.$eval("1==='1'")).toBeFalsy();
|
||||
expect(scope.$eval("1===true")).toBeFalsy();
|
||||
expect(scope.$eval("'true'===true")).toBeFalsy();
|
||||
expect(scope.$eval("1!==2")).toBeTruthy();
|
||||
expect(scope.$eval("1!=='1'")).toBeTruthy();
|
||||
expect(scope.$eval("1!=2")).toBeTruthy();
|
||||
expect(scope.$eval("1<2")).toBeTruthy();
|
||||
expect(scope.$eval("1<=1")).toBeTruthy();
|
||||
expect(scope.$eval("1>2")).toEqual(1>2);
|
||||
expect(scope.$eval("2>=1")).toEqual(2>=1);
|
||||
expect(scope.$eval("true==2<3")).toEqual(true === 2<3);
|
||||
expect(scope.$eval("true==2<3")).toEqual(true == 2<3);
|
||||
expect(scope.$eval("true===2<3")).toEqual(true === 2<3);
|
||||
});
|
||||
|
||||
it('should parse logical', function() {
|
||||
|
||||
@@ -457,7 +457,7 @@ describe('Scope', function() {
|
||||
child.$evalAsync(function(scope) { log += 'child.async;'; });
|
||||
child.$watch('value', function() { log += 'child.$digest;'; });
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual('parent.async;parent.$digest;child.async;child.$digest;');
|
||||
expect(log).toEqual('parent.async;child.async;parent.$digest;child.$digest;');
|
||||
}));
|
||||
|
||||
it('should cause a $digest rerun', inject(function($rootScope) {
|
||||
|
||||
@@ -715,6 +715,53 @@ describe('$route', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should allow using a function as a template', function() {
|
||||
var customTemplateWatcher = jasmine.createSpy('customTemplateWatcher');
|
||||
|
||||
function customTemplateFn(routePathParams) {
|
||||
customTemplateWatcher(routePathParams);
|
||||
expect(routePathParams).toEqual({id: 'id3'});
|
||||
return '<h1>' + routePathParams.id + '</h1>';
|
||||
}
|
||||
|
||||
module(function($routeProvider){
|
||||
$routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
$routeProvider.when('/foo/:id', {template: customTemplateFn});
|
||||
});
|
||||
|
||||
inject(function($route, $location, $rootScope) {
|
||||
$location.path('/foo/id3');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(customTemplateWatcher).toHaveBeenCalledWith({id: 'id3'});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should allow using a function as a templateUrl', function() {
|
||||
var customTemplateUrlWatcher = jasmine.createSpy('customTemplateUrlWatcher');
|
||||
|
||||
function customTemplateUrlFn(routePathParams) {
|
||||
customTemplateUrlWatcher(routePathParams);
|
||||
expect(routePathParams).toEqual({id: 'id3'});
|
||||
return 'foo.html';
|
||||
}
|
||||
|
||||
module(function($routeProvider){
|
||||
$routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
$routeProvider.when('/foo/:id', {templateUrl: customTemplateUrlFn});
|
||||
});
|
||||
|
||||
inject(function($route, $location, $rootScope) {
|
||||
$location.path('/foo/id3');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(customTemplateUrlWatcher).toHaveBeenCalledWith({id: 'id3'});
|
||||
expect($route.current.loadedTemplateUrl).toEqual('foo.html');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('reload', function() {
|
||||
|
||||
it('should reload even if reloadOnSearch is false', function() {
|
||||
|
||||
+24
-6
@@ -2,9 +2,10 @@
|
||||
|
||||
describe('$sniffer', function() {
|
||||
|
||||
function sniffer($window) {
|
||||
function sniffer($window, $document) {
|
||||
$window.navigator = {};
|
||||
return new $SnifferProvider().$get[1]($window);
|
||||
$document = jqLite($document || {});
|
||||
return new $SnifferProvider().$get[2]($window, $document);
|
||||
}
|
||||
|
||||
describe('history', function() {
|
||||
@@ -20,15 +21,15 @@ describe('$sniffer', function() {
|
||||
|
||||
describe('hashchange', function() {
|
||||
it('should be true if onhashchange property defined', function() {
|
||||
expect(sniffer({onhashchange: true, document: {}}).hashchange).toBe(true);
|
||||
expect(sniffer({onhashchange: true}, {}).hashchange).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false if onhashchange property not defined', function() {
|
||||
expect(sniffer({document: {}}).hashchange).toBe(false);
|
||||
expect(sniffer({}, {}).hashchange).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false if documentMode is 7 (IE8 comp mode)', function() {
|
||||
expect(sniffer({onhashchange: true, document: {documentMode: 7}}).hashchange).toBe(false);
|
||||
expect(sniffer({onhashchange: true}, {documentMode: 7}).hashchange).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +43,7 @@ describe('$sniffer', function() {
|
||||
if (elm === 'div') return mockDivElement;
|
||||
});
|
||||
|
||||
$sniffer = sniffer({document: mockDocument});
|
||||
$sniffer = sniffer({}, mockDocument);
|
||||
});
|
||||
|
||||
|
||||
@@ -78,4 +79,21 @@ describe('$sniffer', function() {
|
||||
expect($sniffer.hasEvent('input')).toBe((msie == 9) ? false : true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('csp', function() {
|
||||
it('should be false if document.securityPolicy.isActive not available', function() {
|
||||
expect(sniffer({}, {}).csp).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
it('should use document.securityPolicy.isActive if available', function() {
|
||||
var createDocumentWithCSP = function(csp) {
|
||||
return {securityPolicy: {isActive: csp}};
|
||||
};
|
||||
|
||||
expect(sniffer({}, createDocumentWithCSP(false)).csp).toBe(false);
|
||||
expect(sniffer({}, createDocumentWithCSP(true)).csp).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+16
@@ -327,6 +327,22 @@ describe('ngMock', function() {
|
||||
$timeout.flush();
|
||||
expect(logger).toEqual(['t1', 't3', 't2']);
|
||||
}));
|
||||
|
||||
|
||||
it('should throw an exception when not flushed', inject(function($timeout){
|
||||
$timeout(noop);
|
||||
|
||||
var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}';
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(expectedError);
|
||||
}));
|
||||
|
||||
|
||||
it('should do nothing when all tasks have been flushed', inject(function($timeout) {
|
||||
$timeout(noop);
|
||||
|
||||
$timeout.flush();
|
||||
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,14 @@ describe("resource", function() {
|
||||
},
|
||||
patch: {
|
||||
method: 'PATCH'
|
||||
},
|
||||
conditionalPut: {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'If-None-Match': '*'
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
callback = jasmine.createSpy();
|
||||
}));
|
||||
@@ -213,6 +220,15 @@ describe("resource", function() {
|
||||
});
|
||||
|
||||
|
||||
it('should send correct headers', function() {
|
||||
$httpBackend.expectPUT('/CreditCard/123', undefined, function(headers) {
|
||||
return headers['If-None-Match'] == "*";
|
||||
}).respond({id:123});
|
||||
|
||||
CreditCard.conditionalPut({id: {key:123}});
|
||||
});
|
||||
|
||||
|
||||
it("should read partial resource", function() {
|
||||
$httpBackend.expect('GET', '/CreditCard').respond([{id:{key:123}}]);
|
||||
var ccs = CreditCard.query();
|
||||
@@ -365,6 +381,35 @@ describe("resource", function() {
|
||||
});
|
||||
|
||||
|
||||
it('should support dynamic default parameters (global)', function() {
|
||||
var currentGroup = 'students',
|
||||
Person = $resource('/Person/:group/:id', { group: function() { return currentGroup; }});
|
||||
|
||||
|
||||
$httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'});
|
||||
|
||||
var fedor = Person.get({id: 'fedor'});
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com'});
|
||||
});
|
||||
|
||||
|
||||
it('should support dynamic default parameters (action specific)', function() {
|
||||
var currentGroup = 'students',
|
||||
Person = $resource('/Person/:group/:id', {}, {
|
||||
fetch: {method: 'GET', params: {group: function() { return currentGroup; }}}
|
||||
});
|
||||
|
||||
$httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'});
|
||||
|
||||
var fedor = Person.fetch({id: 'fedor'});
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com'});
|
||||
});
|
||||
|
||||
|
||||
it('should exercise full stack', function() {
|
||||
var Person = $resource('/Person/:id');
|
||||
|
||||
@@ -407,4 +452,26 @@ describe("resource", function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should transform request/response', function() {
|
||||
var Person = $resource('/Person/:id', {}, {
|
||||
save: {
|
||||
method: 'POST',
|
||||
params: {id: '@id'},
|
||||
transformRequest: function(data) {
|
||||
return angular.toJson({ __id: data.id });
|
||||
},
|
||||
transformResponse: function(data) {
|
||||
return { id: data.__id };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$httpBackend.expect('POST', '/Person/123', { __id: 123 }).respond({ __id: 456 });
|
||||
var person = new Person({id:123});
|
||||
person.$save();
|
||||
$httpBackend.flush();
|
||||
expect(person.id).toEqual(456);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,4 +24,11 @@ describe('linky', function() {
|
||||
expect(linky("send email to me@example.com, but")).
|
||||
toEqual('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
|
||||
});
|
||||
|
||||
it('should handle target:', function() {
|
||||
expect(linky("http://example.com", "_blank")).
|
||||
toEqual('<a target="_blank" href="http://example.com">http://example.com</a>')
|
||||
expect(linky("http://example.com", "someNamedIFrame")).
|
||||
toEqual('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>')
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,7 +188,7 @@ describe('HTML', function() {
|
||||
expect(html).toEqual('<div>');
|
||||
});
|
||||
|
||||
describe('explicitly dissallow', function() {
|
||||
describe('explicitly disallow', function() {
|
||||
it('should not allow attributes', function() {
|
||||
writer.start('div', {id:'a', name:'a', style:'a'});
|
||||
expect(html).toEqual('<div>');
|
||||
@@ -230,10 +230,11 @@ describe('HTML', function() {
|
||||
expect(isUri('https://abc')).toBeTruthy();
|
||||
expect(isUri('ftp://abc')).toBeTruthy();
|
||||
expect(isUri('mailto:me@example.com')).toBeTruthy();
|
||||
expect(isUri('tel:123-123-1234')).toBeTruthy();
|
||||
expect(isUri('#anchor')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not be UIR', function() {
|
||||
it('should not be URI', function() {
|
||||
expect(isUri('')).toBeFalsy();
|
||||
expect(isUri('javascript:alert')).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -270,6 +270,16 @@ describe("angular.scenario.dsl", function() {
|
||||
expect($root.futureError).toMatch(/did not match/);
|
||||
});
|
||||
|
||||
it('should fail to select an option that does not exist', function(){
|
||||
doc.append(
|
||||
'<select ng-model="test">' +
|
||||
' <option value=A>one</option>' +
|
||||
' <option value=B selected>two</option>' +
|
||||
'</select>'
|
||||
);
|
||||
$root.dsl.select('test').option('three');
|
||||
expect($root.futureError).toMatch(/not found/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Element', function() {
|
||||
@@ -305,6 +315,58 @@ describe("angular.scenario.dsl", function() {
|
||||
dealoc(elm);
|
||||
});
|
||||
|
||||
it('should execute dblclick', function() {
|
||||
var clicked;
|
||||
// Hash is important, otherwise we actually
|
||||
// go to a different page and break the runner
|
||||
doc.append('<a href="#"></a>');
|
||||
doc.find('a').dblclick(function() {
|
||||
clicked = true;
|
||||
});
|
||||
$root.dsl.element('a').dblclick();
|
||||
});
|
||||
|
||||
it('should navigate page if dblclick on anchor', function() {
|
||||
expect($window.location).not.toEqual('#foo');
|
||||
doc.append('<a href="#foo"></a>');
|
||||
$root.dsl.element('a').dblclick();
|
||||
expect($window.location).toMatch(/#foo$/);
|
||||
});
|
||||
|
||||
it('should not navigate if dblclick event was cancelled', function() {
|
||||
var initLocation = $window.location,
|
||||
elm = jqLite('<a href="#foo"></a>');
|
||||
|
||||
doc.append(elm);
|
||||
elm.bind('dblclick', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$root.dsl.element('a').dblclick();
|
||||
expect($window.location).toBe(initLocation);
|
||||
dealoc(elm);
|
||||
});
|
||||
|
||||
it('should execute mouseover', function() {
|
||||
var mousedOver;
|
||||
doc.append('<div></div>');
|
||||
doc.find('div').mouseover(function() {
|
||||
mousedOver = true;
|
||||
});
|
||||
$root.dsl.element('div').mouseover();
|
||||
expect(mousedOver).toBe(true);
|
||||
});
|
||||
|
||||
it('should bubble up the mouseover event', function() {
|
||||
var mousedOver;
|
||||
doc.append('<div id="outer"><div id="inner"></div></div>');
|
||||
doc.find('#outer').mouseover(function() {
|
||||
mousedOver = true;
|
||||
});
|
||||
$root.dsl.element('#inner').mouseover();
|
||||
expect(mousedOver).toBe(true);
|
||||
});
|
||||
|
||||
it('should count matching elements', function() {
|
||||
doc.append('<span></span><span></span>');
|
||||
$root.dsl.element('span').count();
|
||||
|
||||
@@ -13,7 +13,7 @@ var util = require('util');
|
||||
|
||||
|
||||
var MAX_LENGTH = 70;
|
||||
var PATTERN = /^(\w*)(\(([\w\$\.\-\*/]*)\))?\: (.*)$/;
|
||||
var PATTERN = /^(?:fixup!\s*)?(\w*)(\(([\w\$\.\-\*/]*)\))?\: (.*)$/;
|
||||
var IGNORED = /^WIP\:/;
|
||||
var TYPES = {
|
||||
feat: true,
|
||||
@@ -51,7 +51,7 @@ var validateMessage = function(message) {
|
||||
var match = PATTERN.exec(message);
|
||||
|
||||
if (!match) {
|
||||
error('does not match "<type>(<scope>): <subject>" !');
|
||||
error('does not match "<type>(<scope>): <subject>" ! was: ' + message);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ describe('validate-commit-msg.js', function() {
|
||||
describe('validateMessage', function() {
|
||||
|
||||
it('should be valid', function() {
|
||||
expect(m.validateMessage('fixup! fix($compile): something')).toBe(VALID);
|
||||
expect(m.validateMessage('fix($compile): something')).toBe(VALID);
|
||||
expect(m.validateMessage('feat($location): something')).toBe(VALID);
|
||||
expect(m.validateMessage('docs($filter): something')).toBe(VALID);
|
||||
@@ -50,7 +51,7 @@ describe('validate-commit-msg.js', function() {
|
||||
var msg = 'not correct format';
|
||||
|
||||
expect(m.validateMessage(msg)).toBe(INVALID);
|
||||
expect(errors).toEqual(['INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" !']);
|
||||
expect(errors).toEqual(['INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: not correct format']);
|
||||
});
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# AngularJS build config file
|
||||
---
|
||||
version: 1.0.4
|
||||
codename: bewildering-hair
|
||||
version: 1.1.2
|
||||
codename: tofu-animation
|
||||
stable: 1.0.3
|
||||
|
||||
Reference in New Issue
Block a user