Compare commits
253 Commits
2.0.0-rc.6
...
2.1.0-rc.0
Author | SHA1 | Date | |
---|---|---|---|
ef621a2f00 | |||
df9761951b | |||
f786c560f1 | |||
c5557de3e7 | |||
ec3a5b54de | |||
cf269d9ff4 | |||
5fa5ffb82a | |||
4a57dcfd8d | |||
43923ffcf5 | |||
50c37d45dc | |||
a63359689f | |||
43d3a84df3 | |||
8310c91823 | |||
b64b5ece65 | |||
ed9c2b6281 | |||
1cf5f5fa38 | |||
a32078f85e | |||
decd129a4d | |||
c3c9ecb302 | |||
af520947aa | |||
040bf57966 | |||
65a60b7456 | |||
756ef09d12 | |||
9316f95467 | |||
83d94b7504 | |||
a121136fae | |||
a6bb84e02b | |||
3898dc488e | |||
ca3f9926f9 | |||
1c012a035f | |||
38c5304b7f | |||
9a049be67f | |||
2045c9e8ee | |||
6c4ec05a4a | |||
f7bfda31ff | |||
a92b573309 | |||
4fd13d71c8 | |||
bf7b82b658 | |||
c143fee849 | |||
0286956107 | |||
e884f4854d | |||
df1822fc2a | |||
42b4b6d21b | |||
36bc2ff269 | |||
1564042fe8 | |||
41c8c30973 | |||
61129fa12d | |||
3a5b4882bc | |||
425c1e6042 | |||
58605cf350 | |||
34b31dea7c | |||
a241ab7c07 | |||
745e10e6d2 | |||
33340dbbd1 | |||
52812c08e2 | |||
52f5ae1961 | |||
9be895b6da | |||
9f1c82537e | |||
5ab5cc77bb | |||
f1b6c6efa1 | |||
45ad13560b | |||
2045268cec | |||
fb1076b44a | |||
6fc46526ae | |||
3ef5ede6d6 | |||
136621ebc9 | |||
f23b22a0f4 | |||
0ca971c5bd | |||
3a6fcee0e6 | |||
8972137c29 | |||
cc6481077f | |||
c041b93418 | |||
bc33765913 | |||
31dce72b7b | |||
212f8dbde7 | |||
44da4984f9 | |||
d95344430c | |||
131626fc61 | |||
676bb0fa7d | |||
5a849829c4 | |||
671f73448c | |||
51d73d3e4e | |||
bf81b06a28 | |||
0621f07a2c | |||
1225ecfb14 | |||
5509453e72 | |||
70488ed382 | |||
03aedbe54b | |||
8395aab25d | |||
0dc15eb64a | |||
cba885a1fb | |||
fa4723a208 | |||
5bf08b886f | |||
89802316b9 | |||
2300c23332 | |||
fa39965a37 | |||
115f0fa842 | |||
734b8b8c13 | |||
54b41f57be | |||
df4254ae89 | |||
14ee75924b | |||
bd4045b6e7 | |||
255099aa61 | |||
1c24096650 | |||
32aeb1052d | |||
838d4bbf6c | |||
c4114c2f66 | |||
37b8691c8c | |||
93054d4e3d | |||
cfc12c6539 | |||
c0bdd89b5d | |||
d5515473bf | |||
ffe5c49c3e | |||
ae1dd5bfd0 | |||
cb657c4b55 | |||
42f60ca303 | |||
e33037a2f1 | |||
9cee8bcc83 | |||
003294d5df | |||
785292f44f | |||
15c2912527 | |||
096ae7c404 | |||
5972fdc817 | |||
2c42a50fc3 | |||
caa1cd2470 | |||
5fad37df69 | |||
727c2b38a4 | |||
b6287ccc51 | |||
69e8ace884 | |||
85d9db6bc4 | |||
0a2132ef10 | |||
d299ce4bcf | |||
0b9425bbb4 | |||
1a035a0dc7 | |||
84b4338ab5 | |||
b847257b16 | |||
c65d139081 | |||
57f0269491 | |||
4e6c41b3a1 | |||
7105021c41 | |||
f7313db0be | |||
1d2e70e3a4 | |||
21516c32e6 | |||
00a24b63da | |||
e71558ba89 | |||
7ac47acc1c | |||
60e49a7e4b | |||
c71e35cbf5 | |||
1348c65b0c | |||
ff03d87cdd | |||
a2bf334e6e | |||
f8690caa98 | |||
aa713d1dd9 | |||
a2519c6164 | |||
fa994810d5 | |||
c54580a4af | |||
730415e048 | |||
42a287fabf | |||
42d442dcd5 | |||
cc2873a94d | |||
63e15ffaec | |||
1b15170c89 | |||
26d1423ae9 | |||
61aad7925f | |||
79055f727b | |||
220d8377fe | |||
cc7780adf7 | |||
051a6ebe12 | |||
c9513b713a | |||
66e38b6754 | |||
7b82877ee5 | |||
c9ad5e46d6 | |||
2cdd051109 | |||
57cb82052b | |||
dd8204a655 | |||
cdda4082de | |||
0614c8c99d | |||
a343a8e1c2 | |||
a41c1bbdf4 | |||
f2c6157e74 | |||
32564ece27 | |||
3eee62fa71 | |||
617475005f | |||
0822066175 | |||
82f30e09f0 | |||
c649a5c5ab | |||
53f0c2206d | |||
0bce3907b8 | |||
2170379251 | |||
5a4e46db20 | |||
f5d44a42c9 | |||
673de004d2 | |||
f386cb4ba9 | |||
71e9cae1d0 | |||
df6762a170 | |||
d296298282 | |||
077e0be1e7 | |||
a52d076912 | |||
dae7cfc454 | |||
436af15d63 | |||
7b24028437 | |||
6a2bbffe10 | |||
f78e184822 | |||
78ad9adc1a | |||
9e2ec7a1aa | |||
643afa4b15 | |||
ed2ebeb52a | |||
567900e550 | |||
cc958c74ad | |||
62af613741 | |||
3ff816afa6 | |||
dd03bf12e1 | |||
645108f25b | |||
882efd125e | |||
d91e92c2f5 | |||
8858ebc4ab | |||
df4c0a3d1f | |||
b4363bc8af | |||
d26a827494 | |||
ea95c391c1 | |||
aa9b617c9d | |||
7192fec841 | |||
ee88c3c976 | |||
1ff0add29e | |||
5ee0f09b92 | |||
70b0ab457b | |||
c25d1f7ecc | |||
a45769a0a2 | |||
109dc99d32 | |||
2371d22d49 | |||
6f4b6edfea | |||
8c09933803 | |||
d309f7799c | |||
93deff6c33 | |||
c31535982c | |||
f5101782d9 | |||
5e5ae3cde6 | |||
53cf71430f | |||
04d02b55d1 | |||
043493cb62 | |||
2581c0851a | |||
27d72e87c3 | |||
eef4c22e87 | |||
4287f1716d | |||
ebc8e808a9 | |||
c9e5b599e4 | |||
e42a057048 | |||
0bb94df1da | |||
50e171c09b | |||
96697029c9 | |||
7c3b1367bc | |||
18be339ee9 | |||
ddda62b1f2 |
34
.github/ISSUE_TEMPLATE.md
vendored
34
.github/ISSUE_TEMPLATE.md
vendored
@ -1,3 +1,7 @@
|
|||||||
|
<!--
|
||||||
|
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
|
||||||
|
-->
|
||||||
|
|
||||||
**I'm submitting a ...** (check one with "x")
|
**I'm submitting a ...** (check one with "x")
|
||||||
```
|
```
|
||||||
[ ] bug report => search github for a similar issue or PR before submitting
|
[ ] bug report => search github for a similar issue or PR before submitting
|
||||||
@ -6,28 +10,30 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Current behavior**
|
**Current behavior**
|
||||||
|
<!-- Describe how the bug manifests. -->
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
<!-- Describe what the behavior would be without the bug. -->
|
||||||
|
|
||||||
**Expected/desired behavior**
|
**Minimal reproduction of the problem with instructions**
|
||||||
|
<!--
|
||||||
|
If the current behavior is a bug or you can illustrate your feature request better with an example,
|
||||||
**Reproduction of the problem**
|
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||||
If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
**What is the expected behavior?**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**What is the motivation / use case for changing the behavior?**
|
**What is the motivation / use case for changing the behavior?**
|
||||||
|
<!-- Describe the motivation or the concrete use case -->
|
||||||
|
|
||||||
|
|
||||||
**Please tell us about your environment:**
|
**Please tell us about your environment:**
|
||||||
|
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
||||||
|
|
||||||
* **Angular version:** 2.0.0-rc.X
|
* **Angular version:** 2.0.X
|
||||||
|
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||||
|
|
||||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||||
|
<!-- All browsers where this could be reproduced -->
|
||||||
|
|
||||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
||||||
|
|
||||||
|
* **Node (for AoT issues):** `node --version` =
|
||||||
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,5 +1,5 @@
|
|||||||
**Please check if the PR fulfills these requirements**
|
**Please check if the PR fulfills these requirements**
|
||||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit-message-format
|
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
|
||||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||||
|
|
||||||
|
22
.gitignore
vendored
22
.gitignore
vendored
@ -1,26 +1,9 @@
|
|||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|
||||||
# Don’t commit the following directories created by pub.
|
|
||||||
packages
|
|
||||||
pubspec.lock
|
|
||||||
.pub
|
|
||||||
.packages
|
|
||||||
|
|
||||||
/dist/
|
/dist/
|
||||||
.buildlog
|
|
||||||
node_modules
|
node_modules
|
||||||
bower_components
|
bower_components
|
||||||
|
|
||||||
# Or broccoli working directory
|
|
||||||
tmp
|
|
||||||
|
|
||||||
# Or the files created by dart2js.
|
|
||||||
*.dart.js
|
|
||||||
*.dart.precompiled.js
|
|
||||||
*.js_
|
|
||||||
*.js.deps
|
|
||||||
*.js.map
|
|
||||||
|
|
||||||
# Include when developing application packages.
|
# Include when developing application packages.
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
.c9
|
.c9
|
||||||
@ -37,13 +20,8 @@ modules/.vscode
|
|||||||
# Ignore npm debug log
|
# Ignore npm debug log
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
/docs/bower_components/
|
|
||||||
|
|
||||||
# build-analytics
|
# build-analytics
|
||||||
.build-analytics
|
.build-analytics
|
||||||
|
|
||||||
# built dart payload tests
|
|
||||||
/modules_dart/payload/**/build
|
|
||||||
|
|
||||||
# rollup-test output
|
# rollup-test output
|
||||||
/modules/rollup-test/dist/
|
/modules/rollup-test/dist/
|
||||||
|
148
.travis.yml
148
.travis.yml
@ -1,7 +1,7 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
node_js:
|
||||||
- '5.4.1'
|
- '6.6.0'
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
# firefox: "38.0"
|
# firefox: "38.0"
|
||||||
@ -20,20 +20,9 @@ cache:
|
|||||||
directories:
|
directories:
|
||||||
- ./node_modules
|
- ./node_modules
|
||||||
- ./.chrome/chromium
|
- ./.chrome/chromium
|
||||||
# - $HOME/.pub-cache
|
|
||||||
|
|
||||||
|
|
||||||
#before_cache:
|
|
||||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
|
||||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
|
||||||
# - E2E_BROWSERS=ChromeOnTravis
|
|
||||||
# - LOGS_DIR=/tmp/angular-build/logs
|
|
||||||
# - ARCH=linux-x64
|
|
||||||
|
|
||||||
# GITHUB_TOKEN_ANGULAR
|
# GITHUB_TOKEN_ANGULAR
|
||||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||||
@ -52,146 +41,11 @@ matrix:
|
|||||||
- env: "CI_MODE=saucelabs_optional"
|
- env: "CI_MODE=saucelabs_optional"
|
||||||
- env: "CI_MODE=browserstack_optional"
|
- env: "CI_MODE=browserstack_optional"
|
||||||
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ./scripts/ci-lite/install.sh
|
- ./scripts/ci-lite/install.sh
|
||||||
|
|
||||||
before_script:
|
|
||||||
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- ./scripts/ci-lite/cleanup.sh
|
- ./scripts/ci-lite/cleanup.sh
|
||||||
|
|
||||||
|
|
||||||
#branches:
|
|
||||||
# except:
|
|
||||||
# - g3_v2_0
|
|
||||||
#
|
|
||||||
#cache:
|
|
||||||
# directories:
|
|
||||||
# - $HOME/.pub-cache
|
|
||||||
# - $HOME/.chrome/chromium
|
|
||||||
#
|
|
||||||
#before_cache:
|
|
||||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
|
||||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
|
||||||
#
|
|
||||||
#env:
|
|
||||||
# global:
|
|
||||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
|
||||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
|
||||||
# - CXX=g++-4.8
|
|
||||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
|
||||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
|
||||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
|
||||||
# - E2E_BROWSERS=ChromeOnTravis
|
|
||||||
# - LOGS_DIR=/tmp/angular-build/logs
|
|
||||||
# - SAUCE_USERNAME=angular-ci
|
|
||||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
|
||||||
# - BROWSER_STACK_USERNAME=angularteam1
|
|
||||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
|
||||||
# - ARCH=linux-x64
|
|
||||||
# - DART_DEV_VERSION=latest
|
|
||||||
# - DART_STABLE_VERSION=latest
|
|
||||||
# - DART_CHANNEL=stable
|
|
||||||
# - DART_VERSION=$DART_STABLE_VERSION
|
|
||||||
# # Token for tsd to increase github rate limit
|
|
||||||
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
|
|
||||||
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
|
||||||
# # because those are not visible for pull requests, and those should also be reliable.
|
|
||||||
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
|
||||||
# # (password is in Valentine)
|
|
||||||
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
|
||||||
# # GITHUB_TOKEN_ANGULAR
|
|
||||||
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
|
||||||
# matrix:
|
|
||||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
|
||||||
# - MODE=dart
|
|
||||||
# - MODE=dart DART_CHANNEL=dev
|
|
||||||
# - MODE=saucelabs_required
|
|
||||||
# - MODE=browserstack_required
|
|
||||||
# - MODE=saucelabs_optional
|
|
||||||
# - MODE=browserstack_optional
|
|
||||||
# - MODE=dart_ddc
|
|
||||||
# - MODE=js
|
|
||||||
# - MODE=router
|
|
||||||
# - MODE=build_only
|
|
||||||
# - MODE=typescript_next
|
|
||||||
# - MODE=lint
|
|
||||||
#
|
|
||||||
#matrix:
|
|
||||||
# allow_failures:
|
|
||||||
# - env: "MODE=saucelabs_optional"
|
|
||||||
# - env: "MODE=browserstack_optional"
|
|
||||||
#
|
|
||||||
#addons:
|
|
||||||
# firefox: "38.0"
|
|
||||||
# apt:
|
|
||||||
# sources:
|
|
||||||
# - ubuntu-toolchain-r-test
|
|
||||||
# packages:
|
|
||||||
# - g++-4.8
|
|
||||||
#
|
|
||||||
#before_install:
|
|
||||||
# - node tools/analytics/build-analytics start ci job
|
|
||||||
# - node tools/analytics/build-analytics start ci before_install
|
|
||||||
# - echo ${TSDRC} > .tsdrc
|
|
||||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
|
||||||
# - export DISPLAY=:99.0
|
|
||||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
|
||||||
# - ./scripts/ci/init_android.sh
|
|
||||||
# - sh -e /etc/init.d/xvfb start
|
|
||||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
|
||||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
|
||||||
# - node tools/analytics/build-analytics success ci before_install
|
|
||||||
#
|
|
||||||
#install:
|
|
||||||
# - node tools/analytics/build-analytics start ci install
|
|
||||||
# # Install version of npm that we are locked against
|
|
||||||
# - npm install -g npm@3.5.3
|
|
||||||
# # Install version of Chromium that we are locked against
|
|
||||||
# - ./scripts/ci/install_chromium.sh
|
|
||||||
# # Install version of Dart based on the matrix build variables
|
|
||||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
|
||||||
# # Print the size of caches to ease debugging
|
|
||||||
# - du -sh ./node_modules || true
|
|
||||||
# # Install npm dependecies
|
|
||||||
# # check-node-modules will exit(1) if we don't need to install
|
|
||||||
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
|
||||||
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
|
||||||
# - node tools/analytics/build-analytics success ci install
|
|
||||||
#
|
|
||||||
#before_script:
|
|
||||||
# - node tools/analytics/build-analytics start ci before_script
|
|
||||||
# - mkdir -p $LOGS_DIR
|
|
||||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
|
||||||
# - node tools/analytics/build-analytics success ci before_script
|
|
||||||
#
|
|
||||||
#script:
|
|
||||||
# - node tools/analytics/build-analytics start ci script
|
|
||||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
|
||||||
# - node tools/analytics/build-analytics success ci script
|
|
||||||
#
|
|
||||||
#after_script:
|
|
||||||
# - node tools/analytics/build-analytics start ci after_script
|
|
||||||
# - ./scripts/ci/print-logs.sh
|
|
||||||
# - ./scripts/ci/after-script.sh
|
|
||||||
# - ./scripts/publish/publish-build-artifacts.sh
|
|
||||||
# - node tools/analytics/build-analytics success ci after_script
|
|
||||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
|
||||||
#
|
|
||||||
#notifications:
|
|
||||||
# webhooks:
|
|
||||||
# urls:
|
|
||||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
|
||||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
|
||||||
# - https://buildtimetrend.herokuapp.com/travis
|
|
||||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
|
||||||
# on_success: always # options: [always|never|change] default: always
|
|
||||||
# on_failure: always # options: [always|never|change] default: always
|
|
||||||
# on_start: never # default: never
|
|
||||||
# slack:
|
|
||||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
|
||||||
|
145
CHANGELOG.md
145
CHANGELOG.md
@ -1,3 +1,133 @@
|
|||||||
|
<a name="2.1.0-rc.0"></a>
|
||||||
|
# [2.1.0-rc.0](https://github.com/angular/angular/compare/2.1.0-beta.0...2.1.0-rc.0) (2016-10-05)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **animations:** provide aliases for :enter and :leave transitions ([#11991](https://github.com/angular/angular/issues/11991)) ([e884f48](https://github.com/angular/angular/commit/e884f48))
|
||||||
|
|
||||||
|
Note: 2.1.0-rc.0 release also contains all the changes present in the 2.0.2 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.2"></a>
|
||||||
|
## [2.0.2](https://github.com/angular/angular/compare/2.0.1...2.0.2) (2016-10-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** correctly removes styles on IE ([#11953](https://github.com/angular/angular/pull/11953)), closes [#7916](https://github.com/angular/angular/issues/7916)
|
||||||
|
* **compiler:** do not embed templateUrl in view factories in non-debug mode. ([#11818](https://github.com/angular/angular/issues/11818)) ([51e1994](https://github.com/angular/angular/commit/51e1994)), closes [#11117](https://github.com/angular/angular/issues/11117)
|
||||||
|
* **compiler:** move detection of unsafe properties for binding to ElementSchemaRegistry ([#11378](https://github.com/angular/angular/issues/11378)) ([5911c3b](https://github.com/angular/angular/commit/5911c3b))
|
||||||
|
* **compiler:** fix `:host(tag)` and `:host-context(tag)` ([a6bb84e0](https://github.com/angular/angular/commit/a6bb84e02b7579f8d957ef6ba5b10d83482ed756)), closes [#11972](https://github.com/angular/angular/issues/11972)
|
||||||
|
* **compiler:** fix attribute selectors in :host and :host-context ([#12056](https://github.com/angular/angular/issues/12056)) ([6f7ed32](https://github.com/angular/angular/commit/6f7ed32)), closes [#11917](https://github.com/angular/angular/issues/11917)
|
||||||
|
* **compiler:** support `[@page](https://github.com/page)` and `[@document](https://github.com/document)` CSS rules ([#11878](https://github.com/angular/angular/issues/11878)) ([c99ef49](https://github.com/angular/angular/commit/c99ef49)), closes [#11860](https://github.com/angular/angular/issues/11860)
|
||||||
|
* **compiler:** support `[attr="value with space"]` ([bd012ef](https://github.com/angular/angular/commit/bd012ef)), closes [#6249](https://github.com/angular/angular/issues/6249)
|
||||||
|
* **compiler:** support quoted attribute values ([7395400](https://github.com/angular/angular/commit/7395400)), closes [#6085](https://github.com/angular/angular/issues/6085)
|
||||||
|
* **compiler:** fix `<x>` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000)
|
||||||
|
* **compiler-cli:** allow ReflectorHost passed as argument to CodeGenerator#create ([#11951](https://github.com/angular/angular/issues/11951)) ([826c98e](https://github.com/angular/angular/co
|
||||||
|
* **forms:** properly validate empty strings with patterns ([#11450](https://github.com/angular/angular/issues/11450)) ([e00de0c](https://github.com/angular/angular/commit/e00de0c))
|
||||||
|
* **http:** preserve case of the first init, `set()` or `append()` ([#12023](https://github.com/angular/angular/issues/12023)) ([adb17fe](https://github.com/angular/angular/commit/adb17fe)), closes [#11624](https://github.com/angular/angular/issues/11624)
|
||||||
|
* **http:** remove url params if provided value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e))
|
||||||
|
mmit/826c98e))
|
||||||
|
* **router:** do not reset the router state when updating the component ([#11867](https://github.com/angular/angular/issues/11867)) ([cf750e1](https://github.com/angular/angular/commit/cf750e1))
|
||||||
|
* **upgrade:** bind optional properties when upgrading from ng1 ([#11411](https://github.com/angular/angular/issues/11411)) ([0851238](https://github.com/angular/angular/commit/0851238)), closes [#10181](https://github.com/angular/angular/issues/10181)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.1.0-beta.0"></a>
|
||||||
|
# [2.1.0-beta.0](https://github.com/angular/angular/compare/2.0.0...2.1.0-beta.0) (2016-09-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **router:** add router preloader to optimistically preload routes ([5a84982](https://github.com/angular/angular/commit/5a84982))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
* **router:** update the router not to reset router state when updating root component ([#11799](https://github.com/angular/angular/issues/11799)) ([31dce72](https://github.com/angular/angular/commit/31dce72))
|
||||||
|
|
||||||
|
Note: 2.1.0-beta.0 release also contains all the changes present in the 2.0.1 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.1"></a>
|
||||||
|
## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** fix ngOnChanges signature of NgTemplateOutlet directive ([14ee759](https://github.com/angular/angular/commit/14ee759))
|
||||||
|
* **compiler:** `[attribute~=value]` selector ([#11696](https://github.com/angular/angular/issues/11696)) ([734b8b8](https://github.com/angular/angular/commit/734b8b8)), closes [#9644](https://github.com/angular/angular/issues/9644)
|
||||||
|
* **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652))
|
||||||
|
* **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590)
|
||||||
|
* **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643)
|
||||||
|
* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#11645](https://github.com/angular/angular/issues/11645)
|
||||||
|
* **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418))
|
||||||
|
* **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719)
|
||||||
|
* **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e))
|
||||||
|
* **upgrade:** allow attribute selectors for components in ng2 which are not part of upgrade ([#11808](https://github.com/angular/angular/issues/11808)) ([b81e2e7](https://github.com/angular/angular/commit/b81e2e7)), closes [#11280](https://github.com/angular/angular/issues/11280)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0"></a>
|
||||||
|
# [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **platform-browser:** provide Title service as part of the module ([#11605](https://github.com/angular/angular/issues/11605)) ([85d9db6](https://github.com/angular/angular/commit/85d9db6)), closes [#11600](https://github.com/angular/angular/issues/11600)
|
||||||
|
* **upgrade:** correct the main entry path in package.json ([a2519c6](https://github.com/angular/angular/commit/a2519c6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0-rc.7"></a>
|
||||||
|
# [2.0.0-rc.7](https://github.com/angular/angular/compare/2.0.0-rc.6...2.0.0-rc.7) (2016-09-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** allow `group()` to be used as entry point for an animation trigger ([#11419](https://github.com/angular/angular/issues/11419)) ([6a2bbff](https://github.com/angular/angular/commit/6a2bbff)), closes [#11312](https://github.com/angular/angular/issues/11312)
|
||||||
|
* **core:** ensure parent animations are triggered before children ([#11201](https://github.com/angular/angular/issues/11201)) ([c9e5b59](https://github.com/angular/angular/commit/c9e5b59))
|
||||||
|
* **core:** correctly type event handler proxy functions ([aa9b617](https://github.com/angular/angular/commit/aa9b617))
|
||||||
|
* **core:** fix error when `NgModule.bootstrap` contains `undefined` or `null` ([ea95c39](https://github.com/angular/angular/commit/ea95c39))
|
||||||
|
* **core:** fix an issue with webpack and lazy loader. ([#11387](https://github.com/angular/angular/issues/11387)) ([d26a827](https://github.com/angular/angular/commit/d26a827))
|
||||||
|
* **core:** BCP47 uses hyphens as separator ([#11514](https://github.com/angular/angular/issues/11514)) ([7b82877](https://github.com/angular/angular/commit/7b82877))
|
||||||
|
* **compiler:** fix `CssSelector#getMatchingElementTemplate()` for void tags ([077e0be](https://github.com/angular/angular/commit/077e0be)), closes [#11407](https://github.com/angular/angular/issues/11407)
|
||||||
|
* **compiler:** add missing elements to DOMSchema ([d309f77](https://github.com/angular/angular/commit/d309f77)), closes [#11219](https://github.com/angular/angular/issues/11219)
|
||||||
|
* **compiler:** fix perf regression in ShadowCss ([#11420](https://github.com/angular/angular/issues/11420)) ([78ad9ad](https://github.com/angular/angular/commit/78ad9ad)), closes [#11371](https://github.com/angular/angular/issues/11371)
|
||||||
|
* **compiler-cli:** prepend a rootDir when assuming a file exists ([#11291](https://github.com/angular/angular/issues/11291)) ([c315359](https://github.com/angular/angular/commit/c315359))
|
||||||
|
* **compiler-cli:** propagate errors to main ([#11214](https://github.com/angular/angular/issues/11214)) ([5e5ae3c](https://github.com/angular/angular/commit/5e5ae3c))
|
||||||
|
* **compiler-cli:** use the compilerHost to detect file existence ([#11418](https://github.com/angular/angular/issues/11418)) ([9e2ec7a](https://github.com/angular/angular/commit/9e2ec7a))
|
||||||
|
* **forms:** clear errors on disable ([#11463](https://github.com/angular/angular/issues/11463)) ([673de00](https://github.com/angular/angular/commit/673de00)), closes [#11287](https://github.com/angular/angular/issues/11287)
|
||||||
|
* **forms:** disabled controls should never be invalid ([#11257](https://github.com/angular/angular/issues/11257)) ([043493c](https://github.com/angular/angular/commit/043493c)), closes [#11253](https://github.com/angular/angular/issues/11253)
|
||||||
|
* **forms:** fix disabled support for empty form containers ([#11427](https://github.com/angular/angular/issues/11427)) ([7b24028](https://github.com/angular/angular/commit/7b24028)), closes [#11386](https://github.com/angular/angular/issues/11386)
|
||||||
|
* **forms:** fix resetting radios ([#11546](https://github.com/angular/angular/issues/11546)) ([61aad79](https://github.com/angular/angular/commit/61aad79)), closes [#11516](https://github.com/angular/angular/issues/11516)
|
||||||
|
* **forms:** rename validator change fn due to conflict ([#11492](https://github.com/angular/angular/issues/11492)) ([53f0c22](https://github.com/angular/angular/commit/53f0c22)), closes [#11479](https://github.com/angular/angular/issues/11479)
|
||||||
|
* **forms:** support dots in control names in contains ([#11542](https://github.com/angular/angular/issues/11542)) ([79055f7](https://github.com/angular/angular/commit/79055f7)), closes [#11535](https://github.com/angular/angular/issues/11535)
|
||||||
|
* **forms:** support rebinding nested controls ([#11210](https://github.com/angular/angular/issues/11210)) ([8c09933](https://github.com/angular/angular/commit/8c09933))
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* **core:** remove `…Metadata` for all decorators and use the decorator directly. ([63e15ff](https://github.com/angular/angular/commit/63e15ff))
|
||||||
|
|
||||||
|
|
||||||
|
### PEER-DEPENDENCY UPDATES ###
|
||||||
|
|
||||||
|
* **core**: zone.js@0.6.21
|
||||||
|
* **core**: rxjs@5.0.0-beta.12
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* core: - all `…Metadata` classes have been removed. Use the corresponding decorator
|
||||||
|
as constructor or for `instanceof` checks instead.
|
||||||
|
- Example:
|
||||||
|
* Before: `new ComponentMetadata(…)`
|
||||||
|
* After: `new Component(…)`
|
||||||
|
- Note: `new Component(…)` worked before as well.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.0.0-rc.6"></a>
|
<a name="2.0.0-rc.6"></a>
|
||||||
# [2.0.0-rc.6](https://github.com/angular/angular/compare/2.0.0-rc.5...2.0.0-rc.6) (2016-08-31)
|
# [2.0.0-rc.6](https://github.com/angular/angular/compare/2.0.0-rc.5...2.0.0-rc.6) (2016-08-31)
|
||||||
|
|
||||||
@ -12,7 +142,7 @@
|
|||||||
* **compiler:** make ShadowCSS shim work on Android browser ([#11139](https://github.com/angular/angular/issues/11139)) ([38069ab](https://github.com/angular/angular/commit/38069ab)), closes [#11123](https://github.com/angular/angular/issues/11123)
|
* **compiler:** make ShadowCSS shim work on Android browser ([#11139](https://github.com/angular/angular/issues/11139)) ([38069ab](https://github.com/angular/angular/commit/38069ab)), closes [#11123](https://github.com/angular/angular/issues/11123)
|
||||||
* **compiler:** no longer use assetCacheKey and Function#name for token identity. ([51877ef](https://github.com/angular/angular/commit/51877ef)), closes [#10545](https://github.com/angular/angular/issues/10545) [#10538](https://github.com/angular/angular/issues/10538)
|
* **compiler:** no longer use assetCacheKey and Function#name for token identity. ([51877ef](https://github.com/angular/angular/commit/51877ef)), closes [#10545](https://github.com/angular/angular/issues/10545) [#10538](https://github.com/angular/angular/issues/10538)
|
||||||
* **compiler:** only emit metadata for exported enums ([#10957](https://github.com/angular/angular/issues/10957)) ([a7b7682](https://github.com/angular/angular/commit/a7b7682))
|
* **compiler:** only emit metadata for exported enums ([#10957](https://github.com/angular/angular/issues/10957)) ([a7b7682](https://github.com/angular/angular/commit/a7b7682))
|
||||||
* **compiler:** throw descriptive error meesage for invalid NgModule providers ([#10947](https://github.com/angular/angular/issues/10947)) ([aa5c8ca](https://github.com/angular/angular/commit/aa5c8ca)), closes [#10714](https://github.com/angular/angular/issues/10714)
|
* **compiler:** throw descriptive error message for invalid NgModule providers ([#10947](https://github.com/angular/angular/issues/10947)) ([aa5c8ca](https://github.com/angular/angular/commit/aa5c8ca)), closes [#10714](https://github.com/angular/angular/issues/10714)
|
||||||
* **compiler:** detect invalid elements in templates via DomSchemaRegistry ([1df69cb](https://github.com/angular/angular/commit/1df69cb))
|
* **compiler:** detect invalid elements in templates via DomSchemaRegistry ([1df69cb](https://github.com/angular/angular/commit/1df69cb))
|
||||||
* **compiler:** ExtractorMerger returns return errors together with nodes (as a ParseTreeResult) ([39c0f9e](https://github.com/angular/angular/commit/39c0f9e))
|
* **compiler:** ExtractorMerger returns return errors together with nodes (as a ParseTreeResult) ([39c0f9e](https://github.com/angular/angular/commit/39c0f9e))
|
||||||
* **compiler:** throw better errors when components are passed to imports or modules are passed to declarations. ([#10888](https://github.com/angular/angular/issues/10888)) ([c4fd862](https://github.com/angular/angular/commit/c4fd862)), closes [#10823](https://github.com/angular/angular/issues/10823)
|
* **compiler:** throw better errors when components are passed to imports or modules are passed to declarations. ([#10888](https://github.com/angular/angular/issues/10888)) ([c4fd862](https://github.com/angular/angular/commit/c4fd862)), closes [#10823](https://github.com/angular/angular/issues/10823)
|
||||||
@ -103,6 +233,7 @@
|
|||||||
* **compiler:** Added "strictMetadataEmit" option to ngc ([#10951](https://github.com/angular/angular/issues/10951)) ([39a2c39](https://github.com/angular/angular/commit/39a2c39))
|
* **compiler:** Added "strictMetadataEmit" option to ngc ([#10951](https://github.com/angular/angular/issues/10951)) ([39a2c39](https://github.com/angular/angular/commit/39a2c39))
|
||||||
* **compiler-cli:** allow ngc implementations to provide XHR ([#10708](https://github.com/angular/angular/issues/10708)) ([6e842fc](https://github.com/angular/angular/commit/6e842fc))
|
* **compiler-cli:** allow ngc implementations to provide XHR ([#10708](https://github.com/angular/angular/issues/10708)) ([6e842fc](https://github.com/angular/angular/commit/6e842fc))
|
||||||
* **compiler-cli:** support pathmapping using a separate reflector ([#10985](https://github.com/angular/angular/issues/10985)) ([e0fbca9](https://github.com/angular/angular/commit/e0fbca9))
|
* **compiler-cli:** support pathmapping using a separate reflector ([#10985](https://github.com/angular/angular/issues/10985)) ([e0fbca9](https://github.com/angular/angular/commit/e0fbca9))
|
||||||
|
* **core:** support animation trigger template callbacks ([45e8e73](https://github.com/angular/angular/commit/45e8e73670b96387fc109921fad299742d3f7cbf))
|
||||||
* **core:** make sure animation callback reports the totalTime ([#11022](https://github.com/angular/angular/issues/11022)) ([4f8f8cf](https://github.com/angular/angular/commit/4f8f8cf))
|
* **core:** make sure animation callback reports the totalTime ([#11022](https://github.com/angular/angular/issues/11022)) ([4f8f8cf](https://github.com/angular/angular/commit/4f8f8cf))
|
||||||
* **core:** add NO_ERRORS_SCHEMA that allows any properties to be set on any element ([#10956](https://github.com/angular/angular/issues/10956)) ([c631cfc](https://github.com/angular/angular/commit/c631cfc))
|
* **core:** add NO_ERRORS_SCHEMA that allows any properties to be set on any element ([#10956](https://github.com/angular/angular/issues/10956)) ([c631cfc](https://github.com/angular/angular/commit/c631cfc))
|
||||||
* **core:** make ngprobe tokens pluggable ([f48142e](https://github.com/angular/angular/commit/f48142e))
|
* **core:** make ngprobe tokens pluggable ([f48142e](https://github.com/angular/angular/commit/f48142e))
|
||||||
@ -137,6 +268,16 @@
|
|||||||
|
|
||||||
### BREAKING CHANGES
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* npm packages: code in ESM (ES6 Modules) format is now published at the default location in the npm package with `package.json`'s `main` entry pointing to an UMD bundle (primarily for node, and webpack 1 users).
|
||||||
|
|
||||||
|
If you are using SystemJS to load Angular, you should adjust your SystemJS configuration to point to the UMD bundles (present in the npm package).
|
||||||
|
|
||||||
|
Please see this [example SystemJS config](https://github.com/angular/quickstart/blob/3b7452cc444c49c139ea39523ced0468c2362c16/systemjs.config.js#L17-L34).
|
||||||
|
|
||||||
|
* testing config: due to zone.js peer-dependency upgrade, the order in which various zone specs are loaded has changed.
|
||||||
|
|
||||||
|
Please see this [example Karma config](https://github.com/angular/quickstart/blob/3b7452cc444c49c139ea39523ced0468c2362c16/karma.conf.js#L31-L38).
|
||||||
|
|
||||||
* core: `Type` is now `Type<T>` which means that in most cases you have to
|
* core: `Type` is now `Type<T>` which means that in most cases you have to
|
||||||
use `Type<any>` in place of `Type`.
|
use `Type<any>` in place of `Type`.
|
||||||
|
|
||||||
@ -183,7 +324,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
|||||||
|
|
||||||
We now only accept:
|
We now only accept:
|
||||||
```
|
```
|
||||||
{provider: MyClass, toFactory: ...}
|
{provide: MyClass, useFactory: ...}
|
||||||
```
|
```
|
||||||
|
|
||||||
* core: previously deprecated NgZoneError has been removed
|
* core: previously deprecated NgZoneError has been removed
|
||||||
|
@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
|||||||
## <a name="question"></a> Got a Question or Problem?
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
||||||
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||||
|
|
||||||
## <a name="issue"></a> Found an Issue?
|
## <a name="issue"></a> Found an Issue?
|
||||||
If you find a bug in the source code, you can help us by
|
If you find a bug in the source code, you can help us by
|
||||||
@ -28,8 +28,7 @@ If you find a bug in the source code, you can help us by
|
|||||||
## <a name="feature"></a> Want a Feature?
|
## <a name="feature"></a> Want a Feature?
|
||||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
||||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
||||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
a proposal for your work first, to be sure that we can use it.
|
||||||
and we are not ready to accept major contributions ahead of the full release.
|
|
||||||
Please consider what kind of change it is:
|
Please consider what kind of change it is:
|
||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
|
@ -114,7 +114,7 @@ You should execute the 3 test suites before submitting a PR to github.
|
|||||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||||
|
|
||||||
- CircleCI fails if your code is not formatted properly,
|
- CircleCI fails if your code is not formatted properly,
|
||||||
- Travis CI fails if any of the test suite describe above fails.
|
- Travis CI fails if any of the test suites described above fails.
|
||||||
|
|
||||||
## Update the public API tests
|
## Update the public API tests
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
[](http://issuestats.com/github/angular/angular)
|
[](http://issuestats.com/github/angular/angular)
|
||||||
[](http://issuestats.com/github/angular/angular)
|
[](http://issuestats.com/github/angular/angular)
|
||||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||||
[](https://npmjs.org/package/angular2)
|
|
||||||
|
|
||||||
[](https://saucelabs.com/u/angular2-ci)
|
[](https://saucelabs.com/u/angular2-ci)
|
||||||
|
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||||
|
|
||||||
Angular
|
Angular
|
||||||
=========
|
=========
|
||||||
@ -16,7 +16,6 @@ repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
|||||||
|
|
||||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||||
|
|
||||||
Angular 2 is currently in **Release Candidate**.
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
@ -28,10 +27,9 @@ Angular 2 is currently in **Release Candidate**.
|
|||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
|
[browserstack]: https://www.browserstack.com/
|
||||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
[dart]: http://www.dartlang.org
|
[dart]: http://www.dartlang.org
|
||||||
[dartium]: http://www.dartlang.org/tools/dartium
|
|
||||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||||
[ng2]: http://angular.io
|
[ng2]: http://angular.io
|
||||||
[ngDart]: http://angulardart.org
|
[ngDart]: http://angulardart.org
|
||||||
|
@ -1,31 +1,104 @@
|
|||||||
# Triage Process and Github Labels for Angular 2
|
# Triage Process and Github Labels for Angular 2
|
||||||
|
|
||||||
This document describes how the Angular team uses labels and milestones to triage issues on github.
|
This document describes how the Angular team uses labels and milestones
|
||||||
|
to triage issues on github. The basic idea of the process is that
|
||||||
|
caretaker only assigns a component and type (bug, feature) label. The
|
||||||
|
owner of the component than is in full control of how the issues should
|
||||||
|
be triaged further.
|
||||||
|
|
||||||
# Issues and PRs
|
Once this process is implemented and in use, we will revisit it to see
|
||||||
## Triaged vs Untriaged Issues
|
if further labeling is needed.
|
||||||
|
|
||||||
Every triaged issue must have four attributes assigned to it:
|
## Components
|
||||||
|
|
||||||
* `priority` -- P0 through P4. P0 issues are "drop everything and do this now". P4 are nice to have.
|
A caretaker should be able to determine which component the issue
|
||||||
* `component` -- Which area of Angular knowledge this relates to.
|
belongs to. The components have a clear piece of source code associated
|
||||||
* `effort` -- Rough assessment of how much work this issue is. E.g. `effort: easy` means
|
with it.
|
||||||
"probably a few hours of work".
|
|
||||||
* `type` -- Whether this issue is a bug, feature, or other kind of task.
|
|
||||||
|
|
||||||
Untriaged issues are any issues in the queue that don't yet have these four attributes.
|
* `comp: animations`: `@matsko`
|
||||||
|
* `comp: benchpress`: `@tbosch`
|
||||||
|
* `comp: build & ci`: `@IgorMinar` -- All build and CI scripts
|
||||||
|
* `comp: common`: `@mhevery` -- This includes core components / pipes.
|
||||||
|
* `comp: core & compiler`: `@tbosch` -- Because core and compiler are very
|
||||||
|
intertwined, we will be treating them as one.
|
||||||
|
* `comp: forms`: `@kara`
|
||||||
|
* `comp: http`: `@jeffbcross`
|
||||||
|
* `comp: i18n`: `@vicb`
|
||||||
|
* `comp: metadata-extractor`: `@chuckjaz`
|
||||||
|
* `comp: router`: `@vsavkin`
|
||||||
|
* `comp: testing`: `@juliemr`
|
||||||
|
* `comp: upgrade`: `@mhevery`
|
||||||
|
* `comp: web-worker`: `@vicb`
|
||||||
|
* `comp: zones`: `@mhevery`
|
||||||
|
|
||||||
You can view a report of untriaged issues here, in our
|
There are few components which are cross-cutting. They don't have
|
||||||
[Angular Triage Dashboard](http://mhevery.github.io/github_issues/).
|
a clear location in the source tree. We will treat them as a component
|
||||||
|
even thought no specific source tree is associated with them.
|
||||||
|
|
||||||
|
* `comp: docs`: `@naomiblack`
|
||||||
|
* `comp: packaging`: `@IgorMinar`
|
||||||
|
* `comp: performance`: `@tbosch`
|
||||||
|
* `comp: security`: `@IgorMinar`
|
||||||
|
|
||||||
|
|
||||||
|
## Type
|
||||||
|
What kind of problem is this?
|
||||||
|
|
||||||
|
* `type: RFC / discussion / question`
|
||||||
|
* `type: bug`
|
||||||
|
* `type: chore`
|
||||||
|
* `type: feature`
|
||||||
|
* `type: performance`
|
||||||
|
* `type: refactor`
|
||||||
|
|
||||||
|
## Caretaker Triage Process
|
||||||
|
|
||||||
|
It is the caretaker's responsibility to assign `comp: *` to each new
|
||||||
|
issue as they come in. The reason why we limit the responsibility of the
|
||||||
|
caretaker to this one label is that it is likely that without domain
|
||||||
|
knowledge the caretaker could mislabel issues or lack knowledge of
|
||||||
|
duplicate issues.
|
||||||
|
|
||||||
|
|
||||||
|
## Component's owner Triage Process
|
||||||
|
|
||||||
|
At this point we are leaving each component owner to determine their own
|
||||||
|
process for their component.
|
||||||
|
|
||||||
|
It will be up to the component owner to determine the order in which the
|
||||||
|
issues within the component will be resolved.
|
||||||
|
|
||||||
|
Several owners have adopted the issue categorization based on
|
||||||
|
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
|
||||||
|
used by Angular 1. In this system every issue is assigned frequency and
|
||||||
|
severity based on which the total user pain score is calculated.
|
||||||
|
|
||||||
|
Following is the definition of various frequency and severity levels:
|
||||||
|
|
||||||
|
1. `freq(score): *` – How often does this issue come up? How many developers does this affect?
|
||||||
|
* low (1) - obscure issue affecting a handful of developers
|
||||||
|
* moderate (2) - impacts auxiliary usage patterns, only small number of applications are affected
|
||||||
|
* high (3) - impacts primary usage patterns, affecting most Angular apps
|
||||||
|
* critical (4) - impacts all Angular apps
|
||||||
|
1. `severity(score): *` - How bad is the issue?
|
||||||
|
* inconvenience (1) - causes ugly/boilerplate code in apps
|
||||||
|
* confusing (2) - unexpected or inconsistent behavior; hard-to-debug
|
||||||
|
* broken expected use (3) - it's hard or impossible for a developer using Angular to accomplish something that Angular should be able to do
|
||||||
|
* memory leak (4)
|
||||||
|
* regression (5) - functionality that used to work no longer works in a new release due to an unintentional change
|
||||||
|
* security issue (6)
|
||||||
|
|
||||||
|
|
||||||
|
These criteria are then used to calculate a "user pain" score as follows:
|
||||||
|
|
||||||
|
`pain = severity × frequency`
|
||||||
|
|
||||||
Issues should also have a clear action to complete that can be addressed or resolved within the
|
|
||||||
scope of Angular 2. We'll close issues that don't meet these criteria.
|
|
||||||
|
|
||||||
### Assigning Issues to Milestones
|
### Assigning Issues to Milestones
|
||||||
|
|
||||||
Any issue that is being worked on must have:
|
Any issue that is being worked on must have:
|
||||||
|
|
||||||
* An `assignee`: The person doing the work.
|
* An `Assignee`: The person doing the work.
|
||||||
* A `Milestone`: When we expect to complete this work.
|
* A `Milestone`: When we expect to complete this work.
|
||||||
|
|
||||||
We aim to only have at most three milestones open at a time:
|
We aim to only have at most three milestones open at a time:
|
||||||
@ -37,7 +110,10 @@ We aim to only have at most three milestones open at a time:
|
|||||||
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
|
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
|
||||||
consists of all issues that have been triaged but do not have an assignee or milestone.
|
consists of all issues that have been triaged but do not have an assignee or milestone.
|
||||||
|
|
||||||
## Triaged vs Untriaged PRs
|
## Triaged vs Untrained PRs
|
||||||
|
|
||||||
|
PRs should also be label with a `comp: *` so that it is clear which
|
||||||
|
primary area the PR effects.
|
||||||
|
|
||||||
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
||||||
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
||||||
@ -63,90 +139,6 @@ uncontroversial change.
|
|||||||
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
|
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
|
||||||
PR to land.
|
PR to land.
|
||||||
|
|
||||||
Victor (`vsavkin`) and Tobias (`tbosch`) are owners of the PR queue. Here is a list of [current
|
|
||||||
untriaged PRs](https://github.com/angular/angular/pulls?utf8=%E2%9C%93&q=is%3Aopen+no%3Amilestone+is%3Apr+-label%3A%22pr_action%3A+cleanup%22+-label%3A%22pr_action%3A+merge%22+-label%3A%22pr_action%3A+review%22+-label%3A%22pr_action%3A+discuss%22+-label%3A%22pr_state%3A+blocked%22+-label%3A%22pr_state%3A+WIP%22+).
|
|
||||||
|
|
||||||
# Prioritization of Work
|
|
||||||
|
|
||||||
What should you be working on?
|
|
||||||
|
|
||||||
1. Any PRs that are assigned to you that don't have `pr_state: WIP` or `pr_state: blocked`
|
|
||||||
1. Any issues that are assigned to you in the lowest-numbered Milestone
|
|
||||||
1. Any issues that are assigned to you in any Milestone
|
|
||||||
|
|
||||||
If there are no issues assigned to you in any Milestone, pick an issue, self-assign it, and add
|
|
||||||
it to the most appropriate Milestone based on effort.
|
|
||||||
|
|
||||||
Here are some suggestions for what to work on next:
|
|
||||||
|
|
||||||
* Filter for issues in a component that you are knowledgeable about, and pick something that has a
|
|
||||||
high priority.
|
|
||||||
* Filter for any small effort task that has the special `cust: GT` or `cust:Ionic` tags,
|
|
||||||
and priority > P3.
|
|
||||||
* Add a new task that's really important, add `component`, `priority`, `effort`, `type` and
|
|
||||||
assign it to yourself and the most appropriate milestone.
|
|
||||||
|
|
||||||
# Labels Used in Triage
|
|
||||||
|
|
||||||
## Priority
|
|
||||||
How urgent is this issue? We use priority to determine what should be worked on in each new
|
|
||||||
milestone.
|
|
||||||
|
|
||||||
* `P0: critical` -- drop everything to work on this
|
|
||||||
* `P1: urgent` -- resolve quickly in the current milestone. people are blocked
|
|
||||||
* `P2: required` -- needed for development but not urgent yet. workaround exists, or e.g. new API
|
|
||||||
* `P3: important` -- must complete before Angular 2 is ready for release
|
|
||||||
* `P4: nice to have` -- a good idea, but maybe not until after release
|
|
||||||
|
|
||||||
|
|
||||||
## Effort
|
|
||||||
Rough, non-binding estimate of how much work this issue represents. Please change this assessment
|
|
||||||
for anything you're working on to better reflect reality.
|
|
||||||
|
|
||||||
* `effort: easy` -- straightforward issue that can be resolved in a few hours, e.g. < 1 day of work.
|
|
||||||
* `effort: medium` -- issue that will be a few days of work. Can be completed within a single
|
|
||||||
milestone.
|
|
||||||
* `effort: tough` -- issue that will likely take more than 1 milestone to complete.
|
|
||||||
|
|
||||||
<!-- We don't like these label names as
|
|
||||||
they're not absolute (what is one developer-hour, really?) but decided it wasn't worth arguing
|
|
||||||
over terms. -->
|
|
||||||
|
|
||||||
## Component
|
|
||||||
Which area of Angular knowledge is this issue most closely related to? Helpful when deciding what
|
|
||||||
to work on next.
|
|
||||||
|
|
||||||
* `comp: benchpress` -- benchmarks and performance testing → *tbosch*, *crossj*
|
|
||||||
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks → *iminar*, *caitp*
|
|
||||||
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart → *mprobst*, *alexeagle*
|
|
||||||
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) →
|
|
||||||
*mhevery*
|
|
||||||
* `comp: core/animations` -- animations framework → *matsko*
|
|
||||||
* `comp: core/change_detection` -- change detection → *vsavkin*
|
|
||||||
* `comp: core/di` -- dependency injection → *vicb*, *rkirov*
|
|
||||||
* `comp: core/directives` -- directives
|
|
||||||
* `comp: core/forms` -- forms → *vsavkin*
|
|
||||||
* `comp: core/pipes` -- pipes
|
|
||||||
* `comp: core/view` -- runtime processing of the `View`s
|
|
||||||
* `comp: core/view/compiler` -- static analysis of the templates which generate `ProtoView`s.
|
|
||||||
* `comp: core/testbed` -- e2e tests and support for them
|
|
||||||
* `comp: core/webworker` -- core web worker infrastructure
|
|
||||||
* `comp: dart-transformer` -- Dart transforms → *kegluneq*, *jakemac*
|
|
||||||
* `comp: data-access` -- → *jeffbcross*
|
|
||||||
* `comp: docs` -- API docs and doc generation → *naomiblack*, *petebacondarwin*
|
|
||||||
* `comp: material-components` -- Angular Material components built in Angular 2 → *jelbourn*
|
|
||||||
* `comp: router` -- Component Router → *btford*, *igorminar*, *matsko*
|
|
||||||
* `comp: wrenchjs`
|
|
||||||
|
|
||||||
## Type
|
|
||||||
What kind of problem is this?
|
|
||||||
|
|
||||||
* `type RFC / discussion / question`
|
|
||||||
* `type bug`
|
|
||||||
* `type chore`
|
|
||||||
* `type feature`
|
|
||||||
* `type performance`
|
|
||||||
* `type refactor`
|
|
||||||
|
|
||||||
## Special Labels
|
## Special Labels
|
||||||
|
|
||||||
@ -160,9 +152,6 @@ More active discussion is needed before the issue can be worked on further. Typi
|
|||||||
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
||||||
`cla:yes` should be merged into master.
|
`cla:yes` should be merged into master.
|
||||||
|
|
||||||
### cust
|
|
||||||
This is an issue causing user pain for early adopter customers `cust: GT` or `cust: Ionic`.
|
|
||||||
|
|
||||||
### WORKS_AS_INTENDED
|
### WORKS_AS_INTENDED
|
||||||
|
|
||||||
Only used on closed issues, to indicate to the reporter why we closed it.
|
Only used on closed issues, to indicate to the reporter why we closed it.
|
||||||
|
@ -1,216 +1,98 @@
|
|||||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL)
|
||||||
|
// and BrowserStack (BS).
|
||||||
// If the target is set to null, then the browser is not run anywhere during CI.
|
// If the target is set to null, then the browser is not run anywhere during CI.
|
||||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
|
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented
|
||||||
|
// out in Travis configuration.
|
||||||
var CIconfiguration = {
|
var CIconfiguration = {
|
||||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
'Chrome': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
'Firefox': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||||
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
||||||
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
||||||
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
'ChromeBeta': {unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
||||||
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
'FirefoxBeta': {unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
||||||
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
'ChromeDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||||
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
'FirefoxDev': {unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||||
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
'IE9': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||||
'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'Edge': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
'Android4.1': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
'Android4.2': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
'Android4.3': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
'Android4.4': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
'Android5': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||||
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'Safari7': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||||
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
|
'iOS10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
|
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||||
};
|
};
|
||||||
|
|
||||||
var customLaunchers = {
|
var customLaunchers = {
|
||||||
'DartiumWithWebPlatform': {
|
'DartiumWithWebPlatform':
|
||||||
base: 'Dartium',
|
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
|
||||||
flags: ['--enable-experimental-web-platform-features'] },
|
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
|
||||||
'ChromeNoSandbox': {
|
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '52'},
|
||||||
base: 'Chrome',
|
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
|
||||||
flags: ['--no-sandbox'] },
|
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
|
||||||
'SL_CHROME': {
|
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '46'},
|
||||||
base: 'SauceLabs',
|
'SL_FIREFOXBETA': {base: 'SauceLabs', browserName: 'firefox', version: 'beta'},
|
||||||
browserName: 'chrome',
|
'SL_FIREFOXDEV': {base: 'SauceLabs', browserName: 'firefox', version: 'dev'},
|
||||||
version: '52'
|
'SL_SAFARI7': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.9', version: '7.0'},
|
||||||
},
|
'SL_SAFARI8': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.10', version: '8.0'},
|
||||||
'SL_CHROMEBETA': {
|
'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'},
|
||||||
base: 'SauceLabs',
|
'SL_SAFARI10':
|
||||||
browserName: 'chrome',
|
{base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.0'},
|
||||||
version: 'beta'
|
'SL_IOS7': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '7.1'},
|
||||||
},
|
'SL_IOS8': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '8.4'},
|
||||||
'SL_CHROMEDEV': {
|
'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'},
|
||||||
base: 'SauceLabs',
|
'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
|
||||||
browserName: 'chrome',
|
'SL_IE9':
|
||||||
version: 'dev'
|
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
|
||||||
},
|
|
||||||
'SL_FIREFOX': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'firefox',
|
|
||||||
version: '46'
|
|
||||||
},
|
|
||||||
'SL_FIREFOXBETA': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'firefox',
|
|
||||||
version: 'beta'
|
|
||||||
},
|
|
||||||
'SL_FIREFOXDEV': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'firefox',
|
|
||||||
version: 'dev'
|
|
||||||
},
|
|
||||||
'SL_SAFARI7': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'safari',
|
|
||||||
platform: 'OS X 10.9',
|
|
||||||
version: '7.0'
|
|
||||||
},
|
|
||||||
'SL_SAFARI8': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'safari',
|
|
||||||
platform: 'OS X 10.10',
|
|
||||||
version: '8.0'
|
|
||||||
},
|
|
||||||
'SL_SAFARI9': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'safari',
|
|
||||||
platform: 'OS X 10.11',
|
|
||||||
version: '9.0'
|
|
||||||
},
|
|
||||||
'SL_IOS7': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'iphone',
|
|
||||||
platform: 'OS X 10.10',
|
|
||||||
version: '7.1'
|
|
||||||
},
|
|
||||||
'SL_IOS8': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'iphone',
|
|
||||||
platform: 'OS X 10.10',
|
|
||||||
version: '8.4'
|
|
||||||
},
|
|
||||||
'SL_IOS9': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'iphone',
|
|
||||||
platform: 'OS X 10.10',
|
|
||||||
version: '9.3'
|
|
||||||
},
|
|
||||||
'SL_IE9': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'internet explorer',
|
|
||||||
platform: 'Windows 2008',
|
|
||||||
version: '9'
|
|
||||||
},
|
|
||||||
'SL_IE10': {
|
'SL_IE10': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'internet explorer',
|
browserName: 'internet explorer',
|
||||||
platform: 'Windows 2012',
|
platform: 'Windows 2012',
|
||||||
version: '10'
|
version: '10'
|
||||||
},
|
},
|
||||||
'SL_IE11': {
|
'SL_IE11':
|
||||||
base: 'SauceLabs',
|
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
|
||||||
browserName: 'internet explorer',
|
|
||||||
platform: 'Windows 8.1',
|
|
||||||
version: '11'
|
|
||||||
},
|
|
||||||
'SL_EDGE': {
|
'SL_EDGE': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'MicrosoftEdge',
|
browserName: 'MicrosoftEdge',
|
||||||
platform: 'Windows 10',
|
platform: 'Windows 10',
|
||||||
version: '13.10586'
|
version: '13.10586'
|
||||||
},
|
},
|
||||||
'SL_ANDROID4.1': {
|
'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.1'},
|
||||||
base: 'SauceLabs',
|
'SL_ANDROID4.2': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.2'},
|
||||||
browserName: 'android',
|
'SL_ANDROID4.3': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.3'},
|
||||||
platform: 'Linux',
|
'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'},
|
||||||
version: '4.1'
|
'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.1'},
|
||||||
},
|
|
||||||
'SL_ANDROID4.2': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'android',
|
|
||||||
platform: 'Linux',
|
|
||||||
version: '4.2'
|
|
||||||
},
|
|
||||||
'SL_ANDROID4.3': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'android',
|
|
||||||
platform: 'Linux',
|
|
||||||
version: '4.3'
|
|
||||||
},
|
|
||||||
'SL_ANDROID4.4': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'android',
|
|
||||||
platform: 'Linux',
|
|
||||||
version: '4.4'
|
|
||||||
},
|
|
||||||
'SL_ANDROID5': {
|
|
||||||
base: 'SauceLabs',
|
|
||||||
browserName: 'android',
|
|
||||||
platform: 'Linux',
|
|
||||||
version: '5.1'
|
|
||||||
},
|
|
||||||
|
|
||||||
'BS_CHROME': {
|
'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
|
||||||
base: 'BrowserStack',
|
'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
|
||||||
browser: 'chrome',
|
'BS_SAFARI7': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Mavericks'},
|
||||||
os: 'OS X',
|
'BS_SAFARI8': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Yosemite'},
|
||||||
os_version: 'Yosemite'
|
'BS_SAFARI9': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'El Capitan'},
|
||||||
},
|
'BS_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'},
|
||||||
'BS_FIREFOX': {
|
'BS_IOS7': {base: 'BrowserStack', device: 'iPhone 5S', os: 'ios', os_version: '7.0'},
|
||||||
base: 'BrowserStack',
|
'BS_IOS8': {base: 'BrowserStack', device: 'iPhone 6', os: 'ios', os_version: '8.3'},
|
||||||
browser: 'firefox',
|
'BS_IOS9': {base: 'BrowserStack', device: 'iPhone 6S', os: 'ios', os_version: '9.1'},
|
||||||
os: 'Windows',
|
'BS_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'},
|
||||||
os_version: '10'
|
'BS_IE9':
|
||||||
},
|
{base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'},
|
||||||
'BS_SAFARI7': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
browser: 'safari',
|
|
||||||
os: 'OS X',
|
|
||||||
os_version: 'Mavericks'
|
|
||||||
},
|
|
||||||
'BS_SAFARI8': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
browser: 'safari',
|
|
||||||
os: 'OS X',
|
|
||||||
os_version: 'Yosemite'
|
|
||||||
},
|
|
||||||
'BS_SAFARI9': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
browser: 'safari',
|
|
||||||
os: 'OS X',
|
|
||||||
os_version: 'El Capitan'
|
|
||||||
},
|
|
||||||
'BS_IOS7': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'iPhone 5S',
|
|
||||||
os: 'ios',
|
|
||||||
os_version: '7.0'
|
|
||||||
},
|
|
||||||
'BS_IOS8': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'iPhone 6',
|
|
||||||
os: 'ios',
|
|
||||||
os_version: '8.3'
|
|
||||||
},
|
|
||||||
'BS_IOS9': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'iPhone 6S',
|
|
||||||
os: 'ios',
|
|
||||||
os_version: '9.1'
|
|
||||||
},
|
|
||||||
'BS_IE9': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
browser: 'ie',
|
|
||||||
browser_version: '9.0',
|
|
||||||
os: 'Windows',
|
|
||||||
os_version: '7'
|
|
||||||
},
|
|
||||||
'BS_IE10': {
|
'BS_IE10': {
|
||||||
base: 'BrowserStack',
|
base: 'BrowserStack',
|
||||||
browser: 'ie',
|
browser: 'ie',
|
||||||
@ -225,58 +107,35 @@ var customLaunchers = {
|
|||||||
os: 'Windows',
|
os: 'Windows',
|
||||||
os_version: '10'
|
os_version: '10'
|
||||||
},
|
},
|
||||||
'BS_EDGE': {
|
'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'},
|
||||||
base: 'BrowserStack',
|
'BS_WINDOWSPHONE':
|
||||||
browser: 'edge',
|
{base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.1'},
|
||||||
os: 'Windows',
|
'BS_ANDROID5': {base: 'BrowserStack', device: 'Google Nexus 5', os: 'android', os_version: '5.0'},
|
||||||
os_version: '10'
|
'BS_ANDROID4.4': {base: 'BrowserStack', device: 'HTC One M8', os: 'android', os_version: '4.4'},
|
||||||
},
|
'BS_ANDROID4.3':
|
||||||
'BS_WINDOWSPHONE' : {
|
{base: 'BrowserStack', device: 'Samsung Galaxy S4', os: 'android', os_version: '4.3'},
|
||||||
base: 'BrowserStack',
|
'BS_ANDROID4.2':
|
||||||
device: 'Nokia Lumia 930',
|
{base: 'BrowserStack', device: 'Google Nexus 4', os: 'android', os_version: '4.2'},
|
||||||
os: 'winphone',
|
'BS_ANDROID4.1':
|
||||||
os_version: '8.1'
|
{base: 'BrowserStack', device: 'Google Nexus 7', os: 'android', os_version: '4.1'}
|
||||||
},
|
|
||||||
'BS_ANDROID5': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'Google Nexus 5',
|
|
||||||
os: 'android',
|
|
||||||
os_version: '5.0'
|
|
||||||
},
|
|
||||||
'BS_ANDROID4.4': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'HTC One M8',
|
|
||||||
os: 'android',
|
|
||||||
os_version: '4.4'
|
|
||||||
},
|
|
||||||
'BS_ANDROID4.3': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'Samsung Galaxy S4',
|
|
||||||
os: 'android',
|
|
||||||
os_version: '4.3'
|
|
||||||
},
|
|
||||||
'BS_ANDROID4.2': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'Google Nexus 4',
|
|
||||||
os: 'android',
|
|
||||||
os_version: '4.2'
|
|
||||||
},
|
|
||||||
'BS_ANDROID4.1': {
|
|
||||||
base: 'BrowserStack',
|
|
||||||
device: 'Google Nexus 7',
|
|
||||||
os: 'android',
|
|
||||||
os_version: '4.1'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var sauceAliases = {
|
var sauceAliases = {
|
||||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
'ALL': Object.keys(customLaunchers).filter(function(item) {
|
||||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
return customLaunchers[item].base == 'SauceLabs';
|
||||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
}),
|
||||||
|
'DESKTOP': [
|
||||||
|
'SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7',
|
||||||
|
'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'
|
||||||
|
],
|
||||||
|
'MOBILE': [
|
||||||
|
'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7',
|
||||||
|
'SL_IOS8', 'SL_IOS9', 'SL_IOS10'
|
||||||
|
],
|
||||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
|
||||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
|
||||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||||
@ -284,13 +143,20 @@ var sauceAliases = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var browserstackAliases = {
|
var browserstackAliases = {
|
||||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
|
'ALL': Object.keys(customLaunchers).filter(function(item) {
|
||||||
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
return customLaunchers[item].base == 'BrowserStack';
|
||||||
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
|
}),
|
||||||
|
'DESKTOP': [
|
||||||
|
'BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7',
|
||||||
|
'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'
|
||||||
|
],
|
||||||
|
'MOBILE': [
|
||||||
|
'BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'
|
||||||
|
],
|
||||||
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
|
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
|
||||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
|
||||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
|
||||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||||
};
|
};
|
||||||
@ -303,11 +169,9 @@ module.exports = {
|
|||||||
|
|
||||||
function buildConfiguration(type, target, required) {
|
function buildConfiguration(type, target, required) {
|
||||||
return Object.keys(CIconfiguration)
|
return Object.keys(CIconfiguration)
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
var conf = CIconfiguration[item][type];
|
var conf = CIconfiguration[item][type];
|
||||||
return conf.required === required && conf.target === target;
|
return conf.required === required && conf.target === target;
|
||||||
})
|
})
|
||||||
.map((item) => {
|
.map((item) => target + '_' + item.toUpperCase());
|
||||||
return target + '_' + item.toUpperCase();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
164
build.sh
164
build.sh
@ -4,73 +4,97 @@ set -e -o pipefail
|
|||||||
|
|
||||||
cd `dirname $0`
|
cd `dirname $0`
|
||||||
|
|
||||||
|
PACKAGES=(core
|
||||||
|
compiler
|
||||||
|
common
|
||||||
|
forms
|
||||||
|
platform-browser
|
||||||
|
platform-browser-dynamic
|
||||||
|
platform-server
|
||||||
|
platform-webworker
|
||||||
|
platform-webworker-dynamic
|
||||||
|
http
|
||||||
|
router
|
||||||
|
upgrade
|
||||||
|
compiler-cli
|
||||||
|
benchpress)
|
||||||
|
BUILD_ALL=true
|
||||||
|
BUNDLE=true
|
||||||
|
|
||||||
|
for ARG in "$@"; do
|
||||||
|
case "$ARG" in
|
||||||
|
--packages=*)
|
||||||
|
PACKAGES_STR=${ARG#--packages=}
|
||||||
|
PACKAGES=( ${PACKAGES_STR//,/ } )
|
||||||
|
BUILD_ALL=false
|
||||||
|
;;
|
||||||
|
--bundle=*)
|
||||||
|
BUNDLE=( "${ARG#--bundle=}" )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option $ARG."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||||
|
|
||||||
|
|
||||||
rm -rf ./dist/all/
|
|
||||||
mkdir -p ./dist/all/
|
|
||||||
|
|
||||||
TSCONFIG=./tools/tsconfig.json
|
|
||||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
|
||||||
$(npm bin)/tsc -p ${TSCONFIG}
|
|
||||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
|
||||||
|
|
||||||
echo "====== Copying files needed for e2e tests ====="
|
|
||||||
cp -r ./modules/playground ./dist/all/
|
|
||||||
cp -r ./modules/playground/favicon.ico ./dist/
|
|
||||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
|
||||||
mkdir ./dist/all/playground/vendor
|
|
||||||
cd ./dist/all/playground/vendor
|
|
||||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
|
||||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
|
||||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
|
||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
|
||||||
ln -s ../../../../node_modules/rxjs .
|
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
|
||||||
cd -
|
|
||||||
|
|
||||||
echo "====== Copying files needed for benchmarks ====="
|
|
||||||
cp -r ./modules/benchmarks ./dist/all/
|
|
||||||
cp -r ./modules/benchmarks/favicon.ico ./dist/
|
|
||||||
mkdir ./dist/all/benchmarks/vendor
|
|
||||||
cd ./dist/all/benchmarks/vendor
|
|
||||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
|
||||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
|
||||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
|
||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
|
||||||
ln -s ../../../../node_modules/rxjs .
|
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
|
||||||
ln -s ../../../../bower_components/polymer .
|
|
||||||
cd -
|
|
||||||
|
|
||||||
TSCONFIG=./modules/tsconfig.json
|
|
||||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
|
||||||
# compile ts code
|
|
||||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||||
$TSC -p modules/tsconfig.json
|
TSCONFIG=./tools/tsconfig.json
|
||||||
|
echo "====== (tools)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||||
|
rm -rf ./dist/tools/
|
||||||
|
mkdir -p ./dist/tools/
|
||||||
|
$(npm bin)/tsc -p ${TSCONFIG}
|
||||||
|
|
||||||
rm -rf ./dist/packages-dist
|
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||||
|
|
||||||
for PACKAGE in \
|
if [[ ${BUILD_ALL} == true ]]; then
|
||||||
core \
|
rm -rf ./dist/all/
|
||||||
compiler \
|
mkdir -p ./dist/all/
|
||||||
common \
|
|
||||||
forms \
|
echo "====== Copying files needed for e2e tests ====="
|
||||||
platform-browser \
|
cp -r ./modules/playground ./dist/all/
|
||||||
platform-browser-dynamic \
|
cp -r ./modules/playground/favicon.ico ./dist/
|
||||||
platform-server \
|
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||||
platform-webworker \
|
mkdir ./dist/all/playground/vendor
|
||||||
platform-webworker-dynamic \
|
cd ./dist/all/playground/vendor
|
||||||
http \
|
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||||
router \
|
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||||
upgrade \
|
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||||
compiler-cli \
|
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||||
benchpress
|
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||||
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
|
ln -s ../../../../node_modules/rxjs .
|
||||||
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
|
cd -
|
||||||
|
|
||||||
|
echo "====== Copying files needed for benchmarks ====="
|
||||||
|
cp -r ./modules/benchmarks ./dist/all/
|
||||||
|
cp -r ./modules/benchmarks/favicon.ico ./dist/
|
||||||
|
mkdir ./dist/all/benchmarks/vendor
|
||||||
|
cd ./dist/all/benchmarks/vendor
|
||||||
|
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||||
|
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||||
|
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||||
|
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||||
|
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||||
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
|
ln -s ../../../../node_modules/rxjs .
|
||||||
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
|
ln -s ../../../../bower_components/polymer .
|
||||||
|
ln -s ../../../../node_modules/incremental-dom/dist/incremental-dom-cjs.js
|
||||||
|
cd -
|
||||||
|
|
||||||
|
TSCONFIG=./modules/tsconfig.json
|
||||||
|
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||||
|
# compile ts code
|
||||||
|
$TSC -p modules/tsconfig.json
|
||||||
|
|
||||||
|
rm -rf ./dist/packages-dist
|
||||||
|
fi
|
||||||
|
|
||||||
|
for PACKAGE in ${PACKAGES[@]}
|
||||||
do
|
do
|
||||||
PWD=`pwd`
|
PWD=`pwd`
|
||||||
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
||||||
@ -80,10 +104,13 @@ do
|
|||||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||||
|
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig.json ====="
|
rm -rf ${DESTDIR}
|
||||||
$TSC -p ${SRCDIR}/tsconfig.json
|
|
||||||
|
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||||
|
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||||
|
|
||||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||||
|
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||||
|
|
||||||
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
|
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
|
||||||
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"
|
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"
|
||||||
@ -102,7 +129,12 @@ do
|
|||||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
|
if [[ ${PACKAGE} == benchpress ]]; then
|
||||||
|
cp ${SRCDIR}/*.md ${DESTDIR}
|
||||||
|
cp -r ${SRCDIR}/docs ${DESTDIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${BUNDLE} == true && ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
|
||||||
|
|
||||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||||
mkdir ${DESTDIR}/bundles
|
mkdir ${DESTDIR}/bundles
|
||||||
@ -129,3 +161,5 @@ do
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
./modules/@angular/examples/build.sh
|
||||||
|
191
gulpfile.js
191
gulpfile.js
@ -1,31 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||||
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||||
require('./tools/check-environment')(
|
require('./tools/check-environment')({
|
||||||
{requiredNpmVersion: '>=3.5.3 <4.0.0', requiredNodeVersion: '>=5.4.1 <6.0.0'});
|
requiredNpmVersion: '>=3.5.3 <4.0.0',
|
||||||
|
requiredNodeVersion: '>=5.4.1 <7.0.0',
|
||||||
|
});
|
||||||
|
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
|
||||||
const srcsToFmt =
|
// clang-format entry points
|
||||||
['tools/**/*.ts', 'modules/@angular/**/*.ts', '!tools/public_api_guard/**/*.d.ts',
|
const srcsToFmt = [
|
||||||
'modules/playground/**/*.ts', 'modules/benchmarks/**/*.ts', 'modules/e2e_util/**/*.ts'];
|
'modules/@angular/**/*.{js,ts}',
|
||||||
|
'modules/benchmarks/**/*.{js,ts}',
|
||||||
|
'modules/e2e_util/**/*.{js,ts}',
|
||||||
|
'modules/playground/**/*.{js,ts}',
|
||||||
|
'tools/**/*.{js,ts}',
|
||||||
|
'!tools/public_api_guard/**/*.d.ts',
|
||||||
|
'./*.{js,ts}',
|
||||||
|
'!shims_for_IE.js',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check source code for formatting errors (clang-format)
|
||||||
gulp.task('format:enforce', () => {
|
gulp.task('format:enforce', () => {
|
||||||
const format = require('gulp-clang-format');
|
const format = require('gulp-clang-format');
|
||||||
const clangFormat = require('clang-format');
|
const clangFormat = require('clang-format');
|
||||||
return gulp.src(srcsToFmt).pipe(
|
return gulp.src(srcsToFmt).pipe(
|
||||||
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Format the source code with clang-format (see .clang-format)
|
||||||
gulp.task('format', () => {
|
gulp.task('format', () => {
|
||||||
const format = require('gulp-clang-format');
|
const format = require('gulp-clang-format');
|
||||||
const clangFormat = require('clang-format');
|
const clangFormat = require('clang-format');
|
||||||
return gulp.src(srcsToFmt, { base: '.' }).pipe(
|
return gulp.src(srcsToFmt, {base: '.'})
|
||||||
format.format('file', clangFormat)).pipe(gulp.dest('.'));
|
.pipe(format.format('file', clangFormat))
|
||||||
|
.pipe(gulp.dest('.'));
|
||||||
});
|
});
|
||||||
|
|
||||||
const entrypoints = [
|
const entrypoints = [
|
||||||
@ -49,78 +69,110 @@ const entrypoints = [
|
|||||||
'dist/packages-dist/http/index.d.ts',
|
'dist/packages-dist/http/index.d.ts',
|
||||||
'dist/packages-dist/http/testing/index.d.ts',
|
'dist/packages-dist/http/testing/index.d.ts',
|
||||||
'dist/packages-dist/forms/index.d.ts',
|
'dist/packages-dist/forms/index.d.ts',
|
||||||
'dist/packages-dist/router/index.d.ts'
|
'dist/packages-dist/router/index.d.ts',
|
||||||
];
|
];
|
||||||
const publicApiDir = path.normalize('tools/public_api_guard');
|
const publicApiDir = path.normalize('tools/public_api_guard');
|
||||||
const publicApiArgs = [
|
const publicApiArgs = [
|
||||||
'--rootDir', 'dist/packages-dist',
|
'--rootDir',
|
||||||
'--stripExportPattern', '^__',
|
'dist/packages-dist',
|
||||||
'--allowModuleIdentifiers', 'jasmine',
|
'--stripExportPattern',
|
||||||
'--allowModuleIdentifiers', 'protractor',
|
'^__',
|
||||||
'--allowModuleIdentifiers', 'angular',
|
'--allowModuleIdentifiers',
|
||||||
'--onStabilityMissing', 'error'
|
'jasmine',
|
||||||
|
'--allowModuleIdentifiers',
|
||||||
|
'protractor',
|
||||||
|
'--allowModuleIdentifiers',
|
||||||
|
'angular',
|
||||||
|
'--onStabilityMissing',
|
||||||
|
'error',
|
||||||
].concat(entrypoints);
|
].concat(entrypoints);
|
||||||
|
|
||||||
|
// Build angular
|
||||||
gulp.task('build.sh', (done) => {
|
gulp.task('build.sh', (done) => {
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
childProcess.exec(path.join(__dirname, 'build.sh'), error => done(error));
|
childProcess.exec(path.join(__dirname, 'build.sh'), done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enforce that the public API matches the golden files
|
||||||
// Note that these two commands work on built d.ts files instead of the source
|
// Note that these two commands work on built d.ts files instead of the source
|
||||||
gulp.task('public-api:enforce', (done) => {
|
gulp.task('public-api:enforce', (done) => {
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
childProcess
|
childProcess
|
||||||
.spawn(
|
.spawn(
|
||||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||||
.on('close', (errorCode) => {
|
.on('close', (errorCode) => {
|
||||||
if (errorCode !== 0) {
|
if (errorCode !== 0) {
|
||||||
done(new Error(
|
done(new Error(
|
||||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||||
} else {
|
} else {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Generate the public API golden files
|
||||||
gulp.task('public-api:update', ['build.sh'], (done) => {
|
gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
childProcess
|
childProcess
|
||||||
.spawn(
|
.spawn(
|
||||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||||
.on('close', (errorCode) => done(errorCode));
|
.on('close', done);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the
|
||||||
|
// focused tests is found.
|
||||||
|
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded
|
||||||
|
// tests in our code base.
|
||||||
|
gulp.task('check-tests', function() {
|
||||||
|
const ddescribeIit = require('gulp-ddescribe-iit');
|
||||||
|
return gulp
|
||||||
|
.src([
|
||||||
|
'modules/**/*.spec.ts',
|
||||||
|
'modules/**/*_spec.ts',
|
||||||
|
])
|
||||||
|
.pipe(ddescribeIit({allowDisabledTests: true}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check the coding standards and programming errors
|
||||||
|
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||||
const tslint = require('gulp-tslint');
|
const tslint = require('gulp-tslint');
|
||||||
// Built-in rules are at
|
// Built-in rules are at
|
||||||
// https://github.com/palantir/tslint#supported-rules
|
// https://github.com/palantir/tslint#supported-rules
|
||||||
const tslintConfig = require('./tslint.json');
|
const tslintConfig = require('./tslint.json');
|
||||||
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts'])
|
return gulp
|
||||||
.pipe(tslint({
|
.src([
|
||||||
tslint: require('tslint').default,
|
// todo(vicb): add .js files when supported
|
||||||
configuration: tslintConfig,
|
// see https://github.com/palantir/tslint/pull/1515
|
||||||
rulesDirectory: 'dist/tools/tslint',
|
'modules/@angular/**/*.ts',
|
||||||
formatter: 'prose'
|
'modules/benchpress/**/*.ts',
|
||||||
}))
|
'./*.ts',
|
||||||
.pipe(tslint.report({emitError: true}));
|
])
|
||||||
|
.pipe(tslint({
|
||||||
|
tslint: require('tslint').default,
|
||||||
|
configuration: tslintConfig,
|
||||||
|
rulesDirectory: 'dist/tools/tslint',
|
||||||
|
formatter: 'prose',
|
||||||
|
}))
|
||||||
|
.pipe(tslint.report({emitError: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||||
|
|
||||||
|
// Check for circular dependency in the source code
|
||||||
gulp.task('check-cycle', (done) => {
|
gulp.task('check-cycle', (done) => {
|
||||||
const madge = require('madge');
|
const madge = require('madge');
|
||||||
|
|
||||||
var dependencyObject = madge(['dist/all/'], {
|
const dependencyObject = madge(['dist/all/'], {
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
extensions: ['.js'],
|
extensions: ['.js'],
|
||||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); }
|
||||||
});
|
});
|
||||||
var circularDependencies = dependencyObject.circular().getArray();
|
const circularDependencies = dependencyObject.circular().getArray();
|
||||||
if (circularDependencies.length > 0) {
|
if (circularDependencies.length > 0) {
|
||||||
console.log('Found circular dependencies!');
|
console.log('Found circular dependencies!');
|
||||||
console.log(circularDependencies);
|
console.log(circularDependencies);
|
||||||
@ -129,34 +181,47 @@ gulp.task('check-cycle', (done) => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Serve the built files
|
||||||
gulp.task('serve', () => {
|
gulp.task('serve', () => {
|
||||||
let connect = require('gulp-connect');
|
const connect = require('gulp-connect');
|
||||||
let cors = require('cors');
|
const cors = require('cors');
|
||||||
|
|
||||||
connect.server({
|
connect.server({
|
||||||
root: `${__dirname}/dist`,
|
root: `${__dirname}/dist`,
|
||||||
port: 8000,
|
port: 8000,
|
||||||
livereload: false,
|
livereload: false,
|
||||||
open: false,
|
open: false,
|
||||||
middleware: (connect, opt) => [cors()]
|
middleware: (connect, opt) => [cors()],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve the examples
|
||||||
|
gulp.task('serve-examples', () => {
|
||||||
|
const connect = require('gulp-connect');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
connect.server({
|
||||||
|
root: `${__dirname}/dist/examples`,
|
||||||
|
port: 8001,
|
||||||
|
livereload: false,
|
||||||
|
open: false,
|
||||||
|
middleware: (connect, opt) => [cors()],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Update the changelog with the latest changes
|
||||||
gulp.task('changelog', () => {
|
gulp.task('changelog', () => {
|
||||||
const conventionalChangelog = require('gulp-conventional-changelog');
|
const conventionalChangelog = require('gulp-conventional-changelog');
|
||||||
|
|
||||||
return gulp.src('CHANGELOG.md')
|
return gulp.src('CHANGELOG.md')
|
||||||
.pipe(conventionalChangelog({
|
.pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, {
|
||||||
preset: 'angular',
|
// Conventional Changelog Context
|
||||||
releaseCount: 1
|
// We have to manually set version number so it doesn't get prefixed with `v`
|
||||||
}, {
|
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
||||||
// Conventional Changelog Context
|
currentTag: require('./package.json').version
|
||||||
// We have to manually set version number so it doesn't get prefixed with `v`
|
}))
|
||||||
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
.pipe(gulp.dest('./'));
|
||||||
currentTag: require('./package.json').version
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest('./'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function tsc(projectPath, done) {
|
function tsc(projectPath, done) {
|
||||||
@ -164,8 +229,12 @@ function tsc(projectPath, done) {
|
|||||||
|
|
||||||
childProcess
|
childProcess
|
||||||
.spawn(
|
.spawn(
|
||||||
path.normalize(`${__dirname}/node_modules/.bin/tsc`) + (/^win/.test(os.platform()) ? '.cmd' : ''),
|
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
|
||||||
['-p', path.join(__dirname, projectPath)],
|
['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
|
||||||
{stdio: 'inherit'})
|
.on('close', done);
|
||||||
.on('close', (errorCode) => done(errorCode));
|
}
|
||||||
|
|
||||||
|
// returns the script path for the current platform
|
||||||
|
function platformScriptPath(path) {
|
||||||
|
return /^win/.test(os.platform()) ? `${path}.cmd` : path;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||||
var internalAngularReporter = require('./tools/karma/reporter.js');
|
var internalAngularReporter = require('./tools/karma/reporter.js');
|
||||||
|
|
||||||
@ -17,24 +25,25 @@ module.exports = function(config) {
|
|||||||
// include Angular v1 for upgrade module testing
|
// include Angular v1 for upgrade module testing
|
||||||
'node_modules/angular/angular.min.js',
|
'node_modules/angular/angular.min.js',
|
||||||
|
|
||||||
'node_modules/zone.js/dist/zone.js',
|
'node_modules/zone.js/dist/zone.js', 'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
'node_modules/zone.js/dist/proxy.js', 'node_modules/zone.js/dist/sync-test.js',
|
||||||
'node_modules/zone.js/dist/proxy.js',
|
'node_modules/zone.js/dist/jasmine-patch.js', 'node_modules/zone.js/dist/async-test.js',
|
||||||
'node_modules/zone.js/dist/sync-test.js',
|
|
||||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
|
||||||
'node_modules/zone.js/dist/async-test.js',
|
|
||||||
'node_modules/zone.js/dist/fake-async-test.js',
|
'node_modules/zone.js/dist/fake-async-test.js',
|
||||||
|
|
||||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||||
'shims_for_IE.js',
|
'shims_for_IE.js', 'node_modules/systemjs/dist/system.src.js',
|
||||||
'node_modules/systemjs/dist/system.src.js',
|
|
||||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||||
'node_modules/reflect-metadata/Reflect.js',
|
'node_modules/reflect-metadata/Reflect.js', 'tools/build/file2modulename.js', 'test-main.js',
|
||||||
'tools/build/file2modulename.js',
|
{pattern: 'dist/all/empty.*', included: false, watched: false}, {
|
||||||
'test-main.js',
|
pattern: 'modules/@angular/platform-browser/test/static_assets/**',
|
||||||
{pattern: 'dist/all/empty.*', included: false, watched: false},
|
included: false,
|
||||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
watched: false
|
||||||
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
|
},
|
||||||
|
{
|
||||||
|
pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**',
|
||||||
|
included: false,
|
||||||
|
watched: false,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
exclude: [
|
exclude: [
|
||||||
@ -43,7 +52,8 @@ module.exports = function(config) {
|
|||||||
'dist/all/@angular/compiler-cli/**',
|
'dist/all/@angular/compiler-cli/**',
|
||||||
'dist/all/@angular/benchpress/**',
|
'dist/all/@angular/benchpress/**',
|
||||||
'dist/all/angular1_router.js',
|
'dist/all/angular1_router.js',
|
||||||
'dist/all/@angular/platform-browser/testing/e2e_util.js'
|
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||||
|
'dist/examples/**/e2e_test/**',
|
||||||
],
|
],
|
||||||
|
|
||||||
customLaunchers: browserProvidersConf.customLaunchers,
|
customLaunchers: browserProvidersConf.customLaunchers,
|
||||||
@ -54,11 +64,11 @@ module.exports = function(config) {
|
|||||||
'karma-sauce-launcher',
|
'karma-sauce-launcher',
|
||||||
'karma-chrome-launcher',
|
'karma-chrome-launcher',
|
||||||
'karma-sourcemap-loader',
|
'karma-sourcemap-loader',
|
||||||
internalAngularReporter
|
internalAngularReporter,
|
||||||
],
|
],
|
||||||
|
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
'**/*.js': ['sourcemap']
|
'**/*.js': ['sourcemap'],
|
||||||
},
|
},
|
||||||
|
|
||||||
reporters: ['internal-angular'],
|
reporters: ['internal-angular'],
|
||||||
@ -72,7 +82,7 @@ module.exports = function(config) {
|
|||||||
'selenium-version': '2.53.0',
|
'selenium-version': '2.53.0',
|
||||||
'command-timeout': 600,
|
'command-timeout': 600,
|
||||||
'idle-timeout': 600,
|
'idle-timeout': 600,
|
||||||
'max-duration': 5400
|
'max-duration': 5400,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -81,20 +91,21 @@ module.exports = function(config) {
|
|||||||
startTunnel: false,
|
startTunnel: false,
|
||||||
retryLimit: 3,
|
retryLimit: 3,
|
||||||
timeout: 600,
|
timeout: 600,
|
||||||
pollingTimeout: 10000
|
pollingTimeout: 10000,
|
||||||
},
|
},
|
||||||
|
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
port: 9876,
|
port: 9876,
|
||||||
captureTimeout: 60000,
|
captureTimeout: 60000,
|
||||||
browserDisconnectTimeout : 60000,
|
browserDisconnectTimeout: 60000,
|
||||||
browserDisconnectTolerance : 3,
|
browserDisconnectTolerance: 3,
|
||||||
browserNoActivityTimeout : 60000,
|
browserNoActivityTimeout: 60000,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.TRAVIS) {
|
if (process.env.TRAVIS) {
|
||||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
var buildId =
|
||||||
|
'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||||
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
||||||
config.sauceLabs.build = buildId;
|
config.sauceLabs.build = buildId;
|
||||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
Angular2
|
|
||||||
=========
|
|
||||||
|
|
||||||
The sources for this package are in the main [Angular2](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo. This is the repository for the upcoming 2.0 version. If you're looking for the current official version of Angular you should go to [angular/angular.js](https://github.com/angular/angular.js)
|
|
||||||
|
|
||||||
This package contains different sources for different users:
|
|
||||||
|
|
||||||
1. The files located in the root folder can be consumed using CommonJS.
|
|
||||||
2. The files under `/es6` are es6 compatible files that can be transpiled to
|
|
||||||
es5 using any transpiler. This contains:
|
|
||||||
* `dev/`: a development version that includes runtime type assertions
|
|
||||||
* `prod/`: a production version that does not include runtime type assertions
|
|
||||||
3. The files under `/ts` are the TypeScript source files.
|
|
||||||
|
|
||||||
License: Apache MIT 2.0
|
|
6
modules/@angular/README.md
Normal file
6
modules/@angular/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Angular
|
||||||
|
=======
|
||||||
|
|
||||||
|
The sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
||||||
|
|
||||||
|
License: MIT
|
@ -1,18 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/benchpress",
|
"name": "@angular/benchpress",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.1.0",
|
||||||
"description": "Benchpress - a framework for e2e performance tests",
|
"description": "Benchpress - a framework for e2e performance tests",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/core": "0.0.0-PLACEHOLDER",
|
"@angular/core": "^2.0.0-rc.7",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"rxjs": "5.0.0-beta.11"
|
"rxjs": "5.0.0-beta.12",
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"jpm": "1.1.4",
|
"jpm": "1.1.4",
|
||||||
"firefox-profile": "0.4.0",
|
"firefox-profile": "0.4.0",
|
||||||
"selenium-webdriver": "3.0.0-beta-2"
|
"selenium-webdriver": "^2.53.3"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
16
modules/@angular/benchpress/publish.sh
Executable file
16
modules/@angular/benchpress/publish.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
cd $(dirname $0)/../../..
|
||||||
|
ROOTDIR=$(pwd)
|
||||||
|
SRCDIR=${ROOTDIR}/modules/@angular/benchpress
|
||||||
|
DESTDIR=${ROOTDIR}/dist/packages-dist/benchpress
|
||||||
|
|
||||||
|
rm -fr ${DESTDIR}
|
||||||
|
|
||||||
|
echo "====== BUILDING... ====="
|
||||||
|
./build.sh --packages=core,benchpress --bundle=false
|
||||||
|
|
||||||
|
echo "====== PUBLISHING: ${DESTDIR} ====="
|
||||||
|
npm publish ${DESTDIR} --access public
|
@ -9,8 +9,6 @@
|
|||||||
import {OpaqueToken} from '@angular/core';
|
import {OpaqueToken} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
import {DateWrapper} from './facade/lang';
|
|
||||||
|
|
||||||
export class Options {
|
export class Options {
|
||||||
static SAMPLE_ID = new OpaqueToken('Options.sampleId');
|
static SAMPLE_ID = new OpaqueToken('Options.sampleId');
|
||||||
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
|
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
|
||||||
@ -34,7 +32,7 @@ export class Options {
|
|||||||
{provide: Options.FORCE_GC, useValue: false},
|
{provide: Options.FORCE_GC, useValue: false},
|
||||||
{provide: Options.PREPARE, useValue: Options.NO_PREPARE},
|
{provide: Options.PREPARE, useValue: Options.NO_PREPARE},
|
||||||
{provide: Options.MICRO_METRICS, useValue: {}}, {provide: Options.USER_METRICS, useValue: {}},
|
{provide: Options.MICRO_METRICS, useValue: {}}, {provide: Options.USER_METRICS, useValue: {}},
|
||||||
{provide: Options.NOW, useValue: () => DateWrapper.now()},
|
{provide: Options.NOW, useValue: () => new Date()},
|
||||||
{provide: Options.RECEIVED_DATA, useValue: false},
|
{provide: Options.RECEIVED_DATA, useValue: false},
|
||||||
{provide: Options.REQUEST_COUNT, useValue: false},
|
{provide: Options.REQUEST_COUNT, useValue: false},
|
||||||
{provide: Options.CAPTURE_FRAMES, useValue: false},
|
{provide: Options.CAPTURE_FRAMES, useValue: false},
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
library benchpress.src.firefox_extension.data.installed_script;
|
|
||||||
|
|
||||||
//no dart implementation
|
|
@ -42,11 +42,11 @@ class Profiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addStartEvent(name: string, timeStarted: number) {
|
addStartEvent(name: string, timeStarted: number) {
|
||||||
this._markerEvents.push({ph: 'b', ts: timeStarted - this._profilerStartTime, name: name});
|
this._markerEvents.push({ph: 'B', ts: timeStarted - this._profilerStartTime, name: name});
|
||||||
}
|
}
|
||||||
|
|
||||||
addEndEvent(name: string, timeEnded: number) {
|
addEndEvent(name: string, timeEnded: number) {
|
||||||
this._markerEvents.push({ph: 'e', ts: timeEnded - this._profilerStartTime, name: name});
|
this._markerEvents.push({ph: 'E', ts: timeEnded - this._profilerStartTime, name: name});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,18 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Map} from './facade/collection';
|
|
||||||
import {Date, DateWrapper} from './facade/lang';
|
|
||||||
|
|
||||||
export class MeasureValues {
|
export class MeasureValues {
|
||||||
constructor(
|
constructor(
|
||||||
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
|
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
'timeStamp': DateWrapper.toJson(this.timeStamp),
|
'timeStamp': this.timeStamp.toJSON(),
|
||||||
'runIndex': this.runIndex,
|
'runIndex': this.runIndex,
|
||||||
'values': this.values
|
'values': this.values,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, OpaqueToken} from '@angular/core';
|
import {Injector, OpaqueToken} from '@angular/core';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
|
||||||
|
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
|
|
||||||
@ -57,8 +56,7 @@ export class MultiMetric extends Metric {
|
|||||||
|
|
||||||
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
||||||
var result: {[key: string]: string} = {};
|
var result: {[key: string]: string} = {};
|
||||||
maps.forEach(
|
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
||||||
map => { StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; }); });
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
|
||||||
import {Math, NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
|
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||||
|
|
||||||
@ -95,8 +93,9 @@ export class PerflogMetric extends Metric {
|
|||||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringMapWrapper.forEach(
|
for (let name in this._microMetrics) {
|
||||||
this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); });
|
res[name] = this._microMetrics[name];
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +125,8 @@ export class PerflogMetric extends Metric {
|
|||||||
.then((_) => this._endMeasure(restartMeasure))
|
.then((_) => this._endMeasure(restartMeasure))
|
||||||
.then((forceGcMeasureValues) => {
|
.then((forceGcMeasureValues) => {
|
||||||
this._captureFrames = originalFrameCaptureValue;
|
this._captureFrames = originalFrameCaptureValue;
|
||||||
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
|
measureValues['forcedGcTime'] = forceGcMeasureValues['gcTime'];
|
||||||
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
|
measureValues['forcedGcAmount'] = forceGcMeasureValues['gcAmount'];
|
||||||
return measureValues;
|
return measureValues;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -152,7 +151,7 @@ export class PerflogMetric extends Metric {
|
|||||||
return this._driverExtension.readPerfLog().then((events) => {
|
return this._driverExtension.readPerfLog().then((events) => {
|
||||||
this._addEvents(events);
|
this._addEvents(events);
|
||||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||||
if (isPresent(result)) {
|
if (result) {
|
||||||
this._remainingEvents = events;
|
this._remainingEvents = events;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -166,14 +165,14 @@ export class PerflogMetric extends Metric {
|
|||||||
private _addEvents(events: PerfLogEvent[]) {
|
private _addEvents(events: PerfLogEvent[]) {
|
||||||
var needSort = false;
|
var needSort = false;
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
if (StringWrapper.equals(event['ph'], 'X')) {
|
if (event['ph'] === 'X') {
|
||||||
needSort = true;
|
needSort = true;
|
||||||
var startEvent: PerfLogEvent = {};
|
var startEvent: PerfLogEvent = {};
|
||||||
var endEvent: PerfLogEvent = {};
|
var endEvent: PerfLogEvent = {};
|
||||||
StringMapWrapper.forEach(event, (value, prop) => {
|
for (let prop in event) {
|
||||||
(<any>startEvent)[prop] = value;
|
startEvent[prop] = event[prop];
|
||||||
(<any>endEvent)[prop] = value;
|
endEvent[prop] = event[prop];
|
||||||
});
|
}
|
||||||
startEvent['ph'] = 'B';
|
startEvent['ph'] = 'B';
|
||||||
endEvent['ph'] = 'E';
|
endEvent['ph'] = 'E';
|
||||||
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
|
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
|
||||||
@ -185,7 +184,7 @@ export class PerflogMetric extends Metric {
|
|||||||
});
|
});
|
||||||
if (needSort) {
|
if (needSort) {
|
||||||
// Need to sort because of the ph==='X' events
|
// Need to sort because of the ph==='X' events
|
||||||
ListWrapper.sort(this._remainingEvents, (a, b) => {
|
this._remainingEvents.sort((a, b) => {
|
||||||
var diff = a['ts'] - b['ts'];
|
var diff = a['ts'] - b['ts'];
|
||||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||||
});
|
});
|
||||||
@ -208,7 +207,9 @@ export class PerflogMetric extends Metric {
|
|||||||
result['frameTime.worst'] = 0;
|
result['frameTime.worst'] = 0;
|
||||||
result['frameTime.smooth'] = 0;
|
result['frameTime.smooth'] = 0;
|
||||||
}
|
}
|
||||||
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
|
for (let name in this._microMetrics) {
|
||||||
|
result[name] = 0;
|
||||||
|
}
|
||||||
if (this._receivedData) {
|
if (this._receivedData) {
|
||||||
result['receivedData'] = 0;
|
result['receivedData'] = 0;
|
||||||
}
|
}
|
||||||
@ -218,6 +219,24 @@ export class PerflogMetric extends Metric {
|
|||||||
|
|
||||||
var markStartEvent: PerfLogEvent = null;
|
var markStartEvent: PerfLogEvent = null;
|
||||||
var markEndEvent: PerfLogEvent = null;
|
var markEndEvent: PerfLogEvent = null;
|
||||||
|
events.forEach((event) => {
|
||||||
|
var ph = event['ph'];
|
||||||
|
var name = event['name'];
|
||||||
|
if (ph === 'B' && name === markName) {
|
||||||
|
markStartEvent = event;
|
||||||
|
} else if (ph === 'I' && name === 'navigationStart') {
|
||||||
|
// if a benchmark measures reload of a page, use the last
|
||||||
|
// navigationStart as begin event
|
||||||
|
markStartEvent = event;
|
||||||
|
} else if (ph === 'E' && name === markName) {
|
||||||
|
markEndEvent = event;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!markStartEvent || !markEndEvent) {
|
||||||
|
// not all events have been received, no further processing for now
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var gcTimeInScript = 0;
|
var gcTimeInScript = 0;
|
||||||
var renderTimeInScript = 0;
|
var renderTimeInScript = 0;
|
||||||
|
|
||||||
@ -228,120 +247,99 @@ export class PerflogMetric extends Metric {
|
|||||||
|
|
||||||
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||||
var intervalStartCount: {[key: string]: number} = {};
|
var intervalStartCount: {[key: string]: number} = {};
|
||||||
|
|
||||||
|
var inMeasureRange = false;
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
var ph = event['ph'];
|
var ph = event['ph'];
|
||||||
var name = event['name'];
|
var name = event['name'];
|
||||||
var microIterations = 1;
|
var microIterations = 1;
|
||||||
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||||
if (isPresent(microIterationsMatch)) {
|
if (microIterationsMatch) {
|
||||||
name = microIterationsMatch[1];
|
name = microIterationsMatch[1];
|
||||||
microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10);
|
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||||
|
}
|
||||||
|
if (event === markStartEvent) {
|
||||||
|
inMeasureRange = true;
|
||||||
|
} else if (event === markEndEvent) {
|
||||||
|
inMeasureRange = false;
|
||||||
|
}
|
||||||
|
if (!inMeasureRange || event['pid'] !== markStartEvent['pid']) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) {
|
if (this._requestCount && name === 'sendRequest') {
|
||||||
markStartEvent = event;
|
|
||||||
} else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
|
|
||||||
markEndEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isInstant = StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i');
|
|
||||||
if (this._requestCount && StringWrapper.equals(name, 'sendRequest')) {
|
|
||||||
result['requestCount'] += 1;
|
result['requestCount'] += 1;
|
||||||
} else if (this._receivedData && StringWrapper.equals(name, 'receivedData') && isInstant) {
|
} else if (this._receivedData && name === 'receivedData' && ph === 'I') {
|
||||||
result['receivedData'] += event['args']['encodedDataLength'];
|
result['receivedData'] += event['args']['encodedDataLength'];
|
||||||
} else if (StringWrapper.equals(name, 'navigationStart')) {
|
}
|
||||||
// We count data + requests since the last navigationStart
|
if (ph === 'B' && name === _MARK_NAME_FRAME_CAPUTRE) {
|
||||||
// (there might be chrome extensions loaded by selenium before our page, so there
|
if (frameCaptureStartEvent) {
|
||||||
// will likely be more than one navigationStart).
|
throw new Error('can capture frames only once per benchmark run');
|
||||||
if (this._receivedData) {
|
|
||||||
result['receivedData'] = 0;
|
|
||||||
}
|
}
|
||||||
if (this._requestCount) {
|
if (!this._captureFrames) {
|
||||||
result['requestCount'] = 0;
|
throw new Error(
|
||||||
|
'found start event for frame capture, but frame capture was not requested in benchpress');
|
||||||
|
}
|
||||||
|
frameCaptureStartEvent = event;
|
||||||
|
} else if (ph === 'E' && name === _MARK_NAME_FRAME_CAPUTRE) {
|
||||||
|
if (!frameCaptureStartEvent) {
|
||||||
|
throw new Error('missing start event for frame capture');
|
||||||
|
}
|
||||||
|
frameCaptureEndEvent = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ph === 'I' && frameCaptureStartEvent && !frameCaptureEndEvent && name === 'frame') {
|
||||||
|
frameTimestamps.push(event['ts']);
|
||||||
|
if (frameTimestamps.length >= 2) {
|
||||||
|
frameTimes.push(
|
||||||
|
frameTimestamps[frameTimestamps.length - 1] -
|
||||||
|
frameTimestamps[frameTimestamps.length - 2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPresent(markStartEvent) && isBlank(markEndEvent) &&
|
|
||||||
event['pid'] === markStartEvent['pid']) {
|
|
||||||
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
|
|
||||||
if (isPresent(frameCaptureStartEvent)) {
|
|
||||||
throw new Error('can capture frames only once per benchmark run');
|
|
||||||
}
|
|
||||||
if (!this._captureFrames) {
|
|
||||||
throw new Error(
|
|
||||||
'found start event for frame capture, but frame capture was not requested in benchpress');
|
|
||||||
}
|
|
||||||
frameCaptureStartEvent = event;
|
|
||||||
} else if (
|
|
||||||
StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
|
|
||||||
if (isBlank(frameCaptureStartEvent)) {
|
|
||||||
throw new Error('missing start event for frame capture');
|
|
||||||
}
|
|
||||||
frameCaptureEndEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInstant) {
|
if (ph === 'B') {
|
||||||
if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
|
if (!intervalStarts[name]) {
|
||||||
StringWrapper.equals(name, 'frame')) {
|
intervalStartCount[name] = 1;
|
||||||
frameTimestamps.push(event['ts']);
|
intervalStarts[name] = event;
|
||||||
if (frameTimestamps.length >= 2) {
|
} else {
|
||||||
frameTimes.push(
|
intervalStartCount[name]++;
|
||||||
frameTimestamps[frameTimestamps.length - 1] -
|
|
||||||
frameTimestamps[frameTimestamps.length - 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||||
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
|
intervalStartCount[name]--;
|
||||||
if (isBlank(intervalStarts[name])) {
|
if (intervalStartCount[name] === 0) {
|
||||||
intervalStartCount[name] = 1;
|
var startEvent = intervalStarts[name];
|
||||||
intervalStarts[name] = event;
|
var duration = (event['ts'] - startEvent['ts']);
|
||||||
} else {
|
intervalStarts[name] = null;
|
||||||
intervalStartCount[name]++;
|
if (name === 'gc') {
|
||||||
}
|
result['gcTime'] += duration;
|
||||||
} else if (
|
var amount =
|
||||||
(StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
|
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||||
isPresent(intervalStarts[name])) {
|
result['gcAmount'] += amount;
|
||||||
intervalStartCount[name]--;
|
var majorGc = event['args']['majorGc'];
|
||||||
if (intervalStartCount[name] === 0) {
|
if (majorGc && majorGc) {
|
||||||
var startEvent = intervalStarts[name];
|
result['majorGcTime'] += duration;
|
||||||
var duration = (event['ts'] - startEvent['ts']);
|
|
||||||
intervalStarts[name] = null;
|
|
||||||
if (StringWrapper.equals(name, 'gc')) {
|
|
||||||
result['gcTime'] += duration;
|
|
||||||
var amount =
|
|
||||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
|
||||||
result['gcAmount'] += amount;
|
|
||||||
var majorGc = event['args']['majorGc'];
|
|
||||||
if (isPresent(majorGc) && majorGc) {
|
|
||||||
result['majorGcTime'] += duration;
|
|
||||||
}
|
|
||||||
if (isPresent(intervalStarts['script'])) {
|
|
||||||
gcTimeInScript += duration;
|
|
||||||
}
|
|
||||||
} else if (StringWrapper.equals(name, 'render')) {
|
|
||||||
result['renderTime'] += duration;
|
|
||||||
if (isPresent(intervalStarts['script'])) {
|
|
||||||
renderTimeInScript += duration;
|
|
||||||
}
|
|
||||||
} else if (StringWrapper.equals(name, 'script')) {
|
|
||||||
result['scriptTime'] += duration;
|
|
||||||
} else if (isPresent(this._microMetrics[name])) {
|
|
||||||
(<any>result)[name] += duration / microIterations;
|
|
||||||
}
|
}
|
||||||
|
if (intervalStarts['script']) {
|
||||||
|
gcTimeInScript += duration;
|
||||||
|
}
|
||||||
|
} else if (name === 'render') {
|
||||||
|
result['renderTime'] += duration;
|
||||||
|
if (intervalStarts['script']) {
|
||||||
|
renderTimeInScript += duration;
|
||||||
|
}
|
||||||
|
} else if (name === 'script') {
|
||||||
|
result['scriptTime'] += duration;
|
||||||
|
} else if (this._microMetrics[name]) {
|
||||||
|
(<any>result)[name] += duration / microIterations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!isPresent(markStartEvent) || !isPresent(markEndEvent)) {
|
|
||||||
// not all events have been received, no further processing for now
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(markEndEvent) && isPresent(frameCaptureStartEvent) &&
|
if (frameCaptureStartEvent && !frameCaptureEndEvent) {
|
||||||
isBlank(frameCaptureEndEvent)) {
|
|
||||||
throw new Error('missing end event for frame capture');
|
throw new Error('missing end event for frame capture');
|
||||||
}
|
}
|
||||||
if (this._captureFrames && isBlank(frameCaptureStartEvent)) {
|
if (this._captureFrames && !frameCaptureStartEvent) {
|
||||||
throw new Error('frame capture requested in benchpress, but no start event was found');
|
throw new Error('frame capture requested in benchpress, but no start event was found');
|
||||||
}
|
}
|
||||||
if (frameTimes.length > 0) {
|
if (frameTimes.length > 0) {
|
||||||
|
@ -6,10 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken, Provider} from '@angular/core';
|
import {Inject, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
|
||||||
import {isNumber} from '../facade/lang';
|
import {isNumber} from '../facade/lang';
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
@ -40,7 +39,7 @@ export class UserMetric extends Metric {
|
|||||||
reject = rej;
|
reject = rej;
|
||||||
});
|
});
|
||||||
let adapter = this._wdAdapter;
|
let adapter = this._wdAdapter;
|
||||||
let names = StringMapWrapper.keys(this._userMetrics);
|
let names = Object.keys(this._userMetrics);
|
||||||
|
|
||||||
function getAndClearValues() {
|
function getAndClearValues() {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
||||||
@ -48,9 +47,9 @@ export class UserMetric extends Metric {
|
|||||||
if (values.every(isNumber)) {
|
if (values.every(isNumber)) {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||||
.then((_: any[]) => {
|
.then((_: any[]) => {
|
||||||
let map = StringMapWrapper.create();
|
let map: {[k: string]: any} = {};
|
||||||
for (let i = 0, n = names.length; i < n; i++) {
|
for (let i = 0, n = names.length; i < n; i++) {
|
||||||
StringMapWrapper.set(map, names[i], values[i]);
|
map[names[i]] = values[i];
|
||||||
}
|
}
|
||||||
resolve(map);
|
resolve(map);
|
||||||
}, reject);
|
}, reject);
|
||||||
|
@ -7,10 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
import {print} from '../facade/lang';
|
||||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
|
||||||
import {NumberWrapper, isBlank, isPresent, print} from '../facade/lang';
|
|
||||||
import {Math} from '../facade/math';
|
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Reporter} from '../reporter';
|
import {Reporter} from '../reporter';
|
||||||
import {SampleDescription} from '../sample_description';
|
import {SampleDescription} from '../sample_description';
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {DateWrapper, Json, isBlank, isPresent} from '../facade/lang';
|
import {Json} from '../facade/lang';
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Reporter} from '../reporter';
|
import {Reporter} from '../reporter';
|
||||||
import {SampleDescription} from '../sample_description';
|
import {SampleDescription} from '../sample_description';
|
||||||
@ -45,8 +45,7 @@ export class JsonFileReporter extends Reporter {
|
|||||||
'completeSample': completeSample,
|
'completeSample': completeSample,
|
||||||
'validSample': validSample,
|
'validSample': validSample,
|
||||||
});
|
});
|
||||||
var filePath =
|
var filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||||
`${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`;
|
|
||||||
return this._writeFile(filePath, content);
|
return this._writeFile(filePath, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
|
||||||
import {NumberWrapper} from '../facade/lang';
|
import {NumberWrapper} from '../facade/lang';
|
||||||
import {MeasureValues} from '../measure_values';
|
import {MeasureValues} from '../measure_values';
|
||||||
import {Statistic} from '../statistic';
|
import {Statistic} from '../statistic';
|
||||||
@ -17,7 +17,7 @@ export function formatNum(n: number) {
|
|||||||
|
|
||||||
export function sortedProps(obj: {[key: string]: any}) {
|
export function sortedProps(obj: {[key: string]: any}) {
|
||||||
var props: string[] = [];
|
var props: string[] = [];
|
||||||
StringMapWrapper.forEach(obj, (value, prop) => props.push(prop));
|
props.push(...Object.keys(obj));
|
||||||
props.sort();
|
props.sort();
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from './common_options';
|
import {Options} from './common_options';
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {Metric} from './metric';
|
import {Metric} from './metric';
|
||||||
import {MultiMetric} from './metric/multi_metric';
|
import {MultiMetric} from './metric/multi_metric';
|
||||||
import {PerflogMetric} from './metric/perflog_metric';
|
import {PerflogMetric} from './metric/perflog_metric';
|
||||||
@ -63,7 +63,7 @@ export class Runner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
var inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||||
var adapter = inj.get(WebDriverAdapter);
|
var adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {OpaqueToken} from '@angular/core';
|
import {OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from './common_options';
|
import {Options} from './common_options';
|
||||||
import {StringMapWrapper} from './facade/collection';
|
|
||||||
import {Metric} from './metric';
|
import {Metric} from './metric';
|
||||||
import {Validator} from './validator';
|
import {Validator} from './validator';
|
||||||
|
|
||||||
@ -42,7 +41,7 @@ export class SampleDescription {
|
|||||||
public metrics: {[key: string]: any}) {
|
public metrics: {[key: string]: any}) {
|
||||||
this.description = {};
|
this.description = {};
|
||||||
descriptions.forEach(description => {
|
descriptions.forEach(description => {
|
||||||
StringMapWrapper.forEach(description, (value, prop) => this.description[prop] = value);
|
Object.keys(description).forEach(prop => { this.description[prop] = description[prop]; });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from './common_options';
|
import {Options} from './common_options';
|
||||||
import {Date, DateWrapper, isBlank, isPresent} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {MeasureValues} from './measure_values';
|
import {MeasureValues} from './measure_values';
|
||||||
import {Metric} from './metric';
|
import {Metric} from './metric';
|
||||||
import {Reporter} from './reporter';
|
import {Reporter} from './reporter';
|
||||||
@ -49,7 +49,7 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||||
var resultPromise: Promise<any>;
|
var resultPromise: Promise<SampleState>;
|
||||||
if (this._prepare !== Options.NO_PREPARE) {
|
if (this._prepare !== Options.NO_PREPARE) {
|
||||||
resultPromise = this._driver.waitFor(this._prepare);
|
resultPromise = this._driver.waitFor(this._prepare);
|
||||||
} else {
|
} else {
|
||||||
@ -77,5 +77,5 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SampleState {
|
export class SampleState {
|
||||||
constructor(public completeSample: any[], public validSample: any[]) {}
|
constructor(public completeSample: MeasureValues[], public validSample: MeasureValues[]) {}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,6 @@ export abstract class WebDriverAdapter {
|
|||||||
waitFor(callback: Function): Promise<any> { throw new Error('NYI'); }
|
waitFor(callback: Function): Promise<any> { throw new Error('NYI'); }
|
||||||
executeScript(script: string): Promise<any> { throw new Error('NYI'); }
|
executeScript(script: string): Promise<any> { throw new Error('NYI'); }
|
||||||
executeAsyncScript(script: string): Promise<any> { throw new Error('NYI'); }
|
executeAsyncScript(script: string): Promise<any> { throw new Error('NYI'); }
|
||||||
capabilities(): Promise<Map<string, any>> { throw new Error('NYI'); }
|
capabilities(): Promise<{[key: string]: any}> { throw new Error('NYI'); }
|
||||||
logs(type: string): Promise<any[]> { throw new Error('NYI'); }
|
logs(type: string): Promise<any[]> { throw new Error('NYI'); }
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,22 @@
|
|||||||
import {Injector, OpaqueToken} from '@angular/core';
|
import {Injector, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from './common_options';
|
import {Options} from './common_options';
|
||||||
import {isBlank, isPresent} from './facade/lang';
|
|
||||||
|
|
||||||
export type PerfLogEvent = {
|
export type PerfLogEvent = {
|
||||||
cat?: string,
|
[key: string]: any
|
||||||
ph?: 'X' | 'B' | 'E' | 'b' | 'e',
|
} & {
|
||||||
|
ph?: 'X' | 'B' | 'E' | 'I',
|
||||||
ts?: number,
|
ts?: number,
|
||||||
dur?: number,
|
dur?: number,
|
||||||
name?: string,
|
name?: string,
|
||||||
pid?: string,
|
pid?: string,
|
||||||
args?: {encodedDataLength?: number, usedHeapSize?: number, majorGc?: number}
|
args?: {
|
||||||
|
encodedDataLength?: number,
|
||||||
|
usedHeapSize?: number,
|
||||||
|
majorGc?: boolean,
|
||||||
|
url?: string,
|
||||||
|
method?: string
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,14 +42,14 @@ export abstract class WebDriverExtension {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: WebDriverExtension,
|
provide: WebDriverExtension,
|
||||||
useFactory: (children: WebDriverExtension[], capabilities: any) => {
|
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
||||||
var delegate: WebDriverExtension;
|
var delegate: WebDriverExtension;
|
||||||
children.forEach(extension => {
|
children.forEach(extension => {
|
||||||
if (extension.supports(capabilities)) {
|
if (extension.supports(capabilities)) {
|
||||||
delegate = extension;
|
delegate = extension;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (isBlank(delegate)) {
|
if (!delegate) {
|
||||||
throw new Error('Could not find a delegate for given capabilities!');
|
throw new Error('Could not find a delegate for given capabilities!');
|
||||||
}
|
}
|
||||||
return delegate;
|
return delegate;
|
||||||
@ -64,8 +70,7 @@ export abstract class WebDriverExtension {
|
|||||||
* Format:
|
* Format:
|
||||||
* - cat: category of the event
|
* - cat: category of the event
|
||||||
* - name: event name: 'script', 'gc', 'render', ...
|
* - name: event name: 'script', 'gc', 'render', ...
|
||||||
* - ph: phase: 'B' (begin), 'E' (end), 'b' (nestable start), 'e' (nestable end), 'X' (Complete
|
* - ph: phase: 'B' (begin), 'E' (end), 'X' (Complete event), 'I' (Instant event)
|
||||||
*event)
|
|
||||||
* - ts: timestamp in ms, e.g. 12345
|
* - ts: timestamp in ms, e.g. 12345
|
||||||
* - pid: process id
|
* - pid: process id
|
||||||
* - args: arguments, e.g. {heapSize: 1234}
|
* - args: arguments, e.g. {heapSize: 1234}
|
||||||
|
@ -9,16 +9,12 @@
|
|||||||
import {Inject, Injectable} from '@angular/core';
|
import {Inject, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
|
||||||
import {NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
|
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the following 'traceCategories' to collect metrics in Chrome:
|
* Set the following 'traceCategories' to collect metrics in Chrome:
|
||||||
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline'
|
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline,blink.user_timing'
|
||||||
*
|
*
|
||||||
* In order to collect the frame rate related metrics, add 'benchmark'
|
* In order to collect the frame rate related metrics, add 'benchmark'
|
||||||
* to the list above.
|
* to the list above.
|
||||||
@ -35,18 +31,18 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _parseChromeVersion(userAgent: string): number {
|
private _parseChromeVersion(userAgent: string): number {
|
||||||
if (isBlank(userAgent)) {
|
if (!userAgent) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var v = StringWrapper.split(userAgent, /Chrom(e|ium)\//g)[2];
|
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||||
if (isBlank(v)) {
|
if (!v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
v = v.split('.')[0];
|
v = v.split('.')[0];
|
||||||
if (isBlank(v)) {
|
if (!v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return NumberWrapper.parseInt(v, 10);
|
return parseInt(v, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
gc() { return this._driver.executeScript('window.gc()'); }
|
gc() { return this._driver.executeScript('window.gc()'); }
|
||||||
@ -57,7 +53,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
var script = `console.timeEnd('${name}');`;
|
var script = `console.timeEnd('${name}');`;
|
||||||
if (isPresent(restartName)) {
|
if (restartName) {
|
||||||
script += `console.time('${restartName}');`;
|
script += `console.time('${restartName}');`;
|
||||||
}
|
}
|
||||||
return this._driver.executeScript(script);
|
return this._driver.executeScript(script);
|
||||||
@ -74,10 +70,10 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
var events: PerfLogEvent[] = [];
|
var events: PerfLogEvent[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
var message = JSON.parse(entry['message'])['message'];
|
var message = JSON.parse(entry['message'])['message'];
|
||||||
if (StringWrapper.equals(message['method'], 'Tracing.dataCollected')) {
|
if (message['method'] === 'Tracing.dataCollected') {
|
||||||
events.push(message['params']);
|
events.push(message['params']);
|
||||||
}
|
}
|
||||||
if (StringWrapper.equals(message['method'], 'Tracing.bufferUsage')) {
|
if (message['method'] === 'Tracing.bufferUsage') {
|
||||||
throw new Error('The DevTools trace buffer filled during the test!');
|
throw new Error('The DevTools trace buffer filled during the test!');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -87,107 +83,64 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
private _convertPerfRecordsToEvents(
|
private _convertPerfRecordsToEvents(
|
||||||
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
|
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
|
||||||
if (isBlank(normalizedEvents)) {
|
if (!normalizedEvents) {
|
||||||
normalizedEvents = [];
|
normalizedEvents = [];
|
||||||
}
|
}
|
||||||
var majorGCPids = {};
|
|
||||||
chromeEvents.forEach((event) => {
|
chromeEvents.forEach((event) => {
|
||||||
var categories = this._parseCategories(event['cat']);
|
const categories = this._parseCategories(event['cat']);
|
||||||
var name = event['name'];
|
const normalizedEvent = this._convertEvent(event, categories);
|
||||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
||||||
normalizedEvents.push(normalizeEvent(event, {'name': name}));
|
|
||||||
} else if (this._isEvent(
|
|
||||||
categories, name, ['benchmark'],
|
|
||||||
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
|
|
||||||
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
|
|
||||||
// following events should be used (if available) for more accurate measurments:
|
|
||||||
// 1st choice: vsync_before - ground truth on Android
|
|
||||||
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
|
|
||||||
// new surfaces framework (not broadly enabled yet)
|
|
||||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
|
||||||
// always available if something is rendered
|
|
||||||
var frameCount = event['args']['data']['frame_count'];
|
|
||||||
if (frameCount > 1) {
|
|
||||||
throw new Error('multi-frame render stats not supported');
|
|
||||||
}
|
|
||||||
if (frameCount == 1) {
|
|
||||||
normalizedEvents.push(normalizeEvent(event, {'name': 'frame'}));
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
|
|
||||||
this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
|
||||||
normalizedEvents.push(normalizeEvent(event, {'name': 'render'}));
|
|
||||||
} else if (this._majorChromeVersion < 45) {
|
|
||||||
var normalizedEvent = this._processAsPreChrome45Event(event, categories, majorGCPids);
|
|
||||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
|
||||||
} else {
|
|
||||||
var normalizedEvent = this._processAsPostChrome44Event(event, categories);
|
|
||||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return normalizedEvents;
|
return normalizedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processAsPreChrome45Event(
|
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||||
event: {[key: string]: any}, categories: string[], majorGCPids: {[key: string]: any}) {
|
|
||||||
var name = event['name'];
|
var name = event['name'];
|
||||||
var args = event['args'];
|
var args = event['args'];
|
||||||
var pid = event['pid'];
|
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||||
var ph = event['ph'];
|
return normalizeEvent(event, {'name': name});
|
||||||
if (this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'FunctionCall') &&
|
|
||||||
(isBlank(args) || isBlank(args['data']) ||
|
|
||||||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
|
|
||||||
return normalizeEvent(event, {'name': 'script'});
|
|
||||||
} else if (
|
|
||||||
this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'RecalculateStyles') ||
|
|
||||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Layout') ||
|
|
||||||
this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'UpdateLayerTree') ||
|
|
||||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Paint')) {
|
|
||||||
return normalizeEvent(event, {'name': 'render'});
|
|
||||||
} else if (this._isEvent(
|
} else if (this._isEvent(
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'GCEvent')) {
|
categories, name, ['benchmark'],
|
||||||
var normArgs: {[key: string]: any} = {
|
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
|
||||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
|
||||||
args['usedHeapSizeBefore']
|
// following events should be used (if available) for more accurate measurments:
|
||||||
};
|
// 1st choice: vsync_before - ground truth on Android
|
||||||
if (StringWrapper.equals(ph, 'E')) {
|
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
|
||||||
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
|
// new surfaces framework (not broadly enabled yet)
|
||||||
|
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||||
|
// always available if something is rendered
|
||||||
|
var frameCount = event['args']['data']['frame_count'];
|
||||||
|
if (frameCount > 1) {
|
||||||
|
throw new Error('multi-frame render stats not supported');
|
||||||
|
}
|
||||||
|
if (frameCount == 1) {
|
||||||
|
return normalizeEvent(event, {'name': 'frame'});
|
||||||
}
|
}
|
||||||
majorGCPids[pid] = false;
|
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
|
||||||
} else if (
|
} else if (
|
||||||
this._isEvent(categories, name, ['v8'], 'majorGC') && StringWrapper.equals(ph, 'B')) {
|
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
|
||||||
majorGCPids[pid] = true;
|
this._isEvent(
|
||||||
}
|
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||||
return null; // nothing useful in this event
|
return normalizeEvent(event, {'name': 'render'});
|
||||||
}
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||||
|
|
||||||
private _processAsPostChrome44Event(event: {[key: string]: any}, categories: string[]) {
|
|
||||||
var name = event['name'];
|
|
||||||
var args = event['args'];
|
|
||||||
if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
|
||||||
var normArgs = {
|
var normArgs = {
|
||||||
'majorGc': true,
|
'majorGc': true,
|
||||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
};
|
};
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||||
var normArgs = {
|
var normArgs = {
|
||||||
'majorGc': false,
|
'majorGc': false,
|
||||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
};
|
};
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||||
} else if (
|
} else if (
|
||||||
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
|
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
|
||||||
(isBlank(args) || isBlank(args['data']) ||
|
(!args || !args['data'] ||
|
||||||
(!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript') &&
|
(args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
|
||||||
!StringWrapper.equals(args['data']['scriptName'], '')))) {
|
return normalizeEvent(event, {'name': 'script'});
|
||||||
|
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'EvaluateScript')) {
|
||||||
return normalizeEvent(event, {'name': 'script'});
|
return normalizeEvent(event, {'name': 'script'});
|
||||||
} else if (this._isEvent(
|
} else if (this._isEvent(
|
||||||
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
|
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
|
||||||
@ -205,7 +158,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||||
return normalizeEvent(event, {'name': name});
|
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||||
}
|
}
|
||||||
return null; // nothing useful in this event
|
return null; // nothing useful in this event
|
||||||
}
|
}
|
||||||
@ -216,9 +169,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||||
expectedName: string = null): boolean {
|
expectedName: string = null): boolean {
|
||||||
var hasCategories = expectedCategories.reduce(
|
var hasCategories = expectedCategories.reduce(
|
||||||
(value, cat) => { return value && ListWrapper.contains(eventCategories, cat); }, true);
|
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||||
return isBlank(expectedName) ? hasCategories :
|
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||||
hasCategories && StringWrapper.equals(eventName, expectedName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
perfLogFeatures(): PerfLogFeatures {
|
perfLogFeatures(): PerfLogFeatures {
|
||||||
@ -226,28 +178,31 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
supports(capabilities: {[key: string]: any}): boolean {
|
supports(capabilities: {[key: string]: any}): boolean {
|
||||||
return this._majorChromeVersion != -1 &&
|
return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
|
||||||
StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeEvent(
|
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||||
chromeEvent: {[key: string]: any}, data: {[key: string]: any}): PerfLogEvent {
|
var ph = chromeEvent['ph'].toUpperCase();
|
||||||
var ph = chromeEvent['ph'];
|
if (ph === 'S') {
|
||||||
if (StringWrapper.equals(ph, 'S')) {
|
ph = 'B';
|
||||||
ph = 'b';
|
} else if (ph === 'F') {
|
||||||
} else if (StringWrapper.equals(ph, 'F')) {
|
ph = 'E';
|
||||||
ph = 'e';
|
} else if (ph === 'R') {
|
||||||
|
// mark events from navigation timing
|
||||||
|
ph = 'I';
|
||||||
}
|
}
|
||||||
var result: {[key: string]: any} =
|
var result: {[key: string]: any} =
|
||||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||||
if (chromeEvent['ph'] === 'X') {
|
if (ph === 'X') {
|
||||||
var dur = chromeEvent['dur'];
|
var dur = chromeEvent['dur'];
|
||||||
if (isBlank(dur)) {
|
if (dur === undefined) {
|
||||||
dur = chromeEvent['tdur'];
|
dur = chromeEvent['tdur'];
|
||||||
}
|
}
|
||||||
result['dur'] = isBlank(dur) ? 0.0 : dur / 1000;
|
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||||
|
}
|
||||||
|
for (let prop in data) {
|
||||||
|
result[prop] = data[prop];
|
||||||
}
|
}
|
||||||
StringMapWrapper.forEach(data, (value, prop) => { result[prop] = value; });
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) {
|
private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) {
|
||||||
if (isBlank(events)) {
|
if (!events) {
|
||||||
events = [];
|
events = [];
|
||||||
}
|
}
|
||||||
records.forEach((record) => {
|
records.forEach((record) => {
|
||||||
@ -97,7 +97,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createEvent(
|
function createEvent(
|
||||||
ph: 'X' | 'B' | 'E' | 'b' | 'e', name: string, time: number, args: any = null) {
|
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||||
var result: PerfLogEvent = {
|
var result: PerfLogEvent = {
|
||||||
'cat': 'timeline',
|
'cat': 'timeline',
|
||||||
'name': name,
|
'name': name,
|
||||||
@ -122,9 +122,9 @@ function createEndEvent(name: string, time: number, args: any = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createMarkStartEvent(name: string, time: number) {
|
function createMarkStartEvent(name: string, time: number) {
|
||||||
return createEvent('b', name, time);
|
return createEvent('B', name, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMarkEndEvent(name: string, time: number) {
|
function createMarkEndEvent(name: string, time: number) {
|
||||||
return createEvent('e', name, time);
|
return createEvent('E', name, time);
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as webdriver from 'selenium-webdriver';
|
|
||||||
|
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,51 +19,52 @@ export class SeleniumWebDriverAdapter extends WebDriverAdapter {
|
|||||||
|
|
||||||
constructor(private _driver: any) { super(); }
|
constructor(private _driver: any) { super(); }
|
||||||
|
|
||||||
/** @internal */
|
waitFor(callback: () => any): Promise<any> { return this._driver.call(callback); }
|
||||||
private _convertPromise(thenable: PromiseLike<any>) {
|
|
||||||
var resolve: (result: any) => void;
|
|
||||||
var reject: (error: any) => void;
|
|
||||||
var promise = new Promise((res, rej) => {
|
|
||||||
resolve = res;
|
|
||||||
reject = rej;
|
|
||||||
});
|
|
||||||
thenable.then(
|
|
||||||
// selenium-webdriver uses an own Node.js context,
|
|
||||||
// so we need to convert data into objects of this context.
|
|
||||||
(data: any) => resolve(convertToLocalProcess(data)), reject);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
waitFor(callback: () => any): Promise<any> {
|
executeScript(script: string): Promise<any> { return this._driver.executeScript(script); }
|
||||||
return this._convertPromise(this._driver.controlFlow().execute(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
executeScript(script: string): Promise<any> {
|
|
||||||
return this._convertPromise(this._driver.executeScript(script));
|
|
||||||
}
|
|
||||||
|
|
||||||
executeAsyncScript(script: string): Promise<any> {
|
executeAsyncScript(script: string): Promise<any> {
|
||||||
return this._convertPromise(this._driver.executeAsyncScript(script));
|
return this._driver.executeAsyncScript(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
capabilities(): Promise<any> {
|
capabilities(): Promise<{[key: string]: any}> {
|
||||||
return this._convertPromise(
|
return this._driver.getCapabilities().then((capsObject: any) => {
|
||||||
this._driver.getCapabilities().then((capsObject: any) => capsObject.serialize()));
|
const localData: {[key: string]: any} = {};
|
||||||
|
capsObject.forEach((value: any, key: string) => { localData[key] = value; });
|
||||||
|
return localData;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logs(type: string): Promise<any> {
|
logs(type: string): Promise<any> {
|
||||||
// Needed as selenium-webdriver does not forward
|
// Needed as selenium-webdriver does not forward
|
||||||
// performance logs in the correct way via manage().logs
|
// performance logs in the correct way via manage().logs
|
||||||
return this._convertPromise(this._driver.schedule(
|
return this._driver.schedule(
|
||||||
new webdriver.Command(webdriver.CommandName.GET_LOG).setParameter('type', type),
|
new Command('getLog').setParameter('type', type),
|
||||||
'WebDriver.manage().logs().get(' + type + ')'));
|
'WebDriver.manage().logs().get(' + type + ')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToLocalProcess(data: any): Object {
|
/**
|
||||||
var serialized = JSON.stringify(data);
|
* Copy of the `Command` class of webdriver as
|
||||||
if ('' + serialized === 'undefined') {
|
* it is not exposed via index.js in selenium-webdriver.
|
||||||
return undefined;
|
*/
|
||||||
|
class Command {
|
||||||
|
private parameters_: {[key: string]: any} = {};
|
||||||
|
constructor(private name_: string) {}
|
||||||
|
|
||||||
|
getName() { return this.name_; }
|
||||||
|
|
||||||
|
setParameter(name: string, value: any) {
|
||||||
|
this.parameters_[name] = value;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
return JSON.parse(serialized);
|
|
||||||
|
setParameters(parameters: {[key: string]: any}) {
|
||||||
|
this.parameters_ = parameters;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getParameter(key: string) { return this.parameters_[key]; }
|
||||||
|
|
||||||
|
getParameters() { return this.parameters_; }
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
import {Metric, MultiMetric, ReflectiveInjector} from '../../index';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createMetric(ids: any[]) {
|
function createMetric(ids: any[]) {
|
||||||
var m = ReflectiveInjector
|
var m = ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map(id => { return {provide: id, useValue: new MockMetric(id)}; }),
|
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||||
MultiMetric.provideWith(ids)
|
MultiMetric.provideWith(ids)
|
||||||
])
|
])
|
||||||
.get(MultiMetric);
|
.get(MultiMetric);
|
||||||
|
@ -7,11 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Provider} from '@angular/core';
|
import {Provider} from '@angular/core';
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
|
import {Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, ReflectiveInjector, WebDriverExtension} from '../../index';
|
||||||
import {StringMapWrapper} from '../../src/facade/collection';
|
import {isPresent} from '../../src/facade/lang';
|
||||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
|
||||||
import {TraceEventFactory} from '../trace_event_factory';
|
import {TraceEventFactory} from '../trace_event_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -28,12 +27,12 @@ export function main() {
|
|||||||
requestCount?: boolean
|
requestCount?: boolean
|
||||||
} = {}): Metric {
|
} = {}): Metric {
|
||||||
commandLog = [];
|
commandLog = [];
|
||||||
if (isBlank(perfLogFeatures)) {
|
if (!perfLogFeatures) {
|
||||||
perfLogFeatures =
|
perfLogFeatures =
|
||||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||||
}
|
}
|
||||||
if (isBlank(microMetrics)) {
|
if (!microMetrics) {
|
||||||
microMetrics = StringMapWrapper.create();
|
microMetrics = {};
|
||||||
}
|
}
|
||||||
var providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||||
@ -68,7 +67,7 @@ export function main() {
|
|||||||
|
|
||||||
function sortedKeys(stringMap: {[key: string]: any}) {
|
function sortedKeys(stringMap: {[key: string]: any}) {
|
||||||
var res: string[] = [];
|
var res: string[] = [];
|
||||||
StringMapWrapper.forEach(stringMap, (_, key) => { res.push(key); });
|
res.push(...Object.keys(stringMap));
|
||||||
res.sort();
|
res.sort();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -171,6 +170,22 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should mark and aggregate events since navigationStart',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
var events = [[
|
||||||
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
|
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
||||||
|
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
||||||
|
eventFactory.markEnd('benchpress0', 10)
|
||||||
|
]];
|
||||||
|
var metric = createMetric(events, null);
|
||||||
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
|
expect(data['scriptTime']).toBe(1);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [
|
var events = [
|
||||||
[
|
[
|
||||||
|
@ -7,11 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {Injector, Metric, MultiMetric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, UserMetric, WebDriverAdapter, WebDriverExtension} from '../../index';
|
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||||
import {StringMapWrapper} from '../../src/facade/collection';
|
|
||||||
import {Json, isBlank, isPresent} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var wdAdapter: MockDriverAdapter;
|
var wdAdapter: MockDriverAdapter;
|
||||||
@ -19,12 +17,12 @@ export function main() {
|
|||||||
function createMetric(
|
function createMetric(
|
||||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||||
{userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric {
|
{userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric {
|
||||||
if (isBlank(perfLogFeatures)) {
|
if (!perfLogFeatures) {
|
||||||
perfLogFeatures =
|
perfLogFeatures =
|
||||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||||
}
|
}
|
||||||
if (isBlank(userMetrics)) {
|
if (!userMetrics) {
|
||||||
userMetrics = StringMapWrapper.create();
|
userMetrics = {};
|
||||||
}
|
}
|
||||||
wdAdapter = new MockDriverAdapter();
|
wdAdapter = new MockDriverAdapter();
|
||||||
var providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
|
@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Provider} from '@angular/core';
|
import {Provider} from '@angular/core';
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {ConsoleReporter, MeasureValues, ReflectiveInjector, Reporter, SampleDescription, SampleState} from '../../index';
|
import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
|
||||||
import {Date, DateWrapper, isBlank, isPresent} from '../../src/facade/lang';
|
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('console reporter', () => {
|
describe('console reporter', () => {
|
||||||
@ -25,7 +25,7 @@ export function main() {
|
|||||||
metrics?: {[key: string]: any}
|
metrics?: {[key: string]: any}
|
||||||
}) {
|
}) {
|
||||||
log = [];
|
log = [];
|
||||||
if (isBlank(descriptions)) {
|
if (!descriptions) {
|
||||||
descriptions = [];
|
descriptions = [];
|
||||||
}
|
}
|
||||||
if (isBlank(sampleId)) {
|
if (isBlank(sampleId)) {
|
||||||
@ -90,5 +90,5 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
return new MeasureValues(runIndex, new Date(time), values);
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
|
import {JsonFileReporter, MeasureValues, Options, ReflectiveInjector, SampleDescription} from '../../index';
|
||||||
import {DateWrapper, Json, isPresent} from '../../src/facade/lang';
|
import {Json, isPresent} from '../../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('file reporter', () => {
|
describe('file reporter', () => {
|
||||||
@ -27,7 +27,7 @@ export function main() {
|
|||||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||||
},
|
},
|
||||||
{provide: JsonFileReporter.PATH, useValue: path},
|
{provide: JsonFileReporter.PATH, useValue: path},
|
||||||
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(1234)}, {
|
{provide: Options.NOW, useValue: () => new Date(1234)}, {
|
||||||
provide: Options.WRITE_FILE,
|
provide: Options.WRITE_FILE,
|
||||||
useValue: (filename: string, content: string) => {
|
useValue: (filename: string, content: string) => {
|
||||||
loggedFile = {'filename': filename, 'content': content};
|
loggedFile = {'filename': filename, 'content': content};
|
||||||
@ -77,5 +77,5 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
return new MeasureValues(runIndex, new Date(time), values);
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index';
|
import {MeasureValues, MultiReporter, ReflectiveInjector, Reporter} from '../../index';
|
||||||
import {DateWrapper} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createReporters(ids: any[]) {
|
function createReporters(ids: any[]) {
|
||||||
var r = ReflectiveInjector
|
var r = ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map(id => { return {provide: id, useValue: new MockReporter(id)}; }),
|
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||||
MultiReporter.provideWith(ids)
|
MultiReporter.provideWith(ids)
|
||||||
])
|
])
|
||||||
.get(MultiReporter);
|
.get(MultiReporter);
|
||||||
@ -26,7 +25,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should reportMeasureValues to all',
|
it('should reportMeasureValues to all',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var mv = new MeasureValues(0, DateWrapper.now(), {});
|
var mv = new MeasureValues(0, new Date(), {});
|
||||||
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
||||||
|
|
||||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
||||||
@ -35,9 +34,8 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var completeSample = [
|
var completeSample =
|
||||||
new MeasureValues(0, DateWrapper.now(), {}), new MeasureValues(1, DateWrapper.now(), {})
|
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
||||||
];
|
|
||||||
var validSample = [completeSample[1]];
|
var validSample = [completeSample[1]];
|
||||||
|
|
||||||
createReporters(['m1', 'm2'])
|
createReporters(['m1', 'm2'])
|
||||||
|
@ -6,10 +6,9 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
|
import {Injector, Metric, Options, ReflectiveInjector, Runner, SampleDescription, SampleState, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||||
import {isBlank} from '../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('runner', () => {
|
describe('runner', () => {
|
||||||
@ -17,7 +16,7 @@ export function main() {
|
|||||||
var runner: Runner;
|
var runner: Runner;
|
||||||
|
|
||||||
function createRunner(defaultProviders: any[] = null): Runner {
|
function createRunner(defaultProviders: any[] = null): Runner {
|
||||||
if (isBlank(defaultProviders)) {
|
if (!defaultProviders) {
|
||||||
defaultProviders = [];
|
defaultProviders = [];
|
||||||
}
|
}
|
||||||
runner = new Runner([
|
runner = new Runner([
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
|
import {MeasureValues, Metric, Options, ReflectiveInjector, Reporter, Sampler, Validator, WebDriverAdapter} from '../index';
|
||||||
import {Date, DateWrapper, isBlank, isPresent, stringify} from '../src/facade/lang';
|
import {isBlank, isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var EMPTY_EXECUTE = () => {};
|
var EMPTY_EXECUTE = () => {};
|
||||||
@ -26,10 +26,10 @@ export function main() {
|
|||||||
execute?: any
|
execute?: any
|
||||||
} = {}) {
|
} = {}) {
|
||||||
var time = 1000;
|
var time = 1000;
|
||||||
if (isBlank(metric)) {
|
if (!metric) {
|
||||||
metric = new MockMetric([]);
|
metric = new MockMetric([]);
|
||||||
}
|
}
|
||||||
if (isBlank(reporter)) {
|
if (!reporter) {
|
||||||
reporter = new MockReporter([]);
|
reporter = new MockReporter([]);
|
||||||
}
|
}
|
||||||
if (isBlank(driver)) {
|
if (isBlank(driver)) {
|
||||||
@ -39,7 +39,7 @@ export function main() {
|
|||||||
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
||||||
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
||||||
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
{provide: Options.EXECUTE, useValue: execute}, {provide: Validator, useValue: validator},
|
||||||
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(time++)}
|
{provide: Options.NOW, useValue: () => new Date(time++)}
|
||||||
];
|
];
|
||||||
if (isPresent(prepare)) {
|
if (isPresent(prepare)) {
|
||||||
providers.push({provide: Options.PREPARE, useValue: prepare});
|
providers.push({provide: Options.PREPARE, useValue: prepare});
|
||||||
@ -60,8 +60,8 @@ export function main() {
|
|||||||
createSampler({
|
createSampler({
|
||||||
driver: driver,
|
driver: driver,
|
||||||
validator: createCountingValidator(2),
|
validator: createCountingValidator(2),
|
||||||
prepare: () => { return count++; },
|
prepare: () => count++,
|
||||||
execute: () => { return count++; }
|
execute: () => count++,
|
||||||
});
|
});
|
||||||
sampler.sample().then((_) => {
|
sampler.sample().then((_) => {
|
||||||
expect(count).toBe(4);
|
expect(count).toBe(4);
|
||||||
@ -204,7 +204,7 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
return new MeasureValues(runIndex, new Date(time), values);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCountingValidator(
|
function createCountingValidator(
|
||||||
@ -221,7 +221,7 @@ function createCountingValidator(
|
|||||||
|
|
||||||
function createCountingMetric(log: any[] = []) {
|
function createCountingMetric(log: any[] = []) {
|
||||||
var scriptTime = 0;
|
var scriptTime = 0;
|
||||||
return new MockMetric(log, () => { return {'script': scriptTime++}; });
|
return new MockMetric(log, () => ({'script': scriptTime++}));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockDriverAdapter extends WebDriverAdapter {
|
class MockDriverAdapter extends WebDriverAdapter {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
import {Statistic} from '../src/statistic';
|
import {Statistic} from '../src/statistic';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -21,16 +21,16 @@ export class TraceEventFactory {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
markStart(name: string, time: number) { return this.create('b', name, time); }
|
markStart(name: string, time: number) { return this.create('B', name, time); }
|
||||||
|
|
||||||
markEnd(name: string, time: number) { return this.create('e', name, time); }
|
markEnd(name: string, time: number) { return this.create('E', name, time); }
|
||||||
|
|
||||||
start(name: string, time: number, args: any = null) { return this.create('B', name, time, args); }
|
start(name: string, time: number, args: any = null) { return this.create('B', name, time, args); }
|
||||||
|
|
||||||
end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); }
|
end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); }
|
||||||
|
|
||||||
instant(name: string, time: number, args: any = null) {
|
instant(name: string, time: number, args: any = null) {
|
||||||
return this.create('i', name, time, args);
|
return this.create('I', name, time, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(name: string, time: number, duration: number, args: any = null) {
|
complete(name: string, time: number, duration: number, args: any = null) {
|
||||||
|
@ -6,11 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
import {MeasureValues, ReflectiveInjector, RegressionSlopeValidator} from '../../index';
|
||||||
import {ListWrapper} from '../../src/facade/collection';
|
import {ListWrapper} from '../../src/facade/collection';
|
||||||
import {Date, DateWrapper} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('regression slope validator', () => {
|
describe('regression slope validator', () => {
|
||||||
@ -62,5 +61,5 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
return new MeasureValues(runIndex, new Date(time), values);
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {MeasureValues, ReflectiveInjector, SizeValidator, Validator} from '../../index';
|
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||||
import {ListWrapper} from '../../src/facade/collection';
|
import {ListWrapper} from '../../src/facade/collection';
|
||||||
import {Date, DateWrapper} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('size validator', () => {
|
describe('size validator', () => {
|
||||||
@ -47,5 +46,5 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
function mv(runIndex: number, time: number, values: {[key: string]: number}) {
|
||||||
return new MeasureValues(runIndex, DateWrapper.fromMillis(time), values);
|
return new MeasureValues(runIndex, new Date(time), values);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
||||||
import {StringWrapper, isPresent} from '../src/facade/lang';
|
import {StringWrapper, isPresent} from '../src/facade/lang';
|
||||||
@ -17,7 +17,7 @@ export function main() {
|
|||||||
try {
|
try {
|
||||||
res(ReflectiveInjector
|
res(ReflectiveInjector
|
||||||
.resolveAndCreate([
|
.resolveAndCreate([
|
||||||
ids.map((id) => { return {provide: id, useValue: new MockExtension(id)}; }),
|
ids.map((id) => ({provide: id, useValue: new MockExtension(id)})),
|
||||||
{provide: Options.CAPABILITIES, useValue: caps},
|
{provide: Options.CAPABILITIES, useValue: caps},
|
||||||
WebDriverExtension.provideFirstSupported(ids)
|
WebDriverExtension.provideFirstSupported(ids)
|
||||||
])
|
])
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
import {ChromeDriverExtension, Options, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||||
import {Json, isBlank} from '../../src/facade/lang';
|
import {Json, isBlank} from '../../src/facade/lang';
|
||||||
@ -14,8 +14,6 @@ import {TraceEventFactory} from '../trace_event_factory';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('chrome driver extension', () => {
|
describe('chrome driver extension', () => {
|
||||||
var CHROME44_USER_AGENT =
|
|
||||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.0 Safari/537.36"';
|
|
||||||
var CHROME45_USER_AGENT =
|
var CHROME45_USER_AGENT =
|
||||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
||||||
|
|
||||||
@ -37,11 +35,11 @@ export function main() {
|
|||||||
function createExtension(
|
function createExtension(
|
||||||
perfRecords: any[] = null, userAgent: string = null,
|
perfRecords: any[] = null, userAgent: string = null,
|
||||||
messageMethod = 'Tracing.dataCollected'): WebDriverExtension {
|
messageMethod = 'Tracing.dataCollected'): WebDriverExtension {
|
||||||
if (isBlank(perfRecords)) {
|
if (!perfRecords) {
|
||||||
perfRecords = [];
|
perfRecords = [];
|
||||||
}
|
}
|
||||||
if (isBlank(userAgent)) {
|
if (isBlank(userAgent)) {
|
||||||
userAgent = CHROME44_USER_AGENT;
|
userAgent = CHROME45_USER_AGENT;
|
||||||
}
|
}
|
||||||
log = [];
|
log = [];
|
||||||
extension = ReflectiveInjector
|
extension = ReflectiveInjector
|
||||||
@ -89,228 +87,95 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('readPerfLog Chrome44', () => {
|
it('should normalize times to ms and forward ph and pid event properties',
|
||||||
it('should normalize times to ms and forward ph and pid event properties',
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)])
|
||||||
createExtension([chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)])
|
.readPerfLog()
|
||||||
.readPerfLog()
|
.then((events) => {
|
||||||
.then((events) => {
|
expect(events).toEqual([
|
||||||
expect(events).toEqual([
|
normEvents.complete('script', 1.1, 5.5, null),
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
]);
|
||||||
]);
|
async.done();
|
||||||
async.done();
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
it('should normalize "tdur" to "dur"',
|
it('should normalize "tdur" to "dur"',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var event: any = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null);
|
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||||
event['tdur'] = 5500;
|
event['tdur'] = 5500;
|
||||||
createExtension([event]).readPerfLog().then((events) => {
|
createExtension([event]).readPerfLog().then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
normEvents.complete('script', 1.1, 5.5, null),
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should report FunctionCall events as "script"',
|
it('should report FunctionCall events as "script"',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
createExtension([chromeTimelineEvents.start('FunctionCall', 0)])
|
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)])
|
||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
normEvents.start('script', 0),
|
normEvents.start('script', 0),
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should report gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should report EvaluateScript events as "script"',
|
||||||
createExtension([
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
createExtension([chromeTimelineV8Events.start('EvaluateScript', 0)])
|
||||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
.readPerfLog()
|
||||||
])
|
.then((events) => {
|
||||||
.readPerfLog()
|
expect(events).toEqual([
|
||||||
.then((events) => {
|
normEvents.start('script', 0),
|
||||||
expect(events).toEqual([
|
]);
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
async.done();
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
|
});
|
||||||
]);
|
}));
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should ignore major gc from different processes',
|
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
createExtension([
|
||||||
createExtension([
|
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||||
v8EventsOtherProcess.start('majorGC', 1100, null),
|
])
|
||||||
v8EventsOtherProcess.end('majorGC', 1200, null),
|
.readPerfLog()
|
||||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
.then((events) => {
|
||||||
])
|
expect(events.length).toEqual(2);
|
||||||
.readPerfLog()
|
expect(events[0]).toEqual(
|
||||||
.then((events) => {
|
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
|
||||||
expect(events).toEqual([
|
expect(events[1]).toEqual(
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
|
async.done();
|
||||||
]);
|
});
|
||||||
async.done();
|
}));
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
createExtension([
|
createExtension(
|
||||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
[
|
||||||
v8Events.start('majorGC', 1100, null),
|
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||||
v8Events.end('majorGC', 1200, null),
|
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
], )
|
||||||
])
|
.readPerfLog()
|
||||||
.readPerfLog()
|
.then((events) => {
|
||||||
.then((events) => {
|
expect(events.length).toEqual(2);
|
||||||
expect(events).toEqual([
|
expect(events[0]).toEqual(
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}),
|
expect(events[1]).toEqual(
|
||||||
]);
|
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
||||||
it(`should report ${recordType} as "render"`,
|
it(`should report ${recordType} as "render"`,
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([
|
|
||||||
chromeTimelineEvents.start(recordType, 1234),
|
|
||||||
chromeTimelineEvents.end(recordType, 2345)
|
|
||||||
])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.start('render', 1.234),
|
|
||||||
normEvents.end('render', 2.345),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore FunctionCalls from webdriver',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([chromeTimelineEvents.start(
|
|
||||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('readPerfLog Chrome45', () => {
|
|
||||||
it('should normalize times to ms and forward ph and pid event properties',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should normalize "tdur" to "dur"',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
|
||||||
event['tdur'] = 5500;
|
|
||||||
createExtension([event], CHROME45_USER_AGENT).readPerfLog().then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report FunctionCall events as "script"',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)], CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.start('script', 0),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[
|
|
||||||
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
|
||||||
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
|
|
||||||
],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events.length).toEqual(2);
|
|
||||||
expect(events[0]).toEqual(
|
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
|
|
||||||
expect(events[1]).toEqual(
|
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[
|
|
||||||
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
|
||||||
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
|
|
||||||
],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events.length).toEqual(2);
|
|
||||||
expect(events[0]).toEqual(
|
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
|
|
||||||
expect(events[1]).toEqual(
|
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
|
||||||
it(`should report ${recordType} as "render"`,
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[
|
|
||||||
chrome45TimelineEvents.start(recordType, 1234),
|
|
||||||
chrome45TimelineEvents.end(recordType, 2345)
|
|
||||||
],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.start('render', 1.234),
|
|
||||||
normEvents.end('render', 2.345),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should report UpdateLayoutTree as "render"`,
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
createExtension(
|
createExtension(
|
||||||
[
|
[
|
||||||
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
|
chrome45TimelineEvents.start(recordType, 1234),
|
||||||
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
|
chrome45TimelineEvents.end(recordType, 2345)
|
||||||
],
|
], )
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
@ -320,70 +185,80 @@ export function main() {
|
|||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it('should ignore FunctionCalls from webdriver',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([chromeTimelineV8Events.start(
|
|
||||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should ignore FunctionCalls with empty scriptName',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report navigationStart',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chromeBlinkUserTimingEvents.start('navigationStart', 1234)], CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([normEvents.start('navigationStart', 1.234)]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chrome45TimelineEvents.instant(
|
|
||||||
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual(
|
|
||||||
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chrome45TimelineEvents.instant(
|
|
||||||
'ResourceSendRequest', 1234,
|
|
||||||
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([normEvents.instant(
|
|
||||||
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should report UpdateLayoutTree as "render"`,
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension(
|
||||||
|
[
|
||||||
|
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
|
||||||
|
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
|
||||||
|
], )
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([
|
||||||
|
normEvents.start('render', 1.234),
|
||||||
|
normEvents.end('render', 2.345),
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should ignore FunctionCalls from webdriver',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chromeTimelineV8Events.start(
|
||||||
|
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should ignore FunctionCalls with empty scriptName',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension(
|
||||||
|
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report navigationStart',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chromeBlinkUserTimingEvents.instant('navigationStart', 1234)])
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([normEvents.instant('navigationStart', 1.234)]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chrome45TimelineEvents.instant(
|
||||||
|
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], )
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual(
|
||||||
|
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chrome45TimelineEvents.instant(
|
||||||
|
'ResourceSendRequest', 1234,
|
||||||
|
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})], )
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([normEvents.instant(
|
||||||
|
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
describe('readPerfLog (common)', () => {
|
describe('readPerfLog (common)', () => {
|
||||||
|
|
||||||
it('should execute a dummy script before reading them',
|
it('should execute a dummy script before reading them',
|
||||||
@ -404,8 +279,7 @@ export function main() {
|
|||||||
[
|
[
|
||||||
chromeTimelineEvents.start(recordType, 1234),
|
chromeTimelineEvents.start(recordType, 1234),
|
||||||
chromeTimelineEvents.end(recordType, 2345)
|
chromeTimelineEvents.end(recordType, 2345)
|
||||||
],
|
], )
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
@ -426,7 +300,7 @@ export function main() {
|
|||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
normEvents.create('i', 'frame', 1.1),
|
normEvents.instant('frame', 1.1),
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
@ -522,11 +396,11 @@ class MockDriverAdapter extends WebDriverAdapter {
|
|||||||
logs(type: string) {
|
logs(type: string) {
|
||||||
this._log.push(['logs', type]);
|
this._log.push(['logs', type]);
|
||||||
if (type === 'performance') {
|
if (type === 'performance') {
|
||||||
return Promise.resolve(this._events.map((event) => {
|
return Promise.resolve(this._events.map(
|
||||||
return {
|
(event) => ({
|
||||||
'message': Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
'message':
|
||||||
};
|
Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
||||||
}));
|
})));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
import {IOsDriverExtension, ReflectiveInjector, WebDriverAdapter, WebDriverExtension} from '../../index';
|
||||||
import {Json, isBlank, isPresent} from '../../src/facade/lang';
|
import {Json} from '../../src/facade/lang';
|
||||||
import {TraceEventFactory} from '../trace_event_factory';
|
import {TraceEventFactory} from '../trace_event_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -20,7 +20,7 @@ export function main() {
|
|||||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||||
|
|
||||||
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
||||||
if (isBlank(perfRecords)) {
|
if (!perfRecords) {
|
||||||
perfRecords = [];
|
perfRecords = [];
|
||||||
}
|
}
|
||||||
log = [];
|
log = [];
|
||||||
@ -156,7 +156,7 @@ function timeEndRecord(name: string, time: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) {
|
function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) {
|
||||||
if (isBlank(children)) {
|
if (!children) {
|
||||||
children = [];
|
children = [];
|
||||||
}
|
}
|
||||||
return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children};
|
return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children};
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
* @description
|
* @description
|
||||||
* Entry point for all public APIs of the common package.
|
* Entry point for all public APIs of the common package.
|
||||||
*/
|
*/
|
||||||
export * from './src/common';
|
export * from './src/location';
|
||||||
|
export {NgLocalization} from './src/localization';
|
||||||
|
export {CommonModule} from './src/common_module';
|
||||||
|
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './src/directives/index';
|
||||||
|
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './src/pipes/index';
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/common",
|
"name": "@angular/common",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.0.0-PLACEHOLDER",
|
||||||
"description": "",
|
"description": "Angular - commonly needed directives and services",
|
||||||
"main": "bundles/common.umd.js",
|
"main": "bundles/common.umd.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: '../../../dist/packages-dist/common/testing/index.js',
|
entry: '../../../dist/packages-dist/common/testing/index.js',
|
||||||
@ -10,4 +17,4 @@ export default {
|
|||||||
'rxjs/Observable': 'Rx',
|
'rxjs/Observable': 'Rx',
|
||||||
'rxjs/Subject': 'Rx'
|
'rxjs/Subject': 'Rx'
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: '../../../dist/packages-dist/common/index.js',
|
entry: '../../../dist/packages-dist/common/index.js',
|
||||||
@ -7,6 +14,6 @@ export default {
|
|||||||
globals: {
|
globals: {
|
||||||
'@angular/core': 'ng.core',
|
'@angular/core': 'ng.core',
|
||||||
'rxjs/Observable': 'Rx',
|
'rxjs/Observable': 'Rx',
|
||||||
'rxjs/Subject': 'Rx'
|
'rxjs/Subject': 'Rx',
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Provider} from '@angular/core';
|
|
||||||
|
|
||||||
import {CORE_DIRECTIVES} from './directives/core_directives';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
|
||||||
* application. This includes core directives (e.g., NgIf and NgFor), and forms directives (e.g.,
|
|
||||||
* NgModel).
|
|
||||||
*
|
|
||||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
|
||||||
* property of the `@Component` decorator.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* Instead of writing:
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, NgModel, NgForm} from
|
|
||||||
* '@angular/common';
|
|
||||||
* import {OtherDirective} from './myDirectives';
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-component',
|
|
||||||
* templateUrl: 'myComponent.html',
|
|
||||||
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, NgModel, NgForm,
|
|
||||||
* OtherDirective]
|
|
||||||
* })
|
|
||||||
* export class MyComponent {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* one could import all the common directives at once:
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* import {COMMON_DIRECTIVES} from '@angular/common';
|
|
||||||
* import {OtherDirective} from './myDirectives';
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-component',
|
|
||||||
* templateUrl: 'myComponent.html',
|
|
||||||
* directives: [COMMON_DIRECTIVES, OtherDirective]
|
|
||||||
* })
|
|
||||||
* export class MyComponent {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @experimental Contains forms which are experimental.
|
|
||||||
*/
|
|
||||||
export const COMMON_DIRECTIVES: Provider[] = CORE_DIRECTIVES;
|
|
@ -8,14 +8,14 @@
|
|||||||
|
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
|
|
||||||
import {COMMON_DIRECTIVES} from './common_directives';
|
import {COMMON_DIRECTIVES} from './directives/index';
|
||||||
import {NgLocaleLocalization, NgLocalization} from './localization';
|
import {NgLocaleLocalization, NgLocalization} from './localization';
|
||||||
import {COMMON_PIPES} from './pipes/common_pipes';
|
import {COMMON_PIPES} from './pipes/index';
|
||||||
|
|
||||||
// Note: This does not contain the location providers,
|
// Note: This does not contain the location providers,
|
||||||
// as they need some platform specific implementations to work.
|
// as they need some platform specific implementations to work.
|
||||||
/**
|
/**
|
||||||
* The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ...
|
* The module that includes all the basic Angular directives like {@link NgIf}, {@link NgFor}, ...
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module
|
|
||||||
* @description
|
|
||||||
* Common directives shipped with Angular.
|
|
||||||
*/
|
|
||||||
export {NgClass} from './directives/ng_class';
|
|
||||||
export {NgFor} from './directives/ng_for';
|
|
||||||
export {NgIf} from './directives/ng_if';
|
|
||||||
export {NgPlural, NgPluralCase} from './directives/ng_plural';
|
|
||||||
export {NgStyle} from './directives/ng_style';
|
|
||||||
export {NgSwitch, NgSwitchCase, NgSwitchDefault} from './directives/ng_switch';
|
|
||||||
export {NgTemplateOutlet} from './directives/ng_template_outlet';
|
|
@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Type} from '@angular/core';
|
|
||||||
|
|
||||||
import {NgClass} from './ng_class';
|
|
||||||
import {NgFor} from './ng_for';
|
|
||||||
import {NgIf} from './ng_if';
|
|
||||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
|
||||||
import {NgStyle} from './ng_style';
|
|
||||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
|
||||||
import {NgTemplateOutlet} from './ng_template_outlet';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
|
||||||
* application.
|
|
||||||
*
|
|
||||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
|
||||||
* property of the `@Component` annotation.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
|
|
||||||
*
|
|
||||||
* Instead of writing:
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
|
|
||||||
* import {OtherDirective} from './myDirectives';
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-component',
|
|
||||||
* templateUrl: 'myComponent.html',
|
|
||||||
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, OtherDirective]
|
|
||||||
* })
|
|
||||||
* export class MyComponent {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* one could import all the core directives at once:
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* import {CORE_DIRECTIVES} from '@angular/common';
|
|
||||||
* import {OtherDirective} from './myDirectives';
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-component',
|
|
||||||
* templateUrl: 'myComponent.html',
|
|
||||||
* directives: [CORE_DIRECTIVES, OtherDirective]
|
|
||||||
* })
|
|
||||||
* export class MyComponent {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @stable
|
|
||||||
*/
|
|
||||||
export const CORE_DIRECTIVES: Type<any>[] = [
|
|
||||||
NgClass,
|
|
||||||
NgFor,
|
|
||||||
NgIf,
|
|
||||||
NgTemplateOutlet,
|
|
||||||
NgStyle,
|
|
||||||
NgSwitch,
|
|
||||||
NgSwitchCase,
|
|
||||||
NgSwitchDefault,
|
|
||||||
NgPlural,
|
|
||||||
NgPluralCase,
|
|
||||||
];
|
|
47
modules/@angular/common/src/directives/index.ts
Normal file
47
modules/@angular/common/src/directives/index.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Provider} from '@angular/core';
|
||||||
|
|
||||||
|
import {NgClass} from './ng_class';
|
||||||
|
import {NgFor} from './ng_for';
|
||||||
|
import {NgIf} from './ng_if';
|
||||||
|
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||||
|
import {NgStyle} from './ng_style';
|
||||||
|
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
||||||
|
import {NgTemplateOutlet} from './ng_template_outlet';
|
||||||
|
|
||||||
|
export {
|
||||||
|
NgClass,
|
||||||
|
NgFor,
|
||||||
|
NgIf,
|
||||||
|
NgPlural,
|
||||||
|
NgPluralCase,
|
||||||
|
NgStyle,
|
||||||
|
NgSwitch,
|
||||||
|
NgSwitchCase,
|
||||||
|
NgSwitchDefault,
|
||||||
|
NgTemplateOutlet
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of Angular directives that are likely to be used in each and every Angular
|
||||||
|
* application.
|
||||||
|
*/
|
||||||
|
export const COMMON_DIRECTIVES: Provider[] = [
|
||||||
|
NgClass,
|
||||||
|
NgFor,
|
||||||
|
NgIf,
|
||||||
|
NgTemplateOutlet,
|
||||||
|
NgStyle,
|
||||||
|
NgSwitch,
|
||||||
|
NgSwitchCase,
|
||||||
|
NgSwitchDefault,
|
||||||
|
NgPlural,
|
||||||
|
NgPluralCase,
|
||||||
|
];
|
@ -8,70 +8,35 @@
|
|||||||
|
|
||||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
import {StringMapWrapper, isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {isArray, isPresent, isString} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
* @ngModule CommonModule
|
||||||
* an expression's evaluation result.
|
|
||||||
*
|
*
|
||||||
* The result of an expression evaluation is interpreted differently depending on type of
|
* @whatItDoes Adds and removes CSS classes on an HTML element.
|
||||||
* the expression evaluation result:
|
|
||||||
* - `string` - all the CSS classes listed in a string (space delimited) are added
|
|
||||||
* - `Array` - all the CSS classes (Array elements) are added
|
|
||||||
* - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
|
|
||||||
* evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
|
|
||||||
* is added - otherwise it is removed.
|
|
||||||
*
|
|
||||||
* While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
|
|
||||||
* or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
|
|
||||||
* all the CSS class names in a template.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
|
|
||||||
*
|
*
|
||||||
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* import {Component} from '@angular/core';
|
* <some-element [ngClass]="'first second'">...</some-element>
|
||||||
* import {NgClass} from '@angular/common';
|
|
||||||
*
|
*
|
||||||
* @Component({
|
* <some-element [ngClass]="['first', 'second']">...</some-element>
|
||||||
* selector: 'toggle-button',
|
|
||||||
* inputs: ['isDisabled'],
|
|
||||||
* template: `
|
|
||||||
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
|
|
||||||
* (click)="toggle(!isOn)">
|
|
||||||
* Click me!
|
|
||||||
* </div>`,
|
|
||||||
* styles: [`
|
|
||||||
* .button {
|
|
||||||
* width: 120px;
|
|
||||||
* border: medium solid black;
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* .active {
|
* <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
|
||||||
* background-color: red;
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* .disabled {
|
* <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
|
||||||
* color: gray;
|
|
||||||
* border: medium solid gray;
|
|
||||||
* }
|
|
||||||
* `],
|
|
||||||
* directives: [NgClass]
|
|
||||||
* })
|
|
||||||
* class ToggleButton {
|
|
||||||
* isOn = false;
|
|
||||||
* isDisabled = false;
|
|
||||||
*
|
|
||||||
* toggle(newState) {
|
|
||||||
* if (!this.isDisabled) {
|
|
||||||
* this.isOn = newState;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* The CSS classes are updated as follow depending on the type of the expression evaluation:
|
||||||
|
* - `string` - the CSS classes listed in a string (space delimited) are added,
|
||||||
|
* - `Array` - the CSS classes (Array elements) are added,
|
||||||
|
* - `Object` - keys are CSS class names that get added when the expression given in the value
|
||||||
|
* evaluates to a truthy value, otherwise class are removed.
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngClass]'})
|
@Directive({selector: '[ngClass]'})
|
||||||
@ -79,7 +44,7 @@ export class NgClass implements DoCheck {
|
|||||||
private _iterableDiffer: IterableDiffer;
|
private _iterableDiffer: IterableDiffer;
|
||||||
private _keyValueDiffer: KeyValueDiffer;
|
private _keyValueDiffer: KeyValueDiffer;
|
||||||
private _initialClasses: string[] = [];
|
private _initialClasses: string[] = [];
|
||||||
private _rawClass: string[]|Set<string>;
|
private _rawClass: string[]|Set<string>|{[klass: string]: any};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||||
@ -87,58 +52,57 @@ export class NgClass implements DoCheck {
|
|||||||
|
|
||||||
|
|
||||||
@Input('class')
|
@Input('class')
|
||||||
set initialClasses(v: string) {
|
set klass(v: string) {
|
||||||
this._applyInitialClasses(true);
|
this._applyInitialClasses(true);
|
||||||
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
|
this._initialClasses = typeof v === 'string' ? v.split(/\s+/) : [];
|
||||||
this._applyInitialClasses(false);
|
this._applyInitialClasses(false);
|
||||||
this._applyClasses(this._rawClass, false);
|
this._applyClasses(this._rawClass, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngClass(v: string|string[]|Set<string>|{[key: string]: any}) {
|
set ngClass(v: string|string[]|Set<string>|{[klass: string]: any}) {
|
||||||
this._cleanupClasses(this._rawClass);
|
this._cleanupClasses(this._rawClass);
|
||||||
|
|
||||||
if (isString(v)) {
|
|
||||||
v = (<string>v).split(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
this._rawClass = <string[]|Set<string>>v;
|
|
||||||
this._iterableDiffer = null;
|
this._iterableDiffer = null;
|
||||||
this._keyValueDiffer = null;
|
this._keyValueDiffer = null;
|
||||||
if (isPresent(v)) {
|
|
||||||
if (isListLikeIterable(v)) {
|
this._rawClass = typeof v === 'string' ? v.split(/\s+/) : v;
|
||||||
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
|
|
||||||
|
if (this._rawClass) {
|
||||||
|
if (isListLikeIterable(this._rawClass)) {
|
||||||
|
this._iterableDiffer = this._iterableDiffers.find(this._rawClass).create(null);
|
||||||
} else {
|
} else {
|
||||||
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
|
this._keyValueDiffer = this._keyValueDiffers.find(this._rawClass).create(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoCheck(): void {
|
ngDoCheck(): void {
|
||||||
if (isPresent(this._iterableDiffer)) {
|
if (this._iterableDiffer) {
|
||||||
var changes = this._iterableDiffer.diff(this._rawClass);
|
const changes = this._iterableDiffer.diff(this._rawClass);
|
||||||
if (isPresent(changes)) {
|
if (changes) {
|
||||||
this._applyIterableChanges(changes);
|
this._applyIterableChanges(changes);
|
||||||
}
|
}
|
||||||
}
|
} else if (this._keyValueDiffer) {
|
||||||
if (isPresent(this._keyValueDiffer)) {
|
const changes = this._keyValueDiffer.diff(this._rawClass);
|
||||||
var changes = this._keyValueDiffer.diff(this._rawClass);
|
if (changes) {
|
||||||
if (isPresent(changes)) {
|
|
||||||
this._applyKeyValueChanges(changes);
|
this._applyKeyValueChanges(changes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[key: string]: any}): void {
|
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[klass: string]: any}): void {
|
||||||
this._applyClasses(rawClassVal, true);
|
this._applyClasses(rawClassVal, true);
|
||||||
this._applyInitialClasses(false);
|
this._applyInitialClasses(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyKeyValueChanges(changes: any): void {
|
private _applyKeyValueChanges(changes: any): void {
|
||||||
changes.forEachAddedItem(
|
changes.forEachAddedItem(
|
||||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
|
||||||
|
|
||||||
changes.forEachChangedItem(
|
changes.forEachChangedItem(
|
||||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
|
||||||
|
|
||||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
||||||
if (record.previousValue) {
|
if (record.previousValue) {
|
||||||
this._toggleClass(record.key, false);
|
this._toggleClass(record.key, false);
|
||||||
@ -148,42 +112,34 @@ export class NgClass implements DoCheck {
|
|||||||
|
|
||||||
private _applyIterableChanges(changes: any): void {
|
private _applyIterableChanges(changes: any): void {
|
||||||
changes.forEachAddedItem(
|
changes.forEachAddedItem(
|
||||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, true); });
|
(record: CollectionChangeRecord) => this._toggleClass(record.item, true));
|
||||||
|
|
||||||
changes.forEachRemovedItem(
|
changes.forEachRemovedItem(
|
||||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, false); });
|
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyInitialClasses(isCleanup: boolean) {
|
private _applyInitialClasses(isCleanup: boolean) {
|
||||||
this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
|
this._initialClasses.forEach(klass => this._toggleClass(klass, !isCleanup));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyClasses(
|
private _applyClasses(
|
||||||
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
|
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
|
||||||
if (isPresent(rawClassVal)) {
|
if (rawClassVal) {
|
||||||
if (isArray(rawClassVal)) {
|
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
||||||
(<string[]>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
||||||
} else if (rawClassVal instanceof Set) {
|
|
||||||
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
|
||||||
} else {
|
} else {
|
||||||
StringMapWrapper.forEach(
|
Object.keys(rawClassVal).forEach(klass => {
|
||||||
<{[k: string]: any}>rawClassVal, (expVal: any, className: string) => {
|
if (isPresent(rawClassVal[klass])) this._toggleClass(klass, !isCleanup);
|
||||||
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleClass(className: string, enabled: boolean): void {
|
private _toggleClass(klass: string, enabled: boolean): void {
|
||||||
className = className.trim();
|
klass = klass.trim();
|
||||||
if (className.length > 0) {
|
if (klass) {
|
||||||
if (className.indexOf(' ') > -1) {
|
klass.split(/\s+/g).forEach(
|
||||||
var classes = className.split(/\s+/g);
|
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, enabled); });
|
||||||
for (var i = 0, len = classes.length; i < len; i++) {
|
|
||||||
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {getTypeNameForDebugging, isBlank, isPresent} from '../facade/lang';
|
import {getTypeNameForDebugging} from '../facade/lang';
|
||||||
|
|
||||||
export class NgForRow {
|
export class NgForRow {
|
||||||
constructor(public $implicit: any, public index: number, public count: number) {}
|
constructor(public $implicit: any, public index: number, public count: number) {}
|
||||||
@ -91,16 +91,16 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
@Input() ngForOf: any;
|
@Input() ngForOf: any;
|
||||||
@Input() ngForTrackBy: TrackByFn;
|
@Input() ngForTrackBy: TrackByFn;
|
||||||
|
|
||||||
private _differ: IterableDiffer;
|
private _differ: IterableDiffer = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
|
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
|
||||||
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
private _differs: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
||||||
if (isPresent(value)) {
|
if (value) {
|
||||||
this._templateRef = value;
|
this._template = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,9 +108,9 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
if ('ngForOf' in changes) {
|
if ('ngForOf' in changes) {
|
||||||
// React on ngForOf changes only once all inputs have been initialized
|
// React on ngForOf changes only once all inputs have been initialized
|
||||||
const value = changes['ngForOf'].currentValue;
|
const value = changes['ngForOf'].currentValue;
|
||||||
if (isBlank(this._differ) && isPresent(value)) {
|
if (!this._differ && value) {
|
||||||
try {
|
try {
|
||||||
this._differ = this._iterableDiffers.find(value).create(this._cdr, this.ngForTrackBy);
|
this._differ = this._differs.find(value).create(this._cdr, this.ngForTrackBy);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||||
@ -120,9 +120,9 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngDoCheck() {
|
ngDoCheck() {
|
||||||
if (isPresent(this._differ)) {
|
if (this._differ) {
|
||||||
const changes = this._differ.diff(this.ngForOf);
|
const changes = this._differ.diff(this.ngForOf);
|
||||||
if (isPresent(changes)) this._applyChanges(changes);
|
if (changes) this._applyChanges(changes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,16 +131,16 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
changes.forEachOperation(
|
changes.forEachOperation(
|
||||||
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||||
if (item.previousIndex == null) {
|
if (item.previousIndex == null) {
|
||||||
let view = this._viewContainer.createEmbeddedView(
|
const view = this._viewContainer.createEmbeddedView(
|
||||||
this._templateRef, new NgForRow(null, null, null), currentIndex);
|
this._template, new NgForRow(null, null, null), currentIndex);
|
||||||
let tuple = new RecordViewTuple(item, view);
|
const tuple = new RecordViewTuple(item, view);
|
||||||
insertTuples.push(tuple);
|
insertTuples.push(tuple);
|
||||||
} else if (currentIndex == null) {
|
} else if (currentIndex == null) {
|
||||||
this._viewContainer.remove(adjustedPreviousIndex);
|
this._viewContainer.remove(adjustedPreviousIndex);
|
||||||
} else {
|
} else {
|
||||||
let view = this._viewContainer.get(adjustedPreviousIndex);
|
const view = this._viewContainer.get(adjustedPreviousIndex);
|
||||||
this._viewContainer.move(view, currentIndex);
|
this._viewContainer.move(view, currentIndex);
|
||||||
let tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
|
const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
|
||||||
insertTuples.push(tuple);
|
insertTuples.push(tuple);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -150,13 +150,13 @@ export class NgFor implements DoCheck, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||||
viewRef.context.index = i;
|
viewRef.context.index = i;
|
||||||
viewRef.context.count = ilen;
|
viewRef.context.count = ilen;
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.forEachIdentityChange((record: any) => {
|
changes.forEachIdentityChange((record: any) => {
|
||||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||||
viewRef.context.$implicit = record.item;
|
viewRef.context.$implicit = record.item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,6 @@
|
|||||||
|
|
||||||
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {isBlank} from '../facade/lang';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||||
*
|
*
|
||||||
@ -38,18 +34,17 @@ import {isBlank} from '../facade/lang';
|
|||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngIf]'})
|
@Directive({selector: '[ngIf]'})
|
||||||
export class NgIf {
|
export class NgIf {
|
||||||
private _prevCondition: boolean = null;
|
private _hasView: boolean = false;
|
||||||
|
|
||||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
|
constructor(private _viewContainer: ViewContainerRef, private _template: TemplateRef<Object>) {}
|
||||||
}
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngIf(newCondition: any) {
|
set ngIf(condition: any) {
|
||||||
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
|
if (condition && !this._hasView) {
|
||||||
this._prevCondition = true;
|
this._hasView = true;
|
||||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
this._viewContainer.createEmbeddedView(this._template);
|
||||||
} else if (!newCondition && (isBlank(this._prevCondition) || this._prevCondition)) {
|
} else if (!condition && this._hasView) {
|
||||||
this._prevCondition = false;
|
this._hasView = false;
|
||||||
this._viewContainer.clear();
|
this._viewContainer.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,57 +6,43 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Attribute, Directive, Host, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Attribute, Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import {NgLocalization, getPluralCategory} from '../localization';
|
import {NgLocalization, getPluralCategory} from '../localization';
|
||||||
|
|
||||||
import {SwitchView} from './ng_switch';
|
import {SwitchView} from './ng_switch';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
* @ngModule CommonModule
|
||||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
*
|
||||||
|
* @whatItDoes Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <some-element [ngPlural]="value">
|
||||||
|
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
|
||||||
|
* </some-element>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees
|
||||||
|
* that match the switch expression's pluralization category.
|
||||||
*
|
*
|
||||||
* To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
* To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
||||||
* to a
|
* to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their
|
||||||
* switch expression.
|
* expression:
|
||||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
* - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||||
* expression.
|
* matches the switch expression exactly,
|
||||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
* - otherwise, the view will be treated as a "category match", and will only display if exact
|
||||||
* matches the switch expression exactly.
|
* value matches aren't found and the value maps to its category for the defined locale.
|
||||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
|
||||||
* value matches aren't found and the value maps to its category for the defined locale.
|
|
||||||
*
|
*
|
||||||
* ```typescript
|
* See http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* // best practice is to define the locale at the application level
|
|
||||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
|
||||||
* })
|
|
||||||
* @View({
|
|
||||||
* template: `
|
|
||||||
* <p>Value = {{value}}</p>
|
|
||||||
* <button (click)="inc()">Increment</button>
|
|
||||||
*
|
*
|
||||||
* <div [ngPlural]="value">
|
|
||||||
* <template ngPluralCase="=0">there is nothing</template>
|
|
||||||
* <template ngPluralCase="=1">there is one</template>
|
|
||||||
* <template ngPluralCase="few">there are a few</template>
|
|
||||||
* <template ngPluralCase="other">there is some number</template>
|
|
||||||
* </div>
|
|
||||||
* `,
|
|
||||||
* directives: [NgPlural, NgPluralCase]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* value = 'init';
|
|
||||||
*
|
|
||||||
* inc() {
|
|
||||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngPlural]'})
|
@Directive({selector: '[ngPlural]'})
|
||||||
@ -75,29 +61,42 @@ export class NgPlural {
|
|||||||
|
|
||||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||||
|
|
||||||
/** @internal */
|
private _updateView(): void {
|
||||||
_updateView(): void {
|
|
||||||
this._clearViews();
|
this._clearViews();
|
||||||
|
|
||||||
var key =
|
const cases = Object.keys(this._caseViews);
|
||||||
getPluralCategory(this._switchValue, Object.keys(this._caseViews), this._localization);
|
const key = getPluralCategory(this._switchValue, cases, this._localization);
|
||||||
this._activateView(this._caseViews[key]);
|
this._activateView(this._caseViews[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _clearViews() {
|
||||||
_clearViews() {
|
if (this._activeView) this._activeView.destroy();
|
||||||
if (isPresent(this._activeView)) this._activeView.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _activateView(view: SwitchView) {
|
||||||
_activateView(view: SwitchView) {
|
if (view) {
|
||||||
if (!isPresent(view)) return;
|
this._activeView = view;
|
||||||
this._activeView = view;
|
this._activeView.create();
|
||||||
this._activeView.create();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
*
|
||||||
|
* @whatItDoes Creates a view that will be added/removed from the parent {@link NgPlural} when the
|
||||||
|
* given expression matches the plural expression according to CLDR rules.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <some-element [ngPlural]="value">
|
||||||
|
* <ng-container *ngPluralCase="'=0'">...</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'other'">...</ng-container>
|
||||||
|
* </some-element>
|
||||||
|
*```
|
||||||
|
*
|
||||||
|
* See {@link NgPlural} for more details and example.
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngPluralCase]'})
|
@Directive({selector: '[ngPluralCase]'})
|
||||||
|
@ -8,70 +8,32 @@
|
|||||||
|
|
||||||
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
* @ngModule CommonModule
|
||||||
*
|
*
|
||||||
* An expression assigned to the `ngStyle` property must evaluate to an object and the
|
* @whatItDoes Update an HTML element styles.
|
||||||
* corresponding element styles are updated based on changes to this object. Style names to update
|
|
||||||
* are taken from the object's keys, and values - from the corresponding object's values.
|
|
||||||
*
|
|
||||||
* ### Syntax
|
|
||||||
*
|
|
||||||
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
|
|
||||||
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
|
|
||||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
|
||||||
*
|
*
|
||||||
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* import {Component} from '@angular/core';
|
* <some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
|
||||||
* import {NgStyle} from '@angular/common';
|
|
||||||
*
|
*
|
||||||
* @Component({
|
* <some-element [ngStyle]="{'max-width.px': widthExp}">...</some-element>
|
||||||
* selector: 'ngStyle-example',
|
|
||||||
* template: `
|
|
||||||
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
|
||||||
* Change style of this text!
|
|
||||||
* </h1>
|
|
||||||
*
|
*
|
||||||
* <hr>
|
* <some-element [ngStyle]="objExp">...</some-element>
|
||||||
*
|
|
||||||
* <label>Italic: <input type="checkbox" (change)="changeStyle($event)"></label>
|
|
||||||
* <label>Bold: <input type="checkbox" (change)="changeWeight($event)"></label>
|
|
||||||
* <label>Size: <input type="text" [value]="size" (change)="size = $event.target.value"></label>
|
|
||||||
* `,
|
|
||||||
* directives: [NgStyle]
|
|
||||||
* })
|
|
||||||
* export class NgStyleExample {
|
|
||||||
* style = 'normal';
|
|
||||||
* weight = 'normal';
|
|
||||||
* size = '20px';
|
|
||||||
*
|
|
||||||
* changeStyle($event: any) {
|
|
||||||
* this.style = $event.target.checked ? 'italic' : 'normal';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* changeWeight($event: any) {
|
|
||||||
* this.weight = $event.target.checked ? 'bold' : 'normal';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
|
* @description
|
||||||
* based on the `style` property's value changes.
|
*
|
||||||
|
* The styles are updated according to the value of the expression evaluation:
|
||||||
|
* - keys are style names with an option `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||||
|
* - values are the values assigned to those properties (expressed in the given unit).
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngStyle]'})
|
@Directive({selector: '[ngStyle]'})
|
||||||
export class NgStyle implements DoCheck {
|
export class NgStyle implements DoCheck {
|
||||||
/** @internal */
|
private _ngStyle: {[key: string]: string};
|
||||||
_ngStyle: {[key: string]: string};
|
private _differ: KeyValueDiffer;
|
||||||
/** @internal */
|
|
||||||
_differ: KeyValueDiffer;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
@ -79,34 +41,34 @@ export class NgStyle implements DoCheck {
|
|||||||
@Input()
|
@Input()
|
||||||
set ngStyle(v: {[key: string]: string}) {
|
set ngStyle(v: {[key: string]: string}) {
|
||||||
this._ngStyle = v;
|
this._ngStyle = v;
|
||||||
if (isBlank(this._differ) && isPresent(v)) {
|
if (!this._differ && v) {
|
||||||
this._differ = this._differs.find(this._ngStyle).create(null);
|
this._differ = this._differs.find(v).create(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDoCheck() {
|
ngDoCheck() {
|
||||||
if (isPresent(this._differ)) {
|
if (this._differ) {
|
||||||
var changes = this._differ.diff(this._ngStyle);
|
const changes = this._differ.diff(this._ngStyle);
|
||||||
if (isPresent(changes)) {
|
if (changes) {
|
||||||
this._applyChanges(changes);
|
this._applyChanges(changes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(changes: any): void {
|
private _applyChanges(changes: any): void {
|
||||||
changes.forEachRemovedItem(
|
changes.forEachRemovedItem((record: KeyValueChangeRecord) => this._setStyle(record.key, null));
|
||||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
|
|
||||||
changes.forEachAddedItem(
|
changes.forEachAddedItem(
|
||||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
|
||||||
|
|
||||||
changes.forEachChangedItem(
|
changes.forEachChangedItem(
|
||||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setStyle(name: string, val: string): void {
|
private _setStyle(nameAndUnit: string, value: string): void {
|
||||||
const nameParts = name.split('.');
|
const [name, unit] = nameAndUnit.split('.');
|
||||||
const nameToSet = nameParts[0];
|
value = value && unit ? `${value}${unit}` : value;
|
||||||
const valToSet = isPresent(val) && nameParts.length === 2 ? `${val}${nameParts[1]}` : val;
|
|
||||||
|
|
||||||
this._renderer.setElementStyle(this._ngEl.nativeElement, nameToSet, valToSet);
|
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {isBlank, isPresent, normalizeBlank} from '../facade/lang';
|
|
||||||
|
|
||||||
const _CASE_DEFAULT = new Object();
|
const _CASE_DEFAULT = new Object();
|
||||||
|
|
||||||
@ -23,58 +22,44 @@ export class SwitchView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
|
* @ngModule CommonModule
|
||||||
*
|
*
|
||||||
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
|
* @whatItDoes Adds / removes DOM sub-trees when the nest match expressions matches the switch
|
||||||
* preserved at the location as specified in the template.
|
* expression.
|
||||||
*
|
*
|
||||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
* @howToUse
|
||||||
* obtained from the evaluated switch expression. In other words, you define a container element
|
|
||||||
* (where you place the directive with a switch expression on the
|
|
||||||
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
|
|
||||||
* place a `[ngSwitchCase]` attribute per element.
|
|
||||||
*
|
|
||||||
* The `ngSwitchCase` property is used to inform `NgSwitch` which element to display when the
|
|
||||||
* expression is evaluated. If a matching expression is not found via a `ngSwitchCase` property
|
|
||||||
* then an element with the `ngSwitchDefault` attribute is displayed.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <p>Value = {{value}}</p>
|
|
||||||
* <button (click)="inc()">Increment</button>
|
|
||||||
*
|
|
||||||
* <div [ngSwitch]="value">
|
|
||||||
* <p *ngSwitchCase="'init'">increment to start</p>
|
|
||||||
* <p *ngSwitchCase="0">0, increment again</p>
|
|
||||||
* <p *ngSwitchCase="1">1, increment again</p>
|
|
||||||
* <p *ngSwitchCase="2">2, stop incrementing</p>
|
|
||||||
* <p *ngSwitchDefault>> 2, STOP!</p>
|
|
||||||
* </div>
|
|
||||||
*
|
|
||||||
* <!-- alternate syntax -->
|
|
||||||
*
|
|
||||||
* <p [ngSwitch]="value">
|
|
||||||
* <template ngSwitchCase="init">increment to start</template>
|
|
||||||
* <template [ngSwitchCase]="0">0, increment again</template>
|
|
||||||
* <template [ngSwitchCase]="1">1, increment again</template>
|
|
||||||
* <template [ngSwitchCase]="2">2, stop incrementing</template>
|
|
||||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
|
||||||
* </p>
|
|
||||||
* `,
|
|
||||||
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* value = 'init';
|
|
||||||
*
|
|
||||||
* inc() {
|
|
||||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
* ```
|
||||||
|
* <container-element [ngSwitch]="switch_expression">
|
||||||
|
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||||
|
* <some-element *ngSwitchCase="match_expression_2">...</some-element>
|
||||||
|
* <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
|
||||||
|
* <ng-container *ngSwitchCase="match_expression_3">
|
||||||
|
* <!-- use a ng-container to group multiple root nodes -->
|
||||||
|
* <inner-element></inner-element>
|
||||||
|
* <inner-other-element></inner-other-element>
|
||||||
|
* </ng-container>
|
||||||
|
* <some-element *ngSwitchDefault>...</p>
|
||||||
|
* </container-element>
|
||||||
|
* ```
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* `NgSwitch` stamps out nested views when their match expression value matches the value of the
|
||||||
|
* switch expression.
|
||||||
|
*
|
||||||
|
* In other words:
|
||||||
|
* - you define a container element (where you place the directive with a switch expression on the
|
||||||
|
* `[ngSwitch]="..."` attribute)
|
||||||
|
* - you define inner views inside the `NgSwitch` and place a `*ngSwitchCase` attribute on the view
|
||||||
|
* root elements.
|
||||||
|
*
|
||||||
|
* Elements within `NgSwitch` but outside of a `NgSwitchCase` or `NgSwitchDefault` directives will
|
||||||
|
* be
|
||||||
|
* preserved at the location.
|
||||||
|
*
|
||||||
|
* The `ngSwitchCase` directive informs the parent `NgSwitch` of which view to display when the
|
||||||
|
* expression is evaluated.
|
||||||
|
* When no matching expression is found on a `ngSwitchCase` view, the `ngSwitchDefault` view is
|
||||||
|
* stamped out.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -92,10 +77,10 @@ export class NgSwitch {
|
|||||||
|
|
||||||
// Add the ViewContainers matching the value (with a fallback to default)
|
// Add the ViewContainers matching the value (with a fallback to default)
|
||||||
this._useDefault = false;
|
this._useDefault = false;
|
||||||
var views = this._valueViews.get(value);
|
let views = this._valueViews.get(value);
|
||||||
if (isBlank(views)) {
|
if (!views) {
|
||||||
this._useDefault = true;
|
this._useDefault = true;
|
||||||
views = normalizeBlank(this._valueViews.get(_CASE_DEFAULT));
|
views = this._valueViews.get(_CASE_DEFAULT) || null;
|
||||||
}
|
}
|
||||||
this._activateViews(views);
|
this._activateViews(views);
|
||||||
|
|
||||||
@ -126,19 +111,16 @@ export class NgSwitch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _emptyAllActiveViews(): void {
|
||||||
_emptyAllActiveViews(): void {
|
const activeContainers = this._activeViews;
|
||||||
var activeContainers = this._activeViews;
|
|
||||||
for (var i = 0; i < activeContainers.length; i++) {
|
for (var i = 0; i < activeContainers.length; i++) {
|
||||||
activeContainers[i].destroy();
|
activeContainers[i].destroy();
|
||||||
}
|
}
|
||||||
this._activeViews = [];
|
this._activeViews = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _activateViews(views: SwitchView[]): void {
|
||||||
_activateViews(views: SwitchView[]): void {
|
if (views) {
|
||||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
|
||||||
if (isPresent(views)) {
|
|
||||||
for (var i = 0; i < views.length; i++) {
|
for (var i = 0; i < views.length; i++) {
|
||||||
views[i].create();
|
views[i].create();
|
||||||
}
|
}
|
||||||
@ -148,19 +130,18 @@ export class NgSwitch {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_registerView(value: any, view: SwitchView): void {
|
_registerView(value: any, view: SwitchView): void {
|
||||||
var views = this._valueViews.get(value);
|
let views = this._valueViews.get(value);
|
||||||
if (isBlank(views)) {
|
if (!views) {
|
||||||
views = [];
|
views = [];
|
||||||
this._valueViews.set(value, views);
|
this._valueViews.set(value, views);
|
||||||
}
|
}
|
||||||
views.push(view);
|
views.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _deregisterView(value: any, view: SwitchView): void {
|
||||||
_deregisterView(value: any, view: SwitchView): void {
|
|
||||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||||
if (value === _CASE_DEFAULT) return;
|
if (value === _CASE_DEFAULT) return;
|
||||||
var views = this._valueViews.get(value);
|
const views = this._valueViews.get(value);
|
||||||
if (views.length == 1) {
|
if (views.length == 1) {
|
||||||
this._valueViews.delete(value);
|
this._valueViews.delete(value);
|
||||||
} else {
|
} else {
|
||||||
@ -170,10 +151,24 @@ export class NgSwitch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the sub-tree when the `ngSwitchCase` expression evaluates to the same value as the
|
* @ngModule CommonModule
|
||||||
* enclosing switch expression.
|
|
||||||
*
|
*
|
||||||
* If multiple match expression match the switch expression value, all of them are displayed.
|
* @whatItDoes Creates a view that will be added/removed from the parent {@link NgSwitch} when the
|
||||||
|
* given expression evaluate to respectively the same/different value as the switch
|
||||||
|
* expression.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <container-element [ngSwitch]="switch_expression">
|
||||||
|
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||||
|
* </container-element>
|
||||||
|
*```
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Insert the sub-tree when the expression evaluates to the same value as the enclosing switch
|
||||||
|
* expression.
|
||||||
|
*
|
||||||
|
* If multiple match expressions match the switch expression value, all of them are displayed.
|
||||||
*
|
*
|
||||||
* See {@link NgSwitch} for more details and example.
|
* See {@link NgSwitch} for more details and example.
|
||||||
*
|
*
|
||||||
@ -182,10 +177,8 @@ export class NgSwitch {
|
|||||||
@Directive({selector: '[ngSwitchCase]'})
|
@Directive({selector: '[ngSwitchCase]'})
|
||||||
export class NgSwitchCase {
|
export class NgSwitchCase {
|
||||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||||
/** @internal */
|
private _value: any = _CASE_DEFAULT;
|
||||||
_value: any = _CASE_DEFAULT;
|
private _view: SwitchView;
|
||||||
/** @internal */
|
|
||||||
_view: SwitchView;
|
|
||||||
private _switch: NgSwitch;
|
private _switch: NgSwitch;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -203,8 +196,23 @@ export class NgSwitchCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default case statements are displayed when no match expression matches the switch expression
|
* @ngModule CommonModule
|
||||||
* value.
|
* @whatItDoes Creates a view that is added to the parent {@link NgSwitch} when no case expressions
|
||||||
|
* match the
|
||||||
|
* switch expression.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <container-element [ngSwitch]="switch_expression">
|
||||||
|
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||||
|
* <some-other-element *ngSwitchDefault>...</some-other-element>
|
||||||
|
* </container-element>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Insert the sub-tree when no case expressions evaluate to the same value as the enclosing switch
|
||||||
|
* expression.
|
||||||
*
|
*
|
||||||
* See {@link NgSwitch} for more details and example.
|
* See {@link NgSwitch} for more details and example.
|
||||||
*
|
*
|
||||||
|
@ -6,24 +6,28 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
* @ngModule CommonModule
|
||||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
|
||||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
|
||||||
* available within the `TemplateRef`.
|
|
||||||
*
|
*
|
||||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
* @whatItDoes Inserts an embedded view from a prepared `TemplateRef`
|
||||||
*
|
|
||||||
* ### Syntax
|
|
||||||
*
|
*
|
||||||
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
* <template [ngTemplateOutlet]="templateRefExpression"
|
||||||
* [ngOutletContext]="objectExpression">
|
* [ngOutletContext]="objectExpression">
|
||||||
* </template>
|
* </template>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||||
|
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||||
|
* available within the `TemplateRef`.
|
||||||
|
*
|
||||||
|
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngTemplateOutlet]'})
|
@Directive({selector: '[ngTemplateOutlet]'})
|
||||||
@ -40,7 +44,7 @@ export class NgTemplateOutlet implements OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (this._viewRef) {
|
if (this._viewRef) {
|
||||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export enum Plural {
|
|||||||
Two,
|
Two,
|
||||||
Few,
|
Few,
|
||||||
Many,
|
Many,
|
||||||
Other
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +87,7 @@ export function getPluralCase(locale: string, nLike: number | string): Plural {
|
|||||||
const f = parseInt(nDecimal, 10);
|
const f = parseInt(nDecimal, 10);
|
||||||
const t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0;
|
const t = parseInt(n.toString().replace(/^[^.]*\.?|0+$/g, ''), 10) || 0;
|
||||||
|
|
||||||
const lang = locale.split('_')[0].toLowerCase();
|
const lang = locale.split('-')[0].toLowerCase();
|
||||||
|
|
||||||
switch (lang) {
|
switch (lang) {
|
||||||
case 'af':
|
case 'af':
|
||||||
|
@ -49,16 +49,20 @@ export class Location {
|
|||||||
_subject: EventEmitter<any> = new EventEmitter();
|
_subject: EventEmitter<any> = new EventEmitter();
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_baseHref: string;
|
_baseHref: string;
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_platformStrategy: LocationStrategy;
|
_platformStrategy: LocationStrategy;
|
||||||
|
|
||||||
constructor(platformStrategy: LocationStrategy) {
|
constructor(platformStrategy: LocationStrategy) {
|
||||||
this._platformStrategy = platformStrategy;
|
this._platformStrategy = platformStrategy;
|
||||||
var browserBaseHref = this._platformStrategy.getBaseHref();
|
const browserBaseHref = this._platformStrategy.getBaseHref();
|
||||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||||
this._platformStrategy.onPopState(
|
this._platformStrategy.onPopState((ev) => {
|
||||||
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
|
this._subject.emit({
|
||||||
|
'url': this.path(true),
|
||||||
|
'pop': true,
|
||||||
|
'type': ev.type,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,7 +83,7 @@ export class Location {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a string representing a URL, returns the normalized URL path without leading or
|
* Given a string representing a URL, returns the normalized URL path without leading or
|
||||||
* trailing slashes
|
* trailing slashes.
|
||||||
*/
|
*/
|
||||||
normalize(url: string): string {
|
normalize(url: string): string {
|
||||||
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
|
return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
|
||||||
|
@ -49,6 +49,7 @@ export abstract class LocationStrategy {
|
|||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
|
* ```typescript
|
||||||
* import {Component, NgModule} from '@angular/core';
|
* import {Component, NgModule} from '@angular/core';
|
||||||
* import {APP_BASE_HREF} from '@angular/common';
|
* import {APP_BASE_HREF} from '@angular/common';
|
||||||
*
|
*
|
||||||
|
@ -11,13 +11,3 @@
|
|||||||
* @description
|
* @description
|
||||||
* This module provides a set of common Pipes.
|
* This module provides a set of common Pipes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {AsyncPipe} from './pipes/async_pipe';
|
|
||||||
export {DatePipe} from './pipes/date_pipe';
|
|
||||||
export {I18nPluralPipe} from './pipes/i18n_plural_pipe';
|
|
||||||
export {I18nSelectPipe} from './pipes/i18n_select_pipe';
|
|
||||||
export {JsonPipe} from './pipes/json_pipe';
|
|
||||||
export {LowerCasePipe} from './pipes/lowercase_pipe';
|
|
||||||
export {CurrencyPipe, DecimalPipe, PercentPipe} from './pipes/number_pipe';
|
|
||||||
export {SlicePipe} from './pipes/slice_pipe';
|
|
||||||
export {UpperCasePipe} from './pipes/uppercase_pipe';
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
||||||
import {EventEmitter, Observable} from '../facade/async';
|
import {EventEmitter, Observable} from '../facade/async';
|
||||||
import {isBlank, isPresent, isPromise} from '../facade/lang';
|
import {isPromise} from '../private_import_core';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
interface SubscriptionStrategy {
|
interface SubscriptionStrategy {
|
||||||
@ -37,63 +37,54 @@ class PromiseStrategy implements SubscriptionStrategy {
|
|||||||
onDestroy(subscription: any): void {}
|
onDestroy(subscription: any): void {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _promiseStrategy = new PromiseStrategy();
|
const _promiseStrategy = new PromiseStrategy();
|
||||||
var _observableStrategy = new ObservableStrategy();
|
const _observableStrategy = new ObservableStrategy();
|
||||||
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Unwraps a value from an asynchronous primitive.
|
||||||
|
* @howToUse `observable_or_promise_expression | async`
|
||||||
|
* @description
|
||||||
* The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has
|
* The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has
|
||||||
* emitted.
|
* emitted. When a new value is emitted, the `async` pipe marks the component to be checked for
|
||||||
* When a new value is emitted, the `async` pipe marks the component to be checked for changes.
|
* changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
||||||
* When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
|
||||||
* potential memory leaks.
|
* potential memory leaks.
|
||||||
*
|
*
|
||||||
* ## Usage
|
|
||||||
*
|
|
||||||
* object | async
|
|
||||||
*
|
|
||||||
* where `object` is of type `Observable` or of type `Promise`.
|
|
||||||
*
|
*
|
||||||
* ## Examples
|
* ## Examples
|
||||||
*
|
*
|
||||||
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
|
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
|
||||||
* promise.
|
* promise.
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipePromise'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
||||||
*
|
*
|
||||||
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
||||||
* to the view. Every 500ms, the `time` Observable updates the view with the current time.
|
* to the view. The Observable continuesly updates the view with the current time.
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipeObservable'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Pipe({name: 'async', pure: false})
|
@Pipe({name: 'async', pure: false})
|
||||||
export class AsyncPipe implements OnDestroy {
|
export class AsyncPipe implements OnDestroy {
|
||||||
/** @internal */
|
private _latestValue: Object = null;
|
||||||
_latestValue: Object = null;
|
private _latestReturnedValue: Object = null;
|
||||||
/** @internal */
|
|
||||||
_latestReturnedValue: Object = null;
|
|
||||||
|
|
||||||
/** @internal */
|
private _subscription: Object = null;
|
||||||
_subscription: Object = null;
|
private _obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
|
||||||
/** @internal */
|
|
||||||
_obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
|
|
||||||
/** @internal */
|
|
||||||
_ref: ChangeDetectorRef;
|
|
||||||
private _strategy: SubscriptionStrategy = null;
|
private _strategy: SubscriptionStrategy = null;
|
||||||
|
|
||||||
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
|
constructor(private _ref: ChangeDetectorRef) {}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (isPresent(this._subscription)) {
|
if (this._subscription) {
|
||||||
this._dispose();
|
this._dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||||
if (isBlank(this._obj)) {
|
if (!this._obj) {
|
||||||
if (isPresent(obj)) {
|
if (obj) {
|
||||||
this._subscribe(obj);
|
this._subscribe(obj);
|
||||||
}
|
}
|
||||||
this._latestReturnedValue = this._latestValue;
|
this._latestReturnedValue = this._latestValue;
|
||||||
@ -107,33 +98,32 @@ export class AsyncPipe implements OnDestroy {
|
|||||||
|
|
||||||
if (this._latestValue === this._latestReturnedValue) {
|
if (this._latestValue === this._latestReturnedValue) {
|
||||||
return this._latestReturnedValue;
|
return this._latestReturnedValue;
|
||||||
} else {
|
|
||||||
this._latestReturnedValue = this._latestValue;
|
|
||||||
return WrappedValue.wrap(this._latestValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._latestReturnedValue = this._latestValue;
|
||||||
|
return WrappedValue.wrap(this._latestValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
|
||||||
_subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
|
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
this._strategy = this._selectStrategy(obj);
|
this._strategy = this._selectStrategy(obj);
|
||||||
this._subscription = this._strategy.createSubscription(
|
this._subscription = this._strategy.createSubscription(
|
||||||
obj, (value: Object) => this._updateLatestValue(obj, value));
|
obj, (value: Object) => this._updateLatestValue(obj, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||||
_selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
|
||||||
if (isPromise(obj)) {
|
if (isPromise(obj)) {
|
||||||
return _promiseStrategy;
|
return _promiseStrategy;
|
||||||
} else if ((<any>obj).subscribe) {
|
|
||||||
return _observableStrategy;
|
|
||||||
} else {
|
|
||||||
throw new InvalidPipeArgumentError(AsyncPipe, obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((<any>obj).subscribe) {
|
||||||
|
return _observableStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidPipeArgumentError(AsyncPipe, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _dispose(): void {
|
||||||
_dispose(): void {
|
|
||||||
this._strategy.dispose(this._subscription);
|
this._strategy.dispose(this._subscription);
|
||||||
this._latestValue = null;
|
this._latestValue = null;
|
||||||
this._latestReturnedValue = null;
|
this._latestReturnedValue = null;
|
||||||
@ -141,8 +131,7 @@ export class AsyncPipe implements OnDestroy {
|
|||||||
this._obj = null;
|
this._obj = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _updateLatestValue(async: any, value: Object) {
|
||||||
_updateLatestValue(async: any, value: Object) {
|
|
||||||
if (async === this._obj) {
|
if (async === this._obj) {
|
||||||
this._latestValue = value;
|
this._latestValue = value;
|
||||||
this._ref.markForCheck();
|
this._ref.markForCheck();
|
||||||
|
@ -7,31 +7,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
|
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
|
||||||
import {DateFormatter} from '../facade/intl';
|
import {DateFormatter} from '../facade/intl';
|
||||||
import {DateWrapper, NumberWrapper, isBlank, isDate, isString} from '../facade/lang';
|
import {NumberWrapper, isBlank, isDate} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a date value to a string based on the requested format.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Formats a date according to locale rules.
|
||||||
|
* @howToUse `date_expression | date[:format]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* WARNINGS:
|
* Where:
|
||||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||||
* Instead users should treat the date as an immutable object and change the reference when the
|
* (https://www.w3.org/TR/NOTE-datetime).
|
||||||
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
* - `format` indicates which date/time components to include. The format can be predifined as
|
||||||
* which would be an expensive operation).
|
* shown below or custom as shown in the table.
|
||||||
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||||
* browsers.
|
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||||
|
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. `Friday, September 3, 2010` for `en-US`)
|
||||||
|
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. `September 3, 2010` for `en-US`)
|
||||||
|
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. `Sep 3, 2010` for `en-US`)
|
||||||
|
* - `'shortDate'`: equivalent to `'yMd'` (e.g. `9/3/2010` for `en-US`)
|
||||||
|
* - `'mediumTime'`: equivalent to `'jms'` (e.g. `12:05:08 PM` for `en-US`)
|
||||||
|
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||||
*
|
*
|
||||||
* ## Usage
|
|
||||||
*
|
|
||||||
* expression | date[:format]
|
|
||||||
*
|
|
||||||
* where `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
|
||||||
* (https://www.w3.org/TR/NOTE-datetime) and `format` indicates which date/time components to
|
|
||||||
* include:
|
|
||||||
*
|
*
|
||||||
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
||||||
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
||||||
@ -52,18 +52,15 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* In javascript, only the components specified will be respected (not the ordering,
|
* In javascript, only the components specified will be respected (not the ordering,
|
||||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||||
*
|
*
|
||||||
* `format` can also be one of the following predefined formats:
|
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||||
*
|
*
|
||||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. Sep 3, 2010, 12:05:08 PM for en-US)
|
* WARNINGS:
|
||||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. 9/3/2010, 12:05 PM for en-US)
|
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||||
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. Friday, September 3, 2010 for en-US)
|
* Instead users should treat the date as an immutable object and change the reference when the
|
||||||
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. September 3, 2010)
|
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
||||||
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. Sep 3, 2010 for en-US)
|
* which would be an expensive operation).
|
||||||
* - `'shortDate'`: equivalent to `'yMd'` (e.g. 9/3/2010 for en-US)
|
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
||||||
* - `'mediumTime'`: equivalent to `'jms'` (e.g. 12:05:08 PM for en-US)
|
* browsers.
|
||||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. 12:05 PM for en-US)
|
|
||||||
*
|
|
||||||
* Timezone of the formatted text will be the local system timezone of the end-users machine.
|
|
||||||
*
|
*
|
||||||
* ### Examples
|
* ### Examples
|
||||||
*
|
*
|
||||||
@ -77,14 +74,14 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/date_pipe/date_pipe_example.ts region='DatePipe'}
|
* {@example common/pipes/ts/date_pipe.ts region='DatePipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Pipe({name: 'date', pure: true})
|
@Pipe({name: 'date', pure: true})
|
||||||
export class DatePipe implements PipeTransform {
|
export class DatePipe implements PipeTransform {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
static _ALIASES: {[key: string]: String} = {
|
static _ALIASES: {[key: string]: string} = {
|
||||||
'medium': 'yMMMdjms',
|
'medium': 'yMMMdjms',
|
||||||
'short': 'yMdjm',
|
'short': 'yMdjm',
|
||||||
'fullDate': 'yMMMMEEEEd',
|
'fullDate': 'yMMMMEEEEd',
|
||||||
@ -105,23 +102,15 @@ export class DatePipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NumberWrapper.isNumeric(value)) {
|
if (NumberWrapper.isNumeric(value)) {
|
||||||
value = DateWrapper.fromMillis(parseFloat(value));
|
value = parseFloat(value);
|
||||||
} else if (isString(value)) {
|
|
||||||
value = DateWrapper.fromISOString(value);
|
|
||||||
}
|
}
|
||||||
if (StringMapWrapper.contains(DatePipe._ALIASES, pattern)) {
|
|
||||||
pattern = <string>StringMapWrapper.get(DatePipe._ALIASES, pattern);
|
return DateFormatter.format(
|
||||||
}
|
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||||
return DateFormatter.format(value, this._locale, pattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private supports(obj: any): boolean {
|
private supports(obj: any): boolean {
|
||||||
if (isDate(obj) || NumberWrapper.isNumeric(obj)) {
|
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
|
||||||
return true;
|
(typeof obj === 'string' && isDate(new Date(obj)));
|
||||||
}
|
|
||||||
if (isString(obj) && isDate(DateWrapper.fromISOString(obj))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,46 +7,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {StringWrapper, isBlank, isStringMap} from '../facade/lang';
|
import {isBlank, isStringMap} from '../facade/lang';
|
||||||
import {NgLocalization, getPluralCategory} from '../localization';
|
import {NgLocalization, getPluralCategory} from '../localization';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a value to a string that pluralizes the value properly.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Maps a value to a string that pluralizes the value according to locale rules.
|
||||||
|
* @howToUse `expression | i18nPlural:mapping`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* ## Usage
|
* Where:
|
||||||
*
|
* - `expression` is a number.
|
||||||
* expression | i18nPlural:mapping
|
* - `mapping` is an object that mimics the ICU format, see
|
||||||
*
|
* http://userguide.icu-project.org/formatparse/messages
|
||||||
* where `expression` is a number and `mapping` is an object that mimics the ICU format,
|
|
||||||
* see http://userguide.icu-project.org/formatparse/messages
|
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
* ```
|
* {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'}
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <div>
|
|
||||||
* {{ messages.length | i18nPlural: messageMapping }}
|
|
||||||
* </div>
|
|
||||||
* `,
|
|
||||||
* // best practice is to define the locale at the application level
|
|
||||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* class MyApp {
|
|
||||||
* messages: any[];
|
|
||||||
* messageMapping: {[k:string]: string} = {
|
|
||||||
* '=0': 'No messages.',
|
|
||||||
* '=1': 'One message.',
|
|
||||||
* 'other': '# messages.'
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@ -63,6 +43,6 @@ export class I18nPluralPipe implements PipeTransform {
|
|||||||
|
|
||||||
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
||||||
|
|
||||||
return StringWrapper.replaceAll(pluralMap[key], _INTERPOLATION_REGEXP, value.toString());
|
return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,33 +11,18 @@ import {isBlank, isStringMap} from '../facade/lang';
|
|||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Generic selector that displays the string that matches the current value.
|
||||||
|
* @howToUse `expression | i18nSelect:mapping`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* Generic selector that displays the string that matches the current value.
|
* Where:
|
||||||
*
|
* - `mapping`: is an object that indicates the text that should be displayed
|
||||||
* ## Usage
|
|
||||||
*
|
|
||||||
* expression | i18nSelect:mapping
|
|
||||||
*
|
|
||||||
* where `mapping` is an object that indicates the text that should be displayed
|
|
||||||
* for different values of the provided `expression`.
|
* for different values of the provided `expression`.
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
* ```
|
* {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'}
|
||||||
* <div>
|
|
||||||
* {{ gender | i18nSelect: inviteMap }}
|
|
||||||
* </div>
|
|
||||||
*
|
|
||||||
* class MyApp {
|
|
||||||
* gender: string = 'male';
|
|
||||||
* inviteMap: any = {
|
|
||||||
* 'male': 'Invite him.',
|
|
||||||
* 'female': 'Invite her.',
|
|
||||||
* 'other': 'Invite them.'
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
|
@ -21,15 +21,22 @@ import {CurrencyPipe, DecimalPipe, PercentPipe} from './number_pipe';
|
|||||||
import {SlicePipe} from './slice_pipe';
|
import {SlicePipe} from './slice_pipe';
|
||||||
import {UpperCasePipe} from './uppercase_pipe';
|
import {UpperCasePipe} from './uppercase_pipe';
|
||||||
|
|
||||||
|
export {
|
||||||
|
AsyncPipe,
|
||||||
|
CurrencyPipe,
|
||||||
|
DatePipe,
|
||||||
|
DecimalPipe,
|
||||||
|
I18nPluralPipe,
|
||||||
|
I18nSelectPipe,
|
||||||
|
JsonPipe,
|
||||||
|
LowerCasePipe,
|
||||||
|
PercentPipe,
|
||||||
|
SlicePipe,
|
||||||
|
UpperCasePipe
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of Angular core pipes that are likely to be used in each and every
|
* A collection of Angular pipes that are likely to be used in each and every application.
|
||||||
* application.
|
|
||||||
*
|
|
||||||
* This collection can be used to quickly enumerate all the built-in pipes in the `pipes`
|
|
||||||
* property of the `@Component` decorator.
|
|
||||||
*
|
|
||||||
* @experimental Contains i18n pipes which are experimental
|
|
||||||
*/
|
*/
|
||||||
export const COMMON_PIPES = [
|
export const COMMON_PIPES = [
|
||||||
AsyncPipe,
|
AsyncPipe,
|
@ -13,10 +13,15 @@ import {Json} from '../facade/lang';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms any input value using `JSON.stringify`. Useful for debugging.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Converts value into JSON string.
|
||||||
|
* @howToUse `expression | json`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into string using `JSON.stringify`. Useful for debugging.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* {@example core/pipes/ts/json_pipe/json_pipe_example.ts region='JsonPipe'}
|
* {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
@ -7,16 +7,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank, isString} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms text to lowercase.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Transforms string to lowercase.
|
||||||
|
* @howToUse `expression | lowercase`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -24,7 +29,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
export class LowerCasePipe implements PipeTransform {
|
export class LowerCasePipe implements PipeTransform {
|
||||||
transform(value: string): string {
|
transform(value: string): string {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
if (!isString(value)) {
|
if (typeof value !== 'string') {
|
||||||
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
||||||
}
|
}
|
||||||
return value.toLowerCase();
|
return value.toLowerCase();
|
||||||
|
@ -9,21 +9,23 @@
|
|||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||||
|
|
||||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
||||||
import {NumberWrapper, isBlank, isNumber, isPresent, isString} from '../facade/lang';
|
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
|
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||||
|
|
||||||
function formatNumber(
|
function formatNumber(
|
||||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||||
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
||||||
if (isBlank(value)) return null;
|
if (isBlank(value)) return null;
|
||||||
|
|
||||||
// Convert strings to numbers
|
// Convert strings to numbers
|
||||||
value = isString(value) && NumberWrapper.isNumeric(value) ? +value : value;
|
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
|
||||||
if (!isNumber(value)) {
|
if (typeof value !== 'number') {
|
||||||
throw new InvalidPipeArgumentError(pipe, value);
|
throw new InvalidPipeArgumentError(pipe, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let minInt: number;
|
let minInt: number;
|
||||||
let minFraction: number;
|
let minFraction: number;
|
||||||
let maxFraction: number;
|
let maxFraction: number;
|
||||||
@ -34,8 +36,8 @@ function formatNumber(
|
|||||||
maxFraction = 3;
|
maxFraction = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(digits)) {
|
if (digits) {
|
||||||
var parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||||
if (parts === null) {
|
if (parts === null) {
|
||||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||||
}
|
}
|
||||||
@ -49,41 +51,40 @@ function formatNumber(
|
|||||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NumberFormatter.format(value as number, locale, style, {
|
return NumberFormatter.format(value as number, locale, style, {
|
||||||
minimumIntegerDigits: minInt,
|
minimumIntegerDigits: minInt,
|
||||||
minimumFractionDigits: minFraction,
|
minimumFractionDigits: minFraction,
|
||||||
maximumFractionDigits: maxFraction,
|
maximumFractionDigits: maxFraction,
|
||||||
currency: currency,
|
currency: currency,
|
||||||
currencyAsSymbol: currencyAsSymbol
|
currencyAsSymbol: currencyAsSymbol,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: this pipe uses the Internationalization API.
|
* @ngModule CommonModule
|
||||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
|
* @whatItDoes Formats a number according to locale rules.
|
||||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
* @howToUse `number_expression | number[:digitInfo]`
|
||||||
*
|
*
|
||||||
* Formats a number as local text. i.e. group sizing and separator and other locale-specific
|
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||||
* configurations are based on the active locale.
|
* configurations are based on the active locale.
|
||||||
*
|
*
|
||||||
* ### Usage
|
* where `expression` is a number:
|
||||||
*
|
* - `digitInfo` is a `string` which has a following format: <br>
|
||||||
* expression | number[:digitInfo]
|
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>
|
||||||
*
|
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
||||||
* where `expression` is a number and `digitInfo` has the following format:
|
* - `minFractionDigits` is the minimum number of digits after fraction. Defaults to `0`.
|
||||||
*
|
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
||||||
* {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
|
|
||||||
*
|
|
||||||
* - minIntegerDigits is the minimum number of integer digits to use. Defaults to 1.
|
|
||||||
* - minFractionDigits is the minimum number of digits after fraction. Defaults to 0.
|
|
||||||
* - maxFractionDigits is the maximum number of digits after fraction. Defaults to 3.
|
|
||||||
*
|
*
|
||||||
* For more information on the acceptable range for each of these numbers and other
|
* For more information on the acceptable range for each of these numbers and other
|
||||||
* details see your native internationalization library.
|
* details see your native internationalization library.
|
||||||
*
|
*
|
||||||
|
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||||
|
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||||
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='NumberPipe'}
|
* {@example common/pipes/ts/number_pipe.ts region='NumberPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -97,21 +98,22 @@ export class DecimalPipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: this pipe uses the Internationalization API.
|
* @ngModule CommonModule
|
||||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
|
* @whatItDoes Formats a number as a percentage according to locale rules.
|
||||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
* @howToUse `number_expression | percent[:digitInfo]`
|
||||||
*
|
*
|
||||||
* Formats a number as local percent.
|
* @description
|
||||||
*
|
*
|
||||||
* ### Usage
|
* Formats a number as percentage.
|
||||||
*
|
*
|
||||||
* expression | percent[:digitInfo]
|
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||||
*
|
*
|
||||||
* For more information about `digitInfo` see {@link DecimalPipe}
|
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||||
|
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='PercentPipe'}
|
* {@example common/pipes/ts/number_pipe.ts region='PercentPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -125,26 +127,26 @@ export class PercentPipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: this pipe uses the Internationalization API.
|
* @ngModule CommonModule
|
||||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
|
* @whatItDoes Formats a number as currency using locale rules.
|
||||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
* @howToUse `number_expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
|
* Use `currency` to format a number as currency.
|
||||||
*
|
*
|
||||||
* Formats a number as local currency.
|
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||||
|
* as `USD` for the US dollar and `EUR` for the euro.
|
||||||
|
* - `symbolDisplay` is a boolean indicating whether to use the currency symbol or code.
|
||||||
|
* - `true`: use symbol (e.g. `$`).
|
||||||
|
* - `false`(default): use code (e.g. `USD`).
|
||||||
|
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||||
*
|
*
|
||||||
* ### Usage
|
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||||
*
|
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||||
* expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]
|
|
||||||
*
|
|
||||||
* where `currencyCode` is the ISO 4217 currency code, such as "USD" for the US dollar and
|
|
||||||
* "EUR" for the euro. `symbolDisplay` is a boolean indicating whether to use the currency
|
|
||||||
* symbol (e.g. $) or the currency code (e.g. USD) in the output. The default for this value
|
|
||||||
* is `false`.
|
|
||||||
* For more information about `digitInfo` see {@link DecimalPipe}
|
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='CurrencyPipe'}
|
* {@example common/pipes/ts/number_pipe.ts region='CurrencyPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
@ -7,53 +7,41 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {isBlank} from '../facade/lang';
|
||||||
import {StringWrapper, isArray, isBlank, isString} from '../facade/lang';
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new List or String containing only a subset (slice) of the
|
* @ngModule CommonModule
|
||||||
* elements.
|
* @whatItDoes Creates a new List or String containing a subset (slice) of the elements.
|
||||||
|
* @howToUse `array_or_string_expression | slice:start[:end]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* The starting index of the subset to return is specified by the `start` parameter.
|
* Where the input expression is a `List` or `String`, and:
|
||||||
|
* - `start`: The starting index of the subset to return.
|
||||||
|
* - **a positive integer**: return the item at `start` index and all items after
|
||||||
|
* in the list or string expression.
|
||||||
|
* - **a negative integer**: return the item at `start` index from the end and all items after
|
||||||
|
* in the list or string expression.
|
||||||
|
* - **if positive and greater than the size of the expression**: return an empty list or string.
|
||||||
|
* - **if negative and greater than the size of the expression**: return entire list or string.
|
||||||
|
* - `end`: The ending index of the subset to return.
|
||||||
|
* - **omitted**: return all items until the end.
|
||||||
|
* - **if positive**: return all items before `end` index of the list or string.
|
||||||
|
* - **if negative**: return all items before `end` index from the end of the list or string.
|
||||||
*
|
*
|
||||||
* The ending index of the subset to return is specified by the optional `end` parameter.
|
* All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()`
|
||||||
*
|
* and `String.prototype.slice()`.
|
||||||
* ### Usage
|
|
||||||
*
|
|
||||||
* expression | slice:start[:end]
|
|
||||||
*
|
|
||||||
* All behavior is based on the expected behavior of the JavaScript API
|
|
||||||
* Array.prototype.slice() and String.prototype.slice()
|
|
||||||
*
|
|
||||||
* Where the input expression is a [List] or [String], and `start` is:
|
|
||||||
*
|
|
||||||
* - **a positive integer**: return the item at _start_ index and all items after
|
|
||||||
* in the list or string expression.
|
|
||||||
* - **a negative integer**: return the item at _start_ index from the end and all items after
|
|
||||||
* in the list or string expression.
|
|
||||||
* - **`|start|` greater than the size of the expression**: return an empty list or string.
|
|
||||||
* - **`|start|` negative greater than the size of the expression**: return entire list or
|
|
||||||
* string expression.
|
|
||||||
*
|
|
||||||
* and where `end` is:
|
|
||||||
*
|
|
||||||
* - **omitted**: return all items until the end of the input
|
|
||||||
* - **a positive integer**: return all items before _end_ index of the list or string
|
|
||||||
* expression.
|
|
||||||
* - **a negative integer**: return all items before _end_ index from the end of the list
|
|
||||||
* or string expression.
|
|
||||||
*
|
*
|
||||||
* When operating on a [List], the returned list is always a copy even when all
|
* When operating on a [List], the returned list is always a copy even when all
|
||||||
* the elements are being returned.
|
* the elements are being returned.
|
||||||
*
|
*
|
||||||
* When operating on a blank value, returns it.
|
* When operating on a blank value, the pipe returns the blank value.
|
||||||
*
|
*
|
||||||
* ## List Example
|
* ## List Example
|
||||||
*
|
*
|
||||||
* This `ngFor` example:
|
* This `ngFor` example:
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_list'}
|
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'}
|
||||||
*
|
*
|
||||||
* produces the following:
|
* produces the following:
|
||||||
*
|
*
|
||||||
@ -62,23 +50,22 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
*
|
*
|
||||||
* ## String Examples
|
* ## String Examples
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_string'}
|
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Pipe({name: 'slice', pure: false})
|
@Pipe({name: 'slice', pure: false})
|
||||||
export class SlicePipe implements PipeTransform {
|
export class SlicePipe implements PipeTransform {
|
||||||
transform(value: any, start: number, end: number = null): any {
|
transform(value: any, start: number, end?: number): any {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
|
|
||||||
if (!this.supports(value)) {
|
if (!this.supports(value)) {
|
||||||
throw new InvalidPipeArgumentError(SlicePipe, value);
|
throw new InvalidPipeArgumentError(SlicePipe, value);
|
||||||
}
|
}
|
||||||
if (isString(value)) {
|
|
||||||
return StringWrapper.slice(value, start, end);
|
return value.slice(start, end);
|
||||||
}
|
|
||||||
return ListWrapper.slice(value, start, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private supports(obj: any): boolean { return isString(obj) || isArray(obj); }
|
private supports(obj: any): boolean { return typeof obj === 'string' || Array.isArray(obj); }
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank, isString} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements uppercase transforms to text.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Transforms string to uppercase.
|
||||||
|
* @howToUse `expression | uppercase`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -23,7 +28,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
export class UpperCasePipe implements PipeTransform {
|
export class UpperCasePipe implements PipeTransform {
|
||||||
transform(value: string): string {
|
transform(value: string): string {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
if (!isString(value)) {
|
if (typeof value !== 'string') {
|
||||||
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
||||||
}
|
}
|
||||||
return value.toUpperCase();
|
return value.toUpperCase();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
export class AnimationOutput {
|
|
||||||
constructor(public name: string, public phase: string, public fullPropertyName: string) {}
|
import {__core_private__ as r} from '@angular/core';
|
||||||
}
|
|
||||||
|
export const isPromise: typeof r.isPromise = r.isPromise;
|
@ -6,20 +6,23 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NgClass, NgFor} from '@angular/common';
|
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {ListWrapper, StringMapWrapper} from '../../src/facade/collection';
|
|
||||||
|
|
||||||
function detectChangesAndCheck(fixture: ComponentFixture<any>, classes: string) {
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.children[0].nativeElement.className).toEqual(classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('binding to CSS class list', () => {
|
describe('binding to CSS class list', () => {
|
||||||
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
|
function detectChangesAndExpectClassName(classes: string): void {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.debugElement.children[0].nativeElement.className).toEqual(classes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComponent(): TestComponent { return fixture.debugElement.componentInstance; }
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [TestComponent],
|
declarations: [TestComponent],
|
||||||
@ -27,268 +30,221 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should clean up when the directive is destroyed', async(() => {
|
it('should clean up when the directive is destroyed', async(() => {
|
||||||
let template = '<div *ngFor="let item of items" [ngClass]="item"></div>';
|
fixture = createTestComponent('<div *ngFor="let item of items" [ngClass]="item"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [['0']];
|
getComponent().items = [['0']];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.debugElement.componentInstance.items = [['1']];
|
getComponent().items = [['1']];
|
||||||
|
detectChangesAndExpectClassName('1');
|
||||||
detectChangesAndCheck(fixture, '1');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('expressions evaluating to objects', () => {
|
describe('expressions evaluating to objects', () => {
|
||||||
|
|
||||||
it('should add classes specified in an object literal', async(() => {
|
it('should add classes specified in an object literal', async(() => {
|
||||||
let template = '<div [ngClass]="{foo: true, bar: false}"></div>';
|
fixture = createTestComponent('<div [ngClass]="{foo: true, bar: false}"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should add classes specified in an object literal without change in class names',
|
it('should add classes specified in an object literal without change in class names',
|
||||||
async(() => {
|
async(() => {
|
||||||
let template = `<div [ngClass]="{'foo-bar': true, 'fooBar': true}"></div>`;
|
fixture =
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
createTestComponent(`<div [ngClass]="{'foo-bar': true, 'fooBar': true}"></div>`);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo-bar fooBar');
|
detectChangesAndExpectClassName('foo-bar fooBar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove classes based on changes in object literal values', async(() => {
|
it('should add and remove classes based on changes in object literal values', async(() => {
|
||||||
let template = '<div [ngClass]="{foo: condition, bar: !condition}"></div>';
|
fixture =
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
createTestComponent('<div [ngClass]="{foo: condition, bar: !condition}"></div>');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.condition = false;
|
getComponent().condition = false;
|
||||||
detectChangesAndCheck(fixture, 'bar');
|
detectChangesAndExpectClassName('bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove classes based on changes to the expression object', async(() => {
|
it('should add and remove classes based on changes to the expression object', async(() => {
|
||||||
let template = '<div [ngClass]="objExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
let objExpr = getComponent().objExpr;
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
objExpr['bar'] = true;
|
||||||
detectChangesAndCheck(fixture, 'foo bar');
|
detectChangesAndExpectClassName('foo bar');
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'baz', true);
|
objExpr['baz'] = true;
|
||||||
detectChangesAndCheck(fixture, 'foo bar baz');
|
detectChangesAndExpectClassName('foo bar baz');
|
||||||
|
|
||||||
StringMapWrapper.delete(fixture.debugElement.componentInstance.objExpr, 'bar');
|
delete (objExpr['bar']);
|
||||||
detectChangesAndCheck(fixture, 'foo baz');
|
detectChangesAndExpectClassName('foo baz');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove classes based on reference changes to the expression object',
|
it('should add and remove classes based on reference changes to the expression object',
|
||||||
async(() => {
|
async(() => {
|
||||||
let template = '<div [ngClass]="objExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = {foo: true, bar: true};
|
getComponent().objExpr = {foo: true, bar: true};
|
||||||
detectChangesAndCheck(fixture, 'foo bar');
|
detectChangesAndExpectClassName('foo bar');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = {baz: true};
|
getComponent().objExpr = {baz: true};
|
||||||
detectChangesAndCheck(fixture, 'baz');
|
detectChangesAndExpectClassName('baz');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should remove active classes when expression evaluates to null', async(() => {
|
it('should remove active classes when expression evaluates to null', async(() => {
|
||||||
let template = '<div [ngClass]="objExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = null;
|
getComponent().objExpr = null;
|
||||||
detectChangesAndCheck(fixture, '');
|
detectChangesAndExpectClassName('');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = {'foo': false, 'bar': true};
|
getComponent().objExpr = {'foo': false, 'bar': true};
|
||||||
detectChangesAndCheck(fixture, 'bar');
|
detectChangesAndExpectClassName('bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should allow multiple classes per expression', async(() => {
|
it('should allow multiple classes per expression', async(() => {
|
||||||
let template = '<div [ngClass]="objExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = {'bar baz': true, 'bar1 baz1': true};
|
getComponent().objExpr = {'bar baz': true, 'bar1 baz1': true};
|
||||||
detectChangesAndCheck(fixture, 'bar baz bar1 baz1');
|
detectChangesAndExpectClassName('bar baz bar1 baz1');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = {'bar baz': false, 'bar1 baz1': true};
|
getComponent().objExpr = {'bar baz': false, 'bar1 baz1': true};
|
||||||
detectChangesAndCheck(fixture, 'bar1 baz1');
|
detectChangesAndExpectClassName('bar1 baz1');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should split by one or more spaces between classes', async(() => {
|
it('should split by one or more spaces between classes', async(() => {
|
||||||
let template = '<div [ngClass]="objExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = {'foo bar baz': true};
|
getComponent().objExpr = {'foo bar baz': true};
|
||||||
detectChangesAndCheck(fixture, 'foo bar baz');
|
detectChangesAndExpectClassName('foo bar baz');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expressions evaluating to lists', () => {
|
describe('expressions evaluating to lists', () => {
|
||||||
|
|
||||||
it('should add classes specified in a list literal', async(() => {
|
it('should add classes specified in a list literal', async(() => {
|
||||||
let template = `<div [ngClass]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`;
|
fixture =
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
createTestComponent(`<div [ngClass]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
detectChangesAndCheck(fixture, 'foo bar foo-bar fooBar');
|
detectChangesAndExpectClassName('foo bar foo-bar fooBar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove classes based on changes to the expression', async(() => {
|
it('should add and remove classes based on changes to the expression', async(() => {
|
||||||
let template = '<div [ngClass]="arrExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
let arrExpr = getComponent().arrExpr;
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
detectChangesAndExpectClassName('foo');
|
||||||
var arrExpr: string[] = fixture.debugElement.componentInstance.arrExpr;
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
arrExpr.push('bar');
|
arrExpr.push('bar');
|
||||||
detectChangesAndCheck(fixture, 'foo bar');
|
detectChangesAndExpectClassName('foo bar');
|
||||||
|
|
||||||
arrExpr[1] = 'baz';
|
arrExpr[1] = 'baz';
|
||||||
detectChangesAndCheck(fixture, 'foo baz');
|
detectChangesAndExpectClassName('foo baz');
|
||||||
|
|
||||||
ListWrapper.remove(fixture.debugElement.componentInstance.arrExpr, 'baz');
|
getComponent().arrExpr = arrExpr.filter((v: string) => v !== 'baz');
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
detectChangesAndExpectClassName('foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove classes when a reference changes', async(() => {
|
it('should add and remove classes when a reference changes', async(() => {
|
||||||
let template = '<div [ngClass]="arrExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
detectChangesAndExpectClassName('foo');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.arrExpr = ['bar'];
|
getComponent().arrExpr = ['bar'];
|
||||||
detectChangesAndCheck(fixture, 'bar');
|
detectChangesAndExpectClassName('bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should take initial classes into account when a reference changes', async(() => {
|
it('should take initial classes into account when a reference changes', async(() => {
|
||||||
let template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
fixture = createTestComponent('<div class="foo" [ngClass]="arrExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
detectChangesAndExpectClassName('foo');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.arrExpr = ['bar'];
|
getComponent().arrExpr = ['bar'];
|
||||||
detectChangesAndCheck(fixture, 'foo bar');
|
detectChangesAndExpectClassName('foo bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should ignore empty or blank class names', async(() => {
|
it('should ignore empty or blank class names', async(() => {
|
||||||
let template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
fixture = createTestComponent('<div class="foo" [ngClass]="arrExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
getComponent().arrExpr = ['', ' '];
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
detectChangesAndExpectClassName('foo');
|
||||||
fixture.debugElement.componentInstance.arrExpr = ['', ' '];
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should trim blanks from class names', async(() => {
|
it('should trim blanks from class names', async(() => {
|
||||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
fixture = createTestComponent('<div class="foo" [ngClass]="arrExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.arrExpr = [' bar '];
|
getComponent().arrExpr = [' bar '];
|
||||||
detectChangesAndCheck(fixture, 'foo bar');
|
detectChangesAndExpectClassName('foo bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should allow multiple classes per item in arrays', async(() => {
|
it('should allow multiple classes per item in arrays', async(() => {
|
||||||
var template = '<div [ngClass]="arrExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.arrExpr = ['foo bar baz', 'foo1 bar1 baz1'];
|
getComponent().arrExpr = ['foo bar baz', 'foo1 bar1 baz1'];
|
||||||
detectChangesAndCheck(fixture, 'foo bar baz foo1 bar1 baz1');
|
detectChangesAndExpectClassName('foo bar baz foo1 bar1 baz1');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.arrExpr = ['foo bar baz foobar'];
|
getComponent().arrExpr = ['foo bar baz foobar'];
|
||||||
detectChangesAndCheck(fixture, 'foo bar baz foobar');
|
detectChangesAndExpectClassName('foo bar baz foobar');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expressions evaluating to sets', () => {
|
describe('expressions evaluating to sets', () => {
|
||||||
|
|
||||||
it('should add and remove classes if the set instance changed', async(() => {
|
it('should add and remove classes if the set instance changed', async(() => {
|
||||||
var template = '<div [ngClass]="setExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="setExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
let setExpr = new Set<string>();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
var setExpr = new Set<string>();
|
|
||||||
setExpr.add('bar');
|
setExpr.add('bar');
|
||||||
fixture.debugElement.componentInstance.setExpr = setExpr;
|
getComponent().setExpr = setExpr;
|
||||||
detectChangesAndCheck(fixture, 'bar');
|
detectChangesAndExpectClassName('bar');
|
||||||
|
|
||||||
setExpr = new Set<string>();
|
setExpr = new Set<string>();
|
||||||
setExpr.add('baz');
|
setExpr.add('baz');
|
||||||
fixture.debugElement.componentInstance.setExpr = setExpr;
|
getComponent().setExpr = setExpr;
|
||||||
detectChangesAndCheck(fixture, 'baz');
|
detectChangesAndExpectClassName('baz');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expressions evaluating to string', () => {
|
describe('expressions evaluating to string', () => {
|
||||||
|
|
||||||
it('should add classes specified in a string literal', async(() => {
|
it('should add classes specified in a string literal', async(() => {
|
||||||
var template = `<div [ngClass]="'foo bar foo-bar fooBar'"></div>`;
|
fixture = createTestComponent(`<div [ngClass]="'foo bar foo-bar fooBar'"></div>`);
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
detectChangesAndExpectClassName('foo bar foo-bar fooBar');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
detectChangesAndCheck(fixture, 'foo bar foo-bar fooBar');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove classes based on changes to the expression', async(() => {
|
it('should add and remove classes based on changes to the expression', async(() => {
|
||||||
var template = '<div [ngClass]="strExpr"></div>';
|
fixture = createTestComponent('<div [ngClass]="strExpr"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
detectChangesAndExpectClassName('foo');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.strExpr = 'foo bar';
|
getComponent().strExpr = 'foo bar';
|
||||||
detectChangesAndCheck(fixture, 'foo bar');
|
detectChangesAndExpectClassName('foo bar');
|
||||||
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.strExpr = 'baz';
|
getComponent().strExpr = 'baz';
|
||||||
detectChangesAndCheck(fixture, 'baz');
|
detectChangesAndExpectClassName('baz');
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should remove active classes when switching from string to null', async(() => {
|
it('should remove active classes when switching from string to null', async(() => {
|
||||||
var template = `<div [ngClass]="strExpr"></div>`;
|
fixture = createTestComponent(`<div [ngClass]="strExpr"></div>`);
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
detectChangesAndExpectClassName('foo');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.strExpr = null;
|
|
||||||
detectChangesAndCheck(fixture, '');
|
|
||||||
|
|
||||||
|
getComponent().strExpr = null;
|
||||||
|
detectChangesAndExpectClassName('');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should take initial classes into account when switching from string to null',
|
it('should take initial classes into account when switching from string to null',
|
||||||
async(() => {
|
async(() => {
|
||||||
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
|
fixture = createTestComponent(`<div class="foo" [ngClass]="strExpr"></div>`);
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
detectChangesAndExpectClassName('foo');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.strExpr = null;
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
|
getComponent().strExpr = null;
|
||||||
|
detectChangesAndExpectClassName('foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should ignore empty and blank strings', async(() => {
|
it('should ignore empty and blank strings', async(() => {
|
||||||
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
|
fixture = createTestComponent(`<div class="foo" [ngClass]="strExpr"></div>`);
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
getComponent().strExpr = '';
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
detectChangesAndExpectClassName('foo');
|
||||||
fixture.debugElement.componentInstance.strExpr = '';
|
|
||||||
detectChangesAndCheck(fixture, 'foo');
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -296,83 +252,82 @@ export function main() {
|
|||||||
describe('cooperation with other class-changing constructs', () => {
|
describe('cooperation with other class-changing constructs', () => {
|
||||||
|
|
||||||
it('should co-operate with the class attribute', async(() => {
|
it('should co-operate with the class attribute', async(() => {
|
||||||
var template = '<div [ngClass]="objExpr" class="init foo"></div>';
|
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
let objExpr = getComponent().objExpr;
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
|
||||||
detectChangesAndCheck(fixture, 'init foo bar');
|
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
objExpr['bar'] = true;
|
||||||
detectChangesAndCheck(fixture, 'init bar');
|
detectChangesAndExpectClassName('init foo bar');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = null;
|
objExpr['foo'] = false;
|
||||||
detectChangesAndCheck(fixture, 'init foo');
|
detectChangesAndExpectClassName('init bar');
|
||||||
|
|
||||||
|
getComponent().objExpr = null;
|
||||||
|
detectChangesAndExpectClassName('init foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should co-operate with the interpolated class attribute', async(() => {
|
it('should co-operate with the interpolated class attribute', async(() => {
|
||||||
var template = `<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`;
|
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
let objExpr = getComponent().objExpr;
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
|
||||||
detectChangesAndCheck(fixture, `init foo bar`);
|
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
objExpr['bar'] = true;
|
||||||
detectChangesAndCheck(fixture, `init bar`);
|
detectChangesAndExpectClassName(`init foo bar`);
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = null;
|
objExpr['foo'] = false;
|
||||||
detectChangesAndCheck(fixture, `init foo`);
|
detectChangesAndExpectClassName(`init bar`);
|
||||||
|
|
||||||
|
getComponent().objExpr = null;
|
||||||
|
detectChangesAndExpectClassName(`init foo`);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should co-operate with the class attribute and binding to it', async(() => {
|
it('should co-operate with the class attribute and binding to it', async(() => {
|
||||||
var template = `<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`;
|
fixture =
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
let objExpr = getComponent().objExpr;
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
|
||||||
detectChangesAndCheck(fixture, `init foo bar`);
|
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
objExpr['bar'] = true;
|
||||||
detectChangesAndCheck(fixture, `init bar`);
|
detectChangesAndExpectClassName(`init foo bar`);
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = null;
|
objExpr['foo'] = false;
|
||||||
detectChangesAndCheck(fixture, `init foo`);
|
detectChangesAndExpectClassName(`init bar`);
|
||||||
|
|
||||||
|
getComponent().objExpr = null;
|
||||||
|
detectChangesAndExpectClassName(`init foo`);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should co-operate with the class attribute and class.name binding', async(() => {
|
it('should co-operate with the class attribute and class.name binding', async(() => {
|
||||||
var template =
|
const template =
|
||||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
let objExpr = getComponent().objExpr;
|
||||||
detectChangesAndCheck(fixture, 'init foo baz');
|
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
detectChangesAndExpectClassName('init foo baz');
|
||||||
detectChangesAndCheck(fixture, 'init foo baz bar');
|
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
objExpr['bar'] = true;
|
||||||
detectChangesAndCheck(fixture, 'init baz bar');
|
detectChangesAndExpectClassName('init foo baz bar');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.condition = false;
|
objExpr['foo'] = false;
|
||||||
detectChangesAndCheck(fixture, 'init bar');
|
detectChangesAndExpectClassName('init baz bar');
|
||||||
|
|
||||||
|
getComponent().condition = false;
|
||||||
|
detectChangesAndExpectClassName('init bar');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should co-operate with initial class and class attribute binding when binding changes',
|
it('should co-operate with initial class and class attribute binding when binding changes',
|
||||||
async(() => {
|
async(() => {
|
||||||
var template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
let cmp = getComponent();
|
||||||
detectChangesAndCheck(fixture, 'init foo');
|
|
||||||
|
|
||||||
StringMapWrapper.set(fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
detectChangesAndExpectClassName('init foo');
|
||||||
detectChangesAndCheck(fixture, 'init foo bar');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.strExpr = 'baz';
|
cmp.objExpr['bar'] = true;
|
||||||
detectChangesAndCheck(fixture, 'init bar baz foo');
|
detectChangesAndExpectClassName('init foo bar');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.objExpr = null;
|
cmp.strExpr = 'baz';
|
||||||
detectChangesAndCheck(fixture, 'init baz');
|
detectChangesAndExpectClassName('init bar baz foo');
|
||||||
|
|
||||||
|
cmp.objExpr = null;
|
||||||
|
detectChangesAndExpectClassName('init baz');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -384,8 +339,13 @@ class TestComponent {
|
|||||||
items: any[];
|
items: any[];
|
||||||
arrExpr: string[] = ['foo'];
|
arrExpr: string[] = ['foo'];
|
||||||
setExpr: Set<string> = new Set<string>();
|
setExpr: Set<string> = new Set<string>();
|
||||||
objExpr = {'foo': true, 'bar': false};
|
objExpr: {[klass: string]: any} = {'foo': true, 'bar': false};
|
||||||
strExpr = 'foo';
|
strExpr = 'foo';
|
||||||
|
|
||||||
constructor() { this.setExpr.add('foo'); }
|
constructor() { this.setExpr.add('foo'); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.createComponent(TestComponent);
|
||||||
|
}
|
@ -8,150 +8,135 @@
|
|||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ContentChild, TemplateRef} from '@angular/core';
|
import {Component, ContentChild, TemplateRef} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {ListWrapper} from '../../src/facade/collection';
|
|
||||||
|
|
||||||
let thisArg: any;
|
let thisArg: any;
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ngFor', () => {
|
describe('ngFor', () => {
|
||||||
const TEMPLATE =
|
let fixture: ComponentFixture<any>;
|
||||||
'<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
|
|
||||||
|
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||||
|
|
||||||
|
function detectChangesAndExpectText(text: string): void {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule({
|
||||||
{declarations: [TestComponent, ComponentUsingTestComponent], imports: [CommonModule]});
|
declarations: [
|
||||||
|
TestComponent,
|
||||||
|
ComponentUsingTestComponent,
|
||||||
|
],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reflect initial elements', async(() => {
|
it('should reflect initial elements', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('1;2;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect added elements', async(() => {
|
it('should reflect added elements', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
getComponent().items.push(3);
|
||||||
(<number[]>fixture.debugElement.componentInstance.items).push(3);
|
detectChangesAndExpectText('1;2;3;');
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;3;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect removed elements', async(() => {
|
it('should reflect removed elements', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
getComponent().items.splice(1, 1);
|
||||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 1);
|
detectChangesAndExpectText('1;');
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('1;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect moved elements', async(() => {
|
it('should reflect moved elements', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
getComponent().items.splice(0, 1);
|
||||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 0);
|
getComponent().items.push(1);
|
||||||
(<number[]>fixture.debugElement.componentInstance.items).push(1);
|
detectChangesAndExpectText('2;1;');
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('2;1;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect a mix of all changes (additions/removals/moves)', async(() => {
|
it('should reflect a mix of all changes (additions/removals/moves)', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.items = [0, 1, 2, 3, 4, 5];
|
getComponent().items = [0, 1, 2, 3, 4, 5];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [6, 2, 7, 0, 4, 8];
|
getComponent().items = [6, 2, 7, 0, 4, 8];
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('6;2;7;0;4;8;');
|
detectChangesAndExpectText('6;2;7;0;4;8;');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should iterate over an array of objects', async(() => {
|
it('should iterate over an array of objects', async(() => {
|
||||||
const template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
|
const template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
// INIT
|
// INIT
|
||||||
fixture.debugElement.componentInstance.items = [{'name': 'misko'}, {'name': 'shyam'}];
|
getComponent().items = [{'name': 'misko'}, {'name': 'shyam'}];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('misko;shyam;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('misko;shyam;');
|
|
||||||
|
|
||||||
// GROW
|
// GROW
|
||||||
(<any[]>fixture.debugElement.componentInstance.items).push({'name': 'adam'});
|
getComponent().items.push({'name': 'adam'});
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('misko;shyam;adam;');
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('misko;shyam;adam;');
|
|
||||||
|
|
||||||
// SHRINK
|
// SHRINK
|
||||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 2);
|
getComponent().items.splice(2, 1);
|
||||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 0);
|
getComponent().items.splice(0, 1);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('shyam;');
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('shyam;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should gracefully handle nulls', async(() => {
|
it('should gracefully handle nulls', async(() => {
|
||||||
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should gracefully handle ref changing to null and back', async(() => {
|
it('should gracefully handle ref changing to null and back', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = null;
|
detectChangesAndExpectText('1;2;');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [1, 2, 3];
|
getComponent().items = null;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;3;');
|
|
||||||
|
getComponent().items = [1, 2, 3];
|
||||||
|
detectChangesAndExpectText('1;2;3;');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw on non-iterable ref and suggest using an array', async(() => {
|
it('should throw on non-iterable ref and suggest using an array', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.items = 'whaaa';
|
getComponent().items = <any>'whaaa';
|
||||||
expect(() => fixture.detectChanges())
|
expect(() => fixture.detectChanges())
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
/Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays/);
|
/Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays/);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should throw on ref changing to string', async(() => {
|
it('should throw on ref changing to string', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = 'whaaa';
|
detectChangesAndExpectText('1;2;');
|
||||||
|
|
||||||
|
getComponent().items = <any>'whaaa';
|
||||||
expect(() => fixture.detectChanges()).toThrowError();
|
expect(() => fixture.detectChanges()).toThrowError();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should works with duplicates', async(() => {
|
it('should works with duplicates', async(() => {
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: TEMPLATE}});
|
fixture = createTestComponent();
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
var a = new Foo();
|
const a = new Foo();
|
||||||
fixture.debugElement.componentInstance.items = [a, a];
|
getComponent().items = [a, a];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('foo;foo;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('foo;foo;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should repeat over nested arrays', async(() => {
|
it('should repeat over nested arrays', async(() => {
|
||||||
@ -162,18 +147,13 @@ export function main() {
|
|||||||
'</div>|' +
|
'</div>|' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [['a', 'b'], ['c']];
|
getComponent().items = [['a', 'b'], ['c']];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('a-2;b-2;|c-1;|');
|
||||||
fixture.detectChanges();
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('a-2;b-2;|c-1;|');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [['e'], ['f', 'g']];
|
getComponent().items = [['e'], ['f', 'g']];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('e-1;|f-2;g-2;|');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('e-1;|f-2;g-2;|');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should repeat over nested arrays with no intermediate element', async(() => {
|
it('should repeat over nested arrays with no intermediate element', async(() => {
|
||||||
@ -181,16 +161,13 @@ export function main() {
|
|||||||
'<div template="ngFor let subitem of item">' +
|
'<div template="ngFor let subitem of item">' +
|
||||||
'{{subitem}}-{{item.length}};' +
|
'{{subitem}}-{{item.length}};' +
|
||||||
'</div></template></div>';
|
'</div></template></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [['a', 'b'], ['c']];
|
getComponent().items = [['a', 'b'], ['c']];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('a-2;b-2;c-1;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('a-2;b-2;c-1;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [['e'], ['f', 'g']];
|
getComponent().items = [['e'], ['f', 'g']];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('e-1;f-2;g-2;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('e-1;f-2;g-2;');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
|
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
|
||||||
@ -198,97 +175,77 @@ export function main() {
|
|||||||
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
|
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
|
||||||
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
|
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
const el = fixture.debugElement.nativeElement;
|
|
||||||
const items = [1];
|
const items = [1];
|
||||||
fixture.debugElement.componentInstance.items = items;
|
getComponent().items = items;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('0|even|');
|
||||||
expect(el).toHaveText('0|even|');
|
|
||||||
|
|
||||||
items.push(1);
|
items.push(1);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('0|even|1|');
|
||||||
expect(el).toHaveText('0|even|1|');
|
|
||||||
|
|
||||||
items.push(1);
|
items.push(1);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('0|even|1|2|even|');
|
||||||
expect(el).toHaveText('0|even|1|2|even|');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display indices correctly', async(() => {
|
it('should display indices correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
|
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('0123456789');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('0123456789');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0];
|
getComponent().items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('0123456789');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('0123456789');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display first item correctly', async(() => {
|
it('should display first item correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
|
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
getComponent().items = [0, 1, 2];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('truefalsefalse');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalsefalse');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [2, 1];
|
getComponent().items = [2, 1];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('truefalse');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display last item correctly', async(() => {
|
it('should display last item correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
|
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
getComponent().items = [0, 1, 2];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('falsefalsetrue');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('falsefalsetrue');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [2, 1];
|
getComponent().items = [2, 1];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('falsetrue');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('falsetrue');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display even items correctly', async(() => {
|
it('should display even items correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
|
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
getComponent().items = [0, 1, 2];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('truefalsetrue');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalsetrue');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [2, 1];
|
getComponent().items = [2, 1];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('truefalse');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display odd items correctly', async(() => {
|
it('should display odd items correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
|
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [0, 1, 2, 3];
|
getComponent().items = [0, 1, 2, 3];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('falsetruefalsetrue');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('falsetruefalsetrue');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [2, 1];
|
getComponent().items = [2, 1];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('falsetrue');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('falsetrue');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should allow to use a custom template', async(() => {
|
it('should allow to use a custom template', async(() => {
|
||||||
@ -298,7 +255,7 @@ export function main() {
|
|||||||
const cutTemplate =
|
const cutTemplate =
|
||||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
||||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
||||||
let fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
||||||
|
|
||||||
const testComponent = fixture.debugElement.children[0];
|
const testComponent = fixture.debugElement.children[0];
|
||||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||||
@ -313,7 +270,7 @@ export function main() {
|
|||||||
const cutTemplate =
|
const cutTemplate =
|
||||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
||||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
||||||
let fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
||||||
|
|
||||||
const testComponent = fixture.debugElement.children[0];
|
const testComponent = fixture.debugElement.children[0];
|
||||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||||
@ -328,7 +285,7 @@ export function main() {
|
|||||||
const cutTemplate =
|
const cutTemplate =
|
||||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
||||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
||||||
let fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
||||||
|
|
||||||
const testComponent = fixture.debugElement.children[0];
|
const testComponent = fixture.debugElement.children[0];
|
||||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||||
@ -340,12 +297,11 @@ export function main() {
|
|||||||
it('should set the context to the component instance', async(() => {
|
it('should set the context to the component instance', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
|
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
thisArg = null;
|
thisArg = null;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(thisArg).toBe(fixture.debugElement.componentInstance);
|
expect(thisArg).toBe(getComponent());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not replace tracked items', async(() => {
|
it('should not replace tracked items', async(() => {
|
||||||
@ -353,62 +309,53 @@ export function main() {
|
|||||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
|
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
|
||||||
<p>{{items[i]}}</p>
|
<p>{{items[i]}}</p>
|
||||||
</template>`;
|
</template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
var buildItemList = () => {
|
const buildItemList = () => {
|
||||||
fixture.debugElement.componentInstance.items = [{'id': 'a'}];
|
getComponent().items = [{'id': 'a'}];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
return fixture.debugElement.queryAll(By.css('p'))[0];
|
return fixture.debugElement.queryAll(By.css('p'))[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
var firstP = buildItemList();
|
const firstP = buildItemList();
|
||||||
var finalP = buildItemList();
|
const finalP = buildItemList();
|
||||||
expect(finalP.nativeElement).toBe(firstP.nativeElement);
|
expect(finalP.nativeElement).toBe(firstP.nativeElement);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should update implicit local variable on view', async(() => {
|
it('should update implicit local variable on view', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'blue'}];
|
getComponent().items = [{'id': 'a', 'color': 'blue'}];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('blue');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('blue');
|
|
||||||
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'red'}];
|
getComponent().items = [{'id': 'a', 'color': 'red'}];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('red');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('red');
|
|
||||||
}));
|
}));
|
||||||
it('should move items around and keep them updated ', async(() => {
|
it('should move items around and keep them updated ', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items =
|
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
||||||
[{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
detectChangesAndExpectText('blueyellow');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('blueyellow');
|
getComponent().items = [{'id': 'b', 'color': 'orange'}, {'id': 'a', 'color': 'red'}];
|
||||||
fixture.debugElement.componentInstance.items =
|
detectChangesAndExpectText('orangered');
|
||||||
[{'id': 'b', 'color': 'orange'}, {'id': 'a', 'color': 'red'}];
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('orangered');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should handle added and removed items properly when tracking by index', async(() => {
|
it('should handle added and removed items properly when tracking by index', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.items = ['a', 'b', 'c', 'd'];
|
getComponent().items = ['a', 'b', 'c', 'd'];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.debugElement.componentInstance.items = ['e', 'f', 'g', 'h'];
|
getComponent().items = ['e', 'f', 'g', 'h'];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.debugElement.componentInstance.items = ['e', 'f', 'h'];
|
getComponent().items = ['e', 'f', 'h'];
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('efh');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('efh');
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -421,8 +368,7 @@ class Foo {
|
|||||||
@Component({selector: 'test-cmp', template: ''})
|
@Component({selector: 'test-cmp', template: ''})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
||||||
items: any;
|
items: any[] = [1, 2];
|
||||||
constructor() { this.items = [1, 2]; }
|
|
||||||
trackById(index: number, item: any): string { return item['id']; }
|
trackById(index: number, item: any): string { return item['id']; }
|
||||||
trackByIndex(index: number, item: any): number { return index; }
|
trackByIndex(index: number, item: any): number { return index; }
|
||||||
trackByContext(): void { thisArg = this; }
|
trackByContext(): void { thisArg = this; }
|
||||||
@ -430,6 +376,12 @@ class TestComponent {
|
|||||||
|
|
||||||
@Component({selector: 'outer-cmp', template: ''})
|
@Component({selector: 'outer-cmp', template: ''})
|
||||||
class ComponentUsingTestComponent {
|
class ComponentUsingTestComponent {
|
||||||
items: any;
|
items: any = [1, 2];
|
||||||
constructor() { this.items = [1, 2]; }
|
}
|
||||||
|
|
||||||
|
const TEMPLATE = '<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
|
||||||
|
|
||||||
|
function createTestComponent(template: string = TEMPLATE): ComponentFixture<TestComponent> {
|
||||||
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.createComponent(TestComponent);
|
||||||
}
|
}
|
||||||
|
@ -8,99 +8,94 @@
|
|||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ngIf directive', () => {
|
describe('ngIf directive', () => {
|
||||||
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
|
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({declarations: [TestComponent], imports: [CommonModule]});
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [TestComponent],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work in a template attribute', async(() => {
|
it('should work in a template attribute', async(() => {
|
||||||
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should work in a template element', async(() => {
|
it('should work in a template element', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
|
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('hello2');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello2');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should toggle node when condition changes', async(() => {
|
it('should toggle node when condition changes', async(() => {
|
||||||
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
getComponent().booleanCondition = false;
|
||||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||||
.toEqual(0);
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.booleanCondition = true;
|
getComponent().booleanCondition = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||||
.toEqual(0);
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should handle nested if correctly', async(() => {
|
it('should handle nested if correctly', async(() => {
|
||||||
const template =
|
const template =
|
||||||
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
|
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
|
||||||
.toEqual(0);
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.booleanCondition = true;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.nestedBooleanCondition = false;
|
getComponent().booleanCondition = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(0);
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.nestedBooleanCondition = true;
|
getComponent().nestedBooleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
getComponent().nestedBooleanCondition = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(0);
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
getComponent().booleanCondition = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||||
|
expect(fixture.nativeElement).toHaveText('');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should update several nodes with if', async(() => {
|
it('should update several nodes with if', async(() => {
|
||||||
@ -110,59 +105,52 @@ export function main() {
|
|||||||
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
|
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(3);
|
||||||
.toEqual(3);
|
expect(getDOM().getText(fixture.nativeElement))
|
||||||
expect(getDOM().getText(fixture.debugElement.nativeElement))
|
|
||||||
.toEqual('helloNumberhelloStringhelloFunction');
|
.toEqual('helloNumberhelloStringhelloFunction');
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.numberCondition = 0;
|
getComponent().numberCondition = 0;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('helloString');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('helloString');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.numberCondition = 1;
|
getComponent().numberCondition = 1;
|
||||||
fixture.debugElement.componentInstance.stringCondition = 'bar';
|
getComponent().stringCondition = 'bar';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('helloNumber');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('helloNumber');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not add the element twice if the condition goes from true to true (JS)',
|
it('should not add the element twice if the condition goes from true to true (JS)',
|
||||||
async(() => {
|
async(() => {
|
||||||
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
|
||||||
.toEqual(1);
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().querySelectorAll(fixture.debugElement.nativeElement, 'span').length)
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
.toEqual(1);
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
|
||||||
|
getComponent().numberCondition = 2;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||||
|
expect(fixture.nativeElement).toHaveText('hello');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not recreate the element if the condition goes from true to true (JS)', async(() => {
|
it('should not recreate the element if the condition goes from true to true (JS)', async(() => {
|
||||||
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
getDOM().addClass(
|
|
||||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'span'), 'foo');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(getDOM().hasClass(
|
getDOM().addClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo');
|
||||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'span'), 'foo'))
|
|
||||||
|
getComponent().numberCondition = 2;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getDOM().hasClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo'))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@ -170,16 +158,14 @@ export function main() {
|
|||||||
|
|
||||||
@Component({selector: 'test-cmp', template: ''})
|
@Component({selector: 'test-cmp', template: ''})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
booleanCondition: boolean;
|
booleanCondition: boolean = true;
|
||||||
nestedBooleanCondition: boolean;
|
nestedBooleanCondition: boolean = true;
|
||||||
numberCondition: number;
|
numberCondition: number = 1;
|
||||||
stringCondition: string;
|
stringCondition: string = 'foo';
|
||||||
functionCondition: Function;
|
functionCondition: Function = (s: any, n: any): boolean => s == 'foo' && n == 1;
|
||||||
constructor() {
|
}
|
||||||
this.booleanCondition = true;
|
|
||||||
this.nestedBooleanCondition = true;
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
this.numberCondition = 1;
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
this.stringCondition = 'foo';
|
.createComponent(TestComponent);
|
||||||
this.functionCondition = function(s: any, n: any): boolean { return s == 'foo' && n == 1; };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,21 @@
|
|||||||
|
|
||||||
import {CommonModule, NgLocalization} from '@angular/common';
|
import {CommonModule, NgLocalization} from '@angular/common';
|
||||||
import {Component, Injectable} from '@angular/core';
|
import {Component, Injectable} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('switch', () => {
|
describe('switch', () => {
|
||||||
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
|
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||||
|
|
||||||
|
function detectChangesAndExpectText<T>(text: string): void {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@ -23,104 +33,95 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should display the template according to the exact value', async(() => {
|
it('should display the template according to the exact value', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
'<ul [ngPlural]="switchValue">' +
|
||||||
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
||||||
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 0;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 1;
|
getComponent().switchValue = 0;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('you have no messages.');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
|
|
||||||
|
getComponent().switchValue = 1;
|
||||||
|
detectChangesAndExpectText('you have one message.');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// https://github.com/angular/angular/issues/9868
|
// https://github.com/angular/angular/issues/9868
|
||||||
// https://github.com/angular/angular/issues/9882
|
// https://github.com/angular/angular/issues/9882
|
||||||
it('should not throw when ngPluralCase contains expressions', async(() => {
|
it('should not throw when ngPluralCase contains expressions', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
'<ul [ngPlural]="switchValue">' +
|
||||||
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 0;
|
getComponent().switchValue = 0;
|
||||||
expect(() => fixture.detectChanges()).not.toThrow();
|
expect(() => fixture.detectChanges()).not.toThrow();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should be applicable to <ng-container> elements', async(() => {
|
it('should be applicable to <ng-container> elements', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ng-container [ngPlural]="switchValue">' +
|
'<ng-container [ngPlural]="switchValue">' +
|
||||||
'<template ngPluralCase="=0">you have no messages.</template>' +
|
'<template ngPluralCase="=0">you have no messages.</template>' +
|
||||||
'<template ngPluralCase="=1">you have one message.</template>' +
|
'<template ngPluralCase="=1">you have one message.</template>' +
|
||||||
'</ng-container></div>';
|
'</ng-container></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 0;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 1;
|
getComponent().switchValue = 0;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('you have no messages.');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
|
|
||||||
|
getComponent().switchValue = 1;
|
||||||
|
detectChangesAndExpectText('you have one message.');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display the template according to the category', async(() => {
|
it('should display the template according to the category', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
'<ul [ngPlural]="switchValue">' +
|
||||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||||
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 2;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have a few messages.');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 8;
|
getComponent().switchValue = 2;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('you have a few messages.');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have many messages.');
|
|
||||||
|
getComponent().switchValue = 8;
|
||||||
|
detectChangesAndExpectText('you have many messages.');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should default to other when no matches are found', async(() => {
|
it('should default to other when no matches are found', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
'<ul [ngPlural]="switchValue">' +
|
||||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||||
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 100;
|
getComponent().switchValue = 100;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('default message.');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('default message.');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should prioritize value matches over category matches', async(() => {
|
it('should prioritize value matches over category matches', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngPlural]="switchValue">' +
|
'<ul [ngPlural]="switchValue">' +
|
||||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||||
'<template ngPluralCase="=2">you have two messages.</template>' +
|
'<template ngPluralCase="=2">you have two messages.</template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 2;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have two messages.');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 3;
|
getComponent().switchValue = 2;
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('you have two messages.');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('you have a few messages.');
|
|
||||||
|
getComponent().switchValue = 3;
|
||||||
|
detectChangesAndExpectText('you have a few messages.');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -131,6 +132,7 @@ class TestLocalization extends NgLocalization {
|
|||||||
if (value > 1 && value < 4) {
|
if (value > 1 && value < 4) {
|
||||||
return 'few';
|
return 'few';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value >= 4 && value < 10) {
|
if (value >= 4 && value < 10) {
|
||||||
return 'many';
|
return 'many';
|
||||||
}
|
}
|
||||||
@ -143,3 +145,8 @@ class TestLocalization extends NgLocalization {
|
|||||||
class TestComponent {
|
class TestComponent {
|
||||||
switchValue: number = null;
|
switchValue: number = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.createComponent(TestComponent);
|
||||||
|
}
|
||||||
|
@ -10,69 +10,70 @@ import {CommonModule} from '@angular/common';
|
|||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
|
|
||||||
function expectNativeEl(fixture: ComponentFixture<any>) {
|
|
||||||
return <any>expect(fixture.debugElement.children[0].nativeElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('binding to CSS styles', () => {
|
describe('NgStyle', () => {
|
||||||
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
|
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||||
|
|
||||||
|
function expectNativeEl(fixture: ComponentFixture<any>): any {
|
||||||
|
return expect(fixture.debugElement.children[0].nativeElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({declarations: [TestComponent], imports: [CommonModule]});
|
TestBed.configureTestingModule({declarations: [TestComponent], imports: [CommonModule]});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add styles specified in an object literal', async(() => {
|
it('should add styles specified in an object literal', async(() => {
|
||||||
var template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and change styles specified in an object expression', async(() => {
|
it('should add and change styles specified in an object expression', async(() => {
|
||||||
var template = `<div [ngStyle]="expr"></div>`;
|
const template = `<div [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
let expr: {[k: string]: string};
|
||||||
var expr: Map<string, any>;
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
getComponent().expr = {'max-width': '40px'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
|
|
||||||
expr = fixture.debugElement.componentInstance.expr;
|
expr = getComponent().expr;
|
||||||
(expr as any)['max-width'] = '30%';
|
expr['max-width'] = '30%';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should add and remove styles specified using style.unit notation', async(() => {
|
it('should add and remove styles specified using style.unit notation', async(() => {
|
||||||
var template = `<div [ngStyle]="{'max-width.px': expr}"></div>`;
|
const template = `<div [ngStyle]="{'max-width.px': expr}"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.expr = '40';
|
getComponent().expr = '40';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.expr = null;
|
getComponent().expr = null;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should update styles using style.unit notation when unit changes', async(() => {
|
it('should update styles using style.unit notation when unit changes', async(() => {
|
||||||
var template = `<div [ngStyle]="expr"></div>`;
|
const template = `<div [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.expr = {'max-width.px': '40'};
|
getComponent().expr = {'max-width.px': '40'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.expr = {'max-width.em': '40'};
|
getComponent().expr = {'max-width.em': '40'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40em'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40em'});
|
||||||
}));
|
}));
|
||||||
@ -81,9 +82,9 @@ export function main() {
|
|||||||
it('should change styles specified in an object expression', async(() => {
|
it('should change styles specified in an object expression', async(() => {
|
||||||
const template = `<div [ngStyle]="expr"></div>`;
|
const template = `<div [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.expr = {
|
getComponent().expr = {
|
||||||
// height, width order is important here
|
// height, width order is important here
|
||||||
height: '10px',
|
height: '10px',
|
||||||
width: '10px'
|
width: '10px'
|
||||||
@ -92,7 +93,7 @@ export function main() {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'height': '10px', 'width': '10px'});
|
expectNativeEl(fixture).toHaveCssStyle({'height': '10px', 'width': '10px'});
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.expr = {
|
getComponent().expr = {
|
||||||
// width, height order is important here
|
// width, height order is important here
|
||||||
width: '5px',
|
width: '5px',
|
||||||
height: '5px',
|
height: '5px',
|
||||||
@ -103,29 +104,29 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should remove styles when deleting a key in an object expression', async(() => {
|
it('should remove styles when deleting a key in an object expression', async(() => {
|
||||||
var template = `<div [ngStyle]="expr"></div>`;
|
const template = `<div [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
getComponent().expr = {'max-width': '40px'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||||
|
|
||||||
delete fixture.debugElement.componentInstance.expr['max-width'];
|
delete getComponent().expr['max-width'];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should co-operate with the style attribute', async(() => {
|
it('should co-operate with the style attribute', async(() => {
|
||||||
var template = `<div style="font-size: 12px" [ngStyle]="expr"></div>`;
|
const template = `<div style="font-size: 12px" [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
getComponent().expr = {'max-width': '40px'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px', 'font-size': '12px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px', 'font-size': '12px'});
|
||||||
|
|
||||||
delete fixture.debugElement.componentInstance.expr['max-width'];
|
delete getComponent().expr['max-width'];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
|
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
|
||||||
@ -133,15 +134,15 @@ export function main() {
|
|||||||
|
|
||||||
it('should co-operate with the style.[styleName]="expr" special-case in the compiler',
|
it('should co-operate with the style.[styleName]="expr" special-case in the compiler',
|
||||||
async(() => {
|
async(() => {
|
||||||
var template = `<div [style.font-size.px]="12" [ngStyle]="expr"></div>`;
|
const template = `<div [style.font-size.px]="12" [ngStyle]="expr"></div>`;
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
getComponent().expr = {'max-width': '40px'};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px', 'font-size': '12px'});
|
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px', 'font-size': '12px'});
|
||||||
|
|
||||||
delete fixture.debugElement.componentInstance.expr['max-width'];
|
delete getComponent().expr['max-width'];
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||||
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
|
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
|
||||||
@ -153,3 +154,8 @@ export function main() {
|
|||||||
class TestComponent {
|
class TestComponent {
|
||||||
expr: any;
|
expr: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.createComponent(TestComponent);
|
||||||
|
}
|
||||||
|
@ -8,83 +8,86 @@
|
|||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('switch', () => {
|
describe('NgSwitch', () => {
|
||||||
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
|
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||||
|
|
||||||
|
function detectChangesAndExpectText(text: string): void {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({declarations: [TestComponent], imports: [CommonModule]});
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [TestComponent],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('switch value changes', () => {
|
describe('switch value changes', () => {
|
||||||
it('should switch amongst when values', async(() => {
|
it('should switch amongst when values', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
detectChangesAndExpectText('');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when a');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
getComponent().switchValue = 'a';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when a');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when b');
|
|
||||||
|
getComponent().switchValue = 'b';
|
||||||
|
detectChangesAndExpectText('when b');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// TODO(robwormald): deprecate and remove
|
// TODO(robwormald): deprecate and remove
|
||||||
it('should switch amongst when values using switchCase', async(() => {
|
it('should switch amongst when values using switchCase', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
detectChangesAndExpectText('');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when a');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
getComponent().switchValue = 'a';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when a');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when b');
|
|
||||||
|
getComponent().switchValue = 'b';
|
||||||
|
detectChangesAndExpectText('when b');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should switch amongst when values with fallback to default', async(() => {
|
it('should switch amongst when values with fallback to default', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||||
'<li template="ngSwitchDefault">when default</li>' +
|
'<li template="ngSwitchDefault">when default</li>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
detectChangesAndExpectText('when default');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when default');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when a');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when a');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when default');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when default');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should support multiple whens with the same value', async(() => {
|
it('should support multiple whens with the same value', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||||
@ -94,53 +97,43 @@ export function main() {
|
|||||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
detectChangesAndExpectText('when default1;when default2;');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when default1;when default2;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
getComponent().switchValue = 'a';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when a1;when a2;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when a1;when a2;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when b1;when b2;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when b1;when b2;');
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when values changes', () => {
|
describe('when values changes', () => {
|
||||||
it('should switch amongst when values', async(() => {
|
it('should switch amongst when values', async(() => {
|
||||||
var template = '<div>' +
|
const template = '<div>' +
|
||||||
'<ul [ngSwitch]="switchValue">' +
|
'<ul [ngSwitch]="switchValue">' +
|
||||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||||
'</ul></div>';
|
'</ul></div>';
|
||||||
|
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
getComponent().when1 = 'a';
|
||||||
fixture.debugElement.componentInstance.when1 = 'a';
|
getComponent().when2 = 'b';
|
||||||
fixture.debugElement.componentInstance.when2 = 'b';
|
getComponent().switchValue = 'a';
|
||||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
detectChangesAndExpectText('when 1;');
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when 1;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
getComponent().switchValue = 'b';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when 2;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when 2;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.switchValue = 'c';
|
getComponent().switchValue = 'c';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when default;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when default;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.when1 = 'c';
|
getComponent().when1 = 'c';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when 1;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when 1;');
|
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.when1 = 'd';
|
getComponent().when1 = 'd';
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('when default;');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('when default;');
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -148,13 +141,12 @@ export function main() {
|
|||||||
|
|
||||||
@Component({selector: 'test-cmp', template: ''})
|
@Component({selector: 'test-cmp', template: ''})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
switchValue: any;
|
switchValue: any = null;
|
||||||
when1: any;
|
when1: any = null;
|
||||||
when2: any;
|
when2: any = null;
|
||||||
|
}
|
||||||
constructor() {
|
|
||||||
this.switchValue = null;
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
this.when1 = null;
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
this.when2 = null;
|
.createComponent(TestComponent);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,155 +8,140 @@
|
|||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ContentChildren, Directive, NO_ERRORS_SCHEMA, QueryList, TemplateRef} from '@angular/core';
|
import {Component, ContentChildren, Directive, NO_ERRORS_SCHEMA, QueryList, TemplateRef} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('insert', () => {
|
describe('NgTemplateOutlet', () => {
|
||||||
|
let fixture: ComponentFixture<any>;
|
||||||
|
|
||||||
|
function setTplRef(value: any): void { fixture.componentInstance.currentTplRef = value; }
|
||||||
|
|
||||||
|
function detectChangesAndExpectText(text: string): void {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.debugElement.nativeElement).toHaveText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => { fixture = null; });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule({
|
||||||
{declarations: [TestComponent, CaptureTplRefs], imports: [CommonModule]});
|
declarations: [
|
||||||
|
TestComponent,
|
||||||
|
CaptureTplRefs,
|
||||||
|
],
|
||||||
|
imports: [CommonModule],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing if templateRef is null', async(() => {
|
it('should do nothing if templateRef is null', async(() => {
|
||||||
const template = `<template [ngTemplateOutlet]="null"></template>`;
|
const template = `<template [ngTemplateOutlet]="null"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('');
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should insert content specified by TemplateRef', async(() => {
|
it('should insert content specified by TemplateRef', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
|
||||||
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
detectChangesAndExpectText('');
|
||||||
|
|
||||||
fixture.detectChanges();
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
setTplRef(refs.tplRefs.first);
|
||||||
|
detectChangesAndExpectText('foo');
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('foo');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should clear content if TemplateRef becomes null', async(() => {
|
it('should clear content if TemplateRef becomes null', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
|
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
setTplRef(refs.tplRefs.first);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('foo');
|
||||||
expect(fixture.nativeElement).toHaveText('foo');
|
|
||||||
|
|
||||||
fixture.componentInstance.currentTplRef = null;
|
setTplRef(null);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('');
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should swap content if TemplateRef changes', async(() => {
|
it('should swap content if TemplateRef changes', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
|
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
setTplRef(refs.tplRefs.first);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('foo');
|
||||||
expect(fixture.nativeElement).toHaveText('foo');
|
|
||||||
|
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.last;
|
setTplRef(refs.tplRefs.last);
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('bar');
|
||||||
expect(fixture.nativeElement).toHaveText('bar');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should display template if context is null', async(() => {
|
it('should display template if context is null', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
|
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
detectChangesAndExpectText('');
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
expect(fixture.nativeElement).toHaveText('');
|
|
||||||
|
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
setTplRef(refs.tplRefs.first);
|
||||||
|
detectChangesAndExpectText('foo');
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.nativeElement).toHaveText('foo');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect initial context and changes', async(() => {
|
it('should reflect initial context and changes', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('bar');
|
|
||||||
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
|
setTplRef(refs.tplRefs.first);
|
||||||
|
|
||||||
|
detectChangesAndExpectText('bar');
|
||||||
|
|
||||||
fixture.componentInstance.context.foo = 'alter-bar';
|
fixture.componentInstance.context.foo = 'alter-bar';
|
||||||
|
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('alter-bar');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('alter-bar');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect user defined $implicit property in the context', async(() => {
|
it('should reflect user defined $implicit property in the context', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
setTplRef(refs.tplRefs.first);
|
||||||
|
|
||||||
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
|
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('bar');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('bar');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reflect context re-binding', async(() => {
|
it('should reflect context re-binding', async(() => {
|
||||||
const template =
|
const template =
|
||||||
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
fixture = createTestComponent(template);
|
||||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
|
|
||||||
|
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
var refs = fixture.debugElement.children[0].references['refs'];
|
const refs = fixture.debugElement.children[0].references['refs'];
|
||||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
setTplRef(refs.tplRefs.first);
|
||||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||||
|
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('brooks');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('brooks');
|
|
||||||
|
|
||||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||||
|
|
||||||
fixture.detectChanges();
|
detectChangesAndExpectText('was here');
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('was here');
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -172,3 +157,9 @@ class TestComponent {
|
|||||||
currentTplRef: TemplateRef<any>;
|
currentTplRef: TemplateRef<any>;
|
||||||
context: any = {foo: 'bar'};
|
context: any = {foo: 'bar'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
||||||
|
.createComponent(TestComponent);
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Component, Directive} from '@angular/core';
|
import {Component, Directive} from '@angular/core';
|
||||||
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
@ -22,31 +22,29 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not interpolate children', async(() => {
|
it('should not interpolate children', async(() => {
|
||||||
var template = '<div>{{text}}<span ngNonBindable>{{text}}</span></div>';
|
const template = '<div>{{text}}<span ngNonBindable>{{text}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
const fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('foo{{text}}');
|
expect(fixture.nativeElement).toHaveText('foo{{text}}');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should ignore directives on child nodes', async(() => {
|
it('should ignore directives on child nodes', async(() => {
|
||||||
var template = '<div ngNonBindable><span id=child test-dec>{{text}}</span></div>';
|
const template = '<div ngNonBindable><span id=child test-dec>{{text}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
const fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
// We must use getDOM().querySelector instead of fixture.query here
|
// We must use getDOM().querySelector instead of fixture.query here
|
||||||
// since the elements inside are not compiled.
|
// since the elements inside are not compiled.
|
||||||
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
|
const span = getDOM().querySelector(fixture.nativeElement, '#child');
|
||||||
expect(getDOM().hasClass(span, 'compiled')).toBeFalsy();
|
expect(getDOM().hasClass(span, 'compiled')).toBeFalsy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should trigger directives on the same node', async(() => {
|
it('should trigger directives on the same node', async(() => {
|
||||||
var template = '<div><span id=child ngNonBindable test-dec>{{text}}</span></div>';
|
const template = '<div><span id=child ngNonBindable test-dec>{{text}}</span></div>';
|
||||||
TestBed.overrideComponent(TestComponent, {set: {template: template}});
|
const fixture = createTestComponent(template);
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
|
const span = getDOM().querySelector(fixture.nativeElement, '#child');
|
||||||
expect(getDOM().hasClass(span, 'compiled')).toBeTruthy();
|
expect(getDOM().hasClass(span, 'compiled')).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@ -62,3 +60,8 @@ class TestComponent {
|
|||||||
text: string;
|
text: string;
|
||||||
constructor() { this.text = 'foo'; }
|
constructor() { this.text = 'foo'; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||||
|
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||||
|
.createComponent(TestComponent);
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {LOCALE_ID} from '@angular/core';
|
import {LOCALE_ID} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
||||||
@ -57,7 +57,7 @@ export function main() {
|
|||||||
|
|
||||||
describe('NgLocaleLocalization', () => {
|
describe('NgLocaleLocalization', () => {
|
||||||
it('should return the correct values for the "en" locale', () => {
|
it('should return the correct values for the "en" locale', () => {
|
||||||
const l10n = new NgLocaleLocalization('en_US');
|
const l10n = new NgLocaleLocalization('en-US');
|
||||||
|
|
||||||
expect(l10n.getPluralCategory(0)).toEqual('other');
|
expect(l10n.getPluralCategory(0)).toEqual('other');
|
||||||
expect(l10n.getPluralCategory(1)).toEqual('one');
|
expect(l10n.getPluralCategory(1)).toEqual('one');
|
||||||
@ -126,7 +126,7 @@ export function main() {
|
|||||||
|
|
||||||
describe('getPluralCategory', () => {
|
describe('getPluralCategory', () => {
|
||||||
it('should return plural category', () => {
|
it('should return plural category', () => {
|
||||||
const l10n = new FrLocalization();
|
const l10n = new NgLocaleLocalization('fr');
|
||||||
|
|
||||||
expect(getPluralCategory(0, ['one', 'other'], l10n)).toEqual('one');
|
expect(getPluralCategory(0, ['one', 'other'], l10n)).toEqual('one');
|
||||||
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
|
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
|
||||||
@ -134,7 +134,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return discrete cases', () => {
|
it('should return discrete cases', () => {
|
||||||
const l10n = new FrLocalization();
|
const l10n = new NgLocaleLocalization('fr');
|
||||||
|
|
||||||
expect(getPluralCategory(0, ['one', 'other', '=0'], l10n)).toEqual('=0');
|
expect(getPluralCategory(0, ['one', 'other', '=0'], l10n)).toEqual('=0');
|
||||||
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
|
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
|
||||||
@ -144,15 +144,3 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class FrLocalization extends NgLocalization {
|
|
||||||
getPluralCategory(value: number): string {
|
|
||||||
switch (value) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
return 'one';
|
|
||||||
default:
|
|
||||||
return 'other';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,12 +8,11 @@
|
|||||||
|
|
||||||
import {AsyncPipe} from '@angular/common';
|
import {AsyncPipe} from '@angular/common';
|
||||||
import {WrappedValue} from '@angular/core';
|
import {WrappedValue} from '@angular/core';
|
||||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
import {EventEmitter} from '../../src/facade/async';
|
import {EventEmitter} from '../../src/facade/async';
|
||||||
import {isBlank} from '../../src/facade/lang';
|
|
||||||
import {SpyChangeDetectorRef} from '../spies';
|
import {SpyChangeDetectorRef} from '../spies';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -112,7 +111,7 @@ export function main() {
|
|||||||
var promise: Promise<any>;
|
var promise: Promise<any>;
|
||||||
var ref: SpyChangeDetectorRef;
|
var ref: SpyChangeDetectorRef;
|
||||||
// adds longer timers for passing tests in IE
|
// adds longer timers for passing tests in IE
|
||||||
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10;
|
var timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
promise = new Promise((res, rej) => {
|
promise = new Promise((res, rej) => {
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
|
|
||||||
import {DatePipe} from '@angular/common';
|
import {DatePipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
import {DateWrapper} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DatePipe', () => {
|
describe('DatePipe', () => {
|
||||||
var date: Date;
|
var date: Date;
|
||||||
@ -27,7 +25,7 @@ export function main() {
|
|||||||
// Tracking issue: https://github.com/angular/angular/issues/11187
|
// Tracking issue: https://github.com/angular/angular/issues/11187
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
date = DateWrapper.create(2015, 6, 15, 9, 3, 1);
|
date = new Date(2015, 5, 15, 9, 3, 1);
|
||||||
pipe = new DatePipe('en-US');
|
pipe = new DatePipe('en-US');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {I18nPluralPipe, NgLocalization} from '@angular/common';
|
import {I18nPluralPipe, NgLocalization} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('I18nPluralPipe', () => {
|
describe('I18nPluralPipe', () => {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {I18nSelectPipe} from '@angular/common';
|
import {I18nSelectPipe} from '@angular/common';
|
||||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('I18nSelectPipe', () => {
|
describe('I18nSelectPipe', () => {
|
||||||
|
@ -67,13 +67,13 @@ export function main() {
|
|||||||
it('should work with mutable objects', async(() => {
|
it('should work with mutable objects', async(() => {
|
||||||
let fixture = TestBed.createComponent(TestComp);
|
let fixture = TestBed.createComponent(TestComp);
|
||||||
let mutable: number[] = [1];
|
let mutable: number[] = [1];
|
||||||
fixture.debugElement.componentInstance.data = mutable;
|
fixture.componentInstance.data = mutable;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('[\n 1\n]');
|
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
|
||||||
|
|
||||||
mutable.push(2);
|
mutable.push(2);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('[\n 1,\n 2\n]');
|
expect(fixture.nativeElement).toHaveText('[\n 1,\n 2\n]');
|
||||||
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {LowerCasePipe} from '@angular/common';
|
import {LowerCasePipe} from '@angular/common';
|
||||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('LowerCasePipe', () => {
|
describe('LowerCasePipe', () => {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
|
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
|
||||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
|
||||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import {CommonModule, SlicePipe} from '@angular/common';
|
import {CommonModule, SlicePipe} from '@angular/common';
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {TestBed, async} from '@angular/core/testing';
|
||||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -70,19 +69,15 @@ export function main() {
|
|||||||
expect(pipe.transform(str, 99)).toEqual('');
|
expect(pipe.transform(str, 99)).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Makes Edge to disconnect when running the full unit test campaign
|
it('should return entire input if START is negative and greater than input length', () => {
|
||||||
// TODO: remove when issue is solved: https://github.com/angular/angular/issues/4756
|
expect(pipe.transform(list, -99)).toEqual([1, 2, 3, 4, 5]);
|
||||||
if (!browserDetection.isEdge) {
|
expect(pipe.transform(str, -99)).toEqual('tuvwxyz');
|
||||||
it('should return entire input if START is negative and greater than input length', () => {
|
});
|
||||||
expect(pipe.transform(list, -99)).toEqual([1, 2, 3, 4, 5]);
|
|
||||||
expect(pipe.transform(str, -99)).toEqual('tuvwxyz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not modify the input list', () => {
|
it('should not modify the input list', () => {
|
||||||
expect(pipe.transform(list, 2)).toEqual([3, 4, 5]);
|
expect(pipe.transform(list, 2)).toEqual([3, 4, 5]);
|
||||||
expect(list).toEqual([1, 2, 3, 4, 5]);
|
expect(list).toEqual([1, 2, 3, 4, 5]);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,13 +95,13 @@ export function main() {
|
|||||||
it('should work with mutable arrays', async(() => {
|
it('should work with mutable arrays', async(() => {
|
||||||
let fixture = TestBed.createComponent(TestComp);
|
let fixture = TestBed.createComponent(TestComp);
|
||||||
let mutable: number[] = [1, 2];
|
let mutable: number[] = [1, 2];
|
||||||
fixture.debugElement.componentInstance.data = mutable;
|
fixture.componentInstance.data = mutable;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('2');
|
expect(fixture.nativeElement).toHaveText('2');
|
||||||
|
|
||||||
mutable.push(3);
|
mutable.push(3);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.debugElement.nativeElement).toHaveText('2,3');
|
expect(fixture.nativeElement).toHaveText('2,3');
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user