Compare commits
645 Commits
Author | SHA1 | Date | |
---|---|---|---|
84542d8ae7 | |||
17cb3ec565 | |||
015878afe6 | |||
2af58622c1 | |||
7ffd10541d | |||
481b099d82 | |||
49c4b0fa92 | |||
b8b6b1d27a | |||
892b5ba950 | |||
bd15110c7d | |||
2250082fd7 | |||
87316c52db | |||
606b76d9bb | |||
3d0b1b8184 | |||
261fd16780 | |||
104cc42f6d | |||
a7d28044c5 | |||
055bea2969 | |||
dad0d21b89 | |||
313683f6f3 | |||
338be6d6a5 | |||
4b56f79328 | |||
d7f2a3c71b | |||
1c929ae244 | |||
83d0ff6d13 | |||
d43e5dd44d | |||
61ba223c1a | |||
6164eb25f3 | |||
5e9d3dba3a | |||
16922655ca | |||
7dc12b93fe | |||
1c82b58185 | |||
d6c414c08f | |||
d25d1730c7 | |||
03b35d2e8f | |||
722543739e | |||
56b4296a09 | |||
f1cde4339b | |||
b245b920a6 | |||
f47a71689c | |||
6be55cc214 | |||
504199cf5a | |||
17c5fa9293 | |||
5f49c3ed23 | |||
ebba63057f | |||
5058461af7 | |||
21f5f05893 | |||
f2ee81fa7a | |||
ae1029da35 | |||
230e33f3f1 | |||
ec0ca01224 | |||
1cd73c7a79 | |||
9f6a647908 | |||
29ffdfdffe | |||
5754ecc3e1 | |||
dab15c79dd | |||
21942a88f0 | |||
018865ee6b | |||
f7234378b6 | |||
5f47583c94 | |||
0e7f9f0bff | |||
28a92b2bcd | |||
48be539824 | |||
d788c679b6 | |||
a38f14b39c | |||
6a5e46cedd | |||
6316e5df71 | |||
90fca7c879 | |||
d871ae2dc6 | |||
44e84d87f9 | |||
b9e979e0a5 | |||
cb2aa41782 | |||
189a7e3750 | |||
6efdf84d3e | |||
e61bfc8b24 | |||
070f9d0644 | |||
8d5da1e57a | |||
b6406191c7 | |||
124face441 | |||
de4ace77fe | |||
debb0c9798 | |||
9b87bb6d7f | |||
71e88a8c3c | |||
c26c24c544 | |||
3f178410c3 | |||
b36f4bc00d | |||
355c537883 | |||
f277303ca3 | |||
50afbe094f | |||
15ea758d01 | |||
1f0f429f2a | |||
dbb364e23a | |||
540b1197a6 | |||
d30cc8461b | |||
f27954e62c | |||
69b52eb2b3 | |||
b9b557cdb0 | |||
a72a002a8d | |||
a0437f8c9d | |||
1c279b3264 | |||
cd03c77364 | |||
f6ef7d6e5a | |||
6aeaca3fb4 | |||
af62050729 | |||
cb69656b56 | |||
2fc0560988 | |||
86c50983d7 | |||
21976446e0 | |||
998ce9ad7e | |||
111523677c | |||
2d74a224d0 | |||
4d6ac9d414 | |||
6557bc34f6 | |||
e2622add07 | |||
ecfad467a1 | |||
5918133784 | |||
700bce9ec1 | |||
a64a35a8c1 | |||
b3dcff0cc1 | |||
124267c87a | |||
547bfa92ef | |||
d40bbf4d5c | |||
94b7031fe9 | |||
df0bf1dd74 | |||
c8a9b70890 | |||
efa2d80df8 | |||
a58e5efd09 | |||
86cf0ef892 | |||
5c568fab86 | |||
566104504c | |||
307d305b2d | |||
0a7364feea | |||
4544b1d7a6 | |||
9e0e6b59d1 | |||
14dd2b367a | |||
91eb8914dd | |||
77823d721f | |||
2afe2d107f | |||
17f40fb75f | |||
98936fdf16 | |||
7383e4a801 | |||
65c9b5b6aa | |||
5fab8710cb | |||
f106a18b96 | |||
8db184d349 | |||
c18eb298eb | |||
3f4aa59cfa | |||
79728b4c41 | |||
413167ab1b | |||
203cc7e1f1 | |||
b0cd514709 | |||
392c9ac214 | |||
a26e054857 | |||
c0b001a6af | |||
c8c1f22f9c | |||
e4d5a5f003 | |||
03d9de33a1 | |||
a8a80cf523 | |||
6c1d7908d5 | |||
9aab6d24eb | |||
5ee8155e4e | |||
21de0f239d | |||
13b41bd631 | |||
f3524af68f | |||
0a56f4ea82 | |||
cf52284ac3 | |||
4a09c81724 | |||
16efb13dd1 | |||
986abbe0b2 | |||
25c2141991 | |||
2893c2c0a2 | |||
393c1007a8 | |||
66b6fc010d | |||
f31c9470fa | |||
4bd8f58552 | |||
93556a5720 | |||
5614c4ff0f | |||
c3065aac7a | |||
c767df0e4e | |||
25e5b2fdf0 | |||
307c4693dc | |||
349ad75de3 | |||
f562cbf86c | |||
804943c9b1 | |||
dea59165de | |||
a1322873c8 | |||
b8c839bd51 | |||
d2e5198b93 | |||
6cf7a1bf84 | |||
d46b8deeea | |||
bbb7a39414 | |||
d7d8fab211 | |||
51b06924bd | |||
aa4bd14b3f | |||
3ff6554cbc | |||
dfd8140084 | |||
6ea3ab7e14 | |||
9761db5ac2 | |||
75d1617b63 | |||
614a35d539 | |||
9ab401f4d3 | |||
82c81cd0d2 | |||
12959f444c | |||
25a6da244c | |||
5908b66ae9 | |||
480ef20eb1 | |||
c066281bad | |||
1b9493f725 | |||
ae26504e84 | |||
d420080b3b | |||
2975d8933c | |||
43c0e9a6bb | |||
f275f36081 | |||
e628b66cca | |||
3e73bea3e7 | |||
42cf06fa12 | |||
c4bbafc291 | |||
2d6a003dba | |||
e45b7ffcd9 | |||
627282d2c8 | |||
2f7492c986 | |||
2452cd14e0 | |||
bc69c74be0 | |||
897555ca78 | |||
966bcbad5a | |||
94b8612e4e | |||
b2b72190f8 | |||
f5c8e0989d | |||
4a09251921 | |||
36caaaa8e4 | |||
808275a9d5 | |||
be3784c957 | |||
555301ce3a | |||
7194fc2b9e | |||
2a3ca7bfcf | |||
4cbf8ccf05 | |||
a6c4490fce | |||
2c02d34c05 | |||
6c2d931744 | |||
86ffa884b7 | |||
3e548de99d | |||
909268036b | |||
519a324454 | |||
ef96763fa4 | |||
7dcca307d9 | |||
491d5a22a9 | |||
44572f114f | |||
1ef4696cb7 | |||
07a986d330 | |||
59d2b4c831 | |||
2a5bd2f345 | |||
3c06a5dc25 | |||
adeea5d86a | |||
dddbb1c1cb | |||
bccf0e69dc | |||
b15039d228 | |||
2235048432 | |||
484119e59f | |||
24099bdbd2 | |||
912ca44979 | |||
664a6273e1 | |||
c1a62e2154 | |||
aac37bedc0 | |||
a3884db87c | |||
fc5ac1ebc4 | |||
ad20d7d260 | |||
602522beb2 | |||
4e047302f2 | |||
419a812f04 | |||
f340e1a414 | |||
481c9b3258 | |||
8b2dfb2eca | |||
824ea8406c | |||
1f96a93f59 | |||
009d545787 | |||
53c25210a6 | |||
927aa69726 | |||
ce89039036 | |||
42198cd7d5 | |||
d6ba092a27 | |||
773b31de8f | |||
f79b320fc4 | |||
6a212fd561 | |||
be010a292a | |||
7c36e7f956 | |||
13ba2f90b9 | |||
75277cd94b | |||
46d150266b | |||
1b5384ee54 | |||
9f7d32a326 | |||
9de76ebfa5 | |||
46023e4792 | |||
b55aaf094f | |||
d90b622fa4 | |||
79e2bb9291 | |||
efbbefd353 | |||
c2fae72bc6 | |||
7908679c4b | |||
9ed9ff40b3 | |||
2f14415836 | |||
76e4911e8b | |||
ed5e98d0df | |||
146af1fed9 | |||
c60ba7a72f | |||
05beffe0d0 | |||
08c038ebd9 | |||
582550a90d | |||
1d53a870dd | |||
a0c58a6b5c | |||
d3eff6c483 | |||
2524d510bc | |||
8f5dd1f11e | |||
77ee27c59e | |||
73593d4bf3 | |||
a965d11cce | |||
52be848f94 | |||
69dfcf7385 | |||
785b7b640e | |||
e5a753e111 | |||
768cddbe62 | |||
92f244aa26 | |||
2a4bf9a0df | |||
45ddd6ba78 | |||
7886561997 | |||
752edca81b | |||
1bd858fb43 | |||
fcb4e66493 | |||
ef881475e9 | |||
458ca7112a | |||
2aba8b0ff2 | |||
77dc1ab675 | |||
3052fb234f | |||
79383ce150 | |||
c3c0e2e2a2 | |||
44a142fc02 | |||
3d9d839c6c | |||
69f87ca075 | |||
f224ca1461 | |||
19e869e7c9 | |||
7cab30f85d | |||
73407351e7 | |||
2c110931f8 | |||
2ced2a8a5a | |||
634b3bb88b | |||
4595a61aeb | |||
f80a157b65 | |||
6e35d13fbc | |||
fe35bc34f6 | |||
ad3bf6c54f | |||
a0e9fde653 | |||
3dc61779f0 | |||
09092ac3c2 | |||
778e6ad3b4 | |||
55dc0e4a5f | |||
4708b248d5 | |||
7694f974af | |||
acbf1d859c | |||
f3793b5953 | |||
22c021c57f | |||
d8f23f4b7f | |||
32fcec9fcb | |||
78039b41d6 | |||
89fd54e8e3 | |||
77cbf7f2bb | |||
383f23b578 | |||
2a3f4d7b17 | |||
ec92f4b198 | |||
121e5080aa | |||
fe1d0e29c5 | |||
469010ea8e | |||
f0cdb428f5 | |||
051d74802a | |||
f2bbef3e33 | |||
80d36b8db4 | |||
e3687706c7 | |||
648ce5981b | |||
9c23884da4 | |||
d708a8859c | |||
9ddf9b3d3d | |||
69f006cd89 | |||
4aaae3eada | |||
2e78b76fcf | |||
b2cf379d1c | |||
e25baa08b3 | |||
7103754178 | |||
1a069e8372 | |||
0fc11a43f1 | |||
0e3d655220 | |||
7c5cc9bc41 | |||
5f1dddc5d0 | |||
20a4f9923f | |||
e7c00be19d | |||
74ede9aa9b | |||
d1035da85c | |||
13533d2a30 | |||
953cb50fa5 | |||
3fffcf6645 | |||
d509ee078b | |||
8e221b826f | |||
830a780cb3 | |||
6fda97287e | |||
234c5599f1 | |||
f6710fefeb | |||
bda1909ede | |||
b3e3cd3add | |||
e5fdf4c70a | |||
97471d74b6 | |||
1de04b23b1 | |||
a178bc6c83 | |||
642c1db9ef | |||
579deeb9c5 | |||
bad58824a0 | |||
5494169fb4 | |||
5a3d7a62a2 | |||
a382d6dd20 | |||
52bf188b8f | |||
6f412bb449 | |||
e9fd8645ed | |||
a0aecac0e5 | |||
938ed1c76d | |||
eb8288f76c | |||
0936ceeab4 | |||
e0ad413a8e | |||
3045d02b9a | |||
e86573bac8 | |||
0a94845435 | |||
262bd23b84 | |||
7b8dae19af | |||
7c16ef942e | |||
a318b57257 | |||
fe47e6b783 | |||
091c390032 | |||
e391cacdf9 | |||
32feb8a532 | |||
a664aba2c9 | |||
d520fae70e | |||
fa93fd672e | |||
f4be2f907d | |||
2ea27a76d3 | |||
ec0acf9a1b | |||
a26dd28bdb | |||
7742ec00e7 | |||
2b5c983c13 | |||
ef153649b3 | |||
d321b0ebf5 | |||
b4265e0685 | |||
178fb79b5c | |||
5a7a58b1e0 | |||
f66ac821a2 | |||
fe299f4dfc | |||
4cac650675 | |||
cb7643ccea | |||
faa3478514 | |||
bc3f4bc816 | |||
c9f58cf78c | |||
6ccbfd41dd | |||
7d2554baa1 | |||
52a853e257 | |||
8f2fa0f766 | |||
fc60fa790c | |||
b74185369f | |||
7221632228 | |||
02f1222a8d | |||
c27ce7318f | |||
a838aba756 | |||
bfc97ff2cd | |||
57051f01ce | |||
e319cfefc3 | |||
444014ad96 | |||
867494a060 | |||
69ad99dca6 | |||
da5fc696bb | |||
b44b6ef8f5 | |||
0f21a5823b | |||
5ae6915600 | |||
8b9ab44eee | |||
b0a03fcab3 | |||
c951822c35 | |||
acda82c1ed | |||
a8815d6b08 | |||
d6791ff0e0 | |||
a2d35641e3 | |||
76dd026447 | |||
0ecd9b2df0 | |||
0e9503b500 | |||
f77ab6a2d2 | |||
97bc97153b | |||
445e5922ec | |||
b9fc090143 | |||
592f40aa9c | |||
24facdea2d | |||
aa2d3372a5 | |||
bf60418fdc | |||
cca4a5c519 | |||
6e5f8b59b3 | |||
8409b65153 | |||
38e2203b24 | |||
bd1dcb5f11 | |||
3993279527 | |||
bf1e2613b2 | |||
f7db0668d1 | |||
27d76776b8 | |||
8603d9c269 | |||
d55f747858 | |||
52de0fa558 | |||
d61ecf0663 | |||
5a9c5f28b8 | |||
15fc5dd7ee | |||
a5419608e0 | |||
5f95bf1dd2 | |||
33c8948fd3 | |||
606e51881a | |||
fdf4309b50 | |||
af996ef0c4 | |||
68d2dfdd2a | |||
07bd4b0630 | |||
df1718d624 | |||
17e3410d98 | |||
5effc330ed | |||
3df00828d7 | |||
8c477b2f45 | |||
7787771aba | |||
7275e1beb3 | |||
12ba62e5e2 | |||
e6e007e2f1 | |||
91dd138fa5 | |||
d972d82354 | |||
bdcf46f82e | |||
79e1c7b807 | |||
d22eeb70b8 | |||
aa92512ac6 | |||
f782b08f58 | |||
4202936bbf | |||
e1faca6386 | |||
f5b0e22d35 | |||
00693d70a2 | |||
bcef5efffe | |||
13ecc140e8 | |||
709a6dea06 | |||
16cfb88c00 | |||
efee6f5199 | |||
2aa8aae76d | |||
afb4bd9ef6 | |||
d641c36a45 | |||
f4566f8128 | |||
a67c06708d | |||
d9d57d71dd | |||
e06303a987 | |||
40b92ddf21 | |||
1681e4f57f | |||
71b7654660 | |||
eaaec6979c | |||
c587c63591 | |||
f50c1da4e2 | |||
0254ce1f6c | |||
c9b765f5c0 | |||
8c975ed156 | |||
bb35fcb562 | |||
57230b70a9 | |||
43dc60ce4f | |||
230b3b73d8 | |||
0b7dc2f9ff | |||
de1f44f51f | |||
f1cfddf6d6 | |||
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 |
20
.github/ISSUE_TEMPLATE.md
vendored
20
.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")
|
||||
```
|
||||
[ ] bug report => search github for a similar issue or PR before submitting
|
||||
@ -5,14 +9,18 @@
|
||||
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
```
|
||||
|
||||
**Current behavior**
|
||||
**Current behavior**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Reproduction of the problem**
|
||||
<!-- 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). -->
|
||||
**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,
|
||||
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).
|
||||
-->
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
<!-- Describe the motivation or the concrete use case -->
|
||||
@ -20,12 +28,12 @@
|
||||
**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]
|
||||
|
||||
|
||||
* **Node (for AoT issues):** `node --version` =
|
||||
|
32
.pullapprove.yml
Normal file
32
.pullapprove.yml
Normal file
@ -0,0 +1,32 @@
|
||||
# Configuration for pullapprove.com
|
||||
# See ownership spreadsheet:
|
||||
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
|
||||
|
||||
version: 2
|
||||
|
||||
group_defaults:
|
||||
required: 1
|
||||
reset_on_reopened:
|
||||
enabled: true
|
||||
approve_by_comment:
|
||||
enabled: true
|
||||
approve_regex: '^(Approved|:\+1:|LGTM)'
|
||||
|
||||
groups:
|
||||
config:
|
||||
conditions:
|
||||
files:
|
||||
- "*.yml"
|
||||
- "*.json"
|
||||
teams:
|
||||
- repoowners
|
||||
|
||||
compiler:
|
||||
conditions:
|
||||
files:
|
||||
- "tools/@angular/tsc-wrapped/*"
|
||||
- "modules/@angular/compiler/*"
|
||||
- "modules/@angular/compiler-cli/*"
|
||||
teams:
|
||||
- compiler-owners
|
||||
- repoowners
|
606
CHANGELOG.md
606
CHANGELOG.md
@ -1,5 +1,589 @@
|
||||
<a name="2.4.4"></a>
|
||||
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
|
||||
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([3d0b1b8](https://github.com/angular/angular/commit/3d0b1b8))
|
||||
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([015878a](https://github.com/angular/angular/commit/015878a)), closes [#13973](https://github.com/angular/angular/issues/13973)
|
||||
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([2af5862](https://github.com/angular/angular/commit/2af5862)), closes [#11075](https://github.com/angular/angular/issues/11075)
|
||||
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([49c4b0f](https://github.com/angular/angular/commit/49c4b0f)), closes [#13270](https://github.com/angular/angular/issues/13270)
|
||||
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([bd15110](https://github.com/angular/angular/commit/bd15110))
|
||||
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([2250082](https://github.com/angular/angular/commit/2250082)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
|
||||
|
||||
|
||||
|
||||
<a name="2.4.3"></a>
|
||||
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** avoid evaluating arguments to unknown decorators ([5e9d3db](https://github.com/angular/angular/commit/5e9d3db)), closes [#13605](https://github.com/angular/angular/issues/13605)
|
||||
* **compiler:** fix template binding parsing (`*directive="-..."`) ([7dc12b9](https://github.com/angular/angular/commit/7dc12b9)), closes [#13800](https://github.com/angular/angular/issues/13800)
|
||||
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([6164eb2](https://github.com/angular/angular/commit/6164eb2)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
|
||||
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([313683f](https://github.com/angular/angular/commit/313683f)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
|
||||
* **i18n:** translate attributes inside elements marked for translation ([d7f2a3c](https://github.com/angular/angular/commit/d7f2a3c))
|
||||
* **router:** RouterLink mirrors input `target` as attribute ([1c82b58](https://github.com/angular/angular/commit/1c82b58)), closes [#13837](https://github.com/angular/angular/issues/13837)
|
||||
* **router:** throw an error when navigate to null/undefined path ([61ba223](https://github.com/angular/angular/commit/61ba223)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
|
||||
* **router:** fix checking for object intersection ([1692265](https://github.com/angular/angular/commit/1692265))
|
||||
|
||||
|
||||
|
||||
<a name="2.4.2"></a>
|
||||
## [2.4.2](https://github.com/angular/angular/compare/2.4.1...2.4.2) (2017-01-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** add link to trackBy docs from the error message ([#13634](https://github.com/angular/angular/issues/13634)) ([f723437](https://github.com/angular/angular/commit/f723437))
|
||||
* **common:** do not override locale provided on bootstrap ([#13654](https://github.com/angular/angular/issues/13654)) ([5f49c3e](https://github.com/angular/angular/commit/5f49c3e)), closes [#13607](https://github.com/angular/angular/issues/13607)
|
||||
* **common:** allow null/undefined values for `NgForTrackBy` ([6be55cc](https://github.com/angular/angular/commit/6be55cc)), closes [#13641](https://github.com/angular/angular/issues/13641)
|
||||
* **compiler:** don’t throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([230e33f](https://github.com/angular/angular/commit/230e33f)), closes [#13565](https://github.com/angular/angular/issues/13565)
|
||||
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([1cd73c7](https://github.com/angular/angular/commit/1cd73c7)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
|
||||
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645) [#13982](https://github.com/angular/angular/issues/13982)
|
||||
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([f1cde43](https://github.com/angular/angular/commit/f1cde43))
|
||||
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([b245b92](https://github.com/angular/angular/commit/b245b92))
|
||||
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([21f5f05](https://github.com/angular/angular/commit/21f5f05))
|
||||
* **core/testing:** improve misleading error message when don't call compileComponents ([#13543](https://github.com/angular/angular/issues/13543)) ([0e7f9f0](https://github.com/angular/angular/commit/0e7f9f0)), closes [#11301](https://github.com/angular/angular/issues/11301)
|
||||
* **forms:** Validators.required properly validate arrays ([#13362](https://github.com/angular/angular/issues/13362)) ([17c5fa9](https://github.com/angular/angular/commit/17c5fa9)), closes [#12274](https://github.com/angular/angular/issues/12274)
|
||||
* **language-service:** support TypeScript 2.1 ([#13655](https://github.com/angular/angular/issues/13655)) ([56b4296](https://github.com/angular/angular/commit/56b4296))
|
||||
* **router:** fix lazy loaded module with wildcard route ([#13649](https://github.com/angular/angular/issues/13649)) ([5754ecc](https://github.com/angular/angular/commit/5754ecc)), closes [#12955](https://github.com/angular/angular/issues/12955)
|
||||
* **router:** routerLink support of null ([#13380](https://github.com/angular/angular/issues/13380)) ([018865e](https://github.com/angular/angular/commit/018865e)), closes [#6971](https://github.com/angular/angular/issues/6971)
|
||||
* **router:** update route snapshot before emit new values ([#13558](https://github.com/angular/angular/issues/13558)) ([9f6a647](https://github.com/angular/angular/commit/9f6a647)), closes [#12912](https://github.com/angular/angular/issues/12912)
|
||||
* **upgrade:** fix/improve support for lifecycle hooks ([#13020](https://github.com/angular/angular/issues/13020)) ([21942a8](https://github.com/angular/angular/commit/21942a8))
|
||||
|
||||
|
||||
|
||||
<a name="2.4.1"></a>
|
||||
## [2.4.1](https://github.com/angular/angular/compare/2.4.0...2.4.1) (2016-12-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always quote string map key values in AOT code ([#13602](https://github.com/angular/angular/issues/13602)) ([6a5e46c](https://github.com/angular/angular/commit/6a5e46c))
|
||||
* **animations:** always recover from a failed animation step ([#13604](https://github.com/angular/angular/issues/13604)) ([d788c67](https://github.com/angular/angular/commit/d788c67))
|
||||
* **compiler:** ignore `@import` in comments ([#13368](https://github.com/angular/angular/issues/13368)) ([6316e5d](https://github.com/angular/angular/commit/6316e5d)), closes [#12196](https://github.com/angular/angular/issues/12196)
|
||||
* **core:** improve error message when component factory cannot be found ([#13541](https://github.com/angular/angular/issues/13541)) ([b9e979e](https://github.com/angular/angular/commit/b9e979e)), closes [#12678](https://github.com/angular/angular/issues/12678)
|
||||
* **router:** should reset location if a navigation by location is successful ([#13545](https://github.com/angular/angular/issues/13545)) ([a38f14b](https://github.com/angular/angular/commit/a38f14b)), closes [#13491](https://github.com/angular/angular/issues/13491)
|
||||
|
||||
|
||||
|
||||
<a name="2.4.0"></a>
|
||||
# [2.4.0 stability-interjection](https://github.com/angular/angular/compare/2.3.1...2.4.0) (2016-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** allow players to be destroyed before initialized ([#13346](https://github.com/angular/angular/issues/13346)) ([b36f4bc](https://github.com/angular/angular/commit/b36f4bc)), closes [#13293](https://github.com/angular/angular/issues/13293)
|
||||
* **build:** use bash string comparison operator ([#13502](https://github.com/angular/angular/issues/13502)) ([50afbe0](https://github.com/angular/angular/commit/50afbe0))
|
||||
* **compiler:** do not lex `}}` when interpolation is disabled ([#13531](https://github.com/angular/angular/issues/13531)) ([9b87bb6](https://github.com/angular/angular/commit/9b87bb6)), closes [#13525](https://github.com/angular/angular/issues/13525)
|
||||
* **compiler-cli:** produce metadata for .d.ts files without metadata ([#13526](https://github.com/angular/angular/issues/13526)) ([debb0c9](https://github.com/angular/angular/commit/debb0c9)), closes [#13307](https://github.com/angular/angular/issues/13307) [#13473](https://github.com/angular/angular/issues/13473) [#13521](https://github.com/angular/angular/issues/13521)
|
||||
* **i18n:** add a default example to xmb placeholders ([#13507](https://github.com/angular/angular/issues/13507)) ([3f17841](https://github.com/angular/angular/commit/3f17841))
|
||||
* **upgrade:** fix `registerForNg1Tests` ([#13522](https://github.com/angular/angular/issues/13522)) ([c26c24c](https://github.com/angular/angular/commit/c26c24c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* update to `rxjs@5.0.1` and unpin the rxjs peerDeps via `^5.0.1` ([#13572](https://github.com/angular/angular/issues/13572)) ([8d5da1e](https://github.com/angular/angular/commit/8d5da1e)), closes [#13561](https://github.com/angular/angular/issues/13561) [#13478](https://github.com/angular/angular/issues/13478)
|
||||
|
||||
|
||||
|
||||
<a name="2.3.1"></a>
|
||||
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always cleanup players after they have finished internally ([#13334](https://github.com/angular/angular/issues/13334)) ([a26e054](https://github.com/angular/angular/commit/a26e054)), closes [#13333](https://github.com/angular/angular/issues/13333)
|
||||
* **animations:** throw errors and normalize offset beyond the range of [0,1] ([6557bc3](https://github.com/angular/angular/commit/6557bc3)), closes [#13348](https://github.com/angular/angular/issues/13348) [#13440](https://github.com/angular/angular/issues/13440)
|
||||
* **compiler:** emit quoted object literal keys if the source is quoted ([ecfad46](https://github.com/angular/angular/commit/ecfad46)), closes [#13249](https://github.com/angular/angular/issues/13249) [#13356](https://github.com/angular/angular/issues/13356)
|
||||
* **compiler:** fix merge error in compiler_host ([69b52eb](https://github.com/angular/angular/commit/69b52eb))
|
||||
* **compiler:** fix PR 13322 ([#13331](https://github.com/angular/angular/issues/13331)) ([79728b4](https://github.com/angular/angular/commit/79728b4))
|
||||
* **compiler:** fix simplify a reference without a name ([1c279b3](https://github.com/angular/angular/commit/1c279b3)), closes [#13470](https://github.com/angular/angular/issues/13470)
|
||||
* **compiler:** fix transpiled ES5 code ([#13322](https://github.com/angular/angular/issues/13322)) ([6c1d790](https://github.com/angular/angular/commit/6c1d790)), closes [#13301](https://github.com/angular/angular/issues/13301)
|
||||
* **compiler:** generated CSS files suffixed with ngstyle. ([#13353](https://github.com/angular/angular/issues/13353)) ([c8a9b70](https://github.com/angular/angular/commit/c8a9b70)), closes [#13141](https://github.com/angular/angular/issues/13141)
|
||||
* **compiler:** make sure provider values with `name` property don’t break. ([efa2d80](https://github.com/angular/angular/commit/efa2d80)), closes [#13394](https://github.com/angular/angular/issues/13394) [#13445](https://github.com/angular/angular/issues/13445)
|
||||
* **compiler:** narrow the span reported for invalid pipes ([307d305](https://github.com/angular/angular/commit/307d305)), closes [#13326](https://github.com/angular/angular/issues/13326) [#13411](https://github.com/angular/angular/issues/13411)
|
||||
* **compiler:** propagate exports when upgrading metadata to v2 ([f6ef7d6](https://github.com/angular/angular/commit/f6ef7d6))
|
||||
* **compiler:** resolver should merge host bindings and listeners ([#13474](https://github.com/angular/angular/issues/13474)) ([6aeaca3](https://github.com/angular/angular/commit/6aeaca3)), closes [#13327](https://github.com/angular/angular/issues/13327)
|
||||
* **compiler:** support dotted property binding ([8db184d](https://github.com/angular/angular/commit/8db184d)), closes [angular/flex-layout#34](https://github.com/angular/flex-layout/issues/34)
|
||||
* **compiler:** update to metadata version 3 ([#13464](https://github.com/angular/angular/issues/13464)) ([b9b557c](https://github.com/angular/angular/commit/b9b557c))
|
||||
* **core:** detectChanges() doesn't work on detached instance ([4d6ac9d](https://github.com/angular/angular/commit/4d6ac9d)), closes [#13426](https://github.com/angular/angular/issues/13426) [#13472](https://github.com/angular/angular/issues/13472)
|
||||
* **core:** properly destroy embedded Views attatched to ApplicationRef ([#13459](https://github.com/angular/angular/issues/13459)) ([d40bbf4](https://github.com/angular/angular/commit/d40bbf4)), closes [#13062](https://github.com/angular/angular/issues/13062)
|
||||
* **core:** remove logError from logGroup ([#12925](https://github.com/angular/angular/issues/12925)) ([5fab871](https://github.com/angular/angular/commit/5fab871))
|
||||
* **forms:** ensure `select[multiple]` retains selections ([b3dcff0](https://github.com/angular/angular/commit/b3dcff0)), closes [#12527](https://github.com/angular/angular/issues/12527) [#12654](https://github.com/angular/angular/issues/12654)
|
||||
* **forms:** fix Validators.min/maxLength with FormArray ([#13095](https://github.com/angular/angular/issues/13095)) ([7383e4a](https://github.com/angular/angular/commit/7383e4a)), closes [#13089](https://github.com/angular/angular/issues/13089)
|
||||
* **forms:** introduce checkbox required validator ([124267c](https://github.com/angular/angular/commit/124267c)), closes [#11459](https://github.com/angular/angular/issues/11459) [#13364](https://github.com/angular/angular/issues/13364)
|
||||
* **http:** check response body text against undefined ([#13017](https://github.com/angular/angular/issues/13017)) ([f106a18](https://github.com/angular/angular/commit/f106a18))
|
||||
* **http:** create a copy of headers when merge options ([#13365](https://github.com/angular/angular/issues/13365)) ([65c9b5b](https://github.com/angular/angular/commit/65c9b5b)), closes [#11980](https://github.com/angular/angular/issues/11980)
|
||||
* **language-service:** correctly type `undefined` ([0a7364f](https://github.com/angular/angular/commit/0a7364f)), closes [#13412](https://github.com/angular/angular/issues/13412) [#13414](https://github.com/angular/angular/issues/13414)
|
||||
* **compiler**: better error when directive not listed in NgModule.declarations ([b0cd514](https://github.com/angular/angular/commit/b0cd514))
|
||||
* **language-service:** treat string unions as strings ([#13406](https://github.com/angular/angular/issues/13406)) ([14dd2b3](https://github.com/angular/angular/commit/14dd2b3)), closes [#13403](https://github.com/angular/angular/issues/13403)
|
||||
* **router:** add support for query params with multiple values ([e4d5a5f](https://github.com/angular/angular/commit/e4d5a5f)), closes [#11373](https://github.com/angular/angular/issues/11373)
|
||||
* **router:** Use T type in Resolve interface ([#13242](https://github.com/angular/angular/issues/13242)) ([5ee8155](https://github.com/angular/angular/commit/5ee8155))
|
||||
* **selector:** SelectorMatcher match elements with :not selector ([#12977](https://github.com/angular/angular/issues/12977)) ([392c9ac](https://github.com/angular/angular/commit/392c9ac))
|
||||
* **tsc-wrapped:** generate metadata for exports without module specifier ([cd03c77](https://github.com/angular/angular/commit/cd03c77)), closes [#13327](https://github.com/angular/angular/issues/13327)
|
||||
* **upgrade:** fix downgrade content projection and injector inheritance ([86c5098](https://github.com/angular/angular/commit/86c5098)), closes [#6629](https://github.com/angular/angular/issues/6629) [#7727](https://github.com/angular/angular/issues/7727) [#8729](https://github.com/angular/angular/issues/8729) [#9643](https://github.com/angular/angular/issues/9643) [#9649](https://github.com/angular/angular/issues/9649) [#12675](https://github.com/angular/angular/issues/12675)
|
||||
* **upgrade:** enable Angular 1 unit testing of upgrade module ([2fc0560](https://github.com/angular/angular/commit/2fc0560)), closes [#5462](https://github.com/angular/angular/issues/5462) [#12675](https://github.com/angular/angular/issues/12675)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **animations:** always run the animation queue outside of zones ([e2622ad](https://github.com/angular/angular/commit/e2622ad)), closes [#13440](https://github.com/angular/angular/issues/13440)
|
||||
|
||||
### Note ###
|
||||
|
||||
Due to regression in the 2.3.0 release that was fixed by [#13464](https://github.com/angular/angular/pull/13464),
|
||||
components that have been compiled using 2.3.0 and published to npm will need to be recompiled and republished.
|
||||
|
||||
The >=2.3.1 compiler will issue is the following error if it encounters components compiled with 2.3.0:
|
||||
`Unsupported metadata version 2 for module ${module}. This module should be compiled with a newer version of ngc`.
|
||||
|
||||
We are adding more tests to our test suite to catch these kinds of problems before we cut a release.
|
||||
|
||||
|
||||
<a name="2.3.0"></a>
|
||||
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** make sure the plural category exists ([#13169](https://github.com/angular/angular/issues/13169)) ([82c81cd](https://github.com/angular/angular/commit/82c81cd)), closes [#12379](https://github.com/angular/angular/issues/12379)
|
||||
* **compiler:** include the summaries of reexported modules / directives / pipes ([#13196](https://github.com/angular/angular/issues/13196)) ([75d1617](https://github.com/angular/angular/commit/75d1617))
|
||||
* **compiler:** serialize any `StaticSymbol` correctly, not matter in which context ([5614c4f](https://github.com/angular/angular/commit/5614c4f))
|
||||
* **compiler:** short-circut expressions with an index ([#13263](https://github.com/angular/angular/issues/13263)) ([f31c947](https://github.com/angular/angular/commit/f31c947)), closes [#13254](https://github.com/angular/angular/issues/13254)
|
||||
* **core:** display framework version on bootstrapped component ([#13252](https://github.com/angular/angular/issues/13252)) ([16efb13](https://github.com/angular/angular/commit/16efb13))
|
||||
* **facade:** cache original format string ([#12764](https://github.com/angular/angular/issues/12764)) ([a132287](https://github.com/angular/angular/commit/a132287))
|
||||
* **http:** set the default Accept header ([#12989](https://github.com/angular/angular/issues/12989)) ([986abbe](https://github.com/angular/angular/commit/986abbe)), closes [#6354](https://github.com/angular/angular/issues/6354)
|
||||
* **language-service:** avoid throwing for invalid class declarations ([#13257](https://github.com/angular/angular/issues/13257)) ([93556a5](https://github.com/angular/angular/commit/93556a5)), closes [#13253](https://github.com/angular/angular/issues/13253)
|
||||
* **language-service:** do not throw for invalid metadata ([#13261](https://github.com/angular/angular/issues/13261)) ([4a09c81](https://github.com/angular/angular/commit/4a09c81)), closes [#13255](https://github.com/angular/angular/issues/13255)
|
||||
* **language-service:** remove incompletely used parameter from `createLanguageServiceFromTypescript()` ([#13278](https://github.com/angular/angular/issues/13278)) ([25c2141](https://github.com/angular/angular/commit/25c2141)), closes [#13277](https://github.com/angular/angular/issues/13277)
|
||||
* **language-service:** update to use `CompilerHost` from compiler-cli ([#13189](https://github.com/angular/angular/issues/13189)) ([3ff6554](https://github.com/angular/angular/commit/3ff6554))
|
||||
* **router:** allow specifying a matcher without specifying a path ([bbb7a39](https://github.com/angular/angular/commit/bbb7a39)), closes [#12972](https://github.com/angular/angular/issues/12972)
|
||||
* **router:** fix replaceUrl on RouterLink directives ([349ad75](https://github.com/angular/angular/commit/349ad75))
|
||||
* **router:** fix skipLocationChanges on RouterLink directives ([f562cbf](https://github.com/angular/angular/commit/f562cbf)), closes [#13156](https://github.com/angular/angular/issues/13156)
|
||||
* **router:** make setUpLocationChangeListener idempotent ([25e5b2f](https://github.com/angular/angular/commit/25e5b2f))
|
||||
* **router:** runs guards every time when unsuccessfully navigating to the same url over and over again ([#13209](https://github.com/angular/angular/issues/13209)) ([d46b8de](https://github.com/angular/angular/commit/d46b8de))
|
||||
* **router:** throw a better error message when angular 1 is not bootstraped ([c767df0](https://github.com/angular/angular/commit/c767df0))
|
||||
* **router:** validate nested routes ([#13224](https://github.com/angular/angular/issues/13224)) ([2893c2c](https://github.com/angular/angular/commit/2893c2c)), closes [#12827](https://github.com/angular/angular/issues/12827)
|
||||
* **tsc-wrapped:** have UserError display the actual error ([393c100](https://github.com/angular/angular/commit/393c100))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** read and write `.ngsummary.json` files ([614a35d](https://github.com/angular/angular/commit/614a35d)), closes [#12787](https://github.com/angular/angular/issues/12787)
|
||||
|
||||
|
||||
|
||||
<a name="2.3.0-rc.0"></a>
|
||||
# [2.3.0-rc.0](https://github.com/angular/angular/compare/2.3.0-beta.0...2.3.0-rc.0) (2016-11-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ef96763](https://github.com/angular/angular/commit/ef96763)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([fc5ac1e](https://github.com/angular/angular/commit/fc5ac1e))
|
||||
* **build:** update versions of umd bundles ([#13038](https://github.com/angular/angular/issues/13038)) ([86ffa88](https://github.com/angular/angular/commit/86ffa88)), closes [#13037](https://github.com/angular/angular/issues/13037)
|
||||
* **changelog:** replace beta.1 with beta.0 ([#12961](https://github.com/angular/angular/issues/12961)) ([07a986d](https://github.com/angular/angular/commit/07a986d))
|
||||
* **ci:** pin version of npm on CircleCI ([#12954](https://github.com/angular/angular/issues/12954)) ([a3884db](https://github.com/angular/angular/commit/a3884db))
|
||||
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([7dcca30](https://github.com/angular/angular/commit/7dcca30))
|
||||
* **common:** update DatePipe to allow closure compilation ([b2b7219](https://github.com/angular/angular/commit/b2b7219))
|
||||
* **compiler:** correctly evaluate references to static functions ([#13133](https://github.com/angular/angular/issues/13133)) ([627282d](https://github.com/angular/angular/commit/627282d))
|
||||
* **compiler:** fix performance regression caused by 5b0f9e2 ([43c0e9a](https://github.com/angular/angular/commit/43c0e9a)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
||||
* **compiler:** fix versions of `@angular/tsc-wrapped` ([bccf0e6](https://github.com/angular/angular/commit/bccf0e6))
|
||||
* **compiler-cli:** fix paths in source maps to be relative ([2a3ca7b](https://github.com/angular/angular/commit/2a3ca7b)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
||||
* **compiler-cli:** pin the version of `tsc-wrapped` ([966bcba](https://github.com/angular/angular/commit/966bcba))
|
||||
* **language-service:** harden against partial normalization of directives ([2975d89](https://github.com/angular/angular/commit/2975d89))
|
||||
* **core:** shrinkwrap was out of date with packages. ([e45b7ff](https://github.com/angular/angular/commit/e45b7ff))
|
||||
* **language-service:** make link check pass ([7194fc2](https://github.com/angular/angular/commit/7194fc2))
|
||||
* **router:** guards restore an incorrect url when used with skipLocationChange ([ad20d7d](https://github.com/angular/angular/commit/ad20d7d)), closes [#12825](https://github.com/angular/angular/issues/12825)
|
||||
* **router:** support redirects to named outlets ([602522b](https://github.com/angular/angular/commit/602522b)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||
* **tsc-wrapped:** set correct version number ([897555c](https://github.com/angular/angular/commit/897555c))
|
||||
* **tsc-wrapped:** still emit version 1 metadata to allow use of new components in old setups ([bc69c74](https://github.com/angular/angular/commit/bc69c74))
|
||||
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([1ef4696](https://github.com/angular/angular/commit/1ef4696))
|
||||
* **version:** take all of version string after patch version ([f275f36](https://github.com/angular/angular/commit/f275f36))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** update RxJS peer dependency to 5.0.0-rc.4 Please see [this gist](https://gist.github.com/robwormald/19dea0c70a6e01aadced6731aed4f9f7) if you depend on the `cache` operator ([2d6a003](https://github.com/angular/angular/commit/2d6a003)), closes [#13125](https://github.com/angular/angular/issues/13125)
|
||||
* **core:** upgrade zone.js to v0.7.1 ([c4bbafc](https://github.com/angular/angular/commit/c4bbafc))
|
||||
* **build:** record angular version in the dom ([#13164](https://github.com/angular/angular/issues/13164)) ([e628b66](https://github.com/angular/angular/commit/e628b66))
|
||||
* **core:** expose destroy() method on ViewRef ([808275a](https://github.com/angular/angular/commit/808275a))
|
||||
* **core:** properly support inheritance ([f5c8e09](https://github.com/angular/angular/commit/f5c8e09)), closes [#11606](https://github.com/angular/angular/issues/11606) [#12892](https://github.com/angular/angular/issues/12892)
|
||||
* **language-service:** add services to support editors ([#12987](https://github.com/angular/angular/issues/12987)) ([519a324](https://github.com/angular/angular/commit/519a324))
|
||||
* **router:** add support for custom route reuse strategies ([42cf06f](https://github.com/angular/angular/commit/42cf06f))
|
||||
* **tools:** allow disabling annotation lowering ([c1a62e2](https://github.com/angular/angular/commit/c1a62e2))
|
||||
|
||||
|
||||
|
||||
<a name="2.2.4"></a>
|
||||
## [2.2.4](https://github.com/angular/angular/compare/2.2.3...2.2.4) (2016-11-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** update DatePipe to allow closure compilation ([eba53fd](https://github.com/angular/angular/commit/eba53fd))
|
||||
* **compiler:** fix performance regression caused by 5b0f9e2 ([ee2d6e5](https://github.com/angular/angular/commit/ee2d6e5)), closes [#13146](https://github.com/angular/angular/issues/13146)
|
||||
* **compiler-cli:** fix paths in source maps to be relative ([eb173bc](https://github.com/angular/angular/commit/eb173bc)), closes [#13040](https://github.com/angular/angular/issues/13040)
|
||||
|
||||
|
||||
|
||||
<a name="2.2.3"></a>
|
||||
## [2.2.3](https://github.com/angular/angular/compare/2.2.2...2.2.3) (2016-11-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** Revert: fix versions of `@angular/tsc-wrapped` ([015ca47](https://github.com/angular/angular/commit/015ca47))
|
||||
* **animations:** Revert: blend in all previously transitioned styles into next animation if interrupted ([c12e56e](https://github.com/angular/angular/commit/c12e56e))
|
||||
|
||||
|
||||
|
||||
<a name="2.2.2"></a>
|
||||
## [2.2.2](https://github.com/angular/angular/compare/2.2.1...2.2.2) (2016-11-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** blend in all previously transitioned styles into next animation if interrupted ([#13014](https://github.com/angular/angular/issues/13014)) ([ea4fc9b](https://github.com/angular/angular/commit/ea4fc9b)), closes [#13013](https://github.com/angular/angular/issues/13013)
|
||||
* **benchmarks:** use sanitized style values ([#12943](https://github.com/angular/angular/issues/12943)) ([33a7902](https://github.com/angular/angular/commit/33a7902))
|
||||
* **closure:** quote date pattern aliases ([#13012](https://github.com/angular/angular/issues/13012)) ([0956ace](https://github.com/angular/angular/commit/0956ace))
|
||||
* **compiler:** fix versions of `@angular/tsc-wrapped` ([2fe6fb1](https://github.com/angular/angular/commit/2fe6fb1))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([8df328b](https://github.com/angular/angular/commit/8df328b))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([511cd4d](https://github.com/angular/angular/commit/511cd4d))
|
||||
* **router:** removes a peer dependency from router to upgrade ([115f18f](https://github.com/angular/angular/commit/115f18f))
|
||||
* **router:** removes a peer dependency from router to upgrade ([87d5d49](https://github.com/angular/angular/commit/87d5d49))
|
||||
* **router:** support redirects to named outlets ([09226d9](https://github.com/angular/angular/commit/09226d9)), closes [#12740](https://github.com/angular/angular/issues/12740) [#9921](https://github.com/angular/angular/issues/9921)
|
||||
* **upgrade:** call ng1 lifecycle hooks ([#12875](https://github.com/angular/angular/issues/12875)) ([462316b](https://github.com/angular/angular/commit/462316b))
|
||||
|
||||
|
||||
|
||||
<a name="2.3.0-beta.0"></a>
|
||||
# [2.3.0-beta.0](https://github.com/angular/angular/compare/2.2.0...2.3.0-beta.0) (2016-11-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** assert xliff messages have translations ([7908679](https://github.com/angular/angular/commit/7908679)), closes [#12815](https://github.com/angular/angular/issues/12815) [#12604](https://github.com/angular/angular/issues/12604)
|
||||
* **compiler:** updates hash algo for xmb/xtb files ([2f14415](https://github.com/angular/angular/commit/2f14415))
|
||||
* **core:** fix placeholders handling in i18n. ([76e4911](https://github.com/angular/angular/commit/76e4911)), closes [#12512](https://github.com/angular/angular/issues/12512)
|
||||
* **core:** misc i18n fixes ([ed5e98d](https://github.com/angular/angular/commit/ed5e98d))
|
||||
* **core:** xmb serializer uses decimal messaged IDs ([08c038e](https://github.com/angular/angular/commit/08c038e)), closes [#12511](https://github.com/angular/angular/issues/12511)
|
||||
* **platform-browser:** enable AOT ([efbbefd](https://github.com/angular/angular/commit/efbbefd)), closes [#12783](https://github.com/angular/angular/issues/12783)
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add `attachView` / `detachView` to ApplicationRef ([9f7d32a](https://github.com/angular/angular/commit/9f7d32a)), closes [#9293](https://github.com/angular/angular/issues/9293)
|
||||
* **core:** expose `ViewRef` as `ChangeDetectorRef` ([1b5384e](https://github.com/angular/angular/commit/1b5384e)), closes [#12722](https://github.com/angular/angular/issues/12722)
|
||||
* **core:** implements a decimal fingerprint for i18n ([582550a](https://github.com/angular/angular/commit/582550a))
|
||||
* **router:** register router with ngprobe ([c2fae72](https://github.com/angular/angular/commit/c2fae72))
|
||||
* **router_link:** add skipLocationChange and replaceUrl inputs ([#12850](https://github.com/angular/angular/issues/12850)) ([46d1502](https://github.com/angular/angular/commit/46d1502))
|
||||
|
||||
Note: The 2.3.0-beta.0 release also contains all the changes present in the 2.2.1 release.
|
||||
|
||||
|
||||
|
||||
<a name="2.2.1"></a>
|
||||
## [2.2.1](https://github.com/angular/angular/compare/2.2.0...2.2.1) (2016-11-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** only pass in same typed players as previous players into web-animations ([#12907](https://github.com/angular/angular/issues/12907)) ([583d283](https://github.com/angular/angular/commit/583d283))
|
||||
* **animations:** retain styling when transition destinations are changed ([#12208](https://github.com/angular/angular/issues/12208)) ([5c46c49](https://github.com/angular/angular/commit/5c46c49)), closes [#9661](https://github.com/angular/angular/issues/9661)
|
||||
* **core:** support `ngTemplateOutlet` in production mode ([#12921](https://github.com/angular/angular/issues/12921)) ([4628798](https://github.com/angular/angular/commit/4628798)), closes [#12911](https://github.com/angular/angular/issues/12911)
|
||||
* **http:** correctly handle response body for 204 status code ([21a4de9](https://github.com/angular/angular/commit/21a4de9)), closes [#12830](https://github.com/angular/angular/issues/12830) [#12393](https://github.com/angular/angular/issues/12393)
|
||||
* **http:** return request url if it cannot be retrieved from response ([845ea23](https://github.com/angular/angular/commit/845ea23)), closes [#12837](https://github.com/angular/angular/issues/12837)
|
||||
* **upgrade:** make AoT ngUpgrade work with the testability API and resumeBootstrap() ([#12910](https://github.com/angular/angular/issues/12910)) ([dc1662a](https://github.com/angular/angular/commit/dc1662a))
|
||||
* **platform-browser:** fix disableDebugTools() ([#12918](https://github.com/angular/angular/issues/12918)) ([7b67bad](https://github.com/angular/angular/commit/7b67bad))
|
||||
* **router:** add a banner file for the router ([#12919](https://github.com/angular/angular/issues/12919)) ([364642d](https://github.com/angular/angular/commit/364642d))
|
||||
* **router:** removes a peer dependency from router to upgrade ([1dcf1f4](https://github.com/angular/angular/commit/1dcf1f4))
|
||||
* **forms** allow for null values in HTML select options bound with ngValue ([e0ce545](https://github.com/angular/angular/commit/e0ce545)), closes [#10349](https://github.com/angular/angular/issues/10349)
|
||||
* **router:** should not create a route state if navigation is canceled ([#12868](https://github.com/angular/angular/issues/12868)) ([dabaf85](https://github.com/angular/angular/commit/dabaf85)), closes [#12776](https://github.com/angular/angular/issues/12776)
|
||||
* **common:** select should allow for null values in HTML select options bound with ngValue ([e02c180](https://github.com/angular/angular/commit/e02c180)), closes [#12829](https://github.com/angular/angular/issues/12829)
|
||||
* **compiler-cli:** support ctorParams in function closure ([#12876](https://github.com/angular/angular/issues/12876)) ([6cdc3b5](https://github.com/angular/angular/commit/6cdc3b5))
|
||||
|
||||
|
||||
<a name="2.2.0"></a>
|
||||
# [2.2.0 upgrade-firebooster](https://github.com/angular/angular/compare/2.2.0-rc.0...2.2.0) (2016-11-14)
|
||||
|
||||
### Features (summary of all features from 2.2.0-beta.0 - 2.2.0-rc.0 releases)
|
||||
|
||||
* **common:** support narrow forms for month and weekdays in DatePipe ([#12297](https://github.com/angular/angular/issues/12297)) ([f77ab6a](https://github.com/angular/angular/commit/f77ab6a)), closes [#12294](https://github.com/angular/angular/issues/12294)
|
||||
* **core:** map 'for' attribute to 'htmlFor' property ([#10546](https://github.com/angular/angular/issues/10546)) ([634b3bb](https://github.com/angular/angular/commit/634b3bb)), closes [#7516](https://github.com/angular/angular/issues/7516)
|
||||
* **core:** add the find method to QueryList ([7c16ef9](https://github.com/angular/angular/commit/7c16ef9))
|
||||
* **forms:** add hasError and getError to AbstractControlDirective ([#11985](https://github.com/angular/angular/issues/11985)) ([592f40a](https://github.com/angular/angular/commit/592f40a)), closes [#7255](https://github.com/angular/angular/issues/7255)
|
||||
* **forms:** add ng-pending CSS class during async validation ([#11243](https://github.com/angular/angular/issues/11243)) ([97bc971](https://github.com/angular/angular/commit/97bc971)), closes [#10336](https://github.com/angular/angular/issues/10336)
|
||||
* **forms:** add emitEvent to AbstractControl methods ([#11949](https://github.com/angular/angular/issues/11949)) ([b9fc090](https://github.com/angular/angular/commit/b9fc090))
|
||||
* **forms:** make 'parent' a public property of 'AbstractControl' ([#11855](https://github.com/angular/angular/issues/11855)) ([445e592](https://github.com/angular/angular/commit/445e592))
|
||||
* **forms:** Validator.pattern accepts a RegExp ([#12323](https://github.com/angular/angular/issues/12323)) ([bf60418](https://github.com/angular/angular/commit/bf60418))
|
||||
* **router:** add a provider making angular1/angular2 integration easier ([#12769](https://github.com/angular/angular/issues/12769)) ([6e35d13](https://github.com/angular/angular/commit/6e35d13))
|
||||
* **router:** add support for custom url matchers ([7340735](https://github.com/angular/angular/commit/7340735)), closes [#12442](https://github.com/angular/angular/issues/12442) [#12772](https://github.com/angular/angular/issues/12772)
|
||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
||||
* **router:** add support for ng1/ng2 migration ([#12160](https://github.com/angular/angular/issues/12160)) ([8b9ab44](https://github.com/angular/angular/commit/8b9ab44))
|
||||
* **upgrade:** add support for AoT compiled upgrade applications ([d6791ff](https://github.com/angular/angular/commit/d6791ff)), closes [#12239](https://github.com/angular/angular/issues/12239)
|
||||
* **upgrade:** add support for `require` in UpgradeComponent ([fe1d0e2](https://github.com/angular/angular/commit/fe1d0e2))
|
||||
* **upgrade:** add/improve support for lifecycle hooks in UpgradeComponent ([469010e](https://github.com/angular/angular/commit/469010e))
|
||||
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** introduce direct rendering ([9c23884](https://github.com/angular/angular/commit/9c23884))
|
||||
* **core:** don’t use `DomAdapter` nor zone for regular events ([648ce59](https://github.com/angular/angular/commit/648ce59))
|
||||
* **core:** use `array.push` / `array.pop` instead of `splice` if possible ([0fc11a4](https://github.com/angular/angular/commit/0fc11a4))
|
||||
* **platform-browser:** cache plugin resolution in the EventManager ([73593d4](https://github.com/angular/angular/commit/73593d4)), closes [#12824](https://github.com/angular/angular/issues/12824)
|
||||
* **platform-browser:** don’t use `DomAdapter` any more ([d708a88](https://github.com/angular/angular/commit/d708a88))
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** allow animations to be destroyed manually ([#12719](https://github.com/angular/angular/issues/12719)) ([fe35bc3](https://github.com/angular/angular/commit/fe35bc3)), closes [#12456](https://github.com/angular/angular/issues/12456)
|
||||
* **animations:** always normalize style properties and values during compilation ([#12755](https://github.com/angular/angular/issues/12755)) ([a0e9fde](https://github.com/angular/angular/commit/a0e9fde)), closes [#11582](https://github.com/angular/angular/issues/11582) [#12481](https://github.com/angular/angular/issues/12481)
|
||||
* **animations:** always trigger animations after the change detection check ([#12713](https://github.com/angular/angular/issues/12713)) ([383f23b](https://github.com/angular/angular/commit/383f23b))
|
||||
* **animations:** ensure animations work with web-workers ([#12656](https://github.com/angular/angular/issues/12656)) ([19e869e](https://github.com/angular/angular/commit/19e869e))
|
||||
* **animations:** ensure web-animations are caught within the Angular zone ([f80a157](https://github.com/angular/angular/commit/f80a157)), closes [#11881](https://github.com/angular/angular/issues/11881) [#11712](https://github.com/angular/angular/issues/11712) [#12355](https://github.com/angular/angular/issues/12355) [#11881](https://github.com/angular/angular/issues/11881) [#12546](https://github.com/angular/angular/issues/12546) [#12707](https://github.com/angular/angular/issues/12707) [#12774](https://github.com/angular/angular/issues/12774)
|
||||
* **common:** `NgSwitch` - don’t create the default case if another case matches ([#12726](https://github.com/angular/angular/issues/12726)) ([d8f23f4](https://github.com/angular/angular/commit/d8f23f4)), closes [#11297](https://github.com/angular/angular/issues/11297) [#9420](https://github.com/angular/angular/issues/9420)
|
||||
* **common:** I18nSelectPipe selects other case on default ([4708b24](https://github.com/angular/angular/commit/4708b24))
|
||||
* **common:** no TZ Offset added by DatePipe for dates without time ([#12380](https://github.com/angular/angular/issues/12380)) ([2aba8b0](https://github.com/angular/angular/commit/2aba8b0))
|
||||
* **common:** NgClass should throw a descriptive error when CSS class is not a string ([#12662](https://github.com/angular/angular/issues/12662)) ([f3793b5](https://github.com/angular/angular/commit/f3793b5)), closes [#12586](https://github.com/angular/angular/issues/12586)
|
||||
* **common:** DatePipe should handle empty string ([#12374](https://github.com/angular/angular/issues/12374)) ([3dc6177](https://github.com/angular/angular/commit/3dc6177))
|
||||
* **compiler:** don't convert undefined to null literals ([#11503](https://github.com/angular/angular/issues/11503)) ([f0cdb42](https://github.com/angular/angular/commit/f0cdb42)), closes [#11493](https://github.com/angular/angular/issues/11493)
|
||||
* **compiler:** generate safe access strictNullChecks compatible code ([#12800](https://github.com/angular/angular/issues/12800)) ([a965d11](https://github.com/angular/angular/commit/a965d11)), closes [#12795](https://github.com/angular/angular/issues/12795)
|
||||
* **compiler:** support more than 9 interpolations ([#12710](https://github.com/angular/angular/issues/12710)) ([22c021c](https://github.com/angular/angular/commit/22c021c)), closes [#10253](https://github.com/angular/angular/issues/10253)
|
||||
* **compiler:** use the other case by default in ICU messages ([55dc0e4](https://github.com/angular/angular/commit/55dc0e4))
|
||||
* **compiler-cli:** suppress closure compiler suspiciousCode check in codegen ([#12666](https://github.com/angular/angular/issues/12666)) ([7103754](https://github.com/angular/angular/commit/7103754))
|
||||
* **compiler-cli:** suppress two more closure compiler checks in codegen ([#12698](https://github.com/angular/angular/issues/12698)) ([77cbf7f](https://github.com/angular/angular/commit/77cbf7f))
|
||||
* **core:** allow to query content of templates that are stamped out at a different place ([f2bbef3](https://github.com/angular/angular/commit/f2bbef3)), closes [#12283](https://github.com/angular/angular/issues/12283) [#12094](https://github.com/angular/angular/issues/12094)
|
||||
* **core:** apply host attributes to root elements ([#12761](https://github.com/angular/angular/issues/12761)) ([ad3bf6c](https://github.com/angular/angular/commit/ad3bf6c)), closes [#12744](https://github.com/angular/angular/issues/12744)
|
||||
* **core:** ensure that component views that have no bindings recurse into nested components / view containers. ([051d748](https://github.com/angular/angular/commit/051d748))
|
||||
* **core:** fix pseudo-selector shimming ([#12754](https://github.com/angular/angular/issues/12754)) ([acbf1d8](https://github.com/angular/angular/commit/acbf1d8)), closes [#12730](https://github.com/angular/angular/issues/12730) [#12354](https://github.com/angular/angular/issues/12354)
|
||||
* **forms:** check if registerOnValidatorChange exists on validator before trying to invoke it ([#12801](https://github.com/angular/angular/issues/12801)) ([ef88147](https://github.com/angular/angular/commit/ef88147)), closes [#12593](https://github.com/angular/angular/issues/12593)
|
||||
* **forms:** getRawValue returns any instead of Object ([#12599](https://github.com/angular/angular/issues/12599)) ([09092ac](https://github.com/angular/angular/commit/09092ac))
|
||||
* **http:** preserve header case when copying headers ([#12697](https://github.com/angular/angular/issues/12697)) ([121e508](https://github.com/angular/angular/commit/121e508))
|
||||
* **router:** advance a route only after its children have been deactivated ([#12676](https://github.com/angular/angular/issues/12676)) ([9ddf9b3](https://github.com/angular/angular/commit/9ddf9b3)), closes [#11715](https://github.com/angular/angular/issues/11715)
|
||||
* **router:** avoid router initialization for non root components ([2a4bf9a](https://github.com/angular/angular/commit/2a4bf9a)), closes [#12338](https://github.com/angular/angular/issues/12338) [#12814](https://github.com/angular/angular/issues/12814)
|
||||
* **router:** check if windows.console exists before using it ([#12348](https://github.com/angular/angular/issues/12348)) ([7886561](https://github.com/angular/angular/commit/7886561))
|
||||
* **router:** correctly export concatMap operator in es5 ([#12430](https://github.com/angular/angular/issues/12430)) ([e25baa0](https://github.com/angular/angular/commit/e25baa0))
|
||||
* **router:** do not require the creation of empty-path routes when no url left ([2c11093](https://github.com/angular/angular/commit/2c11093)), closes [#12133](https://github.com/angular/angular/issues/12133)
|
||||
* **router:** ignore null or undefined query parameters ([#12333](https://github.com/angular/angular/issues/12333)) ([3052fb2](https://github.com/angular/angular/commit/3052fb2))
|
||||
* **router:** incorrect injector is used when instantiating components loaded lazily ([#12817](https://github.com/angular/angular/issues/12817)) ([52be848](https://github.com/angular/angular/commit/52be848))
|
||||
* **router:** resolve guard observables on the first emit ([#10412](https://github.com/angular/angular/issues/10412)) ([2e78b76](https://github.com/angular/angular/commit/2e78b76))
|
||||
* **router:** Route.isActive also compares query params ([#12321](https://github.com/angular/angular/issues/12321)) ([785b7b6](https://github.com/angular/angular/commit/785b7b6))
|
||||
* **router:** router should not swallow "unhandled" errors ([e5a753e](https://github.com/angular/angular/commit/e5a753e)), closes [#12802](https://github.com/angular/angular/issues/12802)
|
||||
* **router:** throw an error when encounter undefined route ([#12389](https://github.com/angular/angular/issues/12389)) ([77dc1ab](https://github.com/angular/angular/commit/77dc1ab))
|
||||
* **platform-browser:** enableDebugTools should create AngularTools by merging into context.ng ([#12003](https://github.com/angular/angular/issues/12003)) ([b2cf379](https://github.com/angular/angular/commit/b2cf379)), closes [#12002](https://github.com/angular/angular/issues/12002)
|
||||
* **platform-browser:** provide the ability to register global hammer.js events ([768cddb](https://github.com/angular/angular/commit/768cddb)), closes [#12797](https://github.com/angular/angular/issues/12797)
|
||||
* **tsc-wrapped:** harden collector against invalid asts ([#12793](https://github.com/angular/angular/issues/12793)) ([69f87ca](https://github.com/angular/angular/commit/69f87ca))
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0-rc.0"></a>
|
||||
# [2.2.0-rc.0](https://github.com/angular/angular/compare/2.2.0-beta.1...2.2.0-rc.0) (2016-11-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** dedupe NgModule declarations, … ([a178bc6](https://github.com/angular/angular/commit/a178bc6))
|
||||
* **compiler:** don’t double bind functions ([e391cac](https://github.com/angular/angular/commit/e391cac))
|
||||
* **compiler:** Don’t throw on empty property bindings ([642c1db](https://github.com/angular/angular/commit/642c1db)), closes [#12583](https://github.com/angular/angular/issues/12583)
|
||||
* **compiler:** support multiple components in a view container ([6fda972](https://github.com/angular/angular/commit/6fda972))
|
||||
* **core:** improve error when multiple components match the same element ([e9fd864](https://github.com/angular/angular/commit/e9fd864)), closes [#7067](https://github.com/angular/angular/issues/7067)
|
||||
* **router:** call data observers when the path changes ([1de04b2](https://github.com/angular/angular/commit/1de04b2))
|
||||
* **router:** CanDeactivate receives a wrong component ([830a780](https://github.com/angular/angular/commit/830a780)), closes [#12592](https://github.com/angular/angular/issues/12592)
|
||||
* **router:** rerun resolvers when url changes ([fe47e6b](https://github.com/angular/angular/commit/fe47e6b)), closes [#12603](https://github.com/angular/angular/issues/12603)
|
||||
* **router:** reset URL to the stable state when a navigation gets canceled ([d509ee0](https://github.com/angular/angular/commit/d509ee0)), closes [#10321](https://github.com/angular/angular/issues/10321)
|
||||
* **router:** routerLink should not prevent default on non-link elements ([8e221b8](https://github.com/angular/angular/commit/8e221b8))
|
||||
* **router:** run navigations serially ([091c390](https://github.com/angular/angular/commit/091c390)), closes [#11754](https://github.com/angular/angular/issues/11754)
|
||||
* **upgrade:** silent bootstrap failures ([fa93fd6](https://github.com/angular/angular/commit/fa93fd6)), closes [#12062](https://github.com/angular/angular/issues/12062)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add the find method to QueryList ([7c16ef9](https://github.com/angular/angular/commit/7c16ef9))
|
||||
|
||||
|
||||
|
||||
<a name="2.1.2"></a>
|
||||
# [2.1.2](https://github.com/angular/angular/compare/2.1.1...2.1.2) (2016-10-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** don't access view local variables nor pipes in host expressions ([#12396](https://github.com/angular/angular/issues/12396)) ([867494a](https://github.com/angular/angular/commit/867494a)), closes [#12004](https://github.com/angular/angular/issues/12004) [#12071](https://github.com/angular/angular/issues/12071)
|
||||
* **compiler:** walk third party modules ([#12453](https://github.com/angular/angular/issues/12453)) ([a838aba](https://github.com/angular/angular/commit/a838aba)), closes [#11889](https://github.com/angular/angular/issues/11889) [#12428](https://github.com/angular/angular/issues/12428)
|
||||
* **compiler:** remove double exports of template_ast ([7742ec0](https://github.com/angular/angular/commit/7742ec0))
|
||||
* **compiler:** use Maps instead of objects in selector implementation ([d321b0e](https://github.com/angular/angular/commit/d321b0e))
|
||||
* **compiler-cli:** fix types ([ef15364](https://github.com/angular/angular/commit/ef15364))
|
||||
* **compiler-cli:** assert that all pipes and directives are declared by a module ([7221632](https://github.com/angular/angular/commit/7221632))
|
||||
* **http:** overwrite already set xsrf header ([b4265e0](https://github.com/angular/angular/commit/b4265e0))
|
||||
* **router:** add a test to make sure canDeactivate guards are called for aux routes ([fc60fa7](https://github.com/angular/angular/commit/fc60fa7)), closes [#11345](https://github.com/angular/angular/issues/11345)
|
||||
* **router:** canDeactivate guards are not triggered for componentless routes ([b741853](https://github.com/angular/angular/commit/b741853)), closes [#12375](https://github.com/angular/angular/issues/12375)
|
||||
* **router:** change router not to deactivate aux routes when navigating from a componentless routes ([52a853e](https://github.com/angular/angular/commit/52a853e))
|
||||
* **router:** disallow component routes with named outlets ([8f2fa0f](https://github.com/angular/angular/commit/8f2fa0f)), closes [#11208](https://github.com/angular/angular/issues/11208) [#11082](https://github.com/angular/angular/issues/11082)
|
||||
* **router:** preserve resolve data ([6ccbfd4](https://github.com/angular/angular/commit/6ccbfd4)), closes [#12306](https://github.com/angular/angular/issues/12306)
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0-beta.1"></a>
|
||||
# [2.2.0-beta.1](https://github.com/angular/angular/compare/2.2.0-beta.0...2.2.0-beta.1) (2016-10-27)
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **upgrade:** re-export the new static upgrade APIs on new entry ([a26dd28](https://github.com/angular/angular/commit/a26dd28))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** export routerLinkActive w/ isActive property ([c9f58cf](https://github.com/angular/angular/commit/c9f58cf))
|
||||
|
||||
|
||||
### BREAKING CHANGES (only for beta version users)
|
||||
|
||||
* upgrade: Four newly added APIs in 2.2.0-beta:
|
||||
downgradeComponent, downgradeInjectable, UpgradeComponent, and UpgradeModule are no longer exported by @angular/upgrade.
|
||||
Import these from @angular/upgrade/static instead.
|
||||
|
||||
|
||||
Note: The 2.2.0-beta.1 release also contains all the changes present in the 2.1.2 release.
|
||||
|
||||
|
||||
|
||||
# [2.1.1](https://github.com/angular/angular/compare/2.1.0...2.1.1) (2016-10-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** generate aot code for animation trigger output events ([#12291](https://github.com/angular/angular/issues/12291)) ([6e5f8b5](https://github.com/angular/angular/commit/6e5f8b5)), closes [#11707](https://github.com/angular/angular/issues/11707)
|
||||
* **compiler:** don't redeclare a var in the same scope ([#12386](https://github.com/angular/angular/issues/12386)) ([cca4a5c](https://github.com/angular/angular/commit/cca4a5c))
|
||||
* **core:** fix decorator default values ([bd1dcb5](https://github.com/angular/angular/commit/bd1dcb5))
|
||||
* **core:** fix property decorators ([3993279](https://github.com/angular/angular/commit/3993279)), closes [#12224](https://github.com/angular/angular/issues/12224)
|
||||
* **http:** make normalizeMethodName optimizer-compatible. ([#12370](https://github.com/angular/angular/issues/12370)) ([8409b65](https://github.com/angular/angular/commit/8409b65))
|
||||
* **router:** correctly export filter operator in es5 ([#12286](https://github.com/angular/angular/issues/12286)) ([27d7677](https://github.com/angular/angular/commit/27d7677))
|
||||
* **router:** do not update primary route if only secondary outlet is given ([#11797](https://github.com/angular/angular/issues/11797)) ([da5fc69](https://github.com/angular/angular/commit/da5fc69))
|
||||
* **router:** fix lazy loading triggered by redirects from wildcard routes ([5ae6915](https://github.com/angular/angular/commit/5ae6915)), closes [#12183](https://github.com/angular/angular/issues/12183)
|
||||
* **router:** module loader should start compiling modules when stubbedModules are set ([#11742](https://github.com/angular/angular/issues/11742)) ([b44b6ef](https://github.com/angular/angular/commit/b44b6ef))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **common:** optimize NgSwitch default case ([fdf4309](https://github.com/angular/angular/commit/fdf4309))
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0-beta.0"></a>
|
||||
# [2.2.0-beta.0](https://github.com/angular/angular/compare/2.1.0...2.2.0-beta.0) (2016-10-20)
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** support narrow forms for month and weekdays in DatePipe ([#12297](https://github.com/angular/angular/issues/12297)) ([f77ab6a](https://github.com/angular/angular/commit/f77ab6a)), closes [#12294](https://github.com/angular/angular/issues/12294)
|
||||
* **forms:** add hasError and getError to AbstractControlDirective ([#11985](https://github.com/angular/angular/issues/11985)) ([592f40a](https://github.com/angular/angular/commit/592f40a)), closes [#7255](https://github.com/angular/angular/issues/7255)
|
||||
* **forms:** add ng-pending CSS class during async validation ([#11243](https://github.com/angular/angular/issues/11243)) ([97bc971](https://github.com/angular/angular/commit/97bc971)), closes [#10336](https://github.com/angular/angular/issues/10336)
|
||||
* **forms:** Added emitEvent to AbstractControl methods ([#11949](https://github.com/angular/angular/issues/11949)) ([b9fc090](https://github.com/angular/angular/commit/b9fc090))
|
||||
* **forms:** make 'parent' a public property of 'AbstractControl' ([#11855](https://github.com/angular/angular/issues/11855)) ([445e592](https://github.com/angular/angular/commit/445e592))
|
||||
* **forms:** Validator.pattern accepts a RegExp ([#12323](https://github.com/angular/angular/issues/12323)) ([bf60418](https://github.com/angular/angular/commit/bf60418))
|
||||
* **upgrade:** add support for AoT compiled upgrade applications ([d6791ff](https://github.com/angular/angular/commit/d6791ff)), closes [#12239](https://github.com/angular/angular/issues/12239)
|
||||
* **router:** add support for ng1/ng2 migration ([#12160](https://github.com/angular/angular/issues/12160)) ([8b9ab44](https://github.com/angular/angular/commit/8b9ab44))
|
||||
|
||||
Note: The 2.2.0-beta.0 release also contains all the changes present in the 2.1.1 release.
|
||||
|
||||
|
||||
|
||||
<a name="2.1.0"></a>
|
||||
# [2.1.0 incremental-metamorphosis](https://github.com/angular/angular/compare/2.1.0-rc.0...2.1.0) (2016-10-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** allow whitespace as `<ng-content>` content ([#12225](https://github.com/angular/angular/issues/12225)) ([df1718d](https://github.com/angular/angular/commit/df1718d))
|
||||
* **compiler:** interpolation expressions report the correct offset ([#12125](https://github.com/angular/angular/issues/12125)) ([d641c36](https://github.com/angular/angular/commit/d641c36))
|
||||
* **compiler:** properly shim `:host:before` and `:host(:before)` ([#12171](https://github.com/angular/angular/issues/12171)) ([aa92512](https://github.com/angular/angular/commit/aa92512)), closes [#12165](https://github.com/angular/angular/issues/12165)
|
||||
* **compiler:** validate `@HostBinding` name ([#12139](https://github.com/angular/angular/issues/12139)) ([13ecc14](https://github.com/angular/angular/commit/13ecc14))
|
||||
* **compiler-cli:** don't clone static symbols when simplifying annotation metadata ([#12158](https://github.com/angular/angular/issues/12158)) ([8c477b2](https://github.com/angular/angular/commit/8c477b2))
|
||||
* **compiler-cli:** remove peerDependency on [@angular](https://github.com/angular)/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||
* **compiler-cli:** remove unused parse5 dependency from package.json ([eaaec69](https://github.com/angular/angular/commit/eaaec69))
|
||||
* **forms:** allow optional fields with pattern and minlength validators ([#12147](https://github.com/angular/angular/issues/12147)) ([d22eeb7](https://github.com/angular/angular/commit/d22eeb7))
|
||||
* **forms:** properly validate blank strings with minlength ([#12091](https://github.com/angular/angular/issues/12091)) ([f50c1da](https://github.com/angular/angular/commit/f50c1da))
|
||||
* **http:** fix Headers initialization from Headers and Object ([#12106](https://github.com/angular/angular/issues/12106)) ([f4566f8](https://github.com/angular/angular/commit/f4566f8))
|
||||
* **http:** Headers.append should append to the list ([a67c067](https://github.com/angular/angular/commit/a67c067))
|
||||
* **platform-browser-dynamic:** mark platformBrowserDynamic as stable API ([#12154](https://github.com/angular/angular/issues/12154)) ([bcef5ef](https://github.com/angular/angular/commit/bcef5ef))
|
||||
* **router:** improve error message ([#12102](https://github.com/angular/angular/issues/12102)) ([e06303a](https://github.com/angular/angular/commit/e06303a))
|
||||
* **router:** parent resolve should complete before merging resolved data ([1681e4f](https://github.com/angular/angular/commit/1681e4f)), closes [#12032](https://github.com/angular/angular/issues/12032)
|
||||
* **router:** wildcards routes should support lazy loading ([40b92dd](https://github.com/angular/angular/commit/40b92dd)), closes [#12024](https://github.com/angular/angular/issues/12024)
|
||||
* **upgrade:** allow compilerOptions in bootstrap ([#10575](https://github.com/angular/angular/issues/10575)) ([5effc33](https://github.com/angular/angular/commit/5effc33))
|
||||
|
||||
|
||||
|
||||
<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.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.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` and `@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/commit/826c98e))
|
||||
* **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))
|
||||
* **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.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)
|
||||
# [2.0.0 proprioception-reinforcement](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@ -212,7 +796,7 @@
|
||||
use `Type<any>` in place of `Type`.
|
||||
|
||||
We don't expect that any user applications use the `Type` type directly.
|
||||
|
||||
|
||||
* core: Previously inconsistently named APIs SanitizationService and DomSanitizationService were renamed to Sanitizer and DomSanitizer
|
||||
|
||||
* core: previously deprecated @Component.directives and @Component.pipes support was removed.
|
||||
@ -226,11 +810,11 @@ use `Type<any>` in place of `Type`.
|
||||
* core: deprecated ComponentResolver was removed. Please use ComponentFactoryResolver instead.
|
||||
|
||||
* core: animations defined using an at-symbol prefix that are not property bound are now invalid.
|
||||
|
||||
|
||||
```html
|
||||
<!-- this is now invalid -->
|
||||
<div @flip="flipState"></div>
|
||||
|
||||
|
||||
<!-- change that to -->
|
||||
<div [@flip]="flipState"></div>
|
||||
```
|
||||
@ -256,7 +840,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
```
|
||||
{provide: MyClass, useFactory: ...}
|
||||
```
|
||||
|
||||
|
||||
* core: previously deprecated NgZoneError has been removed
|
||||
|
||||
* core: Exceptions are no longer part of the public API. We don't expect that anyone should be referring to the Exception types.
|
||||
@ -268,7 +852,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
```
|
||||
ErrorHandler.handleError(error: any): void;
|
||||
```
|
||||
|
||||
|
||||
* core: deprecated DynamicComponentLoader was removed; see deprecation notice for migration instructions.
|
||||
|
||||
* core: deprecated SystemJsComponentResolver and SystemJsCmpFactoryResolver have been removed.
|
||||
@ -302,13 +886,13 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
* platform-browser-dynamic: `CACHED_TEMPLATE_PROVIDER` is now renamed to `RESOURCE_CACHE_PROVIDER`
|
||||
|
||||
Before:
|
||||
|
||||
|
||||
```js
|
||||
import {CACHED_TEMPLATE_PROVIDER} from '@angular/platform-browser-dynamic';
|
||||
```
|
||||
|
||||
|
||||
After:
|
||||
|
||||
|
||||
```js
|
||||
import {RESOURCE_CACHE_PROVIDER} from '@angular/platform-browser-dynamic';
|
||||
```
|
||||
@ -326,7 +910,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
* webworkers: web worker platform is now exported via separate packages.
|
||||
|
||||
Please use @angular/platform-webworker and @angular/platform-webworker-dynamic
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-rc.5"></a>
|
||||
@ -520,7 +1104,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
||||
* core:
|
||||
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
|
||||
created during bootstrap instead by calling `NgModuleRef.destroy`.
|
||||
- `AplicationRef.registerDisposeListener` is deprecated.
|
||||
- `ApplicationRef.registerDisposeListener` is deprecated.
|
||||
Use the `ngOnDestroy` lifecycle hook for providers or
|
||||
`NgModuleRef.onDestroy` instead.
|
||||
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Contributing to Angular 2
|
||||
# Contributing to Angular
|
||||
|
||||
We would love for you to contribute to Angular 2 and help make it even better than it is
|
||||
We would love for you to contribute to Angular and help make it even better than it is
|
||||
today! As a contributor, here are the guidelines we would like you to follow:
|
||||
|
||||
- [Code of Conduct](#coc)
|
||||
@ -17,19 +17,27 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
||||
|
||||
## <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]
|
||||
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].
|
||||
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](https://stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
StackOverflow is a much better place to ask questions since:
|
||||
|
||||
- there are thousands of people willing to help on StackOverflow
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
- StackOverflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time we will be systematically closing all the issues that are requests for general support and redirecting people to StackOverflow.
|
||||
|
||||
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
||||
|
||||
## <a name="issue"></a> Found a Bug?
|
||||
If you find a bug in the source code, you can help us by
|
||||
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
||||
[submit a Pull Request](#submit-pr) with a fix.
|
||||
|
||||
## <a name="feature"></a> Want a Feature?
|
||||
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
|
||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
||||
and we are not ready to accept major contributions ahead of the full release.
|
||||
## <a name="feature"></a> Missing a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
|
||||
Repository. 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.
|
||||
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
|
||||
@ -40,24 +48,22 @@ and help you to craft the change so that it is successfully accepted into the pr
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
Before you submit an issue, search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
|
||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
|
||||
|
||||
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
|
||||
- version of Angular used
|
||||
- 3rd-party libraries and their versions
|
||||
- and most importantly - a use-case that fails
|
||||
|
||||
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.
|
||||
|
||||
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||
|
||||
Unfortunately we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.
|
||||
|
||||
You can file new issues by filling out our [new issue form](https://github.com/angular/angular/issues/new).
|
||||
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
@ -95,7 +101,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular 2 test suites to ensure tests are still passing.
|
||||
* Re-run the Angular test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
@ -238,7 +244,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
[github]: https://github.com/angular/angular
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
|
||||
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
||||
[jsfiddle]: http://jsfiddle.net
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[runnable]: http://runnable.com
|
||||
|
48
DEVELOPER.md
48
DEVELOPER.md
@ -1,6 +1,6 @@
|
||||
# Building and Testing Angular 2 for JS
|
||||
# Building and Testing Angular
|
||||
|
||||
This document describes how to set up your development environment to build and test Angular 2 JS version.
|
||||
This document describes how to set up your development environment to build and test Angular.
|
||||
It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||
|
||||
* [Prerequisite Software](#prerequisite-software)
|
||||
@ -74,6 +74,15 @@ use in these instructions.
|
||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||
|
||||
## Installing Bower Modules
|
||||
|
||||
Now run `bower` to install additional dependencies:
|
||||
|
||||
```shell
|
||||
# Install other Angular project dependencies (bower.json)
|
||||
bower install
|
||||
```
|
||||
|
||||
## Windows only
|
||||
|
||||
In order to create the right symlinks, run **as administrator**:
|
||||
@ -114,7 +123,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.
|
||||
|
||||
- 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
|
||||
|
||||
@ -124,9 +133,10 @@ If you happen to modify the public API of Angular, API golden files must be upda
|
||||
$ gulp public-api:update
|
||||
```
|
||||
|
||||
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
|
||||
Note: The command `gulp public-api:enforce` fails when the API doesn't match the golden files. Make sure to rebuild
|
||||
the project before trying to verify after an API change.
|
||||
|
||||
## Formatting your source code
|
||||
## <a name="clang-format"></a> Formatting your source code
|
||||
|
||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
@ -137,4 +147,32 @@ You can automatically format your code by running:
|
||||
$ gulp format
|
||||
```
|
||||
|
||||
## Linting/verifying your source code
|
||||
|
||||
You can check that your code is properly formatted and adheres to coding style by running:
|
||||
|
||||
``` shell
|
||||
$ gulp lint
|
||||
```
|
||||
|
||||
## Publishing your own personal snapshot build
|
||||
|
||||
You may find that your un-merged change needs some validation from external participants.
|
||||
Rather than requiring them to pull your Pull Request and build Angular locally, you can
|
||||
publish the `*-builds` snapshots just like our Travis build does.
|
||||
|
||||
First time, you need to create the github repositories:
|
||||
|
||||
``` shell
|
||||
$ export TOKEN=[get one from https://github.com/settings/tokens]
|
||||
$ CREATE_REPOS=1 ./scripts/publish/publish-build-artifacts.sh [github username]
|
||||
```
|
||||
|
||||
For subsequent snapshots, just run
|
||||
|
||||
``` shell
|
||||
$ ./scripts/publish/publish-build-artifacts.sh [github username]
|
||||
```
|
||||
|
||||
The script will publish the build snapshot to a branch with the same name as your current branch,
|
||||
and create it if it doesn't exist.
|
||||
|
@ -4,7 +4,6 @@
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||
|
62
SAVED_REPLIES.md
Normal file
62
SAVED_REPLIES.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Saved Responses for Angular's Issue Tracker
|
||||
|
||||
The following are canned responses that the Angular team should use to close issues on our issue tracker that fall into the listed resolution categories.
|
||||
|
||||
Since GitHub currently doesn't allow us to have a repository-wide or organization-wide list of [saved replies](https://help.github.com/articles/working-with-saved-replies/), these replies need to be maintained by individual team members. Since the responses can be modified in the future, all responses are versioned to simplify the process of keeping the responses up to date.
|
||||
|
||||
|
||||
## Angular: Already Fixed (v1)
|
||||
```
|
||||
Thanks for reporting this issue. Luckily it has already been fixed in one of the recent releases. Please update to the most recent version to resolve the problem.
|
||||
|
||||
If after upgrade the problem still exists in your application please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||
```
|
||||
|
||||
## Angular: Don't Understand (v1)
|
||||
```
|
||||
I'm sorry but we don't understand the problem you are reporting.
|
||||
|
||||
If the problem still exists please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||
```
|
||||
|
||||
|
||||
## Angular: Duplicate (v1)
|
||||
```
|
||||
Thanks for reporting this issue. However this issue is a duplicate of an existing issue #<ISSUE_NUMBER>. Please subscribe to that issue for future updates.
|
||||
```
|
||||
|
||||
|
||||
## Angular: Insufficient Information Provided (v1)
|
||||
```
|
||||
Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information.
|
||||
|
||||
If the problem still persists, please file a new issue and ensure you provide all of the required information when filling out the issue template.
|
||||
```
|
||||
|
||||
## Angular: Issue Outside of Angular (v1)
|
||||
```
|
||||
I'm sorry but this issue is not caused by Angular. Please contact the author(s) of project <PROJECT NAME> or file issue on their issue tracker.
|
||||
```
|
||||
|
||||
|
||||
## Angular: Non-reproducible (v1)
|
||||
```
|
||||
I'm sorry but we can't reproduce the problem following the instructions you provided.
|
||||
|
||||
If the problem still exists please open a new issue following [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue).
|
||||
```
|
||||
|
||||
## Angular: Obsolete (v1)
|
||||
```
|
||||
Thanks for reporting this issue. This issue is now obsolete due to changes in the recent releases. Please update to the most recent Angular version.
|
||||
|
||||
If the problem still persists, please file a new issue and ensure you provide the version of Angular affected and include the steps to reproduce the problem when filling out the issue template.
|
||||
```
|
||||
|
||||
|
||||
## Angular: Support Request (v1)
|
||||
```
|
||||
Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](http://stackoverflow.com/) using tag `angular`.
|
||||
|
||||
If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-got-a-question-or-problem).
|
||||
```
|
@ -1,7 +1,7 @@
|
||||
# Triage Process and Github Labels for Angular 2
|
||||
|
||||
This document describes how the Angular team uses labels and milestones
|
||||
to triage issues on github. The basic idea of the new process is that
|
||||
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.
|
||||
@ -17,26 +17,27 @@ with it.
|
||||
|
||||
* `comp: animations`: `@matsko`
|
||||
* `comp: benchpress`: `@tbosch`
|
||||
* `comp: build/ci`: `@IgorMinar` -- All build and CI scripts
|
||||
* `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
|
||||
* `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: language service`: `@chuckjaz`
|
||||
* `comp: metadata-extractor`: `@chuckjaz`
|
||||
* `comp: router`: `@vsavkin`
|
||||
* `comp: router`: `@vicb`
|
||||
* `comp: testing`: `@juliemr`
|
||||
* `comp: upgrade`: `@mhevery`
|
||||
* `comp: web-worker`: `@vicb`
|
||||
* `comp: zone`: `@mhevery`
|
||||
* `comp: zones`: `@mhevery`
|
||||
|
||||
There are few components which are cross-cutting. They don't have
|
||||
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: documentation`: `@naomiblack`
|
||||
* `comp: packaging`: `@mhevery`
|
||||
* `comp: docs`: `@naomiblack`
|
||||
* `comp: packaging`: `@IgorMinar`
|
||||
* `comp: performance`: `@tbosch`
|
||||
* `comp: security`: `@IgorMinar`
|
||||
|
||||
@ -53,11 +54,11 @@ What kind of problem is this?
|
||||
|
||||
## Caretaker Triage Process
|
||||
|
||||
It is the caretaker's responsibility to assign `comp: *` and `type: *`
|
||||
to each new issue as they come in. The reason why we limit the
|
||||
responsibility of the caretaker to these two labels is that it is
|
||||
unlikely that without domain knowledge the caretaker could add any
|
||||
additional labels of value.
|
||||
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
|
||||
@ -68,11 +69,37 @@ 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`
|
||||
|
||||
|
||||
### Assigning Issues to Milestones
|
||||
|
||||
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.
|
||||
|
||||
We aim to only have at most three milestones open at a time:
|
||||
|
@ -1,220 +1,102 @@
|
||||
// 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 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 = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { 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}},
|
||||
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
||||
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
||||
'ChromeBeta': { unitTest: {target: null, required: true}, 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}},
|
||||
'FirefoxDev': { unitTest: {target: null, required: true}, 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}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, 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.2': { 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}},
|
||||
'Android5': { unitTest: {target: 'SL', 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}},
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
'ChromeBeta': {unitTest: {target: null, required: true}, 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}},
|
||||
'FirefoxDev': {unitTest: {target: null, required: true}, 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}},
|
||||
'IE11': {unitTest: {target: 'SL', required: true}, 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.2': {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}},
|
||||
'Android5': {unitTest: {target: 'SL', 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}},
|
||||
'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS10': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
var customLaunchers = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] },
|
||||
'ChromeNoSandbox': {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox'] },
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '52'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'beta'
|
||||
},
|
||||
'SL_CHROMEDEV': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'dev'
|
||||
},
|
||||
'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'
|
||||
},
|
||||
'DartiumWithWebPlatform':
|
||||
{base: 'Dartium', flags: ['--enable-experimental-web-platform-features']},
|
||||
'ChromeNoSandbox': {base: 'Chrome', flags: ['--no-sandbox']},
|
||||
'SL_CHROME': {base: 'SauceLabs', browserName: 'chrome', version: '54'},
|
||||
'SL_CHROMEBETA': {base: 'SauceLabs', browserName: 'chrome', version: 'beta'},
|
||||
'SL_CHROMEDEV': {base: 'SauceLabs', browserName: 'chrome', version: 'dev'},
|
||||
'SL_FIREFOX': {base: 'SauceLabs', browserName: 'firefox', version: '50'},
|
||||
'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_SAFARI10':
|
||||
{base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.12', version: '10.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_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
|
||||
'SL_IE9':
|
||||
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
|
||||
'SL_IE10': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2012',
|
||||
version: '10'
|
||||
},
|
||||
'SL_IE11': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
'SL_IE11':
|
||||
{base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
|
||||
'SL_EDGE': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'MicrosoftEdge',
|
||||
platform: 'Windows 10',
|
||||
version: '13.10586'
|
||||
},
|
||||
'SL_ANDROID4.1': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.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'
|
||||
},
|
||||
'SL_ANDROID4.1': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.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': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
os: 'OS X',
|
||||
os_version: 'Yosemite'
|
||||
},
|
||||
'BS_FIREFOX': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'firefox',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'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_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
|
||||
'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
|
||||
'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_SAFARI10': {base: 'BrowserStack', browser: 'safari', os: 'OS X', os_version: 'Sierra'},
|
||||
'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_IOS10': {base: 'BrowserStack', device: 'iPhone SE', os: 'ios', os_version: '10.0'},
|
||||
'BS_IE9':
|
||||
{base: 'BrowserStack', browser: 'ie', browser_version: '9.0', os: 'Windows', os_version: '7'},
|
||||
'BS_IE10': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '10.0',
|
||||
browser_version: '10.1',
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
@ -225,58 +107,35 @@ var customLaunchers = {
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_EDGE': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'edge',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_WINDOWSPHONE' : {
|
||||
base: 'BrowserStack',
|
||||
device: 'Nokia Lumia 930',
|
||||
os: 'winphone',
|
||||
os_version: '8.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'
|
||||
}
|
||||
'BS_EDGE': {base: 'BrowserStack', browser: 'edge', os: 'Windows', os_version: '10'},
|
||||
'BS_WINDOWSPHONE':
|
||||
{base: 'BrowserStack', device: 'Nokia Lumia 930', os: 'winphone', os_version: '8.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 = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {
|
||||
return customLaunchers[item].base == 'SauceLabs';
|
||||
}),
|
||||
'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'],
|
||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
|
||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||
@ -284,13 +143,20 @@ var sauceAliases = {
|
||||
};
|
||||
|
||||
var browserstackAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
|
||||
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {
|
||||
return customLaunchers[item].base == 'BrowserStack';
|
||||
}),
|
||||
'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'],
|
||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||
};
|
||||
@ -303,11 +169,9 @@ module.exports = {
|
||||
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => {
|
||||
return target + '_' + item.toUpperCase();
|
||||
});
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => target + '_' + item.toUpperCase());
|
||||
}
|
||||
|
84
build.sh
84
build.sh
@ -14,12 +14,17 @@ PACKAGES=(core
|
||||
platform-webworker
|
||||
platform-webworker-dynamic
|
||||
http
|
||||
router
|
||||
upgrade
|
||||
router
|
||||
compiler-cli
|
||||
language-service
|
||||
benchpress)
|
||||
BUILD_ALL=true
|
||||
BUNDLE=true
|
||||
VERSION_PREFIX=$(node -p "require('./package.json').version")
|
||||
VERSION_SUFFIX="-$(git log --oneline -1 | awk '{print $1}')"
|
||||
ROUTER_VERSION_PREFIX=$(node -p "require('./package.json').version.replace(/^2/, '3')")
|
||||
REMOVE_BENCHPRESS=false
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
@ -31,6 +36,10 @@ for ARG in "$@"; do
|
||||
--bundle=*)
|
||||
BUNDLE=( "${ARG#--bundle=}" )
|
||||
;;
|
||||
--publish)
|
||||
VERSION_SUFFIX=""
|
||||
REMOVE_BENCHPRESS=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $ARG."
|
||||
exit 1
|
||||
@ -38,6 +47,10 @@ for ARG in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
VERSION="${VERSION_PREFIX}${VERSION_SUFFIX}"
|
||||
ROUTER_VERSION="${ROUTER_VERSION_PREFIX}${VERSION_SUFFIX}"
|
||||
echo "====== BUILDING: Version ${VERSION} (Router ${ROUTER_VERSION})"
|
||||
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||
@ -63,10 +76,11 @@ if [[ ${BUILD_ALL} == true ]]; then
|
||||
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/base64-js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
ln -s ../../../../node_modules/hammerjs/hammer.js .
|
||||
cd -
|
||||
|
||||
echo "====== Copying files needed for benchmarks ====="
|
||||
@ -78,7 +92,6 @@ if [[ ${BUILD_ALL} == true ]]; then
|
||||
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 .
|
||||
@ -101,14 +114,29 @@ do
|
||||
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
|
||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
|
||||
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
|
||||
UMD_UPGRADE_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.js
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||
UMD_STATIC_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.min.js
|
||||
UMD_UPGRADE_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}-upgrade.umd.min.js
|
||||
|
||||
if [[ ${PACKAGE} != router ]]; then
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||
fi
|
||||
if [[ ${PACKAGE} == router ]]; then
|
||||
LICENSE_BANNER=${PWD}/modules/@angular/router-license-banner.txt
|
||||
fi
|
||||
|
||||
rm -rf ${DESTDIR}
|
||||
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||
|
||||
if [[ -e ${SRCDIR}/tsconfig-upgrade.json ]]; then
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-upgrade.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-upgrade.json
|
||||
fi
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||
|
||||
@ -117,6 +145,11 @@ do
|
||||
$TSC -p ${SRCDIR}/tsconfig-testing.json
|
||||
fi
|
||||
|
||||
if [[ -e ${SRCDIR}/tsconfig-2015.json ]]; then
|
||||
echo "====== COMPILING ESM: ${TSC} -p ${SRCDIR}/tsconfig-2015.json"
|
||||
${TSC} -p ${SRCDIR}/tsconfig-2015.json
|
||||
fi
|
||||
|
||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
@ -148,7 +181,6 @@ do
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
|
||||
|
||||
if [[ -e rollup-testing.config.js ]]; then
|
||||
echo "====== Rollup ${PACKAGE} testing"
|
||||
../../../node_modules/.bin/rollup -c rollup-testing.config.js
|
||||
@ -157,9 +189,49 @@ do
|
||||
cat ${UMD_TESTING_ES5_PATH} >> ${UMD_TESTING_ES5_PATH}.tmp
|
||||
mv ${UMD_TESTING_ES5_PATH}.tmp ${UMD_TESTING_ES5_PATH}
|
||||
fi
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
if [[ -e rollup-static.config.js ]]; then
|
||||
echo "====== Rollup ${PACKAGE} static"
|
||||
../../../node_modules/.bin/rollup -c rollup-static.config.js
|
||||
# create dir because it doesn't exist yet, we should move the src code here and remove this line
|
||||
mkdir ${DESTDIR}/static
|
||||
echo "{\"main\": \"../bundles/${PACKAGE}-static.umd.js\"}" > ${DESTDIR}/static/package.json
|
||||
cat ${LICENSE_BANNER} > ${UMD_STATIC_ES5_PATH}.tmp
|
||||
cat ${UMD_STATIC_ES5_PATH} >> ${UMD_STATIC_ES5_PATH}.tmp
|
||||
mv ${UMD_STATIC_ES5_PATH}.tmp ${UMD_STATIC_ES5_PATH}
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_STATIC_ES5_MIN_PATH} ${UMD_STATIC_ES5_PATH}
|
||||
fi
|
||||
|
||||
if [[ -e rollup-upgrade.config.js ]]; then
|
||||
echo "====== Rollup ${PACKAGE} upgrade"
|
||||
../../../node_modules/.bin/rollup -c rollup-upgrade.config.js
|
||||
# create dir because it doesn't exist yet, we should move the src code here and remove this line
|
||||
mkdir ${DESTDIR}/upgrade
|
||||
echo "{\"main\": \"../bundles/${PACKAGE}-upgrade.umd.js\"}" > ${DESTDIR}/upgrade/package.json
|
||||
cat ${LICENSE_BANNER} > ${UMD_UPGRADE_ES5_PATH}.tmp
|
||||
cat ${UMD_UPGRADE_ES5_PATH} >> ${UMD_UPGRADE_ES5_PATH}.tmp
|
||||
mv ${UMD_UPGRADE_ES5_PATH}.tmp ${UMD_UPGRADE_ES5_PATH}
|
||||
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
|
||||
fi
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
fi
|
||||
|
||||
(
|
||||
echo "====== VERSION: Updating version references"
|
||||
cd ${DESTDIR}
|
||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
|
||||
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
|
||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g\" $""(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .)"
|
||||
perl -p -i -e "s/0\.0\.0\-ROUTERPLACEHOLDER/${ROUTER_VERSION}/g" $(grep -ril 0\.0\.0\-ROUTERPLACEHOLDER .) < /dev/null 2> /dev/null
|
||||
)
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "====== Building examples: ./modules/@angular/examples/build.sh ====="
|
||||
./modules/@angular/examples/build.sh
|
||||
|
||||
if [[ ${REMOVE_BENCHPRESS} == true ]]; then
|
||||
echo ""
|
||||
echo "==== Removing benchpress from publication"
|
||||
rm -r dist/packages-dist/benchpress
|
||||
fi
|
||||
|
@ -4,7 +4,7 @@ machine:
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
- npm install -g npm@3.6.0
|
||||
|
||||
test:
|
||||
override:
|
||||
|
40
docs/PUBLIC_API.md
Normal file
40
docs/PUBLIC_API.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Supported Public API Surface of Angular
|
||||
|
||||
Our SemVer, timed-release cycle and deprecation policy currently applies to these npm packages:
|
||||
|
||||
- `@angular/core`
|
||||
- `@angular/common`
|
||||
- `@angular/platform-browser`
|
||||
- `@angular/platform-browser-dynamic`
|
||||
- `@angular/platform-server`
|
||||
- `@angular/platform-webworker`
|
||||
- `@angular/platform-webworker-dynamic`
|
||||
- `@angular/upgrade`
|
||||
- `@angular/router`
|
||||
- `@angular/forms`
|
||||
- `@angular/http`
|
||||
|
||||
|
||||
One intentional omission from this list is `@angular/compiler`, which is currently considered a low level api and is subject to internal changes. These changes will not affect any applications or libraries using the higher-level apis (the command line interface or JIT compilation via `@angular/platform-browser-dynamic`). Only very specific use-cases require direct access to the compiler API (mostly tooling integration for IDEs, linters, etc). If you are working on this kind of integration, please reach out to us first.
|
||||
|
||||
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
|
||||
|
||||
Other projects developed by the Angular team like angular-cli, Angular Material, benchpress, will be covered by these or similar guarantees in the future as they mature.
|
||||
|
||||
Within the supported packages, we provide guarantees for:
|
||||
|
||||
- symbols exported via the main entry point (e.g. `@angular/core`) and testing entry point (e.g. `@angular/core/testing`). This applies to both runtime/JavaScript values and TypeScript types.
|
||||
- symbols exported via global namespace `ng` (e.g. `ng.core`)
|
||||
- bundles located in the `bundles/` directory of our npm packages (e.g. `@angular/core/bundles/core.umd.js`)
|
||||
|
||||
|
||||
We explicitly don't consider the following to be our public API surface:
|
||||
|
||||
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*`
|
||||
- constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes
|
||||
- any class members or symbols marked as `private` or prefixed with underscore
|
||||
- extending any of our classes unless the support for this is specifically documented in the API docs
|
||||
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
|
||||
|
||||
|
||||
Our peer dependencies (e.g. typescript, zone.js, or rxjs) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.
|
152
gulpfile.js
152
gulpfile.js
@ -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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||
@ -13,12 +21,14 @@ const os = require('os');
|
||||
|
||||
// clang-format entry points
|
||||
const srcsToFmt = [
|
||||
'modules/@angular/**/*.ts',
|
||||
'modules/benchmarks/**/*.ts',
|
||||
'modules/e2e_util/**/*.ts',
|
||||
'modules/playground/**/*.ts',
|
||||
'tools/**/*.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)
|
||||
@ -26,15 +36,16 @@ gulp.task('format:enforce', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
const clangFormat = require('clang-format');
|
||||
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', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
const clangFormat = require('clang-format');
|
||||
return gulp.src(srcsToFmt, { base: '.' }).pipe(
|
||||
format.format('file', clangFormat)).pipe(gulp.dest('.'));
|
||||
return gulp.src(srcsToFmt, {base: '.'})
|
||||
.pipe(format.format('file', clangFormat))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
const entrypoints = [
|
||||
@ -47,6 +58,7 @@ const entrypoints = [
|
||||
//'dist/packages-dist/compiler/index.d.ts',
|
||||
//'dist/packages-dist/compiler/testing.d.ts',
|
||||
'dist/packages-dist/upgrade/index.d.ts',
|
||||
'dist/packages-dist/upgrade/static.d.ts',
|
||||
'dist/packages-dist/platform-browser/index.d.ts',
|
||||
'dist/packages-dist/platform-browser/testing/index.d.ts',
|
||||
'dist/packages-dist/platform-browser-dynamic/index.d.ts',
|
||||
@ -62,12 +74,18 @@ const entrypoints = [
|
||||
];
|
||||
const publicApiDir = path.normalize('tools/public_api_guard');
|
||||
const publicApiArgs = [
|
||||
'--rootDir', 'dist/packages-dist',
|
||||
'--stripExportPattern', '^__',
|
||||
'--allowModuleIdentifiers', 'jasmine',
|
||||
'--allowModuleIdentifiers', 'protractor',
|
||||
'--allowModuleIdentifiers', 'angular',
|
||||
'--onStabilityMissing', 'error',
|
||||
'--rootDir',
|
||||
'dist/packages-dist',
|
||||
'--stripExportPattern',
|
||||
'^__',
|
||||
'--allowModuleIdentifiers',
|
||||
'jasmine',
|
||||
'--allowModuleIdentifiers',
|
||||
'protractor',
|
||||
'--allowModuleIdentifiers',
|
||||
'angular',
|
||||
'--onStabilityMissing',
|
||||
'error',
|
||||
].concat(entrypoints);
|
||||
|
||||
// Build angular
|
||||
@ -83,17 +101,17 @@ gulp.task('public-api:enforce', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
done(new Error(
|
||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
done(new Error(
|
||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Generate the public API golden files
|
||||
@ -101,36 +119,40 @@ gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
});
|
||||
|
||||
// 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}));
|
||||
.spawn(
|
||||
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
});
|
||||
|
||||
// Check the coding standards and programming errors
|
||||
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||
const tslint = require('gulp-tslint');
|
||||
// Built-in rules are at
|
||||
// https://github.com/palantir/tslint#supported-rules
|
||||
// https://palantir.github.io/tslint/rules/
|
||||
const tslintConfig = require('./tslint.json');
|
||||
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts'])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
rulesDirectory: 'dist/tools/tslint',
|
||||
formatter: 'prose',
|
||||
}))
|
||||
.pipe(tslint.report({emitError: true}));
|
||||
return gulp
|
||||
.src([
|
||||
// todo(vicb): add .js files when supported
|
||||
// see https://github.com/palantir/tslint/pull/1515
|
||||
'./modules/**/*.ts',
|
||||
'./tools/**/*.ts',
|
||||
'./*.ts',
|
||||
|
||||
// Ignore TypeScript mocks because it's not managed by us
|
||||
'!./tools/@angular/tsc-wrapped/test/typescript.mocks.ts',
|
||||
|
||||
// Ignore generated files due to lack of copyright header
|
||||
// todo(alfaproject): make generated files lintable
|
||||
'!**/*.d.ts',
|
||||
'!**/*.ngfactory.ts',
|
||||
])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
formatter: 'prose',
|
||||
}))
|
||||
.pipe(tslint.report({emitError: true}));
|
||||
});
|
||||
|
||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||
@ -142,7 +164,7 @@ gulp.task('check-cycle', (done) => {
|
||||
const dependencyObject = madge(['dist/all/'], {
|
||||
format: 'cjs',
|
||||
extensions: ['.js'],
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); }
|
||||
});
|
||||
const circularDependencies = dependencyObject.circular().getArray();
|
||||
if (circularDependencies.length > 0) {
|
||||
@ -173,11 +195,11 @@ gulp.task('serve-examples', () => {
|
||||
const cors = require('cors');
|
||||
|
||||
connect.server({
|
||||
root: `${__dirname}/dist/examples`,
|
||||
port: 8001,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()],
|
||||
root: `${__dirname}/dist/examples`,
|
||||
port: 8001,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()],
|
||||
});
|
||||
});
|
||||
|
||||
@ -187,16 +209,13 @@ gulp.task('changelog', () => {
|
||||
const conventionalChangelog = require('gulp-conventional-changelog');
|
||||
|
||||
return gulp.src('CHANGELOG.md')
|
||||
.pipe(conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 1
|
||||
}, {
|
||||
// Conventional Changelog Context
|
||||
// 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
|
||||
currentTag: require('./package.json').version
|
||||
}))
|
||||
.pipe(gulp.dest('./'));
|
||||
.pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, {
|
||||
// Conventional Changelog Context
|
||||
// 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
|
||||
currentTag: require('./package.json').version
|
||||
}))
|
||||
.pipe(gulp.dest('./'));
|
||||
});
|
||||
|
||||
function tsc(projectPath, done) {
|
||||
@ -205,8 +224,7 @@ function tsc(projectPath, done) {
|
||||
childProcess
|
||||
.spawn(
|
||||
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
|
||||
['-p', path.join(__dirname, projectPath)],
|
||||
{stdio: 'inherit'})
|
||||
['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
|
||||
.on('close', done);
|
||||
}
|
||||
|
||||
|
@ -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 internalAngularReporter = require('./tools/karma/reporter.js');
|
||||
|
||||
@ -15,7 +23,8 @@ module.exports = function(config) {
|
||||
|
||||
'node_modules/core-js/client/core.js',
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
'node_modules/angular/angular.js',
|
||||
'node_modules/angular-mocks/angular-mocks.js',
|
||||
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
@ -33,18 +42,30 @@ module.exports = function(config) {
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
{pattern: 'dist/all/empty.*', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
|
||||
{
|
||||
pattern: 'modules/@angular/platform-browser/test/static_assets/**',
|
||||
included: false,
|
||||
watched: false
|
||||
},
|
||||
{
|
||||
pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**',
|
||||
included: false,
|
||||
watched: false,
|
||||
}
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/all/@angular/**/e2e_test/**',
|
||||
'dist/all/@angular/router/**',
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/@angular/**/*node_only_spec.js',
|
||||
'dist/all/@angular/benchpress/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/@angular/compiler/test/aot/**',
|
||||
'dist/all/@angular/examples/**/e2e_test/*',
|
||||
'dist/all/@angular/language-service/**',
|
||||
'dist/all/@angular/router/**',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||
'dist/examples/**/e2e_test/**'
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/examples/**/e2e_test/**',
|
||||
],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
@ -55,11 +76,11 @@ module.exports = function(config) {
|
||||
'karma-sauce-launcher',
|
||||
'karma-chrome-launcher',
|
||||
'karma-sourcemap-loader',
|
||||
internalAngularReporter
|
||||
internalAngularReporter,
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'**/*.js': ['sourcemap']
|
||||
'**/*.js': ['sourcemap'],
|
||||
},
|
||||
|
||||
reporters: ['internal-angular'],
|
||||
@ -73,7 +94,7 @@ module.exports = function(config) {
|
||||
'selenium-version': '2.53.0',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
'max-duration': 5400,
|
||||
}
|
||||
},
|
||||
|
||||
@ -81,21 +102,22 @@ module.exports = function(config) {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 3,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
timeout: 1800,
|
||||
pollingTimeout: 10000,
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout : 60000,
|
||||
browserDisconnectTolerance : 3,
|
||||
browserNoActivityTimeout : 60000,
|
||||
captureTimeout: 180000,
|
||||
browserDisconnectTimeout: 180000,
|
||||
browserDisconnectTolerance: 3,
|
||||
browserNoActivityTimeout: 300000,
|
||||
});
|
||||
|
||||
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')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
@ -5,7 +5,7 @@ See [here for an example project](https://github.com/angular/benchpress-tree).
|
||||
|
||||
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.
|
||||
|
||||
License: Apache MIT 2.0
|
||||
License: MIT
|
||||
|
||||
# Why?
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@angular/core": "^2.0.0-rc.7",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"rxjs": "5.0.0-beta.12",
|
||||
"rxjs": "^5.0.1",
|
||||
"jpm": "1.1.4",
|
||||
"firefox-profile": "0.4.0",
|
||||
"selenium-webdriver": "^2.53.3"
|
||||
|
@ -9,8 +9,6 @@
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import {DateWrapper} from './facade/lang';
|
||||
|
||||
export class Options {
|
||||
static SAMPLE_ID = new OpaqueToken('Options.sampleId');
|
||||
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
|
||||
@ -34,7 +32,7 @@ export class Options {
|
||||
{provide: Options.FORCE_GC, useValue: false},
|
||||
{provide: Options.PREPARE, useValue: Options.NO_PREPARE},
|
||||
{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.REQUEST_COUNT, useValue: false},
|
||||
{provide: Options.CAPTURE_FRAMES, useValue: false},
|
||||
|
@ -10,7 +10,7 @@ declare var exportFunction: any;
|
||||
declare var unsafeWindow: any;
|
||||
|
||||
exportFunction(function() {
|
||||
var curTime = unsafeWindow.performance.now();
|
||||
const curTime = unsafeWindow.performance.now();
|
||||
(<any>self).port.emit('startProfiler', curTime);
|
||||
}, unsafeWindow, {defineAs: 'startProfiler'});
|
||||
|
||||
@ -28,11 +28,11 @@ exportFunction(function() {
|
||||
}, unsafeWindow, {defineAs: 'forceGC'});
|
||||
|
||||
exportFunction(function(name: string) {
|
||||
var curTime = unsafeWindow.performance.now();
|
||||
const curTime = unsafeWindow.performance.now();
|
||||
(<any>self).port.emit('markStart', name, curTime);
|
||||
}, unsafeWindow, {defineAs: 'markStart'});
|
||||
|
||||
exportFunction(function(name: string) {
|
||||
var curTime = unsafeWindow.performance.now();
|
||||
const curTime = unsafeWindow.performance.now();
|
||||
(<any>self).port.emit('markEnd', name, curTime);
|
||||
}, unsafeWindow, {defineAs: 'markEnd'});
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var {Cc, Ci, Cu} = require('chrome');
|
||||
var os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||
var ParserUtil = require('./parser_util');
|
||||
const {Cc, Ci, Cu} = require('chrome');
|
||||
const os = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
|
||||
const ParserUtil = require('./parser_util');
|
||||
|
||||
class Profiler {
|
||||
private _profiler: any;
|
||||
@ -26,8 +26,8 @@ class Profiler {
|
||||
stop() { this._profiler.StopProfiler(); }
|
||||
|
||||
getProfilePerfEvents() {
|
||||
var profileData = this._profiler.getProfileData();
|
||||
var perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
||||
const profileData = this._profiler.getProfileData();
|
||||
let perfEvents = ParserUtil.convertPerfProfileToEvents(profileData);
|
||||
perfEvents = this._mergeMarkerEvents(perfEvents);
|
||||
perfEvents.sort(function(event1: any, event2: any) {
|
||||
return event1.ts - event2.ts;
|
||||
@ -55,9 +55,9 @@ function forceGC() {
|
||||
os.notifyObservers(null, 'child-gc-request', null);
|
||||
};
|
||||
|
||||
var mod = require('sdk/page-mod');
|
||||
var data = require('sdk/self').data;
|
||||
var profiler = new Profiler();
|
||||
const mod = require('sdk/page-mod');
|
||||
const data = require('sdk/self').data;
|
||||
const profiler = new Profiler();
|
||||
mod.PageMod({
|
||||
include: ['*'],
|
||||
contentScriptFile: data.url('installed_script.js'),
|
||||
|
@ -12,11 +12,11 @@
|
||||
* within the perf profile.
|
||||
*/
|
||||
export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
var inProgressEvents = new Map(); // map from event name to start time
|
||||
var finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
||||
var addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
||||
var categorizedEventName = categorizeEvent(eventName);
|
||||
var args: {[key: string]: any} = undefined;
|
||||
const inProgressEvents = new Map(); // map from event name to start time
|
||||
const finishedEvents: {[key: string]: any}[] = []; // Event[] finished events
|
||||
const addFinishedEvent = function(eventName: string, startTime: number, endTime: number) {
|
||||
const categorizedEventName = categorizeEvent(eventName);
|
||||
let args: {[key: string]: any} = undefined;
|
||||
if (categorizedEventName == 'gc') {
|
||||
// TODO: We cannot measure heap size at the moment
|
||||
args = {usedHeapSize: 0};
|
||||
@ -31,17 +31,17 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
}
|
||||
};
|
||||
|
||||
var samples = perfProfile.threads[0].samples;
|
||||
const samples = perfProfile.threads[0].samples;
|
||||
// In perf profile, firefox samples all the frames in set time intervals. Here
|
||||
// we go through all the samples and construct the start and end time for each
|
||||
// event.
|
||||
for (var i = 0; i < samples.length; ++i) {
|
||||
var sample = samples[i];
|
||||
var sampleTime = sample.time;
|
||||
for (let i = 0; i < samples.length; ++i) {
|
||||
const sample = samples[i];
|
||||
const sampleTime = sample.time;
|
||||
|
||||
// Add all the frames into a set so it's easier/faster to find the set
|
||||
// differences
|
||||
var sampleFrames = new Set();
|
||||
const sampleFrames = new Set();
|
||||
sample.frames.forEach(function(frame: {[key: string]: any}) {
|
||||
sampleFrames.add(frame['location']);
|
||||
});
|
||||
@ -49,7 +49,7 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
// If an event is in the inProgressEvents map, but not in the current sample,
|
||||
// then it must have just finished. We add this event to the finishedEvents
|
||||
// array and remove it from the inProgressEvents map.
|
||||
var previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
|
||||
const previousSampleTime = (i == 0 ? /* not used */ -1 : samples[i - 1].time);
|
||||
inProgressEvents.forEach(function(startTime, eventName) {
|
||||
if (!(sampleFrames.has(eventName))) {
|
||||
addFinishedEvent(eventName, startTime, previousSampleTime);
|
||||
@ -69,7 +69,7 @@ export function convertPerfProfileToEvents(perfProfile: any): any[] {
|
||||
|
||||
// If anything is still in progress, we need to included it as a finished event
|
||||
// since recording ended.
|
||||
var lastSampleTime = samples[samples.length - 1].time;
|
||||
const lastSampleTime = samples[samples.length - 1].time;
|
||||
inProgressEvents.forEach(function(startTime, eventName) {
|
||||
addFinishedEvent(eventName, startTime, lastSampleTime);
|
||||
});
|
||||
|
@ -6,15 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var q = require('q');
|
||||
var FirefoxProfile = require('firefox-profile');
|
||||
var jpm = require('jpm/lib/xpi');
|
||||
var pathUtil = require('path');
|
||||
const q = require('q');
|
||||
const FirefoxProfile = require('firefox-profile');
|
||||
const jpm = require('jpm/lib/xpi');
|
||||
const pathUtil = require('path');
|
||||
|
||||
var PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
||||
const PERF_ADDON_PACKAGE_JSON_DIR = '..';
|
||||
|
||||
exports.getAbsolutePath = function(path: string) {
|
||||
var normalizedPath = pathUtil.normalize(path);
|
||||
const normalizedPath = pathUtil.normalize(path);
|
||||
if (pathUtil.resolve(normalizedPath) == normalizedPath) {
|
||||
// Already absolute path
|
||||
return normalizedPath;
|
||||
@ -24,12 +24,12 @@ exports.getAbsolutePath = function(path: string) {
|
||||
};
|
||||
|
||||
exports.getFirefoxProfile = function(extensionPath: string) {
|
||||
var deferred = q.defer();
|
||||
const deferred = q.defer();
|
||||
|
||||
var firefoxProfile = new FirefoxProfile();
|
||||
const firefoxProfile = new FirefoxProfile();
|
||||
firefoxProfile.addExtensions([extensionPath], () => {
|
||||
firefoxProfile.encoded((encodedProfile: any) => {
|
||||
var multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||
const multiCapabilities = [{browserName: 'firefox', firefox_profile: encodedProfile}];
|
||||
deferred.resolve(multiCapabilities);
|
||||
});
|
||||
});
|
||||
@ -38,10 +38,10 @@ exports.getFirefoxProfile = function(extensionPath: string) {
|
||||
};
|
||||
|
||||
exports.getFirefoxProfileWithExtension = function() {
|
||||
var absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
||||
var packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
||||
const absPackageJsonDir = pathUtil.join(__dirname, PERF_ADDON_PACKAGE_JSON_DIR);
|
||||
const packageJson = require(pathUtil.join(absPackageJsonDir, 'package.json'));
|
||||
|
||||
var savedCwd = process.cwd();
|
||||
const savedCwd = process.cwd();
|
||||
process.chdir(absPackageJsonDir);
|
||||
|
||||
return jpm(packageJson).then((xpiPath: string) => {
|
||||
|
@ -6,18 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Map} from './facade/collection';
|
||||
import {Date, DateWrapper} from './facade/lang';
|
||||
|
||||
export class MeasureValues {
|
||||
constructor(
|
||||
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
'timeStamp': DateWrapper.toJson(this.timeStamp),
|
||||
'timeStamp': this.timeStamp.toJSON(),
|
||||
'runIndex': this.runIndex,
|
||||
'values': this.values
|
||||
'values': this.values,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Injector, OpaqueToken} from '@angular/core';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
|
||||
import {Metric} from '../metric';
|
||||
|
||||
@ -56,10 +55,9 @@ export class MultiMetric extends Metric {
|
||||
}
|
||||
|
||||
function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: string} {
|
||||
var result: {[key: string]: string} = {};
|
||||
maps.forEach(
|
||||
map => { StringMapWrapper.forEach(map, (value, prop) => { result[prop] = value; }); });
|
||||
const result: {[key: string]: string} = {};
|
||||
maps.forEach(map => { Object.keys(map).forEach(prop => { result[prop] = map[prop]; }); });
|
||||
return result;
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
||||
const _CHILDREN = new OpaqueToken('MultiMetric.children');
|
||||
|
@ -56,7 +56,7 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
|
||||
describe(): {[key: string]: string} {
|
||||
var res: {[key: string]: any} = {
|
||||
const res: {[key: string]: any} = {
|
||||
'scriptTime': 'script execution time in ms, including gc and render',
|
||||
'pureScriptTime': 'script execution time in ms, without gc nor render'
|
||||
};
|
||||
@ -80,7 +80,7 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
if (this._captureFrames) {
|
||||
if (!this._perfLogFeatures.frameCapture) {
|
||||
var warningMsg = 'WARNING: Metric requested, but not supported by driver';
|
||||
const warningMsg = 'WARNING: Metric requested, but not supported by driver';
|
||||
// using dot syntax for metric name to keep them grouped together in console reporter
|
||||
res['frameTime.mean'] = warningMsg;
|
||||
res['frameTime.worst'] = warningMsg;
|
||||
@ -93,14 +93,14 @@ export class PerflogMetric extends Metric {
|
||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
||||
}
|
||||
}
|
||||
for (let name in this._microMetrics) {
|
||||
for (const name in this._microMetrics) {
|
||||
res[name] = this._microMetrics[name];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
beginMeasure(): Promise<any> {
|
||||
var resultPromise = Promise.resolve(null);
|
||||
let resultPromise = Promise.resolve(null);
|
||||
if (this._forceGc) {
|
||||
resultPromise = resultPromise.then((_) => this._driverExtension.gc());
|
||||
}
|
||||
@ -119,7 +119,7 @@ export class PerflogMetric extends Metric {
|
||||
private _endPlainMeasureAndMeasureForceGc(restartMeasure: boolean) {
|
||||
return this._endMeasure(true).then((measureValues) => {
|
||||
// disable frame capture for measurements during forced gc
|
||||
var originalFrameCaptureValue = this._captureFrames;
|
||||
const originalFrameCaptureValue = this._captureFrames;
|
||||
this._captureFrames = false;
|
||||
return this._driverExtension.gc()
|
||||
.then((_) => this._endMeasure(restartMeasure))
|
||||
@ -137,8 +137,8 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
|
||||
private _endMeasure(restart: boolean): Promise<{[key: string]: number}> {
|
||||
var markName = this._markName(this._measureCount - 1);
|
||||
var nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||
const markName = this._markName(this._measureCount - 1);
|
||||
const nextMarkName = restart ? this._markName(this._measureCount++) : null;
|
||||
return this._driverExtension.timeEnd(markName, nextMarkName)
|
||||
.then((_) => this._readUntilEndMark(markName));
|
||||
}
|
||||
@ -150,26 +150,26 @@ export class PerflogMetric extends Metric {
|
||||
}
|
||||
return this._driverExtension.readPerfLog().then((events) => {
|
||||
this._addEvents(events);
|
||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||
const result = this._aggregateEvents(this._remainingEvents, markName);
|
||||
if (result) {
|
||||
this._remainingEvents = events;
|
||||
return result;
|
||||
}
|
||||
var resolve: (result: any) => void;
|
||||
var promise = new Promise(res => { resolve = res; });
|
||||
let resolve: (result: any) => void;
|
||||
const promise = new Promise(res => { resolve = res; });
|
||||
this._setTimeout(() => resolve(this._readUntilEndMark(markName, loopCount + 1)), 100);
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
private _addEvents(events: PerfLogEvent[]) {
|
||||
var needSort = false;
|
||||
let needSort = false;
|
||||
events.forEach(event => {
|
||||
if (event['ph'] === 'X') {
|
||||
needSort = true;
|
||||
var startEvent: PerfLogEvent = {};
|
||||
var endEvent: PerfLogEvent = {};
|
||||
for (let prop in event) {
|
||||
const startEvent: PerfLogEvent = {};
|
||||
const endEvent: PerfLogEvent = {};
|
||||
for (const prop in event) {
|
||||
startEvent[prop] = event[prop];
|
||||
endEvent[prop] = event[prop];
|
||||
}
|
||||
@ -185,14 +185,14 @@ export class PerflogMetric extends Metric {
|
||||
if (needSort) {
|
||||
// Need to sort because of the ph==='X' events
|
||||
this._remainingEvents.sort((a, b) => {
|
||||
var diff = a['ts'] - b['ts'];
|
||||
const diff = a['ts'] - b['ts'];
|
||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _aggregateEvents(events: PerfLogEvent[], markName: string): {[key: string]: number} {
|
||||
var result: {[key: string]: number} = {'scriptTime': 0, 'pureScriptTime': 0};
|
||||
const result: {[key: string]: number} = {'scriptTime': 0, 'pureScriptTime': 0};
|
||||
if (this._perfLogFeatures.gc) {
|
||||
result['gcTime'] = 0;
|
||||
result['majorGcTime'] = 0;
|
||||
@ -207,7 +207,7 @@ export class PerflogMetric extends Metric {
|
||||
result['frameTime.worst'] = 0;
|
||||
result['frameTime.smooth'] = 0;
|
||||
}
|
||||
for (let name in this._microMetrics) {
|
||||
for (const name in this._microMetrics) {
|
||||
result[name] = 0;
|
||||
}
|
||||
if (this._receivedData) {
|
||||
@ -217,11 +217,11 @@ export class PerflogMetric extends Metric {
|
||||
result['requestCount'] = 0;
|
||||
}
|
||||
|
||||
var markStartEvent: PerfLogEvent = null;
|
||||
var markEndEvent: PerfLogEvent = null;
|
||||
let markStartEvent: PerfLogEvent = null;
|
||||
let markEndEvent: PerfLogEvent = null;
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
const ph = event['ph'];
|
||||
const name = event['name'];
|
||||
if (ph === 'B' && name === markName) {
|
||||
markStartEvent = event;
|
||||
} else if (ph === 'I' && name === 'navigationStart') {
|
||||
@ -237,23 +237,23 @@ export class PerflogMetric extends Metric {
|
||||
return null;
|
||||
}
|
||||
|
||||
var gcTimeInScript = 0;
|
||||
var renderTimeInScript = 0;
|
||||
let gcTimeInScript = 0;
|
||||
let renderTimeInScript = 0;
|
||||
|
||||
var frameTimestamps: number[] = [];
|
||||
var frameTimes: number[] = [];
|
||||
var frameCaptureStartEvent: PerfLogEvent = null;
|
||||
var frameCaptureEndEvent: PerfLogEvent = null;
|
||||
const frameTimestamps: number[] = [];
|
||||
const frameTimes: number[] = [];
|
||||
let frameCaptureStartEvent: PerfLogEvent = null;
|
||||
let frameCaptureEndEvent: PerfLogEvent = null;
|
||||
|
||||
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||
var intervalStartCount: {[key: string]: number} = {};
|
||||
const intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||
const intervalStartCount: {[key: string]: number} = {};
|
||||
|
||||
var inMeasureRange = false;
|
||||
let inMeasureRange = false;
|
||||
events.forEach((event) => {
|
||||
var ph = event['ph'];
|
||||
var name = event['name'];
|
||||
var microIterations = 1;
|
||||
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||
const ph = event['ph'];
|
||||
let name = event['name'];
|
||||
let microIterations = 1;
|
||||
const microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||
if (microIterationsMatch) {
|
||||
name = microIterationsMatch[1];
|
||||
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||
@ -307,15 +307,15 @@ export class PerflogMetric extends Metric {
|
||||
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||
intervalStartCount[name]--;
|
||||
if (intervalStartCount[name] === 0) {
|
||||
var startEvent = intervalStarts[name];
|
||||
var duration = (event['ts'] - startEvent['ts']);
|
||||
const startEvent = intervalStarts[name];
|
||||
const duration = (event['ts'] - startEvent['ts']);
|
||||
intervalStarts[name] = null;
|
||||
if (name === 'gc') {
|
||||
result['gcTime'] += duration;
|
||||
var amount =
|
||||
const amount =
|
||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||
result['gcAmount'] += amount;
|
||||
var majorGc = event['args']['majorGc'];
|
||||
const majorGc = event['args']['majorGc'];
|
||||
if (majorGc && majorGc) {
|
||||
result['majorGcTime'] += duration;
|
||||
}
|
||||
@ -351,7 +351,7 @@ export class PerflogMetric extends Metric {
|
||||
|
||||
private _addFrameMetrics(result: {[key: string]: number}, frameTimes: any[]) {
|
||||
result['frameTime.mean'] = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
|
||||
var firstFrame = frameTimes[0];
|
||||
const firstFrame = frameTimes[0];
|
||||
result['frameTime.worst'] = frameTimes.reduce((a, b) => a > b ? a : b, firstFrame);
|
||||
result['frameTime.best'] = frameTimes.reduce((a, b) => a < b ? a : b, firstFrame);
|
||||
result['frameTime.smooth'] =
|
||||
@ -361,11 +361,11 @@ export class PerflogMetric extends Metric {
|
||||
private _markName(index: number) { return `${_MARK_NAME_PREFIX}${index}`; }
|
||||
}
|
||||
|
||||
var _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
||||
const _MICRO_ITERATIONS_REGEX = /(.+)\*(\d+)$/;
|
||||
|
||||
var _MAX_RETRY_COUNT = 20;
|
||||
var _MARK_NAME_PREFIX = 'benchpress';
|
||||
const _MAX_RETRY_COUNT = 20;
|
||||
const _MARK_NAME_PREFIX = 'benchpress';
|
||||
|
||||
var _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
||||
const _MARK_NAME_FRAME_CAPUTRE = 'frameCapture';
|
||||
// using 17ms as a somewhat looser threshold, instead of 16.6666ms
|
||||
var _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
||||
const _FRAME_TIME_SMOOTH_THRESHOLD = 17;
|
||||
|
@ -6,11 +6,9 @@
|
||||
* 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 {StringMapWrapper} from '../facade/collection';
|
||||
import {isNumber} from '../facade/lang';
|
||||
import {Metric} from '../metric';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
|
||||
@ -35,22 +33,22 @@ export class UserMetric extends Metric {
|
||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||
let resolve: (result: any) => void;
|
||||
let reject: (error: any) => void;
|
||||
let promise = new Promise((res, rej) => {
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
let adapter = this._wdAdapter;
|
||||
let names = StringMapWrapper.keys(this._userMetrics);
|
||||
const adapter = this._wdAdapter;
|
||||
const names = Object.keys(this._userMetrics);
|
||||
|
||||
function getAndClearValues() {
|
||||
Promise.all(names.map(name => adapter.executeScript(`return window.${name}`)))
|
||||
.then((values: any[]) => {
|
||||
if (values.every(isNumber)) {
|
||||
if (values.every(v => typeof v === 'number')) {
|
||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||
.then((_: any[]) => {
|
||||
let map = StringMapWrapper.create();
|
||||
const map: {[k: string]: any} = {};
|
||||
for (let i = 0, n = names.length; i < n; i++) {
|
||||
StringMapWrapper.set(map, names[i], values[i]);
|
||||
map[names[i]] = values[i];
|
||||
}
|
||||
resolve(map);
|
||||
}, reject);
|
||||
|
@ -7,10 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {NumberWrapper, isBlank, isPresent, print} from '../facade/lang';
|
||||
import {Math} from '../facade/math';
|
||||
import {print} from '../facade/lang';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Reporter} from '../reporter';
|
||||
import {SampleDescription} from '../sample_description';
|
||||
@ -31,8 +28,8 @@ export class ConsoleReporter extends Reporter {
|
||||
];
|
||||
|
||||
private static _lpad(value: string, columnWidth: number, fill = ' ') {
|
||||
var result = '';
|
||||
for (var i = 0; i < columnWidth - value.length; i++) {
|
||||
let result = '';
|
||||
for (let i = 0; i < columnWidth - value.length; i++) {
|
||||
result += fill;
|
||||
}
|
||||
return result + value;
|
||||
@ -52,7 +49,7 @@ export class ConsoleReporter extends Reporter {
|
||||
private _printDescription(sampleDescription: SampleDescription) {
|
||||
this._print(`BENCHMARK ${sampleDescription.id}`);
|
||||
this._print('Description:');
|
||||
var props = sortedProps(sampleDescription.description);
|
||||
const props = sortedProps(sampleDescription.description);
|
||||
props.forEach((prop) => { this._print(`- ${prop}: ${sampleDescription.description[prop]}`); });
|
||||
this._print('Metrics:');
|
||||
this._metricNames.forEach((metricName) => {
|
||||
@ -64,8 +61,8 @@ export class ConsoleReporter extends Reporter {
|
||||
}
|
||||
|
||||
reportMeasureValues(measureValues: MeasureValues): Promise<any> {
|
||||
var formattedValues = this._metricNames.map(metricName => {
|
||||
var value = measureValues.values[metricName];
|
||||
const formattedValues = this._metricNames.map(metricName => {
|
||||
const value = measureValues.values[metricName];
|
||||
return formatNum(value);
|
||||
});
|
||||
this._printStringRow(formattedValues);
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from '../common_options';
|
||||
import {DateWrapper, Json, isBlank, isPresent} from '../facade/lang';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Reporter} from '../reporter';
|
||||
import {SampleDescription} from '../sample_description';
|
||||
@ -39,14 +38,15 @@ export class JsonFileReporter extends Reporter {
|
||||
sortedProps(this._description.metrics).forEach((metricName) => {
|
||||
stats[metricName] = formatStats(validSample, metricName);
|
||||
});
|
||||
var content = Json.stringify({
|
||||
'description': this._description,
|
||||
'stats': stats,
|
||||
'completeSample': completeSample,
|
||||
'validSample': validSample,
|
||||
});
|
||||
var filePath =
|
||||
`${this._path}/${this._description.id}_${DateWrapper.toMillis(this._now())}.json`;
|
||||
const content = JSON.stringify(
|
||||
{
|
||||
'description': this._description,
|
||||
'stats': stats,
|
||||
'completeSample': completeSample,
|
||||
'validSample': validSample,
|
||||
},
|
||||
null, 2);
|
||||
const filePath = `${this._path}/${this._description.id}_${this._now().getTime()}.json`;
|
||||
return this._writeFile(filePath, content);
|
||||
}
|
||||
}
|
||||
|
@ -39,4 +39,4 @@ export class MultiReporter extends Reporter {
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiReporter.children');
|
||||
const _CHILDREN = new OpaqueToken('MultiReporter.children');
|
||||
|
@ -6,28 +6,23 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {NumberWrapper} from '../facade/lang';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Statistic} from '../statistic';
|
||||
|
||||
export function formatNum(n: number) {
|
||||
return NumberWrapper.toFixed(n, 2);
|
||||
return n.toFixed(2);
|
||||
}
|
||||
|
||||
export function sortedProps(obj: {[key: string]: any}) {
|
||||
var props: string[] = [];
|
||||
StringMapWrapper.forEach(obj, (value, prop) => props.push(prop));
|
||||
props.sort();
|
||||
return props;
|
||||
return Object.keys(obj).sort();
|
||||
}
|
||||
|
||||
export function formatStats(validSamples: MeasureValues[], metricName: string): string {
|
||||
var samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
||||
var mean = Statistic.calculateMean(samples);
|
||||
var cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
||||
var formattedMean = formatNum(mean);
|
||||
const samples = validSamples.map(measureValues => measureValues.values[metricName]);
|
||||
const mean = Statistic.calculateMean(samples);
|
||||
const cv = Statistic.calculateCoefficientOfVariation(samples, mean);
|
||||
const formattedMean = formatNum(mean);
|
||||
// Note: Don't use the unicode character for +- as it might cause
|
||||
// hickups for consoles...
|
||||
return NumberWrapper.isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||
}
|
||||
return isNaN(cv) ? formattedMean : `${formattedMean}+-${Math.floor(cv)}%`;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {Provider, ReflectiveInjector} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {Metric} from './metric';
|
||||
import {MultiMetric} from './metric/multi_metric';
|
||||
import {PerflogMetric} from './metric/perflog_metric';
|
||||
@ -45,7 +45,7 @@ export class Runner {
|
||||
providers?: Provider[],
|
||||
userMetrics?: {[key: string]: string}
|
||||
}): Promise<SampleState> {
|
||||
var sampleProviders: Provider[] = [
|
||||
const sampleProviders: Provider[] = [
|
||||
_DEFAULT_PROVIDERS, this._defaultProviders, {provide: Options.SAMPLE_ID, useValue: id},
|
||||
{provide: Options.EXECUTE, useValue: execute}
|
||||
];
|
||||
@ -62,33 +62,33 @@ export class Runner {
|
||||
sampleProviders.push(providers);
|
||||
}
|
||||
|
||||
var inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||
var adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||
const inj = ReflectiveInjector.resolveAndCreate(sampleProviders);
|
||||
const adapter: WebDriverAdapter = inj.get(WebDriverAdapter);
|
||||
|
||||
return Promise
|
||||
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
||||
.then((args) => {
|
||||
var capabilities = args[0];
|
||||
var userAgent = args[1];
|
||||
const capabilities = args[0];
|
||||
const userAgent = args[1];
|
||||
|
||||
// This might still create instances twice. We are creating a new injector with all the
|
||||
// providers.
|
||||
// Only WebDriverAdapter is reused.
|
||||
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
||||
// injectors are handled better.
|
||||
var injector = ReflectiveInjector.resolveAndCreate([
|
||||
const injector = ReflectiveInjector.resolveAndCreate([
|
||||
sampleProviders, {provide: Options.CAPABILITIES, useValue: capabilities},
|
||||
{provide: Options.USER_AGENT, useValue: userAgent},
|
||||
{provide: WebDriverAdapter, useValue: adapter}
|
||||
]);
|
||||
|
||||
var sampler = injector.get(Sampler);
|
||||
const sampler = injector.get(Sampler);
|
||||
return sampler.sample();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var _DEFAULT_PROVIDERS = [
|
||||
const _DEFAULT_PROVIDERS = [
|
||||
Options.DEFAULT_PROVIDERS,
|
||||
Sampler.PROVIDERS,
|
||||
ConsoleReporter.PROVIDERS,
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {StringMapWrapper} from './facade/collection';
|
||||
import {Metric} from './metric';
|
||||
import {Validator} from './validator';
|
||||
|
||||
@ -42,7 +41,7 @@ export class SampleDescription {
|
||||
public metrics: {[key: string]: any}) {
|
||||
this.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
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {Date, DateWrapper, isBlank, isPresent} from './facade/lang';
|
||||
import {isPresent} from './facade/lang';
|
||||
import {MeasureValues} from './measure_values';
|
||||
import {Metric} from './metric';
|
||||
import {Reporter} from './reporter';
|
||||
@ -49,7 +49,7 @@ export class Sampler {
|
||||
}
|
||||
|
||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||
var resultPromise: Promise<SampleState>;
|
||||
let resultPromise: Promise<SampleState>;
|
||||
if (this._prepare !== Options.NO_PREPARE) {
|
||||
resultPromise = this._driver.waitFor(this._prepare);
|
||||
} else {
|
||||
@ -64,10 +64,10 @@ export class Sampler {
|
||||
}
|
||||
|
||||
private _report(state: SampleState, metricValues: {[key: string]: any}): Promise<SampleState> {
|
||||
var measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||
var completeSample = state.completeSample.concat([measureValues]);
|
||||
var validSample = this._validator.validate(completeSample);
|
||||
var resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||
const measureValues = new MeasureValues(state.completeSample.length, this._now(), metricValues);
|
||||
const completeSample = state.completeSample.concat([measureValues]);
|
||||
const validSample = this._validator.validate(completeSample);
|
||||
let resultPromise = this._reporter.reportMeasureValues(measureValues);
|
||||
if (isPresent(validSample)) {
|
||||
resultPromise =
|
||||
resultPromise.then((_) => this._reporter.reportSample(completeSample, validSample));
|
||||
|
@ -6,22 +6,20 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Math} from './facade/math';
|
||||
|
||||
export class Statistic {
|
||||
static calculateCoefficientOfVariation(sample: number[], mean: number) {
|
||||
return Statistic.calculateStandardDeviation(sample, mean) / mean * 100;
|
||||
}
|
||||
|
||||
static calculateMean(samples: number[]) {
|
||||
var total = 0;
|
||||
let total = 0;
|
||||
// TODO: use reduce
|
||||
samples.forEach(x => total += x);
|
||||
return total / samples.length;
|
||||
}
|
||||
|
||||
static calculateStandardDeviation(samples: number[], mean: number) {
|
||||
var deviation = 0;
|
||||
let deviation = 0;
|
||||
// TODO: use reduce
|
||||
samples.forEach(x => deviation += Math.pow(x - mean, 2));
|
||||
deviation = deviation / (samples.length);
|
||||
@ -32,9 +30,9 @@ export class Statistic {
|
||||
static calculateRegressionSlope(
|
||||
xValues: number[], xMean: number, yValues: number[], yMean: number) {
|
||||
// See http://en.wikipedia.org/wiki/Simple_linear_regression
|
||||
var dividendSum = 0;
|
||||
var divisorSum = 0;
|
||||
for (var i = 0; i < xValues.length; i++) {
|
||||
let dividendSum = 0;
|
||||
let divisorSum = 0;
|
||||
for (let i = 0; i < xValues.length; i++) {
|
||||
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean);
|
||||
divisorSum += Math.pow(xValues[i] - xMean, 2);
|
||||
}
|
||||
|
@ -8,13 +8,10 @@
|
||||
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Statistic} from '../statistic';
|
||||
import {Validator} from '../validator';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A validator that checks the regression slope of a specific metric.
|
||||
* Waits for the regression slope to be >=0.
|
||||
@ -40,17 +37,17 @@ export class RegressionSlopeValidator extends Validator {
|
||||
|
||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||
if (completeSample.length >= this._sampleSize) {
|
||||
var latestSample = ListWrapper.slice(
|
||||
completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
||||
var xValues: number[] = [];
|
||||
var yValues: number[] = [];
|
||||
for (var i = 0; i < latestSample.length; i++) {
|
||||
const latestSample =
|
||||
completeSample.slice(completeSample.length - this._sampleSize, completeSample.length);
|
||||
const xValues: number[] = [];
|
||||
const yValues: number[] = [];
|
||||
for (let i = 0; i < latestSample.length; i++) {
|
||||
// For now, we only use the array index as x value.
|
||||
// TODO(tbosch): think about whether we should use time here instead
|
||||
xValues.push(i);
|
||||
yValues.push(latestSample[i].values[this._metric]);
|
||||
}
|
||||
var regressionSlope = Statistic.calculateRegressionSlope(
|
||||
const regressionSlope = Statistic.calculateRegressionSlope(
|
||||
xValues, Statistic.calculateMean(xValues), yValues, Statistic.calculateMean(yValues));
|
||||
return regressionSlope >= 0 ? latestSample : null;
|
||||
} else {
|
||||
|
@ -8,12 +8,9 @@
|
||||
|
||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {MeasureValues} from '../measure_values';
|
||||
import {Validator} from '../validator';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A validator that waits for the sample to have a certain size.
|
||||
*/
|
||||
@ -28,8 +25,7 @@ export class SizeValidator extends Validator {
|
||||
|
||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||
if (completeSample.length >= this._sampleSize) {
|
||||
return ListWrapper.slice(
|
||||
completeSample, completeSample.length - this._sampleSize, completeSample.length);
|
||||
return completeSample.slice(completeSample.length - this._sampleSize, completeSample.length);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
import {Injector, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {Options} from './common_options';
|
||||
import {isBlank, isPresent} from './facade/lang';
|
||||
|
||||
export type PerfLogEvent = {
|
||||
[key: string]: any
|
||||
@ -35,7 +34,7 @@ export type PerfLogEvent = {
|
||||
*/
|
||||
export abstract class WebDriverExtension {
|
||||
static provideFirstSupported(childTokens: any[]): any[] {
|
||||
var res = [
|
||||
const res = [
|
||||
{
|
||||
provide: _CHILDREN,
|
||||
useFactory: (injector: Injector) => childTokens.map(token => injector.get(token)),
|
||||
@ -44,13 +43,13 @@ export abstract class WebDriverExtension {
|
||||
{
|
||||
provide: WebDriverExtension,
|
||||
useFactory: (children: WebDriverExtension[], capabilities: {[key: string]: any}) => {
|
||||
var delegate: WebDriverExtension;
|
||||
let delegate: WebDriverExtension;
|
||||
children.forEach(extension => {
|
||||
if (extension.supports(capabilities)) {
|
||||
delegate = extension;
|
||||
}
|
||||
});
|
||||
if (isBlank(delegate)) {
|
||||
if (!delegate) {
|
||||
throw new Error('Could not find a delegate for given capabilities!');
|
||||
}
|
||||
return delegate;
|
||||
@ -102,4 +101,4 @@ export class PerfLogFeatures {
|
||||
}
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
||||
const _CHILDREN = new OpaqueToken('WebDriverExtension.children');
|
||||
|
@ -34,7 +34,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
if (!userAgent) {
|
||||
return -1;
|
||||
}
|
||||
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||
let v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||
if (!v) {
|
||||
return -1;
|
||||
}
|
||||
@ -52,7 +52,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
let script = `console.timeEnd('${name}');`;
|
||||
if (restartName) {
|
||||
script += `console.time('${restartName}');`;
|
||||
}
|
||||
@ -67,9 +67,9 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
return this._driver.executeScript('1+1')
|
||||
.then((_) => this._driver.logs('performance'))
|
||||
.then((entries) => {
|
||||
var events: PerfLogEvent[] = [];
|
||||
const events: PerfLogEvent[] = [];
|
||||
entries.forEach(entry => {
|
||||
var message = JSON.parse(entry['message'])['message'];
|
||||
const message = JSON.parse(entry['message'])['message'];
|
||||
if (message['method'] === 'Tracing.dataCollected') {
|
||||
events.push(message['params']);
|
||||
}
|
||||
@ -95,8 +95,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||
var name = event['name'];
|
||||
var args = event['args'];
|
||||
const name = event['name'];
|
||||
const args = event['args'];
|
||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||
return normalizeEvent(event, {'name': name});
|
||||
} else if (this._isEvent(
|
||||
@ -109,7 +109,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
// 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'];
|
||||
const frameCount = event['args']['data']['frame_count'];
|
||||
if (frameCount > 1) {
|
||||
throw new Error('multi-frame render stats not supported');
|
||||
}
|
||||
@ -122,14 +122,14 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||
return normalizeEvent(event, {'name': 'render'});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||
var normArgs = {
|
||||
const normArgs = {
|
||||
'majorGc': true,
|
||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
};
|
||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||
var normArgs = {
|
||||
const normArgs = {
|
||||
'majorGc': false,
|
||||
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||
args['usedHeapSizeBefore']
|
||||
@ -151,11 +151,11 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
this._isEvent(categories, name, ['devtools.timeline'], 'Paint')) {
|
||||
return normalizeEvent(event, {'name': 'render'});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceReceivedData')) {
|
||||
let normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
|
||||
const normArgs = {'encodedDataLength': args['data']['encodedDataLength']};
|
||||
return normalizeEvent(event, {'name': 'receivedData', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'ResourceSendRequest')) {
|
||||
let data = args['data'];
|
||||
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||
const data = args['data'];
|
||||
const normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||
@ -168,8 +168,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
private _isEvent(
|
||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||
expectedName: string = null): boolean {
|
||||
var hasCategories = expectedCategories.reduce(
|
||||
(value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true);
|
||||
const hasCategories = expectedCategories.reduce(
|
||||
(value, cat) => value && eventCategories.indexOf(cat) !== -1, true);
|
||||
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||
var ph = chromeEvent['ph'].toUpperCase();
|
||||
let ph = chromeEvent['ph'].toUpperCase();
|
||||
if (ph === 'S') {
|
||||
ph = 'B';
|
||||
} else if (ph === 'F') {
|
||||
@ -192,16 +192,16 @@ function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent):
|
||||
// mark events from navigation timing
|
||||
ph = 'I';
|
||||
}
|
||||
var result: {[key: string]: any} =
|
||||
const result: {[key: string]: any} =
|
||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||
if (ph === 'X') {
|
||||
var dur = chromeEvent['dur'];
|
||||
let dur = chromeEvent['dur'];
|
||||
if (dur === undefined) {
|
||||
dur = chromeEvent['tdur'];
|
||||
}
|
||||
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||
}
|
||||
for (let prop in data) {
|
||||
for (const prop in data) {
|
||||
result[prop] = data[prop];
|
||||
}
|
||||
return result;
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {StringWrapper, isPresent} from '../facade/lang';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||
|
||||
@ -34,7 +34,7 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = 'window.markEnd("' + name + '");';
|
||||
let script = 'window.markEnd("' + name + '");';
|
||||
if (isPresent(restartName)) {
|
||||
script += 'window.markStart("' + restartName + '");';
|
||||
}
|
||||
@ -48,6 +48,6 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'firefox');
|
||||
return capabilities['browserName'].toLowerCase() === 'firefox';
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {StringWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||
|
||||
@ -25,7 +25,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
}
|
||||
|
||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||
var script = `console.timeEnd('${name}');`;
|
||||
let script = `console.timeEnd('${name}');`;
|
||||
if (isPresent(restartName)) {
|
||||
script += `console.time('${restartName}');`;
|
||||
}
|
||||
@ -39,10 +39,10 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
return this._driver.executeScript('1+1')
|
||||
.then((_) => this._driver.logs('performance'))
|
||||
.then((entries) => {
|
||||
var records: any[] = [];
|
||||
const records: any[] = [];
|
||||
entries.forEach(entry => {
|
||||
var message = JSON.parse(entry['message'])['message'];
|
||||
if (StringWrapper.equals(message['method'], 'Timeline.eventRecorded')) {
|
||||
const message = JSON.parse(entry['message'])['message'];
|
||||
if (message['method'] === 'Timeline.eventRecorded') {
|
||||
records.push(message['params']['record']);
|
||||
}
|
||||
});
|
||||
@ -52,29 +52,26 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
|
||||
/** @internal */
|
||||
private _convertPerfRecordsToEvents(records: any[], events: PerfLogEvent[] = null) {
|
||||
if (isBlank(events)) {
|
||||
if (!events) {
|
||||
events = [];
|
||||
}
|
||||
records.forEach((record) => {
|
||||
var endEvent: PerfLogEvent = null;
|
||||
var type = record['type'];
|
||||
var data = record['data'];
|
||||
var startTime = record['startTime'];
|
||||
var endTime = record['endTime'];
|
||||
let endEvent: PerfLogEvent = null;
|
||||
const type = record['type'];
|
||||
const data = record['data'];
|
||||
const startTime = record['startTime'];
|
||||
const endTime = record['endTime'];
|
||||
|
||||
if (StringWrapper.equals(type, 'FunctionCall') &&
|
||||
(isBlank(data) || !StringWrapper.equals(data['scriptName'], 'InjectedScript'))) {
|
||||
if (type === 'FunctionCall' && (data == null || data['scriptName'] !== 'InjectedScript')) {
|
||||
events.push(createStartEvent('script', startTime));
|
||||
endEvent = createEndEvent('script', endTime);
|
||||
} else if (StringWrapper.equals(type, 'Time')) {
|
||||
} else if (type === 'Time') {
|
||||
events.push(createMarkStartEvent(data['message'], startTime));
|
||||
} else if (StringWrapper.equals(type, 'TimeEnd')) {
|
||||
} else if (type === 'TimeEnd') {
|
||||
events.push(createMarkEndEvent(data['message'], startTime));
|
||||
} else if (
|
||||
StringWrapper.equals(type, 'RecalculateStyles') || StringWrapper.equals(type, 'Layout') ||
|
||||
StringWrapper.equals(type, 'UpdateLayerTree') || StringWrapper.equals(type, 'Paint') ||
|
||||
StringWrapper.equals(type, 'Rasterize') ||
|
||||
StringWrapper.equals(type, 'CompositeLayers')) {
|
||||
type === 'RecalculateStyles' || type === 'Layout' || type === 'UpdateLayerTree' ||
|
||||
type === 'Paint' || type === 'Rasterize' || type === 'CompositeLayers') {
|
||||
events.push(createStartEvent('render', startTime));
|
||||
endEvent = createEndEvent('render', endTime);
|
||||
}
|
||||
@ -92,13 +89,13 @@ export class IOsDriverExtension extends WebDriverExtension {
|
||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'safari');
|
||||
return capabilities['browserName'].toLowerCase() === 'safari';
|
||||
}
|
||||
}
|
||||
|
||||
function createEvent(
|
||||
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||
var result: PerfLogEvent = {
|
||||
const result: PerfLogEvent = {
|
||||
'cat': 'timeline',
|
||||
'name': name,
|
||||
'ts': time,
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
require('core-js');
|
||||
require('reflect-metadata');
|
||||
var testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
||||
const testHelper = require('../../src/firefox_extension/lib/test_helper.js');
|
||||
|
||||
exports.config = {
|
||||
specs: ['spec.js', 'sample_benchmark.js'],
|
||||
|
@ -10,10 +10,10 @@ import {convertPerfProfileToEvents} from '../../src/firefox_extension/lib/parser
|
||||
|
||||
function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
||||
expect(actualEvents.length == expectedEvents.length);
|
||||
for (var i = 0; i < actualEvents.length; ++i) {
|
||||
var actualEvent = actualEvents[i];
|
||||
var expectedEvent = expectedEvents[i];
|
||||
for (var key in actualEvent) {
|
||||
for (let i = 0; i < actualEvents.length; ++i) {
|
||||
const actualEvent = actualEvents[i];
|
||||
const expectedEvent = expectedEvents[i];
|
||||
for (const key in actualEvent) {
|
||||
expect(actualEvent[key]).toEqual(expectedEvent[key]);
|
||||
}
|
||||
}
|
||||
@ -22,17 +22,17 @@ function assertEventsEqual(actualEvents: any[], expectedEvents: any[]) {
|
||||
export function main() {
|
||||
describe('convertPerfProfileToEvents', function() {
|
||||
it('should convert single instantaneous event', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [
|
||||
{samples: [{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]}]}
|
||||
]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||
});
|
||||
|
||||
it('should convert single non-instantaneous event', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -41,13 +41,13 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(
|
||||
perfEvents, [{ph: 'B', ts: 1, name: 'script'}, {ph: 'E', ts: 100, name: 'script'}]);
|
||||
});
|
||||
|
||||
it('should convert multiple instantaneous events', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -55,13 +55,13 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(
|
||||
perfEvents, [{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'}]);
|
||||
});
|
||||
|
||||
it('should convert multiple mixed events', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -71,7 +71,7 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [
|
||||
{ph: 'X', ts: 1, name: 'script'}, {ph: 'X', ts: 2, name: 'render'},
|
||||
{ph: 'B', ts: 5, name: 'script'}, {ph: 'E', ts: 10, name: 'script'}
|
||||
@ -79,13 +79,13 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should add args to gc events', function() {
|
||||
var profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const profileData = {threads: [{samples: [{time: 1, frames: [{location: 'forceGC'}]}]}]};
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'gc', args: {usedHeapSize: 0}}]);
|
||||
});
|
||||
|
||||
it('should skip unknown events', function() {
|
||||
var profileData = {
|
||||
const profileData = {
|
||||
threads: [{
|
||||
samples: [
|
||||
{time: 1, frames: [{location: 'FirefoxDriver.prototype.executeScript'}]},
|
||||
@ -93,7 +93,7 @@ export function main() {
|
||||
]
|
||||
}]
|
||||
};
|
||||
var perfEvents = convertPerfProfileToEvents(profileData);
|
||||
const perfEvents = convertPerfProfileToEvents(profileData);
|
||||
assertEventsEqual(perfEvents, [{ph: 'X', ts: 1, name: 'script'}]);
|
||||
});
|
||||
});
|
||||
|
@ -6,8 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var benchpress = require('../../index.js');
|
||||
var runner = new benchpress.Runner([
|
||||
import {$, browser} from 'protractor';
|
||||
|
||||
const benchpress = require('../../index.js');
|
||||
const runner = new benchpress.Runner([
|
||||
// use protractor as Webdriver client
|
||||
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||
// use RegressionSlopeValidator to validate samples
|
||||
|
@ -6,9 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
var assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
var found = false;
|
||||
for (var i = 0; i < events.length; ++i) {
|
||||
/* tslint:disable:no-console */
|
||||
import {browser} from 'protractor';
|
||||
|
||||
const assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
let found = false;
|
||||
for (let i = 0; i < events.length; ++i) {
|
||||
if (events[i].name == eventName) {
|
||||
found = true;
|
||||
break;
|
||||
@ -18,7 +21,7 @@ var assertEventsContainsName = function(events: any[], eventName: string) {
|
||||
};
|
||||
|
||||
describe('firefox extension', function() {
|
||||
var TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
||||
const TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
||||
|
||||
it('should measure performance', function() {
|
||||
browser.sleep(3000); // wait for extension to load
|
||||
|
@ -6,17 +6,17 @@
|
||||
* 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';
|
||||
|
||||
export function main() {
|
||||
function createMetric(ids: any[]) {
|
||||
var m = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => { return {provide: id, useValue: new MockMetric(id)}; }),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
.get(MultiMetric);
|
||||
const m = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
.get(MultiMetric);
|
||||
return Promise.resolve(m);
|
||||
}
|
||||
|
||||
@ -56,13 +56,13 @@ class MockMetric extends Metric {
|
||||
beginMeasure(): Promise<string> { return Promise.resolve(`${this._id}_beginMeasure`); }
|
||||
|
||||
endMeasure(restart: boolean): Promise<{[key: string]: any}> {
|
||||
var result: {[key: string]: any} = {};
|
||||
const result: {[key: string]: any} = {};
|
||||
result[this._id] = {'restart': restart};
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
describe(): {[key: string]: string} {
|
||||
var result: {[key: string]: string} = {};
|
||||
const result: {[key: string]: string} = {};
|
||||
result[this._id] = 'describe';
|
||||
return result;
|
||||
}
|
||||
|
@ -7,16 +7,15 @@
|
||||
*/
|
||||
|
||||
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 {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
var commandLog: any[];
|
||||
var eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
let commandLog: any[];
|
||||
const eventFactory = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createMetric(
|
||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||
@ -28,14 +27,14 @@ export function main() {
|
||||
requestCount?: boolean
|
||||
} = {}): Metric {
|
||||
commandLog = [];
|
||||
if (isBlank(perfLogFeatures)) {
|
||||
if (!perfLogFeatures) {
|
||||
perfLogFeatures =
|
||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||
}
|
||||
if (isBlank(microMetrics)) {
|
||||
microMetrics = StringMapWrapper.create();
|
||||
if (!microMetrics) {
|
||||
microMetrics = {};
|
||||
}
|
||||
var providers: Provider[] = [
|
||||
const providers: Provider[] = [
|
||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||
{provide: Options.MICRO_METRICS, useValue: microMetrics}, {
|
||||
provide: PerflogMetric.SET_TIMEOUT,
|
||||
@ -67,8 +66,8 @@ export function main() {
|
||||
describe('perflog metric', () => {
|
||||
|
||||
function sortedKeys(stringMap: {[key: string]: any}) {
|
||||
var res: string[] = [];
|
||||
StringMapWrapper.forEach(stringMap, (_, key) => { res.push(key); });
|
||||
const res: string[] = [];
|
||||
res.push(...Object.keys(stringMap));
|
||||
res.sort();
|
||||
return res;
|
||||
}
|
||||
@ -103,15 +102,15 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should describe itself based on micro metrics', () => {
|
||||
var description =
|
||||
const description =
|
||||
createMetric([[]], null, {microMetrics: {'myMicroMetric': 'someDesc'}}).describe();
|
||||
expect(description['myMicroMetric']).toEqual('someDesc');
|
||||
});
|
||||
|
||||
it('should describe itself if frame capture is requested and available', () => {
|
||||
var description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
const description = createMetric([[]], new PerfLogFeatures({frameCapture: true}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
expect(description['frameTime.mean']).not.toContain('WARNING');
|
||||
expect(description['frameTime.best']).not.toContain('WARNING');
|
||||
expect(description['frameTime.worst']).not.toContain('WARNING');
|
||||
@ -119,9 +118,9 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should describe itself if frame capture is requested and not available', () => {
|
||||
var description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
const description = createMetric([[]], new PerfLogFeatures({frameCapture: false}), {
|
||||
captureFrames: true
|
||||
}).describe();
|
||||
expect(description['frameTime.mean']).toContain('WARNING');
|
||||
expect(description['frameTime.best']).toContain('WARNING');
|
||||
expect(description['frameTime.worst']).toContain('WARNING');
|
||||
@ -132,7 +131,7 @@ export function main() {
|
||||
|
||||
it('should not force gc and mark the timeline',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric([[]], null);
|
||||
const metric = createMetric([[]], null);
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['timeBegin', 'benchpress0']]);
|
||||
|
||||
@ -142,7 +141,7 @@ export function main() {
|
||||
|
||||
it('should force gc and mark the timeline',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric([[]], null, {forceGc: true});
|
||||
const metric = createMetric([[]], null, {forceGc: true});
|
||||
metric.beginMeasure().then((_) => {
|
||||
expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]);
|
||||
|
||||
@ -156,11 +155,11 @@ export function main() {
|
||||
|
||||
it('should mark and aggregate events in between the marks',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [[
|
||||
const events = [[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||
eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10)
|
||||
]];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog'
|
||||
@ -173,13 +172,13 @@ export function main() {
|
||||
|
||||
it('should mark and aggregate events since navigationStart',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [[
|
||||
const 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);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(data['scriptTime']).toBe(1);
|
||||
|
||||
@ -188,7 +187,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
const events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0),
|
||||
eventFactory.markEnd('benchpress0', 1),
|
||||
@ -196,7 +195,7 @@ export function main() {
|
||||
],
|
||||
[eventFactory.markEnd('benchpress1', 3)]
|
||||
];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((_) => metric.endMeasure(true))
|
||||
@ -212,7 +211,7 @@ export function main() {
|
||||
|
||||
it('should loop and aggregate until the end mark is present',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
const events = [
|
||||
[eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)],
|
||||
[eventFactory.end('script', 2)],
|
||||
[
|
||||
@ -220,7 +219,7 @@ export function main() {
|
||||
eventFactory.markEnd('benchpress0', 10)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog',
|
||||
@ -234,7 +233,7 @@ export function main() {
|
||||
|
||||
it('should store events after the end mark for the next call',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var events = [
|
||||
const events = [
|
||||
[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1),
|
||||
eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1),
|
||||
@ -245,7 +244,7 @@ export function main() {
|
||||
eventFactory.markEnd('benchpress1', 6)
|
||||
]
|
||||
];
|
||||
var metric = createMetric(events, null);
|
||||
const metric = createMetric(events, null);
|
||||
metric.beginMeasure()
|
||||
.then((_) => metric.endMeasure(true))
|
||||
.then((data) => {
|
||||
@ -264,7 +263,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
describe('with forced gc', () => {
|
||||
var events: PerfLogEvent[][];
|
||||
let events: PerfLogEvent[][];
|
||||
beforeEach(() => {
|
||||
events = [[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||
@ -277,7 +276,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric(events, null, {forceGc: true});
|
||||
const metric = createMetric(events, null, {forceGc: true});
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||
expect(commandLog).toEqual([
|
||||
['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'],
|
||||
@ -292,7 +291,7 @@ export function main() {
|
||||
|
||||
it('should restart after the forced gc if needed',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var metric = createMetric(events, null, {forceGc: true});
|
||||
const metric = createMetric(events, null, {forceGc: true});
|
||||
metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => {
|
||||
expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']);
|
||||
|
||||
@ -314,7 +313,7 @@ export function main() {
|
||||
} = {}) {
|
||||
events.unshift(eventFactory.markStart('benchpress0', 0));
|
||||
events.push(eventFactory.markEnd('benchpress0', 10));
|
||||
var metric = createMetric([events], null, {
|
||||
const metric = createMetric([events], null, {
|
||||
microMetrics: microMetrics,
|
||||
captureFrames: captureFrames,
|
||||
receivedData: receivedData,
|
||||
@ -503,8 +502,8 @@ export function main() {
|
||||
|
||||
it('should ignore events from different processed as the start mark',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||
var metric = createMetric(
|
||||
const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1');
|
||||
const metric = createMetric(
|
||||
[[
|
||||
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null),
|
||||
eventFactory.end('script', 5, null),
|
||||
@ -686,7 +685,7 @@ class MockDriverExtension extends WebDriverExtension {
|
||||
readPerfLog(): Promise<any> {
|
||||
this._commandLog.push('readPerfLog');
|
||||
if (this._perfLogs.length > 0) {
|
||||
var next = this._perfLogs[0];
|
||||
const next = this._perfLogs[0];
|
||||
this._perfLogs.shift();
|
||||
return Promise.resolve(next);
|
||||
} else {
|
||||
|
@ -7,27 +7,25 @@
|
||||
*/
|
||||
|
||||
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 {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {Json, isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index';
|
||||
|
||||
export function main() {
|
||||
var wdAdapter: MockDriverAdapter;
|
||||
let wdAdapter: MockDriverAdapter;
|
||||
|
||||
function createMetric(
|
||||
perfLogs: PerfLogEvent[], perfLogFeatures: PerfLogFeatures,
|
||||
{userMetrics}: {userMetrics?: {[key: string]: string}} = {}): UserMetric {
|
||||
if (isBlank(perfLogFeatures)) {
|
||||
if (!perfLogFeatures) {
|
||||
perfLogFeatures =
|
||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||
}
|
||||
if (isBlank(userMetrics)) {
|
||||
userMetrics = StringMapWrapper.create();
|
||||
if (!userMetrics) {
|
||||
userMetrics = {};
|
||||
}
|
||||
wdAdapter = new MockDriverAdapter();
|
||||
var providers: Provider[] = [
|
||||
const providers: Provider[] = [
|
||||
Options.DEFAULT_PROVIDERS, UserMetric.PROVIDERS,
|
||||
{provide: Options.USER_METRICS, useValue: userMetrics},
|
||||
{provide: WebDriverAdapter, useValue: wdAdapter}
|
||||
@ -47,7 +45,7 @@ export function main() {
|
||||
describe('endMeasure', () => {
|
||||
it('should stop measuring when all properties have numeric values',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
let metric = createMetric(
|
||||
const metric = createMetric(
|
||||
[[]], new PerfLogFeatures(),
|
||||
{userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}});
|
||||
metric.beginMeasure()
|
||||
@ -73,7 +71,7 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
executeScript(script: string): any {
|
||||
// Just handles `return window.propName` ignores `delete window.propName`.
|
||||
if (script.indexOf('return window.') == 0) {
|
||||
let metricName = script.substring('return window.'.length);
|
||||
const metricName = script.substring('return window.'.length);
|
||||
return Promise.resolve(this.data[metricName]);
|
||||
} else if (script.indexOf('delete window.') == 0) {
|
||||
return Promise.resolve(null);
|
||||
|
@ -7,15 +7,15 @@
|
||||
*/
|
||||
|
||||
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 {Date, DateWrapper, isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {ConsoleReporter, MeasureValues, ReflectiveInjector, SampleDescription} from '../../index';
|
||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('console reporter', () => {
|
||||
var reporter: ConsoleReporter;
|
||||
var log: string[];
|
||||
let reporter: ConsoleReporter;
|
||||
let log: string[];
|
||||
|
||||
function createReporter(
|
||||
{columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
|
||||
@ -25,13 +25,13 @@ export function main() {
|
||||
metrics?: {[key: string]: any}
|
||||
}) {
|
||||
log = [];
|
||||
if (isBlank(descriptions)) {
|
||||
if (!descriptions) {
|
||||
descriptions = [];
|
||||
}
|
||||
if (isBlank(sampleId)) {
|
||||
if (sampleId == null) {
|
||||
sampleId = 'null';
|
||||
}
|
||||
var providers: Provider[] = [
|
||||
const providers: Provider[] = [
|
||||
ConsoleReporter.PROVIDERS, {
|
||||
provide: SampleDescription,
|
||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||
@ -90,5 +90,5 @@ export function main() {
|
||||
}
|
||||
|
||||
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,14 +6,14 @@
|
||||
* 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 {DateWrapper, Json, isPresent} from '../../src/facade/lang';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('file reporter', () => {
|
||||
var loggedFile: any;
|
||||
let loggedFile: any;
|
||||
|
||||
function createReporter({sampleId, descriptions, metrics, path}: {
|
||||
sampleId: string,
|
||||
@ -21,13 +21,13 @@ export function main() {
|
||||
metrics: {[key: string]: string},
|
||||
path: string
|
||||
}) {
|
||||
var providers = [
|
||||
const providers = [
|
||||
JsonFileReporter.PROVIDERS, {
|
||||
provide: SampleDescription,
|
||||
useValue: new SampleDescription(sampleId, descriptions, metrics)
|
||||
},
|
||||
{provide: JsonFileReporter.PATH, useValue: path},
|
||||
{provide: Options.NOW, useValue: () => DateWrapper.fromMillis(1234)}, {
|
||||
{provide: Options.NOW, useValue: () => new Date(1234)}, {
|
||||
provide: Options.WRITE_FILE,
|
||||
useValue: (filename: string, content: string) => {
|
||||
loggedFile = {'filename': filename, 'content': content};
|
||||
@ -49,9 +49,9 @@ export function main() {
|
||||
.reportSample(
|
||||
[mv(0, 0, {'a': 3, 'b': 6})],
|
||||
[mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
|
||||
var regExp = /somePath\/someId_\d+\.json/;
|
||||
const regExp = /somePath\/someId_\d+\.json/;
|
||||
expect(isPresent(loggedFile['filename'].match(regExp))).toBe(true);
|
||||
var parsedContent = Json.parse(loggedFile['content']);
|
||||
const parsedContent = JSON.parse(loggedFile['content']);
|
||||
expect(parsedContent).toEqual({
|
||||
'description': {
|
||||
'id': 'someId',
|
||||
@ -77,5 +77,5 @@ export function main() {
|
||||
}
|
||||
|
||||
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,19 +6,18 @@
|
||||
* 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 {DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
function createReporters(ids: any[]) {
|
||||
var r = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => { return {provide: id, useValue: new MockReporter(id)}; }),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
.get(MultiReporter);
|
||||
const r = ReflectiveInjector
|
||||
.resolveAndCreate([
|
||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
.get(MultiReporter);
|
||||
return Promise.resolve(r);
|
||||
}
|
||||
|
||||
@ -26,7 +25,7 @@ export function main() {
|
||||
|
||||
it('should reportMeasureValues to all',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var mv = new MeasureValues(0, DateWrapper.now(), {});
|
||||
const mv = new MeasureValues(0, new Date(), {});
|
||||
createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => {
|
||||
|
||||
expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]);
|
||||
@ -35,10 +34,9 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var completeSample = [
|
||||
new MeasureValues(0, DateWrapper.now(), {}), new MeasureValues(1, DateWrapper.now(), {})
|
||||
];
|
||||
var validSample = [completeSample[1]];
|
||||
const completeSample =
|
||||
[new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})];
|
||||
const validSample = [completeSample[1]];
|
||||
|
||||
createReporters(['m1', 'm2'])
|
||||
.then((r) => r.reportSample(completeSample, validSample))
|
||||
|
@ -6,18 +6,17 @@
|
||||
* 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 {isBlank} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('runner', () => {
|
||||
var injector: ReflectiveInjector;
|
||||
var runner: Runner;
|
||||
let injector: ReflectiveInjector;
|
||||
let runner: Runner;
|
||||
|
||||
function createRunner(defaultProviders: any[] = null): Runner {
|
||||
if (isBlank(defaultProviders)) {
|
||||
if (!defaultProviders) {
|
||||
defaultProviders = [];
|
||||
}
|
||||
runner = new Runner([
|
||||
@ -77,7 +76,7 @@ export function main() {
|
||||
|
||||
it('should provide Options.EXECUTE',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var execute = () => {};
|
||||
const execute = () => {};
|
||||
createRunner().sample({id: 'someId', execute: execute}).then((_) => {
|
||||
expect(injector.get(Options.EXECUTE)).toEqual(execute);
|
||||
async.done();
|
||||
@ -86,7 +85,7 @@ export function main() {
|
||||
|
||||
it('should provide Options.PREPARE',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var prepare = () => {};
|
||||
const prepare = () => {};
|
||||
createRunner().sample({id: 'someId', prepare: prepare}).then((_) => {
|
||||
expect(injector.get(Options.PREPARE)).toEqual(prepare);
|
||||
async.done();
|
||||
|
@ -6,16 +6,16 @@
|
||||
* 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 {Date, DateWrapper, isBlank, isPresent, stringify} from '../src/facade/lang';
|
||||
import {isBlank, isPresent} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
var EMPTY_EXECUTE = () => {};
|
||||
const EMPTY_EXECUTE = () => {};
|
||||
|
||||
describe('sampler', () => {
|
||||
var sampler: Sampler;
|
||||
let sampler: Sampler;
|
||||
|
||||
function createSampler({driver, metric, reporter, validator, prepare, execute}: {
|
||||
driver?: any,
|
||||
@ -25,21 +25,21 @@ export function main() {
|
||||
prepare?: any,
|
||||
execute?: any
|
||||
} = {}) {
|
||||
var time = 1000;
|
||||
if (isBlank(metric)) {
|
||||
let time = 1000;
|
||||
if (!metric) {
|
||||
metric = new MockMetric([]);
|
||||
}
|
||||
if (isBlank(reporter)) {
|
||||
if (!reporter) {
|
||||
reporter = new MockReporter([]);
|
||||
}
|
||||
if (isBlank(driver)) {
|
||||
driver = new MockDriverAdapter([]);
|
||||
}
|
||||
var providers = [
|
||||
const providers = [
|
||||
Options.DEFAULT_PROVIDERS, Sampler.PROVIDERS, {provide: Metric, useValue: metric},
|
||||
{provide: Reporter, useValue: reporter}, {provide: WebDriverAdapter, useValue: driver},
|
||||
{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)) {
|
||||
providers.push({provide: Options.PREPARE, useValue: prepare});
|
||||
@ -50,18 +50,18 @@ export function main() {
|
||||
|
||||
it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var count = 0;
|
||||
var driver = new MockDriverAdapter([], (callback: Function) => {
|
||||
var result = callback();
|
||||
const log: any[] = [];
|
||||
let count = 0;
|
||||
const driver = new MockDriverAdapter([], (callback: Function) => {
|
||||
const result = callback();
|
||||
log.push(result);
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
createSampler({
|
||||
driver: driver,
|
||||
validator: createCountingValidator(2),
|
||||
prepare: () => { return count++; },
|
||||
execute: () => { return count++; }
|
||||
prepare: () => count++,
|
||||
execute: () => count++,
|
||||
});
|
||||
sampler.sample().then((_) => {
|
||||
expect(count).toBe(4);
|
||||
@ -73,8 +73,8 @@ export function main() {
|
||||
|
||||
it('should call prepare, beginMeasure, execute, endMeasure for every iteration',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var workCount = 0;
|
||||
var log: any[] = [];
|
||||
let workCount = 0;
|
||||
const log: any[] = [];
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
@ -98,8 +98,8 @@ export function main() {
|
||||
|
||||
it('should call execute, endMeasure for every iteration if there is no prepare callback',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var workCount = 0;
|
||||
const log: any[] = [];
|
||||
let workCount = 0;
|
||||
createSampler({
|
||||
metric: createCountingMetric(log),
|
||||
validator: createCountingValidator(2),
|
||||
@ -120,14 +120,14 @@ export function main() {
|
||||
|
||||
it('should only collect metrics for execute and ignore metrics from prepare',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var scriptTime = 0;
|
||||
var iterationCount = 1;
|
||||
let scriptTime = 0;
|
||||
let iterationCount = 1;
|
||||
createSampler({
|
||||
validator: createCountingValidator(2),
|
||||
metric: new MockMetric(
|
||||
[],
|
||||
() => {
|
||||
var result = Promise.resolve({'script': scriptTime});
|
||||
const result = Promise.resolve({'script': scriptTime});
|
||||
scriptTime = 0;
|
||||
return result;
|
||||
}),
|
||||
@ -147,8 +147,8 @@ export function main() {
|
||||
|
||||
it('should call the validator for every execution and store the valid sample',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var validSample = [mv(null, null, {})];
|
||||
const log: any[] = [];
|
||||
const validSample = [mv(null, null, {})];
|
||||
|
||||
createSampler({
|
||||
metric: createCountingMetric(),
|
||||
@ -174,8 +174,8 @@ export function main() {
|
||||
|
||||
it('should report the metric values',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var log: any[] = [];
|
||||
var validSample = [mv(null, null, {})];
|
||||
const log: any[] = [];
|
||||
const validSample = [mv(null, null, {})];
|
||||
createSampler({
|
||||
validator: createCountingValidator(2, validSample),
|
||||
metric: createCountingMetric(),
|
||||
@ -204,7 +204,7 @@ export function main() {
|
||||
}
|
||||
|
||||
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(
|
||||
@ -212,7 +212,7 @@ function createCountingValidator(
|
||||
return new MockValidator(log, (completeSample: MeasureValues[]) => {
|
||||
count--;
|
||||
if (count === 0) {
|
||||
return isPresent(validSample) ? validSample : completeSample;
|
||||
return validSample || completeSample;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -220,8 +220,8 @@ function createCountingValidator(
|
||||
}
|
||||
|
||||
function createCountingMetric(log: any[] = []) {
|
||||
var scriptTime = 0;
|
||||
return new MockMetric(log, () => { return {'script': scriptTime++}; });
|
||||
let scriptTime = 0;
|
||||
return new MockMetric(log, () => ({'script': scriptTime++}));
|
||||
}
|
||||
|
||||
class MockDriverAdapter extends WebDriverAdapter {
|
||||
@ -239,7 +239,8 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
class MockValidator extends Validator {
|
||||
constructor(private _log: any[] = [], private _validate: Function = null) { super(); }
|
||||
validate(completeSample: MeasureValues[]): MeasureValues[] {
|
||||
var stableSample = isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||
const stableSample =
|
||||
isPresent(this._validate) ? this._validate(completeSample) : completeSample;
|
||||
this._log.push(['validate', completeSample, stableSample]);
|
||||
return stableSample;
|
||||
}
|
||||
@ -252,7 +253,7 @@ class MockMetric extends Metric {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
endMeasure(restart: boolean) {
|
||||
var measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||
const measureValues = isPresent(this._endMeasure) ? this._endMeasure() : {};
|
||||
this._log.push(['endMeasure', restart, measureValues]);
|
||||
return Promise.resolve(measureValues);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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';
|
||||
|
||||
export function main() {
|
||||
|
@ -13,7 +13,7 @@ export class TraceEventFactory {
|
||||
constructor(private _cat: string, private _pid: string) {}
|
||||
|
||||
create(ph: any, name: string, time: number, args: any = null) {
|
||||
var res:
|
||||
const res:
|
||||
PerfLogEvent = {'name': name, 'cat': this._cat, 'ph': ph, 'ts': time, 'pid': this._pid};
|
||||
if (isPresent(args)) {
|
||||
res['args'] = args;
|
||||
@ -34,7 +34,7 @@ export class TraceEventFactory {
|
||||
}
|
||||
|
||||
complete(name: string, time: number, duration: number, args: any = null) {
|
||||
var res = this.create('X', name, time, args);
|
||||
const res = this.create('X', name, time, args);
|
||||
res['dur'] = duration;
|
||||
return res;
|
||||
}
|
||||
|
@ -6,15 +6,13 @@
|
||||
* 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 {ListWrapper} from '../../src/facade/collection';
|
||||
import {Date, DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('regression slope validator', () => {
|
||||
var validator: RegressionSlopeValidator;
|
||||
let validator: RegressionSlopeValidator;
|
||||
|
||||
function createValidator({size, metric}: {size: number, metric: string}) {
|
||||
validator = ReflectiveInjector
|
||||
@ -44,23 +42,21 @@ export function main() {
|
||||
|
||||
it('should return the last sampleSize runs when the regression slope is ==0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||
const sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 1}), mv(2, 2, {'script': 1})];
|
||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||
});
|
||||
|
||||
it('should return the last sampleSize runs when the regression slope is >0', () => {
|
||||
createValidator({size: 2, metric: 'script'});
|
||||
var sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||
const sample = [mv(0, 0, {'script': 1}), mv(1, 1, {'script': 2}), mv(2, 2, {'script': 3})];
|
||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
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,15 +6,13 @@
|
||||
* 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 {ListWrapper} from '../../src/facade/collection';
|
||||
import {Date, DateWrapper} from '../../src/facade/lang';
|
||||
import {MeasureValues, ReflectiveInjector, SizeValidator} from '../../index';
|
||||
|
||||
export function main() {
|
||||
describe('size validator', () => {
|
||||
var validator: SizeValidator;
|
||||
let validator: SizeValidator;
|
||||
|
||||
function createValidator(size: number) {
|
||||
validator =
|
||||
@ -37,15 +35,14 @@ export function main() {
|
||||
|
||||
it('should return the last sampleSize runs when it has at least the given size', () => {
|
||||
createValidator(2);
|
||||
var sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||
expect(validator.validate(ListWrapper.slice(sample, 0, 2)))
|
||||
.toEqual(ListWrapper.slice(sample, 0, 2));
|
||||
expect(validator.validate(sample)).toEqual(ListWrapper.slice(sample, 1, 3));
|
||||
const sample = [mv(0, 0, {'a': 1}), mv(1, 1, {'b': 2}), mv(2, 2, {'c': 3})];
|
||||
expect(validator.validate(sample.slice(0, 2))).toEqual(sample.slice(0, 2));
|
||||
expect(validator.validate(sample)).toEqual(sample.slice(1, 3));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
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 {StringWrapper, isPresent} from '../src/facade/lang';
|
||||
import {isPresent} from '../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
function createExtension(ids: any[], caps: any) {
|
||||
@ -17,7 +17,7 @@ export function main() {
|
||||
try {
|
||||
res(ReflectiveInjector
|
||||
.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},
|
||||
WebDriverExtension.provideFirstSupported(ids)
|
||||
])
|
||||
@ -52,6 +52,6 @@ class MockExtension extends WebDriverExtension {
|
||||
constructor(public id: string) { super(); }
|
||||
|
||||
supports(capabilities: {[key: string]: any}): boolean {
|
||||
return StringWrapper.equals(capabilities['browser'], this.id);
|
||||
return capabilities['browser'] === this.id;
|
||||
}
|
||||
}
|
||||
|
@ -6,36 +6,36 @@
|
||||
* 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 {Json, isBlank} from '../../src/facade/lang';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('chrome driver extension', () => {
|
||||
var CHROME45_USER_AGENT =
|
||||
const 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"';
|
||||
|
||||
var log: any[];
|
||||
var extension: ChromeDriverExtension;
|
||||
let log: any[];
|
||||
let extension: ChromeDriverExtension;
|
||||
|
||||
var blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||
var v8Events = new TraceEventFactory('v8', 'pid0');
|
||||
var v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||
var chromeTimelineEvents =
|
||||
const blinkEvents = new TraceEventFactory('blink.console', 'pid0');
|
||||
const v8Events = new TraceEventFactory('v8', 'pid0');
|
||||
const v8EventsOtherProcess = new TraceEventFactory('v8', 'pid1');
|
||||
const chromeTimelineEvents =
|
||||
new TraceEventFactory('disabled-by-default-devtools.timeline', 'pid0');
|
||||
var chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
||||
var chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
||||
var chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
||||
var chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
||||
var benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
const chrome45TimelineEvents = new TraceEventFactory('devtools.timeline', 'pid0');
|
||||
const chromeTimelineV8Events = new TraceEventFactory('devtools.timeline,v8', 'pid0');
|
||||
const chromeBlinkTimelineEvents = new TraceEventFactory('blink,devtools.timeline', 'pid0');
|
||||
const chromeBlinkUserTimingEvents = new TraceEventFactory('blink.user_timing', 'pid0');
|
||||
const benchmarkEvents = new TraceEventFactory('benchmark', 'pid0');
|
||||
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(
|
||||
perfRecords: any[] = null, userAgent: string = null,
|
||||
messageMethod = 'Tracing.dataCollected'): WebDriverExtension {
|
||||
if (isBlank(perfRecords)) {
|
||||
if (!perfRecords) {
|
||||
perfRecords = [];
|
||||
}
|
||||
if (isBlank(userAgent)) {
|
||||
@ -101,7 +101,7 @@ export function main() {
|
||||
|
||||
it('should normalize "tdur" to "dur"',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||
const event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||
event['tdur'] = 5500;
|
||||
createExtension([event]).readPerfLog().then((events) => {
|
||||
expect(events).toEqual([
|
||||
@ -396,11 +396,11 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
logs(type: string) {
|
||||
this._log.push(['logs', type]);
|
||||
if (type === 'performance') {
|
||||
return Promise.resolve(this._events.map((event) => {
|
||||
return {
|
||||
'message': Json.stringify({'message': {'method': this._messageMethod, 'params': event}})
|
||||
};
|
||||
}));
|
||||
return Promise.resolve(this._events.map(
|
||||
(event) => ({
|
||||
'message': JSON.stringify(
|
||||
{'message': {'method': this._messageMethod, 'params': event}}, null, 2)
|
||||
})));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -6,21 +6,20 @@
|
||||
* 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 {Json, isBlank, isPresent} from '../../src/facade/lang';
|
||||
import {TraceEventFactory} from '../trace_event_factory';
|
||||
|
||||
export function main() {
|
||||
describe('ios driver extension', () => {
|
||||
var log: any[];
|
||||
var extension: IOsDriverExtension;
|
||||
let log: any[];
|
||||
let extension: IOsDriverExtension;
|
||||
|
||||
var normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
const normEvents = new TraceEventFactory('timeline', 'pid0');
|
||||
|
||||
function createExtension(perfRecords: any[] = null): WebDriverExtension {
|
||||
if (isBlank(perfRecords)) {
|
||||
if (!perfRecords) {
|
||||
perfRecords = [];
|
||||
}
|
||||
log = [];
|
||||
@ -156,7 +155,7 @@ function timeEndRecord(name: string, time: number) {
|
||||
}
|
||||
|
||||
function durationRecord(type: string, startTime: number, endTime: number, children: any[] = null) {
|
||||
if (isBlank(children)) {
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
return {'type': type, 'startTime': startTime, 'endTime': endTime, 'children': children};
|
||||
@ -184,8 +183,9 @@ class MockDriverAdapter extends WebDriverAdapter {
|
||||
if (type === 'performance') {
|
||||
return Promise.resolve(this._perfRecords.map(function(record) {
|
||||
return {
|
||||
'message': Json.stringify(
|
||||
{'message': {'method': 'Timeline.eventRecorded', 'params': {'record': record}}})
|
||||
'message': JSON.stringify(
|
||||
{'message': {'method': 'Timeline.eventRecorded', 'params': {'record': record}}}, null,
|
||||
2)
|
||||
};
|
||||
}));
|
||||
} else {
|
||||
|
@ -11,9 +11,6 @@
|
||||
* @description
|
||||
* Entry point for all public APIs of the common package.
|
||||
*/
|
||||
export * from './src/location';
|
||||
export {NgLocalization} from './src/localization';
|
||||
export {CommonModule} from './src/common_module';
|
||||
export * from './src/common';
|
||||
|
||||
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';
|
||||
// This file only reexports content of the `src` folder. Keep it that way.
|
||||
|
@ -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 {
|
||||
entry: '../../../dist/packages-dist/common/testing/index.js',
|
||||
@ -10,4 +17,4 @@ export default {
|
||||
'rxjs/Observable': '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 {
|
||||
entry: '../../../dist/packages-dist/common/index.js',
|
||||
@ -7,6 +14,6 @@ export default {
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Subject': 'Rx'
|
||||
'rxjs/Subject': 'Rx',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
20
modules/@angular/common/src/common.ts
Normal file
20
modules/@angular/common/src/common.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @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
|
||||
* Entry point for all public APIs of the common package.
|
||||
*/
|
||||
export * from './location/index';
|
||||
export {NgLocalization} from './localization';
|
||||
export {CommonModule} from './common_module';
|
||||
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
|
||||
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
|
||||
export {VERSION} from './version';
|
||||
export {Version} from '@angular/core';
|
@ -9,9 +9,7 @@
|
||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {isListLikeIterable} from '../facade/collection';
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
import {stringify} from '../facade/lang';
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
@ -27,15 +25,17 @@ import {isPresent} from '../facade/lang';
|
||||
* <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
|
||||
*
|
||||
* <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
|
||||
*
|
||||
* <some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
|
||||
* ```
|
||||
*
|
||||
* @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.
|
||||
* The CSS classes are updated as follows, depending on the type of the expression evaluation:
|
||||
* - `string` - the CSS classes listed in the string (space delimited) are added,
|
||||
* - `Array` - the CSS classes declared as Array elements are added,
|
||||
* - `Object` - keys are CSS classes that get added when the expression given in the value
|
||||
* evaluates to a truthy value, otherwise they are removed.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -50,7 +50,6 @@ export class NgClass implements DoCheck {
|
||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
|
||||
@Input('class')
|
||||
set klass(v: string) {
|
||||
this._applyInitialClasses(true);
|
||||
@ -111,8 +110,14 @@ export class NgClass implements DoCheck {
|
||||
}
|
||||
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem(
|
||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, true));
|
||||
changes.forEachAddedItem((record: CollectionChangeRecord) => {
|
||||
if (typeof record.item === 'string') {
|
||||
this._toggleClass(record.item, true);
|
||||
} else {
|
||||
throw new Error(
|
||||
`NgClass can only toggle CSS classes expressed as strings, got ${stringify(record.item)}`);
|
||||
}
|
||||
});
|
||||
|
||||
changes.forEachRemovedItem(
|
||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
||||
@ -129,7 +134,7 @@ export class NgClass implements DoCheck {
|
||||
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
||||
} else {
|
||||
Object.keys(rawClassVal).forEach(klass => {
|
||||
if (isPresent(rawClassVal[klass])) this._toggleClass(klass, !isCleanup);
|
||||
if (rawClassVal[klass] != null) this._toggleClass(klass, !isCleanup);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
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, isDevMode} from '@angular/core';
|
||||
|
||||
import {getTypeNameForDebugging} from '../facade/lang';
|
||||
|
||||
@ -89,9 +89,23 @@ export class NgForRow {
|
||||
@Directive({selector: '[ngFor][ngForOf]'})
|
||||
export class NgFor implements DoCheck, OnChanges {
|
||||
@Input() ngForOf: any;
|
||||
@Input() ngForTrackBy: TrackByFn;
|
||||
@Input()
|
||||
set ngForTrackBy(fn: TrackByFn) {
|
||||
if (isDevMode() && fn != null && typeof fn !== 'function') {
|
||||
// TODO(vicb): use a log service once there is a public one available
|
||||
if (<any>console && <any>console.warn) {
|
||||
console.warn(
|
||||
`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
|
||||
`See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
|
||||
}
|
||||
}
|
||||
this._trackByFn = fn;
|
||||
}
|
||||
|
||||
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
|
||||
|
||||
private _differ: IterableDiffer = null;
|
||||
private _trackByFn: TrackByFn;
|
||||
|
||||
constructor(
|
||||
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
|
||||
@ -119,7 +133,7 @@ export class NgFor implements DoCheck, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
ngDoCheck(): void {
|
||||
if (this._differ) {
|
||||
const changes = this._differ.diff(this.ngForOf);
|
||||
if (changes) this._applyChanges(changes);
|
||||
@ -150,13 +164,13 @@ export class NgFor implements DoCheck, OnChanges {
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
let viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
@ -11,14 +11,14 @@ import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
/**
|
||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||
*
|
||||
* If the expression assigned to `ngIf` evaluates to a false value then the element
|
||||
* If the expression assigned to `ngIf` evaluates to a falsy value then the element
|
||||
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* <div *ngIf="errorCount > 0" class="error">
|
||||
* <!-- Error message displayed when the errorCount property on the current context is greater
|
||||
* <!-- Error message displayed when the errorCount property in the current context is greater
|
||||
* than 0. -->
|
||||
* {{errorCount}} errors detected
|
||||
* </div>
|
||||
@ -34,7 +34,7 @@ import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
*/
|
||||
@Directive({selector: '[ngIf]'})
|
||||
export class NgIf {
|
||||
private _hasView: boolean = false;
|
||||
private _hasView = false;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _template: TemplateRef<Object>) {}
|
||||
|
||||
|
@ -21,10 +21,9 @@ import {SwitchView} from './ng_switch';
|
||||
* @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>
|
||||
* <template ngPluralCase="=0">there is nothing</template>
|
||||
* <template ngPluralCase="=1">there is one</template>
|
||||
* <template ngPluralCase="few">there are a few</template>
|
||||
* </some-element>
|
||||
* ```
|
||||
*
|
||||
@ -61,8 +60,7 @@ export class NgPlural {
|
||||
|
||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
private _updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
const cases = Object.keys(this._caseViews);
|
||||
@ -70,13 +68,11 @@ export class NgPlural {
|
||||
this._activateView(this._caseViews[key]);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_clearViews() {
|
||||
private _clearViews() {
|
||||
if (this._activeView) this._activeView.destroy();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateView(view: SwitchView) {
|
||||
private _activateView(view: SwitchView) {
|
||||
if (view) {
|
||||
this._activeView = view;
|
||||
this._activeView.create();
|
||||
@ -93,8 +89,8 @@ export class NgPlural {
|
||||
* @howToUse
|
||||
* ```
|
||||
* <some-element [ngPlural]="value">
|
||||
* <ng-container *ngPluralCase="'=0'">...</ng-container>
|
||||
* <ng-container *ngPluralCase="'other'">...</ng-container>
|
||||
* <template ngPluralCase="=0">...</template>
|
||||
* <template ngPluralCase="other">...</template>
|
||||
* </some-element>
|
||||
*```
|
||||
*
|
||||
@ -107,6 +103,7 @@ export class NgPluralCase {
|
||||
constructor(
|
||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
|
||||
ngPlural.addCase(value, new SwitchView(viewContainer, template));
|
||||
const isANumber: boolean = !isNaN(Number(value));
|
||||
ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template));
|
||||
}
|
||||
}
|
||||
|
@ -25,17 +25,15 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
||||
* @description
|
||||
*
|
||||
* 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'),
|
||||
* - keys are style names with an optional `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||
* - values are the values assigned to those properties (expressed in the given unit).
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngStyle]'})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_ngStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
private _ngStyle: {[key: string]: string};
|
||||
private _differ: KeyValueDiffer;
|
||||
|
||||
constructor(
|
||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
@ -69,7 +67,7 @@ export class NgStyle implements DoCheck {
|
||||
|
||||
private _setStyle(nameAndUnit: string, value: string): void {
|
||||
const [name, unit] = nameAndUnit.split('.');
|
||||
value = value !== null && value !== void(0) && unit ? `${value}${unit}` : value;
|
||||
value = value && unit ? `${value}${unit}` : value;
|
||||
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
|
||||
}
|
||||
|
@ -6,19 +6,31 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
|
||||
const _CASE_DEFAULT = new Object();
|
||||
import {Directive, DoCheck, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
export class SwitchView {
|
||||
private _created = false;
|
||||
|
||||
constructor(
|
||||
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
||||
|
||||
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||
create(): void {
|
||||
this._created = true;
|
||||
this._viewContainerRef.createEmbeddedView(this._templateRef);
|
||||
}
|
||||
|
||||
destroy(): void { this._viewContainerRef.clear(); }
|
||||
destroy(): void {
|
||||
this._created = false;
|
||||
this._viewContainerRef.clear();
|
||||
}
|
||||
|
||||
enforceState(created: boolean) {
|
||||
if (created && !this._created) {
|
||||
this.create();
|
||||
} else if (!created && this._created) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,7 +50,7 @@ export class SwitchView {
|
||||
* <inner-element></inner-element>
|
||||
* <inner-other-element></inner-other-element>
|
||||
* </ng-container>
|
||||
* <some-element *ngSwitchDefault>...</p>
|
||||
* <some-element *ngSwitchDefault>...</some-element>
|
||||
* </container-element>
|
||||
* ```
|
||||
* @description
|
||||
@ -53,8 +65,7 @@ export class SwitchView {
|
||||
* root elements.
|
||||
*
|
||||
* Elements within `NgSwitch` but outside of a `NgSwitchCase` or `NgSwitchDefault` directives will
|
||||
* be
|
||||
* preserved at the location.
|
||||
* be preserved at the location.
|
||||
*
|
||||
* The `ngSwitchCase` directive informs the parent `NgSwitch` of which view to display when the
|
||||
* expression is evaluated.
|
||||
@ -65,91 +76,52 @@ export class SwitchView {
|
||||
*/
|
||||
@Directive({selector: '[ngSwitch]'})
|
||||
export class NgSwitch {
|
||||
private _switchValue: any;
|
||||
private _useDefault: boolean = false;
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
private _defaultViews: SwitchView[];
|
||||
private _defaultUsed = false;
|
||||
private _caseCount = 0;
|
||||
private _lastCaseCheckIndex = 0;
|
||||
private _lastCasesMatched = false;
|
||||
private _ngSwitch: any;
|
||||
|
||||
@Input()
|
||||
set ngSwitch(value: any) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViews();
|
||||
|
||||
// Add the ViewContainers matching the value (with a fallback to default)
|
||||
this._useDefault = false;
|
||||
let views = this._valueViews.get(value);
|
||||
if (!views) {
|
||||
this._useDefault = true;
|
||||
views = this._valueViews.get(_CASE_DEFAULT) || null;
|
||||
set ngSwitch(newValue: any) {
|
||||
this._ngSwitch = newValue;
|
||||
if (this._caseCount === 0) {
|
||||
this._updateDefaultCases(true);
|
||||
}
|
||||
this._activateViews(views);
|
||||
|
||||
this._switchValue = value;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_onCaseValueChanged(oldCase: any, newCase: any, view: SwitchView): void {
|
||||
this._deregisterView(oldCase, view);
|
||||
this._registerView(newCase, view);
|
||||
_addCase(): number { return this._caseCount++; }
|
||||
|
||||
if (oldCase === this._switchValue) {
|
||||
view.destroy();
|
||||
ListWrapper.remove(this._activeViews, view);
|
||||
} else if (newCase === this._switchValue) {
|
||||
if (this._useDefault) {
|
||||
this._useDefault = false;
|
||||
this._emptyAllActiveViews();
|
||||
/** @internal */
|
||||
_addDefault(view: SwitchView) {
|
||||
if (!this._defaultViews) {
|
||||
this._defaultViews = [];
|
||||
}
|
||||
this._defaultViews.push(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_matchCase(value: any): boolean {
|
||||
const matched = value == this._ngSwitch;
|
||||
this._lastCasesMatched = this._lastCasesMatched || matched;
|
||||
this._lastCaseCheckIndex++;
|
||||
if (this._lastCaseCheckIndex === this._caseCount) {
|
||||
this._updateDefaultCases(!this._lastCasesMatched);
|
||||
this._lastCaseCheckIndex = 0;
|
||||
this._lastCasesMatched = false;
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
private _updateDefaultCases(useDefault: boolean) {
|
||||
if (this._defaultViews && useDefault !== this._defaultUsed) {
|
||||
this._defaultUsed = useDefault;
|
||||
for (let i = 0; i < this._defaultViews.length; i++) {
|
||||
const defaultView = this._defaultViews[i];
|
||||
defaultView.enforceState(useDefault);
|
||||
}
|
||||
view.create();
|
||||
this._activeViews.push(view);
|
||||
}
|
||||
|
||||
// Switch to default when there is no more active ViewContainers
|
||||
if (this._activeViews.length === 0 && !this._useDefault) {
|
||||
this._useDefault = true;
|
||||
this._activateViews(this._valueViews.get(_CASE_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_emptyAllActiveViews(): void {
|
||||
const activeContainers = this._activeViews;
|
||||
for (var i = 0; i < activeContainers.length; i++) {
|
||||
activeContainers[i].destroy();
|
||||
}
|
||||
this._activeViews = [];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateViews(views: SwitchView[]): void {
|
||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
||||
if (views) {
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
views[i].create();
|
||||
}
|
||||
this._activeViews = views;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_registerView(value: any, view: SwitchView): void {
|
||||
let views = this._valueViews.get(value);
|
||||
if (!views) {
|
||||
views = [];
|
||||
this._valueViews.set(value, views);
|
||||
}
|
||||
views.push(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_deregisterView(value: any, view: SwitchView): void {
|
||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||
if (value === _CASE_DEFAULT) return;
|
||||
const views = this._valueViews.get(value);
|
||||
if (views.length == 1) {
|
||||
this._valueViews.delete(value);
|
||||
} else {
|
||||
ListWrapper.remove(views, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,26 +151,20 @@ export class NgSwitch {
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchCase]'})
|
||||
export class NgSwitchCase {
|
||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||
/** @internal */
|
||||
_value: any = _CASE_DEFAULT;
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
private _switch: NgSwitch;
|
||||
export class NgSwitchCase implements DoCheck {
|
||||
private _view: SwitchView;
|
||||
|
||||
@Input()
|
||||
ngSwitchCase: any;
|
||||
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
this._switch = ngSwitch;
|
||||
@Host() private ngSwitch: NgSwitch) {
|
||||
ngSwitch._addCase();
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchCase(value: any) {
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +194,7 @@ export class NgSwitchCase {
|
||||
export class NgSwitchDefault {
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() sswitch: NgSwitch) {
|
||||
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
ngSwitch._addDefault(new SwitchView(viewContainer, templateRef));
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,23 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
|
||||
*/
|
||||
export function getPluralCategory(
|
||||
value: number, cases: string[], ngLocalization: NgLocalization): string {
|
||||
const nbCase = `=${value}`;
|
||||
let key = `=${value}`;
|
||||
|
||||
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
|
||||
if (cases.indexOf(key) > -1) {
|
||||
return key;
|
||||
}
|
||||
|
||||
key = ngLocalization.getPluralCategory(value);
|
||||
|
||||
if (cases.indexOf(key) > -1) {
|
||||
return key;
|
||||
}
|
||||
|
||||
if (cases.indexOf('other') > -1) {
|
||||
return 'other';
|
||||
}
|
||||
|
||||
throw new Error(`No plural message found for value "${value}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +81,7 @@ export enum Plural {
|
||||
Two,
|
||||
Few,
|
||||
Many,
|
||||
Other
|
||||
Other,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,13 +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
|
||||
*/
|
||||
|
||||
export * from './location/platform_location';
|
||||
export * from './location/location_strategy';
|
||||
export * from './location/hash_location_strategy';
|
||||
export * from './location/path_location_strategy';
|
||||
export * from './location/location';
|
@ -17,6 +17,8 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
||||
|
||||
|
||||
/**
|
||||
* @whatItDoes Use URL hash for storing application location data.
|
||||
* @description
|
||||
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||
* {@link Location} service to represent its state in the
|
||||
* [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
|
||||
@ -27,18 +29,7 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component, NgModule} from '@angular/core';
|
||||
* import {
|
||||
* LocationStrategy,
|
||||
* HashLocationStrategy
|
||||
* } from '@angular/common';
|
||||
*
|
||||
* @NgModule({
|
||||
* providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]
|
||||
* })
|
||||
* class AppModule {}
|
||||
* ```
|
||||
* {@example common/location/ts/hash_location_component.ts region='LocationComponent'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -64,19 +55,19 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
path(includeHash: boolean = false): string {
|
||||
// the hash value is always prefixed with a `#`
|
||||
// and if it is empty then it will stay empty
|
||||
var path = this._platformLocation.hash;
|
||||
let path = this._platformLocation.hash;
|
||||
if (!isPresent(path)) path = '#';
|
||||
|
||||
return path.length > 0 ? path.substring(1) : path;
|
||||
}
|
||||
|
||||
prepareExternalUrl(internal: string): string {
|
||||
var url = Location.joinWithSlash(this._baseHref, internal);
|
||||
const url = Location.joinWithSlash(this._baseHref, internal);
|
||||
return url.length > 0 ? ('#' + url) : url;
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
let url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
@ -84,7 +75,7 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, path: string, queryParams: string) {
|
||||
var url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
let url = this.prepareExternalUrl(path + Location.normalizeQueryParams(queryParams));
|
||||
if (url.length == 0) {
|
||||
url = this._platformLocation.pathname;
|
||||
}
|
||||
|
13
modules/@angular/common/src/location/index.ts
Normal file
13
modules/@angular/common/src/location/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @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 * from './platform_location';
|
||||
export * from './location_strategy';
|
||||
export * from './hash_location_strategy';
|
||||
export * from './path_location_strategy';
|
||||
export * from './location';
|
@ -12,7 +12,8 @@ import {LocationStrategy} from './location_strategy';
|
||||
|
||||
|
||||
/**
|
||||
* `Location` is a service that applications can use to interact with a browser's URL.
|
||||
* @whatItDoes `Location` is a service that applications can use to interact with a browser's URL.
|
||||
* @description
|
||||
* Depending on which {@link LocationStrategy} is used, `Location` will either persist
|
||||
* to the URL's path or the URL's hash segment.
|
||||
*
|
||||
@ -28,19 +29,7 @@ import {LocationStrategy} from './location_strategy';
|
||||
* - `/my/app/user/123/` **is not** normalized
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {Location} from '@angular/common';
|
||||
*
|
||||
* @Component({selector: 'app-component'})
|
||||
* class AppCmp {
|
||||
* constructor(location: Location) {
|
||||
* location.go('/foo');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
|
||||
* @stable
|
||||
*/
|
||||
@Injectable()
|
||||
@ -49,16 +38,20 @@ export class Location {
|
||||
_subject: EventEmitter<any> = new EventEmitter();
|
||||
/** @internal */
|
||||
_baseHref: string;
|
||||
|
||||
/** @internal */
|
||||
_platformStrategy: LocationStrategy;
|
||||
|
||||
constructor(platformStrategy: LocationStrategy) {
|
||||
this._platformStrategy = platformStrategy;
|
||||
var browserBaseHref = this._platformStrategy.getBaseHref();
|
||||
const browserBaseHref = this._platformStrategy.getBaseHref();
|
||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||
this._platformStrategy.onPopState(
|
||||
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
|
||||
this._platformStrategy.onPopState((ev) => {
|
||||
this._subject.emit({
|
||||
'url': this.path(true),
|
||||
'pop': true,
|
||||
'type': ev.type,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,7 +85,7 @@ export class Location {
|
||||
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
||||
*/
|
||||
prepareExternalUrl(url: string): string {
|
||||
if (url.length > 0 && !url.startsWith('/')) {
|
||||
if (url && url[0] !== '/') {
|
||||
url = '/' + url;
|
||||
}
|
||||
return this._platformStrategy.prepareExternalUrl(url);
|
||||
@ -139,7 +132,7 @@ export class Location {
|
||||
* is.
|
||||
*/
|
||||
public static normalizeQueryParams(params: string): string {
|
||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||
return params && params[0] !== '?' ? '?' + params : params;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,7 +145,7 @@ export class Location {
|
||||
if (end.length == 0) {
|
||||
return start;
|
||||
}
|
||||
var slashes = 0;
|
||||
let slashes = 0;
|
||||
if (start.endsWith('/')) {
|
||||
slashes++;
|
||||
}
|
||||
@ -171,25 +164,13 @@ export class Location {
|
||||
/**
|
||||
* If url has a trailing slash, remove it, otherwise return url as is.
|
||||
*/
|
||||
public static stripTrailingSlash(url: string): string {
|
||||
if (/\/$/g.test(url)) {
|
||||
url = url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
public static stripTrailingSlash(url: string): string { return url.replace(/\/$/, ''); }
|
||||
}
|
||||
|
||||
function _stripBaseHref(baseHref: string, url: string): string {
|
||||
if (baseHref.length > 0 && url.startsWith(baseHref)) {
|
||||
return url.substring(baseHref.length);
|
||||
}
|
||||
return url;
|
||||
return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
|
||||
}
|
||||
|
||||
function _stripIndexHtml(url: string): string {
|
||||
if (/\/index.html$/g.test(url)) {
|
||||
// '/index.html'.length == 11
|
||||
return url.substring(0, url.length - 11);
|
||||
}
|
||||
return url;
|
||||
return url.replace(/\/index.html$/, '');
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {LocationChangeListener} from './platform_location';
|
||||
/**
|
||||
* `LocationStrategy` is responsible for representing and reading route state
|
||||
* from the browser's URL. Angular provides two strategies:
|
||||
* {@link HashLocationStrategy} and {@link PathLocationStrategy} (default).
|
||||
* {@link HashLocationStrategy} and {@link PathLocationStrategy}.
|
||||
*
|
||||
* This is used under the hood of the {@link Location} service.
|
||||
*
|
||||
|
@ -17,14 +17,13 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
||||
|
||||
|
||||
/**
|
||||
* @whatItDoes Use URL for storing application location data.
|
||||
* @description
|
||||
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||
* {@link Location} service to represent its state in the
|
||||
* [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
|
||||
* browser's URL.
|
||||
*
|
||||
* `PathLocationStrategy` is the default binding for {@link LocationStrategy}
|
||||
* provided in {@link ROUTER_PROVIDERS}.
|
||||
*
|
||||
* If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
|
||||
* or add a base element to the document. This URL prefix that will be preserved
|
||||
* when generating and recognizing URLs.
|
||||
@ -37,6 +36,10 @@ import {LocationChangeListener, PlatformLocation} from './platform_location';
|
||||
* `location.go('/foo')`, the browser's URL will become
|
||||
* `example.com/my/app/foo`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example common/location/ts/path_location_component.ts region='LocationComponent'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Injectable()
|
||||
@ -79,12 +82,12 @@ export class PathLocationStrategy extends LocationStrategy {
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
const externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
this._platformLocation.pushState(state, title, externalUrl);
|
||||
}
|
||||
|
||||
replaceState(state: any, title: string, url: string, queryParams: string) {
|
||||
var externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
const externalUrl = this.prepareExternalUrl(url + Location.normalizeQueryParams(queryParams));
|
||||
this._platformLocation.replaceState(state, title, externalUrl);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ const _observableStrategy = new ObservableStrategy();
|
||||
* {@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
|
||||
* to the view. The Observable continuesly updates the view with the current time.
|
||||
* to the view. The Observable continuously updates the view with the current time.
|
||||
*
|
||||
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||
*
|
||||
|
@ -7,11 +7,14 @@
|
||||
*/
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||
import {DateFormatter} from '../facade/intl';
|
||||
import {NumberWrapper, isBlank, isDate} from '../facade/lang';
|
||||
|
||||
import {NumberWrapper, isDate} from '../facade/lang';
|
||||
|
||||
import {DateFormatter} from './intl';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Formats a date according to locale rules.
|
||||
@ -33,27 +36,30 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||
*
|
||||
*
|
||||
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
||||
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
||||
* | era | G | G (AD) | GGGG (Anno Domini)| - | - |
|
||||
* | year | y | - | - | y (2015) | yy (15) |
|
||||
* | month | M | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
||||
* | day | d | - | - | d (3) | dd (03) |
|
||||
* | weekday | E | EEE (Sun) | EEEE (Sunday) | - | - |
|
||||
* | hour | j | - | - | j (13) | jj (13) |
|
||||
* | hour12 | h | - | - | h (1 PM) | hh (01 PM)|
|
||||
* | hour24 | H | - | - | H (13) | HH (13) |
|
||||
* | minute | m | - | - | m (5) | mm (05) |
|
||||
* | second | s | - | - | s (9) | ss (09) |
|
||||
* | timezone | z | - | z (Pacific Standard Time)| - | - |
|
||||
* | timezone | Z | Z (GMT-8:00) | - | - | - |
|
||||
* | timezone | a | a (PM) | - | - | - |
|
||||
* | Component | Symbol | Narrow | Short Form | Long Form | Numeric | 2-digit |
|
||||
* |-----------|:------:|--------|--------------|-------------------|-----------|-----------|
|
||||
* | era | G | G (A) | GGG (AD) | GGGG (Anno Domini)| - | - |
|
||||
* | year | y | - | - | - | y (2015) | yy (15) |
|
||||
* | month | M | L (S) | MMM (Sep) | MMMM (September) | M (9) | MM (09) |
|
||||
* | day | d | - | - | - | d (3) | dd (03) |
|
||||
* | weekday | E | E (S) | EEE (Sun) | EEEE (Sunday) | - | - |
|
||||
* | hour | j | - | - | - | j (13) | jj (13) |
|
||||
* | hour12 | h | - | - | - | h (1 PM) | hh (01 PM)|
|
||||
* | hour24 | H | - | - | - | H (13) | HH (13) |
|
||||
* | minute | m | - | - | - | m (5) | mm (05) |
|
||||
* | second | s | - | - | - | s (9) | ss (09) |
|
||||
* | timezone | z | - | - | z (Pacific Standard Time)| - | - |
|
||||
* | timezone | Z | - | Z (GMT-8:00) | - | - | - |
|
||||
* | timezone | a | - | a (PM) | - | - | - |
|
||||
*
|
||||
* In javascript, only the components specified will be respected (not the ordering,
|
||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||
*
|
||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||
*
|
||||
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
|
||||
* applied and the formatted text will have the same day, month and year of the expression.
|
||||
*
|
||||
* WARNINGS:
|
||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||
* Instead users should treat the date as an immutable object and change the reference when the
|
||||
@ -95,22 +101,42 @@ export class DatePipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private _locale: string) {}
|
||||
|
||||
transform(value: any, pattern: string = 'mediumDate'): string {
|
||||
let date: Date;
|
||||
|
||||
if (isBlank(value)) return null;
|
||||
|
||||
if (!this.supports(value)) {
|
||||
if (typeof value === 'string') {
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
if (isDate(value)) {
|
||||
date = value;
|
||||
} else if (NumberWrapper.isNumeric(value)) {
|
||||
date = new Date(parseFloat(value));
|
||||
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
|
||||
/**
|
||||
* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
||||
* before Date creation to avoid time offset and errors in the new Date.
|
||||
* If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
||||
* date, some browsers (e.g. IE 9) will throw an invalid Date error
|
||||
* If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
|
||||
* is applied
|
||||
* Note: ISO months are 0 for January, 1 for February, ...
|
||||
*/
|
||||
const [y, m, d] = value.split('-').map((val: string) => parseInt(val, 10));
|
||||
date = new Date(y, m - 1, d);
|
||||
} else {
|
||||
date = new Date(value);
|
||||
}
|
||||
|
||||
if (!isDate(date)) {
|
||||
throw new InvalidPipeArgumentError(DatePipe, value);
|
||||
}
|
||||
|
||||
if (NumberWrapper.isNumeric(value)) {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
|
||||
return DateFormatter.format(
|
||||
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||
}
|
||||
|
||||
private supports(obj: any): boolean {
|
||||
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
|
||||
(typeof obj === 'string' && isDate(new Date(obj)));
|
||||
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||
}
|
||||
}
|
||||
|
||||
function isBlank(obj: any): boolean {
|
||||
return obj == null || obj === '';
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isStringMap} from '../facade/lang';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
@ -35,9 +34,9 @@ export class I18nPluralPipe implements PipeTransform {
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
transform(value: number, pluralMap: {[count: string]: string}): string {
|
||||
if (isBlank(value)) return '';
|
||||
if (value == null) return '';
|
||||
|
||||
if (!isStringMap(pluralMap)) {
|
||||
if (typeof pluralMap !== 'object' || pluralMap === null) {
|
||||
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isStringMap} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
@ -16,9 +15,10 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* @howToUse `expression | i18nSelect:mapping`
|
||||
* @description
|
||||
*
|
||||
* Where:
|
||||
* - `mapping`: is an object that indicates the text that should be displayed
|
||||
* Where `mapping` is an object that indicates the text that should be displayed
|
||||
* for different values of the provided `expression`.
|
||||
* If none of the keys of the mapping match the value of the `expression`, then the content
|
||||
* of the `other` key is returned when present, otherwise an empty string is returned.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
@ -29,12 +29,20 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
@Pipe({name: 'i18nSelect', pure: true})
|
||||
export class I18nSelectPipe implements PipeTransform {
|
||||
transform(value: string, mapping: {[key: string]: string}): string {
|
||||
if (isBlank(value)) return '';
|
||||
if (value == null) return '';
|
||||
|
||||
if (!isStringMap(mapping)) {
|
||||
if (typeof mapping !== 'object' || typeof value !== 'string') {
|
||||
throw new InvalidPipeArgumentError(I18nSelectPipe, mapping);
|
||||
}
|
||||
|
||||
return mapping.hasOwnProperty(value) ? mapping[value] : '';
|
||||
if (mapping.hasOwnProperty(value)) {
|
||||
return mapping[value];
|
||||
}
|
||||
|
||||
if (mapping.hasOwnProperty('other')) {
|
||||
return mapping['other'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
226
modules/@angular/common/src/pipes/intl.ts
Normal file
226
modules/@angular/common/src/pipes/intl.ts
Normal file
@ -0,0 +1,226 @@
|
||||
/**
|
||||
* @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 enum NumberFormatStyle {
|
||||
Decimal,
|
||||
Percent,
|
||||
Currency,
|
||||
}
|
||||
|
||||
export class NumberFormatter {
|
||||
static format(
|
||||
num: number, locale: string, style: NumberFormatStyle,
|
||||
{minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, currency,
|
||||
currencyAsSymbol = false}: {
|
||||
minimumIntegerDigits?: number,
|
||||
minimumFractionDigits?: number,
|
||||
maximumFractionDigits?: number,
|
||||
currency?: string,
|
||||
currencyAsSymbol?: boolean
|
||||
} = {}): string {
|
||||
const options: Intl.NumberFormatOptions = {
|
||||
minimumIntegerDigits,
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits,
|
||||
style: NumberFormatStyle[style].toLowerCase()
|
||||
};
|
||||
|
||||
if (style == NumberFormatStyle.Currency) {
|
||||
options.currency = currency;
|
||||
options.currencyDisplay = currencyAsSymbol ? 'symbol' : 'code';
|
||||
}
|
||||
return new Intl.NumberFormat(locale, options).format(num);
|
||||
}
|
||||
}
|
||||
|
||||
type DateFormatterFn = (date: Date, locale: string) => string;
|
||||
|
||||
const DATE_FORMATS_SPLIT =
|
||||
/((?:[^yMLdHhmsazZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|z|Z|G+|w+))(.*)/;
|
||||
|
||||
const PATTERN_ALIASES: {[format: string]: DateFormatterFn} = {
|
||||
// Keys are quoted so they do not get renamed during closure compilation.
|
||||
'yMMMdjms': datePartGetterFactory(combine([
|
||||
digitCondition('year', 1),
|
||||
nameCondition('month', 3),
|
||||
digitCondition('day', 1),
|
||||
digitCondition('hour', 1),
|
||||
digitCondition('minute', 1),
|
||||
digitCondition('second', 1),
|
||||
])),
|
||||
'yMdjm': datePartGetterFactory(combine([
|
||||
digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1),
|
||||
digitCondition('hour', 1), digitCondition('minute', 1)
|
||||
])),
|
||||
'yMMMMEEEEd': datePartGetterFactory(combine([
|
||||
digitCondition('year', 1), nameCondition('month', 4), nameCondition('weekday', 4),
|
||||
digitCondition('day', 1)
|
||||
])),
|
||||
'yMMMMd': datePartGetterFactory(
|
||||
combine([digitCondition('year', 1), nameCondition('month', 4), digitCondition('day', 1)])),
|
||||
'yMMMd': datePartGetterFactory(
|
||||
combine([digitCondition('year', 1), nameCondition('month', 3), digitCondition('day', 1)])),
|
||||
'yMd': datePartGetterFactory(
|
||||
combine([digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1)])),
|
||||
'jms': datePartGetterFactory(combine(
|
||||
[digitCondition('hour', 1), digitCondition('second', 1), digitCondition('minute', 1)])),
|
||||
'jm': datePartGetterFactory(combine([digitCondition('hour', 1), digitCondition('minute', 1)]))
|
||||
};
|
||||
|
||||
const DATE_FORMATS: {[format: string]: DateFormatterFn} = {
|
||||
// Keys are quoted so they do not get renamed.
|
||||
'yyyy': datePartGetterFactory(digitCondition('year', 4)),
|
||||
'yy': datePartGetterFactory(digitCondition('year', 2)),
|
||||
'y': datePartGetterFactory(digitCondition('year', 1)),
|
||||
'MMMM': datePartGetterFactory(nameCondition('month', 4)),
|
||||
'MMM': datePartGetterFactory(nameCondition('month', 3)),
|
||||
'MM': datePartGetterFactory(digitCondition('month', 2)),
|
||||
'M': datePartGetterFactory(digitCondition('month', 1)),
|
||||
'LLLL': datePartGetterFactory(nameCondition('month', 4)),
|
||||
'L': datePartGetterFactory(nameCondition('month', 1)),
|
||||
'dd': datePartGetterFactory(digitCondition('day', 2)),
|
||||
'd': datePartGetterFactory(digitCondition('day', 1)),
|
||||
'HH': digitModifier(
|
||||
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false)))),
|
||||
'H': hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))),
|
||||
'hh': digitModifier(
|
||||
hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true)))),
|
||||
'h': hourExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
||||
'jj': datePartGetterFactory(digitCondition('hour', 2)),
|
||||
'j': datePartGetterFactory(digitCondition('hour', 1)),
|
||||
'mm': digitModifier(datePartGetterFactory(digitCondition('minute', 2))),
|
||||
'm': datePartGetterFactory(digitCondition('minute', 1)),
|
||||
'ss': digitModifier(datePartGetterFactory(digitCondition('second', 2))),
|
||||
's': datePartGetterFactory(digitCondition('second', 1)),
|
||||
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
|
||||
// we can be just safely rely on using `sss` since we currently don't support single or two digit
|
||||
// fractions
|
||||
'sss': datePartGetterFactory(digitCondition('second', 3)),
|
||||
'EEEE': datePartGetterFactory(nameCondition('weekday', 4)),
|
||||
'EEE': datePartGetterFactory(nameCondition('weekday', 3)),
|
||||
'EE': datePartGetterFactory(nameCondition('weekday', 2)),
|
||||
'E': datePartGetterFactory(nameCondition('weekday', 1)),
|
||||
'a': hourClockExtractor(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))),
|
||||
'Z': timeZoneGetter('short'),
|
||||
'z': timeZoneGetter('long'),
|
||||
'ww': datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the
|
||||
// first Thursday of the year. not support ?
|
||||
'w':
|
||||
datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday
|
||||
// of the year not support ?
|
||||
'G': datePartGetterFactory(nameCondition('era', 1)),
|
||||
'GG': datePartGetterFactory(nameCondition('era', 2)),
|
||||
'GGG': datePartGetterFactory(nameCondition('era', 3)),
|
||||
'GGGG': datePartGetterFactory(nameCondition('era', 4))
|
||||
};
|
||||
|
||||
|
||||
function digitModifier(inner: DateFormatterFn): DateFormatterFn {
|
||||
return function(date: Date, locale: string): string {
|
||||
const result = inner(date, locale);
|
||||
return result.length == 1 ? '0' + result : result;
|
||||
};
|
||||
}
|
||||
|
||||
function hourClockExtractor(inner: DateFormatterFn): DateFormatterFn {
|
||||
return function(date: Date, locale: string): string { return inner(date, locale).split(' ')[1]; };
|
||||
}
|
||||
|
||||
function hourExtractor(inner: DateFormatterFn): DateFormatterFn {
|
||||
return function(date: Date, locale: string): string { return inner(date, locale).split(' ')[0]; };
|
||||
}
|
||||
|
||||
function intlDateFormat(date: Date, locale: string, options: Intl.DateTimeFormatOptions): string {
|
||||
return new Intl.DateTimeFormat(locale, options).format(date).replace(/[\u200e\u200f]/g, '');
|
||||
}
|
||||
|
||||
function timeZoneGetter(timezone: string): DateFormatterFn {
|
||||
// To workaround `Intl` API restriction for single timezone let format with 24 hours
|
||||
const options = {hour: '2-digit', hour12: false, timeZoneName: timezone};
|
||||
return function(date: Date, locale: string): string {
|
||||
const result = intlDateFormat(date, locale, options);
|
||||
// Then extract first 3 letters that related to hours
|
||||
return result ? result.substring(3) : '';
|
||||
};
|
||||
}
|
||||
|
||||
function hour12Modify(
|
||||
options: Intl.DateTimeFormatOptions, value: boolean): Intl.DateTimeFormatOptions {
|
||||
options.hour12 = value;
|
||||
return options;
|
||||
}
|
||||
|
||||
function digitCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
|
||||
const result: {[k: string]: string} = {};
|
||||
result[prop] = len === 2 ? '2-digit' : 'numeric';
|
||||
return result;
|
||||
}
|
||||
|
||||
function nameCondition(prop: string, len: number): Intl.DateTimeFormatOptions {
|
||||
const result: {[k: string]: string} = {};
|
||||
if (len < 4) {
|
||||
result[prop] = len > 1 ? 'short' : 'narrow';
|
||||
} else {
|
||||
result[prop] = 'long';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function combine(options: Intl.DateTimeFormatOptions[]): Intl.DateTimeFormatOptions {
|
||||
return (<any>Object).assign({}, ...options);
|
||||
}
|
||||
|
||||
function datePartGetterFactory(ret: Intl.DateTimeFormatOptions): DateFormatterFn {
|
||||
return (date: Date, locale: string): string => intlDateFormat(date, locale, ret);
|
||||
}
|
||||
|
||||
const DATE_FORMATTER_CACHE = new Map<string, string[]>();
|
||||
|
||||
function dateFormatter(format: string, date: Date, locale: string): string {
|
||||
const fn = PATTERN_ALIASES[format];
|
||||
|
||||
if (fn) return fn(date, locale);
|
||||
|
||||
const cacheKey = format;
|
||||
let parts = DATE_FORMATTER_CACHE.get(cacheKey);
|
||||
|
||||
if (!parts) {
|
||||
parts = [];
|
||||
let match: RegExpExecArray;
|
||||
DATE_FORMATS_SPLIT.exec(format);
|
||||
|
||||
while (format) {
|
||||
match = DATE_FORMATS_SPLIT.exec(format);
|
||||
if (match) {
|
||||
parts = parts.concat(match.slice(1));
|
||||
format = parts.pop();
|
||||
} else {
|
||||
parts.push(format);
|
||||
format = null;
|
||||
}
|
||||
}
|
||||
|
||||
DATE_FORMATTER_CACHE.set(cacheKey, parts);
|
||||
}
|
||||
|
||||
return parts.reduce((text, part) => {
|
||||
const fn = DATE_FORMATS[part];
|
||||
return text + (fn ? fn(date, locale) : partToTime(part));
|
||||
}, '');
|
||||
}
|
||||
|
||||
function partToTime(part: string): string {
|
||||
return part === '\'\'' ? '\'' : part.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
|
||||
}
|
||||
|
||||
export class DateFormatter {
|
||||
static format(date: Date, locale: string, pattern: string): string {
|
||||
return dateFormatter(pattern, date, locale);
|
||||
}
|
||||
}
|
@ -8,10 +8,6 @@
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
import {Json} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @ngModule CommonModule
|
||||
* @whatItDoes Converts value into JSON string.
|
||||
@ -27,5 +23,5 @@ import {Json} from '../facade/lang';
|
||||
*/
|
||||
@Pipe({name: 'json', pure: false})
|
||||
export class JsonPipe implements PipeTransform {
|
||||
transform(value: any): string { return Json.stringify(value); }
|
||||
transform(value: any): string { return JSON.stringify(value, null, 2); }
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* @howToUse `expression | lowercase`
|
||||
* @description
|
||||
*
|
||||
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
||||
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||
|
||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
||||
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {NumberWrapper} from '../facade/lang';
|
||||
|
||||
import {NumberFormatStyle, NumberFormatter} from './intl';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||
@ -18,7 +18,7 @@ const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||
function formatNumber(
|
||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
||||
if (isBlank(value)) return null;
|
||||
if (value == null) return null;
|
||||
|
||||
// Convert strings to numbers
|
||||
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
|
||||
@ -37,17 +37,17 @@ function formatNumber(
|
||||
}
|
||||
|
||||
if (digits) {
|
||||
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
const parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
if (parts === null) {
|
||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
if (isPresent(parts[1])) { // min integer digits
|
||||
if (parts[1] != null) { // min integer digits
|
||||
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
|
||||
}
|
||||
if (isPresent(parts[3])) { // min fraction digits
|
||||
if (parts[3] != null) { // min fraction digits
|
||||
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
|
||||
}
|
||||
if (isPresent(parts[5])) { // max fraction digits
|
||||
if (parts[5] != null) { // max fraction digits
|
||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank} from '../facade/lang';
|
||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
|
||||
/**
|
||||
@ -58,7 +57,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
@Pipe({name: 'slice', pure: false})
|
||||
export class SlicePipe implements PipeTransform {
|
||||
transform(value: any, start: number, end?: number): any {
|
||||
if (isBlank(value)) return value;
|
||||
if (value == null) return value;
|
||||
|
||||
if (!this.supports(value)) {
|
||||
throw new InvalidPipeArgumentError(SlicePipe, value);
|
||||
|
@ -16,7 +16,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* @howToUse `expression | uppercase`
|
||||
* @description
|
||||
*
|
||||
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
||||
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
19
modules/@angular/common/src/version.ts
Normal file
19
modules/@angular/common/src/version.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @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
|
||||
* Entry point for all public APIs of the common package.
|
||||
*/
|
||||
|
||||
import {Version} from '@angular/core';
|
||||
/**
|
||||
* @stable
|
||||
*/
|
||||
export const VERSION = new Version('0.0.0-PLACEHOLDER');
|
@ -8,7 +8,6 @@
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS class list', () => {
|
||||
@ -66,7 +65,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression object', async(() => {
|
||||
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
detectChangesAndExpectClassName('foo');
|
||||
|
||||
@ -135,7 +134,7 @@ export function main() {
|
||||
|
||||
it('should add and remove classes based on changes to the expression', async(() => {
|
||||
fixture = createTestComponent('<div [ngClass]="arrExpr"></div>');
|
||||
let arrExpr = getComponent().arrExpr;
|
||||
const arrExpr = getComponent().arrExpr;
|
||||
detectChangesAndExpectClassName('foo');
|
||||
|
||||
arrExpr.push('bar');
|
||||
@ -187,6 +186,13 @@ export function main() {
|
||||
getComponent().arrExpr = ['foo bar baz foobar'];
|
||||
detectChangesAndExpectClassName('foo bar baz foobar');
|
||||
}));
|
||||
|
||||
it('should throw with descriptive error message when CSS class is not a string', () => {
|
||||
fixture = createTestComponent(`<div [ngClass]="['foo', {}]"></div>`);
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(
|
||||
/NgClass can only toggle CSS classes expressed as strings, got \[object Object\]/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('expressions evaluating to sets', () => {
|
||||
@ -253,7 +259,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the class attribute', async(() => {
|
||||
fixture = createTestComponent('<div [ngClass]="objExpr" class="init foo"></div>');
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
objExpr['bar'] = true;
|
||||
detectChangesAndExpectClassName('init foo bar');
|
||||
@ -267,7 +273,7 @@ export function main() {
|
||||
|
||||
it('should co-operate with the interpolated class attribute', async(() => {
|
||||
fixture = createTestComponent(`<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`);
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
objExpr['bar'] = true;
|
||||
detectChangesAndExpectClassName(`init foo bar`);
|
||||
@ -282,7 +288,7 @@ export function main() {
|
||||
it('should co-operate with the class attribute and binding to it', async(() => {
|
||||
fixture =
|
||||
createTestComponent(`<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`);
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
objExpr['bar'] = true;
|
||||
detectChangesAndExpectClassName(`init foo bar`);
|
||||
@ -298,7 +304,7 @@ export function main() {
|
||||
const template =
|
||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||
fixture = createTestComponent(template);
|
||||
let objExpr = getComponent().objExpr;
|
||||
const objExpr = getComponent().objExpr;
|
||||
|
||||
detectChangesAndExpectClassName('init foo baz');
|
||||
|
||||
@ -316,7 +322,7 @@ export function main() {
|
||||
async(() => {
|
||||
const template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||
fixture = createTestComponent(template);
|
||||
let cmp = getComponent();
|
||||
const cmp = getComponent();
|
||||
|
||||
detectChangesAndExpectClassName('init foo');
|
||||
|
||||
@ -348,4 +354,4 @@ class TestComponent {
|
||||
function createTestComponent(template: string): ComponentFixture<TestComponent> {
|
||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.createComponent(TestComponent);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, ContentChild, TemplateRef} from '@angular/core';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
@ -29,10 +29,7 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
TestComponent,
|
||||
ComponentUsingTestComponent,
|
||||
],
|
||||
declarations: [TestComponent],
|
||||
imports: [CommonModule],
|
||||
});
|
||||
});
|
||||
@ -77,7 +74,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
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 *ngFor="let item of items">{{item["name"]}};</li></ul>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
// INIT
|
||||
@ -95,7 +92,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should gracefully handle nulls', async(() => {
|
||||
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
||||
const template = '<ul><li *ngFor="let item of null">{{item}};</li></ul>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
detectChangesAndExpectText('');
|
||||
@ -140,12 +137,8 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should repeat over nested arrays', async(() => {
|
||||
const template = '<div>' +
|
||||
'<div template="ngFor let item of items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div>|' +
|
||||
'</div>' +
|
||||
const template = '<div *ngFor="let item of items">' +
|
||||
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>|' +
|
||||
'</div>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -157,10 +150,9 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should repeat over nested arrays with no intermediate element', async(() => {
|
||||
const template = '<div><template ngFor let-item [ngForOf]="items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div></template></div>';
|
||||
const template = '<div *ngFor="let item of items">' +
|
||||
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>' +
|
||||
'</div>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [['a', 'b'], ['c']];
|
||||
@ -170,10 +162,11 @@ export function main() {
|
||||
detectChangesAndExpectText('e-1;f-2;g-2;');
|
||||
}));
|
||||
|
||||
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
|
||||
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
|
||||
it('should repeat over nested ngIf that are the last node in the ngFor template', async(() => {
|
||||
const template = `<div *ngFor="let item of items; let i=index">` +
|
||||
`<div>{{i}}|</div>` +
|
||||
`<div *ngIf="i % 2 == 0">even|</div>` +
|
||||
`</div>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -189,8 +182,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should display indices correctly', async(() => {
|
||||
const template =
|
||||
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
|
||||
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
@ -202,7 +194,7 @@ export function main() {
|
||||
|
||||
it('should display first item correctly', async(() => {
|
||||
const template =
|
||||
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
|
||||
'<span *ngFor="let item of items; let isFirst=first">{{isFirst.toString()}}</span>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [0, 1, 2];
|
||||
@ -214,7 +206,7 @@ export function main() {
|
||||
|
||||
it('should display last item correctly', async(() => {
|
||||
const template =
|
||||
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
|
||||
'<span *ngFor="let item of items; let isLast=last">{{isLast.toString()}}</span>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [0, 1, 2];
|
||||
@ -226,7 +218,7 @@ export function main() {
|
||||
|
||||
it('should display even items correctly', async(() => {
|
||||
const template =
|
||||
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
|
||||
'<span *ngFor="let item of items; let isEven=even">{{isEven.toString()}}</span>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [0, 1, 2];
|
||||
@ -238,7 +230,7 @@ export function main() {
|
||||
|
||||
it('should display odd items correctly', async(() => {
|
||||
const template =
|
||||
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
|
||||
'<span *ngFor="let item of items; let isOdd=odd">{{isOdd.toString()}}</span>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [0, 1, 2, 3];
|
||||
@ -249,54 +241,57 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should allow to use a custom template', async(() => {
|
||||
const tcTemplate =
|
||||
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>';
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: tcTemplate}});
|
||||
const cutTemplate =
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
||||
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
||||
|
||||
const testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
const template =
|
||||
'<ng-container *ngFor="let item of items; template: tpl"></ng-container>' +
|
||||
'<template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></template>';
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
detectChangesAndExpectText('0: a;1: b;2: c;');
|
||||
}));
|
||||
|
||||
it('should use a default template if a custom one is null', async(() => {
|
||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
||||
const cutTemplate =
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
||||
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
||||
|
||||
const testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
const template =
|
||||
`<ul><ng-container *ngFor="let item of items; template: null; let i=index">{{i}}: {{item}};</ng-container></ul>`;
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
detectChangesAndExpectText('0: a;1: b;2: c;');
|
||||
}));
|
||||
|
||||
it('should use a custom template when both default and a custom one are present', async(() => {
|
||||
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`;
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
|
||||
const cutTemplate =
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
|
||||
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
|
||||
fixture = TestBed.createComponent(ComponentUsingTestComponent);
|
||||
|
||||
const testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
const template =
|
||||
'<ng-container *ngFor="let item of items; template: tpl">{{i}};</ng-container>' +
|
||||
'<template let-item let-i="index" #tpl>{{i}}: {{item}};</template>';
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
detectChangesAndExpectText('0: a;1: b;2: c;');
|
||||
}));
|
||||
|
||||
describe('track by', () => {
|
||||
it('should console.warn if trackBy is not a function', async(() => {
|
||||
// TODO(vicb): expect a warning message when we have a proper log service
|
||||
const template = `<p *ngFor="let item of items; trackBy: value"></p>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.componentInstance.value = 0;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
|
||||
// TODO(vicb): expect no warning message when we have a proper log service
|
||||
const template = `<p *ngFor="let item of items; trackBy: value">{{ item }}</p>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.componentInstance.value = null;
|
||||
detectChangesAndExpectText('abc');
|
||||
fixture.componentInstance.value = undefined;
|
||||
detectChangesAndExpectText('abc');
|
||||
}));
|
||||
|
||||
it('should set the context to the component instance', async(() => {
|
||||
const template =
|
||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
|
||||
`<p *ngFor="let item of items; trackBy: trackByContext.bind(this)"></p>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
thisArg = null;
|
||||
@ -306,9 +301,7 @@ export function main() {
|
||||
|
||||
it('should not replace tracked items', async(() => {
|
||||
const template =
|
||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
|
||||
<p>{{items[i]}}</p>
|
||||
</template>`;
|
||||
`<p *ngFor="let item of items; trackBy: trackById; let i=index">{{items[i]}}</p>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
const buildItemList = () => {
|
||||
@ -324,7 +317,7 @@ export function main() {
|
||||
|
||||
it('should update implicit local variable on view', async(() => {
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [{'id': 'a', 'color': 'blue'}];
|
||||
@ -333,9 +326,10 @@ export function main() {
|
||||
getComponent().items = [{'id': 'a', 'color': 'red'}];
|
||||
detectChangesAndExpectText('red');
|
||||
}));
|
||||
|
||||
it('should move items around and keep them updated ', async(() => {
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
||||
@ -346,8 +340,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should handle added and removed items properly when tracking by index', async(() => {
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
||||
const template = `<div *ngFor="let item of items; trackBy: trackByIndex">{{item}}</div>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().items = ['a', 'b', 'c', 'd'];
|
||||
@ -367,21 +360,16 @@ class Foo {
|
||||
|
||||
@Component({selector: 'test-cmp', template: ''})
|
||||
class TestComponent {
|
||||
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
||||
value: any;
|
||||
items: any[] = [1, 2];
|
||||
trackById(index: number, item: any): string { return item['id']; }
|
||||
trackByIndex(index: number, item: any): number { return index; }
|
||||
trackByContext(): void { thisArg = this; }
|
||||
}
|
||||
|
||||
@Component({selector: 'outer-cmp', template: ''})
|
||||
class ComponentUsingTestComponent {
|
||||
items: any = [1, 2];
|
||||
}
|
||||
|
||||
const TEMPLATE = '<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
|
||||
const TEMPLATE = '<div><span *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);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
@ -28,131 +29,114 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should work in a template attribute', async(() => {
|
||||
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
||||
const template = '<span *ngIf="booleanCondition">hello</span>';
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('hello');
|
||||
}));
|
||||
|
||||
it('should work in a template element', async(() => {
|
||||
const template =
|
||||
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
|
||||
|
||||
it('should work on a template element', async(() => {
|
||||
const template = '<template [ngIf]="booleanCondition">hello2</template>';
|
||||
fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('hello2');
|
||||
}));
|
||||
|
||||
it('should toggle node when condition changes', async(() => {
|
||||
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
|
||||
|
||||
const template = '<span *ngIf="booleanCondition">hello</span>';
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
getComponent().booleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('hello');
|
||||
|
||||
getComponent().booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
}));
|
||||
|
||||
it('should handle nested if correctly', async(() => {
|
||||
const template =
|
||||
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
|
||||
'<div *ngIf="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
getComponent().booleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('hello');
|
||||
|
||||
getComponent().nestedBooleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
getComponent().nestedBooleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('hello');
|
||||
|
||||
getComponent().booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
}));
|
||||
|
||||
it('should update several nodes with if', async(() => {
|
||||
const template = '<div>' +
|
||||
'<span template="ngIf numberCondition + 1 >= 2">helloNumber</span>' +
|
||||
'<span template="ngIf stringCondition == \'foo\'">helloString</span>' +
|
||||
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
|
||||
'</div>';
|
||||
const template = '<span *ngIf="numberCondition + 1 >= 2">helloNumber</span>' +
|
||||
'<span *ngIf="stringCondition == \'foo\'">helloString</span>' +
|
||||
'<span *ngIf="functionCondition(stringCondition, numberCondition)">helloFunction</span>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(3);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(3);
|
||||
expect(getDOM().getText(fixture.nativeElement))
|
||||
.toEqual('helloNumberhelloStringhelloFunction');
|
||||
|
||||
getComponent().numberCondition = 0;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('helloString');
|
||||
|
||||
getComponent().numberCondition = 1;
|
||||
getComponent().stringCondition = 'bar';
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
|
||||
expect(fixture.nativeElement).toHaveText('helloNumber');
|
||||
}));
|
||||
|
||||
it('should not add the element twice if the condition goes from true to true (JS)',
|
||||
async(() => {
|
||||
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
|
||||
it('should not add the element twice if the condition goes from truthy to truthy', async(() => {
|
||||
const template = '<span *ngIf="numberCondition">hello</span>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
let els = fixture.debugElement.queryAll(By.css('span'));
|
||||
expect(els.length).toEqual(1);
|
||||
getDOM().addClass(els[0].nativeElement, 'marker');
|
||||
expect(fixture.nativeElement).toHaveText('hello');
|
||||
|
||||
getComponent().numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
|
||||
els = fixture.debugElement.queryAll(By.css('span'));
|
||||
expect(els.length).toEqual(1);
|
||||
expect(getDOM().hasClass(els[0].nativeElement, 'marker')).toBe(true);
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('hello');
|
||||
}));
|
||||
|
||||
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>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
getDOM().addClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo');
|
||||
|
||||
getComponent().numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().hasClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo'))
|
||||
.toBe(true);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
describe('ngPlural', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
|
||||
function getComponent(): TestComponent { return fixture.componentInstance; }
|
||||
@ -33,10 +33,25 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should display the template according to the exact value', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
||||
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
getComponent().switchValue = 0;
|
||||
detectChangesAndExpectText('you have no messages.');
|
||||
|
||||
getComponent().switchValue = 1;
|
||||
detectChangesAndExpectText('you have one message.');
|
||||
}));
|
||||
|
||||
it('should display the template according to the exact numeric value', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="0"><li>you have no messages.</li></template>' +
|
||||
'<template ngPluralCase="1"><li>you have one message.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
@ -51,10 +66,9 @@ export function main() {
|
||||
// https://github.com/angular/angular/issues/9868
|
||||
// https://github.com/angular/angular/issues/9882
|
||||
it('should not throw when ngPluralCase contains expressions', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
||||
'</ul></div>';
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -64,11 +78,10 @@ export function main() {
|
||||
|
||||
|
||||
it('should be applicable to <ng-container> elements', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ng-container [ngPlural]="switchValue">' +
|
||||
const template = '<ng-container [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0">you have no messages.</template>' +
|
||||
'<template ngPluralCase="=1">you have one message.</template>' +
|
||||
'</ng-container></div>';
|
||||
'</ng-container>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -80,11 +93,10 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should display the template according to the category', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
||||
'</ul></div>';
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -96,11 +108,10 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should default to other when no matches are found', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
||||
'</ul></div>';
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
@ -109,11 +120,10 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should prioritize value matches over category matches', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
const template = '<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="=2">you have two messages.</template>' +
|
||||
'</ul></div>';
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
|
@ -29,22 +29,19 @@ export function main() {
|
||||
it('should add styles specified in an object literal', async(() => {
|
||||
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
}));
|
||||
|
||||
it('should add and change styles specified in an object expression', async(() => {
|
||||
const template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
let expr: {[k: string]: string};
|
||||
|
||||
getComponent().expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
|
||||
expr = getComponent().expr;
|
||||
let expr = getComponent().expr;
|
||||
expr['max-width'] = '30%';
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
|
||||
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {Attribute, Component, Directive} from '@angular/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
@ -32,109 +32,147 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('switch value changes', () => {
|
||||
it('should switch amongst when values', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
it('should switch amongst when values', () => {
|
||||
const template = '<ul [ngSwitch]="switchValue">' +
|
||||
'<li *ngSwitchCase="\'a\'">when a</li>' +
|
||||
'<li *ngSwitchCase="\'b\'">when b</li>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
detectChangesAndExpectText('');
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b');
|
||||
}));
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b');
|
||||
});
|
||||
|
||||
// TODO(robwormald): deprecate and remove
|
||||
it('should switch amongst when values using switchCase', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
it('should switch amongst when values with fallback to default', () => {
|
||||
const template = '<ul [ngSwitch]="switchValue">' +
|
||||
'<li *ngSwitchCase="\'a\'">when a</li>' +
|
||||
'<li *ngSwitchDefault>when default</li>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default');
|
||||
|
||||
detectChangesAndExpectText('');
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when default');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b');
|
||||
}));
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default');
|
||||
});
|
||||
|
||||
it('should switch amongst when values with fallback to default', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||
'<li template="ngSwitchDefault">when default</li>' +
|
||||
'</ul></div>';
|
||||
it('should support multiple whens with the same value', () => {
|
||||
const template = '<ul [ngSwitch]="switchValue">' +
|
||||
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
|
||||
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
|
||||
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
|
||||
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
|
||||
'<li *ngSwitchDefault>when default1;</li>' +
|
||||
'<li *ngSwitchDefault>when default2;</li>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default');
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a');
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a1;when a2;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when default');
|
||||
}));
|
||||
|
||||
it('should support multiple whens with the same value', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a1;when a2;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
}));
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when values changes', () => {
|
||||
it('should switch amongst when values', async(() => {
|
||||
const template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||
'</ul></div>';
|
||||
it('should switch amongst when values', () => {
|
||||
const template = '<ul [ngSwitch]="switchValue">' +
|
||||
'<li *ngSwitchCase="when1">when 1;</li>' +
|
||||
'<li *ngSwitchCase="when2">when 2;</li>' +
|
||||
'<li *ngSwitchDefault>when default;</li>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().when1 = 'a';
|
||||
getComponent().when2 = 'b';
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
fixture = createTestComponent(template);
|
||||
getComponent().when1 = 'a';
|
||||
getComponent().when2 = 'b';
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when 2;');
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when 2;');
|
||||
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default;');
|
||||
getComponent().switchValue = 'c';
|
||||
detectChangesAndExpectText('when default;');
|
||||
|
||||
getComponent().when1 = 'c';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
getComponent().when1 = 'c';
|
||||
detectChangesAndExpectText('when 1;');
|
||||
|
||||
getComponent().when1 = 'd';
|
||||
detectChangesAndExpectText('when default;');
|
||||
}));
|
||||
getComponent().when1 = 'd';
|
||||
detectChangesAndExpectText('when default;');
|
||||
});
|
||||
});
|
||||
|
||||
describe('corner cases', () => {
|
||||
|
||||
it('should not create the default case if another case matches', () => {
|
||||
const log: string[] = [];
|
||||
|
||||
@Directive({selector: '[test]'})
|
||||
class TestDirective {
|
||||
constructor(@Attribute('test') test: string) { log.push(test); }
|
||||
}
|
||||
|
||||
const template = '<div [ngSwitch]="switchValue">' +
|
||||
'<div *ngSwitchCase="\'a\'" test="aCase"></div>' +
|
||||
'<div *ngSwitchDefault test="defaultCase"></div>' +
|
||||
'</div>';
|
||||
|
||||
TestBed.configureTestingModule({declarations: [TestDirective]});
|
||||
TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.createComponent(TestComponent);
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.switchValue = 'a';
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(log).toEqual(['aCase']);
|
||||
});
|
||||
|
||||
it('should create the default case if there is no other case', () => {
|
||||
const template = '<ul [ngSwitch]="switchValue">' +
|
||||
'<li *ngSwitchDefault>when default1;</li>' +
|
||||
'<li *ngSwitchDefault>when default2;</li>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
});
|
||||
|
||||
it('should allow defaults before cases', () => {
|
||||
const template = '<ul [ngSwitch]="switchValue">' +
|
||||
'<li *ngSwitchDefault>when default1;</li>' +
|
||||
'<li *ngSwitchDefault>when default2;</li>' +
|
||||
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
|
||||
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
|
||||
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
|
||||
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
|
||||
'</ul>';
|
||||
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('when default1;when default2;');
|
||||
|
||||
getComponent().switchValue = 'a';
|
||||
detectChangesAndExpectText('when a1;when a2;');
|
||||
|
||||
getComponent().switchValue = 'b';
|
||||
detectChangesAndExpectText('when b1;when b2;');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -34,29 +34,22 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should do nothing if templateRef is null', async(() => {
|
||||
const template = `<template [ngTemplateOutlet]="null"></template>`;
|
||||
it('should do nothing if templateRef is `null`', async(() => {
|
||||
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
detectChangesAndExpectText('');
|
||||
}));
|
||||
|
||||
it('should insert content specified by TemplateRef', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
const template = `<template #tpl>foo</template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
const refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
setTplRef(refs.tplRefs.first);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should clear content if TemplateRef becomes null', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
it('should clear content if TemplateRef becomes `null`', async(() => {
|
||||
const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
const refs = fixture.debugElement.children[0].references['refs'];
|
||||
@ -70,7 +63,8 @@ export function main() {
|
||||
|
||||
it('should swap content if TemplateRef changes', async(() => {
|
||||
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>` +
|
||||
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
@ -83,70 +77,47 @@ export function main() {
|
||||
detectChangesAndExpectText('bar');
|
||||
}));
|
||||
|
||||
it('should display template if context is null', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
|
||||
it('should display template if context is `null`', async(() => {
|
||||
const template = `<template #tpl>foo</template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="null"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
detectChangesAndExpectText('');
|
||||
|
||||
const refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
setTplRef(refs.tplRefs.first);
|
||||
detectChangesAndExpectText('foo');
|
||||
}));
|
||||
|
||||
it('should reflect initial context and changes', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const refs = fixture.debugElement.children[0].references['refs'];
|
||||
setTplRef(refs.tplRefs.first);
|
||||
|
||||
detectChangesAndExpectText('bar');
|
||||
|
||||
fixture.componentInstance.context.foo = 'alter-bar';
|
||||
|
||||
detectChangesAndExpectText('alter-bar');
|
||||
}));
|
||||
|
||||
it('should reflect user defined $implicit property in the context', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||
it('should reflect user defined `$implicit` property in the context', async(() => {
|
||||
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const refs = fixture.debugElement.children[0].references['refs'];
|
||||
setTplRef(refs.tplRefs.first);
|
||||
|
||||
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
|
||||
detectChangesAndExpectText('bar');
|
||||
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
|
||||
detectChangesAndExpectText('bra');
|
||||
}));
|
||||
|
||||
it('should reflect context re-binding', async(() => {
|
||||
const template =
|
||||
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
|
||||
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
|
||||
fixture = createTestComponent(template);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const refs = fixture.debugElement.children[0].references['refs'];
|
||||
setTplRef(refs.tplRefs.first);
|
||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||
|
||||
detectChangesAndExpectText('brooks');
|
||||
|
||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||
|
||||
detectChangesAndExpectText('was here');
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
|
||||
class CaptureTplRefs {
|
||||
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
||||
@ -162,4 +133,4 @@ function createTestComponent(template: string): ComponentFixture<TestComponent>
|
||||
return TestBed.overrideComponent(TestComponent, {set: {template: template}})
|
||||
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]})
|
||||
.createComponent(TestComponent);
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,10 @@
|
||||
*/
|
||||
|
||||
import {LOCALE_ID} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
import {TestBed, inject} from '@angular/core/testing';
|
||||
|
||||
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../src/localization';
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('l10n', () => {
|
||||
|
||||
@ -141,6 +138,23 @@ export function main() {
|
||||
expect(getPluralCategory(5, ['one', 'other', '=5'], l10n)).toEqual('=5');
|
||||
expect(getPluralCategory(6, ['one', 'other', '=5'], l10n)).toEqual('other');
|
||||
});
|
||||
|
||||
it('should fallback to other when the case is not present', () => {
|
||||
const l10n = new NgLocaleLocalization('ro');
|
||||
expect(getPluralCategory(1, ['one', 'other'], l10n)).toEqual('one');
|
||||
// 2 -> 'few'
|
||||
expect(getPluralCategory(2, ['one', 'other'], l10n)).toEqual('other');
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('should report an error when the "other" category is not present', () => {
|
||||
expect(() => {
|
||||
const l10n = new NgLocaleLocalization('ro');
|
||||
// 2 -> 'few'
|
||||
getPluralCategory(2, ['one'], l10n);
|
||||
}).toThrowError('No plural message found for value "2"');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -8,22 +8,21 @@
|
||||
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
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 {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
import {SpyChangeDetectorRef} from '../spies';
|
||||
|
||||
export function main() {
|
||||
describe('AsyncPipe', () => {
|
||||
|
||||
describe('Observable', () => {
|
||||
var emitter: EventEmitter<any>;
|
||||
var pipe: AsyncPipe;
|
||||
var ref: any;
|
||||
var message = {};
|
||||
let emitter: EventEmitter<any>;
|
||||
let pipe: AsyncPipe;
|
||||
let ref: any;
|
||||
const message = {};
|
||||
|
||||
beforeEach(() => {
|
||||
emitter = new EventEmitter();
|
||||
@ -63,7 +62,7 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(emitter);
|
||||
|
||||
var newEmitter = new EventEmitter();
|
||||
const newEmitter = new EventEmitter();
|
||||
expect(pipe.transform(newEmitter)).toBe(null);
|
||||
emitter.emit(message);
|
||||
|
||||
@ -105,14 +104,14 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('Promise', () => {
|
||||
var message = new Object();
|
||||
var pipe: AsyncPipe;
|
||||
var resolve: (result: any) => void;
|
||||
var reject: (error: any) => void;
|
||||
var promise: Promise<any>;
|
||||
var ref: SpyChangeDetectorRef;
|
||||
const message = new Object();
|
||||
let pipe: AsyncPipe;
|
||||
let resolve: (result: any) => void;
|
||||
let reject: (error: any) => void;
|
||||
let promise: Promise<any>;
|
||||
let ref: SpyChangeDetectorRef;
|
||||
// adds longer timers for passing tests in IE
|
||||
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10;
|
||||
const timer = (getDOM() && browserDetection.isIE) ? 50 : 10;
|
||||
|
||||
beforeEach(() => {
|
||||
promise = new Promise((res, rej) => {
|
||||
@ -155,7 +154,7 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(promise);
|
||||
|
||||
var promise = new Promise<any>(() => {});
|
||||
promise = new Promise<any>(() => {});
|
||||
expect(pipe.transform(promise)).toBe(null);
|
||||
|
||||
// this should not affect the pipe, so it should return WrappedValue
|
||||
@ -169,7 +168,7 @@ export function main() {
|
||||
|
||||
it('should request a change detection check upon receiving a new value',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var markForCheck = ref.spy('markForCheck');
|
||||
const markForCheck = ref.spy('markForCheck');
|
||||
pipe.transform(promise);
|
||||
resolve(message);
|
||||
|
||||
@ -203,14 +202,14 @@ export function main() {
|
||||
|
||||
describe('null', () => {
|
||||
it('should return null when given null', () => {
|
||||
var pipe = new AsyncPipe(null);
|
||||
const pipe = new AsyncPipe(null);
|
||||
expect(pipe.transform(null)).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('other types', () => {
|
||||
it('should throw when given an invalid object', () => {
|
||||
var pipe = new AsyncPipe(null);
|
||||
const pipe = new AsyncPipe(null);
|
||||
expect(() => pipe.transform(<any>'some bogus object')).toThrowError();
|
||||
});
|
||||
});
|
||||
|
@ -8,15 +8,18 @@
|
||||
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('DatePipe', () => {
|
||||
var date: Date;
|
||||
var pipe: DatePipe;
|
||||
let date: Date;
|
||||
const isoStringWithoutTime = '2015-01-01';
|
||||
let pipe: DatePipe;
|
||||
|
||||
// Check the transformation of a date into a pattern
|
||||
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
|
||||
expect(pipe.transform(date, pattern)).toEqual(output);
|
||||
}
|
||||
|
||||
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
|
||||
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
|
||||
@ -27,7 +30,7 @@ export function main() {
|
||||
// Tracking issue: https://github.com/angular/angular/issues/11187
|
||||
|
||||
beforeEach(() => {
|
||||
date = DateWrapper.create(2015, 6, 15, 9, 3, 1);
|
||||
date = new Date(2015, 5, 15, 9, 3, 1);
|
||||
pipe = new DatePipe('en-US');
|
||||
});
|
||||
|
||||
@ -36,7 +39,9 @@ export function main() {
|
||||
|
||||
describe('supports', () => {
|
||||
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
||||
|
||||
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
||||
|
||||
it('should support numeric strings',
|
||||
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
||||
|
||||
@ -44,85 +49,143 @@ export function main() {
|
||||
() => { expect(() => pipe.transform('123456789.11')).not.toThrow(); });
|
||||
|
||||
it('should support ISO string',
|
||||
() => { expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow(); });
|
||||
() => expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow());
|
||||
|
||||
it('should not support other objects', () => {
|
||||
expect(() => pipe.transform({})).toThrow();
|
||||
expect(() => pipe.transform('')).toThrow();
|
||||
});
|
||||
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
|
||||
|
||||
it('should support ISO string without time',
|
||||
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform({})).toThrowError(); });
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
it('should format each component correctly', () => {
|
||||
expect(pipe.transform(date, 'y')).toEqual('2015');
|
||||
expect(pipe.transform(date, 'yy')).toEqual('15');
|
||||
expect(pipe.transform(date, 'M')).toEqual('6');
|
||||
expect(pipe.transform(date, 'MM')).toEqual('06');
|
||||
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
|
||||
expect(pipe.transform(date, 'MMMM')).toEqual('June');
|
||||
expect(pipe.transform(date, 'd')).toEqual('15');
|
||||
expect(pipe.transform(date, 'E')).toEqual('Mon');
|
||||
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
|
||||
const dateFixtures: any = {
|
||||
'y': '2015',
|
||||
'yy': '15',
|
||||
'M': '6',
|
||||
'MM': '06',
|
||||
'MMM': 'Jun',
|
||||
'MMMM': 'June',
|
||||
'd': '15',
|
||||
'dd': '15',
|
||||
'EEE': 'Mon',
|
||||
'EEEE': 'Monday'
|
||||
};
|
||||
|
||||
const isoStringWithoutTimeFixtures: any = {
|
||||
'y': '2015',
|
||||
'yy': '15',
|
||||
'M': '1',
|
||||
'MM': '01',
|
||||
'MMM': 'Jan',
|
||||
'MMMM': 'January',
|
||||
'd': '1',
|
||||
'dd': '01',
|
||||
'EEE': 'Thu',
|
||||
'EEEE': 'Thursday'
|
||||
};
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'h')).toEqual('9');
|
||||
expect(pipe.transform(date, 'hh')).toEqual('09');
|
||||
expect(pipe.transform(date, 'j')).toEqual('9 AM');
|
||||
dateFixtures['h'] = '9';
|
||||
dateFixtures['hh'] = '09';
|
||||
dateFixtures['j'] = '9 AM';
|
||||
isoStringWithoutTimeFixtures['h'] = '12';
|
||||
isoStringWithoutTimeFixtures['hh'] = '12';
|
||||
isoStringWithoutTimeFixtures['j'] = '12 AM';
|
||||
}
|
||||
|
||||
// IE and Edge can't format a date to minutes and seconds without hours
|
||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||
!browserDetection.supportsNativeIntlApi) {
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'HH')).toEqual('09');
|
||||
dateFixtures['HH'] = '09';
|
||||
isoStringWithoutTimeFixtures['HH'] = '00';
|
||||
}
|
||||
expect(pipe.transform(date, 'm')).toEqual('3');
|
||||
expect(pipe.transform(date, 's')).toEqual('1');
|
||||
expect(pipe.transform(date, 'mm')).toEqual('03');
|
||||
expect(pipe.transform(date, 'ss')).toEqual('01');
|
||||
dateFixtures['E'] = 'M';
|
||||
dateFixtures['L'] = 'J';
|
||||
dateFixtures['m'] = '3';
|
||||
dateFixtures['s'] = '1';
|
||||
dateFixtures['mm'] = '03';
|
||||
dateFixtures['ss'] = '01';
|
||||
isoStringWithoutTimeFixtures['m'] = '0';
|
||||
isoStringWithoutTimeFixtures['s'] = '0';
|
||||
isoStringWithoutTimeFixtures['mm'] = '00';
|
||||
isoStringWithoutTimeFixtures['ss'] = '00';
|
||||
}
|
||||
|
||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||
});
|
||||
|
||||
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
||||
});
|
||||
|
||||
expect(pipe.transform(date, 'Z')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format common multi component patterns', () => {
|
||||
expect(pipe.transform(date, 'E, M/d/y')).toEqual('Mon, 6/15/2015');
|
||||
expect(pipe.transform(date, 'E, M/d')).toEqual('Mon, 6/15');
|
||||
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
|
||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'yMEd')).toEqual('20156Mon15');
|
||||
expect(pipe.transform(date, 'MEd')).toEqual('6Mon15');
|
||||
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
|
||||
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
||||
const dateFixtures: any = {
|
||||
'EEE, M/d/y': 'Mon, 6/15/2015',
|
||||
'EEE, M/d': 'Mon, 6/15',
|
||||
'MMM d': 'Jun 15',
|
||||
'dd/MM/yyyy': '15/06/2015',
|
||||
'MM/dd/yyyy': '06/15/2015',
|
||||
'yMEEEd': '20156Mon15',
|
||||
'MEEEd': '6Mon15',
|
||||
'MMMd': 'Jun15',
|
||||
'yMMMMEEEEd': 'Monday, June 15, 2015'
|
||||
};
|
||||
|
||||
// IE and Edge can't format a date to minutes and seconds without hours
|
||||
if (!browserDetection.isEdge && !browserDetection.isIE ||
|
||||
!browserDetection.supportsNativeIntlApi) {
|
||||
expect(pipe.transform(date, 'ms')).toEqual('31');
|
||||
dateFixtures['ms'] = '31';
|
||||
}
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'jm')).toEqual('9:03 AM');
|
||||
dateFixtures['jm'] = '9:03 AM';
|
||||
}
|
||||
|
||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should format with pattern aliases', () => {
|
||||
const dateFixtures: any = {
|
||||
'MM/dd/yyyy': '06/15/2015',
|
||||
'fullDate': 'Monday, June 15, 2015',
|
||||
'longDate': 'June 15, 2015',
|
||||
'mediumDate': 'Jun 15, 2015',
|
||||
'shortDate': '6/15/2015'
|
||||
};
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
// IE and Edge do not add a coma after the year in these 2 cases
|
||||
if ((browserDetection.isEdge || browserDetection.isIE) &&
|
||||
browserDetection.supportsNativeIntlApi) {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015 9:03:01 AM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015 9:03 AM');
|
||||
dateFixtures['medium'] = 'Jun 15, 2015 9:03:01 AM';
|
||||
dateFixtures['short'] = '6/15/2015 9:03 AM';
|
||||
} else {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 AM');
|
||||
dateFixtures['medium'] = 'Jun 15, 2015, 9:03:01 AM';
|
||||
dateFixtures['short'] = '6/15/2015, 9:03 AM';
|
||||
}
|
||||
}
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
|
||||
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
|
||||
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
|
||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
|
||||
dateFixtures['mediumTime'] = '9:03:01 AM';
|
||||
dateFixtures['shortTime'] = '9:03 AM';
|
||||
}
|
||||
|
||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should remove bidi control characters',
|
||||
|
@ -8,14 +8,14 @@
|
||||
|
||||
import {I18nPluralPipe, NgLocalization} from '@angular/common';
|
||||
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() {
|
||||
describe('I18nPluralPipe', () => {
|
||||
var localization: NgLocalization;
|
||||
var pipe: I18nPluralPipe;
|
||||
let localization: NgLocalization;
|
||||
let pipe: I18nPluralPipe;
|
||||
|
||||
var mapping = {
|
||||
const mapping = {
|
||||
'=0': 'No messages.',
|
||||
'=1': 'One message.',
|
||||
'many': 'Many messages.',
|
||||
@ -32,27 +32,27 @@ export function main() {
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return 0 text if value is 0', () => {
|
||||
var val = pipe.transform(0, mapping);
|
||||
const val = pipe.transform(0, mapping);
|
||||
expect(val).toEqual('No messages.');
|
||||
});
|
||||
|
||||
it('should return 1 text if value is 1', () => {
|
||||
var val = pipe.transform(1, mapping);
|
||||
const val = pipe.transform(1, mapping);
|
||||
expect(val).toEqual('One message.');
|
||||
});
|
||||
|
||||
it('should return category messages', () => {
|
||||
var val = pipe.transform(4, mapping);
|
||||
const val = pipe.transform(4, mapping);
|
||||
expect(val).toEqual('Many messages.');
|
||||
});
|
||||
|
||||
it('should interpolate the value into the text where indicated', () => {
|
||||
var val = pipe.transform(6, mapping);
|
||||
const val = pipe.transform(6, mapping);
|
||||
expect(val).toEqual('There are 6 messages, that is 6.');
|
||||
});
|
||||
|
||||
it('should use "" if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
const val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
|
@ -8,40 +8,35 @@
|
||||
|
||||
import {I18nSelectPipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nSelectPipe', () => {
|
||||
var pipe: I18nSelectPipe;
|
||||
var mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||
|
||||
beforeEach(() => { pipe = new I18nSelectPipe(); });
|
||||
const pipe: I18nSelectPipe = new I18nSelectPipe();
|
||||
const mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return male text if value is male', () => {
|
||||
var val = pipe.transform('male', mapping);
|
||||
it('should return the "male" text if value is "male"', () => {
|
||||
const val = pipe.transform('male', mapping);
|
||||
expect(val).toEqual('Invite him.');
|
||||
});
|
||||
|
||||
it('should return female text if value is female', () => {
|
||||
var val = pipe.transform('female', mapping);
|
||||
it('should return the "female" text if value is "female"', () => {
|
||||
const val = pipe.transform('female', mapping);
|
||||
expect(val).toEqual('Invite her.');
|
||||
});
|
||||
|
||||
it('should return "" if value is anything other than male or female', () => {
|
||||
var val = pipe.transform('Anything else', mapping);
|
||||
expect(val).toEqual('');
|
||||
it('should return the "other" text if value is neither "male" nor "female"',
|
||||
() => { expect(pipe.transform('Anything else', mapping)).toEqual('Invite them.'); });
|
||||
|
||||
it('should return an empty text if value is null or undefined', () => {
|
||||
expect(pipe.transform(null, mapping)).toEqual('');
|
||||
expect(pipe.transform(void 0, mapping)).toEqual('');
|
||||
});
|
||||
|
||||
it('should use "" if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
it('should not support bad arguments',
|
||||
it('should throw on bad arguments',
|
||||
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
||||
});
|
||||
|
||||
|
@ -11,16 +11,14 @@ import {Component} from '@angular/core';
|
||||
import {TestBed, async} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {Json, StringWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('JsonPipe', () => {
|
||||
var regNewLine = '\n';
|
||||
var inceptionObj: any;
|
||||
var inceptionObjString: string;
|
||||
var pipe: JsonPipe;
|
||||
const regNewLine = '\n';
|
||||
let inceptionObj: any;
|
||||
let inceptionObjString: string;
|
||||
let pipe: JsonPipe;
|
||||
|
||||
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
|
||||
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
||||
|
||||
beforeEach(() => {
|
||||
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
|
||||
@ -41,14 +39,14 @@ export function main() {
|
||||
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
||||
|
||||
it('should return JSON-formatted string even when normalized', () => {
|
||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||
var dream2 = normalize(inceptionObjString);
|
||||
const dream1 = normalize(pipe.transform(inceptionObj));
|
||||
const dream2 = normalize(inceptionObjString);
|
||||
expect(dream1).toEqual(dream2);
|
||||
});
|
||||
|
||||
it('should return JSON-formatted string similar to Json.stringify', () => {
|
||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||
var dream2 = normalize(Json.stringify(inceptionObj));
|
||||
const dream1 = normalize(pipe.transform(inceptionObj));
|
||||
const dream2 = normalize(JSON.stringify(inceptionObj, null, 2));
|
||||
expect(dream1).toEqual(dream2);
|
||||
});
|
||||
});
|
||||
@ -65,8 +63,8 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should work with mutable objects', async(() => {
|
||||
let fixture = TestBed.createComponent(TestComp);
|
||||
let mutable: number[] = [1];
|
||||
const fixture = TestBed.createComponent(TestComp);
|
||||
const mutable: number[] = [1];
|
||||
fixture.componentInstance.data = mutable;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('[\n 1\n]');
|
||||
@ -74,7 +72,6 @@ export function main() {
|
||||
mutable.push(2);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('[\n 1,\n 2\n]');
|
||||
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -7,13 +7,13 @@
|
||||
*/
|
||||
|
||||
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() {
|
||||
describe('LowerCasePipe', () => {
|
||||
var upper: string;
|
||||
var lower: string;
|
||||
var pipe: LowerCasePipe;
|
||||
let upper: string;
|
||||
let lower: string;
|
||||
let pipe: LowerCasePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
lower = 'something';
|
||||
@ -23,14 +23,14 @@ export function main() {
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return lowercase', () => {
|
||||
var val = pipe.transform(upper);
|
||||
const val = pipe.transform(upper);
|
||||
expect(val).toEqual(lower);
|
||||
});
|
||||
|
||||
it('should lowercase when there is a new value', () => {
|
||||
var val = pipe.transform(upper);
|
||||
const val = pipe.transform(upper);
|
||||
expect(val).toEqual(lower);
|
||||
var val2 = pipe.transform('WAT');
|
||||
const val2 = pipe.transform('WAT');
|
||||
expect(val2).toEqual('wat');
|
||||
});
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user