From d21acafa7751da92856052fc44d36d38120444ea Mon Sep 17 00:00:00 2001 From: Leonardo Souza Date: Mon, 24 Apr 2017 07:59:20 -0300 Subject: [PATCH 001/599] docs(guide/interpolation): fix typo in markdown (code highlight) Closes #15935 --- docs/content/guide/interpolation.ngdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/guide/interpolation.ngdoc b/docs/content/guide/interpolation.ngdoc index 0cf11370b431..22c69a125155 100644 --- a/docs/content/guide/interpolation.ngdoc +++ b/docs/content/guide/interpolation.ngdoc @@ -32,7 +32,7 @@ If the interpolated value is not a `String`, it is computed as follows: - `undefined` and `null` are converted to `''` - if the value is an object that is not a `Number`, `Date` or `Array`, $interpolate looks for a custom `toString()` function on the object, and uses that. Custom means that -`myObject.toString !== `Object.prototype.toString`. +`myObject.toString !== Object.prototype.toString`. - if the above doesn't apply, `JSON.stringify` is used. ### Binding to boolean attributes From 2a8a29ac43361fe6d6d1fa6ef3cf8113d5359e2f Mon Sep 17 00:00:00 2001 From: Valentin Date: Wed, 3 May 2017 22:30:04 +0300 Subject: [PATCH 002/599] docs(guide/templates): add missing closing ` ``` From 1069086299fd784fae6cb16c59036d7f328b8d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Sat, 6 May 2017 17:50:42 +0200 Subject: [PATCH 003/599] chore(*): update all Karma-related packages except Karma The updated karma-chrome-launcher adds support for ChromeHeadless & ChromeCanaryHeadless launchers; test with: karma start karma-jqlite.conf.js --browsers=ChromeCanaryHeadless The updated karma-firefox-launcher disables multi-process which may increase stability on Jenkins. Closes #15966 --- package.json | 14 ++-- yarn.lock | 181 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 124 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 64a2bcb12efb..3ecc772b7123 100644 --- a/package.json +++ b/package.json @@ -60,14 +60,14 @@ "jasmine-node": "^2.0.0", "jasmine-reporters": "^2.2.0", "jquery": "^3.2.1", - "karma": "^1.1.2", - "karma-browserstack-launcher": "^1.0.1", - "karma-chrome-launcher": "^1.0.1", - "karma-firefox-launcher": "^1.0.0", - "karma-jasmine": "^1.0.2", - "karma-junit-reporter": "^1.1.0", + "karma": "^1.7.0", + "karma-browserstack-launcher": "^1.2.0", + "karma-chrome-launcher": "^2.1.1", + "karma-firefox-launcher": "^1.0.1", + "karma-jasmine": "^1.1.0", + "karma-junit-reporter": "^1.2.0", "karma-ng-scenario": "^1.0.0", - "karma-sauce-launcher": "^1.0.0", + "karma-sauce-launcher": "^1.1.0", "karma-script-launcher": "^1.0.0", "load-grunt-tasks": "^3.5.0", "lodash": "~2.4.1", diff --git a/yarn.lock b/yarn.lock index 60ae25c1d951..16e902960683 100644 --- a/yarn.lock +++ b/yarn.lock @@ -482,18 +482,18 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" -body-parser@^1.12.4: - version "1.16.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.16.0.tgz#924a5e472c6229fb9d69b85a20d5f2532dec788b" +body-parser@^1.16.1: + version "1.17.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.1.tgz#75b3bc98ddd6e7e0d8ffe750dfaca5c66993fa47" dependencies: bytes "2.4.0" content-type "~1.0.2" - debug "2.6.0" + debug "2.6.1" depd "~1.1.0" - http-errors "~1.5.1" + http-errors "~1.6.1" iconv-lite "0.4.15" on-finished "~2.3.0" - qs "6.2.1" + qs "6.4.0" raw-body "~2.2.0" type-is "~1.6.14" @@ -1235,7 +1235,7 @@ connect-livereload@^0.5.0: version "0.5.4" resolved "https://registry.yarnpkg.com/connect-livereload/-/connect-livereload-0.5.4.tgz#80157d1371c9f37cc14039ab1895970d119dc3bc" -connect@^3.3.5, connect@^3.4.0: +connect@^3.4.0: version "3.5.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198" dependencies: @@ -1244,6 +1244,15 @@ connect@^3.3.5, connect@^3.4.0: parseurl "~1.3.1" utils-merge "1.0.0" +connect@^3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.1.tgz#b7760693a74f0454face1d9378edb3f885b43227" + dependencies: + debug "2.6.3" + finalhandler "1.0.1" + parseurl "~1.3.1" + utils-merge "1.0.0" + console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -1482,6 +1491,18 @@ debug@2.3.3: dependencies: ms "0.7.2" +debug@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" + dependencies: + ms "0.7.2" + +debug@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" + dependencies: + ms "0.7.2" + decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1556,7 +1577,7 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@~1.1.0: +depd@1.1.0, depd@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" @@ -1773,9 +1794,9 @@ end-of-stream@~0.1.5: dependencies: once "~1.3.0" -engine.io-client@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.2.tgz#c38767547f2a7d184f5752f6f0ad501006703766" +engine.io-client@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" dependencies: component-emitter "1.2.1" component-inherit "0.0.3" @@ -1786,7 +1807,7 @@ engine.io-client@1.8.2: parsejson "0.0.3" parseqs "0.0.5" parseuri "0.0.5" - ws "1.1.1" + ws "1.1.2" xmlhttprequest-ssl "1.5.3" yeast "0.1.2" @@ -1801,16 +1822,16 @@ engine.io-parser@1.3.2: has-binary "0.1.7" wtf-8 "1.0.0" -engine.io@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.2.tgz#6b59be730b348c0125b0a4589de1c355abcf7a7e" +engine.io@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4" dependencies: accepts "1.3.3" base64id "1.0.0" cookie "0.3.1" debug "2.3.3" engine.io-parser "1.3.2" - ws "1.1.1" + ws "1.1.2" ent@~2.2.0: version "2.2.0" @@ -2210,6 +2231,18 @@ finalhandler@0.5.1: statuses "~1.3.1" unpipe "~1.0.0" +finalhandler@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.1.tgz#bcd15d1689c0e5ed729b6f7f541a6df984117db8" + dependencies: + debug "2.6.3" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.1" + statuses "~1.3.1" + unpipe "~1.0.0" + find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" @@ -3013,6 +3046,15 @@ http-errors@~1.5.0, http-errors@~1.5.1: setprototypeof "1.0.2" statuses ">= 1.3.1 < 2" +http-errors@~1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" + dependencies: + depd "1.1.0" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + http-proxy@^1.13.0: version "1.16.2" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" @@ -3570,7 +3612,7 @@ junk@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/junk/-/junk-1.0.3.tgz#87be63488649cbdca6f53ab39bec9ccd2347f592" -karma-browserstack-launcher@^1.0.1: +karma-browserstack-launcher@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/karma-browserstack-launcher/-/karma-browserstack-launcher-1.2.0.tgz#acfa534835ba590041eef009c1169a219120bb5b" dependencies: @@ -3578,22 +3620,22 @@ karma-browserstack-launcher@^1.0.1: browserstacktunnel-wrapper "~1.4.2" q "~1.4.1" -karma-chrome-launcher@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-1.0.1.tgz#be5ae7c4264f9a0a2e22e3d984beb325ad92c8cb" +karma-chrome-launcher@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf" dependencies: fs-access "^1.0.0" which "^1.2.1" -karma-firefox-launcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-1.0.0.tgz#e08af3ce42e39860c2952ea7b7eaa64d63508bdc" +karma-firefox-launcher@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-1.0.1.tgz#ce58f47c2013a88156d55a5d61337c099cf5bb51" -karma-jasmine@^1.0.2: +karma-jasmine@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" -karma-junit-reporter@^1.1.0: +karma-junit-reporter@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/karma-junit-reporter/-/karma-junit-reporter-1.2.0.tgz#4f9c40cedfb1a395f8aef876abf96189917c6396" dependencies: @@ -3604,7 +3646,7 @@ karma-ng-scenario@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/karma-ng-scenario/-/karma-ng-scenario-1.0.0.tgz#03315b27ee866f40443cf88bfebf7963f86543e1" -karma-sauce-launcher@^1.0.0: +karma-sauce-launcher@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/karma-sauce-launcher/-/karma-sauce-launcher-1.1.0.tgz#3d083cf5659d6736ab97bcee5d8acd86ad522212" dependencies: @@ -3617,16 +3659,16 @@ karma-script-launcher@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz#cd017c4de5ef09e5a9da793276176108dd4b542d" -karma@^1.1.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-1.4.1.tgz#41981a71d54237606b0a3ea8c58c90773f41650e" +karma@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.0.tgz#6f7a1a406446fa2e187ec95398698f4cee476269" dependencies: bluebird "^3.3.0" - body-parser "^1.12.4" + body-parser "^1.16.1" chokidar "^1.4.1" colors "^1.1.0" combine-lists "^1.0.0" - connect "^3.3.5" + connect "^3.6.0" core-js "^2.2.0" di "^0.0.1" dom-serialize "^2.2.0" @@ -3638,16 +3680,16 @@ karma@^1.1.2: lodash "^3.8.0" log4js "^0.6.31" mime "^1.3.4" - minimatch "^3.0.0" + minimatch "^3.0.2" optimist "^0.6.1" qjobs "^1.1.4" range-parser "^1.2.0" - rimraf "^2.3.3" + rimraf "^2.6.0" safe-buffer "^5.0.1" - socket.io "1.7.2" + socket.io "1.7.3" source-map "^0.5.3" - tmp "0.0.28" - useragent "^2.1.10" + tmp "0.0.31" + useragent "^2.1.12" kind-of@^3.0.2: version "3.1.0" @@ -5067,9 +5109,9 @@ qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" -qs@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625" +qs@6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" qs@^1.2.1, qs@~1.2.0: version "1.2.2" @@ -5441,7 +5483,7 @@ right-pad@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" -rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@~2.5.1, rimraf@~2.5.4: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -5453,6 +5495,12 @@ rimraf@2.4.3: dependencies: glob "^5.0.14" +rimraf@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + rimraf@~2.2.0, rimraf@~2.2.8: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" @@ -5510,13 +5558,7 @@ sauce-connect-launcher@^0.17.0: lodash "3.10.1" rimraf "2.4.3" -saucelabs@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.4.0.tgz#b934a9af9da2874b3f40aae1fcde50a4466f5f38" - dependencies: - https-proxy-agent "^1.0.0" - -saucelabs@~1.3.0: +saucelabs@^1.3.0, saucelabs@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.3.0.tgz#d240e8009df7fa87306ec4578a69ba3b5c424fee" dependencies: @@ -5647,6 +5689,10 @@ setprototypeof@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + sha.js@^2.3.6, sha.js@~2.4.4: version "2.4.8" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" @@ -5762,15 +5808,15 @@ socket.io-adapter@0.5.0: debug "2.3.3" socket.io-parser "2.3.1" -socket.io-client@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.2.tgz#39fdb0c3dd450e321b7e40cfd83612ec533dd644" +socket.io-client@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377" dependencies: backo2 "1.0.2" component-bind "1.0.0" component-emitter "1.2.1" debug "2.3.3" - engine.io-client "1.8.2" + engine.io-client "1.8.3" has-binary "0.1.7" indexof "0.0.1" object-component "0.0.3" @@ -5787,16 +5833,16 @@ socket.io-parser@2.3.1: isarray "0.0.1" json3 "3.3.2" -socket.io@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.2.tgz#83bbbdf2e79263b378900da403e7843e05dc3b71" +socket.io@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b" dependencies: debug "2.3.3" - engine.io "1.8.2" + engine.io "1.8.3" has-binary "0.1.7" object-assign "4.1.0" socket.io-adapter "0.5.0" - socket.io-client "1.7.2" + socket.io-client "1.7.3" socket.io-parser "2.3.1" sorted-object@^1.0.0: @@ -6214,13 +6260,13 @@ tmp@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" -tmp@0.0.28, tmp@0.0.x: - version "0.0.28" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" +tmp@0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" dependencies: os-tmpdir "~1.0.1" -tmp@^0.0.29: +tmp@0.0.x, tmp@^0.0.29: version "0.0.29" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" dependencies: @@ -6460,9 +6506,9 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" -useragent@^2.1.10: - version "2.1.12" - resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.12.tgz#aa7da6cdc48bdc37ba86790871a7321d64edbaa2" +useragent@^2.1.12: + version "2.1.13" + resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.13.tgz#bba43e8aa24d5ceb83c2937473e102e21df74c10" dependencies: lru-cache "2.2.x" tmp "0.0.x" @@ -6700,7 +6746,14 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@1.1.1, ws@^1.0.1: +ws@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +ws@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018" dependencies: From 301fdda648680d89ccab607c413a7ddede7b0165 Mon Sep 17 00:00:00 2001 From: Lucas Mirelmann Date: Sun, 7 May 2017 18:05:20 +0200 Subject: [PATCH 004/599] fix(httpParamSerializerJQLike): Follow jQuery for `null` and `undefined` Follow jQuery when serializing `null` and `undefined`. Closes: #15969 Closes: #15971 --- src/ng/http.js | 4 ++-- test/ng/httpSpec.js | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ng/http.js b/src/ng/http.js index 6c9785e5ac06..9262e3dec5ea 100644 --- a/src/ng/http.js +++ b/src/ng/http.js @@ -110,7 +110,6 @@ function $HttpParamSerializerJQLikeProvider() { return parts.join('&'); function serialize(toSerialize, prefix, topLevel) { - if (toSerialize === null || isUndefined(toSerialize)) return; if (isArray(toSerialize)) { forEach(toSerialize, function(value, index) { serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']'); @@ -123,7 +122,8 @@ function $HttpParamSerializerJQLikeProvider() { (topLevel ? '' : ']')); }); } else { - parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize))); + parts.push(encodeUriQuery(prefix) + '=' + + (toSerialize == null ? '' : encodeUriQuery(serializeValue(toSerialize)))); } } }; diff --git a/test/ng/httpSpec.js b/test/ng/httpSpec.js index f03582bdde23..4cab7d53ac71 100644 --- a/test/ng/httpSpec.js +++ b/test/ng/httpSpec.js @@ -2306,5 +2306,11 @@ describe('$http param serializers', function() { 'a%5B%5D=b&a%5B%5D=c&d%5B0%5D%5Be%5D=f&d%5B0%5D%5Bg%5D=h&d%5B%5D=i&d%5B2%5D%5Bj%5D=k'); //a[]=b&a[]=c&d[0][e]=f&d[0][g]=h&d[]=i&d[2][j]=k }); + + it('should serialize `null` and `undefined` elements as empty', function() { + expect(jqrSer({items:['foo', 'bar', null, undefined, 'baz'], x: null, y: undefined})).toEqual( + 'items%5B%5D=foo&items%5B%5D=bar&items%5B%5D=&items%5B%5D=&items%5B%5D=baz&x=&y='); + //items[]=foo&items[]=bar&items[]=&items[]=&items[]=baz&x=&y= + }); }); }); From 2f3bd89a5184e9140fc680a46a301caa22d14199 Mon Sep 17 00:00:00 2001 From: musclor Date: Mon, 8 May 2017 21:37:10 +0200 Subject: [PATCH 005/599] docs(guide/component): remove redundant unit test Fixes #15968 Closes #15974 --- docs/content/guide/component.ngdoc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/content/guide/component.ngdoc b/docs/content/guide/component.ngdoc index 24eb24be744c..9a74d3edbbc2 100644 --- a/docs/content/guide/component.ngdoc +++ b/docs/content/guide/component.ngdoc @@ -462,7 +462,7 @@ The examples use the [Jasmine](http://jasmine.github.io/) testing framework. **Controller Test:** ```js -describe('component: heroDetail', function() { +describe('HeroDetailController', function() { var $componentController; beforeEach(module('heroApp')); @@ -470,15 +470,6 @@ describe('component: heroDetail', function() { $componentController = _$componentController_; })); - it('should expose a `hero` object', function() { - // Here we are passing actual bindings to the component - var bindings = {hero: {name: 'Wolverine'}}; - var ctrl = $componentController('heroDetail', null, bindings); - - expect(ctrl.hero).toBeDefined(); - expect(ctrl.hero.name).toBe('Wolverine'); - }); - it('should call the `onDelete` binding, when deleting the hero', function() { var onDeleteSpy = jasmine.createSpy('onDelete'); var bindings = {hero: {}, onDelete: onDeleteSpy}; From 30eb1943419038250627fb93a7c8f411e6f79231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski?= Date: Tue, 9 May 2017 15:08:35 +0200 Subject: [PATCH 006/599] test(*): run class-related tests everywhere; fix eval syntax 1. Wrap an evaled class definition in parens; previously they weren't; the test wasn't failing only because it was disabled everywhere outside of Chrome and Chrome <59 incorrectly accepted such input. 2. There's no reason to restrict class-related tests just to Chrome; now they run in every browser that supports ES6 classes. The classes support test was modified to check not only if a class definition parses but also if it stringifies correctly which is required by AngularJS. This restriction disables class-related tests in current Firefox (53) but will work in v55 or newer. Closes #15967 --- test/.eslintrc.json | 3 ++- test/auto/injectorSpec.js | 40 ++++++++++++++------------------ test/helpers/testabilityPatch.js | 5 ++-- test/ng/compileSpec.js | 7 +++--- test/ngMock/angular-mocksSpec.js | 2 +- 5 files changed, 27 insertions(+), 30 deletions(-) diff --git a/test/.eslintrc.json b/test/.eslintrc.json index 007b458949ad..cb6b29f75dae 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -176,6 +176,7 @@ "browserSupportsCssAnimations": false, "browserTrigger": false, "jqLiteCacheSize": false, - "createAsync": false + "createAsync": false, + "support": false } } diff --git a/test/auto/injectorSpec.js b/test/auto/injectorSpec.js index 2cdb34ed1675..bf8fe284e7ed 100644 --- a/test/auto/injectorSpec.js +++ b/test/auto/injectorSpec.js @@ -1,7 +1,5 @@ 'use strict'; -/* globals support: false */ - describe('injector.modules', function() { it('should expose the loaded module info on the instance injector', function() { var test1 = angular.module('test1', ['test2']).info({ version: '1.1' }); @@ -327,26 +325,24 @@ describe('injector', function() { expect(instance.aVal()).toEqual('a-value'); }); - if (/chrome/.test(window.navigator.userAgent)) { - they('should detect ES6 classes regardless of whitespace/comments ($prop)', [ - 'class Test {}', - 'class Test{}', - 'class //<--ES6 stuff\nTest {}', - 'class//<--ES6 stuff\nTest {}', - 'class {}', - 'class{}', - 'class //<--ES6 stuff\n {}', - 'class//<--ES6 stuff\n {}', - 'class/* Test */{}', - 'class /* Test */ {}' - ], function(classDefinition) { - // eslint-disable-next-line no-eval - var Clazz = eval('(' + classDefinition + ')'); - var instance = injector.invoke(Clazz); - - expect(instance).toEqual(jasmine.any(Clazz)); - }); - } + they('should detect ES6 classes regardless of whitespace/comments ($prop)', [ + 'class Test {}', + 'class Test{}', + 'class //<--ES6 stuff\nTest {}', + 'class//<--ES6 stuff\nTest {}', + 'class {}', + 'class{}', + 'class //<--ES6 stuff\n {}', + 'class//<--ES6 stuff\n {}', + 'class/* Test */{}', + 'class /* Test */ {}' + ], function(classDefinition) { + // eslint-disable-next-line no-eval + var Clazz = eval('(' + classDefinition + ')'); + var instance = injector.invoke(Clazz); + + expect(instance).toEqual(jasmine.any(Clazz)); + }); } }); diff --git a/test/helpers/testabilityPatch.js b/test/helpers/testabilityPatch.js index 06ee2aa79399..87a4ecaeb534 100644 --- a/test/helpers/testabilityPatch.js +++ b/test/helpers/testabilityPatch.js @@ -4,7 +4,7 @@ if (window.bindJQuery) bindJQuery(); var supportTests = { - classes: '(class {})', + classes: '/^class\\b/.test((class C {}).toString())', fatArrow: 'a => a', ES6Function: '({ fn(x) { return; } })' }; @@ -15,8 +15,7 @@ for (var prop in supportTests) { if (supportTests.hasOwnProperty(prop)) { try { // eslint-disable-next-line no-eval - eval(supportTests[prop]); - support[prop] = true; + support[prop] = !!eval(supportTests[prop]); } catch (e) { support[prop] = false; } diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b87a7e7df9dd..62bd5841f3e8 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -6175,10 +6175,10 @@ describe('$compile', function() { }); it('should eventually expose isolate scope variables on ES6 class controller with controllerAs when bindToController is true', function() { - if (!/chrome/i.test(window.navigator.userAgent)) return; + if (!support.classes) return; var controllerCalled = false; // eslint-disable-next-line no-eval - var Controller = eval( + var Controller = eval('(\n' + 'class Foo {\n' + ' constructor($scope) {}\n' + ' $onInit() {\n' + @@ -6194,7 +6194,8 @@ describe('$compile', function() { ' expect(this.fn()).toBe(\'called!\');\n' + ' controllerCalled = true;\n' + ' }\n' + - '}'); + '}\n' + + ')'); spyOn(Controller.prototype, '$onInit').and.callThrough(); module(function($compileProvider) { diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index 4bfad9d3bb10..67f9674f8034 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -2100,7 +2100,7 @@ describe('ngMock', function() { ); - if (/chrome/.test(window.navigator.userAgent)) { + if (support.classes) { it('should support assigning bindings to class-based controller', function() { var called = false; var data = [ From d0d829899c4bcdf4b322d45245d7b811cd4cb855 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Tue, 9 May 2017 21:54:15 -0700 Subject: [PATCH 007/599] test($rootScope): add $watch/$watchCollection/$watchGroup tests Closes #15949 --- test/ng/rootScopeSpec.js | 763 ++++++++++++++++++++++++++++----------- 1 file changed, 544 insertions(+), 219 deletions(-) diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 364196a94b2b..92e713a5b99e 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -146,16 +146,6 @@ describe('Scope', function() { expect(spy).toHaveBeenCalled(); })); - it('should not keep constant expressions on watch queue', inject(function($rootScope) { - $rootScope.$watch('1 + 1', function() {}); - expect($rootScope.$$watchers.length).toEqual(1); - expect($rootScope.$$watchersCount).toEqual(1); - $rootScope.$digest(); - - expect($rootScope.$$watchers.length).toEqual(0); - expect($rootScope.$$watchersCount).toEqual(0); - })); - it('should decrement the watcherCount when destroying a child scope', inject(function($rootScope) { var child1 = $rootScope.$new(), child2 = $rootScope.$new(), @@ -212,54 +202,136 @@ describe('Scope', function() { expect($rootScope.$$watchersCount).toBe(2); })); - it('should not keep constant literals on the watch queue', inject(function($rootScope) { - $rootScope.$watch('[]', function() {}); - $rootScope.$watch('{}', function() {}); - expect($rootScope.$$watchers.length).toEqual(2); - $rootScope.$digest(); + describe('constants cleanup', function() { + it('should remove $watch of constant literals after initial digest', inject(function($rootScope) { + $rootScope.$watch('[]', function() {}); + $rootScope.$watch('{}', function() {}); + $rootScope.$watch('1', function() {}); + $rootScope.$watch('"foo"', function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(0); - })); + expect($rootScope.$$watchers.length).toEqual(0); + })); - it('should clean up stable watches on the watch queue', inject(function($rootScope) { - $rootScope.$watch('::foo', function() {}); - expect($rootScope.$$watchers.length).toEqual(1); + it('should remove $watchCollection of constant literals after initial digest', inject(function($rootScope) { + $rootScope.$watchCollection('[]', function() {}); + $rootScope.$watchCollection('{}', function() {}); + $rootScope.$watchCollection('1', function() {}); + $rootScope.$watchCollection('"foo"', function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(1); + expect($rootScope.$$watchers.length).toEqual(0); + })); - $rootScope.foo = 'foo'; - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(0); - })); + it('should remove $watchGroup of constant literals after initial digest', inject(function($rootScope) { + $rootScope.$watchGroup(['[]', '{}', '1', '"foo"'], function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); - it('should clean up stable watches from $watchCollection', inject(function($rootScope) { - $rootScope.$watchCollection('::foo', function() {}); - expect($rootScope.$$watchers.length).toEqual(1); + expect($rootScope.$$watchers.length).toEqual(0); + })); - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(1); + it('should remove $watch of filtered constant literals after initial digest', inject(function($rootScope) { + $rootScope.$watch('[1] | filter:"x"', function() {}); + $rootScope.$watch('1 | number:2', function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); - $rootScope.foo = []; - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(0); - })); + expect($rootScope.$$watchers.length).toEqual(0); + })); - it('should clean up stable watches from $watchGroup', inject(function($rootScope) { - $rootScope.$watchGroup(['::foo', '::bar'], function() {}); - expect($rootScope.$$watchers.length).toEqual(2); + it('should remove $watchCollection of filtered constant literals after initial digest', inject(function($rootScope) { + $rootScope.$watchCollection('[1] | filter:"x"', function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(2); + expect($rootScope.$$watchers.length).toEqual(0); + })); - $rootScope.foo = 'foo'; - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(1); + it('should remove $watchGroup of filtered constant literals after initial digest', inject(function($rootScope) { + $rootScope.$watchGroup(['[1] | filter:"x"', '1 | number:2'], function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); - $rootScope.bar = 'bar'; - $rootScope.$digest(); - expect($rootScope.$$watchers.length).toEqual(0); - })); + expect($rootScope.$$watchers.length).toEqual(0); + })); + + it('should remove $watch of constant expressions after initial digest', inject(function($rootScope) { + $rootScope.$watch('1 + 1', function() {}); + $rootScope.$watch('"a" + "b"', function() {}); + $rootScope.$watch('"ab".length', function() {}); + $rootScope.$watch('[].length', function() {}); + $rootScope.$watch('(1 + 1) | number:2', function() {}); + expect($rootScope.$$watchers.length).not.toEqual(0); + $rootScope.$digest(); + + expect($rootScope.$$watchers.length).toEqual(0); + })); + }); + + describe('onetime cleanup', function() { + it('should clean up stable watches on the watch queue', inject(function($rootScope) { + $rootScope.$watch('::foo', function() {}); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.foo = 'foo'; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(0); + })); + + it('should clean up stable watches from $watchCollection', inject(function($rootScope) { + $rootScope.$watchCollection('::foo', function() {}); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.foo = []; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(0); + })); + + it('should clean up stable watches from $watchCollection literals', inject(function($rootScope) { + $rootScope.$watchCollection('::[foo, bar]', function() {}); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.foo = 1; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.foo = 2; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.bar = 3; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(0); + })); + + it('should clean up stable watches from $watchGroup', inject(function($rootScope) { + $rootScope.$watchGroup(['::foo', '::bar'], function() {}); + expect($rootScope.$$watchers.length).toEqual(2); + + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(2); + + $rootScope.foo = 'foo'; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(1); + + $rootScope.bar = 'bar'; + $rootScope.$digest(); + expect($rootScope.$$watchers.length).toEqual(0); + })); + }); it('should delegate exceptions', function() { module(function($exceptionHandlerProvider) { @@ -689,249 +761,500 @@ describe('Scope', function() { })); }); - describe('$watchCollection', function() { - var log, $rootScope, deregister; + describe('variable', function() { + var log, $rootScope, deregister; - beforeEach(inject(function(_$rootScope_, _log_) { - $rootScope = _$rootScope_; - log = _log_; - deregister = $rootScope.$watchCollection('obj', function logger(newVal, oldVal) { - var msg = {newVal: newVal, oldVal: oldVal}; + beforeEach(inject(function(_$rootScope_, _log_) { + $rootScope = _$rootScope_; + log = _log_; + deregister = $rootScope.$watchCollection('obj', function logger(newVal, oldVal) { + var msg = {newVal: newVal, oldVal: oldVal}; - if (newVal === oldVal) { - msg.identical = true; - } + if (newVal === oldVal) { + msg.identical = true; + } - log(msg); - }); - })); + log(msg); + }); + })); - it('should not trigger if nothing change', inject(function($rootScope) { - $rootScope.$digest(); - expect(log).toEqual([{ newVal: undefined, oldVal: undefined, identical: true }]); - log.reset(); + it('should not trigger if nothing change', function() { + $rootScope.$digest(); + expect(log).toEqual([{ newVal: undefined, oldVal: undefined, identical: true }]); + log.reset(); - $rootScope.$digest(); - expect(log).toEqual([]); - })); + $rootScope.$digest(); + expect(log).toEqual([]); + }); - it('should allow deregistration', function() { - $rootScope.obj = []; - $rootScope.$digest(); - expect(log.toArray().length).toBe(1); - log.reset(); + it('should allow deregistration', function() { + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.toArray().length).toBe(1); + log.reset(); - $rootScope.obj.push('a'); - deregister(); + $rootScope.obj.push('a'); + deregister(); - $rootScope.$digest(); - expect(log).toEqual([]); - }); + $rootScope.$digest(); + expect(log).toEqual([]); + }); - describe('array', function() { + describe('array', function() { - it('should return oldCollection === newCollection only on the first listener call', - inject(function($rootScope, log) { + it('should return oldCollection === newCollection only on the first listener call', + inject(function($rootScope, log) { - // first time should be identical - $rootScope.obj = ['a', 'b']; - $rootScope.$digest(); - expect(log).toEqual([{newVal: ['a', 'b'], oldVal: ['a', 'b'], identical: true}]); - log.reset(); + // first time should be identical + $rootScope.obj = ['a', 'b']; + $rootScope.$digest(); + expect(log).toEqual([{newVal: ['a', 'b'], oldVal: ['a', 'b'], identical: true}]); + log.reset(); - // second time should be different - $rootScope.obj[1] = 'c'; - $rootScope.$digest(); - expect(log).toEqual([{newVal: ['a', 'c'], oldVal: ['a', 'b']}]); - })); + // second time should be different + $rootScope.obj[1] = 'c'; + $rootScope.$digest(); + expect(log).toEqual([{newVal: ['a', 'c'], oldVal: ['a', 'b']}]); + })); - it('should trigger when property changes into array', function() { - $rootScope.obj = 'test'; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: 'test', oldVal: 'test', identical: true}]); + it('should trigger when property changes into array', function() { + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: 'test', oldVal: 'test', identical: true}]); - $rootScope.obj = []; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: [], oldVal: 'test'}]); + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [], oldVal: 'test'}]); - $rootScope.obj = {}; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {}, oldVal: []}]); + $rootScope.obj = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {}, oldVal: []}]); - $rootScope.obj = []; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: [], oldVal: {}}]); + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [], oldVal: {}}]); - $rootScope.obj = undefined; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: undefined, oldVal: []}]); - }); + $rootScope.obj = undefined; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: undefined, oldVal: []}]); + }); - it('should not trigger change when object in collection changes', function() { - $rootScope.obj = [{}]; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: [{}], oldVal: [{}], identical: true}]); + it('should not trigger change when object in collection changes', function() { + $rootScope.obj = [{}]; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [{}], oldVal: [{}], identical: true}]); - $rootScope.obj[0].name = 'foo'; - $rootScope.$digest(); - expect(log).toEqual([]); - }); + $rootScope.obj[0].name = 'foo'; + $rootScope.$digest(); + expect(log).toEqual([]); + }); - it('should watch array properties', function() { - $rootScope.obj = []; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: [], oldVal: [], identical: true}]); + it('should watch array properties', function() { + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [], oldVal: [], identical: true}]); - $rootScope.obj.push('a'); - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: ['a'], oldVal: []}]); + $rootScope.obj.push('a'); + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: ['a'], oldVal: []}]); - $rootScope.obj[0] = 'b'; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: ['b'], oldVal: ['a']}]); + $rootScope.obj[0] = 'b'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: ['b'], oldVal: ['a']}]); - $rootScope.obj.push([]); - $rootScope.obj.push({}); - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: ['b', [], {}], oldVal: ['b']}]); + $rootScope.obj.push([]); + $rootScope.obj.push({}); + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: ['b', [], {}], oldVal: ['b']}]); - var temp = $rootScope.obj[1]; - $rootScope.obj[1] = $rootScope.obj[2]; - $rootScope.obj[2] = temp; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: ['b', {}, []], oldVal: ['b', [], {}]}]); + var temp = $rootScope.obj[1]; + $rootScope.obj[1] = $rootScope.obj[2]; + $rootScope.obj[2] = temp; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: ['b', {}, []], oldVal: ['b', [], {}]}]); - $rootScope.obj.shift(); - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: [{}, []], oldVal: ['b', {}, []]}]); - }); + $rootScope.obj.shift(); + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [{}, []], oldVal: ['b', {}, []]}]); + }); - it('should not infinitely digest when current value is NaN', function() { - $rootScope.obj = [NaN]; - expect(function() { + it('should not infinitely digest when current value is NaN', function() { + $rootScope.obj = [NaN]; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + }); + + it('should watch array-like objects like arrays', function() { + window.document.body.innerHTML = '

