Compare commits

...

41 Commits

Author SHA1 Message Date
Peter Bacon Darwin b879223cf3 fix(input): ensure that hidden input values are correct after history.back
Due to the nature of some browser's PageCache/BFCache, returning to an Angular
app sometimes causes `input[hidden]` elements to retain the last value
that was stored before the page was navigated away from previously.

This is particularly problematic if the input has an interpolated value.
E.g. `<input type="hidden" value="{{ 1 + 2 }}">` since when the browser
returns, instead of the original interpolation template, the HTML contains
the previous value `<input type="hidden" value="3">`.

This commit instructs the browser not to attempt to reinstate the previous
value when navigating back in history by setting `autocomplete="off"` on
the hidden input element element.
2016-10-10 22:22:30 +01:00
Georgios Kalpakas bba6004147 docs(ngModel): rename $asyncValidators error to nopromise and add missing error page
Closes #13795
2016-01-19 16:11:44 +02:00
Matias Niemelä d386b7ae4b fix(ngAnimate): do not use event.timeStamp anymore for time tracking
Due to recent changes in Chrome, Firefox and Webkit use of the
event.timeStamp value will lead to unpredictable behaviour due to
precision changes. Therefore it's best to stick entirely to use
`Date.now()` when it comes to confirming the end of transition-
ending values. See #13494 for more info.

Applies to 1.2, 1.3, 1.4 and 1.5.

Closes #13494
Closes #13495
2016-01-05 12:55:16 +00:00
Peter Bacon Darwin ebc5a477db chore(npm-shrinkwrap): update protractor dependency 2015-12-18 09:38:34 +00:00
Peter Bacon Darwin 21c935a303 chore(Gruntfile): replace double quotes with single quotes 2015-12-17 22:29:29 +00:00
Peter Bacon Darwin 7e5ef766da chore(GruntFile): fix whitespace in lists 2015-12-17 22:29:29 +00:00
Peter Bacon Darwin 1cb96303e9 chore(GruntFile): move validate-angular-files task into its own file
Closes #13569
2015-12-17 22:29:28 +00:00
Matias Niemelä d2058d92bd chore(build): add a validation step for angularFiles
Closes #13553
2015-12-17 22:29:28 +00:00
Peter Bacon Darwin 053f4e26ae chore(angularFiles): add documentation only file to list of files
This prevents errors when checking `validate-angular-files`
2015-12-17 22:29:28 +00:00
Peter Bacon Darwin 45ca45a29a chore(npm-shrinkwrap): install glob package 2015-12-17 22:29:25 +00:00
Peter Bacon Darwin e2bf5726ab chore(jenkins): remove unused argument definition 2015-12-17 14:15:38 +00:00
Peter Bacon Darwin bf706f2f52 chore(jenkins): run Jenkins builds on Node 4 (via nvm)
Closes #13568
2015-12-17 14:11:09 +00:00
Peter Bacon Darwin d2fe381ea8 chore(jenkins): move jenkins_build.sh to scripts/jenkins/build.sh 2015-12-16 14:23:12 +00:00
Peter Bacon Darwin 63c83ffa43 chore(travis): update to use node 4.x 2015-12-16 11:15:48 +00:00
Martin Staffa 7fa13c3da2 chore(travis): add a new job that runs ci-checks
Previously, ddescribe, merge-conflicts, jshint, and jscs would run
after unit & e2e tests ran. The order was orginally changed as part of
https://github.com/angular/angular.js/pull/9792.

While the logic is sound that style errors shouldn't block tests from
running, ddescribe should always run. This was not guaraneteed; when
Travis exits with a warning after some browsers have run, ddescribe
doesn't get run and it doesn't become apparent that not
all tests have run.

Additionally, a separate job clearly separates style from test errors,
which e.g. means you can open a PR that includes an iit to speed up
the job, and see immediately if the test passes, because the ddescribe
error is in another job.
2015-12-16 10:38:12 +00:00
Georgios Kalpakas 4b91042559 docs(form): remove mention of interpolated control names not being supported
The docs state that interpolation cannot be used in control names.
This used to be true, but not anymore.

Closes #13520
2015-12-15 20:05:18 +02:00
Jason Bedard a9ecde1e33 perf(copy): avoid regex in isTypedArray
Closes: #12054
2015-11-19 10:00:37 +00:00
Jason Bedard ada25a9266 perf(copy): only validate/clear user specified destination
Closes #12068
2015-11-19 10:00:22 +00:00
Peter Bacon Darwin 0f956b2893 fix(angular.copy): support copying XML nodes
Closes #5429
Closes #12786
2015-11-19 09:59:44 +00:00
Jason Bedard 17eb3d717f fix(copy): do not copy the same object twice 2015-11-19 09:59:19 +00:00
Peter Bacon Darwin c9ccc801b9 fix(copy): support copying properties with a null prototype
Partially cherry-picked from f7b999703f
2015-11-19 09:58:24 +00:00
Justin Schiff 420490aa1b fix(angular.copy): support copying %TypedArray%s
angular.copy can now copy a %TypedArray%s.

Limitations: It is not possible to update the length of a %TypedArray%, so currently an error is thrown
if the destination object is a %TypedArray%. However, it is possible to change values in a typed array,
so in the future this may only be a problem if the length of the source and destination is different.

Closes #10745
2015-11-19 09:46:07 +00:00
Matias Niemelä 2a65c3deb7 chore(CHANGELOG): update with changes for 1.3.20 2015-09-29 13:54:03 -07:00
Igor Minar ef0c333776 build(travis): make sauce connect process query a bit more specific 2015-09-23 14:01:51 -07:00
Georgios Kalpakas bce1d8ecad chore(check-node-modules): make check/reinstall node_modules work across platforms
The previous implementations (based on shell scripts) threw errors on
Windows, because it was not able to `rm -rf` 'node_modules' (due to the
255 character limit in file-paths).

This implementation works consistently across platforms and is heavily based on
'https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js'.

Fixes #11143
Closes #11353

Closes #12792
2015-09-23 23:25:04 +03:00
Igor Minar 5a1bc6eb32 build(travis): fix typo in a comment 2015-09-23 11:00:44 -07:00
Igor Minar 1f7a0b2b72 build(travis): gracefully shut down the sauce connect tunnel after the tests are done running
This is to prevent sauce connect tunnel leaks.

Closes #12921
2015-09-23 09:40:46 -07:00
Lucas Mirelmann a665550933 test($parse): fix test for Firefox and IE 2015-09-20 19:54:37 +02:00
Lucas Mirelmann d434f3db53 fix($parse): do not convert to string computed properties multiple times
Do not convert to string properties multiple times.
2015-09-20 13:39:33 +02:00
Peter Bacon Darwin 7e08975b67 chore(travis): upgrade Travis builds to use container infrastructure 2015-09-17 12:31:52 +01:00
Magee Mooney 82c481bb23 docs(gdocs.js): fix typo (Eror -> Error)
Closes #12858
2015-09-16 23:11:39 +01:00
Peter Bacon Darwin b1f46bb1b2 chore(bower/publish): move DIST_TAG so that it gets the correct value
In the position that DIST_TAG was being assigned it was trying to get the
`distTag` value from the wrong (i.e. a bower-...) repository.
2015-09-16 23:11:35 +01:00
Peter Bacon Darwin 3a6bf0d5bc revert: fix($compile): throw error on invalid directive name
This reverts commit 634e467172, which introduced
a breaking change between 1.3.15 and 1.3.16.

Closes #12169
2015-09-15 14:38:21 +01:00
Peter Bacon Darwin e201f9040f docs(CHANGELOG): update with 1.3.19 changes 2015-09-15 13:34:09 +01:00
Peter Bacon Darwin 40e9bcd1b4 chore(scripts/publish): get dist-tag from package.json
Closes #12722
2015-09-14 21:45:20 +01:00
Matias Niemelä f98e038418 feat(ngAnimate): introduce $animate.flush for unit testing 2015-09-14 13:08:57 -07:00
Lucas Galfaso ec98c94ccb fix($parse): throw error when accessing a restricted property indirectly
When accessing an instance thru a computed member and the property is an array,
then also check the string value of the array.

Closes #12833
2015-09-13 16:35:46 +01:00
Pawel Kozlowski f13055a0a5 fix($http): propagate status -1 for timed out requests
Fixes #4491
Closes #8756
2015-09-07 14:34:10 +01:00
Peter Bacon Darwin 623ce1ad2c fix($location): don't crash if navigating outside the app base
Previously, if you navigate outside of the Angular application, say be clicking
the back button, the $location service would try to handle the url change
and error due to the URL not being valid for the application.

This fixes that issue by ensuring that a reload happens when you navigate
to a URL that is not within the application.

Closes #11667
2015-09-07 14:33:17 +01:00
Peter Bacon Darwin 34cf141838 refactor($location): compute appBaseNoFile only once 2015-09-07 14:33:17 +01:00
Martin Staffa 274e93537e fix(ngModel): validate pattern against the viewValue
Since the HTML5 pattern validation constraint validates the input value,
we should also validate against the viewValue. While this worked in
core up to Angular 1.2, in 1.3, we changed not only validation,
but the way `input[date]` and `input[number]` are handled - they parse
their input values into `Date` and `Number` respectively, which cannot
be validated by a regex.

Fixes #12344

BREAKING CHANGE:

The `ngPattern` and `pattern` directives will validate the regex
against the `viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the modelValue
(the result of the $parsers) was validated.

This fixes issues where `input[date]` and `input[number]` cannot
be validated because the viewValue string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.

This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.

If you rely on the pattern being validated against the modelValue,
you must create your own validator directive that overwrites
the built-in pattern validator:

```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
  return {
    restrict: 'A',
    require: '?ngModel',
    priority: 1,
    compile: function() {
      var regexp, patternExp;

      return {
        pre: function(scope, elm, attr, ctrl) {
          if (!ctrl) return;

          attr.$observe('pattern', function(regex) {
            /**
             * The built-in directive will call our overwritten validator
             * (see below). We just need to update the regex.
             * The preLink fn guaranetees our observer is called first.
             */
            if (isString(regex) && regex.length > 0) {
              regex = new RegExp('^' + regex + '$');
            }

            if (regex && !regex.test) {
              //The built-in validator will throw at this point
              return;
            }

            regexp = regex || undefined;
          });

        },
        post: function(scope, elm, attr, ctrl) {
          if (!ctrl) return;

          regexp, patternExp = attr.ngPattern || attr.pattern;

          //The postLink fn guarantees we overwrite the built-in pattern validator
          ctrl.$validators.pattern = function(value) {
            return ctrl.$isEmpty(value) ||
              isUndefined(regexp) ||
              regexp.test(value);
          };
        }
      };
    }
  };
});
```
2015-08-28 10:57:07 +02:00
53 changed files with 2272 additions and 885 deletions
+17 -21
View File
@@ -1,6 +1,7 @@
language: node_js
sudo: false
node_js:
- '0.10'
- '4.2'
cache:
directories:
@@ -14,28 +15,26 @@ branches:
env:
matrix:
- JOB=ci-checks
- JOB=unit BROWSER_PROVIDER=saucelabs
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
- JOB=unit BROWSER_PROVIDER=browserstack
- JOB=docs-e2e BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
global:
- CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=VojtaJina
- BROWSER_STACK_ACCESS_KEY=QCQJ1ZpWXpBkSwEdD8ev
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
matrix:
allow_failures:
- env: "JOB=unit BROWSER_PROVIDER=browserstack"
- env: "JOB=docs-e2e BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack"
# node 4 likes the G++ v4.8 compiler
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
install:
# Check the size of caches
@@ -45,21 +44,18 @@ install:
- npm config set spin false
# Log HTTP requests
- npm config set loglevel http
- npm install -g npm@2.5
# Instal npm dependecies and ensure that npm cache is not stale
- scripts/npm/install-dependencies.sh
#- npm install -g npm@2.5
# Install npm dependencies and ensure that npm cache is not stale
- npm install
before_script:
- mkdir -p $LOGS_DIR
- ./scripts/travis/start_browser_provider.sh
- npm install -g grunt-cli
- grunt package
- ./scripts/travis/wait_for_browser_provider.sh
- ./scripts/travis/before_build.sh
script:
- ./scripts/travis/build.sh
after_script:
- ./scripts/travis/tear_down_browser_provider.sh
- ./scripts/travis/print_logs.sh
notifications:
+116
View File
@@ -1,3 +1,119 @@
<a name="1.3.20"></a>
# 1.3.20 shallow-translucence (2015-09-29)
## Bug Fixes
- **$parse:** do not convert to string computed properties multiple times
([d434f3db](https://github.com/angular/angular.js/commit/d434f3db53d6209eb140b904e83bbde401686c16))
## Breaking Changes
<a name="1.3.19"></a>
# 1.3.19 glutinous-shriek (2015-09-15)
## Bug Fixes
- **$http:** propagate status -1 for timed out requests
([f13055a0](https://github.com/angular/angular.js/commit/f13055a0a53a39b160448713a5617edee6042801),
[#4491](https://github.com/angular/angular.js/issues/4491), [#8756](https://github.com/angular/angular.js/issues/8756))
- **$location:** don't crash if navigating outside the app base
([623ce1ad](https://github.com/angular/angular.js/commit/623ce1ad2cf68024719c5cae5d682d00195df30c),
[#11667](https://github.com/angular/angular.js/issues/11667))
- **$parse:** throw error when accessing a restricted property indirectly
([ec98c94c](https://github.com/angular/angular.js/commit/ec98c94ccbfc97b655447956738d5f6ff98b2f33),
[#12833](https://github.com/angular/angular.js/issues/12833))
- **ngModel:** validate pattern against the viewValue
([274e9353](https://github.com/angular/angular.js/commit/274e93537ed4e95aefeacea48909eb334894f0ac),
[#12344](https://github.com/angular/angular.js/issues/12344))
## Features
- **ngAnimate:** introduce `$animate.flush` for unit testing
([f98e0384](https://github.com/angular/angular.js/commit/f98e038418f7367b2373adcf4887f64a8e8bdcb0))
## Possible Breaking Changes
- **ngModel:** due to [274e9353](https://github.com/angular/angular.js/commit/274e93537ed4e95aefeacea48909eb334894f0ac),
The `ngPattern` and `pattern` directives will validate the regex
against the `viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the modelValue
(the result of the $parsers) was validated.
This fixes issues where `input[date]` and `input[number]` cannot
be validated because the viewValue string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.
This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.
If you rely on the pattern being validated against the modelValue,
you must create your own validator directive that overwrites
the built-in pattern validator:
```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
compile: function() {
var regexp, patternExp;
return {
pre: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.$observe('pattern', function(regex) {
/**
* The built-in directive will call our overwritten validator
* (see below). We just need to update the regex.
* The preLink fn guaranetees our observer is called first.
*/
if (isString(regex) && regex.length > 0) {
regex = new RegExp('^' + regex + '$');
}
if (regex && !regex.test) {
//The built-in validator will throw at this point
return;
}
regexp = regex || undefined;
});
},
post: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
regexp, patternExp = attr.ngPattern || attr.pattern;
//The postLink fn guarantees we overwrite the built-in pattern validator
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) ||
isUndefined(regexp) ||
regexp.test(value);
};
}
};
}
};
});
```
<a name="1.3.18"></a>
# 1.3.18 collective-penmanship (2015-08-18)
+14 -12
View File
@@ -155,7 +155,7 @@ module.exports = function(grunt) {
jscs: {
src: ['src/**/*.js', 'test/**/*.js'],
options: {
config: ".jscsrc"
config: '.jscsrc'
}
},
@@ -220,9 +220,9 @@ module.exports = function(grunt) {
dest: 'build/angular-aria.js',
src: util.wrap(files['angularModules']['ngAria'], 'module')
},
"promises-aplus-adapter": {
'promises-aplus-adapter': {
dest:'tmp/promises-aplus-adapter++.js',
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
}
},
@@ -241,7 +241,7 @@ module.exports = function(grunt) {
},
"ddescribe-iit": {
'ddescribe-iit': {
files: [
'src/**/*.js',
'test/**/*.js',
@@ -262,7 +262,7 @@ module.exports = function(grunt) {
}
},
"merge-conflict": {
'merge-conflict': {
files: [
'src/**/*',
'test/**/*',
@@ -292,11 +292,11 @@ module.exports = function(grunt) {
},
shell: {
"npm-install": {
command: path.normalize('scripts/npm/install-dependencies.sh')
'npm-install': {
command: 'node scripts/npm/check-node-modules.js'
},
"promises-aplus-tests": {
'promises-aplus-tests': {
options: {
stdout: false,
stderr: true,
@@ -327,8 +327,10 @@ module.exports = function(grunt) {
grunt.task.run('shell:npm-install');
}
//alias tasks
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
@@ -338,11 +340,11 @@ module.exports = function(grunt) {
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter', 'shell:promises-aplus-tests']);
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
grunt.registerTask('minify', ['bower', 'clean', 'build', 'minall']);
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package', ['bower', 'validate-angular-files', 'clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
grunt.registerTask('default', ['package']);
};
+2 -1
View File
@@ -32,6 +32,7 @@ var angularFiles = {
'src/ng/q.js',
'src/ng/raf.js',
'src/ng/rootScope.js',
'src/ng/rootElement.js',
'src/ng/sanitizeUri.js',
'src/ng/sce.js',
'src/ng/sniffer.js',
@@ -78,7 +79,7 @@ var angularFiles = {
],
'angularLoader': [
'stringify.js',
'src/stringify.js',
'src/minErr.js',
'src/loader.js'
],
-8
View File
@@ -1,8 +0,0 @@
@ngdoc error
@name $compile:baddir
@fullName Invalid Directive Name
@description
This error occurs when the name of a directive is not valid.
Directives must start with a lowercase character.
+7
View File
@@ -0,0 +1,7 @@
@ngdoc error
@name ng:cpta
@fullName Copying TypedArray
@description
Copying TypedArray's with a destination is not supported because TypedArray
objects can not be mutated, they are fixed length.
@@ -0,0 +1,28 @@
@ngdoc error
@name ngModel:nopromise
@fullName No promise
@description
The return value of an async validator, must always be a promise. If you want to return a
non-promise value, you can convert it to a promise using {@link ng.$q#resolve `$q.resolve()`} or
{@link ng.$q#reject `$q.reject()`}.
Example:
```
.directive('asyncValidator', function($q) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$asyncValidators.myAsyncValidation = function(modelValue, viewValue) {
if (/* I don't need to hit the backend API */) {
return $q.resolve(); // to mark as valid or
// return $q.reject(); // to mark as invalid
} else {
// ...send a request to the backend and return a promise
}
};
}
};
})
```
+1 -1
View File
@@ -173,7 +173,7 @@ function request(method, url, options, response) {
res.on('error', function (e) { console.log(e); });
break;
case 401:
console.log('Eror: Login credentials expired! Please login.');
console.log('Error: Login credentials expired! Please login.');
break;
default:
data = [];
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e -o pipefail
echo "Shutting down Browserstack tunnel"
echo "TODO: implement me"
exit 1
+58
View File
@@ -0,0 +1,58 @@
'use strict';
var path = require('path');
var fs = require('fs');
var glob = require("glob");
var _ = require('lodash');
var files = require('../../angularFiles').files;
module.exports = function(grunt) {
grunt.registerTask('validate-angular-files', function() {
var combinedFiles = _.clone(files.angularModules);
combinedFiles.ng = files.angularSrc;
combinedFiles.angularLoader = files.angularLoader;
var errorsDetected = false;
var directories = [];
var detectedFiles = {};
for (var section in combinedFiles) {
var sectionFiles = combinedFiles[section];
if (section != 'angularLoader') {
directories.push('src/' + section);
}
grunt.log.debug('Validating ' + sectionFiles.length + ' files from the "' + section + '" module.');
sectionFiles.forEach(function(file) {
detectedFiles[file] = true;
if (!fs.existsSync(file)) {
grunt.log.error(file + ' does not exist in the local file structure.');
errorsDetected = true;
}
});
}
directories.forEach(function(directory) {
glob.sync(directory + '/**/*').forEach(function(filePath) {
if (!fs.lstatSync(filePath).isDirectory()) {
var fileName = path.basename(filePath);
var isHiddenFile = fileName[0] == '.';
if (!isHiddenFile && !detectedFiles[filePath]) {
grunt.log.error(filePath + ' exists in the local file structure but isn\'t used by any module.');
errorsDetected = true;
}
}
});
});
if (errorsDetected) {
throw new Error('Not all files were properly detected in the local file structure.');
} else {
grunt.log.ok('All files were detected successfully!');
}
});
};
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
set -e -o pipefail
echo "Shutting down Sauce Connect tunnel"
killall sc
while [[ -n `ps -ef | grep "sauce-connect-" | grep -v "grep"` ]]; do
printf "."
sleep .5
done
echo ""
echo "Sauce Connect tunnel has been shut down"
+240 -74
View File
@@ -207,7 +207,7 @@
"dependencies": {
"traceur": {
"version": "0.0.33",
"resolved": "git+https://github.com/vojtajina/traceur-compiler#d90b1e34c799bf61cd1aafdc33db0a554fa9e617",
"resolved": "git+https://github.com/vojtajina/traceur-compiler.git#d90b1e34c799bf61cd1aafdc33db0a554fa9e617",
"dependencies": {
"commander": {
"version": "2.7.1",
@@ -2580,14 +2580,6 @@
},
"async-each": {
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.6",
"dependencies": {
"nan": {
"version": "1.8.4"
}
}
}
}
}
@@ -2740,6 +2732,49 @@
}
}
},
"glob": {
"version": "6.0.1",
"dependencies": {
"inflight": {
"version": "1.0.4",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"inherits": {
"version": "2.0.1"
},
"minimatch": {
"version": "3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.2",
"dependencies": {
"balanced-match": {
"version": "0.3.0"
},
"concat-map": {
"version": "0.0.1"
}
}
}
}
},
"once": {
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"path-is-absolute": {
"version": "1.0.0"
}
}
},
"grunt": {
"version": "0.4.5",
"dependencies": {
@@ -5470,14 +5505,6 @@
},
"async-each": {
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.5",
"dependencies": {
"nan": {
"version": "1.5.3"
}
}
}
}
},
@@ -6505,37 +6532,45 @@
}
},
"protractor": {
"version": "2.0.0",
"version": "2.5.1",
"dependencies": {
"request": {
"version": "2.36.0",
"version": "2.57.0",
"dependencies": {
"qs": {
"version": "0.6.6"
},
"json-stringify-safe": {
"version": "5.0.0"
},
"mime": {
"version": "1.2.11"
},
"forever-agent": {
"version": "0.5.2"
},
"node-uuid": {
"version": "1.4.3"
},
"tough-cookie": {
"version": "0.12.1",
"bl": {
"version": "0.9.4",
"dependencies": {
"punycode": {
"version": "1.3.2"
"readable-stream": {
"version": "1.0.33",
"dependencies": {
"core-util-is": {
"version": "1.0.2"
},
"isarray": {
"version": "0.0.1"
},
"string_decoder": {
"version": "0.10.31"
},
"inherits": {
"version": "2.0.1"
}
}
}
}
},
"caseless": {
"version": "0.10.0"
},
"forever-agent": {
"version": "0.6.1"
},
"form-data": {
"version": "0.1.4",
"version": "0.2.0",
"dependencies": {
"async": {
"version": "0.9.2"
},
"combined-stream": {
"version": "0.0.7",
"dependencies": {
@@ -6543,17 +6578,34 @@
"version": "0.0.5"
}
}
},
"async": {
"version": "0.9.0"
}
}
},
"json-stringify-safe": {
"version": "5.0.1"
},
"mime-types": {
"version": "2.0.14",
"dependencies": {
"mime-db": {
"version": "1.12.0"
}
}
},
"node-uuid": {
"version": "1.4.7"
},
"qs": {
"version": "3.1.0"
},
"tunnel-agent": {
"version": "0.4.0"
"version": "0.4.2"
},
"tough-cookie": {
"version": "2.2.1"
},
"http-signature": {
"version": "0.10.1",
"version": "0.11.0",
"dependencies": {
"assert-plus": {
"version": "0.1.5"
@@ -6567,38 +6619,120 @@
}
},
"oauth-sign": {
"version": "0.3.0"
"version": "0.8.0"
},
"hawk": {
"version": "1.0.0",
"version": "2.3.1",
"dependencies": {
"hoek": {
"version": "0.9.1"
"version": "2.16.3"
},
"boom": {
"version": "0.4.2"
"version": "2.10.1"
},
"cryptiles": {
"version": "0.2.2"
"version": "2.0.5"
},
"sntp": {
"version": "0.2.4"
"version": "1.0.9"
}
}
},
"aws-sign2": {
"version": "0.5.0"
},
"stringstream": {
"version": "0.0.5"
},
"combined-stream": {
"version": "1.0.5",
"dependencies": {
"delayed-stream": {
"version": "1.0.0"
}
}
},
"isstream": {
"version": "0.1.2"
},
"har-validator": {
"version": "1.8.0",
"dependencies": {
"bluebird": {
"version": "2.10.2"
},
"chalk": {
"version": "1.1.1",
"dependencies": {
"ansi-styles": {
"version": "2.1.0"
},
"escape-string-regexp": {
"version": "1.0.3"
},
"has-ansi": {
"version": "2.0.0",
"dependencies": {
"ansi-regex": {
"version": "2.0.0"
}
}
},
"strip-ansi": {
"version": "3.0.0",
"dependencies": {
"ansi-regex": {
"version": "2.0.0"
}
}
},
"supports-color": {
"version": "2.0.0"
}
}
},
"commander": {
"version": "2.9.0",
"dependencies": {
"graceful-readlink": {
"version": "1.0.1"
}
}
},
"is-my-json-valid": {
"version": "2.12.3",
"dependencies": {
"generate-function": {
"version": "2.0.0"
},
"generate-object-property": {
"version": "1.2.0",
"dependencies": {
"is-property": {
"version": "1.0.2"
}
}
},
"jsonpointer": {
"version": "2.0.0"
},
"xtend": {
"version": "4.0.1"
}
}
}
}
}
}
},
"selenium-webdriver": {
"version": "2.45.1",
"version": "2.47.0",
"dependencies": {
"rimraf": {
"version": "2.3.2",
"version": "2.4.4",
"dependencies": {
"glob": {
"version": "4.5.3",
"version": "5.0.15",
"dependencies": {
"inflight": {
"version": "1.0.4",
@@ -6612,13 +6746,13 @@
"version": "2.0.1"
},
"minimatch": {
"version": "2.0.4",
"version": "3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
"version": "1.1.2",
"dependencies": {
"balanced-match": {
"version": "0.2.0"
"version": "0.3.0"
},
"concat-map": {
"version": "0.0.1"
@@ -6628,12 +6762,15 @@
}
},
"once": {
"version": "1.3.1",
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"path-is-absolute": {
"version": "1.0.0"
}
}
}
@@ -6643,33 +6780,33 @@
"version": "0.0.24"
},
"ws": {
"version": "0.7.1",
"version": "0.8.1",
"dependencies": {
"options": {
"version": "0.0.6"
},
"ultron": {
"version": "1.0.1"
"version": "1.0.2"
},
"bufferutil": {
"version": "1.0.1",
"version": "1.2.1",
"dependencies": {
"bindings": {
"version": "1.2.1"
},
"nan": {
"version": "1.6.2"
"version": "2.1.0"
}
}
},
"utf-8-validate": {
"version": "1.0.1",
"version": "1.2.1",
"dependencies": {
"bindings": {
"version": "1.2.1"
},
"nan": {
"version": "1.6.2"
"version": "2.1.0"
}
}
}
@@ -6682,10 +6819,10 @@
"version": "0.6.1"
},
"xmlbuilder": {
"version": "2.6.2",
"version": "4.2.0",
"dependencies": {
"lodash": {
"version": "3.5.0"
"version": "3.10.1"
}
}
}
@@ -6700,18 +6837,47 @@
"version": "1.1.0"
},
"jasminewd2": {
"version": "0.0.3"
"version": "0.0.6"
},
"jasmine": {
"version": "2.1.1",
"version": "2.3.2",
"dependencies": {
"exit": {
"version": "0.1.2"
},
"jasmine-core": {
"version": "2.1.3"
"version": "2.3.4"
}
}
},
"saucelabs": {
"version": "0.1.1"
"version": "1.0.1",
"dependencies": {
"https-proxy-agent": {
"version": "1.0.0",
"dependencies": {
"agent-base": {
"version": "2.0.1",
"dependencies": {
"semver": {
"version": "5.0.3"
}
}
},
"debug": {
"version": "2.2.0",
"dependencies": {
"ms": {
"version": "0.7.1"
}
}
},
"extend": {
"version": "3.0.0"
}
}
}
}
},
"glob": {
"version": "3.2.11",
@@ -6723,10 +6889,10 @@
"version": "0.3.0",
"dependencies": {
"lru-cache": {
"version": "2.5.0"
"version": "2.7.3"
},
"sigmund": {
"version": "1.0.0"
"version": "1.0.1"
}
}
}
@@ -6739,7 +6905,7 @@
"version": "0.6.1",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
"version": "0.0.3"
},
"minimist": {
"version": "0.0.10"
@@ -6756,14 +6922,14 @@
"version": "0.1.32",
"dependencies": {
"amdefine": {
"version": "0.1.0"
"version": "1.0.0"
}
}
}
}
},
"html-entities": {
"version": "1.1.2"
"version": "1.1.3"
},
"accessibility-developer-tools": {
"version": "2.6.0"
+544 -290
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -1,6 +1,7 @@
{
"name": "angularjs",
"branchVersion": "1.3.*",
"distTag": "previous_1_3",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
@@ -10,6 +11,10 @@
"npm": "~2.5"
},
"engineStrict": true,
"scripts": {
"preinstall": "node scripts/npm/check-node-modules.js --purge",
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js"
},
"devDependencies": {
"angular-benchpress": "0.x.x",
"benchmark": "1.x.x",
@@ -20,6 +25,7 @@
"dgeni": "^0.4.0",
"dgeni-packages": "^0.10.0",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
"grunt": "~0.4.2",
"grunt-bump": "~0.0.13",
"grunt-contrib-clean": "~0.6.0",
+4 -13
View File
@@ -29,6 +29,8 @@ function init {
angular-touch
angular-messages
)
# get the npm dist-tag from a custom property (distTag) in package.json
DIST_TAG=$(readJsonProp "package.json" "distTag")
}
@@ -110,19 +112,8 @@ function publish {
# don't publish every build to npm
if [ "${NEW_VERSION/+sha}" = "$NEW_VERSION" ] ; then
if [ "${NEW_VERSION/-}" = "$NEW_VERSION" ] ; then
if [[ $NEW_VERSION =~ ^1\.2\.[0-9]+$ ]] ; then
# publish 1.2.x releases with the appropriate tag
# this ensures that `npm install` by default will not grab `1.2.x` releases
npm publish --tag=old
else
# publish releases as "latest"
npm publish
fi
else
# publish prerelease builds with the beta tag
npm publish --tag=beta
fi
echo "-- Publishing to npm as $DIST_TAG"
npm publish --tag=$DIST_TAG
fi
cd $SCRIPT_DIR
@@ -7,6 +7,8 @@ echo "#################################"
# Enable tracing and exit on first failure
set -xe
scripts/jenkins/set-node-version.sh
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
@@ -19,6 +21,7 @@ rm -f angular.js.size
# BUILD #
npm install -g grunt-cli
npm install --color false
grunt ci-checks package --no-color
+2 -11
View File
@@ -4,9 +4,7 @@ echo "#################################"
echo "#### Update master ##############"
echo "#################################"
ARG_DEFS=(
"[--no-test=(true|false)]"
)
ARG_DEFS=()
function init {
if [[ ! $VERBOSE ]]; then
@@ -17,14 +15,7 @@ function init {
function build {
cd ../..
if [[ $NO_TEST == "true" ]]; then
npm install --color false
grunt ci-checks package --no-color
else
./jenkins_build.sh
fi
scripts/jenkins/build.sh
cd $SCRIPT_DIR
}
+2
View File
@@ -35,8 +35,10 @@ function init {
}
function build {
./set-node-version.sh
cd ../..
npm install -g grunt-cli
npm install --color false
grunt ci-checks package --no-color
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
# Install nvm for this shell
source ~/.nvm/nvm.sh
# Use node.js at 4.2.x
nvm install 4.2
+74
View File
@@ -0,0 +1,74 @@
// Implementation based on:
// https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js
'use strict';
// Imports
var fs = require('fs');
var path = require('path');
// Constants
var PROJECT_ROOT = path.join(__dirname, '../../');
var NODE_MODULES_DIR = 'node_modules';
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
// Run
_main();
// Functions - Definitions
function _main() {
var purgeIfStale = process.argv.indexOf('--purge') !== -1;
process.chdir(PROJECT_ROOT);
checkNodeModules(purgeIfStale);
}
function checkNodeModules(purgeIfStale) {
var nodeModulesOk = compareMarkerFiles(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE);
if (nodeModulesOk) {
console.log(':-) npm dependencies are looking good!');
} else if (purgeIfStale) {
console.log(':-( npm dependencies are stale or in an unknown state!');
console.log(' Purging \'' + NODE_MODULES_DIR + '\'...');
deleteDirSync(NODE_MODULES_DIR);
} else {
var separator = new Array(81).join('!');
console.warn(separator);
console.warn(':-( npm dependencies are stale or in an unknown state!');
console.warn('You can rebuild the dependencies by running `npm install`.');
console.warn(separator);
}
return nodeModulesOk;
}
function compareMarkerFiles(markerFilePath, cachedMarkerFilePath) {
if (!fs.existsSync(cachedMarkerFilePath)) return false;
var opts = {encoding: 'utf-8'};
var markerContent = fs.readFileSync(markerFilePath, opts);
var cachedMarkerContent = fs.readFileSync(cachedMarkerFilePath, opts);
return markerContent === cachedMarkerContent;
}
// Custom implementation of `rm -rf` that works consistently across OSes
function deleteDirSync(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(deleteDirOrFileSync);
fs.rmdirSync(path);
}
// Helpers
function deleteDirOrFileSync(subpath) {
var curPath = path + '/' + subpath;
if (fs.lstatSync(curPath).isDirectory()) {
deleteDirSync(curPath);
} else {
fs.unlinkSync(curPath);
}
}
}
+60
View File
@@ -0,0 +1,60 @@
'use strict';
// Imports
var fs = require('fs');
var path = require('path');
// Constants
var PROJECT_ROOT = path.join(__dirname, '../../');
var NODE_MODULES_DIR = 'node_modules';
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
// Run
_main();
// Functions - Definitions
function _main() {
process.chdir(PROJECT_ROOT);
copyFile(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE, onCopied);
}
// Implementation based on:
// https://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js#answer-21995878
function copyFile(srcPath, dstPath, callback) {
var callbackCalled = false;
if (!fs.existsSync(srcPath)) {
done(new Error('Missing source file: ' + srcPath));
return;
}
var rs = fs.createReadStream(srcPath);
rs.on('error', done);
var ws = fs.createWriteStream(dstPath);
ws.on('error', done);
ws.on('finish', done);
rs.pipe(ws);
// Helpers
function done(err) {
if (callback && !callbackCalled) {
callbackCalled = true;
callback(err);
}
}
}
function onCopied(err) {
if (err) {
var separator = new Array(81).join('!');
console.error(separator);
console.error(
'Failed to copy `' + NPM_SHRINKWRAP_FILE + '` to `' + NPM_SHRINKWRAP_CACHED_FILE + '`:');
console.error(err);
console.error(separator);
}
}
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash
set -e
SHRINKWRAP_FILE=npm-shrinkwrap.json
SHRINKWRAP_CACHED_FILE=node_modules/npm-shrinkwrap.cached.json
if diff -q $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE; then
echo 'No shrinkwrap changes detected. npm install will be skipped...';
else
echo 'Blowing away node_modules and reinstalling npm dependencies...'
rm -rf node_modules
npm install
cp $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE
echo 'npm install successful!'
fi
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -e
mkdir -p $LOGS_DIR
if [ $JOB != "ci-checks" ]; then
echo "start_browser_provider"
./scripts/travis/start_browser_provider.sh
fi
npm install -g grunt-cli
if [ $JOB != "ci-checks" ]; then
grunt package
echo "wait_for_browser_provider"
./scripts/travis/wait_for_browser_provider.sh
fi
+4 -3
View File
@@ -5,7 +5,9 @@ set -e
export BROWSER_STACK_ACCESS_KEY=`echo $BROWSER_STACK_ACCESS_KEY | rev`
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
if [ $JOB = "unit" ]; then
if [ $JOB = "ci-checks" ]; then
grunt ci-checks
elif [ $JOB = "unit" ]; then
if [ "$BROWSER_PROVIDER" == "browserstack" ]; then
BROWSERS="BS_Chrome,BS_Safari,BS_Firefox,BS_IE_9,BS_IE_10,BS_IE_11,BS_iOS"
else
@@ -14,7 +16,6 @@ if [ $JOB = "unit" ]; then
grunt test:promises-aplus
grunt test:unit --browsers $BROWSERS --reporters dots
grunt ci-checks
grunt tests:docs --browsers $BROWSERS --reporters dots
elif [ $JOB = "docs-e2e" ]; then
grunt test:travis-protractor --specs "docs/app/e2e/**/*.scenario.js"
@@ -31,5 +32,5 @@ elif [ $JOB = "e2e" ]; then
export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
grunt test:travis-protractor --specs "$TARGET_SPECS"
else
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."
echo "Unknown job type. Please set JOB=ci-checks, JOB=unit or JOB=e2e-*."
fi
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Has to be run from project root directory.
./lib/${BROWSER_PROVIDER}/teardown_tunnel.sh
+111 -59
View File
@@ -36,6 +36,7 @@
isUndefined: true,
isDefined: true,
isObject: true,
isBlankObject: true,
isString: true,
isNumber: true,
isDate: true,
@@ -172,6 +173,7 @@ var
splice = [].splice,
push = [].push,
toString = Object.prototype.toString,
getPrototypeOf = Object.getPrototypeOf,
ngMinErr = minErr('ng'),
/** @name angular */
@@ -461,6 +463,16 @@ function isObject(value) {
}
/**
* Determine if a value is an object with a null prototype
*
* @returns {boolean} True if `value` is an `Object` with a null prototype
*/
function isBlankObject(value) {
return value !== null && typeof value === 'object' && !getPrototypeOf(value);
}
/**
* @ngdoc function
* @name angular.isString
@@ -597,6 +609,12 @@ function isPromiseLike(obj) {
}
var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
function isTypedArray(value) {
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
}
var trim = function(value) {
return isString(value) ? value.trim() : value;
};
@@ -713,77 +731,111 @@ function arrayRemove(array, value) {
</file>
</example>
*/
function copy(source, destination, stackSource, stackDest) {
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
function copy(source, destination) {
var stackSource = [];
var stackDest = [];
if (destination) {
if (isTypedArray(destination)) {
throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
}
if (source === destination) {
throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
}
// Empty the destination object
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
if (key !== '$$hashKey') {
delete destination[key];
}
});
}
stackSource.push(source);
stackDest.push(destination);
return copyRecurse(source, destination);
}
if (!destination) {
destination = source;
if (source) {
if (isArray(source)) {
destination = copy(source, [], stackSource, stackDest);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isObject(source)) {
var emptyObject = Object.create(Object.getPrototypeOf(source));
destination = copy(source, emptyObject, stackSource, stackDest);
}
}
} else {
if (source === destination) throw ngMinErr('cpi',
"Can't copy! Source and destination are identical.");
return copyElement(source);
stackSource = stackSource || [];
stackDest = stackDest || [];
if (isObject(source)) {
var index = stackSource.indexOf(source);
if (index !== -1) return stackDest[index];
stackSource.push(source);
stackDest.push(destination);
}
var result;
function copyRecurse(source, destination) {
var h = destination.$$hashKey;
var result, key;
if (isArray(source)) {
destination.length = 0;
for (var i = 0; i < source.length; i++) {
result = copy(source[i], null, stackSource, stackDest);
if (isObject(source[i])) {
stackSource.push(source[i]);
stackDest.push(result);
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
}
} else if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
destination[key] = copyElement(source[key]);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = copyElement(source[key]);
}
destination.push(result);
}
} else {
var h = destination.$$hashKey;
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
delete destination[key];
});
}
for (var key in source) {
if (source.hasOwnProperty(key)) {
result = copy(source[key], null, stackSource, stackDest);
if (isObject(source[key])) {
stackSource.push(source[key]);
stackDest.push(result);
}
destination[key] = result;
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
destination[key] = copyElement(source[key]);
}
}
setHashKey(destination,h);
}
setHashKey(destination, h);
return destination;
}
function copyElement(source) {
// Simple values
if (!isObject(source)) {
return source;
}
// Already copied values
var index = stackSource.indexOf(source);
if (index !== -1) {
return stackDest[index];
}
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
}
var needsRecurse = false;
var destination;
if (isArray(source)) {
destination = [];
needsRecurse = true;
} else if (isTypedArray(source)) {
destination = new source.constructor(source);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isFunction(source.cloneNode)) {
destination = source.cloneNode(true);
} else {
destination = Object.create(getPrototypeOf(source));
needsRecurse = true;
}
stackSource.push(source);
stackDest.push(destination);
return needsRecurse
? copyRecurse(source, destination)
: destination;
}
return destination;
}
/**
-9
View File
@@ -757,14 +757,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return bindings;
}
function assertValidDirectiveName(name) {
var letter = name.charAt(0);
if (!letter || letter !== lowercase(letter)) {
throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
}
return name;
}
/**
* @ngdoc method
* @name $compileProvider#directive
@@ -783,7 +775,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
this.directive = function registerDirective(name, directiveFactory) {
assertNotHasOwnProperty(name, 'directive');
if (isString(name)) {
assertValidDirectiveName(name);
assertArg(directiveFactory, 'directiveFactory');
if (!hasDirectives.hasOwnProperty(name)) {
hasDirectives[name] = [];
+3 -7
View File
@@ -318,13 +318,9 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
*
* In Angular, forms can be nested. This means that the outer form is valid when all of the child
* forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
* Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
* `<form>` but can be nested. This allows you to have nested forms, which is very useful when
* using Angular validation directives in forms that are dynamically generated using the
* {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
* attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
* `ngForm` directive and nest these in an outer `form` element.
*
* Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
* `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
* of controls needs to be determined.
*
* # CSS classes
* - `ng-valid` is set if the form is valid.
+9 -6
View File
@@ -1535,13 +1535,16 @@ var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
return {
restrict: 'E',
require: ['?ngModel'],
link: {
pre: function(scope, element, attr, ctrls) {
if (ctrls[0]) {
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
$browser, $filter, $parse);
compile: function(tElement, tAttr) {
if (lowercase(tAttr.type) === 'hidden') tAttr.$set('autocomplete', 'off');
return {
pre: function(scope, element, attr, ctrls) {
if (ctrls[0]) {
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
$browser, $filter, $parse);
}
}
}
};
}
};
}];
+1 -1
View File
@@ -596,7 +596,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
forEach(ctrl.$asyncValidators, function(validator, name) {
var promise = validator(modelValue, viewValue);
if (!isPromiseLike(promise)) {
throw ngModelMinErr("$asyncValidators",
throw ngModelMinErr('nopromise',
"Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
}
setValidity(name, undefined);
+3 -2
View File
@@ -43,8 +43,9 @@ var patternDirective = function() {
ctrl.$validate();
});
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
ctrl.$validators.pattern = function(modelValue, viewValue) {
// HTML5 pattern constraint validates the input value, so we validate the viewValue
return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
};
}
};
+2 -2
View File
@@ -1110,8 +1110,8 @@ function $HttpProvider() {
* Resolves the raw $http promise.
*/
function resolvePromise(response, status, headers, statusText) {
// normalize internal statuses to 0
status = Math.max(status, 0);
//status: HTTP response status code, 0, -1 (aborted by timeout / promise)
status = status >= -1 ? status : 0;
(isSuccess(status) ? deferred.resolve : deferred.reject)({
data: response,
+16 -8
View File
@@ -89,12 +89,12 @@ function serverBase(url) {
*
* @constructor
* @param {string} appBase application base URL
* @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} basePrefix url path prefix
*/
function LocationHtml5Url(appBase, basePrefix) {
function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
this.$$html5 = true;
basePrefix = basePrefix || '';
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this);
@@ -168,10 +168,10 @@ function LocationHtml5Url(appBase, basePrefix) {
*
* @constructor
* @param {string} appBase application base URL
* @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangUrl(appBase, hashPrefix) {
var appBaseNoFile = stripFile(appBase);
function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
parseAbsoluteUrl(appBase, this);
@@ -280,14 +280,13 @@ function LocationHashbangUrl(appBase, hashPrefix) {
*
* @constructor
* @param {string} appBase application base URL
* @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangInHtml5Url(appBase, hashPrefix) {
function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
this.$$html5 = true;
LocationHashbangUrl.apply(this, arguments);
var appBaseNoFile = stripFile(appBase);
this.$$parseLinkUrl = function(url, relHref) {
if (relHref && relHref[0] === '#') {
// special case for links to hash fragments:
@@ -823,7 +822,9 @@ function $LocationProvider() {
appBase = stripHash(initialUrl);
LocationMode = LocationHashbangUrl;
}
$location = new LocationMode(appBase, '#' + hashPrefix);
var appBaseNoFile = stripFile(appBase);
$location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
$location.$$parseLinkUrl(initialUrl, initialUrl);
$location.$$state = $browser.state();
@@ -903,6 +904,13 @@ function $LocationProvider() {
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl, newState) {
if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
// If we are navigating outside of the app then force a reload
$window.location.href = newUrl;
return;
}
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
+21 -2
View File
@@ -48,6 +48,25 @@ function ensureSafeMemberName(name, fullExpression) {
return name;
}
function getStringValue(name, fullExpression) {
// From the JavaScript docs:
// Property names must be strings. This means that non-string objects cannot be used
// as keys in an object. Any non-string object, including a number, is typecasted
// into a string via the toString method.
//
// So, to ensure that we are checking the same `name` that JavaScript would use,
// we cast it to a string, if possible.
// Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
// this is, this will handle objects that misbehave.
name = name + '';
if (!isString(name)) {
throw $parseMinErr('iseccst',
'Cannot convert object to primitive value! '
+ 'Expression: {0}', fullExpression);
}
return name;
}
function ensureSafeObject(obj, fullExpression) {
// nifty check if obj is Function that is fast and works across iframes and other contexts
if (obj) {
@@ -689,7 +708,7 @@ Parser.prototype = {
return extend(function $parseObjectIndex(self, locals) {
var o = obj(self, locals),
i = indexFn(self, locals),
i = getStringValue(indexFn(self, locals), expression),
v;
ensureSafeMemberName(i, expression);
@@ -698,7 +717,7 @@ Parser.prototype = {
return v;
}, {
assign: function(self, value, locals) {
var key = ensureSafeMemberName(indexFn(self, locals), expression);
var key = ensureSafeMemberName(getStringValue(indexFn(self, locals), expression), expression);
// prevent overwriting of Function.constructor which would break ensureSafeObject check
var o = ensureSafeObject(obj(self, locals), expression);
if (!o) obj.assign(self, o = {}, locals);
+2 -2
View File
@@ -1956,7 +1956,7 @@ angular.module('ngAnimate', ['ng'])
function onAnimationProgress(event) {
event.stopPropagation();
var ev = event.originalEvent || event;
var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
var timeStamp = ev.$manualTimeStamp || Date.now();
/* Firefox (or possibly just Gecko) likes to not round values up
* when a ms measurement is used for the animation */
@@ -1964,7 +1964,7 @@ angular.module('ngAnimate', ['ng'])
/* $manualTimeStamp is a mocked timeStamp value which is set
* within browserTrigger(). This is only here so that tests can
* mock animations properly. Real events fallback to event.timeStamp,
* mock animations properly. Real events fallback to Date.now(),
* or, if they don't, then a timeStamp is automatically created for them.
* We're checking to see if the timeStamp surpasses the expected delay,
* but we're using elapsedTime instead of the timeStamp on the 2nd
+48 -10
View File
@@ -782,8 +782,8 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
};
});
$provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser',
function($delegate, $$asyncCallback, $timeout, $browser) {
$provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser', '$rootScope', '$$rAF',
function($delegate, $$asyncCallback, $timeout, $browser, $rootScope, $$rAF) {
var animate = {
queue: [],
cancel: $delegate.cancel,
@@ -803,6 +803,43 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
fn();
});
reflowQueue = [];
},
flush: function() {
$rootScope.$digest();
var doNextRun, somethingFlushed = false;
do {
doNextRun = false;
if (reflowQueue.length) {
doNextRun = somethingFlushed = true;
this.triggerReflow();
}
if ($$rAF.queue.length) {
doNextRun = somethingFlushed = true;
$$rAF.flush();
}
if ($$asyncCallback.queue.length) {
doNextRun = somethingFlushed = true;
this.triggerCallbackEvents();
}
if (timeoutsRemaining()) {
var oldValue = timeoutsRemaining();
this.triggerCallbackPromise();
var newValue = timeoutsRemaining();
if (newValue < oldValue) {
doNextRun = somethingFlushed = true;
}
}
} while (doNextRun);
if (!somethingFlushed) {
throw new Error('No pending animations ready to be closed or flushed');
}
$rootScope.$digest();
function timeoutsRemaining() {
return $browser.deferredFns.length;
}
}
};
@@ -1752,8 +1789,7 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
}];
angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
var queue = [];
var rafFn = function(fn) {
var queue, rafFn = function(fn) {
var index = queue.length;
queue.push(fn);
return function() {
@@ -1761,6 +1797,8 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
};
};
queue = rafFn.queue = [];
rafFn.supported = $delegate.supported;
rafFn.flush = function() {
@@ -1773,22 +1811,22 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
queue[i]();
}
queue = [];
queue.length = 0;
};
return rafFn;
}];
angular.mock.$AsyncCallbackDecorator = ['$delegate', function($delegate) {
var callbacks = [];
var addFn = function(fn) {
callbacks.push(fn);
var queue, addFn = function(fn) {
queue.push(fn);
};
queue = addFn.queue = [];
addFn.flush = function() {
angular.forEach(callbacks, function(fn) {
angular.forEach(queue, function(fn) {
fn();
});
callbacks = [];
queue.length = 0;
};
return addFn;
}];
+2 -1
View File
@@ -165,6 +165,7 @@
"spyOnlyCallsWithArgs": false,
"createMockStyleSheet": false,
"browserTrigger": false,
"jqLiteCacheSize": false
"jqLiteCacheSize": false,
"browserSupportsCssAnimations": false
}
}
+283 -2
View File
@@ -78,6 +78,186 @@ describe('angular', function() {
expect(copy(objWithRegExp.re) === objWithRegExp.re).toBeFalsy();
});
it("should copy a Uint8Array with no destination", function() {
if (typeof Uint8Array !== 'undefined') {
var src = new Uint8Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint8Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Uint8ClampedArray with no destination", function() {
if (typeof Uint8ClampedArray !== 'undefined') {
var src = new Uint8ClampedArray(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint8ClampedArray).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Uint16Array with no destination", function() {
if (typeof Uint16Array !== 'undefined') {
var src = new Uint16Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint16Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Uint32Array with no destination", function() {
if (typeof Uint32Array !== 'undefined') {
var src = new Uint32Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Int8Array with no destination", function() {
if (typeof Int8Array !== 'undefined') {
var src = new Int8Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Int8Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Int16Array with no destination", function() {
if (typeof Int16Array !== 'undefined') {
var src = new Int16Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Int16Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Int32Array with no destination", function() {
if (typeof Int32Array !== 'undefined') {
var src = new Int32Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Int32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Float32Array with no destination", function() {
if (typeof Float32Array !== 'undefined') {
var src = new Float32Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Float32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Float64Array with no destination", function() {
if (typeof Float64Array !== 'undefined') {
var src = new Float64Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Float64Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should throw an exception if a Uint8Array is the destination", function() {
if (typeof Uint8Array !== 'undefined') {
var src = new Uint8Array();
var dst = new Uint8Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Uint8ClampedArray is the destination", function() {
if (typeof Uint8ClampedArray !== 'undefined') {
var src = new Uint8ClampedArray();
var dst = new Uint8ClampedArray(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Uint16Array is the destination", function() {
if (typeof Uint16Array !== 'undefined') {
var src = new Uint16Array();
var dst = new Uint16Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Uint32Array is the destination", function() {
if (typeof Uint32Array !== 'undefined') {
var src = new Uint32Array();
var dst = new Uint32Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Int8Array is the destination", function() {
if (typeof Int8Array !== 'undefined') {
var src = new Int8Array();
var dst = new Int8Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Int16Array is the destination", function() {
if (typeof Int16Array !== 'undefined') {
var src = new Int16Array();
var dst = new Int16Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Int32Array is the destination", function() {
if (typeof Int32Array !== 'undefined') {
var src = new Int32Array();
var dst = new Int32Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Float32Array is the destination", function() {
if (typeof Float32Array !== 'undefined') {
var src = new Float32Array();
var dst = new Float32Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Float64Array is the destination", function() {
if (typeof Float64Array !== 'undefined') {
var src = new Float64Array();
var dst = new Float64Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should deeply copy an array into an existing array", function() {
var src = [1, {name:"value"}];
var dst = [{key:"v"}];
@@ -133,11 +313,19 @@ describe('angular', function() {
it('should throw an exception if a Scope is being copied', inject(function($rootScope) {
expect(function() { copy($rootScope.$new()); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy({child: $rootScope.$new()}, {}); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy([$rootScope.$new()]); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
}));
it('should throw an exception if a Window is being copied', function() {
expect(function() { copy(window); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy({child: window}); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy([window], []); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
});
it('should throw an exception when source and destination are equivalent', function() {
@@ -154,9 +342,14 @@ describe('angular', function() {
hashKey(src);
dst = copy(src);
expect(hashKey(dst)).not.toEqual(hashKey(src));
src = {foo: {}};
hashKey(src.foo);
dst = copy(src);
expect(hashKey(src.foo)).not.toEqual(hashKey(dst.foo));
});
it('should retain the previous $$hashKey', function() {
it('should retain the previous $$hashKey when copying object with hashKey', function() {
var src,dst,h;
src = {};
dst = {};
@@ -171,7 +364,21 @@ describe('angular', function() {
expect(hashKey(dst)).toEqual(h);
});
it('should handle circular references when circularRefs is turned on', function() {
it('should retain the previous $$hashKey when copying non-object', function() {
var dst = {};
var h = hashKey(dst);
copy(null, dst);
expect(hashKey(dst)).toEqual(h);
copy(42, dst);
expect(hashKey(dst)).toEqual(h);
copy(new Date(), dst);
expect(hashKey(dst)).toEqual(h);
});
it('should handle circular references', function() {
var a = {b: {a: null}, self: null, selfs: [null, null, [null]]};
a.b.a = a;
a.self = a;
@@ -182,13 +389,87 @@ describe('angular', function() {
expect(aCopy).not.toBe(a);
expect(aCopy).toBe(aCopy.self);
expect(aCopy).toBe(aCopy.selfs[2][0]);
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
var copyTo = [];
aCopy = copy(a, copyTo);
expect(aCopy).toBe(copyTo);
expect(aCopy).not.toBe(a);
expect(aCopy).toBe(aCopy.self);
});
it('should deeply copy XML nodes', function() {
var anElement = document.createElement('foo');
anElement.appendChild(document.createElement('bar'));
var theCopy = anElement.cloneNode(true);
expect(copy(anElement).outerHTML).toEqual(theCopy.outerHTML);
expect(copy(anElement)).not.toBe(anElement);
});
it('should not try to call a non-function called `cloneNode`', function() {
expect(copy.bind(null, { cloneNode: 100 })).not.toThrow();
});
it('should handle objects with multiple references', function() {
var b = {};
var a = [b, -1, b];
var aCopy = copy(a);
expect(aCopy[0]).not.toBe(a[0]);
expect(aCopy[0]).toBe(aCopy[2]);
var copyTo = [];
aCopy = copy(a, copyTo);
expect(aCopy).toBe(copyTo);
expect(aCopy[0]).not.toBe(a[0]);
expect(aCopy[0]).toBe(aCopy[2]);
});
it('should handle date/regex objects with multiple references', function() {
var re = /foo/;
var d = new Date();
var o = {re: re, re2: re, d: d, d2: d};
var oCopy = copy(o);
expect(oCopy.re).toBe(oCopy.re2);
expect(oCopy.d).toBe(oCopy.d2);
oCopy = copy(o, {});
expect(oCopy.re).toBe(oCopy.re2);
expect(oCopy.d).toBe(oCopy.d2);
});
it('should clear destination arrays correctly when source is non-array', function() {
expect(copy(null, [1,2,3])).toEqual([]);
expect(copy(undefined, [1,2,3])).toEqual([]);
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
expect(copy(new Date(), [1,2,3])).toEqual([]);
expect(copy(/a/, [1,2,3])).toEqual([]);
expect(copy(true, [1,2,3])).toEqual([]);
});
it('should clear destination objects correctly when source is non-array', function() {
expect(copy(null, {0:1,1:2,2:3})).toEqual({});
expect(copy(undefined, {0:1,1:2,2:3})).toEqual({});
expect(copy(new Date(), {0:1,1:2,2:3})).toEqual({});
expect(copy(/a/, {0:1,1:2,2:3})).toEqual({});
expect(copy(true, {0:1,1:2,2:3})).toEqual({});
});
it('should copy objects with no prototype parent', function() {
var obj = extend(Object.create(null), {
a: 1,
b: 2,
c: 3
});
var dest = copy(obj);
expect(Object.getPrototypeOf(dest)).toBe(null);
expect(dest.a).toBe(1);
expect(dest.b).toBe(2);
expect(dest.c).toBe(3);
expect(Object.keys(dest)).toEqual(['a', 'b', 'c']);
});
});
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html ng-app>
<body>
<form>
<input type="hidden" value="{{value}}" />
<button ng-click="value = '{{ 7 * 6 }}'">Click me</button>
</form>
<script src="angular.js"></script>
</body>
</html>
+15
View File
@@ -0,0 +1,15 @@
describe('hidden thingy', function() {
it('should pass', function() {
loadFixture('input-hidden');
expect(element(by.css('input')).getAttribute('value')).toEqual('');
element(by.css('button')).click();
expect(element(by.css('input')).getAttribute('value')).toEqual('{{ 7 * 6 }}');
loadFixture('sample');
browser.driver.executeScript('history.back()');
var expectedValue = browser.params.browser === 'safari' ? '{{ 7 * 6 }}' : '';
expect(element(by.css('input')).getAttribute('value')).toEqual(expectedValue);
});
});
+9
View File
@@ -56,3 +56,12 @@ function createMockStyleSheet(doc, wind) {
}
};
}
function browserSupportsCssAnimations() {
var nav = window.navigator.appVersion;
if (nav.indexOf('MSIE') >= 0) {
var version = parseInt(navigator.appVersion.match(/MSIE ([\d.]+)/)[1]);
return version >= 10; //only IE10+ support keyframes / transitions
}
return true;
}
-10
View File
@@ -147,7 +147,6 @@ describe('$compile', function() {
describe('configuration', function() {
it('should register a directive', function() {
module(function() {
directive('div', function(log) {
@@ -202,15 +201,6 @@ describe('$compile', function() {
});
inject(function($compile) {});
});
it('should throw an exception if a directive name starts with a non-lowercase letter', function() {
module(function() {
expect(function() {
directive('BadDirectiveName', function() { });
}).toThrowMinErr('$compile','baddir', "Directive name 'BadDirectiveName' is invalid. The first character must be a lowercase letter");
});
inject(function($compile) {});
});
});
+1 -1
View File
@@ -472,7 +472,7 @@ describe('ngClass animations', function() {
//is spaced-out then it is required so that the original digestion
//is kicked into gear
$rootScope.$digest();
$animate.triggerCallbacks();
$animate.flush();
expect(element.data('state')).toBe('crazy-enter');
expect(enterComplete).toBe(true);
+7 -7
View File
@@ -433,7 +433,7 @@ describe('ngInclude', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -450,7 +450,7 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$apply(function() {
$rootScope.tpl = 'another.html';
@@ -459,7 +459,7 @@ describe('ngInclude', function() {
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$apply(function() {
$rootScope.tpl = 'template.html';
@@ -468,7 +468,7 @@ describe('ngInclude', function() {
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalled();
expect(autoScrollSpy.callCount).toBe(3);
@@ -484,7 +484,7 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -500,7 +500,7 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$apply(function() {
$rootScope.tpl = 'template.html';
@@ -522,7 +522,7 @@ describe('ngInclude', function() {
$rootScope.$apply("tpl = 'template.html'");
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}
+1 -1
View File
@@ -904,7 +904,7 @@ describe('ngModel', function() {
expect(function() {
scope.$apply('value = "123"');
}).toThrowMinErr("ngModel", "$asyncValidators",
}).toThrowMinErr("ngModel", "nopromise",
"Expected asynchronous validator to return a promise but got 'true' instead.");
}));
+1 -1
View File
@@ -1486,7 +1486,7 @@ describe('ngRepeat animations', function() {
$rootScope.$digest();
expect(element.text()).toBe('123'); // the original order should be preserved
$animate.triggerReflow();
$animate.flush();
$timeout.flush(1500); // 1s * 1.5 closing buffer
expect(element.text()).toBe('13');
+15
View File
@@ -204,6 +204,21 @@ describe('validators', function() {
expect($rootScope.form.test.$error.pattern).toBe(true);
expect(inputElm).not.toBeValid();
});
it('should validate the viewValue and not the modelValue', function() {
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="\\d{4}">');
var ctrl = inputElm.controller('ngModel');
ctrl.$parsers.push(function(value) {
return (value * 10) + '';
});
helper.changeInputValueTo('1234');
expect($rootScope.form.test.$error.pattern).not.toBe(true);
expect($rootScope.form.test.$modelValue).toBe('12340');
expect(inputElm).toBeValid();
});
});
+1 -1
View File
@@ -1657,7 +1657,7 @@ describe('$http', function() {
$http({method: 'GET', url: '/some', timeout: canceler.promise}).error(
function(data, status, headers, config) {
expect(data).toBeUndefined();
expect(status).toBe(0);
expect(status).toBe(-1);
expect(headers()).toEqual({});
expect(config.url).toBe('/some');
callback();
+49 -35
View File
@@ -46,7 +46,7 @@ describe('$location', function() {
it('should not include the drive name in path() on WIN', function() {
//See issue #4680 for details
var locationUrl = new LocationHashbangUrl('file:///base', '#!');
var locationUrl = new LocationHashbangUrl('file:///base', 'file:///', '#!');
locationUrl.$$parse('file:///base#!/foo?a=b&c#hash');
expect(locationUrl.path()).toBe('/foo');
@@ -54,7 +54,7 @@ describe('$location', function() {
it('should include the drive name if it was provided in the input url', function() {
var locationUrl = new LocationHashbangUrl('file:///base', '#!');
var locationUrl = new LocationHashbangUrl('file:///base', 'file:///', '#!');
locationUrl.$$parse('file:///base#!/C:/foo?a=b&c#hash');
expect(locationUrl.path()).toBe('/C:/foo');
@@ -64,7 +64,7 @@ describe('$location', function() {
describe('NewUrl', function() {
function createLocationHtml5Url() {
var locationUrl = new LocationHtml5Url('http://www.domain.com:9877/');
var locationUrl = new LocationHtml5Url('http://www.domain.com:9877/', 'http://www.domain.com:9877/');
locationUrl.$$parse('http://www.domain.com:9877/path/b?search=a&b=c&d#hash');
return locationUrl;
}
@@ -299,18 +299,18 @@ describe('$location', function() {
it('should parse new url', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/base');
expect(locationUrl.path()).toBe('/base');
locationUrl = new LocationHtml5Url('http://host.com/');
locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/base#');
expect(locationUrl.path()).toBe('/base');
});
it('should prefix path with forward-slash', function() {
var locationUrl = new LocationHtml5Url('http://server/');
var locationUrl = new LocationHtml5Url('http://server/', 'http://server/') ;
locationUrl.path('b');
expect(locationUrl.path()).toBe('/b');
@@ -319,7 +319,7 @@ describe('$location', function() {
it('should set path to forward-slash when empty', function() {
var locationUrl = new LocationHtml5Url('http://server/');
var locationUrl = new LocationHtml5Url('http://server/', 'http://server/') ;
locationUrl.$$parse('http://server/');
expect(locationUrl.path()).toBe('/');
expect(locationUrl.absUrl()).toBe('http://server/');
@@ -356,7 +356,7 @@ describe('$location', function() {
});
it('should prepend path with basePath', function() {
var locationUrl = new LocationHtml5Url('http://server/base/');
var locationUrl = new LocationHtml5Url('http://server/base/', 'http://server/base/') ;
locationUrl.$$parse('http://server/base/abc?a');
expect(locationUrl.path()).toBe('/abc');
expect(locationUrl.search()).toEqual({a: true});
@@ -367,7 +367,7 @@ describe('$location', function() {
it('should throw error when invalid server url given', function() {
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', '/base');
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', 'http://server.org/base/', '/base');
expect(function() {
locationUrl.$$parse('http://other.server.org/path#/path');
@@ -376,7 +376,7 @@ describe('$location', function() {
it('should throw error when invalid base url given', function() {
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', '/base');
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', 'http://server.org/base/', '/base');
expect(function() {
locationUrl.$$parse('http://server.org/path#/path');
@@ -444,7 +444,7 @@ describe('$location', function() {
it('should decode special characters', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/a%20%3C%3E%23?i%20j=%3C%3E%23#x%20%3C%3E%23');
expect(locationUrl.path()).toBe('/a <>#');
expect(locationUrl.search()).toEqual({'i j': '<>#'});
@@ -452,7 +452,7 @@ describe('$location', function() {
});
it('should decode pluses as spaces in urls', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/?a+b=c+d');
expect(locationUrl.search()).toEqual({'a b':'c d'});
});
@@ -470,7 +470,7 @@ describe('$location', function() {
describe('HashbangUrl', function() {
function createHashbangUrl() {
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base', '#!');
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base', 'http://www.server.org:1234/', '#!');
locationUrl.$$parse('http://www.server.org:1234/base#!/path?a=b&c#hash');
return locationUrl;
}
@@ -499,7 +499,7 @@ describe('$location', function() {
it('should preserve query params in base', function() {
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base?base=param', '#');
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base?base=param', 'http://www.server.org:1234/', '#');
locationUrl.$$parse('http://www.server.org:1234/base?base=param#/path?a=b&c#hash');
expect(locationUrl.absUrl()).toBe('http://www.server.org:1234/base?base=param#/path?a=b&c#hash');
@@ -511,7 +511,7 @@ describe('$location', function() {
it('should prefix path with forward-slash', function() {
var locationUrl = new LocationHashbangUrl('http://host.com/base', '#');
var locationUrl = new LocationHashbangUrl('http://host.com/base', 'http://host.com/', '#');
locationUrl.$$parse('http://host.com/base#path');
expect(locationUrl.path()).toBe('/path');
expect(locationUrl.absUrl()).toBe('http://host.com/base#/path');
@@ -523,7 +523,7 @@ describe('$location', function() {
it('should set path to forward-slash when empty', function() {
var locationUrl = new LocationHashbangUrl('http://server/base', '#!');
var locationUrl = new LocationHashbangUrl('http://server/base', 'http://server/', '#!');
locationUrl.$$parse('http://server/base');
locationUrl.path('aaa');
@@ -592,7 +592,7 @@ describe('$location', function() {
it('should decode special characters', function() {
var locationUrl = new LocationHashbangUrl('http://host.com/a', '#');
var locationUrl = new LocationHashbangUrl('http://host.com/a', 'http://host.com/', '#');
locationUrl.$$parse('http://host.com/a#/%20%3C%3E%23?i%20j=%3C%3E%23#x%20%3C%3E%23');
expect(locationUrl.path()).toBe('/ <>#');
expect(locationUrl.search()).toEqual({'i j': '<>#'});
@@ -601,35 +601,35 @@ describe('$location', function() {
it('should return decoded characters for search specified in URL', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/?q=1%2F2%203');
expect(locationUrl.search()).toEqual({'q': '1/2 3'});
});
it('should return decoded characters for search specified with setter', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/');
locationUrl.search('q', '1/2 3');
expect(locationUrl.search()).toEqual({'q': '1/2 3'});
});
it('should return an array for duplicate params', function() {
var locationUrl = new LocationHtml5Url('http://host.com');
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com') ;
locationUrl.$$parse('http://host.com');
locationUrl.search('q', ['1/2 3','4/5 6']);
expect(locationUrl.search()).toEqual({'q': ['1/2 3','4/5 6']});
});
it('should encode an array correctly from search and add to url', function() {
var locationUrl = new LocationHtml5Url('http://host.com');
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com') ;
locationUrl.$$parse('http://host.com');
locationUrl.search({'q': ['1/2 3','4/5 6']});
expect(locationUrl.absUrl()).toEqual('http://host.com?q=1%2F2%203&q=4%2F5%206');
});
it('should rewrite params when specifing a single param in search', function() {
var locationUrl = new LocationHtml5Url('http://host.com');
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com') ;
locationUrl.$$parse('http://host.com');
locationUrl.search({'q': '1/2 3'});
expect(locationUrl.absUrl()).toEqual('http://host.com?q=1%2F2%203');
@@ -860,7 +860,6 @@ describe('$location', function() {
});
});
// location.href = '...' fires hashchange event synchronously, so it might happen inside $apply
it('should not $apply when browser url changed inside $apply', function() {
initService({html5Mode:false,hashPrefix: '!',supportHistory: true});
@@ -1150,6 +1149,19 @@ describe('$location', function() {
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b/bar', false, null]);
});
});
it('should force a page reload if navigating outside of the application base href', function() {
initService({html5Mode:true, supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b/', baseHref:'/a/b/'});
inject(function($window, $browser, $location) {
$window.location.href = 'http://new.com/a/outside.html';
spyOn($window.location, '$$setHref');
expect($window.location.$$setHref).not.toHaveBeenCalled();
$browser.$$checkUrlChange();
expect($window.location.$$setHref).toHaveBeenCalledWith('http://new.com/a/outside.html');
});
});
});
@@ -2353,8 +2365,8 @@ describe('$location', function() {
var locationUrl, locationIndexUrl;
beforeEach(function() {
locationUrl = new LocationHtml5Url('http://server/pre/', 'http://server/pre/path');
locationIndexUrl = new LocationHtml5Url('http://server/pre/index.html', 'http://server/pre/path');
locationUrl = new LocationHtml5Url('http://server/pre/', 'http://server/pre/', 'http://server/pre/path');
locationIndexUrl = new LocationHtml5Url('http://server/pre/index.html', 'http://server/pre/', 'http://server/pre/path');
});
it('should rewrite URL', function() {
@@ -2416,7 +2428,7 @@ describe('$location', function() {
it('should rewrite URL', function() {
/* jshint scripturl: true */
locationUrl = new LocationHashbangUrl('http://server/pre/', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/', 'http://server/pre/', '#');
expect(parseLinkAndReturn(locationUrl, 'http://other')).toEqual(undefined);
expect(parseLinkAndReturn(locationUrl, 'http://server/pre/')).toEqual('http://server/pre/');
@@ -2425,7 +2437,7 @@ describe('$location', function() {
});
it("should not set hash if one was not originally specified", function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html');
expect(locationUrl.url()).toBe('');
@@ -2433,7 +2445,7 @@ describe('$location', function() {
});
it("should parse hash if one was specified", function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html#/foo/bar');
expect(locationUrl.url()).toBe('/foo/bar');
@@ -2442,7 +2454,7 @@ describe('$location', function() {
it("should prefix hash url with / if one was originally missing", function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html#not-starting-with-slash');
expect(locationUrl.url()).toBe('/not-starting-with-slash');
@@ -2452,7 +2464,7 @@ describe('$location', function() {
it('should not strip stuff from path just because it looks like Windows drive when it\'s not',
function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html#http%3A%2F%2Fexample.com%2F');
expect(locationUrl.url()).toBe('/http://example.com/');
@@ -2464,7 +2476,7 @@ describe('$location', function() {
});
it('should allow navigating outside the original base URL', function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/next/index.html');
expect(locationUrl.url()).toBe('');
@@ -2478,8 +2490,8 @@ describe('$location', function() {
var locationUrl, locationIndexUrl;
beforeEach(function() {
locationUrl = new LocationHashbangInHtml5Url('http://server/pre/', '#!');
locationIndexUrl = new LocationHashbangInHtml5Url('http://server/pre/index.html', '#!');
locationUrl = new LocationHashbangInHtml5Url('http://server/pre/', 'http://server/pre/', '#!');
locationIndexUrl = new LocationHashbangInHtml5Url('http://server/pre/index.html', 'http://server/pre/', '#!');
});
it('should rewrite URL', function() {
@@ -2540,8 +2552,10 @@ describe('$location', function() {
win.addEventListener = angular.noop;
win.removeEventListener = angular.noop;
win.location = {
get href() { return parser.href; },
set href(val) { parser.href = val; },
get href() { return this.$$getHref(); },
$$getHref: function() { return parser.href; },
set href(val) { this.$$setHref(val); },
$$setHref: function(val) { parser.href = val; },
get hash() { return parser.hash; },
// The parser correctly strips on a single preceding hash character if necessary
// before joining the fragment onto the href by a new hash character
+23
View File
@@ -1190,6 +1190,20 @@ describe('parser', function() {
scope.$eval('{}["__proto__"].foo = 1');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('{}[["__proto__"]]');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('{}[["__proto__"]].foo = 1');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('0[["__proto__"]]');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('0[["__proto__"]].foo = 1');
}).toThrowMinErr('$parse', 'isecfld');
scope.a = "__pro";
scope.b = "to__";
expect(function() {
@@ -1201,6 +1215,15 @@ describe('parser', function() {
});
});
it('should prevent the exploit', function() {
expect(function() {
scope.$eval('(1)[{0: "__proto__", 1: "__proto__", 2: "__proto__", 3: "safe", length: 4, toString: [].pop}].foo = 1');
});
if (!msie || msie > 10) {
expect((1)['__proto__'].foo).toBeUndefined();
}
});
it('should prevent the exploit', function() {
expect(function() {
scope.$eval('' +
File diff suppressed because it is too large Load Diff
+179
View File
@@ -1828,6 +1828,185 @@ describe('ngMockE2E', function() {
}));
});
});
describe('ngAnimateMock', function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
var ss, element, trackedAnimations;
beforeEach(module(function($animateProvider) {
trackedAnimations = [];
$animateProvider.register('.animate', function($timeout) {
return {
leave: logFn('leave'),
addClass: logFn('addClass')
};
function logFn(method) {
return function(element) {
trackedAnimations.push(getDoneCallback(arguments));
// this will never finish an animation so we'll issue a call
// to timeout so that the mock driver won't throw an exception
$timeout(angular.noop, 0, false);
};
}
function getDoneCallback(args) {
for (var i = args.length; i > 0; i--) {
if (angular.isFunction(args[i])) return args[i];
}
}
});
return function($animate, $rootElement, $document, $rootScope, $window) {
if (ss) {
ss.destroy();
}
ss = createMockStyleSheet($document, $window);
element = angular.element('<div class="animate"></div>');
$rootElement.append(element);
angular.element($document[0].body).append($rootElement);
$animate.enabled(true);
$rootScope.$digest();
};
}));
describe('$animate.queue', function() {
it('should maintain a queue of the executed animations', inject(function($animate) {
element.removeClass('animate'); // we don't care to test any actual animations
var options = {};
$animate.addClass(element, 'on', options);
var first = $animate.queue[0];
expect(first.element).toBe(element);
expect(first.event).toBe('addClass');
expect(first.options).toBe(options);
$animate.removeClass(element, 'off', options);
var second = $animate.queue[1];
expect(second.element).toBe(element);
expect(second.event).toBe('removeClass');
expect(second.options).toBe(options);
$animate.leave(element, options);
var third = $animate.queue[2];
expect(third.element).toBe(element);
expect(third.event).toBe('leave');
expect(third.options).toBe(options);
}));
});
describe('$animate.flush()', function() {
it('should throw an error if there is nothing to animate', inject(function($animate) {
expect(function() {
$animate.flush();
}).toThrow('No pending animations ready to be closed or flushed');
}));
it('should trigger the animation to start',
inject(function($animate) {
expect(trackedAnimations.length).toBe(0);
$animate.leave(element);
$animate.flush();
expect(trackedAnimations.length).toBe(1);
}));
it('should trigger the animation to end once run and called',
inject(function($animate) {
$animate.leave(element);
$animate.flush();
expect(element.parent().length).toBe(1);
trackedAnimations[0]();
$animate.flush();
expect(element.parent().length).toBe(0);
}));
it('should trigger the animation promise callback to fire once run and closed',
inject(function($animate) {
var doneSpy = jasmine.createSpy();
$animate.leave(element).then(doneSpy);
$animate.flush();
trackedAnimations[0]();
expect(doneSpy).not.toHaveBeenCalled();
$animate.flush();
expect(doneSpy).toHaveBeenCalled();
}));
it('should trigger a series of CSS animations to trigger and start once run',
inject(function($animate, $rootScope) {
if (!browserSupportsCssAnimations()) return;
ss.addRule('.leave-me.ng-leave', 'transition:1s linear all;');
var i, elm, elms = [];
for (i = 0; i < 5; i++) {
elm = angular.element('<div class="leave-me"></div>');
element.append(elm);
elms.push(elm);
$animate.leave(elm);
}
$rootScope.$digest();
for (i = 0; i < 5; i++) {
elm = elms[i];
expect(elm.hasClass('ng-leave')).toBe(true);
expect(elm.hasClass('ng-leave-active')).toBe(false);
}
$animate.flush();
for (i = 0; i < 5; i++) {
elm = elms[i];
expect(elm.hasClass('ng-leave')).toBe(true);
expect(elm.hasClass('ng-leave-active')).toBe(true);
}
}));
it('should trigger parent and child animations to run within the same flush',
inject(function($animate, $rootScope) {
var child = angular.element('<div class="animate child"></div>');
element.append(child);
expect(trackedAnimations.length).toBe(0);
$animate.addClass(element, 'go');
$animate.addClass(child, 'start');
$animate.flush();
expect(trackedAnimations.length).toBe(2);
}));
it('should trigger animation callbacks when called',
inject(function($animate, $rootScope) {
var spy = jasmine.createSpy();
element.on('$animate:before', spy);
element.on('$animate:close', spy);
$animate.addClass(element, 'on');
expect(spy).not.toHaveBeenCalled();
$animate.flush();
expect(spy.callCount).toBe(1);
trackedAnimations[0]();
$animate.flush();
expect(spy.callCount).toBe(2);
}));
});
});
});
describe('make sure that we can create an injector outside of tests', function() {
+7 -7
View File
@@ -712,7 +712,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
$animate.triggerCallbacks();
$animate.flush();
$location.path('/');
$rootScope.$digest();
@@ -776,7 +776,7 @@ describe('ngView animations', function() {
expect($animate.queue.shift().event).toBe('addClass');
expect($animate.queue.shift().event).toBe('removeClass');
$animate.triggerReflow();
$animate.flush();
expect(item.hasClass('classy')).toBe(false);
expect(item.hasClass('boring')).toBe(true);
@@ -914,7 +914,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -928,7 +928,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -941,7 +941,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -955,7 +955,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -972,7 +972,7 @@ describe('ngView animations', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect($animate.enter).toHaveBeenCalledOnce();
expect(autoScrollSpy).toHaveBeenCalledOnce();