Compare commits
350 Commits
creating-l
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
80073944a1 | ||
![]() |
a2aa45d3c7 | ||
![]() |
0731c7f634 | ||
![]() |
cf2d40e359 | ||
![]() |
0c47b7184c | ||
![]() |
752100487b | ||
![]() |
7a183c9920 | ||
![]() |
eac35a64e1 | ||
![]() |
0348f47f62 | ||
![]() |
79da3e8cd2 | ||
![]() |
67b431603a | ||
![]() |
604bd6e1cb | ||
![]() |
fdf572b3c5 | ||
![]() |
f6936406ca | ||
![]() |
a4909d6aa0 | ||
![]() |
600af0a5f6 | ||
![]() |
a0fcc0b839 | ||
![]() |
cf6efd91ae | ||
![]() |
ee02855bd5 | ||
![]() |
53fd049d12 | ||
![]() |
a025e98ede | ||
![]() |
768f81627e | ||
![]() |
c2924a10e7 | ||
![]() |
5af3281103 | ||
![]() |
0496e59a10 | ||
![]() |
6f07ef5236 | ||
![]() |
0f9ed1d7d6 | ||
![]() |
3aa0ca984c | ||
![]() |
77832458c9 | ||
![]() |
e10a578603 | ||
![]() |
b142d33bbe | ||
![]() |
bcabf710d0 | ||
![]() |
a725fe261a | ||
![]() |
ebee38994f | ||
![]() |
6ebb0246ab | ||
![]() |
c7a054fa31 | ||
![]() |
6ff6acbb21 | ||
![]() |
8a09cb9794 | ||
![]() |
eeb59b4f4d | ||
![]() |
7afbc3a1c1 | ||
![]() |
3da8ebf27e | ||
![]() |
7e33d531ae | ||
![]() |
32577820f4 | ||
![]() |
3686c640d6 | ||
![]() |
cbe53247fa | ||
![]() |
bbbae1065a | ||
![]() |
b5c3bb998d | ||
![]() |
4ff7aeadbb | ||
![]() |
86eaf57d53 | ||
![]() |
ff7b7a9c2f | ||
![]() |
dea267e5c8 | ||
![]() |
07200be358 | ||
![]() |
45e6bb4a74 | ||
![]() |
b44f7b1a5c | ||
![]() |
3c9c40c4b7 | ||
![]() |
15bb08eea6 | ||
![]() |
10953bddcb | ||
![]() |
7b87a797ed | ||
![]() |
4ea5a19a10 | ||
![]() |
ee263f3b97 | ||
![]() |
f73846cd6b | ||
![]() |
b2fd28d2b0 | ||
![]() |
05535d985b | ||
![]() |
4dc8aa2a27 | ||
![]() |
3d084ac910 | ||
![]() |
f8678d7513 | ||
![]() |
c50136e2cb | ||
![]() |
c3b4e0f37e | ||
![]() |
4d33c41e92 | ||
![]() |
eff4e3e3c2 | ||
![]() |
e6c7536d62 | ||
![]() |
5f4a06039e | ||
![]() |
05b9bb71b1 | ||
![]() |
c7cae616e0 | ||
![]() |
14daffecc5 | ||
![]() |
f6ae7d4f49 | ||
![]() |
316d9d5c34 | ||
![]() |
ba99c7792a | ||
![]() |
66a2af0c77 | ||
![]() |
ab87527e1d | ||
![]() |
a0486577a8 | ||
![]() |
6f4c65c029 | ||
![]() |
295f3796c6 | ||
![]() |
6362ec46d5 | ||
![]() |
de2b1c4bb4 | ||
![]() |
6aad16e11f | ||
![]() |
78b657e8f3 | ||
![]() |
ee9e65ecb1 | ||
![]() |
5341121fde | ||
![]() |
265581e525 | ||
![]() |
06da025393 | ||
![]() |
317bbd8f46 | ||
![]() |
9cb99bbaa8 | ||
![]() |
8a70ac0950 | ||
![]() |
9956446ad7 | ||
![]() |
f0865de125 | ||
![]() |
5cb6779305 | ||
![]() |
1e2e9201b2 | ||
![]() |
6062e658db | ||
![]() |
eca92ea193 | ||
![]() |
3177ce1f35 | ||
![]() |
faa17f8959 | ||
![]() |
d42a09e9b6 | ||
![]() |
408d38b223 | ||
![]() |
333a1266cc | ||
![]() |
a4912ff297 | ||
![]() |
a9920d6776 | ||
![]() |
71b1f1ad88 | ||
![]() |
a198e2984c | ||
![]() |
e160d8466c | ||
![]() |
575479a6a1 | ||
![]() |
8b97de61f5 | ||
![]() |
d870e5c309 | ||
![]() |
2da56a70a2 | ||
![]() |
0b2f134680 | ||
![]() |
5d32a199a9 | ||
![]() |
f677c22fba | ||
![]() |
97ae2d3b9b | ||
![]() |
a3fe2de883 | ||
![]() |
7f6c3b3fc1 | ||
![]() |
ab4ab682e5 | ||
![]() |
a69cb738a8 | ||
![]() |
e9eeee5608 | ||
![]() |
d89ab70036 | ||
![]() |
fa82a82a07 | ||
![]() |
39f92d985b | ||
![]() |
da0129b83e | ||
![]() |
c980caecac | ||
![]() |
b88abd81c8 | ||
![]() |
201c38e407 | ||
![]() |
31c53670e7 | ||
![]() |
e169f00193 | ||
![]() |
e1ee231993 | ||
![]() |
7d4aebd603 | ||
![]() |
2cde8da71c | ||
![]() |
6f54c28c87 | ||
![]() |
17841959f5 | ||
![]() |
cfe5fadb3a | ||
![]() |
3cd4a5ded5 | ||
![]() |
36156244a1 | ||
![]() |
a66a804082 | ||
![]() |
d2524eef7f | ||
![]() |
f6d2af17fc | ||
![]() |
a86754b4c4 | ||
![]() |
2968435f81 | ||
![]() |
4096bbfeee | ||
![]() |
0314fd4b6e | ||
![]() |
3f2257f209 | ||
![]() |
271e0f9896 | ||
![]() |
79b5e1891d | ||
![]() |
b4b453d6de | ||
![]() |
06f085560c | ||
![]() |
38fb735551 | ||
![]() |
b0ecee8155 | ||
![]() |
ba46a0c05b | ||
![]() |
0112164c34 | ||
![]() |
f60fafbb2e | ||
![]() |
004079b07a | ||
![]() |
523a1a71d7 | ||
![]() |
d3999d904a | ||
![]() |
63894062d4 | ||
![]() |
5c60a65eaf | ||
![]() |
c387933d3c | ||
![]() |
882336e0e6 | ||
![]() |
e91aa6db32 | ||
![]() |
325b3fd0db | ||
![]() |
84348299b3 | ||
![]() |
e934864a90 | ||
![]() |
920205bc61 | ||
![]() |
0dcc34837e | ||
![]() |
57d8d7fcd4 | ||
![]() |
f26f014ffc | ||
![]() |
134aa72467 | ||
![]() |
a7a8938291 | ||
![]() |
9338556872 | ||
![]() |
28ef4bc441 | ||
![]() |
686f9ae4b2 | ||
![]() |
a57119761b | ||
![]() |
6881149540 | ||
![]() |
4f842e23df | ||
![]() |
47873a339a | ||
![]() |
03e02185d9 | ||
![]() |
e565d97bc8 | ||
![]() |
6bf8d2b356 | ||
![]() |
2981656ebe | ||
![]() |
5ec0ba72cd | ||
![]() |
f020941629 | ||
![]() |
f1a92872a4 | ||
![]() |
9e7ce2c916 | ||
![]() |
788532dc99 | ||
![]() |
54679ea9cc | ||
![]() |
cb0aeaf708 | ||
![]() |
8de04115ec | ||
![]() |
e65e5ea9d0 | ||
![]() |
19b577bf66 | ||
![]() |
ccb0b42ffb | ||
![]() |
1d46ef38d2 | ||
![]() |
06356d806a | ||
![]() |
45847c62ce | ||
![]() |
9cb6bb8535 | ||
![]() |
e9b25a1e98 | ||
![]() |
ad51b996a2 | ||
![]() |
8a05f311b9 | ||
![]() |
e72267bc00 | ||
![]() |
2e0973a814 | ||
![]() |
27092a822a | ||
![]() |
0cd8e48c36 | ||
![]() |
2b95289ee2 | ||
![]() |
5df5ce6463 | ||
![]() |
04ad0077f4 | ||
![]() |
1bf2f118c7 | ||
![]() |
2119abf954 | ||
![]() |
fc728d0cfb | ||
![]() |
dd01edd168 | ||
![]() |
7c3edad3d4 | ||
![]() |
445ac15a78 | ||
![]() |
856db56cca | ||
![]() |
354e66efad | ||
![]() |
702958e968 | ||
![]() |
df7f3b04b5 | ||
![]() |
7525f3afc1 | ||
![]() |
570d156ce4 | ||
![]() |
26be5b4994 | ||
![]() |
3821dc5f6c | ||
![]() |
18cd1a9937 | ||
![]() |
0551fbdf88 | ||
![]() |
dca4443a8e | ||
![]() |
2a745c8df8 | ||
![]() |
a18f82b458 | ||
![]() |
696a9b01ef | ||
![]() |
d7c043ba35 | ||
![]() |
0c2490368e | ||
![]() |
bb88c9fa3d | ||
![]() |
8227b56f9e | ||
![]() |
1609815743 | ||
![]() |
c4d76a65da | ||
![]() |
763023472b | ||
![]() |
ba175be41f | ||
![]() |
f0766a4474 | ||
![]() |
6f0f0d3ea2 | ||
![]() |
576e329f33 | ||
![]() |
585ef2bdd7 | ||
![]() |
2fcabe1557 | ||
![]() |
192ef42304 | ||
![]() |
8fbf40bf40 | ||
![]() |
8f074296c2 | ||
![]() |
e49b053dac | ||
![]() |
87baa06cc6 | ||
![]() |
db394e2b81 | ||
![]() |
ff9f4de4f1 | ||
![]() |
3a46c2da7c | ||
![]() |
bfd13c06e1 | ||
![]() |
5303773daf | ||
![]() |
ba11f7b90b | ||
![]() |
1db4f508e6 | ||
![]() |
6334749d2c | ||
![]() |
3c7359f026 | ||
![]() |
8aa29438ac | ||
![]() |
fc709423f2 | ||
![]() |
3012a8e71c | ||
![]() |
77f38d3be1 | ||
![]() |
7c0f11789b | ||
![]() |
0a791e4a50 | ||
![]() |
d89200ad24 | ||
![]() |
00b7186cb2 | ||
![]() |
e8896b9de3 | ||
![]() |
6f6102d8ad | ||
![]() |
4c7f233fa8 | ||
![]() |
fd51e01335 | ||
![]() |
3a525d196b | ||
![]() |
1eebb7f189 | ||
![]() |
8effc83b92 | ||
![]() |
7f8c2225f2 | ||
![]() |
887c350f9d | ||
![]() |
103a95c182 | ||
![]() |
1b17722091 | ||
![]() |
b280d54470 | ||
![]() |
d96824acdb | ||
![]() |
9ebd461a22 | ||
![]() |
d8c07b83c3 | ||
![]() |
8f73169979 | ||
![]() |
3c0424e7e0 | ||
![]() |
de14b2c670 | ||
![]() |
c573d91285 | ||
![]() |
16c7441c2f | ||
![]() |
736f6337b2 | ||
![]() |
9c8bc4a239 | ||
![]() |
cf4da82ac3 | ||
![]() |
57575e379f | ||
![]() |
28c3e2d15d | ||
![]() |
2a45b932e2 | ||
![]() |
5a12ab4a13 | ||
![]() |
8a543d8b50 | ||
![]() |
9893576ef0 | ||
![]() |
af80bdb470 | ||
![]() |
b4449e35bf | ||
![]() |
3d0be5213e | ||
![]() |
be3e7050d5 | ||
![]() |
cf98db28d5 | ||
![]() |
5621452ff0 | ||
![]() |
b0676e8fab | ||
![]() |
b142283ba2 | ||
![]() |
e79b6c31f9 | ||
![]() |
65cc0c8bd6 | ||
![]() |
03a6252123 | ||
![]() |
174aac68f7 | ||
![]() |
5d3ba8dec7 | ||
![]() |
fbe1a9cbaa | ||
![]() |
8f7d89436f | ||
![]() |
96aa14df01 | ||
![]() |
2b6ab57d78 | ||
![]() |
b65b6163f7 | ||
![]() |
f74cfadf64 | ||
![]() |
8e5969bb52 | ||
![]() |
8fdfa7b604 | ||
![]() |
2fdc18b42c | ||
![]() |
1296003445 | ||
![]() |
4dfc3fe6a2 | ||
![]() |
67e3ecc7e3 | ||
![]() |
9821443150 | ||
![]() |
c91cdea0cd | ||
![]() |
6a3e68b606 | ||
![]() |
1822cbcd46 | ||
![]() |
a71f114ba4 | ||
![]() |
253337dc0a | ||
![]() |
6e2db228af | ||
![]() |
cce5583e75 | ||
![]() |
307db74e93 | ||
![]() |
88d4b269b5 | ||
![]() |
ee22aa592e | ||
![]() |
dd09fb5348 | ||
![]() |
d0060dcfdc | ||
![]() |
7b2e2f5d91 | ||
![]() |
b518b30dae | ||
![]() |
51aa4aa1c3 | ||
![]() |
8bb8f98c28 | ||
![]() |
3c1866b5bb | ||
![]() |
062b8d90af | ||
![]() |
8df888dfb4 | ||
![]() |
778ad3776a | ||
![]() |
5a733629b5 | ||
![]() |
65da87722d | ||
![]() |
937b89c230 | ||
![]() |
b3d81a837b | ||
![]() |
7491b854b3 | ||
![]() |
a1c9f7cdb3 | ||
![]() |
d88711c2fc | ||
![]() |
5e742d29d0 | ||
![]() |
970d10c671 | ||
![]() |
8851ba9c9d |
9
.bazelrc
9
.bazelrc
@ -136,15 +136,6 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com
|
|||||||
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
|
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
|
||||||
test:saucelabs --flaky_test_attempts=1
|
test:saucelabs --flaky_test_attempts=1
|
||||||
|
|
||||||
###############################
|
|
||||||
# NodeJS rules settings
|
|
||||||
# These settings are required for rules_nodejs
|
|
||||||
###############################
|
|
||||||
|
|
||||||
# Turn on managed directories feature in Bazel
|
|
||||||
# This allows us to avoid installing a second copy of node_modules
|
|
||||||
common --experimental_allow_incremental_repository_updates
|
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# User bazel configuration
|
# User bazel configuration
|
||||||
# NOTE: This needs to be the *last* entry in the config.
|
# NOTE: This needs to be the *last* entry in the config.
|
||||||
|
@ -30,11 +30,6 @@ var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
|||||||
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
||||||
|
|
||||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
|
||||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
|
||||||
var_5: &components_repo_unit_tests_cache_key v7-angular-components-f428c00465dfcf8a020237f22532480eedbd2cb6
|
|
||||||
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
|
|
||||||
|
|
||||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||||
# `build-ivy-npm-packages`.
|
# `build-ivy-npm-packages`.
|
||||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||||
@ -156,27 +151,6 @@ commands:
|
|||||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||||
git config --global gc.auto 0 || true
|
git config --global gc.auto 0 || true
|
||||||
|
|
||||||
init_saucelabs_environment:
|
|
||||||
description: Sets up a domain that resolves to the local host.
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: Preparing environment for running tests on Sauce Labs.
|
|
||||||
command: |
|
|
||||||
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
|
||||||
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
|
||||||
# `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not
|
|
||||||
# resolve to anything on SauceLabs VMs ensures that such requests are always resolved
|
|
||||||
# through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM).
|
|
||||||
# More context can be found in: https://github.com/angular/angular/pull/35171.
|
|
||||||
setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local"
|
|
||||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
|
||||||
- run:
|
|
||||||
# Sets up a local domain in the machine's host file that resolves to the local
|
|
||||||
# host. This domain is helpful in Sauce Labs tests where devices are not able to
|
|
||||||
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
|
||||||
name: Setting up alias domain for local host.
|
|
||||||
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
|
||||||
|
|
||||||
# Normally this would be an individual job instead of a command.
|
# Normally this would be an individual job instead of a command.
|
||||||
# But startup and setup time for each individual windows job are high enough to discourage
|
# But startup and setup time for each individual windows job are high enough to discourage
|
||||||
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
||||||
@ -272,109 +246,8 @@ jobs:
|
|||||||
- run: yarn -s tslint
|
- run: yarn -s tslint
|
||||||
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
||||||
- run: yarn -s ts-circular-deps:check
|
- run: yarn -s ts-circular-deps:check
|
||||||
- run: yarn -s ng-dev pullapprove verify
|
|
||||||
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
||||||
|
|
||||||
test:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
# Now that large integration tests are running locally in parallel (they can't run on RBE yet
|
|
||||||
# as they require network access for yarn install), this test is running out of memory
|
|
||||||
# consistently with the xlarge machine.
|
|
||||||
# TODO: switch back to xlarge once integration tests are running on remote-exec
|
|
||||||
resource_class: 2xlarge+
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- install_chrome_libs
|
|
||||||
- install_java
|
|
||||||
- run:
|
|
||||||
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
|
||||||
no_output_timeout: 20m
|
|
||||||
|
|
||||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
|
||||||
test_ivy_aot:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- install_chrome_libs
|
|
||||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
|
||||||
# not be able to easily find the output bin directory when uploading artifacts for size
|
|
||||||
# measurements.
|
|
||||||
- run:
|
|
||||||
command: yarn test-ivy-aot //... --symlink_prefix=dist/
|
|
||||||
no_output_timeout: 20m
|
|
||||||
|
|
||||||
# Publish bundle artifacts which will be used to calculate the size change. **Note**: Make
|
|
||||||
# sure that the size plugin from the Angular robot fetches the artifacts from this CircleCI
|
|
||||||
# job (see .github/angular-robot.yml). Additionally any artifacts need to be stored with the
|
|
||||||
# following path format: "{projectName}/{context}/{fileName}". This format is necessary
|
|
||||||
# because otherwise the bot is not able to pick up the artifacts from CircleCI. See:
|
|
||||||
# https://github.com/angular/github-robot/blob/master/functions/src/plugins/size.ts#L392-L394
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
|
||||||
destination: core/hello_world/bundle
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
|
|
||||||
destination: core/todo/bundle
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.br
|
|
||||||
destination: core/hello_world/bundle.br
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
|
||||||
destination: core/todo/bundle.br
|
|
||||||
|
|
||||||
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
|
|
||||||
saucelabs_view_engine:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
|
||||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
|
||||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- init_saucelabs_environment
|
|
||||||
- run:
|
|
||||||
name: Run Bazel tests on Saucelabs with ViewEngine
|
|
||||||
# See /tools/saucelabs/README.md for more info
|
|
||||||
command: |
|
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_setup
|
|
||||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "ivy-only", ...) except attr("tags", "fixme-saucelabs-ve", ...)')
|
|
||||||
yarn bazel test --config=saucelabs ${TESTS}
|
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_stop
|
|
||||||
no_output_timeout: 40m
|
|
||||||
- notify_webhook_on_fail:
|
|
||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
|
||||||
|
|
||||||
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
|
|
||||||
saucelabs_ivy:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
|
||||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
|
||||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- init_saucelabs_environment
|
|
||||||
- run:
|
|
||||||
name: Run Bazel tests on Saucelabs with Ivy
|
|
||||||
# See /tools/saucelabs/README.md for more info
|
|
||||||
command: |
|
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_setup
|
|
||||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "no-ivy-aot", ...) except attr("tags", "fixme-saucelabs-ivy", ...)')
|
|
||||||
yarn bazel test --config=saucelabs --config=ivy ${TESTS}
|
|
||||||
yarn bazel run //tools/saucelabs:sauce_service_stop
|
|
||||||
no_output_timeout: 40m
|
|
||||||
- notify_webhook_on_fail:
|
|
||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
|
||||||
|
|
||||||
test_aio:
|
test_aio:
|
||||||
executor: default-executor
|
executor: default-executor
|
||||||
steps:
|
steps:
|
||||||
@ -393,10 +266,6 @@ jobs:
|
|||||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||||
# Run accessibility tests
|
# Run accessibility tests
|
||||||
- run: yarn --cwd aio test-a11y-score-localhost
|
- run: yarn --cwd aio test-a11y-score-localhost
|
||||||
# Check the bundle sizes.
|
|
||||||
- run: yarn --cwd aio payload-size
|
|
||||||
# Run unit tests for Firebase redirects
|
|
||||||
- run: yarn --cwd aio redirects-test
|
|
||||||
|
|
||||||
deploy_aio:
|
deploy_aio:
|
||||||
executor: default-executor
|
executor: default-executor
|
||||||
@ -426,8 +295,6 @@ jobs:
|
|||||||
- run: yarn --cwd aio e2e --configuration=ci
|
- run: yarn --cwd aio e2e --configuration=ci
|
||||||
# Run PWA-score tests
|
# Run PWA-score tests
|
||||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||||
# Check the bundle sizes.
|
|
||||||
- run: yarn --cwd aio payload-size aio-local<<# parameters.viewengine >>-viewengine<</ parameters.viewengine >>
|
|
||||||
|
|
||||||
test_aio_tools:
|
test_aio_tools:
|
||||||
executor: default-executor
|
executor: default-executor
|
||||||
@ -441,26 +308,6 @@ jobs:
|
|||||||
- run: yarn --cwd aio tools-test
|
- run: yarn --cwd aio tools-test
|
||||||
- run: ./aio/aio-builds-setup/scripts/test.sh
|
- run: ./aio/aio-builds-setup/scripts/test.sh
|
||||||
|
|
||||||
test_docs_examples:
|
|
||||||
parameters:
|
|
||||||
viewengine:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
resource_class: xlarge
|
|
||||||
parallelism: 5
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- install_chrome_libs
|
|
||||||
# Install aio
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
|
||||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
|
||||||
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
|
|
||||||
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
|
|
||||||
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.viewengine >>--viewengine<</ parameters.viewengine >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
|
||||||
|
|
||||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||||
aio_preview:
|
aio_preview:
|
||||||
executor: default-executor
|
executor: default-executor
|
||||||
@ -489,7 +336,6 @@ jobs:
|
|||||||
name: Wait for preview and run tests
|
name: Wait for preview and run tests
|
||||||
command: node aio/scripts/test-preview.js $CI_PULL_REQUEST $CI_COMMIT $CI_AIO_MIN_PWA_SCORE
|
command: node aio/scripts/test-preview.js $CI_PULL_REQUEST $CI_COMMIT $CI_AIO_MIN_PWA_SCORE
|
||||||
|
|
||||||
|
|
||||||
# The `build-npm-packages` tasks exist for backwards-compatibility with old scripts and
|
# The `build-npm-packages` tasks exist for backwards-compatibility with old scripts and
|
||||||
# tests that rely on the pre-Bazel `dist/packages-dist` output structure (build.sh).
|
# tests that rely on the pre-Bazel `dist/packages-dist` output structure (build.sh).
|
||||||
# Having multiple jobs that independently build in this manner duplicates some work; we build
|
# Having multiple jobs that independently build in this manner duplicates some work; we build
|
||||||
@ -501,7 +347,7 @@ jobs:
|
|||||||
build-npm-packages:
|
build-npm-packages:
|
||||||
executor:
|
executor:
|
||||||
name: default-executor
|
name: default-executor
|
||||||
resource_class: xlarge
|
resource_class: medium
|
||||||
steps:
|
steps:
|
||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
@ -523,246 +369,6 @@ jobs:
|
|||||||
- "~/bazel_repository_cache"
|
- "~/bazel_repository_cache"
|
||||||
- "~/.cache/bazelisk"
|
- "~/.cache/bazelisk"
|
||||||
|
|
||||||
# Build the ivy npm packages.
|
|
||||||
build-ivy-npm-packages:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- run: node scripts/build/build-ivy-npm-packages.js
|
|
||||||
|
|
||||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: *workspace_location
|
|
||||||
paths:
|
|
||||||
- ng/dist/packages-dist-ivy-aot
|
|
||||||
- ng/dist/zone.js-dist-ivy-aot
|
|
||||||
|
|
||||||
# This job creates compressed tarballs (`.tgz` files) for all Angular packages and stores them as
|
|
||||||
# build artifacts. This makes it easy to try out changes from a PR build for testing purposes.
|
|
||||||
# More info CircleCI build artifacts: https://circleci.com/docs/2.0/artifacts
|
|
||||||
#
|
|
||||||
# NOTE: Currently, this job only runs for PR builds. See `publish_snapshot` for non-PR builds.
|
|
||||||
publish_packages_as_artifacts:
|
|
||||||
executor: default-executor
|
|
||||||
environment:
|
|
||||||
NG_PACKAGES_DIR: &ng_packages_dir 'dist/packages-dist'
|
|
||||||
NG_PACKAGES_ARCHIVES_DIR: &ng_packages_archives_dir 'dist/packages-dist-archives'
|
|
||||||
ZONEJS_PACKAGES_DIR: &zonejs_packages_dir 'dist/zone.js-dist'
|
|
||||||
ZONEJS_PACKAGES_ARCHIVES_DIR: &zonejs_packages_archives_dir 'dist/zone.js-dist-archives'
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
# Publish `@angular/*` packages.
|
|
||||||
- run:
|
|
||||||
name: Create artifacts for @angular/* packages
|
|
||||||
command: ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $NG_PACKAGES_DIR $NG_PACKAGES_ARCHIVES_DIR
|
|
||||||
- store_artifacts:
|
|
||||||
path: *ng_packages_archives_dir
|
|
||||||
destination: angular
|
|
||||||
# Publish `zone.js` package.
|
|
||||||
- run:
|
|
||||||
name: Create artifacts for zone.js package
|
|
||||||
command: ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $ZONEJS_PACKAGES_DIR $ZONEJS_PACKAGES_ARCHIVES_DIR
|
|
||||||
- store_artifacts:
|
|
||||||
path: *zonejs_packages_archives_dir
|
|
||||||
destination: zone.js
|
|
||||||
|
|
||||||
# This job updates the content of repos like github.com/angular/core-builds
|
|
||||||
# for every green build on angular/angular.
|
|
||||||
publish_snapshot:
|
|
||||||
executor: default-executor
|
|
||||||
steps:
|
|
||||||
# See below - ideally this job should not trigger for non-upstream builds.
|
|
||||||
# But since it does, we have to check this condition.
|
|
||||||
- run:
|
|
||||||
name: Skip this job for Pull Requests and Fork builds
|
|
||||||
# Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this
|
|
||||||
# step can be run before `init_environment`.
|
|
||||||
command: >
|
|
||||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]] ||
|
|
||||||
[[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] ||
|
|
||||||
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
|
|
||||||
circleci step halt
|
|
||||||
fi
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
# CircleCI has a config setting to force SSH for all github connections
|
|
||||||
# This is not compatible with our mechanism of using a Personal Access Token
|
|
||||||
# Clear the global setting
|
|
||||||
- run: git config --global --unset "url.ssh://git@github.com.insteadof"
|
|
||||||
- run:
|
|
||||||
name: Decrypt github credentials
|
|
||||||
# We need ensure that the same default digest is used for encoding and decoding with
|
|
||||||
# OpenSSL. OpenSSL versions might have different default digests which can cause
|
|
||||||
# decryption failures based on the installed OpenSSL version. https://stackoverflow.com/a/39641378/4317734
|
|
||||||
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
|
||||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
|
||||||
|
|
||||||
aio_monitoring_stable:
|
|
||||||
executor: default-executor
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- install_chrome_libs
|
|
||||||
- run: setPublicVar_CI_STABLE_BRANCH
|
|
||||||
- run:
|
|
||||||
name: Check out `aio/` and yarn from the stable branch
|
|
||||||
command: |
|
|
||||||
git fetch origin $CI_STABLE_BRANCH
|
|
||||||
git checkout --force origin/$CI_STABLE_BRANCH -- aio/ .yarn/ .yarnrc
|
|
||||||
# Ignore yarn's engines check, because we checked out `aio/package.json` from the stable
|
|
||||||
# branch and there could be a node version skew, which is acceptable in this monitoring job.
|
|
||||||
- run: yarn config set ignore-engines true
|
|
||||||
- run:
|
|
||||||
name: Run tests against https://angular.io/
|
|
||||||
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
|
|
||||||
- notify_webhook_on_fail:
|
|
||||||
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
|
|
||||||
- notify_webhook_on_fail:
|
|
||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
|
||||||
|
|
||||||
aio_monitoring_next:
|
|
||||||
executor: default-executor
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- install_chrome_libs
|
|
||||||
- run:
|
|
||||||
name: Run tests against https://next.angular.io/
|
|
||||||
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
|
||||||
- notify_webhook_on_fail:
|
|
||||||
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
|
|
||||||
- notify_webhook_on_fail:
|
|
||||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
|
||||||
|
|
||||||
legacy-unit-tests-saucelabs:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
|
||||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
|
||||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
- init_saucelabs_environment
|
|
||||||
- run:
|
|
||||||
name: Starting Saucelabs tunnel service
|
|
||||||
command: ./tools/saucelabs/sauce-service.sh run
|
|
||||||
background: true
|
|
||||||
- run: yarn tsc -p packages
|
|
||||||
- run: yarn tsc -p modules
|
|
||||||
- run: yarn bazel build //packages/zone.js:npm_package
|
|
||||||
- run:
|
|
||||||
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
|
|
||||||
name: Waiting for Saucelabs tunnel to connect
|
|
||||||
command: ./tools/saucelabs/sauce-service.sh ready-wait
|
|
||||||
- run:
|
|
||||||
name: Running tests on Saucelabs.
|
|
||||||
command: |
|
|
||||||
browsers=$(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
|
|
||||||
yarn karma start ./karma-js.conf.js --single-run --browsers=${browsers}
|
|
||||||
- run:
|
|
||||||
name: Stop Saucelabs tunnel service
|
|
||||||
command: ./tools/saucelabs/sauce-service.sh stop
|
|
||||||
|
|
||||||
# Job that runs all unit tests of the `angular/components` repository.
|
|
||||||
components-repo-unit-tests:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
# Restore the cache before cloning the repository because the clone script re-uses
|
|
||||||
# the restored repository if present. This reduces the amount of times the components
|
|
||||||
# repository needs to be cloned (this is slow and increases based on commits in the repo).
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- *components_repo_unit_tests_cache_key
|
|
||||||
# Whenever the `angular/components` SHA is updated, the cache key will no longer
|
|
||||||
# match. The fallback cache will still match, and CircleCI will restore the most
|
|
||||||
# recently cached repository folder. Without the fallback cache, we'd need to download
|
|
||||||
# the repository from scratch and it would slow down the job. This is because we can't
|
|
||||||
# clone the repository with reduced `--depth`, but rather need to clone the whole
|
|
||||||
# repository to be able to support arbitrary SHAs.
|
|
||||||
- *components_repo_unit_tests_cache_key_fallback
|
|
||||||
- run:
|
|
||||||
name: "Fetching angular/components repository"
|
|
||||||
command: ./scripts/ci/clone_angular_components_repo.sh
|
|
||||||
- run:
|
|
||||||
# Run yarn install to fetch the Bazel binaries as used in the components repo.
|
|
||||||
name: Installing dependencies.
|
|
||||||
# TODO: remove this once the repo has been updated to use NodeJS v12 and Yarn 1.19.1.
|
|
||||||
# We temporarily ignore the "engines" because the Angular components repository has
|
|
||||||
# minimum dependency on NodeJS v12 and Yarn 1.19.1, but the framework repository uses
|
|
||||||
# older versions.
|
|
||||||
command: yarn --ignore-engines --cwd ${COMPONENTS_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
|
|
||||||
- save_cache:
|
|
||||||
key: *components_repo_unit_tests_cache_key
|
|
||||||
paths:
|
|
||||||
# Temporary directory must be kept in sync with the `$COMPONENTS_REPO_TMP_DIR` env
|
|
||||||
# variable. It needs to be hardcoded here, because env variables interpolation is
|
|
||||||
# not supported.
|
|
||||||
- "/tmp/angular-components-repo"
|
|
||||||
- run:
|
|
||||||
# Updates the `angular/components` `package.json` file to refer to the release output
|
|
||||||
# inside the `packages-dist` directory. Note that it's not necessary to perform a yarn
|
|
||||||
# install as Bazel runs Yarn automatically when needed.
|
|
||||||
name: Setting up release packages.
|
|
||||||
command: node scripts/ci/update-deps-to-dist-packages.js ${COMPONENTS_REPO_TMP_DIR}/package.json dist/packages-dist/
|
|
||||||
- run:
|
|
||||||
name: "Running `angular/components` unit tests"
|
|
||||||
command: ./scripts/ci/run_angular_components_unit_tests.sh
|
|
||||||
|
|
||||||
test_zonejs:
|
|
||||||
executor:
|
|
||||||
name: default-executor
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- custom_attach_workspace
|
|
||||||
- init_environment
|
|
||||||
# Install
|
|
||||||
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
|
|
||||||
# Run zone.js tools tests
|
|
||||||
- run: yarn --cwd packages/zone.js promisetest
|
|
||||||
- run: yarn --cwd packages/zone.js promisefinallytest
|
|
||||||
- run: yarn bazel build //packages/zone.js:npm_package &&
|
|
||||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/ &&
|
|
||||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
|
||||||
yarn --cwd packages/zone.js electrontest
|
|
||||||
- run: yarn --cwd packages/zone.js jesttest
|
|
||||||
|
|
||||||
# Windows jobs
|
|
||||||
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
|
||||||
test_win:
|
|
||||||
executor: windows-executor
|
|
||||||
steps:
|
|
||||||
- setup_win
|
|
||||||
- run:
|
|
||||||
# Ran into a command parsing problem where `-browser:chromium-local` was converted to
|
|
||||||
# `-browser: chromium-local` (a space was added) in https://circleci.com/gh/angular/angular/357511.
|
|
||||||
# Probably a powershell command parsing thing. There's no problem using a yarn script though.
|
|
||||||
command: yarn circleci-win-ve
|
|
||||||
no_output_timeout: 45m
|
|
||||||
# Save bazel repository cache to use on subsequent runs.
|
|
||||||
# We don't save node_modules because it's faster to use the linux workspace and reinstall.
|
|
||||||
- save_cache:
|
|
||||||
key: *cache_key_win
|
|
||||||
paths:
|
|
||||||
- "C:/Users/circleci/bazel_repository_cache"
|
|
||||||
|
|
||||||
test_ivy_aot_win:
|
|
||||||
executor: windows-executor
|
|
||||||
steps:
|
|
||||||
- setup_win
|
|
||||||
- run:
|
|
||||||
command: yarn circleci-win-ivy
|
|
||||||
no_output_timeout: 45m
|
|
||||||
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
@ -775,21 +381,9 @@ workflows:
|
|||||||
- lint:
|
- lint:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
- test:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- test_ivy_aot:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- build-npm-packages:
|
- build-npm-packages:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
- build-ivy-npm-packages:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- legacy-unit-tests-saucelabs:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- test_aio:
|
- test_aio:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
@ -799,22 +393,9 @@ workflows:
|
|||||||
- test_aio_local:
|
- test_aio_local:
|
||||||
requires:
|
requires:
|
||||||
- build-npm-packages
|
- build-npm-packages
|
||||||
- test_aio_local:
|
|
||||||
name: test_aio_local_viewengine
|
|
||||||
viewengine: true
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_aio_tools:
|
- test_aio_tools:
|
||||||
requires:
|
requires:
|
||||||
- build-npm-packages
|
- build-npm-packages
|
||||||
- test_docs_examples:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_docs_examples:
|
|
||||||
name: test_docs_examples_viewengine
|
|
||||||
viewengine: true
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- aio_preview:
|
- aio_preview:
|
||||||
# Only run on PR builds. (There can be no previews for non-PR builds.)
|
# Only run on PR builds. (There can be no previews for non-PR builds.)
|
||||||
<<: *only_on_pull_requests
|
<<: *only_on_pull_requests
|
||||||
@ -823,78 +404,3 @@ workflows:
|
|||||||
- test_aio_preview:
|
- test_aio_preview:
|
||||||
requires:
|
requires:
|
||||||
- aio_preview
|
- aio_preview
|
||||||
- publish_packages_as_artifacts:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- publish_snapshot:
|
|
||||||
# Note: no filters on this job because we want it to run for all upstream branches
|
|
||||||
# We'd really like to filter out pull requests here, but not yet available:
|
|
||||||
# https://discuss.circleci.com/t/workflows-pull-request-filter/14396/4
|
|
||||||
# Instead, the job just exits immediately at the first step.
|
|
||||||
requires:
|
|
||||||
# Only publish if tests and integration tests pass
|
|
||||||
- test
|
|
||||||
- test_ivy_aot
|
|
||||||
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
|
|
||||||
- test_aio_local
|
|
||||||
- test_aio_local_viewengine
|
|
||||||
- test_docs_examples
|
|
||||||
- test_docs_examples_viewengine
|
|
||||||
# Get the artifacts to publish from the build-packages-dist job
|
|
||||||
# since the publishing script expects the legacy outputs layout.
|
|
||||||
- build-npm-packages
|
|
||||||
- build-ivy-npm-packages
|
|
||||||
- legacy-unit-tests-saucelabs
|
|
||||||
- components-repo-unit-tests:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_zonejs:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
# Windows Jobs
|
|
||||||
# These are very slow so we run them on non-PRs only for now.
|
|
||||||
# TODO: remove the filter when CircleCI makes Windows FS faster.
|
|
||||||
# The Windows jobs are only run after their non-windows counterparts finish successfully.
|
|
||||||
# This isn't strictly necessary as there is no artifact dependency, but helps economize
|
|
||||||
# CI resources by not attempting to build when we know should fail.
|
|
||||||
- test_win:
|
|
||||||
<<: *skip_on_pull_requests
|
|
||||||
requires:
|
|
||||||
- test
|
|
||||||
- test_ivy_aot_win:
|
|
||||||
<<: *skip_on_pull_requests
|
|
||||||
requires:
|
|
||||||
- test_ivy_aot
|
|
||||||
|
|
||||||
monitoring:
|
|
||||||
jobs:
|
|
||||||
- setup
|
|
||||||
- aio_monitoring_stable:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- aio_monitoring_next:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- saucelabs_ivy:
|
|
||||||
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
|
|
||||||
# each karma_web_test target is provisioning and tearing down browsers which is adding
|
|
||||||
# a lot of overhead. Running once daily on master only to avoid wasting resources and
|
|
||||||
# slowing down CI for PRs.
|
|
||||||
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
|
|
||||||
# share provisioned browsers and we can remove the legacy saucelabs job.
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- saucelabs_view_engine:
|
|
||||||
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
|
|
||||||
# each karma_web_test target is provisioning and tearing down browsers which is adding
|
|
||||||
# a lot of overhead. Running once daily on master only to avoid wasting resources and
|
|
||||||
# slowing down CI for PRs.
|
|
||||||
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
|
|
||||||
# share provisioned browsers and we can remove the legacy saucelabs job.
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
triggers:
|
|
||||||
- schedule:
|
|
||||||
<<: *only_on_master
|
|
||||||
# Runs monitoring jobs at 10:00AM every day.
|
|
||||||
cron: "0 10 * * *"
|
|
||||||
|
@ -17,7 +17,7 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
setPublicVar CI "$CI"
|
setPublicVar CI "$CI"
|
||||||
setPublicVar PROJECT_ROOT "$projectDir";
|
setPublicVar PROJECT_ROOT "$projectDir";
|
||||||
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
setPublicVar CI_AIO_MIN_PWA_SCORE "62";
|
||||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
||||||
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
||||||
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
|
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
|
||||||
@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
|||||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||||
setPublicVar COMPONENTS_REPO_COMMIT "f428c00465dfcf8a020237f22532480eedbd2cb6"
|
setPublicVar COMPONENTS_REPO_COMMIT "09e68db8ed5b1253f2fe38ff954ef0df019fc25a"
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,12 +1,12 @@
|
|||||||
## Lista de Verificación del PR
|
## Lista de Verificación del PR
|
||||||
Comprueba si tu PR cumple los siguientes requisitos:
|
Comprueba si tu PR cumple los siguientes requisitos:
|
||||||
|
|
||||||
- [ ] El mensaje de commit conforme con [nuestras reglas](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit)
|
- [ ] El mensaje de commit esta conforme con [nuestras reglas](https://github.com/angular-hispano/angular/blob/master/CONTRIBUTING.md#-formato-para-el-mensaje-de-los-commits)
|
||||||
- [ ] Prueba los cambios que agregaste (arreglo de bugs / funcionalidades)
|
- [ ] Probe los cambios que agregué (arreglo de bugs / funcionalidades)
|
||||||
- [ ] Revisa tus traducciones o cambios de contenido
|
- [ ] Revisé previamente las traducciones o cambios de contenido
|
||||||
- [ ] Consulté el [diccionario de términos](https://github.com/angular-hispano/angular/issues/9) en español
|
- [ ] Consulté el [diccionario de términos](https://github.com/angular-hispano/angular/blob/master/aio/diccionario-de-términos.md) en español
|
||||||
- [ ] Se han creado dos archivos con la extensión correspondiente(.en.md para el archivo en inglés y .md para el Archivo en español)
|
- [ ] He creado dos archivos con la extensión correspondiente(.en.md para el archivo en inglés y .md para el Archivo en español)
|
||||||
- [ ] PR enlazada con el issue correspondiente
|
- [ ] He enlazado el commit con el issue correspondiente <!-- ejemplo Fixes #X -->
|
||||||
|
|
||||||
|
|
||||||
## Tipo de PR
|
## Tipo de PR
|
||||||
@ -16,13 +16,13 @@ Comprueba si tu PR cumple los siguientes requisitos:
|
|||||||
|
|
||||||
- [ ] Bugfix
|
- [ ] Bugfix
|
||||||
- [ ] Funcionalidad
|
- [ ] Funcionalidad
|
||||||
- [ ] Actualizar el estilo del código (formato, variables locales)
|
- [ ] Actualización de el estilo del código (formato, variables locales)
|
||||||
- [ ] Refactorización (no cambios en la funcionalidad, no cambios en el api)
|
- [ ] Refactorización (no cambios en la funcionalidad, no cambios en el api)
|
||||||
- [ ] Cambios relacionados al build
|
- [ ] Cambios relacionados al build
|
||||||
- [ ] Cambios relacionados al CI (Integración continua)
|
- [ ] Cambios relacionados al CI (Integración continua)
|
||||||
- [ ] Cambios en el contenido de la documentación
|
- [ ] Cambios en el contenido de la documentación
|
||||||
- [ ] Cambios en la aplicación / infrastructura de angular.io
|
- [ ] Cambios en la aplicación / infraestructura de angular.io
|
||||||
- [ ] Otro... Por favor describela:
|
- [ ] Otro... Por favor describe la:
|
||||||
|
|
||||||
## ¿Cuál es el comportamiento actual?
|
## ¿Cuál es el comportamiento actual?
|
||||||
<!-- Describe el comportamiento actual que está modificando o vincule a un problema relevante.
|
<!-- Describe el comportamiento actual que está modificando o vincule a un problema relevante.
|
||||||
|
@ -6,7 +6,7 @@ import {CommitMessageConfig} from '../dev-infra/commit-message/config';
|
|||||||
export const commitMessage: CommitMessageConfig = {
|
export const commitMessage: CommitMessageConfig = {
|
||||||
maxLineLength: 120,
|
maxLineLength: 120,
|
||||||
minBodyLength: 20,
|
minBodyLength: 20,
|
||||||
minBodyLengthTypeExcludes: ['docs'],
|
minBodyLengthTypeExcludes: ['docs', 'upstream'],
|
||||||
types: [
|
types: [
|
||||||
'build',
|
'build',
|
||||||
'ci',
|
'ci',
|
||||||
@ -18,6 +18,7 @@ export const commitMessage: CommitMessageConfig = {
|
|||||||
'release',
|
'release',
|
||||||
'style',
|
'style',
|
||||||
'test',
|
'test',
|
||||||
|
'upstream',
|
||||||
],
|
],
|
||||||
scopes: [
|
scopes: [
|
||||||
'animations',
|
'animations',
|
||||||
|
@ -67,6 +67,25 @@ version: 3
|
|||||||
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
||||||
# used throughout the config.
|
# used throughout the config.
|
||||||
meta:
|
meta:
|
||||||
|
# The following groups have no file based conditions and will be initially `active` on all PRs
|
||||||
|
# - `global-approvers`
|
||||||
|
# - `global-docs-approvers`
|
||||||
|
# - `required-minimum-review`
|
||||||
|
#
|
||||||
|
# By checking the number of active/pending/rejected groups when these are excluded, we can determine
|
||||||
|
# if any other groups are matched.
|
||||||
|
#
|
||||||
|
# Note: Because all inactive groups start as pending, we are only checking pending and rejected active groups.
|
||||||
|
#
|
||||||
|
# Also note that the ordering of groups matters in this file. The only groups visible to the current
|
||||||
|
# one are those that appear above it.
|
||||||
|
no-groups-above-this-pending: &no-groups-above-this-pending
|
||||||
|
len(groups.active.pending.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
||||||
|
no-groups-above-this-rejected: &no-groups-above-this-rejected
|
||||||
|
len(groups.active.rejected.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
||||||
|
no-groups-above-this-active: &no-groups-above-this-active
|
||||||
|
len(groups.active.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
||||||
|
|
||||||
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
||||||
can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
@ -863,6 +882,7 @@ groups:
|
|||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
|
'aio/content/guide/roadmap.md',
|
||||||
'aio/content/marketing/**',
|
'aio/content/marketing/**',
|
||||||
'aio/content/images/bios/**',
|
'aio/content/images/bios/**',
|
||||||
'aio/content/images/marketing/**',
|
'aio/content/images/marketing/**',
|
||||||
@ -1090,6 +1110,7 @@ groups:
|
|||||||
'dev-infra/**',
|
'dev-infra/**',
|
||||||
'docs/BAZEL.md',
|
'docs/BAZEL.md',
|
||||||
'docs/CARETAKER.md',
|
'docs/CARETAKER.md',
|
||||||
|
'docs/CODING_STANDARDS.md',
|
||||||
'docs/COMMITTER.md',
|
'docs/COMMITTER.md',
|
||||||
'docs/DEBUG.md',
|
'docs/DEBUG.md',
|
||||||
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
||||||
@ -1142,6 +1163,8 @@ groups:
|
|||||||
public-api:
|
public-api:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
|
- *no-groups-above-this-pending
|
||||||
|
- *no-groups-above-this-rejected
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
@ -1155,14 +1178,16 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- alxhub
|
- alxhub
|
||||||
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: -1 # request reviews from everyone
|
request: 4 # Request reviews from four people
|
||||||
required: 3 # require at least 3 approvals
|
required: 3 # Require that three people approve
|
||||||
reviewed_for: required
|
reviewed_for: required
|
||||||
|
|
||||||
|
|
||||||
@ -1172,6 +1197,8 @@ groups:
|
|||||||
size-tracking:
|
size-tracking:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
|
- *no-groups-above-this-pending
|
||||||
|
- *no-groups-above-this-rejected
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
@ -1179,14 +1206,16 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- alxhub
|
- alxhub
|
||||||
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: -1 # request reviews from everyone
|
request: 4 # Request reviews from four people
|
||||||
required: 2 # require at least 2 approvals
|
required: 2 # Require that two people approve
|
||||||
reviewed_for: required
|
reviewed_for: required
|
||||||
|
|
||||||
|
|
||||||
@ -1196,6 +1225,8 @@ groups:
|
|||||||
circular-dependencies:
|
circular-dependencies:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
|
- *no-groups-above-this-pending
|
||||||
|
- *no-groups-above-this-rejected
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
@ -1203,9 +1234,11 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
|
- alxhub
|
||||||
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- josephperrott
|
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
@ -1227,7 +1260,10 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
|
- alxhub
|
||||||
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- josephperrott
|
- josephperrott
|
||||||
- mhevery
|
- mhevery
|
||||||
@ -1257,14 +1293,7 @@ groups:
|
|||||||
# `global-approvers` can still approve PRs that match this `fallback` rule,
|
# `global-approvers` can still approve PRs that match this `fallback` rule,
|
||||||
# but that should be an exception and not an expectation.
|
# but that should be an exception and not an expectation.
|
||||||
conditions:
|
conditions:
|
||||||
# The following groups have no file based conditions and will be initially `active` on all PRs
|
- *no-groups-above-this-active
|
||||||
# - `global-approvers`
|
|
||||||
# - `global-docs-approvers`
|
|
||||||
# - `required-minimum-review`
|
|
||||||
#
|
|
||||||
# By checking the number of active groups when these are excluded, we can determine
|
|
||||||
# if any other groups are matched.
|
|
||||||
- len(groups.active.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
|
||||||
# When any of the `global-*` groups is approved, they cause other groups to deactivate.
|
# When any of the `global-*` groups is approved, they cause other groups to deactivate.
|
||||||
# In those cases, the condition above would evaluate to `true` while in reality, only a global
|
# In those cases, the condition above would evaluate to `true` while in reality, only a global
|
||||||
# approval has been provided. To ensure we don't activate the fallback group in such cases,
|
# approval has been provided. To ensure we don't activate the fallback group in such cases,
|
||||||
|
92
CHANGELOG.md
92
CHANGELOG.md
@ -1,3 +1,95 @@
|
|||||||
|
<a name="10.1.0-next.4"></a>
|
||||||
|
# 10.1.0-next.4 (2020-08-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** narrow `NgIf` context variables in template type checker ([#36627](https://github.com/angular/angular/issues/36627)) ([9c8bc4a](https://github.com/angular/angular/commit/9c8bc4a))
|
||||||
|
* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **core:** rename async to waitForAsync to avoid confusing ([#37583](https://github.com/angular/angular/issues/37583)) ([8f07429](https://github.com/angular/angular/commit/8f07429))
|
||||||
|
* **core:** update reference and doc to change `async` to `waitAsync`. ([#37583](https://github.com/angular/angular/issues/37583)) ([8fbf40b](https://github.com/angular/angular/commit/8fbf40b))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.8"></a>
|
||||||
|
## 10.0.8 (2020-08-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** add PURE annotation to getInheritedFactory calls ([#38291](https://github.com/angular/angular/issues/38291)) ([03d8e31](https://github.com/angular/angular/commit/03d8e31))
|
||||||
|
* **compiler:** update unparsable character reference entity error messages ([#38319](https://github.com/angular/angular/issues/38319)) ([cea4678](https://github.com/angular/angular/commit/cea4678)), closes [#26067](https://github.com/angular/angular/issues/26067)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.7"></a>
|
||||||
|
## 10.0.7 (2020-07-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** Metadata should not include methods on Object.prototype ([#38292](https://github.com/angular/angular/issues/38292)) ([879ff08](https://github.com/angular/angular/commit/879ff08))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.0-next.3"></a>
|
||||||
|
# 10.1.0-next.3 (2020-07-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **elements:** run strategy methods in correct zone ([#37814](https://github.com/angular/angular/issues/37814)) ([8df888d](https://github.com/angular/angular/commit/8df888d)), closes [#24181](https://github.com/angular/angular/issues/24181)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.6"></a>
|
||||||
|
## 10.0.6 (2020-07-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** share identical stylesheets between components in the same file ([#38213](https://github.com/angular/angular/issues/38213)) ([264950b](https://github.com/angular/angular/commit/264950b)), closes [#38204](https://github.com/angular/angular/issues/38204)
|
||||||
|
* **compiler-cli:** Add support for string literal class members ([#38226](https://github.com/angular/angular/issues/38226)) ([b1e7775](https://github.com/angular/angular/commit/b1e7775))
|
||||||
|
* **core:** `Attribute` decorator `attributeName` is mandatory ([#38131](https://github.com/angular/angular/issues/38131)) ([1c4fcce](https://github.com/angular/angular/commit/1c4fcce)), closes [#32658](https://github.com/angular/angular/issues/32658)
|
||||||
|
* **core:** unify the signature between ngZone and noopZone ([#37581](https://github.com/angular/angular/issues/37581)) ([d5264f5](https://github.com/angular/angular/commit/d5264f5))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.0-next.2"></a>
|
||||||
|
# 10.1.0-next.2 (2020-07-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#35464](https://github.com/angular/angular/issues/35464)) ([737506e](https://github.com/angular/angular/commit/737506e)), closes [#30497](https://github.com/angular/angular/issues/30497)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** add ReadonlyMap in place of Map in keyValuePipe ([#37311](https://github.com/angular/angular/issues/37311)) ([3373453](https://github.com/angular/angular/commit/3373453)), closes [#37308](https://github.com/angular/angular/issues/37308)
|
||||||
|
* **forms:** AbstractControl to store raw validators in addition to combined validators function ([#37881](https://github.com/angular/angular/issues/37881)) ([ad7046b](https://github.com/angular/angular/commit/ad7046b))
|
||||||
|
* **localize:** allow duplicate messages to be handled during extraction ([#38082](https://github.com/angular/angular/issues/38082)) ([cf9a47b](https://github.com/angular/angular/commit/cf9a47b)), closes [#38077](https://github.com/angular/angular/issues/38077)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.5"></a>
|
||||||
|
## 10.0.5 (2020-07-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** properly associate source spans for implicitly closed elements ([#38126](https://github.com/angular/angular/issues/38126)) ([e80278c](https://github.com/angular/angular/commit/e80278c)), closes [#36118](https://github.com/angular/angular/issues/36118)
|
||||||
|
* **compiler-cli:** ensure file_system handles mixed Windows drives ([#38030](https://github.com/angular/angular/issues/38030)) ([dba4023](https://github.com/angular/angular/commit/dba4023)), closes [#36777](https://github.com/angular/angular/issues/36777)
|
||||||
|
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#38119](https://github.com/angular/angular/issues/38119)) ([14b4718](https://github.com/angular/angular/commit/14b4718)), closes [#30497](https://github.com/angular/angular/issues/30497)
|
||||||
|
* **core:** error due to integer overflow when there are too many host bindings ([#38014](https://github.com/angular/angular/issues/38014)) ([7b6e73c](https://github.com/angular/angular/commit/7b6e73c)), closes [#37876](https://github.com/angular/angular/issues/37876) [#37876](https://github.com/angular/angular/issues/37876)
|
||||||
|
* **core:** incorrectly validating properties on ng-content and ng-container ([#37773](https://github.com/angular/angular/issues/37773)) ([17ddab9](https://github.com/angular/angular/commit/17ddab9))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.0.4"></a>
|
<a name="10.0.4"></a>
|
||||||
## 10.0.4 (2020-07-15)
|
## 10.0.4 (2020-07-15)
|
||||||
|
|
||||||
|
@ -1,12 +1,72 @@
|
|||||||
# Contributor Code of Conduct
|
# Código de Conducta
|
||||||
## Version 0.3b-angular
|
|
||||||
|
|
||||||
As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
|
## 1. Propósito
|
||||||
|
|
||||||
Communication through any of Angular's channels (GitHub, Gitter, IRC, mailing lists, Google+, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
“Prometemos brindar cortesía y respeto a cualquier persona involucrada en esta comunidad, sin importar el género con el que se identifique, su orientación sexual, limitación física, edad, raza, etnia, religión o nivel de conocimiento. Esperamos que cualquiera que desee contribuir en este proyecto brinde el mismo comportamiento”
|
||||||
|
|
||||||
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.
|
Bajo ese principio queremos enfocar esta comunidad, una comunidad de respeto por el otro, donde cualquiera que sienta pasión por Angular y desee involucrarse con cualquier tipo de actividad deberá ayudar a mantener una atmósfera de cortesía por el otro, respetando los pensamientos, acciones, ideales y propuestas del otro.
|
||||||
|
|
||||||
If any member of the community violates this code of conduct, the maintainers of the Angular project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
|
## 2. Comportamiento esperado
|
||||||
|
|
||||||
If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [conduct@angular.io](mailto:conduct@angular.io).
|
- Evitar usar expresiones o gestos insultantes, humillantes o intimidatorios para referirnos a otros.
|
||||||
|
- Ejercita la consideración y el respeto en tus comunicaciones y acciones.
|
||||||
|
- Absténte de adoptar una conducta y un lenguaje degradantes, discriminatorios, abusivos o acosadores.
|
||||||
|
- Evitar comportamientos que agredan las expresiones de identidad, género, creencias religiosas, acciones que lastimen o los aíslen a otros.
|
||||||
|
- Evitar comentarios sobre ideas políticas.
|
||||||
|
- No se toleran conductas físicas y actitudes dirigidas al descrédito personal, físico o emocional de cualquier persona.
|
||||||
|
- No se toleran chistes y comentarios excluyentes, sexistas y racistas.
|
||||||
|
- Se rechaza actitudes de hostigamiento.
|
||||||
|
- Se rechaza el acoso: este se entiende como cualquier tipo de comentario verbal que refuerce discriminación por género, identidad y expresión de género, orientación sexual, discapacidad, apariencia física, tamaño corporal, raza, edad o religión en contextos laborales o sociales.
|
||||||
|
- No se tolera el contacto físico y/o la atención sexual no deseada.
|
||||||
|
- Promover la igualdad de oportunidades de formación, educación, intercambio y retroalimentación antes, durante y después de los eventos.
|
||||||
|
- Intenta colaborar en lugar de generar conflicto.
|
||||||
|
- Sé consciente de tu entorno y de tus compañeros participantes. Alerta a líderes de la comunidad si notas una situación peligrosa, alguien en apuros, o violaciones de este Código de Conducta, incluso si parecen intrascendentes.
|
||||||
|
|
||||||
|
## 3. Comportamiento inaceptable
|
||||||
|
Comportamientos inaceptables incluyen: intimidación, acoso, abuso, discriminación, comunicación despectiva o degradante o acciones por cualquier participante en nuestra comunidad ya sea virtual, o en las comunicaciones uno-a-uno que se realizan en el contexto de la comunidad. Por favor ser respetuoso con todos.
|
||||||
|
|
||||||
|
El acoso incluye: comentarios nocivos o perjudiciales, verbales o escritos relacionados con el género, la orientación sexual, raza, religión, discapacidad; uso inadecuado de desnudos y / o imágenes sexuales en espacios públicos (incluyendo la presentación diapositivas); intimidación deliberada, acecho o seguimiento; fotografías o grabaciones acosadoras; interrupción sostenida de charlas y otros eventos; contacto físico inapropiado, y atención sexual no deseada.
|
||||||
|
|
||||||
|
## 4. Consecuencias de comportamiento inaceptable
|
||||||
|
Se espera que personas a quienes se les solicite que detengan su comportamiento inaceptable lo hagan de manera inmediata esto será válido para cualquier miembro de la comunidad.
|
||||||
|
|
||||||
|
En caso de presentarse una violación al código de conducta de manera repetida se tendrá cero tolerancia a este comportamiento por parte de los organizadores.
|
||||||
|
|
||||||
|
Si un miembro de la comunidad participa en una conducta inaceptable, los organizadores pueden tomar cualquier acción que consideren apropiada, hasta e incluyendo una prohibición temporal o expulsión permanente de la comunidad, sin previo aviso.
|
||||||
|
|
||||||
|
Priorizamos la seguridad de las personas marginadas sobre la comodidad de las personas privilegiadas. Nos reservamos el derecho de no actuar sobre las quejas relacionadas con:
|
||||||
|
|
||||||
|
- El "racismo inverso", "sexismo inverso" y "cisfobia".
|
||||||
|
- Comunicación razonable de límites, como "déjame en paz", "vete" o "no estoy discutiendo esto contigo".
|
||||||
|
- Al comunicarse en un "tono", no se encuentra agradable.
|
||||||
|
- Identificando comportamientos o suposiciones racistas, sexistas, cissexistas u opresivas.
|
||||||
|
|
||||||
|
## 5. ¿Qué hacer si se incumple el código de conducta?
|
||||||
|
Si eres víctima o testigo de una conducta inaceptable, o tienes cualquier inquietud, por favor comunícate con el equipo organizador lo antes posible:
|
||||||
|
|
||||||
|
- Mensaje directo: Michael Prentice (@splaktar)
|
||||||
|
- Mensaje directo: Jorge Cano (@jorgeucano)
|
||||||
|
- Mensaje directo: Andrés Villanueva (@villanuevand)
|
||||||
|
- Email: [soporte@angular.lat](mailto:soporte@angular.lat)
|
||||||
|
|
||||||
|
### ¿Qué pasa después?
|
||||||
|
- Una vez haya sido notificada el no cumplimiento de la norma, el equipo de liderazgo se reunirán y analizarán el caso.
|
||||||
|
- Los incidentes presentados se manejarán con discreción.
|
||||||
|
- Los organizadores se comunicarán con la persona que incumplió la norma y tomarán las medidas respectivas.
|
||||||
|
- Se realizará un acompañamiento a la persona agredida y se apoyará.
|
||||||
|
- Si el incidente se hace público, Angular Hispano no se hace responsable de los perjuicios que esto pueda ocasionar en el agresor o el agredido.
|
||||||
|
|
||||||
|
## 6. Alcance
|
||||||
|
Esperamos que todos los participantes de la comunidad (organizadores, voluntarios, contribuyentes pagados o de otro modo; patrocinadores; y otros invitados) se atengan a este Código de Conducta en todos los espacios virtuales y presenciales, así como en todas las comunicaciones de uno-a-uno pertinentes la comunidad.
|
||||||
|
|
||||||
|
### Colaboradores
|
||||||
|
|
||||||
|
- Alejandra Giraldo
|
||||||
|
- Vanessa Marely
|
||||||
|
- Mariano Alvarez
|
||||||
|
- Andrés Villanueva
|
||||||
|
- Michael Prentice
|
||||||
|
- javascript&friends
|
||||||
|
- Angular
|
||||||
|
- She Codes Angular
|
||||||
|
- Colombia dev
|
359
CONTRIBUTING.en.md
Normal file
359
CONTRIBUTING.en.md
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
# Contributing to Angular
|
||||||
|
|
||||||
|
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)
|
||||||
|
- [Question or Problem?](#question)
|
||||||
|
- [Issues and Bugs](#issue)
|
||||||
|
- [Feature Requests](#feature)
|
||||||
|
- [Submission Guidelines](#submit)
|
||||||
|
- [Coding Rules](#rules)
|
||||||
|
- [Commit Message Guidelines](#commit)
|
||||||
|
- [Signing the CLA](#cla)
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="coc"></a> Code of Conduct
|
||||||
|
|
||||||
|
Help us keep Angular open and inclusive.
|
||||||
|
Please read and follow our [Code of Conduct][coc].
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
|
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests.
|
||||||
|
Instead, we recommend using [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) to ask support-related questions. When creating a new question on Stack Overflow, make sure to add the `angular` tag.
|
||||||
|
|
||||||
|
Stack Overflow is a much better place to ask questions since:
|
||||||
|
|
||||||
|
- there are thousands of people willing to help on Stack Overflow
|
||||||
|
- questions and answers stay available for public viewing so your question/answer might help someone else
|
||||||
|
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||||
|
|
||||||
|
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||||
|
|
||||||
|
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> 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 consider the size of the change in order to determine the right steps to proceed:
|
||||||
|
|
||||||
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
|
||||||
|
This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
|
||||||
|
|
||||||
|
**Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
|
||||||
|
|
||||||
|
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="submit"></a> Submission Guidelines
|
||||||
|
|
||||||
|
|
||||||
|
### <a name="submit-issue"></a> Submitting an Issue
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 require that you provide a minimal reproduction.
|
||||||
|
Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
|
||||||
|
|
||||||
|
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
||||||
|
|
||||||
|
We require a minimal reproduction to save maintainers' time and ultimately be able to fix more bugs.
|
||||||
|
Often, developers find coding problems themselves while preparing a minimal reproduction.
|
||||||
|
We understand that sometimes it might be hard to extract essential bits of code from a larger codebase 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 doesn't have enough info to be reproduced.
|
||||||
|
|
||||||
|
You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
|
||||||
|
|
||||||
|
|
||||||
|
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||||
|
|
||||||
|
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||||
|
|
||||||
|
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
|
||||||
|
You don't want to duplicate existing efforts.
|
||||||
|
|
||||||
|
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
||||||
|
Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||||
|
|
||||||
|
3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||||
|
We cannot accept code without a signed CLA.
|
||||||
|
Make sure you author all contributed Git commits with email address associated with your CLA signature.
|
||||||
|
|
||||||
|
4. Fork the angular/angular repo.
|
||||||
|
|
||||||
|
5. Make your changes in a new git branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git checkout -b my-fix-branch master
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Create your patch, **including appropriate test cases**.
|
||||||
|
|
||||||
|
7. Follow our [Coding Rules](#rules).
|
||||||
|
|
||||||
|
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
|
||||||
|
|
||||||
|
9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
|
||||||
|
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit -a
|
||||||
|
```
|
||||||
|
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||||
|
|
||||||
|
10. Push your branch to GitHub:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git push origin my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
11. In GitHub, send a pull request to `angular:master`.
|
||||||
|
|
||||||
|
If we ask for changes via code reviews then:
|
||||||
|
|
||||||
|
* Make the required updates.
|
||||||
|
* 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
|
||||||
|
git rebase master -i
|
||||||
|
git push -f
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! Thank you for your contribution!
|
||||||
|
|
||||||
|
|
||||||
|
#### After your pull request is merged
|
||||||
|
|
||||||
|
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
|
||||||
|
|
||||||
|
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git push origin --delete my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
* Check out the master branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git checkout master -f
|
||||||
|
```
|
||||||
|
|
||||||
|
* Delete the local branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git branch -D my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
* Update your master with the latest upstream version:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git pull --ff upstream master
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="rules"></a> Coding Rules
|
||||||
|
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||||
|
|
||||||
|
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||||
|
* All public API methods **must be documented**.
|
||||||
|
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
|
||||||
|
|
||||||
|
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="commit"></a> Commit Message Format
|
||||||
|
|
||||||
|
*This specification is inspired and supersedes the [AngularJS commit message format][commit-message-format].*
|
||||||
|
|
||||||
|
We have very precise rules over how our Git commit messages must be formatted.
|
||||||
|
This format leads to **easier to read commit history**.
|
||||||
|
|
||||||
|
Each commit message consists of a **header**, a **body**, and a **footer**.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
<header>
|
||||||
|
<BLANK LINE>
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format.
|
||||||
|
|
||||||
|
The `body` is mandatory for all commits except for those of scope "docs".
|
||||||
|
When the body is required it must be at least 20 characters long.
|
||||||
|
|
||||||
|
The `footer` is optional.
|
||||||
|
|
||||||
|
Any line of the commit message cannot be longer than 100 characters.
|
||||||
|
|
||||||
|
|
||||||
|
#### <a href="commit-header"></a>Commit Message Header
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <short summary>
|
||||||
|
│ │ │
|
||||||
|
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
|
||||||
|
│ │
|
||||||
|
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||||
|
│ elements|forms|http|language-service|localize|platform-browser|
|
||||||
|
│ platform-browser-dynamic|platform-server|platform-webworker|
|
||||||
|
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
||||||
|
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
||||||
|
│
|
||||||
|
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||||
|
```
|
||||||
|
|
||||||
|
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
||||||
|
|
||||||
|
|
||||||
|
##### Type
|
||||||
|
|
||||||
|
Must be one of the following:
|
||||||
|
|
||||||
|
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||||
|
* **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
|
||||||
|
* **docs**: Documentation only changes
|
||||||
|
* **feat**: A new feature
|
||||||
|
* **fix**: A bug fix
|
||||||
|
* **perf**: A code change that improves performance
|
||||||
|
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||||
|
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||||
|
* **test**: Adding missing tests or correcting existing tests
|
||||||
|
|
||||||
|
|
||||||
|
##### Scope
|
||||||
|
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||||
|
|
||||||
|
The following is the list of supported scopes:
|
||||||
|
|
||||||
|
* `animations`
|
||||||
|
* `bazel`
|
||||||
|
* `benchpress`
|
||||||
|
* `common`
|
||||||
|
* `compiler`
|
||||||
|
* `compiler-cli`
|
||||||
|
* `core`
|
||||||
|
* `elements`
|
||||||
|
* `forms`
|
||||||
|
* `http`
|
||||||
|
* `language-service`
|
||||||
|
* `localize`
|
||||||
|
* `platform-browser`
|
||||||
|
* `platform-browser-dynamic`
|
||||||
|
* `platform-server`
|
||||||
|
* `platform-webworker`
|
||||||
|
* `platform-webworker-dynamic`
|
||||||
|
* `router`
|
||||||
|
* `service-worker`
|
||||||
|
* `upgrade`
|
||||||
|
* `zone.js`
|
||||||
|
|
||||||
|
There are currently a few exceptions to the "use package name" rule:
|
||||||
|
|
||||||
|
* `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||||
|
|
||||||
|
* `changelog`: used for updating the release notes in CHANGELOG.md
|
||||||
|
|
||||||
|
* `dev-infra`: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
||||||
|
|
||||||
|
* `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
||||||
|
|
||||||
|
* `migrations`: used for changes to the `ng update` migrations.
|
||||||
|
|
||||||
|
* `ngcc`: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
||||||
|
|
||||||
|
* `ve`: used for changes specific to ViewEngine (legacy compiler/renderer).
|
||||||
|
|
||||||
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
|
||||||
|
|
||||||
|
|
||||||
|
##### Summary
|
||||||
|
|
||||||
|
Use the summary field to provide a succinct description of the change:
|
||||||
|
|
||||||
|
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
|
* don't capitalize the first letter
|
||||||
|
* no dot (.) at the end
|
||||||
|
|
||||||
|
|
||||||
|
#### Commit Message Body
|
||||||
|
|
||||||
|
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
|
||||||
|
|
||||||
|
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
|
||||||
|
You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
|
||||||
|
|
||||||
|
|
||||||
|
#### Commit Message Footer
|
||||||
|
|
||||||
|
The footer can contain information about breaking changes and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
|
||||||
|
|
||||||
|
```
|
||||||
|
BREAKING CHANGE: <breaking change summary>
|
||||||
|
<BLANK LINE>
|
||||||
|
<breaking change description + migration instructions>
|
||||||
|
<BLANK LINE>
|
||||||
|
<BLANK LINE>
|
||||||
|
Fixes #<issue number>
|
||||||
|
```
|
||||||
|
|
||||||
|
Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
|
||||||
|
|
||||||
|
|
||||||
|
### Revert commits
|
||||||
|
|
||||||
|
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
|
||||||
|
|
||||||
|
The content of the commit message body should contain:
|
||||||
|
|
||||||
|
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
|
||||||
|
- a clear description of the reason for reverting the commit message.
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="cla"></a> Signing the CLA
|
||||||
|
|
||||||
|
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||||
|
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||||
|
|
||||||
|
* For individuals, we have a [simple click-through form][individual-cla].
|
||||||
|
* For corporations, we'll need you to
|
||||||
|
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
||||||
|
|
||||||
|
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
|
||||||
|
|
||||||
|
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
|
||||||
|
|
||||||
|
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
|
||||||
|
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
|
||||||
|
* https://help.github.com/articles/about-commit-email-addresses/
|
||||||
|
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[angular-group]: https://groups.google.com/forum/#!forum/angular
|
||||||
|
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||||
|
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||||
|
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
|
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
|
||||||
|
[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/jsguide.html
|
||||||
|
[jsfiddle]: http://jsfiddle.net
|
||||||
|
[plunker]: http://plnkr.co/edit
|
||||||
|
[runnable]: http://runnable.com
|
||||||
|
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
303
CONTRIBUTING.md
303
CONTRIBUTING.md
@ -1,243 +1,243 @@
|
|||||||
# Contributing to Angular
|
# Contribuye a Angular
|
||||||
|
|
||||||
We would love for you to contribute to Angular and help make it even better than it is today!
|
¡Nos encantaría que contribuyeras a Angular y que ayudaras a hacerlo aún mejor de lo que es hoy!
|
||||||
As a contributor, here are the guidelines we would like you to follow:
|
Como colaborador, estos son los lineamientos que nos gustaría que siguieras:
|
||||||
|
|
||||||
- [Code of Conduct](#coc)
|
- [Código de conducta](#coc)
|
||||||
- [Question or Problem?](#question)
|
- [¿Preguntas o problemas?](#question)
|
||||||
- [Issues and Bugs](#issue)
|
- [_Issues_ y _bugs_](#issue)
|
||||||
- [Feature Requests](#feature)
|
- [Solicitud de funcionalidades](#feature)
|
||||||
- [Submission Guidelines](#submit)
|
- [Guía para la creación de issues y PRs](#submit)
|
||||||
- [Coding Rules](#rules)
|
- [Reglas del código](#rules)
|
||||||
- [Commit Message Guidelines](#commit)
|
- [Convención para el mensaje de los _commits_](#commit)
|
||||||
- [Signing the CLA](#cla)
|
- [Firma del Acuerdo de Licencia de Colaborador (CLA)](#cla)
|
||||||
|
|
||||||
|
|
||||||
## <a name="coc"></a> Code of Conduct
|
## <a name="coc"></a> Código de conducta
|
||||||
|
|
||||||
Help us keep Angular open and inclusive.
|
Ayúdanos a mantener Angular abierto e inclusivo.
|
||||||
Please read and follow our [Code of Conduct][coc].
|
Por favor lee y sigue nuestro [Código de conducta][coc].
|
||||||
|
|
||||||
|
|
||||||
## <a name="question"></a> Got a Question or Problem?
|
## <a name="question"></a> ¿Tienes alguna pregunta o problema?
|
||||||
|
|
||||||
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests.
|
No abras *issues* para preguntas de soporte general ya que queremos mantener los *issues* de GitHub para reporte de *bugs* y solicitud de funcionalidades.
|
||||||
Instead, we recommend using [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) to ask support-related questions. When creating a new question on Stack Overflow, make sure to add the `angular` tag.
|
En su lugar, recomendamos utilizar [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) para hacer preguntas relacionadas con soporte. Al crear una nueva pregunta en Stack Overflow, asegúrate de agregar el etiqueta (tag) de `angular`.
|
||||||
|
|
||||||
Stack Overflow is a much better place to ask questions since:
|
Stack Overflow es mucho mejor para hacer preguntas ya que:
|
||||||
|
|
||||||
- there are thousands of people willing to help on Stack Overflow
|
- Hay miles de personas dispuestas a ayudar en preguntas y respuestas de Stack Overflow
|
||||||
- questions and answers stay available for public viewing so your question/answer might help someone else
|
que permanecen disponibles para el público, por lo que tu pregunta o respuesta podría ayudar a otra persona.
|
||||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
- El sistema de votación de Stack Overflow asegura que las mejores respuestas sobresalgan y sean visibles.
|
||||||
|
|
||||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
Para ahorrar tu tiempo y el nuestro, cerraremos sistemáticamente todos los *issues* que sean solicitudes de soporte general y redirigiremos a las personas a Stack Overflow.
|
||||||
|
|
||||||
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
Si deseas chatear sobre alguna pregunta en tiempo real, puedes hacerlo a través de nuestro [canal de Gitter][gitter].
|
||||||
|
|
||||||
|
|
||||||
## <a name="issue"></a> Found a Bug?
|
## <a name="issue"></a> ¿Encontraste un 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].
|
Si encontraste un error en el código fuente, puedes ayudarnos [creando un *issue*](#submit-issue) en nuestro [repositorio de GitHub][github].
|
||||||
Even better, you can [submit a Pull Request](#submit-pr) with a fix.
|
O incluso mejor, puedes [crear un *Pull Request*](#submit-pr) con la solución.
|
||||||
|
|
||||||
|
|
||||||
## <a name="feature"></a> Missing a Feature?
|
## <a name="feature"></a> ¿Falta alguna funcionalidad?
|
||||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
|
Puedes solicitar una nueva funcionalidad [creando un *issue*](#submit-issue) en nuestro repositorio de GitHub.
|
||||||
If you would like to *implement* a new feature, please consider the size of the change in order to determine the right steps to proceed:
|
Si deseas implementar una nueva funcionalidad, por favor considera el tamaño del cambio para determinar los pasos correctos para continuar:
|
||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
|
* Para un **cambio significativo**, primero abre un *issue* y describe tu propuesta para que pueda ser discutida.
|
||||||
This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
|
Este proceso nos permite coordinar mejor nuestros esfuerzos, evitar trabajo duplicado y ayudarte a diseñar el cambio para que sea aceptado con éxito en el proyecto.
|
||||||
|
|
||||||
**Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
|
**Nota**: Agregar un nuevo tema a la documentación o reescribir significativamente un tema, también cuenta como *cambio significativo*.
|
||||||
|
|
||||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
* **Cambios pequeños** pueden ser elaborados y directamente [creados como un _pull request_](#submit-pr).
|
||||||
|
|
||||||
|
|
||||||
## <a name="submit"></a> Submission Guidelines
|
## <a name="submit"></a> Guía para la creación de issues y PRs
|
||||||
|
|
||||||
|
|
||||||
### <a name="submit-issue"></a> Submitting an Issue
|
### <a name="submit-issue"></a> Creación de _issues_
|
||||||
|
|
||||||
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.
|
Antes de crear un *issue*, por favor busca en el el *issue tracker*, quizá un *issue* para tu problema ya existe y la discusión puede informarte sobre soluciones alternativas disponibles.
|
||||||
|
|
||||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it.
|
Queremos solucionar todos los problemas lo antes posible, pero antes de corregir un bug necesitamos reproducirlo y confirmarlo.
|
||||||
In order to reproduce bugs, we require that you provide a minimal reproduction.
|
Para reproducir errores, requerimos que proporciones una reproducción mínima.
|
||||||
Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
|
Tener un escenario reproducible mínimo nos brinda una gran cantidad de información importante sin tener que ir y venir con preguntas adicionales.
|
||||||
|
|
||||||
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
Una reproducción mínima nos permite confirmar rápidamente un bug (o señalar un problema de código), así también confirmar que estamos solucionando el problema correcto.
|
||||||
|
|
||||||
We require a minimal reproduction to save maintainers' time and ultimately be able to fix more bugs.
|
Requerimos una reproducción mínima para ahorrar tiempo a los encargados del mantenimiento y en última instancia, poder corregir más bugs.
|
||||||
Often, developers find coding problems themselves while preparing a minimal reproduction.
|
A menudo los desarrolladores encuentran problemas de código mientras preparan una reproducción mínima.
|
||||||
We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
Entendemos que a veces puede ser difícil extraer porciones esenciales de código de un código más grande, pero realmente necesitamos aislar el problema antes de poder solucionarlo.
|
||||||
|
|
||||||
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 doesn't have enough info to be reproduced.
|
Desafortunadamente no podemos investigar/corregir errores sin una reproducción mínima, por lo que si no tenemos tu retroalimentación del bug, vamos a cerrar el *issue* ya que no tiene suficiente información para reproducirse.
|
||||||
|
|
||||||
You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
|
Puedes presentar nuevos *issues* seleccionando nuestra [plantilla de _issues_](https://github.com/angular/angular/issues/new/choose) y complentando la plantilla.
|
||||||
|
|
||||||
|
|
||||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
### <a name="submit-pr"></a> Creación de un Pull Requests (PR)
|
||||||
|
|
||||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
Antes de crear tu Pull Request (PR) considera los siguientes lineamientos:
|
||||||
|
|
||||||
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
|
1. Busca en [GitHub](https://github.com/angular/angular/pulls) PRs que estén abiertos o cerrados y que estén relacionados con el que vas a crear.
|
||||||
You don't want to duplicate existing efforts.
|
No deseas duplicar los esfuerzos existentes.
|
||||||
|
|
||||||
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
2. Asegúrate de que el PR describa el problema que estás solucionando o que documente el diseño de la funcionalidad que deseas agregar.
|
||||||
Discussing the design upfront helps to ensure that we're ready to accept your work.
|
Discutir el diseño por adelantado ayuda a garantizar que estemos listos para aceptar tu trabajo.
|
||||||
|
|
||||||
3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
3. Por favor firma nuestro [Acuerdo de Licencia de Colaborador (CLA)](#cla) antes de crear PRs.
|
||||||
We cannot accept code without a signed CLA.
|
No podemos aceptar el código sin el Acuerdo de Licencia de Colaborador (CLA) firmado.
|
||||||
Make sure you author all contributed Git commits with email address associated with your CLA signature.
|
Asegúrate de crear todas las contribuciones de Git con la dirección de correo electrónico asociada con tu firma del Acuerdo de Licencia de Colaborador (CLA).
|
||||||
|
|
||||||
4. Fork the angular/angular repo.
|
4. Haz *fork* del repositorio angular/angular.
|
||||||
|
|
||||||
5. Make your changes in a new git branch:
|
5. Haz tus cambios en una nueva rama de Git:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git checkout -b my-fix-branch master
|
git checkout -b my-fix-branch master
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Create your patch, **including appropriate test cases**.
|
6. Crea tu correción, **incluyendo casos de prueba apropiados**.
|
||||||
|
|
||||||
7. Follow our [Coding Rules](#rules).
|
7. Sigue nuestras [Reglas de código](#rules).
|
||||||
|
|
||||||
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
|
8. Ejecuta todo el conjunto de pruebas de Angular, tal como está descrito en la [documentación del desarrollador][dev-doc], y asegúrate de que todas las pruebas pasen.
|
||||||
|
|
||||||
9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
|
9. Crea un commit de tus cambios utilizando un mensaje de commit descriptivo que siga nuestra [convención para el mensaje de los commits](#commit).
|
||||||
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
Es necesario cumplir con estas convenciones porque las notas de las versiones se generan automáticamente a partir de estos mensajes.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git commit -a
|
git commit -a
|
||||||
```
|
```
|
||||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
Nota: la opción de la línea de comandos de Git `-a` automaticamente hará "add" y "rm" a los archivos editados.
|
||||||
|
|
||||||
10. Push your branch to GitHub:
|
10. Haz push de tu rama a GitHub:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git push origin my-fix-branch
|
git push origin my-fix-branch
|
||||||
```
|
```
|
||||||
|
|
||||||
11. In GitHub, send a pull request to `angular:master`.
|
11. En GitHub, crea un pull request a `angular:master`.
|
||||||
|
|
||||||
If we ask for changes via code reviews then:
|
Si solicitamos cambios a través de revisiones de código, sigue las siguientes indicaciones:
|
||||||
|
|
||||||
* Make the required updates.
|
* Haz los cambios requeridos.
|
||||||
* Re-run the Angular test suites to ensure tests are still passing.
|
* Ejecuta nuevamente el conjunto de pruebas de Angular para asegurar que todas las pruebas aún están pasando.
|
||||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
* Haz rebase de tu rama a la rama master y haz push con la opción `-f` a tu repositorio de Github (esto actualizará tu Pull Request):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git rebase master -i
|
git rebase master -i
|
||||||
git push -f
|
git push -f
|
||||||
```
|
```
|
||||||
|
|
||||||
That's it! Thank you for your contribution!
|
¡Es todo! ¡Muchas gracias por tu contribución!
|
||||||
|
|
||||||
|
|
||||||
#### After your pull request is merged
|
#### Después del merge de tu pull request
|
||||||
|
|
||||||
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
|
Después de que se hizo merge de tu pull request, puedes eliminar de forma segura tu rama y hacer pull de los cambios del repositorio principal (upstream):
|
||||||
|
|
||||||
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
* Elimina la rama remota en GitHub a través de la interfaz de usuario web de GitHub o en tu línea de comandos de la siguiente manera:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git push origin --delete my-fix-branch
|
git push origin --delete my-fix-branch
|
||||||
```
|
```
|
||||||
|
|
||||||
* Check out the master branch:
|
* Muévete a la rama master:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git checkout master -f
|
git checkout master -f
|
||||||
```
|
```
|
||||||
|
|
||||||
* Delete the local branch:
|
* Elimina tu rama local:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git branch -D my-fix-branch
|
git branch -D my-fix-branch
|
||||||
```
|
```
|
||||||
|
|
||||||
* Update your master with the latest upstream version:
|
* Actualiza tu rama master con la última versión del fork (upstream):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git pull --ff upstream master
|
git pull --ff upstream master
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## <a name="rules"></a> Coding Rules
|
## <a name="rules"></a> Reglas del código
|
||||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
Para garantizar la coherencia en todo el código fuente, ten en cuenta estas reglas mientras trabajas:
|
||||||
|
|
||||||
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
* Todas las funcionalidades o solución de bugs **deben ser probadas** por una o más pruebas (pruebas unitarias).
|
||||||
* All public API methods **must be documented**.
|
* Todos los métodos públicos del API **deben ser documentados**.
|
||||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
|
* Seguimos la [guía de estilo JavaScript de Google][js-style-guide], pero cada línea no debe exceder **100 caracteres**.
|
||||||
|
|
||||||
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
Un formateador automatizado está disponible, revisar [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
||||||
|
|
||||||
|
|
||||||
## <a name="commit"></a> Commit Message Format
|
## <a name="commit"></a> Formato para el mensaje de los commits
|
||||||
|
|
||||||
*This specification is inspired and supersedes the [AngularJS commit message format][commit-message-format].*
|
*Esta especificación está inspirada y reemplaza el [Formato de mensaje de commits de AngularJS][commit-message-format].*
|
||||||
|
|
||||||
We have very precise rules over how our Git commit messages must be formatted.
|
Tenemos reglas muy precisas sobre cómo deben formatearse nuestros mensajes de los commits de Git.
|
||||||
This format leads to **easier to read commit history**.
|
Este formato permite tener **un historial de commits más facil de leer**.
|
||||||
|
|
||||||
Each commit message consists of a **header**, a **body**, and a **footer**.
|
Cada mensaje de un commit consta del **header**, el **body**, y el **footer**.
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
<header>
|
<header>
|
||||||
<BLANK LINE>
|
<LINEA VACIA>
|
||||||
<body>
|
<body>
|
||||||
<BLANK LINE>
|
<LINEA VACIA>
|
||||||
<footer>
|
<footer>
|
||||||
```
|
```
|
||||||
|
|
||||||
The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format.
|
El `header` es obligatorio y debe ajustarse al formato del [mensaje del header del commit](#commit-header).
|
||||||
|
|
||||||
The `body` is mandatory for all commits except for those of scope "docs".
|
El `body` es obligatorio para todos los commits excepto los que tenga scope "docs".
|
||||||
When the body is required it must be at least 20 characters long.
|
Cuando el body es requerido debe tener al menos 20 caracteres.
|
||||||
|
|
||||||
The `footer` is optional.
|
El `footer` es opcional.
|
||||||
|
|
||||||
Any line of the commit message cannot be longer than 100 characters.
|
Cualquier línea del mensaje del commit no puede tener más de 100 caracteres.
|
||||||
|
|
||||||
|
|
||||||
#### <a href="commit-header"></a>Commit Message Header
|
#### <a href="commit-header"></a>Mensaje del header del commit
|
||||||
|
|
||||||
```
|
```
|
||||||
<type>(<scope>): <short summary>
|
<tipo>(<alcance>): <resumen>
|
||||||
│ │ │
|
│ │ │
|
||||||
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
|
│ │ └─⫸ Resumen corto escrito en modo imperativo, tiempo presente. Sin mayúsculas. Sin punto final.
|
||||||
│ │
|
│ │
|
||||||
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
│ └─⫸ Alcance del commit: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||||
│ elements|forms|http|language-service|localize|platform-browser|
|
│ elements|forms|http|language-service|localize|platform-browser|
|
||||||
│ platform-browser-dynamic|platform-server|platform-webworker|
|
│ platform-browser-dynamic|platform-server|platform-webworker|
|
||||||
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
||||||
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
||||||
│
|
│
|
||||||
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
└─⫸ Tipo de commit: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||||
```
|
```
|
||||||
|
|
||||||
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
El `<tipo>` y `<resumen>` son obligatorios, el `(<alcance>)` es opcional.
|
||||||
|
|
||||||
|
|
||||||
##### Type
|
##### Tipo
|
||||||
|
|
||||||
Must be one of the following:
|
El tipo debe ser uno de los siguientes:
|
||||||
|
|
||||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
* **build**: cambios que afectan el sistema de compilación o dependencias externas (ejemplos de scopes: gulp, broccoli, npm)
|
||||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
|
* **ci**: cambios en nuestros archivos y scripts de configuración de CI (ejemplos de scopes: Circle, BrowserStack, SauceLabs)
|
||||||
* **docs**: Documentation only changes
|
* **docs**: cambios en la documentación
|
||||||
* **feat**: A new feature
|
* **feat**: una nueva funcionalidad
|
||||||
* **fix**: A bug fix
|
* **fix**: una solución de un bug
|
||||||
* **perf**: A code change that improves performance
|
* **perf**: un cambio de código que mejora el rendimiento.
|
||||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
* **refactor**: un cambio de código que no corrige ningún error ni agrega ninguna funcionalidad
|
||||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
* **style**: cambios que no afectan el significado del código (espacios en blanco, formato, falta de punto y coma, etc.)
|
||||||
* **test**: Adding missing tests or correcting existing tests
|
* **test**: se agregan pruebas faltantes o se corrigen pruebas existentes
|
||||||
|
|
||||||
|
|
||||||
##### Scope
|
##### Alcance
|
||||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
El alcance debe ser el nombre del paquete npm afectado (tal como lo percibe la persona que lee el registro de cambios generado a partir de los mensajes de commit).
|
||||||
|
|
||||||
The following is the list of supported scopes:
|
La siguiente es la lista de alcances permitidos:
|
||||||
|
|
||||||
* `animations`
|
* `animations`
|
||||||
* `bazel`
|
* `bazel`
|
||||||
@ -261,80 +261,73 @@ The following is the list of supported scopes:
|
|||||||
* `upgrade`
|
* `upgrade`
|
||||||
* `zone.js`
|
* `zone.js`
|
||||||
|
|
||||||
There are currently a few exceptions to the "use package name" rule:
|
Actualmente hay algunas excepciones a la regla "usar el nombre de paquete":
|
||||||
|
|
||||||
* `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
* `packaging`: usado para cambios que cambian el diseño de los paquetes de npm en todos nuestros paquetes. Ejemplos: cambios de la ruta públic, package.json cambios hechos a todos los paquetes, cambios a archivos o formatos d.ts, cambios a bundles, etc.
|
||||||
|
|
||||||
* `changelog`: used for updating the release notes in CHANGELOG.md
|
* `changelog`: utilizado para actualizar las notas de la versión en CHANGELOG.md
|
||||||
|
|
||||||
* `dev-infra`: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
* `dev-infra`: utilizado para cambios relacionados con dev-infra dentro de los directorios /scripts, /tools y /dev-infra
|
||||||
|
|
||||||
* `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
* `docs-infra`: utilizado para cambios relacionados con la documentación (angular.io) dentro del directorio /aio del repositorio
|
||||||
|
|
||||||
* `migrations`: used for changes to the `ng update` migrations.
|
* `migrations`: utilizado para los cambios en las migraciones `ng update`.
|
||||||
|
|
||||||
* `ngcc`: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
* `ngcc`: usado para los cambios del [Compilador de compatibilidad de Angular](./packages/compiler-cli/ngcc/README.md)
|
||||||
|
|
||||||
* `ve`: used for changes specific to ViewEngine (legacy compiler/renderer).
|
* `ve`: utilizado para cambios específicos de ViewEngine (legacy compiler/renderer).
|
||||||
|
|
||||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
|
* alcance vacío: útil para cambios de `style`, `test` y `refactor` que se realizan en todos los paquetes (ejemplo: `style: add missing semicolons`) y para cambios de la documentación que no están relacionados a un paquete en específico(ejemplo: `docs: corrige error gramatical en el tutorial`).
|
||||||
|
|
||||||
|
|
||||||
##### Summary
|
##### Resumen
|
||||||
|
|
||||||
Use the summary field to provide a succinct description of the change:
|
Usa el campo resumen para proporcionar una descripción breve del cambio:
|
||||||
|
|
||||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
* usa el modo imperativo, tiempo presente: "cambia" no "cambió" o "cambios"
|
||||||
* don't capitalize the first letter
|
* no debe de contener ninguna letra mayúscula
|
||||||
* no dot (.) at the end
|
* no debe de conter punto (.) al final
|
||||||
|
|
||||||
|
|
||||||
#### Commit Message Body
|
#### Mensaje del cuerpo del commit
|
||||||
|
|
||||||
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
|
Tal como en el resumen, usa el modo imperativo, tiempo presente: "cambia" no "cambió" o "cambios".
|
||||||
|
|
||||||
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
|
Explica la razón del cambio en el el mensaje del cuerpo del commit. Este mensaje de confirmación debe explicar _por qué_ está realizando el cambio.
|
||||||
You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
|
Puedes incluir una comparación del comportamiento anterior con el nuevo comportamiento para ilustrar el impacto del cambio.
|
||||||
|
|
||||||
|
|
||||||
#### Commit Message Footer
|
#### Mensaje del footer del commit
|
||||||
|
|
||||||
The footer can contain information about breaking changes and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
|
El footer puede contener información sobre cambios significativos y también es el lugar para hacer referencia a issues de GitHub, tickets de Jira y otros PRs que están relacionados con el commit.
|
||||||
|
|
||||||
```
|
```
|
||||||
BREAKING CHANGE: <breaking change summary>
|
CAMBIO SIGNIFICATIVO: <resumen del cambio significativo>
|
||||||
<BLANK LINE>
|
<LINEA VACIA>
|
||||||
<breaking change description + migration instructions>
|
<descripción del cambio significativo + instrucciones para la migración>
|
||||||
<BLANK LINE>
|
<LINEA VACIA>
|
||||||
<BLANK LINE>
|
<LINEA VACIA>
|
||||||
Fixes #<issue number>
|
Fix #<issue número>
|
||||||
```
|
```
|
||||||
|
|
||||||
Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
|
La sección de cambios significativos debería comenzar con la frase "CAMBIO SIGNIFICATIVO: " seguido de un resumen del cambio significativo, una línea en blanco y una descripción detallada del cambio significativo que también incluya instrucciones de migración.
|
||||||
|
|
||||||
|
|
||||||
### Revert commits
|
### Revirtiendo commits
|
||||||
|
|
||||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
|
Si el commit revierte un commit previo, el commit debería comenzar con `revert: `, seguido por el header del commit revertido.
|
||||||
|
|
||||||
The content of the commit message body should contain:
|
El contenido del mensaje del commit debería contener:
|
||||||
|
|
||||||
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
|
- Información sobre el SHA del commit que se revierte en el siguiente formato: `Esto revierte el commit <SHA>`,
|
||||||
- a clear description of the reason for reverting the commit message.
|
- Una descripción clara de la razón para revertir el mensaje del _commit_.
|
||||||
|
|
||||||
|
|
||||||
## <a name="cla"></a> Signing the CLA
|
## <a name="cla"></a> Firma del Acuerdo de Licencia de Colaborador (CLA)
|
||||||
|
|
||||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
Por favor firma nuestro Acuerdo de Licencia de Colaborador (CLA) cuando creas tu primer pull request. Para que cualquier cambio de código sea aceptado, el Acuerdo de Licencia de Colaborador (CLA) debe ser firmado. Es un proceso rápido con nuestro CLA assistant que está integrado con nuestro CI.
|
||||||
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
|
||||||
|
|
||||||
* For individuals, we have a [simple click-through form][individual-cla].
|
Los siguientes documentos pueden ayudarte a resolver problemas con cuentas de GitHub y múltiples direcciones de correo electrónico:
|
||||||
* For corporations, we'll need you to
|
|
||||||
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
|
|
||||||
|
|
||||||
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
|
|
||||||
|
|
||||||
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
|
|
||||||
|
|
||||||
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
|
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
|
||||||
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
|
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
|
||||||
|
26
README.en.md
Normal file
26
README.en.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
||||||
|
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
[](https://www.npmjs.com/@angular/core)
|
||||||
|
|
||||||
|
|
||||||
|
# Angular
|
||||||
|
|
||||||
|
Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
[Get started in 5 minutes][quickstart].
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
[Learn about the latest improvements][changelog].
|
||||||
|
|
||||||
|
## Want to help?
|
||||||
|
|
||||||
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||||
|
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
|
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
|
[quickstart]: https://angular.io/start
|
||||||
|
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
||||||
|
[ng]: https://angular.io
|
40
README.md
40
README.md
@ -1,25 +1,25 @@
|
|||||||
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
# Angular en español
|
||||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[](https://www.npmjs.com/@angular/core)
|
|
||||||
|
|
||||||
|
Angular es una plataforma de desarrollo para construir aplicaciones web y móviles que usa
|
||||||
# Angular
|
TypeScript/JavaScript y otros lenguajes de programación.
|
||||||
|
|
||||||
Angular es una plataforma de desarrollo para construir aplicaciones web y móviles que usa TypeScript/JavaScript y otros lenguajes de programación.
|
|
||||||
|
|
||||||
## Guía rápida
|
|
||||||
|
|
||||||
[Comienza a usarlo en 5 minutos][quickstart].
|
|
||||||
|
|
||||||
## Registro de cambios (Changelog)
|
|
||||||
|
|
||||||
[Últimas mejoras realizadas][changelog].
|
|
||||||
|
|
||||||
## ¿Quieres ayudar?
|
## ¿Quieres ayudar?
|
||||||
|
|
||||||
¿Quieres encontrar fallos, colaborar con código, o mejorar la documentación? ¡Excelente! Lee nuestras
|
### Documentación en español
|
||||||
pautas para [colaborar][contributing] y luego revisa algunos de nuestras incidencias (issues) en [ayuda comunitaria](https://github.com/angular-hispano/angular/labels/ayuda%20comunitaria).
|
|
||||||
|
|
||||||
[contributing]: https://github.com/angular-hispano/angular/blob/master/CONTRIBUTING.md
|
¿Quieres mejorar la documentación? ¡Excelente! Lee nuestras pautas para
|
||||||
[quickstart]: https://docs.angular.lat/start
|
[colaborar](CONTRIBUTING.md) y luego revisa algunos de nuestras
|
||||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
[issues](https://github.com/angular-hispano/angular/issues).
|
||||||
|
|
||||||
|
### El framework
|
||||||
|
|
||||||
|
La colaboración para corregir errores y agregar funciones en el framework debe realizarse en inglés a través
|
||||||
|
del repositorio [angular/angular](https://github.com/angular/angular) upstream.
|
||||||
|
|
||||||
|
## Guía rápida
|
||||||
|
|
||||||
|
[Comienza a usarlo en 5 minutos](https://docs.angular.lat/start).
|
||||||
|
|
||||||
|
## Registro de cambios (Changelog)
|
||||||
|
|
||||||
|
[Últimas mejoras realizadas](CHANGELOG.md).
|
||||||
|
167
aio/README.md
167
aio/README.md
@ -1,140 +1,131 @@
|
|||||||
# Angular documentation project (https://angular.io)
|
# Proyecto de documentación Angular (https://docs.angular.lat)
|
||||||
|
|
||||||
Everything in this folder is part of the documentation project. This includes
|
Todo en esta carpeta es parte del proyecto de documentación. Esto incluye:
|
||||||
|
|
||||||
* the web site for displaying the documentation
|
* El sitio web para mostrar la documentación
|
||||||
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
* La configuración de dgeni para convertir los archivos de origen a archivos renderizados que se pueden vizualizar en el sitio web.
|
||||||
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
* Las herramientas para establecer ejemplos para el desarrollo; y generar archivos en tiempo real y archivos zip desde los ejemplos.
|
||||||
|
|
||||||
## Developer tasks
|
## Tareas de desarrollador
|
||||||
|
|
||||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
Nosotros usamos [Yarn](https://yarnpkg.com) para gestionar las dependencias y crear tareas de compilación.
|
||||||
You should run all these tasks from the `angular/aio` folder.
|
Debes ejecutar todas estas tareas desde la carpeta `angular/aio`.
|
||||||
Here are the most important tasks you might need to use:
|
Aquí están las tareas más importantes que podrías necesitar usar:
|
||||||
|
|
||||||
* `yarn` - install all the dependencies.
|
* `yarn` - instalar todas las dependencias.
|
||||||
* `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
|
* `yarn setup` - instalar todas las dependencias, boilerplate, stackblitz, zips y ejecuta dgeni en los documentos.
|
||||||
* `yarn setup-local` - same as `setup`, but build the Angular packages from the source code and use these locally built versions (instead of the ones fetched from npm) for aio and docs examples boilerplate.
|
* `yarn setup-local` - igual que `setup`, pero crea los paquetes de Angular a partir del código y usa estas versiones construidas localmente (en lugar de las recuperadas desde npm) para aio y ejemplos de documentos boilerplate.
|
||||||
|
|
||||||
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
|
* `yarn build` - crear una compilación de producción de la aplicación (después de instalar dependencias, boilerplate, etc).
|
||||||
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
|
* `yarn build-local` - igual que `build`, pero usa `setup-local` en lugar de `setup`.
|
||||||
* `yarn build-local-with-viewengine` - same as `build-local`, but in addition also turns on `ViewEngine` (pre-Ivy) mode in aio.
|
* `yarn build-local-with-viewengine` - igual que `build-local`, pero además también enciende el modo `ViewEngine` (pre-Ivy) en aio.
|
||||||
(Note: To turn on `ViewEngine` mode in docs examples, see `yarn boilerplate:add:viewengine` below.)
|
(Nota: Encender el modo `ViewEngine` en ejemplos de documentos, ver `yarn boilerplate:add:viewengine` abajo.)
|
||||||
|
|
||||||
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
|
* `yarn start` - ejecutar un servidor web de desarrollo que observa los archivos; luego crea el doc-viewer y vuelve a cargar la página, según sea necesario.
|
||||||
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
|
* `yarn serve-and-sync` - ejecutar ambos, el `docs-watch` y `start` en la misma consola.
|
||||||
* `yarn lint` - check that the doc-viewer code follows our style rules.
|
* `yarn lint` - comprobar que el código del doc-viewer sigue nuestras reglas de estilo.
|
||||||
* `yarn test` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
|
* `yarn test` - observar todos los archivos de origen, para el doc-viewer, y ejecuta todas las pruebas unitarias cuando haya algún cambio.
|
||||||
* `yarn test --watch=false` - run all the unit tests once.
|
* `yarn test --watch=false` -ejecutar todas las pruebas unitarias una sola vez.
|
||||||
* `yarn e2e` - run all the e2e tests for the doc-viewer.
|
* `yarn e2e` - ejecutar todas las pruebas de e2e para el doc-viewer.
|
||||||
|
|
||||||
* `yarn docs` - generate all the docs from the source files.
|
* `yarn docs` - generar toda la documentación desde los archivos fuente.
|
||||||
* `yarn docs-watch` - watch the Angular source and the docs files and run a short-circuited doc-gen for the docs that changed.
|
* `yarn docs-watch` - observar el código Angular, los archivos de documentación y ejecutar un 'doc-gen' en corto circuito para los documentos que fueron cambiados.
|
||||||
* `yarn docs-lint` - check that the doc gen code follows our style rules.
|
* `yarn docs-lint` - comprobar que el código del documento generado sigue nuestras reglas de estilo.
|
||||||
* `yarn docs-test` - run the unit tests for the doc generation code.
|
* `yarn docs-test` - ejecutar las pruebas unitarias para el código de generación de doc.
|
||||||
|
|
||||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
* `yarn boilerplate:add` - generar todo el código boilerplate para los ejemplos, para que puedan ejecutarse localmente.
|
||||||
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
|
* `yarn boilerplate:add:viewengine` - igual que `boilerplate:add` pero también enciende el modo `ViewEngine` (pre-Ivy).
|
||||||
|
|
||||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
* `yarn boilerplate:remove` - eliminar todo el código boilerplate que fue añadido a través`yarn boilerplate:add`.
|
||||||
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
* `yarn generate-stackblitz` - generar los archivos stackblitz que están usados por la etiqueta `live-example` en documentos.
|
||||||
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
* `yarn generate-zips` - generar los archivos zip desde los ejemplos. Zip está disponible a través de la etiqueta `live-example` en los documentos.
|
||||||
|
|
||||||
* `yarn example-e2e` - run all e2e tests for examples. Available options:
|
* `yarn example-e2e` - ejecutar todas las pruebas e2e para ejemplos. Opciones disponibles:
|
||||||
- `--setup`: generate boilerplate, force webdriver update & other setup, then run tests.
|
- `--setup`: generar boilerplate, forzar la actualización del controlador web y otras configuraciones, luego ejecutar las pruebas.
|
||||||
- `--local`: run e2e tests with the local version of Angular contained in the "dist" folder.
|
- `--local`: ejecutar pruebas e2e con la versión local de Angular contenida en la carpeta "dist".
|
||||||
_Requires `--setup` in order to take effect._
|
_Requiere `--setup` para que surta efecto._
|
||||||
- `--viewengine`: run e2e tests in `ViewEngine` (pre-Ivy) mode.
|
- `--viewengine`: ejecutar pruebas e2e en modo `ViewEngine` (pre-Ivy).
|
||||||
- `--filter=foo`: limit e2e tests to those containing the word "foo".
|
- `--filter=foo`: limitar pruebas e2e a las que contienen la palabra "foo".
|
||||||
|
|
||||||
> **Note for Windows users**
|
> **Nota para usuarios Windows**
|
||||||
>
|
>
|
||||||
> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](./tools/examples/README.md#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
|
> Configurar los ejemplos implica crear algunos [enlaces simbólicos](https://es.wikipedia.org/wiki/Enlace_simb%C3%B3lico) (ver [Aquí](./tools/examples/README.md#symlinked-node_modules) para más detalles). En Windows, esto requiere tener [Habilitado el Modo de desarrollador ](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (compatible con Windows 10 o más reciente) o ejecutar los comandos de configuración cómo administrador.
|
||||||
>
|
>
|
||||||
> The affected commands are:
|
> Los comandos afectados son:
|
||||||
> - `yarn setup` / `yarn setup-*`
|
> - `yarn setup` / `yarn setup-*`
|
||||||
> - `yarn build` / `yarn build-*`
|
> - `yarn build` / `yarn build-*`
|
||||||
> - `yarn boilerplate:add`
|
> - `yarn boilerplate:add`
|
||||||
> - `yarn example-e2e --setup`
|
> - `yarn example-e2e --setup`
|
||||||
|
|
||||||
## Using ServiceWorker locally
|
## Usando ServiceWorker localmente
|
||||||
|
|
||||||
Running `yarn start` (even when explicitly targeting production mode) does not set up the
|
Ejecutando `yarn start` (incluso cuando se apunta explícitamente al modo de producción) no configura el
|
||||||
ServiceWorker. If you want to test the ServiceWorker locally, you can use `yarn build` and then
|
ServiceWorker. Si quieres probar el ServiceWorker localmente, puedes usar `yarn build` y luego
|
||||||
serve the files in `dist/` with `yarn http-server dist -p 4200`.
|
ejecutar los archivos en `dist/` con `yarn http-server dist -p 4200`.
|
||||||
|
|
||||||
|
|
||||||
## Guide to authoring
|
## Guía de autoría
|
||||||
|
Existen dos tipos de contenido en la documentación:
|
||||||
|
|
||||||
There are two types of content in the documentation:
|
* **Documentación de API**: descripciones de los módulos, clases, interfaces, decoradores, etc que son parte de la plataforma Angular.
|
||||||
|
La documentacion de API está generada directamente desde el código fuente.
|
||||||
|
El código fuente está contenido en archivos TypeScript , almacenados en la carpeta `angular/packages`.
|
||||||
|
Cada elemento de la API puede tener un comentario anterior, el cual contiene etiquetas y contenido de estilos JSDoc.
|
||||||
|
El contenido está escrito en markdown.
|
||||||
|
|
||||||
* **API docs**: descriptions of the modules, classes, interfaces, decorators, etc that make up the Angular platform.
|
* **Otro contenido**: guias, tutoriales, y otro material de marketing.
|
||||||
API docs are generated directly from the source code.
|
Todos los demás contenidos se escriben utilizando markdown en archivos de texto, ubicados en la carpeta `angular/aio/content`.
|
||||||
The source code is contained in TypeScript files, located in the `angular/packages` folder.
|
Más específicamente, hay subcarpetas que contienen tipos particulares de contenido: guías, tutoriales y marketing.
|
||||||
Each API item may have a preceding comment, which contains JSDoc style tags and content.
|
|
||||||
The content is written in markdown.
|
|
||||||
|
|
||||||
* **Other content**: guides, tutorials, and other marketing material.
|
* **Ejempos de código**: los ejemplos de código deben ser comprobables para garantizar su precisión.
|
||||||
All other content is written using markdown in text files, located in the `angular/aio/content` folder.
|
Además, nuestros ejemplos tienen un aspecto específico y permiten al usuario copiar el código fuente. Para mayor
|
||||||
More specifically, there are sub-folders that contain particular types of content: guides, tutorial and marketing.
|
ejemplos se representan en una interfaz con pestañas (e.g. template, HTML, y TypeScript en pestañas separadas). Adicionalmente, algunos son ejemplos en acción, que proporcionan enlaces donde se puede editar el código, ejecutar, y/o descargar. Para obtener más detalles sobre cómo trabajar con ejemplos de código, lea los [fragmentos de código](https://docs.angular.lat/guide/docs-style-guide#code-snippets), [código fuente de marcado ](https://docs.angular.lat/guide/docs-style-guide#source-code-markup), y [ ejemplos en acción ](https://docs.angular.lat/guide/docs-style-guide#live-examples) paginas de los [ autores de guías de estilo ](https://docs.angular.lat/guide/docs-style-guide).
|
||||||
|
|
||||||
* **Code examples**: code examples need to be testable to ensure their accuracy.
|
Usamos la herramienta [dgeni](https://github.com/angular/dgeni) para convertir estos archivos en docs que se pueden ver en el doc-viewer.
|
||||||
Also, our examples have a specific look and feel and allow the user to copy the source code. For larger
|
Las [guías de estilo para Autores](https://docs.angular.lat/guide/docs-style-guide) prescriben pautas para
|
||||||
examples they are rendered in a tabbed interface (e.g. template, HTML, and TypeScript on separate
|
escribir páginas de guía, explica cómo usar la documentación de clases y componentes, y cómo marcar el código fuente para producir fragmentos de código.
|
||||||
tabs). Additionally, some are live examples, which provide links where the code can be edited, executed, and/or downloaded. For details on working with code examples, please read the [Code snippets](https://angular.io/guide/docs-style-guide#code-snippets), [Source code markup](https://angular.io/guide/docs-style-guide#source-code-markup), and [Live examples](https://angular.io/guide/docs-style-guide#live-examples) pages of the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
|
|
||||||
|
|
||||||
We use the [dgeni](https://github.com/angular/dgeni) tool to convert these files into docs that can be viewed in the doc-viewer.
|
### Generando documentos completos
|
||||||
|
|
||||||
The [Authors Style Guide](https://angular.io/guide/docs-style-guide) prescribes guidelines for
|
La principal tarea para generar los documentos es `yarn docs`. Esto procesará todos los archivos fuente (API y otros), extrayendo la documentación y generando archivos JSON que pueden ser consumidos por el doc-viewer.
|
||||||
writing guide pages, explains how to use the documentation classes and components, and how to markup sample source code to produce code snippets.
|
|
||||||
|
|
||||||
### Generating the complete docs
|
### Generación parcial de doc para editores
|
||||||
|
|
||||||
The main task for generating the docs is `yarn docs`. This will process all the source files (API and other),
|
La generación completa de documentos puede llevar hasta un minuto. Eso es demasiado lento para la creación y edición eficiente de documentos.
|
||||||
extracting the documentation and generating JSON files that can be consumed by the doc-viewer.
|
|
||||||
|
|
||||||
### Partial doc generation for editors
|
Puedes ealizar pequeños cambios en un editor inteligente que muestre un markdown con formato:
|
||||||
|
>En VS Code, _Cmd-K, V_ abre la vista previa de markdown en el panel lateral; _Cmd-B_ alterna la barra izquierda
|
||||||
|
|
||||||
Full doc generation can take up to one minute. That's too slow for efficient document creation and editing.
|
Puedes también mirar los cambios que se muestran correctamente en el doc-viewer
|
||||||
|
con un tiempo de ciclo rápido de edición / visualización.
|
||||||
|
|
||||||
You can make small changes in a smart editor that displays formatted markdown:
|
Para este propósito, usa la tarea `yarn docs-watch`, que observa los cambios en los archivos de origen y solo vuelve a procesar los archivos necesarios para generar los documentos relacionados con el archivo que ha cambiado.
|
||||||
>In VS Code, _Cmd-K, V_ opens markdown preview in side pane; _Cmd-B_ toggles left sidebar
|
Dado que esta tarea tiene accesos directos, es mucho más rápido (a menudo menos de 1 segundo) pero no producirá contenido de fidelidad completa. Por ejemplo, los enlaces a otros documentoss y ejemplos de código pueden no mostrarse correctamente. Esto se nota especialmente en los enlaces a otros documentos y en los ejemplos incrustados, que no siempre se representan correctamente.
|
||||||
|
|
||||||
You also want to see those changes displayed properly in the doc viewer
|
La configuración general es la siguiente:
|
||||||
with a quick, edit/view cycle time.
|
|
||||||
|
|
||||||
For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only
|
* Abrir una terminal, estar seguro que las dependencias están instaladas; ejecutar una generación inicial del doc; luego iniciar el doc-viewer:
|
||||||
re-processes the files necessary to generate the docs that are related to the file that has changed.
|
|
||||||
Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full
|
|
||||||
fidelity content. For example, links to other docs and code examples may not render correctly. This is
|
|
||||||
most particularly noticed in links to other docs and in the embedded examples, which may not always render
|
|
||||||
correctly.
|
|
||||||
|
|
||||||
The general setup is as follows:
|
|
||||||
|
|
||||||
* Open a terminal, ensure the dependencies are installed; run an initial doc generation; then start the doc-viewer:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn setup
|
yarn setup
|
||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
* Open a second terminal and start watching the docs
|
* Abrir una segunda terminal e iniciar el observador de documentos.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn docs-watch
|
yarn docs-watch
|
||||||
```
|
```
|
||||||
|
|
||||||
>Alternatively, try the consolidated `serve-and-sync` command that builds, watches and serves in the same terminal window
|
>Alternativamente, prueba el comando fusionado `serve-and-sync` que crea, observa y ejecuta en la misma ventana de la terminal
|
||||||
```bash
|
```bash
|
||||||
yarn serve-and-sync
|
yarn serve-and-sync
|
||||||
```
|
```
|
||||||
|
|
||||||
* Open a browser at https://localhost:4200/ and navigate to the document on which you want to work.
|
* Abre un navegador con la siguiente dirección https://localhost:4200/ y navega hasta el documento en el que quieras trabajar.
|
||||||
You can automatically open the browser by using `yarn start -o` in the first terminal.
|
Puedes automáticamente abrir el navegador utilizando `yarn start -o` en la primera terminal.
|
||||||
|
|
||||||
* Make changes to the page's associated doc or example files. Every time a file is saved, the doc will
|
* Realiza cambios en la página de documentación asociada o en los archivos de ejemplo. Cada vez que un archivo es guardado, la documentación se regenerará, la aplicación se reconstruirá y la página se volverá a cargar.
|
||||||
be regenerated, the app will rebuild and the page will reload.
|
|
||||||
|
|
||||||
* If you get a build error complaining about examples or any other odd behavior, be sure to consult
|
*Si recibes un error de compilación acerca de los ejemplos o cualquier otro error, asegúrate de consultar las
|
||||||
the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
|
[guías de estilo para Autores](https://angular.io/guide/docs-style-guide) para más información.
|
||||||
|
1
aio/content/examples/.gitignore
vendored
1
aio/content/examples/.gitignore
vendored
@ -18,6 +18,7 @@
|
|||||||
**/src/karma.conf.js
|
**/src/karma.conf.js
|
||||||
**/.angular-cli.json
|
**/.angular-cli.json
|
||||||
**/.editorconfig
|
**/.editorconfig
|
||||||
|
**/.gitignore
|
||||||
**/angular.json
|
**/angular.json
|
||||||
**/tsconfig.json
|
**/tsconfig.json
|
||||||
**/bs-config.e2e.json
|
**/bs-config.e2e.json
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Accessibility example e2e tests', () => {
|
describe('Accessibility example e2e tests', () => {
|
||||||
@ -8,11 +6,11 @@ describe('Accessibility example e2e tests', () => {
|
|||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display Accessibility Example', function () {
|
it('should display Accessibility Example', () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual('Accessibility Example');
|
expect(element(by.css('h1')).getText()).toEqual('Accessibility Example');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should take a number and change progressbar width', function () {
|
it('should take a number and change progressbar width', () => {
|
||||||
element(by.css('input')).sendKeys('16');
|
element(by.css('input')).sendKeys('16');
|
||||||
expect(element(by.css('input')).getAttribute('value')).toEqual('016');
|
expect(element(by.css('input')).getAttribute('value')).toEqual('016');
|
||||||
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');
|
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// tslint:disable: no-host-metadata-property
|
||||||
// #docregion progressbar-component
|
// #docregion progressbar-component
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('AngularJS to Angular Quick Reference Tests', function () {
|
describe('AngularJS to Angular Quick Reference Tests', () => {
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display no poster images after bootstrap', function () {
|
it('should display no poster images after bootstrap', () => {
|
||||||
testImagesAreDisplayed(false);
|
testImagesAreDisplayed(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display proper movie data', function () {
|
it('should display proper movie data', () => {
|
||||||
// We check only a few samples
|
// We check only a few samples
|
||||||
let expectedSamples: any[] = [
|
const expectedSamples: any[] = [
|
||||||
{row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true},
|
{row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true},
|
||||||
{row: 0, column: 2, value: 'Celeritas'},
|
{row: 0, column: 2, value: 'Celeritas'},
|
||||||
{row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format
|
{row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format
|
||||||
@ -25,18 +23,17 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Go through the samples
|
// Go through the samples
|
||||||
let movieRows = getMovieRows();
|
const movieRows = getMovieRows();
|
||||||
for (let i = 0; i < expectedSamples.length; i++) {
|
for (const sample of expectedSamples) {
|
||||||
let sample = expectedSamples[i];
|
const tableCell = movieRows.get(sample.row)
|
||||||
let tableCell = movieRows.get(sample.row)
|
|
||||||
.all(by.tagName('td')).get(sample.column);
|
.all(by.tagName('td')).get(sample.column);
|
||||||
// Check the cell or its nested element
|
// Check the cell or its nested element
|
||||||
let elementToCheck = sample.element
|
const elementToCheck = sample.element
|
||||||
? tableCell.element(by.tagName(sample.element))
|
? tableCell.element(by.tagName(sample.element))
|
||||||
: tableCell;
|
: tableCell;
|
||||||
|
|
||||||
// Check element attribute or text
|
// Check element attribute or text
|
||||||
let valueToCheck = sample.attr
|
const valueToCheck = sample.attr
|
||||||
? elementToCheck.getAttribute(sample.attr)
|
? elementToCheck.getAttribute(sample.attr)
|
||||||
: elementToCheck.getText();
|
: elementToCheck.getText();
|
||||||
|
|
||||||
@ -51,42 +48,42 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display images after Show Poster', function () {
|
it('should display images after Show Poster', () => {
|
||||||
testPosterButtonClick('Show Poster', true);
|
testPosterButtonClick('Show Poster', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide images after Hide Poster', function () {
|
it('should hide images after Hide Poster', () => {
|
||||||
testPosterButtonClick('Hide Poster', false);
|
testPosterButtonClick('Hide Poster', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display no movie when no favorite hero is specified', function () {
|
it('should display no movie when no favorite hero is specified', () => {
|
||||||
testFavoriteHero(null, 'Please enter your favorite hero.');
|
testFavoriteHero(null, 'Please enter your favorite hero.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display no movie for Magneta', function () {
|
it('should display no movie for Magneta', () => {
|
||||||
testFavoriteHero('Magneta', 'No movie, sorry!');
|
testFavoriteHero('Magneta', 'No movie, sorry!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a movie for Dr Nice', function () {
|
it('should display a movie for Dr Nice', () => {
|
||||||
testFavoriteHero('Dr Nice', 'Excellent choice!');
|
testFavoriteHero('Dr Nice', 'Excellent choice!');
|
||||||
});
|
});
|
||||||
|
|
||||||
function testImagesAreDisplayed(isDisplayed: boolean) {
|
function testImagesAreDisplayed(isDisplayed: boolean) {
|
||||||
let expectedMovieCount = 3;
|
const expectedMovieCount = 3;
|
||||||
|
|
||||||
let movieRows = getMovieRows();
|
const movieRows = getMovieRows();
|
||||||
expect(movieRows.count()).toBe(expectedMovieCount);
|
expect(movieRows.count()).toBe(expectedMovieCount);
|
||||||
for (let i = 0; i < expectedMovieCount; i++) {
|
for (let i = 0; i < expectedMovieCount; i++) {
|
||||||
let movieImage = movieRows.get(i).element(by.css('td > img'));
|
const movieImage = movieRows.get(i).element(by.css('td > img'));
|
||||||
expect(movieImage.isDisplayed()).toBe(isDisplayed);
|
expect(movieImage.isDisplayed()).toBe(isDisplayed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
|
function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
|
||||||
let posterButton = element(by.css('app-movie-list tr > th > button'));
|
const posterButton = element(by.css('app-movie-list tr > th > button'));
|
||||||
expect(posterButton.getText()).toBe(expectedButtonText);
|
expect(posterButton.getText()).toBe(expectedButtonText);
|
||||||
|
|
||||||
posterButton.click().then(function () {
|
posterButton.click().then(() => {
|
||||||
testImagesAreDisplayed(isDisplayed);
|
testImagesAreDisplayed(isDisplayed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -96,12 +93,12 @@ describe('AngularJS to Angular Quick Reference Tests', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testFavoriteHero(heroName: string, expectedLabel: string) {
|
function testFavoriteHero(heroName: string, expectedLabel: string) {
|
||||||
let movieListComp = element(by.tagName('app-movie-list'));
|
const movieListComp = element(by.tagName('app-movie-list'));
|
||||||
let heroInput = movieListComp.element(by.tagName('input'));
|
const heroInput = movieListComp.element(by.tagName('input'));
|
||||||
let favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
|
const favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
|
||||||
let resultLabel = movieListComp.element(by.css('span > p'));
|
const resultLabel = movieListComp.element(by.css('span > p'));
|
||||||
|
|
||||||
heroInput.clear().then(function () {
|
heroInput.clear().then(() => {
|
||||||
heroInput.sendKeys(heroName || '');
|
heroInput.sendKeys(heroName || '');
|
||||||
expect(resultLabel.getText()).toBe(expectedLabel);
|
expect(resultLabel.getText()).toBe(expectedLabel);
|
||||||
if (heroName) {
|
if (heroName) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
|
||||||
import { MovieListComponent } from './movie-list.component';
|
import { MovieListComponent } from './movie-list.component';
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [ BrowserModule ],
|
imports: [ BrowserModule ],
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { MovieListComponent } from './movie-list.component';
|
import { MovieListComponent } from './movie-list.component';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, ExpectedConditions as EC } from 'protractor';
|
import { browser, ExpectedConditions as EC } from 'protractor';
|
||||||
import { logging } from 'selenium-webdriver';
|
import { logging } from 'selenium-webdriver';
|
||||||
import * as openClose from './open-close.po';
|
import * as openClose from './open-close.po';
|
||||||
|
@ -34,7 +34,7 @@ export class AppComponent {
|
|||||||
|
|
||||||
// #docregion prepare-router-outlet
|
// #docregion prepare-router-outlet
|
||||||
prepareRoute(outlet: RouterOutlet) {
|
prepareRoute(outlet: RouterOutlet) {
|
||||||
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
|
return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion prepare-router-outlet
|
// #enddocregion prepare-router-outlet
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
// tslint:disable: variable-name
|
||||||
// #docplaster
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
import { Component, HostBinding, OnInit } from '@angular/core';
|
import { Component, HostBinding, OnInit } from '@angular/core';
|
||||||
import { trigger, transition, animate, style, query, stagger } from '@angular/animations';
|
import { trigger, transition, animate, style, query, stagger } from '@angular/animations';
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
@ -52,13 +54,11 @@ export class HeroListPageComponent implements OnInit {
|
|||||||
@HostBinding('@pageAnimations')
|
@HostBinding('@pageAnimations')
|
||||||
public animatePage = true;
|
public animatePage = true;
|
||||||
|
|
||||||
_heroes = [];
|
|
||||||
// #docregion filter-animations
|
// #docregion filter-animations
|
||||||
heroTotal = -1;
|
heroTotal = -1;
|
||||||
// #enddocregion filter-animations
|
// #enddocregion filter-animations
|
||||||
get heroes() {
|
get heroes() { return this._heroes; }
|
||||||
return this._heroes;
|
private _heroes = [];
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._heroes = HEROES;
|
this._heroes = HEROES;
|
||||||
|
@ -8,8 +8,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
|
|||||||
// #docregion trigger, trigger-wildcard1, trigger-transition
|
// #docregion trigger, trigger-wildcard1, trigger-transition
|
||||||
animations: [
|
animations: [
|
||||||
trigger('openClose', [
|
trigger('openClose', [
|
||||||
// #enddocregion events1
|
// #docregion state1
|
||||||
// #docregion state1, events1
|
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion events1
|
// #enddocregion events1
|
||||||
state('open', style({
|
state('open', style({
|
||||||
@ -34,8 +33,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
|
|||||||
transition('closed => open', [
|
transition('closed => open', [
|
||||||
animate('0.5s')
|
animate('0.5s')
|
||||||
]),
|
]),
|
||||||
// #enddocregion trigger, component
|
// #enddocregion transition2, trigger, component
|
||||||
// #enddocregion transition2
|
|
||||||
// #docregion trigger-wildcard1
|
// #docregion trigger-wildcard1
|
||||||
transition('* => closed', [
|
transition('* => closed', [
|
||||||
animate('1s')
|
animate('1s')
|
||||||
@ -70,7 +68,9 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
|
|||||||
})
|
})
|
||||||
// #docregion events
|
// #docregion events
|
||||||
export class OpenCloseComponent {
|
export class OpenCloseComponent {
|
||||||
// #enddocregion events1, events
|
// #enddocregion events1, events, component
|
||||||
|
@Input() logging = false;
|
||||||
|
// #docregion component
|
||||||
isOpen = true;
|
isOpen = true;
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
@ -78,9 +78,8 @@ export class OpenCloseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion component
|
// #enddocregion component
|
||||||
@Input() logging = false;
|
|
||||||
// #docregion events1, events
|
// #docregion events1, events
|
||||||
onAnimationEvent ( event: AnimationEvent ) {
|
onAnimationEvent( event: AnimationEvent ) {
|
||||||
// #enddocregion events1, events
|
// #enddocregion events1, events
|
||||||
if (!this.logging) {
|
if (!this.logging) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { protractor, browser, element, by, ElementFinder } from 'protractor';
|
import { protractor, browser, element, by, ElementFinder } from 'protractor';
|
||||||
|
|
||||||
const nameSuffix = 'X';
|
const nameSuffix = 'X';
|
||||||
@ -21,7 +19,7 @@ describe('Architecture', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it(`has h2 '${expectedH2}'`, () => {
|
it(`has h2 '${expectedH2}'`, () => {
|
||||||
let h2 = element.all(by.css('h2')).map((elt: any) => elt.getText());
|
const h2 = element.all(by.css('h2')).map((elt: any) => elt.getText());
|
||||||
expect(h2).toEqual(expectedH2);
|
expect(h2).toEqual(expectedH2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -34,42 +32,42 @@ function heroTests() {
|
|||||||
const targetHero: Hero = { id: 2, name: 'Dr Nice' };
|
const targetHero: Hero = { id: 2, name: 'Dr Nice' };
|
||||||
|
|
||||||
it('has the right number of heroes', () => {
|
it('has the right number of heroes', () => {
|
||||||
let page = getPageElts();
|
const page = getPageElts();
|
||||||
expect(page.heroes.count()).toEqual(3);
|
expect(page.heroes.count()).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has no hero details initially', function () {
|
it('has no hero details initially', () => {
|
||||||
let page = getPageElts();
|
const page = getPageElts();
|
||||||
expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
|
expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows selected hero details', async () => {
|
it('shows selected hero details', async () => {
|
||||||
await element(by.cssContainingText('li', targetHero.name)).click();
|
await element(by.cssContainingText('li', targetHero.name)).click();
|
||||||
let page = getPageElts();
|
const page = getPageElts();
|
||||||
let hero = await heroFromDetail(page.heroDetail);
|
const hero = await heroFromDetail(page.heroDetail);
|
||||||
expect(hero.id).toEqual(targetHero.id);
|
expect(hero.id).toEqual(targetHero.id);
|
||||||
expect(hero.name).toEqual(targetHero.name);
|
expect(hero.name).toEqual(targetHero.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`shows updated hero name in details`, async () => {
|
it(`shows updated hero name in details`, async () => {
|
||||||
let input = element.all(by.css('input')).first();
|
const input = element.all(by.css('input')).first();
|
||||||
input.sendKeys(nameSuffix);
|
input.sendKeys(nameSuffix);
|
||||||
let page = getPageElts();
|
const page = getPageElts();
|
||||||
let hero = await heroFromDetail(page.heroDetail);
|
const hero = await heroFromDetail(page.heroDetail);
|
||||||
let newName = targetHero.name + nameSuffix;
|
const newName = targetHero.name + nameSuffix;
|
||||||
expect(hero.id).toEqual(targetHero.id);
|
expect(hero.id).toEqual(targetHero.id);
|
||||||
expect(hero.name).toEqual(newName);
|
expect(hero.name).toEqual(newName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function salesTaxTests() {
|
function salesTaxTests() {
|
||||||
it('has no sales tax initially', function () {
|
it('has no sales tax initially', () => {
|
||||||
let page = getPageElts();
|
const page = getPageElts();
|
||||||
expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info');
|
expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows sales tax', async function () {
|
it('shows sales tax', async () => {
|
||||||
let page = getPageElts();
|
const page = getPageElts();
|
||||||
page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER);
|
page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER);
|
||||||
expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00');
|
expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00');
|
||||||
});
|
});
|
||||||
@ -88,13 +86,11 @@ function getPageElts() {
|
|||||||
|
|
||||||
async function heroFromDetail(detail: ElementFinder): Promise<Hero> {
|
async function heroFromDetail(detail: ElementFinder): Promise<Hero> {
|
||||||
// Get hero id from the first <div>
|
// Get hero id from the first <div>
|
||||||
// let _id = await detail.all(by.css('div')).first().getText();
|
const id = await detail.all(by.css('div')).first().getText();
|
||||||
let _id = await detail.all(by.css('div')).first().getText();
|
|
||||||
// Get name from the h2
|
// Get name from the h2
|
||||||
// let _name = await detail.element(by.css('h4')).getText();
|
const name = await detail.element(by.css('h4')).getText();
|
||||||
let _name = await detail.element(by.css('h4')).getText();
|
|
||||||
return {
|
return {
|
||||||
id: +_id.substr(_id.indexOf(' ') + 1),
|
id: +id.substr(id.indexOf(' ') + 1),
|
||||||
name: _name.substr(0, _name.lastIndexOf(' '))
|
name: name.substr(0, name.lastIndexOf(' ')),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
// #docregion imports
|
// #docregion imports
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
import { HeroDetailComponent } from './hero-detail.component';
|
import { HeroDetailComponent } from './hero-detail.component';
|
||||||
import { HeroListComponent } from './hero-list.component';
|
import { HeroListComponent } from './hero-list.component';
|
||||||
import { SalesTaxComponent } from './sales-tax.component';
|
import { SalesTaxComponent } from './sales-tax.component';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
import { BackendService } from './backend.service';
|
import { BackendService } from './backend.service';
|
||||||
import { Logger } from './logger.service';
|
import { Logger } from './logger.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -18,7 +18,7 @@ export class BackendService {
|
|||||||
// TODO: get from the database
|
// TODO: get from the database
|
||||||
return Promise.resolve<Hero[]>(HEROES);
|
return Promise.resolve<Hero[]>(HEROES);
|
||||||
}
|
}
|
||||||
let err = new Error('Cannot get object of this type');
|
const err = new Error('Cannot get object of this type');
|
||||||
this.logger.error(err);
|
this.logger.error(err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
// #docregion metadata, providers
|
// #docregion metadata, providers
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -22,7 +22,7 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #docregion module
|
// #docregion module
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
// #docregion import-browser-module
|
// #docregion import-browser-module
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
// #enddocregion import-browser-module
|
// #enddocregion import-browser-module
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { SalesTaxService } from './sales-tax.service';
|
import { SalesTaxService } from './sales-tax.service';
|
||||||
import { TaxRateService } from './tax-rate.service';
|
import { TaxRateService } from './tax-rate.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sales-tax',
|
selector: 'app-sales-tax',
|
||||||
|
@ -7,7 +7,7 @@ export class SalesTaxService {
|
|||||||
constructor(private rateService: TaxRateService) { }
|
constructor(private rateService: TaxRateService) { }
|
||||||
|
|
||||||
getVAT(value: string | number) {
|
getVAT(value: string | number) {
|
||||||
let amount = (typeof value === 'string') ?
|
const amount = (typeof value === 'string') ?
|
||||||
parseFloat(value) : value;
|
parseFloat(value) : value;
|
||||||
return (amount || 0) * this.rateService.getRate('VAT');
|
return (amount || 0) * this.rateService.getRate('VAT');
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,32 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Attribute binding example', function () {
|
describe('Attribute binding example', () => {
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display Property Binding with Angular', function () {
|
it('should display Property Binding with Angular', () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings');
|
expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a table', function() {
|
it('should display a table', () => {
|
||||||
expect(element.all(by.css('table')).isPresent()).toBe(true);
|
expect(element.all(by.css('table')).isPresent()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display an Aria button', function () {
|
it('should display an Aria button', () => {
|
||||||
expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria');
|
expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a blue background on div', function () {
|
it('should display a blue background on div', () => {
|
||||||
expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)');
|
expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a blue div with a red border', function () {
|
it('should display a blue div with a red border', () => {
|
||||||
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a div with many classes', function () {
|
it('should display a div with many classes', () => {
|
||||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
|
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
|
||||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
|
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, HostBinding } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'comp-with-host-binding',
|
selector: 'comp-with-host-binding',
|
||||||
template: 'I am a component!',
|
template: 'I am a component!',
|
||||||
host: {
|
|
||||||
'[class.special]': 'isSpecial',
|
|
||||||
'[style.color]': 'color',
|
|
||||||
'[style.width]': 'width'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
export class CompWithHostBindingComponent {
|
export class CompWithHostBindingComponent {
|
||||||
|
@HostBinding('class.special')
|
||||||
isSpecial = false;
|
isSpecial = false;
|
||||||
|
|
||||||
|
@HostBinding('style.color')
|
||||||
color = 'green';
|
color = 'green';
|
||||||
|
|
||||||
|
@HostBinding('style.width')
|
||||||
width = '200px';
|
width = '200px';
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Attribute directives', () => {
|
describe('Attribute directives', () => {
|
||||||
|
|
||||||
let _title = 'My First Attribute Directive';
|
const title = 'My First Attribute Directive';
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should display correct title: ${_title}`, () => {
|
it(`should display correct title: ${title}`, () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual(_title);
|
expect(element(by.css('h1')).getText()).toEqual(title);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to select green highlight', () => {
|
it('should be able to select green highlight', () => {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component.1';
|
import { AppComponent } from './app.component.1';
|
||||||
import { HighlightDirective as HLD1 } from './highlight.directive.1';
|
import { HighlightDirective as HLD1 } from './highlight.directive.1';
|
||||||
import { HighlightDirective as HLD2 } from './highlight.directive.2';
|
import { HighlightDirective as HLD2 } from './highlight.directive.2';
|
||||||
import { HighlightDirective as HLD3 } from './highlight.directive.3';
|
import { HighlightDirective as HLD3 } from './highlight.directive.3';
|
||||||
|
@ -3,57 +3,55 @@ import { logging } from 'selenium-webdriver';
|
|||||||
|
|
||||||
describe('Binding syntax e2e tests', () => {
|
describe('Binding syntax e2e tests', () => {
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => browser.get(''));
|
||||||
browser.get('');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// helper function used to test what's logged to the console
|
// helper function used to test what's logged to the console
|
||||||
async function logChecker(button, contents) {
|
async function logChecker(button, contents) {
|
||||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||||
const message = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
|
const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
|
||||||
expect(message.length).toBeGreaterThan(0);
|
expect(messages.length).toBeGreaterThan(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
it('should display Binding syntax', function () {
|
it('should display Binding syntax', () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual('Binding syntax');
|
expect(element(by.css('h1')).getText()).toEqual('Binding syntax');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display Save button', function () {
|
it('should display Save button', () => {
|
||||||
expect(element.all(by.css('button')).get(0).getText()).toBe('Save');
|
expect(element.all(by.css('button')).get(0).getText()).toBe('Save');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display HTML attributes and DOM properties', function () {
|
it('should display HTML attributes and DOM properties', () => {
|
||||||
expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties');
|
expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display 1. Use the inspector...', function () {
|
it('should display 1. Use the inspector...', () => {
|
||||||
expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector');
|
expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display Disabled property vs. attribute', function () {
|
it('should display Disabled property vs. attribute', () => {
|
||||||
expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute');
|
expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should log a message including Sarah', async () => {
|
it('should log a message including Sarah', async () => {
|
||||||
let attributeButton = element.all(by.css('button')).get(1);
|
const attributeButton = element.all(by.css('button')).get(1);
|
||||||
await attributeButton.click();
|
await attributeButton.click();
|
||||||
const contents = 'Sarah';
|
const contents = 'Sarah';
|
||||||
logChecker(attributeButton, contents);
|
logChecker(attributeButton, contents);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log a message including Sarah for DOM property', async () => {
|
it('should log a message including Sarah for DOM property', async () => {
|
||||||
let DOMPropertyButton = element.all(by.css('button')).get(2);
|
const DOMPropertyButton = element.all(by.css('button')).get(2);
|
||||||
await DOMPropertyButton.click();
|
await DOMPropertyButton.click();
|
||||||
const contents = 'Sarah';
|
const contents = 'Sarah';
|
||||||
logChecker(DOMPropertyButton, contents);
|
logChecker(DOMPropertyButton, contents);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log a message including Sally for DOM property', async () => {
|
it('should log a message including Sally for DOM property', async () => {
|
||||||
let DOMPropertyButton = element.all(by.css('button')).get(2);
|
const DOMPropertyButton = element.all(by.css('button')).get(2);
|
||||||
let input = element(by.css('input'));
|
const input = element(by.css('input'));
|
||||||
input.sendKeys('Sally');
|
input.sendKeys('Sally');
|
||||||
await DOMPropertyButton.click();
|
await DOMPropertyButton.click();
|
||||||
const contents = 'Sally';
|
const contents = 'Sally';
|
||||||
@ -61,14 +59,14 @@ describe('Binding syntax e2e tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should log a message that Test Button works', async () => {
|
it('should log a message that Test Button works', async () => {
|
||||||
let testButton = element.all(by.css('button')).get(3);
|
const testButton = element.all(by.css('button')).get(3);
|
||||||
await testButton.click();
|
await testButton.click();
|
||||||
const contents = 'Test';
|
const contents = 'Test';
|
||||||
logChecker(testButton, contents);
|
logChecker(testButton, contents);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should toggle Test Button disabled', async () => {
|
it('should toggle Test Button disabled', async () => {
|
||||||
let toggleButton = element.all(by.css('button')).get(4);
|
const toggleButton = element.all(by.css('button')).get(4);
|
||||||
await toggleButton.click();
|
await toggleButton.click();
|
||||||
const contents = 'true';
|
const contents = 'true';
|
||||||
logChecker(toggleButton, contents);
|
logChecker(toggleButton, contents);
|
||||||
|
@ -26,7 +26,7 @@ export class AppComponent {
|
|||||||
|
|
||||||
toggleDisabled(): any {
|
toggleDisabled(): any {
|
||||||
|
|
||||||
let testButton = <HTMLInputElement> document.getElementById('testButton');
|
const testButton = document.getElementById('testButton') as HTMLInputElement;
|
||||||
testButton.disabled = !testButton.disabled;
|
testButton.disabled = !testButton.disabled;
|
||||||
console.warn(testButton.disabled);
|
console.warn(testButton.disabled);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,19 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Built-in Directives', function () {
|
describe('Built-in Directives', () => {
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have title Built-in Directives', function () {
|
it('should have title Built-in Directives', () => {
|
||||||
let title = element.all(by.css('h1')).get(0);
|
const title = element.all(by.css('h1')).get(0);
|
||||||
expect(title.getText()).toEqual('Built-in Directives');
|
expect(title.getText()).toEqual('Built-in Directives');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change first Teapot header', async () => {
|
it('should change first Teapot header', async () => {
|
||||||
let firstLabel = element.all(by.css('p')).get(0);
|
const firstLabel = element.all(by.css('p')).get(0);
|
||||||
let firstInput = element.all(by.css('input')).get(0);
|
const firstInput = element.all(by.css('input')).get(0);
|
||||||
|
|
||||||
expect(firstLabel.getText()).toEqual('Current item name: Teapot');
|
expect(firstLabel.getText()).toEqual('Current item name: Teapot');
|
||||||
firstInput.sendKeys('abc');
|
firstInput.sendKeys('abc');
|
||||||
@ -23,49 +21,49 @@ describe('Built-in Directives', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should modify sentence when modified checkbox checked', function () {
|
it('should modify sentence when modified checkbox checked', () => {
|
||||||
let modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1);
|
const modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1);
|
||||||
let modifiedSentence = element.all(by.css('div')).get(1);
|
const modifiedSentence = element.all(by.css('div')).get(1);
|
||||||
|
|
||||||
modifiedChkbxLabel.click();
|
modifiedChkbxLabel.click();
|
||||||
expect(modifiedSentence.getText()).toContain('modified');
|
expect(modifiedSentence.getText()).toContain('modified');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should modify sentence when normal checkbox checked', function () {
|
it('should modify sentence when normal checkbox checked', () => {
|
||||||
let normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4);
|
const normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4);
|
||||||
let normalSentence = element.all(by.css('div')).get(7);
|
const normalSentence = element.all(by.css('div')).get(7);
|
||||||
|
|
||||||
normalChkbxLabel.click();
|
normalChkbxLabel.click();
|
||||||
expect(normalSentence.getText()).toContain('normal weight and, extra large');
|
expect(normalSentence.getText()).toContain('normal weight and, extra large');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should toggle app-item-detail', function () {
|
it('should toggle app-item-detail', () => {
|
||||||
let toggleButton = element.all(by.css('button')).get(3);
|
const toggleButton = element.all(by.css('button')).get(3);
|
||||||
let toggledDiv = element.all(by.css('app-item-detail')).get(0);
|
const toggledDiv = element.all(by.css('app-item-detail')).get(0);
|
||||||
|
|
||||||
toggleButton.click();
|
toggleButton.click();
|
||||||
expect(toggledDiv.isDisplayed()).toBe(true);
|
expect(toggledDiv.isDisplayed()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide app-item-detail', function () {
|
it('should hide app-item-detail', () => {
|
||||||
let hiddenMessage = element.all(by.css('p')).get(11);
|
const hiddenMessage = element.all(by.css('p')).get(11);
|
||||||
let hiddenDiv = element.all(by.css('app-item-detail')).get(2);
|
const hiddenDiv = element.all(by.css('app-item-detail')).get(2);
|
||||||
|
|
||||||
expect(hiddenMessage.getText()).toContain('in the DOM');
|
expect(hiddenMessage.getText()).toContain('in the DOM');
|
||||||
expect(hiddenDiv.isDisplayed()).toBe(true);
|
expect(hiddenDiv.isDisplayed()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have 10 lists each containing the string Teapot', function () {
|
it('should have 10 lists each containing the string Teapot', () => {
|
||||||
let listDiv = element.all(by.cssContainingText('.box', 'Teapot'));
|
const listDiv = element.all(by.cssContainingText('.box', 'Teapot'));
|
||||||
expect(listDiv.count()).toBe(10);
|
expect(listDiv.count()).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should switch case', function () {
|
it('should switch case', () => {
|
||||||
let tvRadioButton = element.all(by.css('input[type="radio"]')).get(3);
|
const tvRadioButton = element.all(by.css('input[type="radio"]')).get(3);
|
||||||
let tvDiv = element(by.css('app-lost-item'));
|
const tvDiv = element(by.css('app-lost-item'));
|
||||||
|
|
||||||
let fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4);
|
const fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4);
|
||||||
let fishbowlDiv = element(by.css('app-unknown-item'));
|
const fishbowlDiv = element(by.css('app-unknown-item'));
|
||||||
|
|
||||||
tvRadioButton.click();
|
tvRadioButton.click();
|
||||||
expect(tvDiv.getText()).toContain('Television');
|
expect(tvDiv.getText()).toContain('Television');
|
||||||
|
@ -30,6 +30,14 @@ export class AppComponent implements OnInit {
|
|||||||
itemsWithTrackByCountReset = 0;
|
itemsWithTrackByCountReset = 0;
|
||||||
itemIdIncrement = 1;
|
itemIdIncrement = 1;
|
||||||
|
|
||||||
|
// #docregion setClasses
|
||||||
|
currentClasses: {};
|
||||||
|
// #enddocregion setClasses
|
||||||
|
|
||||||
|
// #docregion setStyles
|
||||||
|
currentStyles: {};
|
||||||
|
// #enddocregion setStyles
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.resetItems();
|
this.resetItems();
|
||||||
this.setCurrentClasses();
|
this.setCurrentClasses();
|
||||||
@ -41,20 +49,18 @@ export class AppComponent implements OnInit {
|
|||||||
this.currentItem.name = name.toUpperCase();
|
this.currentItem.name = name.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion setClasses
|
// #docregion setClasses
|
||||||
currentClasses: {};
|
|
||||||
setCurrentClasses() {
|
setCurrentClasses() {
|
||||||
// CSS classes: added/removed per current state of component properties
|
// CSS classes: added/removed per current state of component properties
|
||||||
this.currentClasses = {
|
this.currentClasses = {
|
||||||
'saveable': this.canSave,
|
saveable: this.canSave,
|
||||||
'modified': !this.isUnchanged,
|
modified: !this.isUnchanged,
|
||||||
'special': this.isSpecial
|
special: this.isSpecial
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// #enddocregion setClasses
|
// #enddocregion setClasses
|
||||||
|
|
||||||
// #docregion setStyles
|
// #docregion setStyles
|
||||||
currentStyles: {};
|
|
||||||
setCurrentStyles() {
|
setCurrentStyles() {
|
||||||
// CSS styles: set per current state of component properties
|
// CSS styles: set per current state of component properties
|
||||||
this.currentStyles = {
|
this.currentStyles = {
|
||||||
@ -70,11 +76,7 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
giveNullCustomerValue() {
|
giveNullCustomerValue() {
|
||||||
!(this.nullCustomer = null) ? (this.nullCustomer = 'Kelly') : (this.nullCustomer = null);
|
this.nullCustomer = 'Kelly';
|
||||||
}
|
|
||||||
|
|
||||||
resetNullItem() {
|
|
||||||
this.nullCustomer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resetItems() {
|
resetItems() {
|
||||||
@ -84,7 +86,7 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetList() {
|
resetList() {
|
||||||
this.resetItems()
|
this.resetItems();
|
||||||
this.itemsWithTrackByCountReset = 0;
|
this.itemsWithTrackByCountReset = 0;
|
||||||
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
|
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
|
||||||
}
|
}
|
||||||
@ -107,7 +109,7 @@ export class AppComponent implements OnInit {
|
|||||||
trackByItems(index: number, item: Item): number { return item.id; }
|
trackByItems(index: number, item: Item): number { return item.id; }
|
||||||
// #enddocregion trackByItems
|
// #enddocregion trackByItems
|
||||||
|
|
||||||
trackById(index: number, item: any): number { return item['id']; }
|
trackById(index: number, item: any): number { return item.id; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Built Template Functions Example', function () {
|
describe('Built Template Functions Example', () => {
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have title Built-in Template Functions', function () {
|
it('should have title Built-in Template Functions', () => {
|
||||||
let title = element.all(by.css('h1')).get(0);
|
const title = element.all(by.css('h1')).get(0);
|
||||||
expect(title.getText()).toEqual('Built-in Template Functions');
|
expect(title.getText()).toEqual('Built-in Template Functions');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display $any( ) in h2', function () {
|
it('should display $any( ) in h2', () => {
|
||||||
let header = element(by.css('h2'));
|
const header = element(by.css('h2'));
|
||||||
expect(header.getText()).toContain('$any( )');
|
expect(header.getText()).toContain('$any( )');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,87 +1,85 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Component Communication Cookbook Tests', function () {
|
describe('Component Communication Cookbook Tests', () => {
|
||||||
|
|
||||||
// Note: '?e2e' which app can read to know it is running in protractor
|
// Note: '?e2e' which app can read to know it is running in protractor
|
||||||
// e.g. `if (!/e2e/.test(location.search)) { ...`
|
// e.g. `if (!/e2e/.test(location.search)) { ...`
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('?e2e');
|
browser.get('?e2e');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Parent-to-child communication', function() {
|
describe('Parent-to-child communication', () => {
|
||||||
// #docregion parent-to-child
|
// #docregion parent-to-child
|
||||||
// ...
|
// ...
|
||||||
let _heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
|
const heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
|
||||||
let _masterName = 'Master';
|
const masterName = 'Master';
|
||||||
|
|
||||||
it('should pass properties to children properly', function () {
|
it('should pass properties to children properly', () => {
|
||||||
let parent = element.all(by.tagName('app-hero-parent')).get(0);
|
const parent = element.all(by.tagName('app-hero-parent')).get(0);
|
||||||
let heroes = parent.all(by.tagName('app-hero-child'));
|
const heroes = parent.all(by.tagName('app-hero-child'));
|
||||||
|
|
||||||
for (let i = 0; i < _heroNames.length; i++) {
|
for (let i = 0; i < heroNames.length; i++) {
|
||||||
let childTitle = heroes.get(i).element(by.tagName('h3')).getText();
|
const childTitle = heroes.get(i).element(by.tagName('h3')).getText();
|
||||||
let childDetail = heroes.get(i).element(by.tagName('p')).getText();
|
const childDetail = heroes.get(i).element(by.tagName('p')).getText();
|
||||||
expect(childTitle).toEqual(_heroNames[i] + ' says:');
|
expect(childTitle).toEqual(heroNames[i] + ' says:');
|
||||||
expect(childDetail).toContain(_masterName);
|
expect(childDetail).toContain(masterName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion parent-to-child
|
// #enddocregion parent-to-child
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Parent-to-child communication with setter', function() {
|
describe('Parent-to-child communication with setter', () => {
|
||||||
// #docregion parent-to-child-setter
|
// #docregion parent-to-child-setter
|
||||||
// ...
|
// ...
|
||||||
it('should display trimmed, non-empty names', function () {
|
it('should display trimmed, non-empty names', () => {
|
||||||
let _nonEmptyNameIndex = 0;
|
const nonEmptyNameIndex = 0;
|
||||||
let _nonEmptyName = '"Dr IQ"';
|
const nonEmptyName = '"Dr IQ"';
|
||||||
let parent = element.all(by.tagName('app-name-parent')).get(0);
|
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
||||||
let hero = parent.all(by.tagName('app-name-child')).get(_nonEmptyNameIndex);
|
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
|
||||||
|
|
||||||
let displayName = hero.element(by.tagName('h3')).getText();
|
const displayName = hero.element(by.tagName('h3')).getText();
|
||||||
expect(displayName).toEqual(_nonEmptyName);
|
expect(displayName).toEqual(nonEmptyName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should replace empty name with default name', function () {
|
it('should replace empty name with default name', () => {
|
||||||
let _emptyNameIndex = 1;
|
const emptyNameIndex = 1;
|
||||||
let _defaultName = '"<no name set>"';
|
const defaultName = '"<no name set>"';
|
||||||
let parent = element.all(by.tagName('app-name-parent')).get(0);
|
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
||||||
let hero = parent.all(by.tagName('app-name-child')).get(_emptyNameIndex);
|
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
|
||||||
|
|
||||||
let displayName = hero.element(by.tagName('h3')).getText();
|
const displayName = hero.element(by.tagName('h3')).getText();
|
||||||
expect(displayName).toEqual(_defaultName);
|
expect(displayName).toEqual(defaultName);
|
||||||
});
|
});
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion parent-to-child-setter
|
// #enddocregion parent-to-child-setter
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Parent-to-child communication with ngOnChanges', function() {
|
describe('Parent-to-child communication with ngOnChanges', () => {
|
||||||
// #docregion parent-to-child-onchanges
|
// #docregion parent-to-child-onchanges
|
||||||
// ...
|
// ...
|
||||||
// Test must all execute in this exact order
|
// Test must all execute in this exact order
|
||||||
it('should set expected initial values', function () {
|
it('should set expected initial values', () => {
|
||||||
let actual = getActual();
|
const actual = getActual();
|
||||||
|
|
||||||
let initialLabel = 'Version 1.23';
|
const initialLabel = 'Version 1.23';
|
||||||
let initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';
|
const initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';
|
||||||
|
|
||||||
expect(actual.label).toBe(initialLabel);
|
expect(actual.label).toBe(initialLabel);
|
||||||
expect(actual.count).toBe(1);
|
expect(actual.count).toBe(1);
|
||||||
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set expected values after clicking \'Minor\' twice', function () {
|
it('should set expected values after clicking \'Minor\' twice', () => {
|
||||||
let repoTag = element(by.tagName('app-version-parent'));
|
const repoTag = element(by.tagName('app-version-parent'));
|
||||||
let newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||||
|
|
||||||
newMinorButton.click().then(function() {
|
newMinorButton.click().then(() => {
|
||||||
newMinorButton.click().then(function() {
|
newMinorButton.click().then(() => {
|
||||||
let actual = getActual();
|
const actual = getActual();
|
||||||
|
|
||||||
let labelAfter2Minor = 'Version 1.25';
|
const labelAfter2Minor = 'Version 1.25';
|
||||||
let logAfter2Minor = 'minor changed from 24 to 25';
|
const logAfter2Minor = 'minor changed from 24 to 25';
|
||||||
|
|
||||||
expect(actual.label).toBe(labelAfter2Minor);
|
expect(actual.label).toBe(labelAfter2Minor);
|
||||||
expect(actual.count).toBe(3);
|
expect(actual.count).toBe(3);
|
||||||
@ -90,15 +88,15 @@ describe('Component Communication Cookbook Tests', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set expected values after clicking \'Major\' once', function () {
|
it('should set expected values after clicking \'Major\' once', () => {
|
||||||
let repoTag = element(by.tagName('app-version-parent'));
|
const repoTag = element(by.tagName('app-version-parent'));
|
||||||
let newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||||
|
|
||||||
newMajorButton.click().then(function() {
|
newMajorButton.click().then(() => {
|
||||||
let actual = getActual();
|
const actual = getActual();
|
||||||
|
|
||||||
let labelAfterMajor = 'Version 2.0';
|
const labelAfterMajor = 'Version 2.0';
|
||||||
let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
||||||
|
|
||||||
expect(actual.label).toBe(labelAfterMajor);
|
expect(actual.label).toBe(labelAfterMajor);
|
||||||
expect(actual.count).toBe(4);
|
expect(actual.count).toBe(4);
|
||||||
@ -107,14 +105,14 @@ describe('Component Communication Cookbook Tests', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function getActual() {
|
function getActual() {
|
||||||
let versionTag = element(by.tagName('app-version-child'));
|
const versionTag = element(by.tagName('app-version-child'));
|
||||||
let label = versionTag.element(by.tagName('h3')).getText();
|
const label = versionTag.element(by.tagName('h3')).getText();
|
||||||
let ul = versionTag.element((by.tagName('ul')));
|
const ul = versionTag.element((by.tagName('ul')));
|
||||||
let logs = ul.all(by.tagName('li'));
|
const logs = ul.all(by.tagName('li'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: label,
|
label,
|
||||||
logs: logs,
|
logs,
|
||||||
count: logs.count()
|
count: logs.count()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -123,30 +121,30 @@ describe('Component Communication Cookbook Tests', function () {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Child-to-parent communication', function() {
|
describe('Child-to-parent communication', () => {
|
||||||
// #docregion child-to-parent
|
// #docregion child-to-parent
|
||||||
// ...
|
// ...
|
||||||
it('should not emit the event initially', function () {
|
it('should not emit the event initially', () => {
|
||||||
let voteLabel = element(by.tagName('app-vote-taker'))
|
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||||
.element(by.tagName('h3')).getText();
|
.element(by.tagName('h3')).getText();
|
||||||
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process Agree vote', function () {
|
it('should process Agree vote', () => {
|
||||||
let agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
||||||
.all(by.tagName('button')).get(0);
|
.all(by.tagName('button')).get(0);
|
||||||
agreeButton1.click().then(function() {
|
agreeButton1.click().then(() => {
|
||||||
let voteLabel = element(by.tagName('app-vote-taker'))
|
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||||
.element(by.tagName('h3')).getText();
|
.element(by.tagName('h3')).getText();
|
||||||
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
|
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process Disagree vote', function () {
|
it('should process Disagree vote', () => {
|
||||||
let agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
||||||
.all(by.tagName('button')).get(1);
|
.all(by.tagName('button')).get(1);
|
||||||
agreeButton1.click().then(function() {
|
agreeButton1.click().then(() => {
|
||||||
let voteLabel = element(by.tagName('app-vote-taker'))
|
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||||
.element(by.tagName('h3')).getText();
|
.element(by.tagName('h3')).getText();
|
||||||
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
|
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
|
||||||
});
|
});
|
||||||
@ -157,31 +155,31 @@ describe('Component Communication Cookbook Tests', function () {
|
|||||||
|
|
||||||
// Can't run timer tests in protractor because
|
// Can't run timer tests in protractor because
|
||||||
// interaction w/ zones causes all tests to freeze & timeout.
|
// interaction w/ zones causes all tests to freeze & timeout.
|
||||||
xdescribe('Parent calls child via local var', function() {
|
xdescribe('Parent calls child via local var', () => {
|
||||||
countDownTimerTests('countdown-parent-lv');
|
countDownTimerTests('countdown-parent-lv');
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('Parent calls ViewChild', function() {
|
xdescribe('Parent calls ViewChild', () => {
|
||||||
countDownTimerTests('countdown-parent-vc');
|
countDownTimerTests('countdown-parent-vc');
|
||||||
});
|
});
|
||||||
|
|
||||||
function countDownTimerTests(parentTag: string) {
|
function countDownTimerTests(parentTag: string) {
|
||||||
// #docregion countdown-timer-tests
|
// #docregion countdown-timer-tests
|
||||||
// ...
|
// ...
|
||||||
it('timer and parent seconds should match', function () {
|
it('timer and parent seconds should match', () => {
|
||||||
let parent = element(by.tagName(parentTag));
|
const parent = element(by.tagName(parentTag));
|
||||||
let message = parent.element(by.tagName('app-countdown-timer')).getText();
|
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
||||||
browser.sleep(10); // give `seconds` a chance to catchup with `message`
|
browser.sleep(10); // give `seconds` a chance to catchup with `message`
|
||||||
let seconds = parent.element(by.className('seconds')).getText();
|
const seconds = parent.element(by.className('seconds')).getText();
|
||||||
expect(message).toContain(seconds);
|
expect(message).toContain(seconds);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stop the countdown', function () {
|
it('should stop the countdown', () => {
|
||||||
let parent = element(by.tagName(parentTag));
|
const parent = element(by.tagName(parentTag));
|
||||||
let stopButton = parent.all(by.tagName('button')).get(1);
|
const stopButton = parent.all(by.tagName('button')).get(1);
|
||||||
|
|
||||||
stopButton.click().then(function() {
|
stopButton.click().then(() => {
|
||||||
let message = parent.element(by.tagName('app-countdown-timer')).getText();
|
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
||||||
expect(message).toContain('Holding');
|
expect(message).toContain('Holding');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -190,39 +188,39 @@ describe('Component Communication Cookbook Tests', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('Parent and children communicate via a service', function() {
|
describe('Parent and children communicate via a service', () => {
|
||||||
// #docregion bidirectional-service
|
// #docregion bidirectional-service
|
||||||
// ...
|
// ...
|
||||||
it('should announce a mission', function () {
|
it('should announce a mission', () => {
|
||||||
let missionControl = element(by.tagName('app-mission-control'));
|
const missionControl = element(by.tagName('app-mission-control'));
|
||||||
let announceButton = missionControl.all(by.tagName('button')).get(0);
|
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||||
announceButton.click().then(function () {
|
announceButton.click().then(() => {
|
||||||
let history = missionControl.all(by.tagName('li'));
|
const history = missionControl.all(by.tagName('li'));
|
||||||
expect(history.count()).toBe(1);
|
expect(history.count()).toBe(1);
|
||||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Lovell', function () {
|
it('should confirm the mission by Lovell', () => {
|
||||||
testConfirmMission(1, 2, 'Lovell');
|
testConfirmMission(1, 2, 'Lovell');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Haise', function () {
|
it('should confirm the mission by Haise', () => {
|
||||||
testConfirmMission(3, 3, 'Haise');
|
testConfirmMission(3, 3, 'Haise');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Swigert', function () {
|
it('should confirm the mission by Swigert', () => {
|
||||||
testConfirmMission(2, 4, 'Swigert');
|
testConfirmMission(2, 4, 'Swigert');
|
||||||
});
|
});
|
||||||
|
|
||||||
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
||||||
let _confirmedLog = ' confirmed the mission';
|
const confirmedLog = ' confirmed the mission';
|
||||||
let missionControl = element(by.tagName('app-mission-control'));
|
const missionControl = element(by.tagName('app-mission-control'));
|
||||||
let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
||||||
confirmButton.click().then(function () {
|
confirmButton.click().then(() => {
|
||||||
let history = missionControl.all(by.tagName('li'));
|
const history = missionControl.all(by.tagName('li'));
|
||||||
expect(history.count()).toBe(expectedLogCount);
|
expect(history.count()).toBe(expectedLogCount);
|
||||||
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
|
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { AstronautComponent } from './astronaut.component';
|
import { AstronautComponent } from './astronaut.component';
|
||||||
@ -15,7 +15,7 @@ import { VersionParentComponent } from './version-parent.component';
|
|||||||
import { VoterComponent } from './voter.component';
|
import { VoterComponent } from './voter.component';
|
||||||
import { VoteTakerComponent } from './votetaker.component';
|
import { VoteTakerComponent } from './votetaker.component';
|
||||||
|
|
||||||
let directives: any[] = [
|
const directives: any[] = [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
AstronautComponent,
|
AstronautComponent,
|
||||||
CountdownTimerComponent,
|
CountdownTimerComponent,
|
||||||
@ -30,7 +30,7 @@ let directives: any[] = [
|
|||||||
VoteTakerComponent
|
VoteTakerComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
let schemas: any[] = [];
|
const schemas: any[] = [];
|
||||||
|
|
||||||
// Include Countdown examples
|
// Include Countdown examples
|
||||||
// unless in e2e tests which they break.
|
// unless in e2e tests which they break.
|
||||||
@ -49,6 +49,6 @@ if (!/e2e/.test(location.search)) {
|
|||||||
],
|
],
|
||||||
declarations: directives,
|
declarations: directives,
|
||||||
bootstrap: [ AppComponent ],
|
bootstrap: [ AppComponent ],
|
||||||
schemas: schemas
|
schemas
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { Component, Input, OnDestroy } from '@angular/core';
|
import { Component, Input, OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
import { MissionService } from './mission.service';
|
import { MissionService } from './mission.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-astronaut',
|
selector: 'app-astronaut',
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
// #docregion vc
|
// #docregion vc
|
||||||
import { AfterViewInit, ViewChild } from '@angular/core';
|
import { AfterViewInit, ViewChild } from '@angular/core';
|
||||||
// #docregion lv
|
// #docregion lv
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { CountdownTimerComponent } from './countdown-timer.component';
|
import { CountdownTimerComponent } from './countdown-timer.component';
|
||||||
|
|
||||||
// #enddocregion lv
|
// #enddocregion lv
|
||||||
// #enddocregion vc
|
// #enddocregion vc
|
||||||
|
@ -12,6 +12,6 @@ import { Hero } from './hero';
|
|||||||
})
|
})
|
||||||
export class HeroChildComponent {
|
export class HeroChildComponent {
|
||||||
@Input() hero: Hero;
|
@Input() hero: Hero;
|
||||||
@Input('master') masterName: string;
|
@Input('master') masterName: string; // tslint:disable-line: no-input-rename
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MissionService {
|
export class MissionService {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { MissionService } from './mission.service';
|
import { MissionService } from './mission.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-mission-control',
|
selector: 'app-mission-control',
|
||||||
@ -34,7 +34,7 @@ export class MissionControlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
announce() {
|
announce() {
|
||||||
let mission = this.missions[this.nextMission++];
|
const mission = this.missions[this.nextMission++];
|
||||||
this.missionService.announceMission(mission);
|
this.missionService.announceMission(mission);
|
||||||
this.history.push(`Mission "${mission}" announced`);
|
this.history.push(`Mission "${mission}" announced`);
|
||||||
if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
|
if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// tslint:disable: variable-name
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
@ -6,13 +7,11 @@ import { Component, Input } from '@angular/core';
|
|||||||
template: '<h3>"{{name}}"</h3>'
|
template: '<h3>"{{name}}"</h3>'
|
||||||
})
|
})
|
||||||
export class NameChildComponent {
|
export class NameChildComponent {
|
||||||
private _name = '';
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
|
get name(): string { return this._name; }
|
||||||
set name(name: string) {
|
set name(name: string) {
|
||||||
this._name = (name && name.trim()) || '<no name set>';
|
this._name = (name && name.trim()) || '<no name set>';
|
||||||
}
|
}
|
||||||
|
private _name = '';
|
||||||
get name(): string { return this._name; }
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -18,14 +18,14 @@ export class VersionChildComponent implements OnChanges {
|
|||||||
changeLog: string[] = [];
|
changeLog: string[] = [];
|
||||||
|
|
||||||
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
|
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
|
||||||
let log: string[] = [];
|
const log: string[] = [];
|
||||||
for (let propName in changes) {
|
for (const propName in changes) {
|
||||||
let changedProp = changes[propName];
|
const changedProp = changes[propName];
|
||||||
let to = JSON.stringify(changedProp.currentValue);
|
const to = JSON.stringify(changedProp.currentValue);
|
||||||
if (changedProp.isFirstChange()) {
|
if (changedProp.isFirstChange()) {
|
||||||
log.push(`Initial value of ${propName} set to ${to}`);
|
log.push(`Initial value of ${propName} set to ${to}`);
|
||||||
} else {
|
} else {
|
||||||
let from = JSON.stringify(changedProp.previousValue);
|
const from = JSON.stringify(changedProp.previousValue);
|
||||||
log.push(`${propName} changed from ${from} to ${to}`);
|
log.push(`${propName} changed from ${from} to ${to}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vote-taker',
|
selector: 'app-vote-taker',
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Component Style Tests', function () {
|
describe('Component Style Tests', () => {
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('scopes component styles to component view', function() {
|
it('scopes component styles to component view', () => {
|
||||||
let componentH1 = element(by.css('app-root > h1'));
|
const componentH1 = element(by.css('app-root > h1'));
|
||||||
let externalH1 = element(by.css('body > h1'));
|
const externalH1 = element(by.css('body > h1'));
|
||||||
|
|
||||||
// Note: sometimes webdriver returns the fontWeight as "normal",
|
// Note: sometimes webdriver returns the fontWeight as "normal",
|
||||||
// other times as "400", both of which are equal in CSS terms.
|
// other times as "400", both of which are equal in CSS terms.
|
||||||
@ -19,49 +17,49 @@ describe('Component Style Tests', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('allows styling :host element', function() {
|
it('allows styling :host element', () => {
|
||||||
let host = element(by.css('app-hero-details'));
|
const host = element(by.css('app-hero-details'));
|
||||||
|
|
||||||
expect(host.getCssValue('borderWidth')).toEqual('1px');
|
expect(host.getCssValue('borderWidth')).toEqual('1px');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports :host() in function form', function() {
|
it('supports :host() in function form', () => {
|
||||||
let host = element(by.css('app-hero-details'));
|
const host = element(by.css('app-hero-details'));
|
||||||
|
|
||||||
host.element(by.buttonText('Activate')).click();
|
host.element(by.buttonText('Activate')).click();
|
||||||
expect(host.getCssValue('borderWidth')).toEqual('3px');
|
expect(host.getCssValue('borderWidth')).toEqual('3px');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows conditional :host-context() styling', function() {
|
it('allows conditional :host-context() styling', () => {
|
||||||
let h2 = element(by.css('app-hero-details h2'));
|
const h2 = element(by.css('app-hero-details h2'));
|
||||||
|
|
||||||
expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
|
expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
|
||||||
});
|
});
|
||||||
|
|
||||||
it('styles both view and content children with /deep/', function() {
|
it('styles both view and content children with /deep/', () => {
|
||||||
let viewH3 = element(by.css('app-hero-team h3'));
|
const viewH3 = element(by.css('app-hero-team h3'));
|
||||||
let contentH3 = element(by.css('app-hero-controls h3'));
|
const contentH3 = element(by.css('app-hero-controls h3'));
|
||||||
|
|
||||||
expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
|
expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
|
||||||
expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
|
expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('includes styles loaded with CSS @import', function() {
|
it('includes styles loaded with CSS @import', () => {
|
||||||
let host = element(by.css('app-hero-details'));
|
const host = element(by.css('app-hero-details'));
|
||||||
|
|
||||||
expect(host.getCssValue('padding')).toEqual('10px');
|
expect(host.getCssValue('padding')).toEqual('10px');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('processes template inline styles', function() {
|
it('processes template inline styles', () => {
|
||||||
let button = element(by.css('app-hero-controls button'));
|
const button = element(by.css('app-hero-controls button'));
|
||||||
let externalButton = element(by.css('body > button'));
|
const externalButton = element(by.css('body > button'));
|
||||||
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
|
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
|
||||||
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
|
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('processes template <link>s', function() {
|
it('processes template <link>s', () => {
|
||||||
let li = element(by.css('app-hero-team li:first-child'));
|
const li = element(by.css('app-hero-team li:first-child'));
|
||||||
let externalLi = element(by.css('body > ul li'));
|
const externalLi = element(by.css('body > ul li'));
|
||||||
|
|
||||||
expect(li.getCssValue('listStyleType')).toEqual('square');
|
expect(li.getCssValue('listStyleType')).toEqual('square');
|
||||||
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');
|
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');
|
||||||
|
@ -1,76 +1,74 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Dependency Injection Cookbook', function () {
|
describe('Dependency Injection Cookbook', () => {
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Logged in User example', function () {
|
it('should render Logged in User example', () => {
|
||||||
let loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
|
const loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
|
||||||
expect(loggedInUser).toBeDefined();
|
expect(loggedInUser).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('"Bombasto" should be the logged in user', function () {
|
it('"Bombasto" should be the logged in user', () => {
|
||||||
let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
|
const loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
|
||||||
expect(loggedInUser).toBeDefined();
|
expect(loggedInUser).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render sorted heroes', function () {
|
it('should render sorted heroes', () => {
|
||||||
let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
|
const sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
|
||||||
expect(sortedHeroes).toBeDefined();
|
expect(sortedHeroes).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Dr Nice should be in sorted heroes', function () {
|
it('Dr Nice should be in sorted heroes', () => {
|
||||||
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0);
|
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0);
|
||||||
expect(sortedHero).toBeDefined();
|
expect(sortedHero).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('RubberMan should be in sorted heroes', function () {
|
it('RubberMan should be in sorted heroes', () => {
|
||||||
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
|
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
|
||||||
expect(sortedHero).toBeDefined();
|
expect(sortedHero).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Magma should be in sorted heroes', function () {
|
it('Magma should be in sorted heroes', () => {
|
||||||
let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
|
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
|
||||||
expect(sortedHero).toBeDefined();
|
expect(sortedHero).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Hero of the Month', function () {
|
it('should render Hero of the Month', () => {
|
||||||
let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
|
const heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
|
||||||
expect(heroOfTheMonth).toBeDefined();
|
expect(heroOfTheMonth).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Hero Bios', function () {
|
it('should render Hero Bios', () => {
|
||||||
let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
|
const heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
|
||||||
expect(heroBios).toBeDefined();
|
expect(heroBios).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Magma\'s description in Hero Bios', function () {
|
it('should render Magma\'s description in Hero Bios', () => {
|
||||||
let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
|
const magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
|
||||||
expect(magmaText).toBeDefined();
|
expect(magmaText).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Magma\'s phone in Hero Bios and Contacts', function () {
|
it('should render Magma\'s phone in Hero Bios and Contacts', () => {
|
||||||
let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
|
const magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
|
||||||
expect(magmaPhone).toBeDefined();
|
expect(magmaPhone).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render Hero-of-the-Month runner-ups', function () {
|
it('should render Hero-of-the-Month runner-ups', () => {
|
||||||
let runnersUp = element(by.id('rups1')).getText();
|
const runnersUp = element(by.id('rups1')).getText();
|
||||||
expect(runnersUp).toContain('RubberMan, Dr Nice');
|
expect(runnersUp).toContain('RubberMan, Dr Nice');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render DateLogger log entry in Hero-of-the-Month', function () {
|
it('should render DateLogger log entry in Hero-of-the-Month', () => {
|
||||||
let logs = element.all(by.id('logs')).get(0).getText();
|
const logs = element.all(by.id('logs')).get(0).getText();
|
||||||
expect(logs).toContain('INFO: starting up at');
|
expect(logs).toContain('INFO: starting up at');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should highlight Hero Bios and Contacts container when mouseover', function () {
|
it('should highlight Hero Bios and Contacts container when mouseover', () => {
|
||||||
let target = element(by.css('div[appHighlight="yellow"]'));
|
const target = element(by.css('div[appHighlight="yellow"]'));
|
||||||
let yellow = 'rgba(255, 255, 0, 1)';
|
const yellow = 'rgba(255, 255, 0, 1)';
|
||||||
|
|
||||||
expect(target.getCssValue('background-color')).not.toEqual(yellow);
|
expect(target.getCssValue('background-color')).not.toEqual(yellow);
|
||||||
|
|
||||||
@ -81,25 +79,25 @@ describe('Dependency Injection Cookbook', function () {
|
|||||||
browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000);
|
browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('in Parent Finder', function () {
|
describe('in Parent Finder', () => {
|
||||||
let cathy1 = element(by.css('alex cathy'));
|
const cathy1 = element(by.css('alex cathy'));
|
||||||
let craig1 = element(by.css('alex craig'));
|
const craig1 = element(by.css('alex craig'));
|
||||||
let carol1 = element(by.css('alex carol p'));
|
const carol1 = element(by.css('alex carol p'));
|
||||||
let carol2 = element(by.css('barry carol p'));
|
const carol2 = element(by.css('barry carol p'));
|
||||||
|
|
||||||
it('"Cathy" should find "Alex" via the component class', function () {
|
it('"Cathy" should find "Alex" via the component class', () => {
|
||||||
expect(cathy1.getText()).toContain('Found Alex via the component');
|
expect(cathy1.getText()).toContain('Found Alex via the component');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('"Craig" should not find "Alex" via the base class', function () {
|
it('"Craig" should not find "Alex" via the base class', () => {
|
||||||
expect(craig1.getText()).toContain('Did not find Alex via the base');
|
expect(craig1.getText()).toContain('Did not find Alex via the base');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('"Carol" within "Alex" should have "Alex" parent', function () {
|
it('"Carol" within "Alex" should have "Alex" parent', () => {
|
||||||
expect(carol1.getText()).toContain('Alex');
|
expect(carol1.getText()).toContain('Alex');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('"Carol" within "Barry" should have "Barry" parent', function () {
|
it('"Carol" within "Barry" should have "Barry" parent', () => {
|
||||||
expect(carol2.getText()).toContain('Barry');
|
expect(carol2.getText()).toContain('Barry');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
|
||||||
const routes: Routes = [];
|
const routes: Routes = [];
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
// #docregion import-services
|
// #docregion import-services
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
import { UserContextService } from './user-context.service';
|
import { UserContextService } from './user-context.service';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
// import { AppRoutingModule } from './app-routing.module';
|
// import { AppRoutingModule } from './app-routing.module';
|
||||||
import { LocationStrategy,
|
import { LocationStrategy,
|
||||||
HashLocationStrategy } from '@angular/common';
|
HashLocationStrategy } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { HeroData } from './hero-data';
|
import { HeroData } from './hero-data';
|
||||||
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
|
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||||
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeroBioComponent } from './hero-bio.component';
|
import { HeroBioComponent } from './hero-bio.component';
|
||||||
import { HeroBiosComponent,
|
import { HeroBiosComponent,
|
||||||
HeroBiosAndContactsComponent } from './hero-bios.component';
|
HeroBiosAndContactsComponent } from './hero-bios.component';
|
||||||
import { HeroOfTheMonthComponent } from './hero-of-the-month.component';
|
import { HeroOfTheMonthComponent } from './hero-of-the-month.component';
|
||||||
import { HeroContactComponent } from './hero-contact.component';
|
import { HeroContactComponent } from './hero-contact.component';
|
||||||
import { HeroesBaseComponent,
|
import { HeroesBaseComponent,
|
||||||
SortedHeroesComponent } from './sorted-heroes.component';
|
SortedHeroesComponent } from './sorted-heroes.component';
|
||||||
import { HighlightDirective } from './highlight.directive';
|
import { HighlightDirective } from './highlight.directive';
|
||||||
import { ParentFinderComponent,
|
import { ParentFinderComponent,
|
||||||
AlexComponent,
|
AlexComponent,
|
||||||
AliceComponent,
|
AliceComponent,
|
||||||
@ -30,8 +30,8 @@ import { ParentFinderComponent,
|
|||||||
CathyComponent,
|
CathyComponent,
|
||||||
BarryComponent,
|
BarryComponent,
|
||||||
BethComponent,
|
BethComponent,
|
||||||
BobComponent } from './parent-finder.component';
|
BobComponent } from './parent-finder.component';
|
||||||
import { StorageComponent } from './storage.component';
|
import { StorageComponent } from './storage.component';
|
||||||
|
|
||||||
const declarations = [
|
const declarations = [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
@ -42,11 +42,11 @@ const declarations = [
|
|||||||
ParentFinderComponent,
|
ParentFinderComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
const a_components = [AliceComponent, AlexComponent ];
|
const componentListA = [ AliceComponent, AlexComponent ];
|
||||||
|
|
||||||
const b_components = [ BarryComponent, BethComponent, BobComponent ];
|
const componentListB = [ BarryComponent, BethComponent, BobComponent ];
|
||||||
|
|
||||||
const c_components = [
|
const componentListC = [
|
||||||
CarolComponent, ChrisComponent, CraigComponent,
|
CarolComponent, ChrisComponent, CraigComponent,
|
||||||
CathyComponent
|
CathyComponent
|
||||||
];
|
];
|
||||||
@ -61,9 +61,9 @@ const c_components = [
|
|||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
declarations,
|
declarations,
|
||||||
a_components,
|
componentListA,
|
||||||
b_components,
|
componentListB,
|
||||||
c_components,
|
componentListC,
|
||||||
StorageComponent,
|
StorageComponent,
|
||||||
],
|
],
|
||||||
bootstrap: [ AppComponent ],
|
bootstrap: [ AppComponent ],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* tslint:disable:one-line*/
|
/* tslint:disable:one-line*/
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { HeroCacheService } from './hero-cache.service';
|
import { HeroCacheService } from './hero-cache.service';
|
||||||
|
|
||||||
// #docregion component
|
// #docregion component
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
|
|
||||||
//////// HeroBiosComponent ////
|
//////// HeroBiosComponent ////
|
||||||
// #docregion simple
|
// #docregion simple
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
// #docregion service
|
// #docregion service
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { Component, Host, Optional } from '@angular/core';
|
import { Component, Host, Optional } from '@angular/core';
|
||||||
|
|
||||||
import { HeroCacheService } from './hero-cache.service';
|
import { HeroCacheService } from './hero-cache.service';
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
|
|
||||||
// #docregion component
|
// #docregion component
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -3,7 +3,7 @@ import { Hero } from './hero';
|
|||||||
|
|
||||||
export class HeroData {
|
export class HeroData {
|
||||||
createDb() {
|
createDb() {
|
||||||
let heroes = [
|
const heroes = [
|
||||||
new Hero(1, 'Windstorm'),
|
new Hero(1, 'Windstorm'),
|
||||||
new Hero(2, 'Bombasto'),
|
new Hero(2, 'Bombasto'),
|
||||||
new Hero(3, 'Magneta'),
|
new Hero(3, 'Magneta'),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent
|
// Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent
|
||||||
// Injecting with the MinimalLogger "interface-class"
|
// Injecting with the MinimalLogger "interface-class"
|
||||||
import { Component, NgModule } from '@angular/core';
|
import { Component, NgModule } from '@angular/core';
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
import { MinimalLogger } from './minimal-logger.service';
|
import { MinimalLogger } from './minimal-logger.service';
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -10,12 +10,12 @@ export const TITLE = new InjectionToken<string>('title');
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { DateLoggerService } from './date-logger.service';
|
import { DateLoggerService } from './date-logger.service';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
import { MinimalLogger } from './minimal-logger.service';
|
import { MinimalLogger } from './minimal-logger.service';
|
||||||
import { RUNNERS_UP,
|
import { RUNNERS_UP,
|
||||||
runnersUpFactory } from './runners-up';
|
runnersUpFactory } from './runners-up';
|
||||||
|
|
||||||
// #enddocregion hero-of-the-month
|
// #enddocregion hero-of-the-month
|
||||||
// #docregion some-hero
|
// #docregion some-hero
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/* tslint:disable:no-unused-variable component-selector-name one-line check-open-brace */
|
// tslint:disable: component-selector space-before-function-paren
|
||||||
/* tslint:disable:*/
|
|
||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, forwardRef, Optional, SkipSelf } from '@angular/core';
|
import { Component, forwardRef, Optional, SkipSelf } from '@angular/core';
|
||||||
@ -20,8 +19,7 @@ const DifferentParent = Parent;
|
|||||||
// The `parentType` defaults to `Parent` when omitting the second parameter.
|
// The `parentType` defaults to `Parent` when omitting the second parameter.
|
||||||
// #docregion provide-the-parent
|
// #docregion provide-the-parent
|
||||||
export function provideParent
|
export function provideParent
|
||||||
// #enddocregion provide-parent, provide-the-parent
|
// #enddocregion provide-the-parent
|
||||||
// #docregion provide-parent
|
|
||||||
(component: any, parentType?: any) {
|
(component: any, parentType?: any) {
|
||||||
return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
|
return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { InjectionToken } from '@angular/core';
|
import { InjectionToken } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
// #docregion runners-up
|
// #docregion runners-up
|
||||||
@ -22,5 +22,5 @@ export function runnersUpFactory(take: number) {
|
|||||||
.join(', ');
|
.join(', ');
|
||||||
// #docregion factory-synopsis
|
// #docregion factory-synopsis
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
// #enddocregion factory-synopsis
|
// #enddocregion factory-synopsis
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
/////// HeroesBaseComponent /////
|
/////// HeroesBaseComponent /////
|
||||||
// #docregion heroes-base, injection
|
// #docregion heroes-base, injection
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { LoggerService } from './logger.service';
|
import { LoggerService } from './logger.service';
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
|
|
||||||
// #docregion injectables, injectable
|
// #docregion injectables, injectable
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -24,7 +24,7 @@ export class UserContextService {
|
|||||||
// #enddocregion ctor, injectables
|
// #enddocregion ctor, injectables
|
||||||
|
|
||||||
loadUser(userId: number) {
|
loadUser(userId: number) {
|
||||||
let user = this.userService.getUserById(userId);
|
const user = this.userService.getUserById(userId);
|
||||||
this.name = user.name;
|
this.name = user.name;
|
||||||
this.role = user.role;
|
this.role = user.role;
|
||||||
|
|
||||||
|
@ -1,202 +1,196 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by, ElementFinder } from 'protractor';
|
import { browser, element, by, ElementFinder } from 'protractor';
|
||||||
|
|
||||||
describe('Dependency Injection Tests', function () {
|
describe('Dependency Injection Tests', () => {
|
||||||
|
|
||||||
let expectedMsg: string;
|
let expectedMsg: string;
|
||||||
let expectedMsgRx: RegExp;
|
let expectedMsgRx: RegExp;
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Cars:', function() {
|
describe('Cars:', () => {
|
||||||
|
|
||||||
it('DI car displays as expected', function () {
|
it('DI car displays as expected', () => {
|
||||||
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
|
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#di')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#di')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('No DI car displays as expected', function () {
|
it('No DI car displays as expected', () => {
|
||||||
expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.';
|
expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Injector car displays as expected', function () {
|
it('Injector car displays as expected', () => {
|
||||||
expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.';
|
expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#injector')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#injector')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Factory car displays as expected', function () {
|
it('Factory car displays as expected', () => {
|
||||||
expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.';
|
expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#factory')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#factory')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Simple car displays as expected', function () {
|
it('Simple car displays as expected', () => {
|
||||||
expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.';
|
expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#simple')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#simple')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Super car displays as expected', function () {
|
it('Super car displays as expected', () => {
|
||||||
expectedMsg = 'Super car with 12 cylinders and Flintstone tires.';
|
expectedMsg = 'Super car with 12 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#super')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#super')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test car displays as expected', function () {
|
it('Test car displays as expected', () => {
|
||||||
expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.';
|
expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.';
|
||||||
expect(element(by.css('#test')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#test')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Other Injections:', function() {
|
describe('Other Injections:', () => {
|
||||||
it('DI car displays as expected', function () {
|
it('DI car displays as expected', () => {
|
||||||
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
|
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
|
||||||
expect(element(by.css('#car')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#car')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Hero displays as expected', function () {
|
it('Hero displays as expected', () => {
|
||||||
expectedMsg = 'Dr Nice';
|
expectedMsg = 'Dr Nice';
|
||||||
expect(element(by.css('#hero')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#hero')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Optional injection displays as expected', function () {
|
it('Optional injection displays as expected', () => {
|
||||||
expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!';
|
expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!';
|
||||||
expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Tests:', function() {
|
describe('Tests:', () => {
|
||||||
|
|
||||||
it('Tests display as expected', function () {
|
it('Tests display as expected', () => {
|
||||||
expectedMsgRx = /Tests passed/;
|
expectedMsgRx = /Tests passed/;
|
||||||
expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
|
expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Provider variations:', function() {
|
describe('Provider variations:', () => {
|
||||||
|
|
||||||
it('P1 (class) displays as expected', function () {
|
it('P1 (class) displays as expected', () => {
|
||||||
expectedMsg = 'Hello from logger provided with Logger class';
|
expectedMsg = 'Hello from logger provided with Logger class';
|
||||||
expect(element(by.css('#p1')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p1')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P3 (provide) displays as expected', function () {
|
it('P3 (provide) displays as expected', () => {
|
||||||
expectedMsg = 'Hello from logger provided with useClass:Logger';
|
expectedMsg = 'Hello from logger provided with useClass:Logger';
|
||||||
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P4 (useClass:BetterLogger) displays as expected', function () {
|
it('P4 (useClass:BetterLogger) displays as expected', () => {
|
||||||
expectedMsg = 'Hello from logger provided with useClass:BetterLogger';
|
expectedMsg = 'Hello from logger provided with useClass:BetterLogger';
|
||||||
expect(element(by.css('#p4')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p4')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () {
|
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', () => {
|
||||||
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
|
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
|
||||||
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P6a (no alias) displays as expected', function () {
|
it('P6a (no alias) displays as expected', () => {
|
||||||
expectedMsg = 'Hello OldLogger (but we want NewLogger)';
|
expectedMsg = 'Hello OldLogger (but we want NewLogger)';
|
||||||
expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P6b (alias) displays as expected', function () {
|
it('P6b (alias) displays as expected', () => {
|
||||||
expectedMsg = 'Hello from NewLogger (via aliased OldLogger)';
|
expectedMsg = 'Hello from NewLogger (via aliased OldLogger)';
|
||||||
expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P7 (useValue) displays as expected', function () {
|
it('P7 (useValue) displays as expected', () => {
|
||||||
expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"';
|
expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"';
|
||||||
expect(element(by.css('#p7')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p7')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P8 (useFactory) displays as expected', function () {
|
it('P8 (useFactory) displays as expected', () => {
|
||||||
expectedMsg = 'Hero service injected successfully via heroServiceProvider';
|
expectedMsg = 'Hero service injected successfully via heroServiceProvider';
|
||||||
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P9 (InjectionToken) displays as expected', function () {
|
it('P9 (InjectionToken) displays as expected', () => {
|
||||||
expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
|
expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
|
||||||
expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('P10 (optional dependency) displays as expected', function () {
|
it('P10 (optional dependency) displays as expected', () => {
|
||||||
expectedMsg = 'Optional logger was not available';
|
expectedMsg = 'Optional logger was not available';
|
||||||
expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
|
expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('User/Heroes:', function() {
|
describe('User/Heroes:', () => {
|
||||||
it('User is Bob - unauthorized', function () {
|
it('User is Bob - unauthorized', () => {
|
||||||
expectedMsgRx = /Bob, is not authorized/;
|
expectedMsgRx = /Bob, is not authorized/;
|
||||||
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
|
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have button', function () {
|
it('should have button', () => {
|
||||||
expect(element.all(by.cssContainingText('button', 'Next User'))
|
expect(element.all(by.cssContainingText('button', 'Next User'))
|
||||||
.get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
|
.get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unauthorized user should have multiple unauthorized heroes', function () {
|
it('unauthorized user should have multiple unauthorized heroes', () => {
|
||||||
let heroes = element.all(by.css('#unauthorized app-hero-list div'));
|
const heroes = element.all(by.css('#unauthorized app-hero-list div'));
|
||||||
expect(heroes.count()).toBeGreaterThan(0);
|
expect(heroes.count()).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unauthorized user should have no secret heroes', function () {
|
it('unauthorized user should have no secret heroes', () => {
|
||||||
let heroes = element.all(by.css('#unauthorized app-hero-list div'));
|
const heroes = element.all(by.css('#unauthorized app-hero-list div'));
|
||||||
expect(heroes.count()).toBeGreaterThan(0);
|
expect(heroes.count()).toBeGreaterThan(0);
|
||||||
|
|
||||||
let filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
|
const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
|
||||||
return elem.getText().then((text: string) => {
|
return elem.getText().then((text: string) => /secret/.test(text));
|
||||||
return /secret/.test(text);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(filteredHeroes.count()).toEqual(0);
|
expect(filteredHeroes.count()).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unauthorized user should have no authorized heroes listed', function () {
|
it('unauthorized user should have no authorized heroes listed', () => {
|
||||||
expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0);
|
expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('after button click', function() {
|
describe('after button click', () => {
|
||||||
|
|
||||||
beforeAll(function (done: any) {
|
beforeAll((done: any) => {
|
||||||
let buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
|
const buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
|
||||||
buttonEle.click().then(done, done);
|
buttonEle.click().then(done, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('User is Alice - authorized', function () {
|
it('User is Alice - authorized', () => {
|
||||||
expectedMsgRx = /Alice, is authorized/;
|
expectedMsgRx = /Alice, is authorized/;
|
||||||
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
|
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('authorized user should have multiple authorized heroes ', function () {
|
it('authorized user should have multiple authorized heroes ', () => {
|
||||||
let heroes = element.all(by.css('#authorized app-hero-list div'));
|
const heroes = element.all(by.css('#authorized app-hero-list div'));
|
||||||
expect(heroes.count()).toBeGreaterThan(0);
|
expect(heroes.count()).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', function () {
|
it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', () => {
|
||||||
let heroes = element.all(by.css('#tspAuthorized app-hero-list div'));
|
const heroes = element.all(by.css('#tspAuthorized app-hero-list div'));
|
||||||
expect(heroes.count()).toBeGreaterThan(0);
|
expect(heroes.count()).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('authorized user should have secret heroes', function () {
|
it('authorized user should have secret heroes', () => {
|
||||||
let heroes = element.all(by.css('#authorized app-hero-list div'));
|
const heroes = element.all(by.css('#authorized app-hero-list div'));
|
||||||
expect(heroes.count()).toBeGreaterThan(0);
|
expect(heroes.count()).toBeGreaterThan(0);
|
||||||
|
|
||||||
let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number) {
|
const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
|
||||||
return elem.getText().then(function(text: string) {
|
return elem.getText().then((text: string) => /secret/.test(text));
|
||||||
return /secret/.test(text);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(filteredHeroes.count()).toBeGreaterThan(0);
|
expect(filteredHeroes.count()).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('authorized user should have no unauthorized heroes listed', function () {
|
it('authorized user should have no unauthorized heroes listed', () => {
|
||||||
expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0);
|
expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// #docregion imports
|
// #docregion imports
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { APP_CONFIG, AppConfig } from './app.config';
|
import { APP_CONFIG, AppConfig } from './app.config';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
|
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { CarComponent } from './car/car.component';
|
import { CarComponent } from './car/car.component';
|
||||||
import { HeroesComponent } from './heroes/heroes.component';
|
import { HeroesComponent } from './heroes/heroes.component';
|
||||||
|
@ -7,7 +7,7 @@ import { Car, Engine, Tires } from './car';
|
|||||||
export function simpleCar() {
|
export function simpleCar() {
|
||||||
// #docregion car-ctor-instantiation
|
// #docregion car-ctor-instantiation
|
||||||
// Simple car with 4 cylinders and Flintstone tires.
|
// Simple car with 4 cylinders and Flintstone tires.
|
||||||
let car = new Car(new Engine(), new Tires());
|
const car = new Car(new Engine(), new Tires());
|
||||||
// #enddocregion car-ctor-instantiation
|
// #enddocregion car-ctor-instantiation
|
||||||
car.description = 'Simple';
|
car.description = 'Simple';
|
||||||
return car;
|
return car;
|
||||||
@ -16,30 +16,31 @@ export function simpleCar() {
|
|||||||
|
|
||||||
///////// example 2 ////////////
|
///////// example 2 ////////////
|
||||||
// #docregion car-ctor-instantiation-with-param
|
// #docregion car-ctor-instantiation-with-param
|
||||||
class Engine2 {
|
class Engine2 {
|
||||||
constructor(public cylinders: number) { }
|
constructor(public cylinders: number) { }
|
||||||
}
|
}
|
||||||
// #enddocregion car-ctor-instantiation-with-param
|
// #enddocregion car-ctor-instantiation-with-param
|
||||||
|
|
||||||
export function superCar() {
|
export function superCar() {
|
||||||
// #docregion car-ctor-instantiation-with-param
|
// #docregion car-ctor-instantiation-with-param
|
||||||
// Super car with 12 cylinders and Flintstone tires.
|
// Super car with 12 cylinders and Flintstone tires.
|
||||||
let bigCylinders = 12;
|
const bigCylinders = 12;
|
||||||
let car = new Car(new Engine2(bigCylinders), new Tires());
|
const car = new Car(new Engine2(bigCylinders), new Tires());
|
||||||
// #enddocregion car-ctor-instantiation-with-param
|
// #enddocregion car-ctor-instantiation-with-param
|
||||||
car.description = 'Super';
|
car.description = 'Super';
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////// example 3 //////////
|
/////////// example 3 //////////
|
||||||
// #docregion car-ctor-instantiation-with-mocks
|
// #docregion car-ctor-instantiation-with-mocks
|
||||||
class MockEngine extends Engine { cylinders = 8; }
|
class MockEngine extends Engine { cylinders = 8; }
|
||||||
class MockTires extends Tires { make = 'YokoGoodStone'; }
|
class MockTires extends Tires { make = 'YokoGoodStone'; }
|
||||||
|
|
||||||
// #enddocregion car-ctor-instantiation-with-mocks
|
// #enddocregion car-ctor-instantiation-with-mocks
|
||||||
export function testCar() {
|
export function testCar() {
|
||||||
// #docregion car-ctor-instantiation-with-mocks
|
// #docregion car-ctor-instantiation-with-mocks
|
||||||
// Test car with 8 cylinders and YokoGoodStone tires.
|
// Test car with 8 cylinders and YokoGoodStone tires.
|
||||||
let car = new Car(new MockEngine(), new MockTires());
|
const car = new Car(new MockEngine(), new MockTires());
|
||||||
// #enddocregion car-ctor-instantiation-with-mocks
|
// #enddocregion car-ctor-instantiation-with-mocks
|
||||||
car.description = 'Test';
|
car.description = 'Test';
|
||||||
return car;
|
return car;
|
||||||
|
@ -4,7 +4,7 @@ import { Engine, Tires, Car } from './car';
|
|||||||
// BAD pattern!
|
// BAD pattern!
|
||||||
export class CarFactory {
|
export class CarFactory {
|
||||||
createCar() {
|
createCar() {
|
||||||
let car = new Car(this.createEngine(), this.createTires());
|
const car = new Car(this.createEngine(), this.createTires());
|
||||||
car.description = 'Factory';
|
car.description = 'Factory';
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Injector } from '@angular/core';
|
import { Injector } from '@angular/core';
|
||||||
|
|
||||||
import { Car, Engine, Tires } from './car';
|
import { Car, Engine, Tires } from './car';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
// #docregion injector
|
// #docregion injector
|
||||||
export function useInjector() {
|
export function useInjector() {
|
||||||
@ -26,14 +26,14 @@ export function useInjector() {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
// #docregion injector-call
|
// #docregion injector-call
|
||||||
let car = injector.get(Car);
|
const car = injector.get(Car);
|
||||||
// #enddocregion injector-call, injector-create-and-call
|
// #enddocregion injector-call, injector-create-and-call
|
||||||
car.description = 'Injector';
|
car.description = 'Injector';
|
||||||
|
|
||||||
injector = Injector.create({
|
injector = Injector.create({
|
||||||
providers: [{ provide: Logger, deps: [] }]
|
providers: [{ provide: Logger, deps: [] }]
|
||||||
});
|
});
|
||||||
let logger = injector.get(Logger);
|
const logger = injector.get(Logger);
|
||||||
logger.log('Injector car.drive() said: ' + car.drive());
|
logger.log('Injector car.drive() said: ' + car.drive());
|
||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Car, Engine, Tires } from './car';
|
import { Car, Engine, Tires } from './car';
|
||||||
import { Car as CarNoDi } from './car-no-di';
|
import { Car as CarNoDi } from './car-no-di';
|
||||||
import { CarFactory } from './car-factory';
|
import { CarFactory } from './car-factory';
|
||||||
|
|
||||||
import { testCar,
|
import { testCar,
|
||||||
simpleCar,
|
simpleCar,
|
||||||
superCar } from './car-creations';
|
superCar } from './car-creations';
|
||||||
|
|
||||||
import { useInjector } from './car-injector';
|
import { useInjector } from './car-injector';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -27,9 +27,9 @@ import { useInjector } from './car-injector';
|
|||||||
providers: [Car, Engine, Tires]
|
providers: [Car, Engine, Tires]
|
||||||
})
|
})
|
||||||
export class CarComponent {
|
export class CarComponent {
|
||||||
factoryCar = (new CarFactory).createCar();
|
factoryCar = (new CarFactory()).createCar();
|
||||||
injectorCar = useInjector();
|
injectorCar = useInjector();
|
||||||
noDiCar = new CarNoDi;
|
noDiCar = new CarNoDi();
|
||||||
simpleCar = simpleCar();
|
simpleCar = simpleCar();
|
||||||
superCar = superCar();
|
superCar = superCar();
|
||||||
testCar = testCar();
|
testCar = testCar();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-list',
|
selector: 'app-hero-list',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
import { HeroService } from './hero.service.1';
|
import { HeroService } from './hero.service.1';
|
||||||
/*
|
/*
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* tslint:disable:one-line */
|
/* tslint:disable:one-line */
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
/* tslint:disable:one-line */
|
/* tslint:disable:one-line */
|
||||||
// #docregion
|
// #docregion
|
||||||
import { HeroService } from './hero.service';
|
import { HeroService } from './hero.service';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
import { UserService } from '../user.service';
|
import { UserService } from '../user.service';
|
||||||
|
|
||||||
// #docregion factory
|
// #docregion factory
|
||||||
let heroServiceFactory = (logger: Logger, userService: UserService) => {
|
const heroServiceFactory = (logger: Logger, userService: UserService) => {
|
||||||
return new HeroService(logger, userService.user.isAuthorized);
|
return new HeroService(logger, userService.user.isAuthorized);
|
||||||
};
|
};
|
||||||
// #enddocregion factory
|
// #enddocregion factory
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HEROES } from './mock-heroes';
|
import { HEROES } from './mock-heroes';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
import { UserService } from '../user.service';
|
import { UserService } from '../user.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -17,7 +17,7 @@ export class HeroService {
|
|||||||
private isAuthorized: boolean) { }
|
private isAuthorized: boolean) { }
|
||||||
|
|
||||||
getHeroes() {
|
getHeroes() {
|
||||||
let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
|
const auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
|
||||||
this.logger.log(`Getting heroes for ${auth} user.`);
|
this.logger.log(`Getting heroes for ${auth} user.`);
|
||||||
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
|
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { heroServiceProvider } from './hero.service.provider';
|
import { heroServiceProvider } from './hero.service.provider';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Injector, OnInit } from '@angular/core';
|
import { Component, Injector, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Car, Engine, Tires } from './car/car';
|
import { Car, Engine, Tires } from './car/car';
|
||||||
import { Hero } from './heroes/hero';
|
import { Hero } from './heroes/hero';
|
||||||
import { HeroService } from './heroes/hero.service';
|
import { HeroService } from './heroes/hero.service';
|
||||||
import { heroServiceProvider } from './heroes/hero.service.provider';
|
import { heroServiceProvider } from './heroes/hero.service.provider';
|
||||||
import { Logger } from './logger.service';
|
import { Logger } from './logger.service';
|
||||||
|
|
||||||
// #docregion injector
|
// #docregion injector
|
||||||
@Component({
|
@Component({
|
||||||
@ -36,7 +36,7 @@ export class InjectorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get rodent() {
|
get rodent() {
|
||||||
let rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
|
const rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
|
||||||
return this.injector.get(ROUS, rousDontExist);
|
return this.injector.get(ROUS, rousDontExist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ const template = '{{log}}';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-1',
|
selector: 'provider-1',
|
||||||
template: template,
|
template,
|
||||||
// #docregion providers-1, providers-logger
|
// #docregion providers-1, providers-logger
|
||||||
providers: [Logger]
|
providers: [Logger]
|
||||||
// #enddocregion providers-1, providers-logger
|
// #enddocregion providers-1, providers-logger
|
||||||
@ -35,7 +35,7 @@ export class Provider1Component {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-3',
|
selector: 'provider-3',
|
||||||
template: template,
|
template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-3
|
// #docregion providers-3
|
||||||
[{ provide: Logger, useClass: Logger }]
|
[{ provide: Logger, useClass: Logger }]
|
||||||
@ -54,7 +54,7 @@ export class BetterLogger extends Logger {}
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-4',
|
selector: 'provider-4',
|
||||||
template: template,
|
template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-4
|
// #docregion providers-4
|
||||||
[{ provide: Logger, useClass: BetterLogger }]
|
[{ provide: Logger, useClass: BetterLogger }]
|
||||||
@ -76,7 +76,7 @@ export class EvenBetterLogger extends Logger {
|
|||||||
constructor(private userService: UserService) { super(); }
|
constructor(private userService: UserService) { super(); }
|
||||||
|
|
||||||
log(message: string) {
|
log(message: string) {
|
||||||
let name = this.userService.user.name;
|
const name = this.userService.user.name;
|
||||||
super.log(`Message to ${name}: ${message}`);
|
super.log(`Message to ${name}: ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ export class EvenBetterLogger extends Logger {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-5',
|
selector: 'provider-5',
|
||||||
template: template,
|
template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-5
|
// #docregion providers-5
|
||||||
[ UserService,
|
[ UserService,
|
||||||
@ -107,12 +107,12 @@ export class OldLogger {
|
|||||||
logs: string[] = [];
|
logs: string[] = [];
|
||||||
log(message: string) {
|
log(message: string) {
|
||||||
throw new Error('Should not call the old logger!');
|
throw new Error('Should not call the old logger!');
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-6a',
|
selector: 'provider-6a',
|
||||||
template: template,
|
template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-6a
|
// #docregion providers-6a
|
||||||
[ NewLogger,
|
[ NewLogger,
|
||||||
@ -135,7 +135,7 @@ export class Provider6aComponent {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-6b',
|
selector: 'provider-6b',
|
||||||
template: template,
|
template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-6b
|
// #docregion providers-6b
|
||||||
[ NewLogger,
|
[ NewLogger,
|
||||||
@ -168,7 +168,7 @@ export const SilentLogger = {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-7',
|
selector: 'provider-7',
|
||||||
template: template,
|
template,
|
||||||
providers:
|
providers:
|
||||||
// #docregion providers-7
|
// #docregion providers-7
|
||||||
[{ provide: Logger, useValue: SilentLogger }]
|
[{ provide: Logger, useValue: SilentLogger }]
|
||||||
@ -186,7 +186,7 @@ export class Provider7Component {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-8',
|
selector: 'provider-8',
|
||||||
template: template,
|
template,
|
||||||
providers: [heroServiceProvider, Logger, UserService]
|
providers: [heroServiceProvider, Logger, UserService]
|
||||||
})
|
})
|
||||||
export class Provider8Component {
|
export class Provider8Component {
|
||||||
@ -202,7 +202,7 @@ export class Provider8Component {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-9',
|
selector: 'provider-9',
|
||||||
template: template,
|
template,
|
||||||
/*
|
/*
|
||||||
// #docregion providers-9-interface
|
// #docregion providers-9-interface
|
||||||
// FAIL! Can't use interface as provider token
|
// FAIL! Can't use interface as provider token
|
||||||
@ -237,11 +237,11 @@ export class Provider9Component implements OnInit {
|
|||||||
import { Optional } from '@angular/core';
|
import { Optional } from '@angular/core';
|
||||||
// #enddocregion import-optional
|
// #enddocregion import-optional
|
||||||
|
|
||||||
let some_message = 'Hello from the injected logger';
|
const someMessage = 'Hello from the injected logger';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'provider-10',
|
selector: 'provider-10',
|
||||||
template: template,
|
template,
|
||||||
providers: [{ provide: Logger, useValue: null }]
|
providers: [{ provide: Logger, useValue: null }]
|
||||||
})
|
})
|
||||||
export class Provider10Component implements OnInit {
|
export class Provider10Component implements OnInit {
|
||||||
@ -249,7 +249,7 @@ export class Provider10Component implements OnInit {
|
|||||||
// #docregion provider-10-ctor
|
// #docregion provider-10-ctor
|
||||||
constructor(@Optional() private logger?: Logger) {
|
constructor(@Optional() private logger?: Logger) {
|
||||||
if (this.logger) {
|
if (this.logger) {
|
||||||
this.logger.log(some_message);
|
this.logger.log(someMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion provider-10-ctor
|
// #enddocregion provider-10-ctor
|
||||||
|
@ -43,7 +43,7 @@ var testResults: {pass: string; message: string};
|
|||||||
|
|
||||||
function expect(actual: any) {
|
function expect(actual: any) {
|
||||||
return {
|
return {
|
||||||
toEqual: function(expected: any){
|
toEqual: (expected: any) => {
|
||||||
testResults = actual === expected ?
|
testResults = actual === expected ?
|
||||||
{pass: 'passed', message: testName} :
|
{pass: 'passed', message: testName} :
|
||||||
{pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};
|
{pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { ServiceModule } from './service-and-module';
|
import { ServiceModule } from './service-and-module';
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
|
@ -8,8 +8,8 @@ export class User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get the user; don't 'new' it.
|
// TODO: get the user; don't 'new' it.
|
||||||
let alice = new User('Alice', true);
|
const alice = new User('Alice', true);
|
||||||
let bob = new User('Bob', false);
|
const bob = new User('Bob', false);
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Displaying Data Tests', function () {
|
describe('Displaying Data Tests', () => {
|
||||||
let _title = 'Tour of Heroes';
|
const title = 'Tour of Heroes';
|
||||||
let _defaultHero = 'Windstorm';
|
const defaultHero = 'Windstorm';
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display correct title: ' + _title, function () {
|
it('should display correct title: ' + title, () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual(_title);
|
expect(element(by.css('h1')).getText()).toEqual(title);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have correct default hero: ' + _defaultHero, function () {
|
it('should have correct default hero: ' + defaultHero, () => {
|
||||||
expect(element(by.css('h2')).getText()).toContain(_defaultHero);
|
expect(element(by.css('h2')).getText()).toContain(defaultHero);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have heroes', function () {
|
it('should have heroes', () => {
|
||||||
let heroEls = element.all(by.css('li'));
|
const heroEls = element.all(by.css('li'));
|
||||||
expect(heroEls.count()).not.toBe(0, 'should have heroes');
|
expect(heroEls.count()).not.toBe(0, 'should have heroes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display "there are many heroes!"', function () {
|
it('should display "there are many heroes!"', () => {
|
||||||
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
|
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Docs Style Guide', function () {
|
describe('Docs Style Guide', () => {
|
||||||
let _title = 'Authors Style Guide Sample';
|
const title = 'Authors Style Guide Sample';
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display correct title: ' + _title, function () {
|
it('should display correct title: ' + title, () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual(_title);
|
expect(element(by.css('h1')).getText()).toEqual(title);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
// #docregion class
|
// #docregion class
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
/* tslint:disable:quotemark */
|
/* tslint:disable:quotemark */
|
||||||
describe('Dynamic Component Loader', function () {
|
describe('Dynamic Component Loader', () => {
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load ad banner', function () {
|
it('should load ad banner', () => {
|
||||||
let headline = element(by.xpath("//h4[text()='Featured Hero Profile']"));
|
const headline = element(by.xpath("//h4[text()='Featured Hero Profile']"));
|
||||||
let name = element(by.xpath("//h3[text()='Bombasto']"));
|
const name = element(by.xpath("//h3[text()='Bombasto']"));
|
||||||
let bio = element(by.xpath("//p[text()='Brave as they come']"));
|
const bio = element(by.xpath("//p[text()='Brave as they come']"));
|
||||||
|
|
||||||
expect(name).toBeDefined();
|
expect(name).toBeDefined();
|
||||||
expect(headline).toBeDefined();
|
expect(headline).toBeDefined();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
|
import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
import { AdDirective } from './ad.directive';
|
import { AdDirective } from './ad.directive';
|
||||||
import { AdItem } from './ad-item';
|
import { AdItem } from './ad-item';
|
||||||
import { AdComponent } from './ad.component';
|
import { AdComponent } from './ad.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -11,7 +11,7 @@ import { AdComponent } from './ad.component';
|
|||||||
template: `
|
template: `
|
||||||
<div class="ad-banner-example">
|
<div class="ad-banner-example">
|
||||||
<h3>Advertisements</h3>
|
<h3>Advertisements</h3>
|
||||||
<ng-template ad-host></ng-template>
|
<ng-template adHost></ng-template>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
// #enddocregion ad-host
|
// #enddocregion ad-host
|
||||||
@ -43,8 +43,8 @@ export class AdBannerComponent implements OnInit, OnDestroy {
|
|||||||
const viewContainerRef = this.adHost.viewContainerRef;
|
const viewContainerRef = this.adHost.viewContainerRef;
|
||||||
viewContainerRef.clear();
|
viewContainerRef.clear();
|
||||||
|
|
||||||
const componentRef = viewContainerRef.createComponent(componentFactory);
|
const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);
|
||||||
(<AdComponent>componentRef.instance).data = adItem.data;
|
componentRef.instance.data = adItem.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAds() {
|
getAds() {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
// tslint:disable: directive-selector
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Directive, ViewContainerRef } from '@angular/core';
|
import { Directive, ViewContainerRef } from '@angular/core';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[ad-host]',
|
selector: '[adHost]',
|
||||||
})
|
})
|
||||||
export class AdDirective {
|
export class AdDirective {
|
||||||
constructor(public viewContainerRef: ViewContainerRef) { }
|
constructor(public viewContainerRef: ViewContainerRef) { }
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { HeroJobAdComponent } from './hero-job-ad.component';
|
import { HeroJobAdComponent } from './hero-job-ad.component';
|
||||||
import { HeroProfileComponent } from './hero-profile.component';
|
import { HeroProfileComponent } from './hero-profile.component';
|
||||||
import { AdItem } from './ad-item';
|
import { AdItem } from './ad-item';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdService {
|
export class AdService {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { AdService } from './ad.service';
|
import { AdService } from './ad.service';
|
||||||
import { AdItem } from './ad-item';
|
import { AdItem } from './ad-item';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeroJobAdComponent } from './hero-job-ad.component';
|
import { HeroJobAdComponent } from './hero-job-ad.component';
|
||||||
import { AdBannerComponent } from './ad-banner.component';
|
import { AdBannerComponent } from './ad-banner.component';
|
||||||
import { HeroProfileComponent } from './hero-profile.component';
|
import { HeroProfileComponent } from './hero-profile.component';
|
||||||
import { AdDirective } from './ad.directive';
|
import { AdDirective } from './ad.directive';
|
||||||
import { AdService } from './ad.service';
|
import { AdService } from './ad.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [ BrowserModule ],
|
imports: [ BrowserModule ],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { AdComponent } from './ad.component';
|
import { AdComponent } from './ad.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { AdComponent } from './ad.component';
|
import { AdComponent } from './ad.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user