' + + 'a' + + 'b' + + '

'; + + $rootScope.obj = window.document.getElementsByTagName('a'); $rootScope.$digest(); - }).not.toThrow(); - }); - it('should watch array-like objects like arrays', function() { - var arrayLikelog = []; - $rootScope.$watchCollection('arrayLikeObject', function logger(obj) { - forEach(obj, function(element) { + var arrayLikelog = []; + forEach(log.empty()[0].newVal, function(element) { arrayLikelog.push(element.name); }); + expect(arrayLikelog).toEqual(['x', 'y']); }); - window.document.body.innerHTML = '

' + - 'a' + - 'b' + - '

'; + }); - $rootScope.arrayLikeObject = window.document.getElementsByTagName('a'); - $rootScope.$digest(); - expect(arrayLikelog).toEqual(['x', 'y']); + + describe('object', function() { + + it('should return oldCollection === newCollection only on the first listener call', function() { + + $rootScope.obj = {'a': 'b'}; + // first time should be identical + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': 'b'}, oldVal: {'a': 'b'}, identical: true}]); + + // second time not identical + $rootScope.obj.a = 'c'; + $rootScope.$digest(); + expect(log).toEqual([{newVal: {'a': 'c'}, oldVal: {'a': 'b'}}]); + }); + + + it('should trigger when property changes into object', function() { + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: 'test', oldVal: 'test', identical: true}]); + + $rootScope.obj = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {}, oldVal: 'test'}]); + }); + + + it('should not trigger change when object in collection changes', function() { + $rootScope.obj = {name: {}}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {name: {}}, oldVal: {name: {}}, identical: true}]); + + $rootScope.obj.name.bar = 'foo'; + $rootScope.$digest(); + expect(log.empty()).toEqual([]); + }); + + + it('should watch object properties', function() { + $rootScope.obj = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {}, oldVal: {}, identical: true}]); + + $rootScope.obj.a = 'A'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {a: 'A'}, oldVal: {}}]); + + $rootScope.obj.a = 'B'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {a: 'B'}, oldVal: {a: 'A'}}]); + + $rootScope.obj.b = []; + $rootScope.obj.c = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {a: 'B', b: [], c: {}}, oldVal: {a: 'B'}}]); + + var temp = $rootScope.obj.a; + $rootScope.obj.a = $rootScope.obj.b; + $rootScope.obj.c = temp; + $rootScope.$digest(); + expect(log.empty()). + toEqual([{newVal: {a: [], b: [], c: 'B'}, oldVal: {a: 'B', b: [], c: {}}}]); + + delete $rootScope.obj.a; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {b: [], c: 'B'}, oldVal: {a: [], b: [], c: 'B'}}]); + }); + + + it('should not infinitely digest when current value is NaN', function() { + $rootScope.obj = {a: NaN}; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + }); + + + it('should handle objects created using `Object.create(null)`', function() { + $rootScope.obj = Object.create(null); + $rootScope.obj.a = 'a'; + $rootScope.obj.b = 'b'; + $rootScope.$digest(); + expect(log.empty()[0].newVal).toEqual(extend(Object.create(null), {a: 'a', b: 'b'})); + + delete $rootScope.obj.b; + $rootScope.$digest(); + expect(log.empty()[0].newVal).toEqual(extend(Object.create(null), {a: 'a'})); + }); }); }); + describe('literal', function() { + describe('array', function() { + var log, $rootScope; - describe('object', function() { + beforeEach(inject(function(_$rootScope_, _log_) { + $rootScope = _$rootScope_; + log = _log_; + $rootScope.$watchCollection('[obj]', function logger(newVal, oldVal) { + var msg = {newVal: newVal, oldVal: oldVal}; - it('should return oldCollection === newCollection only on the first listener call', - inject(function($rootScope, log) { + if (newVal === oldVal) { + msg.identical = true; + } - $rootScope.obj = {'a': 'b'}; - // first time should be identical - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {'a': 'b'}, oldVal: {'a': 'b'}, identical: true}]); + log(msg); + }); + })); - // second time not identical - $rootScope.obj.a = 'c'; - $rootScope.$digest(); - expect(log).toEqual([{newVal: {'a': 'c'}, oldVal: {'a': 'b'}}]); - })); + it('should return oldCollection === newCollection only on the first listener call', function() { - it('should trigger when property changes into object', function() { - $rootScope.obj = 'test'; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: 'test', oldVal: 'test', identical: true}]); + // first time should be identical + $rootScope.obj = 'a'; + $rootScope.$digest(); + expect(log).toEqual([{newVal: ['a'], oldVal: ['a'], identical: true}]); + log.reset(); - $rootScope.obj = {}; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {}, oldVal: 'test'}]); - }); + // second time should be different + $rootScope.obj = 'b'; + $rootScope.$digest(); + expect(log).toEqual([{newVal: ['b'], oldVal: ['a']}]); + }); - it('should not trigger change when object in collection changes', function() { - $rootScope.obj = {name: {}}; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {name: {}}, oldVal: {name: {}}, identical: true}]); + it('should trigger when property changes into array', function() { + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: ['test'], oldVal: ['test'], identical: true}]); - $rootScope.obj.name.bar = 'foo'; - $rootScope.$digest(); - expect(log.empty()).toEqual([]); - }); + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [[]], oldVal: ['test']}]); + $rootScope.obj = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [{}], oldVal: [[]]}]); - it('should watch object properties', function() { - $rootScope.obj = {}; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {}, oldVal: {}, identical: true}]); + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [[]], oldVal: [{}]}]); - $rootScope.obj.a = 'A'; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {a: 'A'}, oldVal: {}}]); + $rootScope.obj = undefined; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [undefined], oldVal: [[]]}]); + }); - $rootScope.obj.a = 'B'; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {a: 'B'}, oldVal: {a: 'A'}}]); - $rootScope.obj.b = []; - $rootScope.obj.c = {}; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {a: 'B', b: [], c: {}}, oldVal: {a: 'B'}}]); + it('should not trigger change when object in collection changes', function() { + $rootScope.obj = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: [{}], oldVal: [{}], identical: true}]); - var temp = $rootScope.obj.a; - $rootScope.obj.a = $rootScope.obj.b; - $rootScope.obj.c = temp; - $rootScope.$digest(); - expect(log.empty()). - toEqual([{newVal: {a: [], b: [], c: 'B'}, oldVal: {a: 'B', b: [], c: {}}}]); + $rootScope.obj.name = 'foo'; + $rootScope.$digest(); + expect(log).toEqual([]); + }); - delete $rootScope.obj.a; - $rootScope.$digest(); - expect(log.empty()).toEqual([{newVal: {b: [], c: 'B'}, oldVal: {a: [], b: [], c: 'B'}}]); + + it('should not infinitely digest when current value is NaN', function() { + $rootScope.obj = NaN; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + }); }); - it('should not infinitely digest when current value is NaN', function() { - $rootScope.obj = {a: NaN}; - expect(function() { + describe('object', function() { + var log, $rootScope; + + beforeEach(inject(function(_$rootScope_, _log_) { + $rootScope = _$rootScope_; + log = _log_; + $rootScope.$watchCollection('{a: obj}', function logger(newVal, oldVal) { + var msg = {newVal: newVal, oldVal: oldVal}; + + if (newVal === oldVal) { + msg.identical = true; + } + + log(msg); + }); + })); + + it('should return oldCollection === newCollection only on the first listener call', function() { + + $rootScope.obj = 'b'; + // first time should be identical + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': 'b'}, oldVal: {'a': 'b'}, identical: true}]); + + // second time not identical + $rootScope.obj = 'c'; + $rootScope.$digest(); + expect(log).toEqual([{newVal: {'a': 'c'}, oldVal: {'a': 'b'}}]); + }); + + + it('should trigger when property changes into object', function() { + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': 'test'}, oldVal: {'a': 'test'}, identical: true}]); + + $rootScope.obj = {}; $rootScope.$digest(); - }).not.toThrow(); + expect(log.empty()).toEqual([{newVal: {'a': {}}, oldVal: {'a': 'test'}}]); + }); + + + it('should not trigger change when object in collection changes', function() { + $rootScope.obj = {name: 'foo'}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': {name: 'foo'}}, oldVal: {'a': {name: 'foo'}}, identical: true}]); + + $rootScope.obj.name = 'bar'; + $rootScope.$digest(); + expect(log.empty()).toEqual([]); + }); + + + it('should watch object properties', function() { + $rootScope.obj = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': {}}, oldVal: {'a': {}}, identical: true}]); + + $rootScope.obj = 'A'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': 'A'}, oldVal: {'a': {}}}]); + + $rootScope.obj = 'B'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {a: 'B'}, oldVal: {a: 'A'}}]); + + $rootScope.obj = []; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {a: []}, oldVal: {a: 'B'}}]); + + delete $rootScope.obj; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {a: undefined}, oldVal: {a: []}}]); + }); + + + it('should not infinitely digest when current value is NaN', function() { + $rootScope.obj = NaN; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + }); }); - it('should handle objects created using `Object.create(null)`', function() { - $rootScope.obj = Object.create(null); - $rootScope.obj.a = 'a'; - $rootScope.obj.b = 'b'; - $rootScope.$digest(); - expect(log.empty()[0].newVal).toEqual(extend(Object.create(null), {a: 'a', b: 'b'})); + describe('object computed property', function() { + var log, $rootScope; - delete $rootScope.obj.b; - $rootScope.$digest(); - expect(log.empty()[0].newVal).toEqual(extend(Object.create(null), {a: 'a'})); + beforeEach(inject(function(_$rootScope_, _log_) { + $rootScope = _$rootScope_; + log = _log_; + $rootScope.$watchCollection('{[key]: obj}', function logger(newVal, oldVal) { + var msg = {newVal: newVal, oldVal: oldVal}; + + if (newVal === oldVal) { + msg.identical = true; + } + + log(msg); + }); + })); + + + it('should default to "undefined" key', function() { + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'undefined': 'test'}, oldVal: {'undefined': 'test'}, identical: true}]); + }); + + + it('should trigger when key changes', function() { + $rootScope.key = 'a'; + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': 'test'}, oldVal: {'a': 'test'}, identical: true}]); + + $rootScope.key = 'b'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'b': 'test'}, oldVal: {'a': 'test'}}]); + + $rootScope.key = true; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'true': 'test'}, oldVal: {'b': 'test'}}]); + }); + + + it('should not trigger when key changes but stringified key does not', function() { + $rootScope.key = 1; + $rootScope.obj = 'test'; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'1': 'test'}, oldVal: {'1': 'test'}, identical: true}]); + + $rootScope.key = '1'; + $rootScope.$digest(); + expect(log.empty()).toEqual([]); + + $rootScope.key = true; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'true': 'test'}, oldVal: {'1': 'test'}}]); + + $rootScope.key = 'true'; + $rootScope.$digest(); + expect(log.empty()).toEqual([]); + + $rootScope.key = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'[object Object]': 'test'}, oldVal: {'true': 'test'}}]); + + $rootScope.key = {}; + $rootScope.$digest(); + expect(log.empty()).toEqual([]); + }); + + + it('should not trigger change when object in collection changes', function() { + $rootScope.key = 'a'; + $rootScope.obj = {name: 'foo'}; + $rootScope.$digest(); + expect(log.empty()).toEqual([{newVal: {'a': {name: 'foo'}}, oldVal: {'a': {name: 'foo'}}, identical: true}]); + + $rootScope.obj.name = 'bar'; + $rootScope.$digest(); + expect(log.empty()).toEqual([]); + }); + + + it('should not infinitely digest when key value is NaN', function() { + $rootScope.key = NaN; + $rootScope.obj = NaN; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); + }); }); }); }); - describe('optimizations', function() { function setupWatches(scope, log) { @@ -1108,6 +1431,8 @@ describe('Scope', function() { scope.$watchGroup(['1'], noop); //multi constant scope.$watchGroup(['1', '2'], noop); + //multi one-time/constant + scope.$watchGroup(['::a', '1'], noop); expect(scope.$$watchersCount).not.toBe(0); scope.$apply('a = b = 1'); From 8a89586bede0256494cb3027d856f1ebf592afde Mon Sep 17 00:00:00 2001 From: David Fuka Date: Wed, 10 May 2017 04:26:49 -0700 Subject: [PATCH 008/599] docs(errors/badjsonp): fix typo Closes #15977 --- docs/content/error/$http/badjsonp.ngdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/error/$http/badjsonp.ngdoc b/docs/content/error/$http/badjsonp.ngdoc index ab42b46c7fdf..bdb85ac0c1c5 100644 --- a/docs/content/error/$http/badjsonp.ngdoc +++ b/docs/content/error/$http/badjsonp.ngdoc @@ -9,7 +9,7 @@ value is `JSON_CALLBACK`. `$http` JSONP requests need to attach a callback query parameter to the URL. The name of this parameter is specified in the configuration object (or in the defaults) via the `jsonpCallbackParam` -property. You must not provide your own parameter with this name in the configuratio of the request. +property. You must not provide your own parameter with this name in the configuration of the request. In previous versions of AngularJS, you specified where to add the callback parameter value via the `JSON_CALLBACK` placeholder. This is no longer allowed. From e1dd00e4ef4fbdb255698908ca0ac7d7f1aa3968 Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Wed, 10 May 2017 14:10:23 +0200 Subject: [PATCH 009/599] chore(*): update jasmine-core to 2.5.2 This might resolve an issue where Firefox 51+ fails on Jenkins at this test: https://github.com/angular/angular.js/blob/8a89586bede0256494cb3027d856f1ebf592afde/test/ng/directive/ngOptionsSpec.js#L2891 The failure can also be reproduced locally when running the whole test suite with the Firefox window unfocused. With 2.5.2, the failure happens too, but much less frequent. Latest jasmine (2.6.2) has problems with some browsers, see here: https://github.com/jasmine/jasmine/issues/1327 Closes #15978 --- package.json | 2 +- yarn.lock | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 3ecc772b7123..a17028b5c413 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "gulp-sourcemaps": "^1.2.2", "gulp-uglify": "^1.0.1", "gulp-util": "^3.0.1", - "jasmine-core": "^2.4.0", + "jasmine-core": "2.5.2", "jasmine-node": "^2.0.0", "jasmine-reporters": "^2.2.0", "jquery": "^3.2.1", diff --git a/yarn.lock b/yarn.lock index 16e902960683..8274bae642f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1473,9 +1473,9 @@ debug-fabulous@0.0.X: lazy-debug-legacy "0.0.X" object-assign "4.1.0" -debug@2, debug@2.6.0, debug@2.X, debug@^2.1.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" +debug@2, debug@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" dependencies: ms "0.7.2" @@ -1491,15 +1491,15 @@ debug@2.3.3: dependencies: ms "0.7.2" -debug@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" +debug@2.6.0, debug@2.X, debug@^2.1.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" dependencies: ms "0.7.2" -debug@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" +debug@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" dependencies: ms "0.7.2" @@ -3475,7 +3475,7 @@ jade@0.26.3: commander "0.6.1" mkdirp "0.3.0" -jasmine-core@^2.4.0, jasmine-core@~2.5.2: +jasmine-core@2.5.2, jasmine-core@~2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297" @@ -5483,9 +5483,9 @@ right-pad@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@~2.5.1, rimraf@~2.5.4: - version "2.5.4" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" +rimraf@2, rimraf@^2.5.2, rimraf@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: glob "^7.0.5" @@ -5495,9 +5495,9 @@ rimraf@2.4.3: dependencies: glob "^5.0.14" -rimraf@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" +rimraf@^2.2.8, rimraf@^2.5.1, rimraf@~2.5.1, rimraf@~2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: glob "^7.0.5" @@ -6260,13 +6260,13 @@ tmp@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" -tmp@0.0.31: +tmp@0.0.31, tmp@0.0.x: version "0.0.31" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" dependencies: os-tmpdir "~1.0.1" -tmp@0.0.x, tmp@^0.0.29: +tmp@^0.0.29: version "0.0.29" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" dependencies: From e79c472fdd765000ac82a3c99b678a96eeccb588 Mon Sep 17 00:00:00 2001 From: Jake Danforth Date: Wed, 10 May 2017 08:46:17 -0700 Subject: [PATCH 010/599] docs(error/badrestrict): fix typo (of --> or) Closes #15979 --- docs/content/error/$compile/badrestrict.ngdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/error/$compile/badrestrict.ngdoc b/docs/content/error/$compile/badrestrict.ngdoc index 7c4eb41a1984..45288d4b935a 100644 --- a/docs/content/error/$compile/badrestrict.ngdoc +++ b/docs/content/error/$compile/badrestrict.ngdoc @@ -5,7 +5,7 @@ This error occurs when the restrict property of a directive is not valid. -The directive restrict property must be a string including one of more of the following characters: +The directive restrict property must be a string including one or more of the following characters: * E (element) * A (attribute) * C (class) @@ -15,4 +15,4 @@ For example: ```javascript restrict: 'E' restrict: 'EAC' -``` \ No newline at end of file +``` From cd5efa095e448dfe179f8cd3ed34988ee34fa271 Mon Sep 17 00:00:00 2001 From: tsclaus Date: Tue, 9 May 2017 14:10:55 -0400 Subject: [PATCH 011/599] docs(ngRepeat): fix argument name in comment to match actual argument (element --> clone) Closes #15975 --- src/ng/directive/ngRepeat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index c83a6223c65f..8397030c57eb 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -429,7 +429,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani // Store a list of elements from previous run. This is a hash where key is the item from the // iterator, and the value is objects with following properties. // - scope: bound scope - // - element: previous element. + // - clone: previous element. // - index: position // // We are using no-proto object so that we don't need to guard against inherited props via From 66945e217eef76c9321ca4750921196fdf0851cc Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Thu, 11 May 2017 17:22:43 +0300 Subject: [PATCH 012/599] docs(form): improve the docs for `FormController.$setValidity()` Fixes #15963 --- src/ng/directive/form.js | 37 +++++++++++++++++++++++++++++-------- src/ng/directive/ngModel.js | 2 +- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 28538806fef1..b0bd565ff946 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -26,17 +26,23 @@ function nullFormRenameControl(control, name) { * @property {boolean} $dirty True if user has already interacted with the form. * @property {boolean} $valid True if all of the containing forms and controls are valid. * @property {boolean} $invalid True if at least one containing control or form is invalid. - * @property {boolean} $pending True if at least one containing control or form is pending. * @property {boolean} $submitted True if user has submitted the form even if its invalid. * - * @property {Object} $error Is an object hash, containing references to controls or - * forms with failing validators, where: + * @property {Object} $pending An object hash, containing references to controls or forms with + * pending validators, where: + * + * - keys are validations tokens (error names). + * - values are arrays of controls or forms that have a pending validator for the given error name. + * + * See {@link form.FormController#$error $error} for a list of built-in validation tokens. + * + * @property {Object} $error An object hash, containing references to controls or forms with failing + * validators, where: * * - keys are validation tokens (error names), - * - values are arrays of controls or forms that have a failing validator for given error name. + * - values are arrays of controls or forms that have a failing validator for the given error name. * * Built-in validation tokens: - * * - `email` * - `max` * - `maxlength` @@ -282,9 +288,24 @@ FormController.prototype = { * @name form.FormController#$setValidity * * @description - * Sets the validity of a form control. - * - * This method will also propagate to parent forms. + * Change the validity state of the form, and notify the parent form (if any). + * + * Application developers will rarely need to call this method directly. It is used internally, by + * {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a + * control's validity state to the parent `FormController`. + * + * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be + * assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for + * unfulfilled `$asyncValidators`), so that it is available for data-binding. The + * `validationErrorKey` should be in camelCase and will get converted into dash-case for + * class name. Example: `myError` will result in `ng-valid-my-error` and + * `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`. + * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending + * (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`. + * Skipped is used by AngularJS when validators do not run because of parse errors and when + * `$asyncValidators` do not run because any of the `$validators` failed. + * @param {NgModelController | FormController} controller - The controller whose validity state is + * triggering the change. */ addSetValidityMethod({ clazz: FormController, diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index 063ebf05f003..cdba3c11de28 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -940,7 +940,7 @@ function setupModelWatcher(ctrl) { * (for unfulfilled `$asyncValidators`), so that it is available for data-binding. * The `validationErrorKey` should be in camelCase and will get converted into dash-case * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` - * class and can be bound to as `{{someForm.someControl.$error.myError}}` . + * classes and can be bound to as `{{ someForm.someControl.$error.myError }}`. * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), * or skipped (null). Pending is used for unfulfilled `$asyncValidators`. * Skipped is used by AngularJS when validators do not run because of parse errors and From 9bcdfcfe17f2655b11350aad5846a9a3cc481cc2 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Thu, 11 May 2017 17:23:44 +0300 Subject: [PATCH 013/599] docs(*): fix dangling links Closes #15984 --- docs/content/guide/migration.ngdoc | 6 +++--- src/ng/directive/select.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/guide/migration.ngdoc b/docs/content/guide/migration.ngdoc index 46ca03ec9d02..0d4b7d3b92b4 100644 --- a/docs/content/guide/migration.ngdoc +++ b/docs/content/guide/migration.ngdoc @@ -1312,7 +1312,7 @@ jqLite/jQuery collections #### Helper Functions: -The {@link angular.lowercase `angular.lowercase`} and {@link angular.uppercase `angular.uppercase`} functions have been **deprecated** and will be removed +The `angular.lowercase` and `angular.uppercase` functions have been **deprecated** and will be removed in version 1.7.0. It is recommended to use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) and [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) functions instead. @@ -1461,8 +1461,8 @@ to render error messages with ngMessages that are listed with a directive such a involves pulling error message data from a server and then displaying that data via the mechanics of ngMessages. Be sure to read the breaking change involved with `ngMessagesInclude` to upgrade your template code. -Other changes, such as the ordering of elements with ngRepeat and ngOptions and the way ngPattern and pattern directives -validate the regex, may also affect the behavior of your application. And be sure to also read up on the changes to `$cookies`. +Other changes, such as the ordering of elements with ngRepeat and ngOptions and the way ngPattern and pattern directives +validate the regex, may also affect the behavior of your application. And be sure to also read up on the changes to `$cookies`. The migration jump from 1.3 to 1.4 should be relatively straightforward otherwise. diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js index f7d63d753ac4..7b8e5b4a47d2 100644 --- a/src/ng/directive/select.js +++ b/src/ng/directive/select.js @@ -483,7 +483,7 @@ var SelectController = * the content of the `value` attribute or the textContent of the `