Compare commits
310 Commits
creating-l
...
10.1.1
Author | SHA1 | Date | |
---|---|---|---|
1c156eb304 | |||
396548442e | |||
c54161098d | |||
f1b355b54f | |||
e40ffb95c8 | |||
20564f997f | |||
b1398d1771 | |||
7e9134aae8 | |||
0a55058440 | |||
bb1122d087 | |||
2ea49c7add | |||
83d69978fd | |||
62de2131e1 | |||
e156e29edd | |||
775c305771 | |||
190dca0fdc | |||
309709d4b2 | |||
028ef30b34 | |||
56d5ff2a89 | |||
b4eb016e56 | |||
6b0dba48b1 | |||
cfd4c0b4dc | |||
38762020d3 | |||
a1c34c6f0a | |||
b084bffb64 | |||
6a28675a5e | |||
4de8dc3554 | |||
ab4f953c78 | |||
ee432aaab8 | |||
5863537575 | |||
fcd2eb2ffb | |||
251a28cb15 | |||
54bb1c3d6a | |||
6c6dd5f38c | |||
9794f20674 | |||
027b041cfd | |||
4886cf5965 | |||
f21d50d2e6 | |||
0ef985368e | |||
0a277c6c40 | |||
9bf32c4dcb | |||
1c2ccfed4d | |||
25afbcc459 | |||
29c89c9297 | |||
efc76064d9 | |||
dbab74429f | |||
6aac499ee7 | |||
32f33f095f | |||
b0bd777ba9 | |||
c01bd0fe8e | |||
5588324802 | |||
437ecc8090 | |||
0dda97ea66 | |||
5e4aeaa348 | |||
cbbf8b542f | |||
91dfb18840 | |||
e44ddf5baa | |||
6b1a505566 | |||
659705ad78 | |||
8864b0ed69 | |||
4e596b672f | |||
83866827c3 | |||
7006cac50a | |||
dd82f2fefd | |||
bf003340ab | |||
5e35edd724 | |||
c132dcd0ae | |||
bbe331569b | |||
21e9a0032c | |||
fb06903237 | |||
ecc6fd0d28 | |||
80cab26023 | |||
841dfa68f9 | |||
f0af387f6c | |||
14e90bef58 | |||
db3a21b382 | |||
b8351f3b10 | |||
81053d3160 | |||
bdba1a062d | |||
23f855b300 | |||
94a3e0e81d | |||
9cbde86534 | |||
18e474f522 | |||
cb3db0d31b | |||
d36828a7a1 | |||
f18e2d5898 | |||
375f0a6f67 | |||
281b647f15 | |||
0fc44e0436 | |||
201a546af8 | |||
6e643d9874 | |||
4985267211 | |||
b48cc6ead5 | |||
874792dc43 | |||
e7da4040d6 | |||
2a643e1ab6 | |||
364284b0dc | |||
956b25a100 | |||
8017ca4db3 | |||
22f1ac3e37 | |||
4ee5e730ab | |||
6442875c99 | |||
8f24bc9443 | |||
ac461e1efd | |||
f245c6bb15 | |||
68a9a01a64 | |||
8cd4099db9 | |||
0b54c0c6b4 | |||
1ec609946f | |||
7ad32649c0 | |||
9ad69c1503 | |||
9af2de821c | |||
0270020ac2 | |||
6b662d10c1 | |||
55fd725e74 | |||
f77fd5e02a | |||
63ba74fe4e | |||
aaa1d8e2fe | |||
dbfb50e9f4 | |||
bee44b3359 | |||
723a9ff095 | |||
e472f5f688 | |||
8373b720f3 | |||
301513311e | |||
64cf087ae5 | |||
fec9dcbeb0 | |||
e0e5c9f195 | |||
cfe424e875 | |||
3b9c802dee | |||
ca07da4563 | |||
81c3e809aa | |||
be96510ce9 | |||
cb05c0102f | |||
5f90b64328 | |||
71079ce47e | |||
ca798804b2 | |||
b071495f92 | |||
d5f819ebc1 | |||
1388c1761f | |||
fb8f4b4d72 | |||
f42e6ce917 | |||
175c79d1d8 | |||
945751e2e8 | |||
b769771d60 | |||
a80f654af9 | |||
aa847cb014 | |||
8763d8201c | |||
9f7a37b4e9 | |||
773f7908c0 | |||
f4ced74e3a | |||
8366effeec | |||
5f2e475abf | |||
aa3520eb7d | |||
823dd5b341 | |||
d6d7caa2a8 | |||
dcf7baf3d1 | |||
4d17418569 | |||
a2e069fdda | |||
28534d83ee | |||
a6292faa97 | |||
e2e5f83869 | |||
71138f6004 | |||
fa0104017a | |||
80b67e02b7 | |||
18098d38b8 | |||
9514fd9080 | |||
2e9fdbde9e | |||
df76a2048b | |||
3d156162af | |||
5dc8d287aa | |||
6da9e5851a | |||
250e299dc3 | |||
8f708b561c | |||
e34c33cd46 | |||
6d8c73a4d6 | |||
6ff28ac944 | |||
0de93fd402 | |||
445ac15a78 | |||
856db56cca | |||
354e66efad | |||
702958e968 | |||
df7f3b04b5 | |||
7525f3afc1 | |||
570d156ce4 | |||
26be5b4994 | |||
3821dc5f6c | |||
18cd1a9937 | |||
0551fbdf88 | |||
dca4443a8e | |||
2a745c8df8 | |||
a18f82b458 | |||
696a9b01ef | |||
d7c043ba35 | |||
0c2490368e | |||
bb88c9fa3d | |||
8227b56f9e | |||
1609815743 | |||
763023472b | |||
ba175be41f | |||
f0766a4474 | |||
6f0f0d3ea2 | |||
576e329f33 | |||
585ef2bdd7 | |||
2fcabe1557 | |||
192ef42304 | |||
8fbf40bf40 | |||
8f074296c2 | |||
e49b053dac | |||
87baa06cc6 | |||
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.
|
||||||
|
@ -32,8 +32,8 @@ var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelver
|
|||||||
|
|
||||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
# 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.
|
# 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_5: &components_repo_unit_tests_cache_key v9-angular-components-09e68db8ed5b1253f2fe38ff954ef0df019fc25a
|
||||||
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
|
var_6: &components_repo_unit_tests_cache_key_fallback v9-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`.
|
||||||
@ -656,6 +656,18 @@ jobs:
|
|||||||
- run: yarn tsc -p packages
|
- run: yarn tsc -p packages
|
||||||
- run: yarn tsc -p modules
|
- run: yarn tsc -p modules
|
||||||
- run: yarn bazel build //packages/zone.js:npm_package
|
- run: yarn bazel build //packages/zone.js:npm_package
|
||||||
|
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
||||||
|
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
||||||
|
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
|
||||||
|
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
|
||||||
|
# test in all supported browsers on Saucelabs anyway until this job can be removed.
|
||||||
|
- run:
|
||||||
|
name: Preparing Bazel-generated fixtures required in legacy tests
|
||||||
|
command: |
|
||||||
|
yarn bazel build //packages/core/test:downleveled_es5_fixture
|
||||||
|
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
|
||||||
|
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
|
||||||
|
dist/all/@angular/core/test/reflection/es5_downleveled_inheritance_fixture.js
|
||||||
- run:
|
- run:
|
||||||
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
|
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
|
||||||
name: Waiting for Saucelabs tunnel to connect
|
name: Waiting for Saucelabs tunnel to connect
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
28
.github/angular-robot.yml
vendored
28
.github/angular-robot.yml
vendored
@ -73,15 +73,14 @@ merge:
|
|||||||
- "packages/zone.js/scripts/**"
|
- "packages/zone.js/scripts/**"
|
||||||
|
|
||||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||||
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
|
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.\nPlease help to unblock it by resolving these conflicts. Thanks!"
|
||||||
\nPlease help to unblock it by resolving these conflicts. Thanks!"
|
|
||||||
|
|
||||||
# label to monitor
|
# label to monitor
|
||||||
mergeLabel: "PR action: merge"
|
mergeLabel: "action: merge"
|
||||||
|
|
||||||
# adding any of these labels will also add the merge label
|
# adding any of these labels will also add the merge label
|
||||||
mergeLinkedLabels:
|
mergeLinkedLabels:
|
||||||
- "PR action: merge-assistance"
|
- "action: merge-assistance"
|
||||||
|
|
||||||
# list of checks that will determine if the merge label can be added
|
# list of checks that will determine if the merge label can be added
|
||||||
checks:
|
checks:
|
||||||
@ -94,17 +93,17 @@ merge:
|
|||||||
|
|
||||||
# whether the PR shouldn't have a conflict with the base branch
|
# whether the PR shouldn't have a conflict with the base branch
|
||||||
noConflict: true
|
noConflict: true
|
||||||
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")
|
# list of labels that a PR needs to have, checked with a regexp (e.g. "target:" will work for the label "target: master")
|
||||||
requiredLabels:
|
requiredLabels:
|
||||||
- "PR target: *"
|
- "target: *"
|
||||||
- "cla: yes"
|
- "cla: yes"
|
||||||
|
|
||||||
# list of labels that a PR shouldn't have, checked after the required labels with a regexp
|
# list of labels that a PR shouldn't have, checked after the required labels with a regexp
|
||||||
forbiddenLabels:
|
forbiddenLabels:
|
||||||
- "PR target: TBD"
|
- "target: TBD"
|
||||||
- "PR action: cleanup"
|
- "action: cleanup"
|
||||||
- "PR action: review"
|
- "action: review"
|
||||||
- "PR state: blocked"
|
- "state: blocked"
|
||||||
- "cla: no"
|
- "cla: no"
|
||||||
|
|
||||||
# list of PR statuses that need to be successful
|
# list of PR statuses that need to be successful
|
||||||
@ -121,12 +120,7 @@ merge:
|
|||||||
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
|
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
|
||||||
# {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option
|
# {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option
|
||||||
# {{PLACEHOLDER}} will be replaced by the list of failing checks
|
# {{PLACEHOLDER}} will be replaced by the list of failing checks
|
||||||
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:
|
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:\n{{PLACEHOLDER}}\n\n**If you want your PR to be merged, it has to pass all the CI checks.**\n\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
|
||||||
\n{{PLACEHOLDER}}
|
|
||||||
\n
|
|
||||||
\n**If you want your PR to be merged, it has to pass all the CI checks.**
|
|
||||||
\n
|
|
||||||
\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
|
|
||||||
|
|
||||||
# options for the triage plugin
|
# options for the triage plugin
|
||||||
triage:
|
triage:
|
||||||
@ -186,4 +180,4 @@ rerunCircleCI:
|
|||||||
# set to true to disable
|
# set to true to disable
|
||||||
disabled: false
|
disabled: false
|
||||||
# the label which when added triggers a rerun of the default CircleCI workflow
|
# the label which when added triggers a rerun of the default CircleCI workflow
|
||||||
triggerRerunLabel: "PR action: rerun CI at HEAD"
|
triggerRerunLabel: "action: rerun CI at HEAD"
|
||||||
|
19
.ng-dev/caretaker.ts
Normal file
19
.ng-dev/caretaker.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {CaretakerConfig} from '../dev-infra/caretaker/config';
|
||||||
|
|
||||||
|
/** The configuration for `ng-dev caretaker` commands. */
|
||||||
|
export const caretaker: CaretakerConfig = {
|
||||||
|
githubQueries: [
|
||||||
|
{
|
||||||
|
name: 'Merge Queue',
|
||||||
|
query: `is:pr is:open status:success label:"action: merge"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Merge Assistance Queue',
|
||||||
|
query: `is:pr is:open status:success label:"action: merge-assistance"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Primary Triage Queue',
|
||||||
|
query: `is:open is:issue no:milestone`,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
@ -7,18 +7,6 @@ export const commitMessage: CommitMessageConfig = {
|
|||||||
maxLineLength: 120,
|
maxLineLength: 120,
|
||||||
minBodyLength: 20,
|
minBodyLength: 20,
|
||||||
minBodyLengthTypeExcludes: ['docs'],
|
minBodyLengthTypeExcludes: ['docs'],
|
||||||
types: [
|
|
||||||
'build',
|
|
||||||
'ci',
|
|
||||||
'docs',
|
|
||||||
'feat',
|
|
||||||
'fix',
|
|
||||||
'perf',
|
|
||||||
'refactor',
|
|
||||||
'release',
|
|
||||||
'style',
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
scopes: [
|
scopes: [
|
||||||
'animations',
|
'animations',
|
||||||
'bazel',
|
'bazel',
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import {caretaker} from './caretaker';
|
||||||
import {commitMessage} from './commit-message';
|
import {commitMessage} from './commit-message';
|
||||||
import {format} from './format';
|
import {format} from './format';
|
||||||
import {github} from './github';
|
import {github} from './github';
|
||||||
@ -8,4 +9,5 @@ module.exports = {
|
|||||||
format,
|
format,
|
||||||
github,
|
github,
|
||||||
merge,
|
merge,
|
||||||
|
caretaker,
|
||||||
};
|
};
|
||||||
|
@ -1,38 +1,25 @@
|
|||||||
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
||||||
|
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
||||||
|
import {github} from './github';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||||
* are respected by the merge script (e.g. the target labels).
|
* are respected by the merge script (e.g. the target labels).
|
||||||
*/
|
*/
|
||||||
export const merge = (): MergeConfig => {
|
export const merge: DevInfraMergeConfig['merge'] = async api => {
|
||||||
// TODO: resume dynamically determining patch branch
|
|
||||||
const patch = '10.0.x';
|
|
||||||
return {
|
return {
|
||||||
githubApiMerge: false,
|
githubApiMerge: false,
|
||||||
claSignedLabel: 'cla: yes',
|
claSignedLabel: 'cla: yes',
|
||||||
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
mergeReadyLabel: /^action: merge(-assistance)?/,
|
||||||
caretakerNoteLabel: 'PR action: merge-assistance',
|
caretakerNoteLabel: 'action: merge-assistance',
|
||||||
commitMessageFixupLabel: 'commit message fixup',
|
commitMessageFixupLabel: 'commit message fixup',
|
||||||
labels: [
|
labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'),
|
||||||
{
|
|
||||||
pattern: 'PR target: master-only',
|
|
||||||
branches: ['master'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'PR target: patch-only',
|
|
||||||
branches: [patch],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'PR target: master & patch',
|
|
||||||
branches: ['master', patch],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
requiredBaseCommits: {
|
requiredBaseCommits: {
|
||||||
// PRs that target either `master` or the patch branch, need to be rebased
|
// PRs that target either `master` or the patch branch, need to be rebased
|
||||||
// on top of the latest commit message validation fix.
|
// on top of the latest commit message validation fix.
|
||||||
// These SHAs are the commits that update the required license text in the header.
|
// These SHAs are the commits that update the required license text in the header.
|
||||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
||||||
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
'10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
@ -490,8 +509,8 @@ groups:
|
|||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'packages/core/src/i18n/**',
|
'packages/core/src/i18n/**',
|
||||||
'packages/core/src/render3/i18n.ts',
|
'packages/core/src/render3/i18n/**',
|
||||||
'packages/core/src/render3/i18n.md',
|
'packages/core/src/render3/instructions/i18n.ts',
|
||||||
'packages/core/src/render3/interfaces/i18n.ts',
|
'packages/core/src/render3/interfaces/i18n.ts',
|
||||||
'packages/common/locales/**',
|
'packages/common/locales/**',
|
||||||
'packages/common/src/i18n/**',
|
'packages/common/src/i18n/**',
|
||||||
@ -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,
|
||||||
|
269
CHANGELOG.md
269
CHANGELOG.md
@ -1,3 +1,216 @@
|
|||||||
|
<a name="10.1.1"></a>
|
||||||
|
## 10.1.1 (2020-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** correct confusion between field and property names ([#38685](https://github.com/angular/angular/issues/38685)) ([a1c34c6](https://github.com/angular/angular/commit/a1c34c6))
|
||||||
|
* **compiler-cli:** compute source-mappings for localized strings ([#38747](https://github.com/angular/angular/issues/38747)) ([b4eb016](https://github.com/angular/angular/commit/b4eb016)), closes [#38588](https://github.com/angular/angular/issues/38588)
|
||||||
|
* **compiler-cli:** ensure that a declaration is available in type-to-value conversion ([#38684](https://github.com/angular/angular/issues/38684)) ([56d5ff2](https://github.com/angular/angular/commit/56d5ff2)), closes [#38670](https://github.com/angular/angular/issues/38670)
|
||||||
|
* **core:** reset `tView` between tests in Ivy TestBed ([#38659](https://github.com/angular/angular/issues/38659)) ([efc7606](https://github.com/angular/angular/commit/efc7606)), closes [#38600](https://github.com/angular/angular/issues/38600)
|
||||||
|
* **localize:** do not expose NodeJS typings in $localize runtime code ([#38700](https://github.com/angular/angular/issues/38700)) ([4de8dc3](https://github.com/angular/angular/commit/4de8dc3)), closes [#38692](https://github.com/angular/angular/issues/38692)
|
||||||
|
* **localize:** enable whitespace preservation marker in XLIFF files ([#38737](https://github.com/angular/angular/issues/38737)) ([190dca0](https://github.com/angular/angular/commit/190dca0)), closes [#38679](https://github.com/angular/angular/issues/38679)
|
||||||
|
* **localize:** install `[@angular](https://github.com/angular)/localize` in `devDependencies` by default ([#38680](https://github.com/angular/angular/issues/38680)) ([dbab744](https://github.com/angular/angular/commit/dbab744)), closes [#38329](https://github.com/angular/angular/issues/38329)
|
||||||
|
* **localize:** render context of translation file parse errors ([#38673](https://github.com/angular/angular/issues/38673)) ([32f33f0](https://github.com/angular/angular/commit/32f33f0)), closes [#38377](https://github.com/angular/angular/issues/38377)
|
||||||
|
* **localize:** render location in XLIFF 2 even if there is no metadata ([#38713](https://github.com/angular/angular/issues/38713)) ([ab4f953](https://github.com/angular/angular/commit/ab4f953)), closes [#38705](https://github.com/angular/angular/issues/38705)
|
||||||
|
* **ngcc:** use aliased exported types correctly ([#38666](https://github.com/angular/angular/issues/38666)) ([6a28675](https://github.com/angular/angular/commit/6a28675)), closes [#38238](https://github.com/angular/angular/issues/38238)
|
||||||
|
* **router:** If users are using the Alt key when clicking the router links, prioritize browser’s default behavior ([#38375](https://github.com/angular/angular/issues/38375)) ([309709d](https://github.com/angular/angular/commit/309709d))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **core:** use `ngDevMode` to tree-shake error messages ([#38612](https://github.com/angular/angular/issues/38612)) ([b084bff](https://github.com/angular/angular/commit/b084bff))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.0"></a>
|
||||||
|
# 10.1.0 (2020-09-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **bazel:** provide LinkablePackageInfo from ng_module ([#37623](https://github.com/angular/angular/issues/37623)) ([6898eab](https://github.com/angular/angular/commit/6898eab))
|
||||||
|
* **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)
|
||||||
|
* **compiler-cli:** add `SourceFile.getOriginalLocation()` to sourcemaps package ([#32912](https://github.com/angular/angular/issues/32912)) ([6abb8d0](https://github.com/angular/angular/commit/6abb8d0))
|
||||||
|
* **compiler-cli:** Add compiler option to report errors when assigning to restricted input fields ([#38249](https://github.com/angular/angular/issues/38249)) ([71138f6](https://github.com/angular/angular/commit/71138f6))
|
||||||
|
* **compiler-cli:** add support for TypeScript 4.0 ([#38076](https://github.com/angular/angular/issues/38076)) ([0fc44e0](https://github.com/angular/angular/commit/0fc44e0))
|
||||||
|
* **compiler-cli:** explain why an expression cannot be used in AOT compilations ([#37587](https://github.com/angular/angular/issues/37587)) ([712f1bd](https://github.com/angular/angular/commit/712f1bd))
|
||||||
|
* **compiler:** support unary operators for more accurate type checking ([#37918](https://github.com/angular/angular/issues/37918)) ([874792d](https://github.com/angular/angular/commit/874792d)), closes [#20845](https://github.com/angular/angular/issues/20845) [#36178](https://github.com/angular/angular/issues/36178)
|
||||||
|
* **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:** support injection token as predicate in queries ([#37506](https://github.com/angular/angular/issues/37506)) ([97dc85b](https://github.com/angular/angular/commit/97dc85b)), closes [#21152](https://github.com/angular/angular/issues/21152) [#36144](https://github.com/angular/angular/issues/36144)
|
||||||
|
* **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))
|
||||||
|
* **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)
|
||||||
|
* **localize:** expose `canParse()` diagnostics ([#37909](https://github.com/angular/angular/issues/37909)) ([ec32eba](https://github.com/angular/angular/commit/ec32eba)), closes [#37901](https://github.com/angular/angular/issues/37901)
|
||||||
|
* **localize:** implement message extraction tool ([#32912](https://github.com/angular/angular/issues/32912)) ([190561d](https://github.com/angular/angular/commit/190561d))
|
||||||
|
* **platform-browser:** Allow `sms`-URLs ([#31463](https://github.com/angular/angular/issues/31463)) ([fc5c34d](https://github.com/angular/angular/commit/fc5c34d)), closes [#31462](https://github.com/angular/angular/issues/31462)
|
||||||
|
* **platform-server:** add option for absolute URL HTTP support ([#37539](https://github.com/angular/angular/issues/37539)) ([d37049a](https://github.com/angular/angular/commit/d37049a)), closes [#37071](https://github.com/angular/angular/issues/37071)
|
||||||
|
* **router:** better warning message when a router outlet has not been instantiated ([#30246](https://github.com/angular/angular/issues/30246)) ([1609815](https://github.com/angular/angular/commit/1609815))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **bazel:** fix integration test for bazel building ([#38629](https://github.com/angular/angular/issues/38629)) ([dd82f2f](https://github.com/angular/angular/commit/dd82f2f))
|
||||||
|
* **common:** date pipe gives wrong week number ([#37632](https://github.com/angular/angular/issues/37632)) ([ef1fb6d](https://github.com/angular/angular/commit/ef1fb6d)), closes [#33961](https://github.com/angular/angular/issues/33961)
|
||||||
|
* **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-cli:** avoid creating value expressions for symbols from type-only imports ([#37912](https://github.com/angular/angular/issues/37912)) ([18098d3](https://github.com/angular/angular/commit/18098d3)), closes [#37900](https://github.com/angular/angular/issues/37900)
|
||||||
|
* **compiler-cli:** ensure source-maps can handle webpack:// protocol ([#32912](https://github.com/angular/angular/issues/32912)) ([decd95e](https://github.com/angular/angular/commit/decd95e))
|
||||||
|
* **compiler-cli:** only read source-map comment from last line ([#32912](https://github.com/angular/angular/issues/32912)) ([07a07e3](https://github.com/angular/angular/commit/07a07e3))
|
||||||
|
* **compiler-cli:** type-check inputs that include undefined when there's coercion members ([#38273](https://github.com/angular/angular/issues/38273)) ([7525f3a](https://github.com/angular/angular/commit/7525f3a))
|
||||||
|
* **compiler:** incorrectly inferring namespace for HTML nodes inside SVG ([#38477](https://github.com/angular/angular/issues/38477)) ([0dda97e](https://github.com/angular/angular/commit/0dda97e)), closes [#37218](https://github.com/angular/angular/issues/37218)
|
||||||
|
* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222))
|
||||||
|
* **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)
|
||||||
|
* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([ca07da4](https://github.com/angular/angular/commit/ca07da4)), closes [#38453](https://github.com/angular/angular/issues/38453)
|
||||||
|
* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([c509243](https://github.com/angular/angular/commit/c509243))
|
||||||
|
* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([b950d46](https://github.com/angular/angular/commit/b950d46)), closes [#25214](https://github.com/angular/angular/issues/25214)
|
||||||
|
* **core:** move generated i18n statements to the `consts` field of ComponentDef ([#38404](https://github.com/angular/angular/issues/38404)) ([cb05c01](https://github.com/angular/angular/commit/cb05c01))
|
||||||
|
* **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)
|
||||||
|
* **forms:** handle form groups/arrays own pending async validation ([#22575](https://github.com/angular/angular/issues/22575)) ([77b62a5](https://github.com/angular/angular/commit/77b62a5)), closes [#10064](https://github.com/angular/angular/issues/10064)
|
||||||
|
* **language-service:** non-existent module format in package output ([#37623](https://github.com/angular/angular/issues/37623)) ([413a0fb](https://github.com/angular/angular/commit/413a0fb))
|
||||||
|
* **localize:** ensure required XLIFF parameters are serialized ([#38575](https://github.com/angular/angular/issues/38575)) ([f0af387](https://github.com/angular/angular/commit/f0af387)), closes [#38570](https://github.com/angular/angular/issues/38570)
|
||||||
|
* **localize:** extract the correct message ids ([#38498](https://github.com/angular/angular/issues/38498)) ([ac461e1](https://github.com/angular/angular/commit/ac461e1))
|
||||||
|
* **localize:** render ICU placeholders in extracted translation files ([#38484](https://github.com/angular/angular/issues/38484)) ([81c3e80](https://github.com/angular/angular/commit/81c3e80))
|
||||||
|
* **localize:** render text of extracted placeholders ([#38536](https://github.com/angular/angular/issues/38536)) ([14e90be](https://github.com/angular/angular/commit/14e90be))
|
||||||
|
* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([3b9c802](https://github.com/angular/angular/commit/3b9c802)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453)
|
||||||
|
* **router:** defer loading of wildcard module until needed ([#38348](https://github.com/angular/angular/issues/38348)) ([8f708b5](https://github.com/angular/angular/commit/8f708b5)), closes [#25494](https://github.com/angular/angular/issues/25494)
|
||||||
|
* **router:** fix navigation ignoring logic to compare to the browser url ([#37716](https://github.com/angular/angular/issues/37716)) ([a5ffca0](https://github.com/angular/angular/commit/a5ffca0)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586)
|
||||||
|
* **router:** properly compare array queryParams for equality ([#37709](https://github.com/angular/angular/issues/37709)) ([#37860](https://github.com/angular/angular/issues/37860)) ([1801d0c](https://github.com/angular/angular/commit/1801d0c))
|
||||||
|
* **router:** remove parenthesis for primary outlet segment after removing auxiliary outlet segment ([#24656](https://github.com/angular/angular/issues/24656)) ([#37163](https://github.com/angular/angular/issues/37163)) ([71f008f](https://github.com/angular/angular/commit/71f008f))
|
||||||
|
* **router:** restore 'history.state' object for navigations coming from Angular router ([#28108](https://github.com/angular/angular/issues/28108)) ([#28176](https://github.com/angular/angular/issues/28176)) ([df76a20](https://github.com/angular/angular/commit/df76a20))
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
* **router:** export DefaultRouteReuseStrategy to Router public_api ([#31575](https://github.com/angular/angular/issues/31575)) ([ca79880](https://github.com/angular/angular/commit/ca79880))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
* **compiler-cli:** don't emit template guards when child scope is empty ([#38418](https://github.com/angular/angular/issues/38418)) ([1388c17](https://github.com/angular/angular/commit/1388c17))
|
||||||
|
* **compiler-cli:** fix regressions in incremental program reuse ([#37641](https://github.com/angular/angular/issues/37641)) ([5103d90](https://github.com/angular/angular/commit/5103d90))
|
||||||
|
* **compiler-cli:** only generate directive declarations when used ([#38418](https://github.com/angular/angular/issues/38418)) ([fb8f4b4](https://github.com/angular/angular/commit/fb8f4b4))
|
||||||
|
* **compiler-cli:** only generate type-check code for referenced DOM elements ([#38418](https://github.com/angular/angular/issues/38418)) ([f42e6ce](https://github.com/angular/angular/commit/f42e6ce))
|
||||||
|
* **forms:** use internal `ngDevMode` flag to tree-shake error messages in prod builds ([#37821](https://github.com/angular/angular/issues/37821)) ([201a546](https://github.com/angular/angular/commit/201a546)), closes [#37697](https://github.com/angular/angular/issues/37697)
|
||||||
|
* **ngcc:** shortcircuit tokenizing in ESM dependency host ([#37639](https://github.com/angular/angular/issues/37639)) ([bd7f440](https://github.com/angular/angular/commit/bd7f440))
|
||||||
|
* **ngcc:** use `EntryPointManifest` to speed up noop `ProgramBaseEntryPointFinder` ([#37665](https://github.com/angular/angular/issues/37665)) ([9318e23](https://github.com/angular/angular/commit/9318e23))
|
||||||
|
* **router:** apply prioritizedGuardValue operator to optimize CanLoad guards ([#37523](https://github.com/angular/angular/issues/37523)) ([d7dd295](https://github.com/angular/angular/commit/d7dd295))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.14"></a>
|
||||||
|
## 10.0.14 (2020-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.12"></a>
|
||||||
|
## 10.0.12 (2020-08-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** adding references to const enums in runtime code ([#38542](https://github.com/angular/angular/issues/38542)) ([814b436](https://github.com/angular/angular/commit/814b436)), closes [#38513](https://github.com/angular/angular/issues/38513)
|
||||||
|
* **core:** remove closing body tag from inert DOM builder ([#38454](https://github.com/angular/angular/issues/38454)) ([5528536](https://github.com/angular/angular/commit/5528536))
|
||||||
|
* **localize:** include the last placeholder in parsed translation text ([#38452](https://github.com/angular/angular/issues/38452)) ([57d1a48](https://github.com/angular/angular/commit/57d1a48))
|
||||||
|
* **localize:** parse all parts of a translation with nested HTML ([#38452](https://github.com/angular/angular/issues/38452)) ([07b99f5](https://github.com/angular/angular/commit/07b99f5)), closes [#38422](https://github.com/angular/angular/issues/38422)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **language-service:** introduce hybrid visitor to locate AST node ([#38540](https://github.com/angular/angular/issues/38540)) ([66d8c22](https://github.com/angular/angular/commit/66d8c22))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.11"></a>
|
||||||
|
## 10.0.11 (2020-08-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **router:** ensure routerLinkActive updates when associated routerLinks change (resubmit of [#38349](https://github.com/angular/angular/issues/38349)) ([#38511](https://github.com/angular/angular/issues/38511)) ([0af9533](https://github.com/angular/angular/commit/0af9533)), closes [#18469](https://github.com/angular/angular/issues/18469)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.10"></a>
|
||||||
|
## 10.0.10 (2020-08-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** Allow scrolling when browser supports scrollTo ([#38468](https://github.com/angular/angular/issues/38468)) ([b32126c](https://github.com/angular/angular/commit/b32126c)), closes [#30630](https://github.com/angular/angular/issues/30630)
|
||||||
|
* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38500](https://github.com/angular/angular/issues/38500)) ([863acb6](https://github.com/angular/angular/commit/863acb6)), closes [#38453](https://github.com/angular/angular/issues/38453)
|
||||||
|
* **core:** error if CSS custom property in host binding has number in name ([#38432](https://github.com/angular/angular/issues/38432)) ([cb83b8a](https://github.com/angular/angular/commit/cb83b8a)), closes [#37292](https://github.com/angular/angular/issues/37292)
|
||||||
|
* **core:** fix multiple nested views removal from ViewContainerRef ([#38317](https://github.com/angular/angular/issues/38317)) ([d5e09f4](https://github.com/angular/angular/commit/d5e09f4)), closes [#38201](https://github.com/angular/angular/issues/38201)
|
||||||
|
* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38500](https://github.com/angular/angular/issues/38500)) ([f3dd6c2](https://github.com/angular/angular/commit/f3dd6c2)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453)
|
||||||
|
* **router:** ensure routerLinkActive updates when associated routerLinks change ([#38349](https://github.com/angular/angular/issues/38349)) ([989e8a1](https://github.com/angular/angular/commit/989e8a1)), closes [#18469](https://github.com/angular/angular/issues/18469)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.9"></a>
|
||||||
|
## 10.0.9 (2020-08-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** ensure scrollRestoration is writable ([#30630](https://github.com/angular/angular/issues/30630)) ([#38357](https://github.com/angular/angular/issues/38357)) ([58f4b3a](https://github.com/angular/angular/commit/58f4b3a)), closes [#30629](https://github.com/angular/angular/issues/30629)
|
||||||
|
* **compiler:** evaluate safe navigation expressions in correct binding order ([#37911](https://github.com/angular/angular/issues/37911)) ([f5b9d87](https://github.com/angular/angular/commit/f5b9d87)), closes [#37194](https://github.com/angular/angular/issues/37194)
|
||||||
|
* **compiler-cli:** avoid creating value expressions for symbols from type-only imports ([#38415](https://github.com/angular/angular/issues/38415)) ([ca2b4bc](https://github.com/angular/angular/commit/ca2b4bc)), closes [#37912](https://github.com/angular/angular/issues/37912)
|
||||||
|
* **compiler-cli:** infer quote expressions as any type in type checker ([#37917](https://github.com/angular/angular/issues/37917)) ([5b87c67](https://github.com/angular/angular/commit/5b87c67)), closes [#36568](https://github.com/angular/angular/issues/36568)
|
||||||
|
* **compiler-cli:** mark eager `NgModuleFactory` construction as not side effectful ([#38320](https://github.com/angular/angular/issues/38320)) ([016a41b](https://github.com/angular/angular/commit/016a41b)), closes [#38147](https://github.com/angular/angular/issues/38147)
|
||||||
|
* **compiler-cli:** match wrapHost parameter types within plugin interface ([#38004](https://github.com/angular/angular/issues/38004)) ([df01a82](https://github.com/angular/angular/commit/df01a82))
|
||||||
|
* **compiler-cli:** preserve quotes in class member names ([#38387](https://github.com/angular/angular/issues/38387)) ([c9acb7b](https://github.com/angular/angular/commit/c9acb7b)), closes [#38311](https://github.com/angular/angular/issues/38311)
|
||||||
|
* **core:** prevent NgModule scope being overwritten in JIT compiler ([#37795](https://github.com/angular/angular/issues/37795)) ([3acebdc](https://github.com/angular/angular/commit/3acebdc)), closes [#37105](https://github.com/angular/angular/issues/37105)
|
||||||
|
* **core:** queries not matching string injection tokens ([#38321](https://github.com/angular/angular/issues/38321)) ([32109dc](https://github.com/angular/angular/commit/32109dc)), closes [#38313](https://github.com/angular/angular/issues/38313) [#38315](https://github.com/angular/angular/issues/38315)
|
||||||
|
* **core:** Store the currently selected ICU in `LView` ([#38345](https://github.com/angular/angular/issues/38345)) ([ee5123f](https://github.com/angular/angular/commit/ee5123f))
|
||||||
|
* **platform-server:** remove styles added by ServerStylesHost on destruction ([#38367](https://github.com/angular/angular/issues/38367)) ([7f11149](https://github.com/angular/angular/commit/7f11149))
|
||||||
|
* **router:** prevent calling unsubscribe on undefined subscription in RouterPreloader ([#38344](https://github.com/angular/angular/issues/38344)) ([4151314](https://github.com/angular/angular/commit/4151314))
|
||||||
|
* **service-worker:** fix the chrome debugger syntax highlighter ([#38332](https://github.com/angular/angular/issues/38332)) ([f5d5bac](https://github.com/angular/angular/commit/f5d5bac))
|
||||||
|
|
||||||
|
|
||||||
|
<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.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.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)
|
||||||
|
|
||||||
@ -18,62 +231,6 @@
|
|||||||
* **bazel:** provide LinkablePackageInfo from ng_module ([#37778](https://github.com/angular/angular/issues/37778)) ([6cd10a1](https://github.com/angular/angular/commit/6cd10a1)), closes [/github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl#L144-L146](https://github.com//github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl/issues/L144-L146)
|
* **bazel:** provide LinkablePackageInfo from ng_module ([#37778](https://github.com/angular/angular/issues/37778)) ([6cd10a1](https://github.com/angular/angular/commit/6cd10a1)), closes [/github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl#L144-L146](https://github.com//github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl/issues/L144-L146)
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.0-next.1"></a>
|
|
||||||
# 10.1.0-next.1 (2020-07-15)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **bazel:** ng_module rule does not expose flat module information in Ivy ([#36971](https://github.com/angular/angular/issues/36971)) ([1550663](https://github.com/angular/angular/commit/1550663))
|
|
||||||
* **compiler:** check more cases for pipe usage inside host bindings ([#37883](https://github.com/angular/angular/issues/37883)) ([9322b9a](https://github.com/angular/angular/commit/9322b9a)), closes [#34655](https://github.com/angular/angular/issues/34655) [#37610](https://github.com/angular/angular/issues/37610)
|
|
||||||
* **compiler-cli:** ensure file_system handles mixed Windows drives ([#37959](https://github.com/angular/angular/issues/37959)) ([6b31155](https://github.com/angular/angular/commit/6b31155)), closes [#36777](https://github.com/angular/angular/issues/36777)
|
|
||||||
* **language-service:** remove completion for string ([#37983](https://github.com/angular/angular/issues/37983)) ([10aba15](https://github.com/angular/angular/commit/10aba15))
|
|
||||||
* **ngcc:** report a warning if ngcc tries to use a solution-style tsconfig ([#38003](https://github.com/angular/angular/issues/38003)) ([b358495](https://github.com/angular/angular/commit/b358495)), closes [#36386](https://github.com/angular/angular/issues/36386)
|
|
||||||
* **router:** ensure duplicate popstate/hashchange events are handled correctly ([#37674](https://github.com/angular/angular/issues/37674)) ([9185c6e](https://github.com/angular/angular/commit/9185c6e)), closes [/github.com/angular/angular/issues/16710#issuecomment-646919529](https://github.com//github.com/angular/angular/issues/16710/issues/issuecomment-646919529) [#16710](https://github.com/angular/angular/issues/16710)
|
|
||||||
* **service-worker:** correctly handle relative base href ([#37922](https://github.com/angular/angular/issues/37922)) ([d19ef65](https://github.com/angular/angular/commit/d19ef65)), closes [#25055](https://github.com/angular/angular/issues/25055) [#25055](https://github.com/angular/angular/issues/25055)
|
|
||||||
* **service-worker:** correctly serve `ngsw/state` with a non-root SW scope ([#37922](https://github.com/angular/angular/issues/37922)) ([2156bee](https://github.com/angular/angular/commit/2156bee)), closes [#30505](https://github.com/angular/angular/issues/30505)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.0-next.0"></a>
|
|
||||||
# 10.1.0-next.0 (2020-07-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **common:** date pipe gives wrong week number ([#37632](https://github.com/angular/angular/issues/37632)) ([ef1fb6d](https://github.com/angular/angular/commit/ef1fb6d)), closes [#33961](https://github.com/angular/angular/issues/33961)
|
|
||||||
* **compiler-cli:** ensure source-maps can handle webpack:// protocol ([#32912](https://github.com/angular/angular/issues/32912)) ([decd95e](https://github.com/angular/angular/commit/decd95e))
|
|
||||||
* **compiler-cli:** only read source-map comment from last line ([#32912](https://github.com/angular/angular/issues/32912)) ([07a07e3](https://github.com/angular/angular/commit/07a07e3))
|
|
||||||
* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([c509243](https://github.com/angular/angular/commit/c509243))
|
|
||||||
* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([b950d46](https://github.com/angular/angular/commit/b950d46)), closes [#25214](https://github.com/angular/angular/issues/25214)
|
|
||||||
* **forms:** handle form groups/arrays own pending async validation ([#22575](https://github.com/angular/angular/issues/22575)) ([77b62a5](https://github.com/angular/angular/commit/77b62a5)), closes [#10064](https://github.com/angular/angular/issues/10064)
|
|
||||||
* **language-service:** non-existent module format in package output ([#37623](https://github.com/angular/angular/issues/37623)) ([413a0fb](https://github.com/angular/angular/commit/413a0fb))
|
|
||||||
* **router:** fix navigation ignoring logic to compare to the browser url ([#37716](https://github.com/angular/angular/issues/37716)) ([a5ffca0](https://github.com/angular/angular/commit/a5ffca0)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586)
|
|
||||||
* **router:** properly compare array queryParams for equality ([#37709](https://github.com/angular/angular/issues/37709)) ([#37860](https://github.com/angular/angular/issues/37860)) ([1801d0c](https://github.com/angular/angular/commit/1801d0c))
|
|
||||||
* **router:** remove parenthesis for primary outlet segment after removing auxiliary outlet segment ([#24656](https://github.com/angular/angular/issues/24656)) ([#37163](https://github.com/angular/angular/issues/37163)) ([71f008f](https://github.com/angular/angular/commit/71f008f))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **bazel:** provide LinkablePackageInfo from ng_module ([#37623](https://github.com/angular/angular/issues/37623)) ([6898eab](https://github.com/angular/angular/commit/6898eab))
|
|
||||||
* **compiler-cli:** add `SourceFile.getOriginalLocation()` to sourcemaps package ([#32912](https://github.com/angular/angular/issues/32912)) ([6abb8d0](https://github.com/angular/angular/commit/6abb8d0))
|
|
||||||
* **compiler-cli:** explain why an expression cannot be used in AOT compilations ([#37587](https://github.com/angular/angular/issues/37587)) ([712f1bd](https://github.com/angular/angular/commit/712f1bd))
|
|
||||||
* **core:** support injection token as predicate in queries ([#37506](https://github.com/angular/angular/issues/37506)) ([97dc85b](https://github.com/angular/angular/commit/97dc85b)), closes [#21152](https://github.com/angular/angular/issues/21152) [#36144](https://github.com/angular/angular/issues/36144)
|
|
||||||
* **localize:** expose `canParse()` diagnostics ([#37909](https://github.com/angular/angular/issues/37909)) ([ec32eba](https://github.com/angular/angular/commit/ec32eba)), closes [#37901](https://github.com/angular/angular/issues/37901)
|
|
||||||
* **localize:** implement message extraction tool ([#32912](https://github.com/angular/angular/issues/32912)) ([190561d](https://github.com/angular/angular/commit/190561d))
|
|
||||||
* **platform-browser:** Allow `sms`-URLs ([#31463](https://github.com/angular/angular/issues/31463)) ([fc5c34d](https://github.com/angular/angular/commit/fc5c34d)), closes [#31462](https://github.com/angular/angular/issues/31462)
|
|
||||||
* **platform-server:** add option for absolute URL HTTP support ([#37539](https://github.com/angular/angular/issues/37539)) ([d37049a](https://github.com/angular/angular/commit/d37049a)), closes [#37071](https://github.com/angular/angular/issues/37071)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **compiler-cli:** fix regressions in incremental program reuse ([#37641](https://github.com/angular/angular/issues/37641)) ([5103d90](https://github.com/angular/angular/commit/5103d90))
|
|
||||||
* **ngcc:** shortcircuit tokenizing in ESM dependency host ([#37639](https://github.com/angular/angular/issues/37639)) ([bd7f440](https://github.com/angular/angular/commit/bd7f440))
|
|
||||||
* **ngcc:** use `EntryPointManifest` to speed up noop `ProgramBaseEntryPointFinder` ([#37665](https://github.com/angular/angular/issues/37665)) ([9318e23](https://github.com/angular/angular/commit/9318e23))
|
|
||||||
* **router:** apply prioritizedGuardValue operator to optimize CanLoad guards ([#37523](https://github.com/angular/angular/issues/37523)) ([d7dd295](https://github.com/angular/angular/commit/d7dd295))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.0.3"></a>
|
<a name="10.0.3"></a>
|
||||||
## 10.0.3 (2020-07-08)
|
## 10.0.3 (2020-07-08)
|
||||||
|
|
||||||
|
@ -230,7 +230,6 @@ Must be one of the following:
|
|||||||
* **fix**: A bug fix
|
* **fix**: A bug fix
|
||||||
* **perf**: A code change that improves performance
|
* **perf**: A code change that improves performance
|
||||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
* **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
|
* **test**: Adding missing tests or correcting existing tests
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,13 +16,6 @@ import {BuildNums, PrNums, SHA} from './constants';
|
|||||||
|
|
||||||
const logger = new Logger('mock-external-apis');
|
const logger = new Logger('mock-external-apis');
|
||||||
|
|
||||||
const log = (...args: any[]) => {
|
|
||||||
// Filter out non-matching URL checks
|
|
||||||
if (!/^matching.+: false$/.test(args[0])) {
|
|
||||||
logger.log(...args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const AIO_CIRCLE_CI_TOKEN = getEnvVar('AIO_CIRCLE_CI_TOKEN');
|
const AIO_CIRCLE_CI_TOKEN = getEnvVar('AIO_CIRCLE_CI_TOKEN');
|
||||||
const AIO_GITHUB_TOKEN = getEnvVar('AIO_GITHUB_TOKEN');
|
const AIO_GITHUB_TOKEN = getEnvVar('AIO_GITHUB_TOKEN');
|
||||||
|
|
||||||
@ -91,8 +84,8 @@ const createArchive = (buildNum: number, prNum: number, sha: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create request scopes
|
// Create request scopes
|
||||||
const circleCiApi = nock(CIRCLE_CI_API_HOST).log(log).persist();
|
const circleCiApi = nock(CIRCLE_CI_API_HOST).persist();
|
||||||
const githubApi = nock(GITHUB_API_HOST).log(log).persist().matchHeader('Authorization', `token ${AIO_GITHUB_TOKEN}`);
|
const githubApi = nock(GITHUB_API_HOST).persist().matchHeader('Authorization', `token ${AIO_GITHUB_TOKEN}`);
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
|
@ -27,28 +27,28 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"delete-empty": "^3.0.0",
|
"delete-empty": "^3.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"jasmine": "^3.5.0",
|
"jasmine": "^3.6.1",
|
||||||
"nock": "^12.0.3",
|
"nock": "^13.0.4",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.1",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.4",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.19",
|
||||||
"tar-stream": "^2.1.2",
|
"tar-stream": "^2.1.3",
|
||||||
"tslib": "^1.11.1"
|
"tslib": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/body-parser": "^1.19.0",
|
"@types/body-parser": "^1.19.0",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.8",
|
||||||
"@types/jasmine": "^3.5.10",
|
"@types/jasmine": "^3.5.14",
|
||||||
"@types/nock": "^11.1.0",
|
"@types/nock": "^11.1.0",
|
||||||
"@types/node": "^13.13.2",
|
"@types/node": "^14.6.4",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/shelljs": "^0.8.7",
|
"@types/shelljs": "^0.8.8",
|
||||||
"@types/supertest": "^2.0.8",
|
"@types/supertest": "^2.0.10",
|
||||||
"nodemon": "^2.0.3",
|
"nodemon": "^2.0.4",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^4.0.2",
|
||||||
"tslint": "^6.1.1",
|
"tslint": "^6.1.3",
|
||||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^4.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,23 +214,24 @@ describe('GithubApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'https.request()\' with the correct options', () => {
|
it('should call \'https.request()\' with the correct options', async () => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(200);
|
.reply(200);
|
||||||
|
|
||||||
(api as any).request('method', '/path');
|
await (api as any).request('method', '/path');
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should add the \'Authorization\' header containing the \'githubToken\'', () => {
|
it('should add the \'Authorization\' header containing the \'githubToken\'', async () => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method', undefined, {
|
.intercept('/path', 'method', undefined, {
|
||||||
reqheaders: {Authorization: 'token 12345'},
|
reqheaders: {Authorization: 'token 12345'},
|
||||||
})
|
})
|
||||||
.reply(200);
|
.reply(200);
|
||||||
(api as any).request('method', '/path');
|
|
||||||
|
await (api as any).request('method', '/path');
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -244,12 +245,13 @@ describe('GithubApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should \'JSON.stringify\' and send the data along with the request', () => {
|
it('should \'JSON.stringify\' and send the data along with the request', async () => {
|
||||||
const data = {key: 'value'};
|
const data = {key: 'value'};
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method', JSON.stringify(data))
|
.intercept('/path', 'method', JSON.stringify(data))
|
||||||
.reply(200);
|
.reply(200);
|
||||||
(api as any).request('method', '/path', data);
|
|
||||||
|
await (api as any).request('method', '/path', data);
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
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,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,7 +78,6 @@ 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
|
||||||
|
@ -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(' ')),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,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();
|
||||||
@ -42,19 +50,17 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
|
@ -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 { }
|
||||||
|
@ -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
|
||||||
|
@ -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,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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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 ],
|
||||||
|
@ -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,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) };
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,5 @@ export function runnersUpFactory(take: number) {
|
|||||||
.join(', ');
|
.join(', ');
|
||||||
// #docregion factory-synopsis
|
// #docregion factory-synopsis
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
// #enddocregion factory-synopsis
|
// #enddocregion factory-synopsis
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
@ -20,11 +20,12 @@ export function simpleCar() {
|
|||||||
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;
|
||||||
@ -39,7 +40,7 @@ export function superCar() {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -5,7 +5,7 @@ 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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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}.`};
|
||||||
|
@ -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,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,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();
|
||||||
|
@ -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,27 +1,25 @@
|
|||||||
'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 Form', function () {
|
describe('Dynamic Form', () => {
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should submit form', function () {
|
it('should submit form', () => {
|
||||||
let firstNameElement = element.all(by.css('input[id=firstName]')).get(0);
|
const firstNameElement = element.all(by.css('input[id=firstName]')).get(0);
|
||||||
expect(firstNameElement.getAttribute('value')).toEqual('Bombasto');
|
expect(firstNameElement.getAttribute('value')).toEqual('Bombasto');
|
||||||
|
|
||||||
let emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
|
const emailElement = element.all(by.css('input[id=emailAddress]')).get(0);
|
||||||
let email = 'test@test.com';
|
const email = 'test@test.com';
|
||||||
emailElement.sendKeys(email);
|
emailElement.sendKeys(email);
|
||||||
expect(emailElement.getAttribute('value')).toEqual(email);
|
expect(emailElement.getAttribute('value')).toEqual(email);
|
||||||
|
|
||||||
element(by.css('select option[value="solid"]')).click();
|
element(by.css('select option[value="solid"]')).click();
|
||||||
|
|
||||||
let saveButton = element.all(by.css('button')).get(0);
|
const saveButton = element.all(by.css('button')).get(0);
|
||||||
saveButton.click().then(function() {
|
saveButton.click().then(() => {
|
||||||
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
|
expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,13 +10,14 @@ export class QuestionBase<T> {
|
|||||||
options: {key: string, value: string}[];
|
options: {key: string, value: string}[];
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
value?: T,
|
value?: T;
|
||||||
key?: string,
|
key?: string;
|
||||||
label?: string,
|
label?: string;
|
||||||
required?: boolean,
|
required?: boolean;
|
||||||
order?: number,
|
order?: number;
|
||||||
controlType?: string,
|
controlType?: string;
|
||||||
type?: string
|
type?: string;
|
||||||
|
options?: {key: string, value: string}[];
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.value = options.value;
|
this.value = options.value;
|
||||||
this.key = options.key || '';
|
this.key = options.key || '';
|
||||||
@ -25,5 +26,6 @@ export class QuestionBase<T> {
|
|||||||
this.order = options.order === undefined ? 1 : options.order;
|
this.order = options.order === undefined ? 1 : options.order;
|
||||||
this.controlType = options.controlType || '';
|
this.controlType = options.controlType || '';
|
||||||
this.type = options.type || '';
|
this.type = options.type || '';
|
||||||
|
this.options = options.options || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ export class QuestionControlService {
|
|||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
toFormGroup(questions: QuestionBase<string>[] ) {
|
toFormGroup(questions: QuestionBase<string>[] ) {
|
||||||
let group: any = {};
|
const group: any = {};
|
||||||
|
|
||||||
questions.forEach(question => {
|
questions.forEach(question => {
|
||||||
group[question.key] = question.required ? new FormControl(question.value || '', Validators.required)
|
group[question.key] = question.required ? new FormControl(question.value || '', Validators.required)
|
||||||
|
@ -3,10 +3,4 @@ import { QuestionBase } from './question-base';
|
|||||||
|
|
||||||
export class DropdownQuestion extends QuestionBase<string> {
|
export class DropdownQuestion extends QuestionBase<string> {
|
||||||
controlType = 'dropdown';
|
controlType = 'dropdown';
|
||||||
options: {key: string, value: string}[] = [];
|
|
||||||
|
|
||||||
constructor(options: {} = {}) {
|
|
||||||
super(options);
|
|
||||||
this.options = options['options'] || [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,4 @@ import { QuestionBase } from './question-base';
|
|||||||
|
|
||||||
export class TextboxQuestion extends QuestionBase<string> {
|
export class TextboxQuestion extends QuestionBase<string> {
|
||||||
controlType = 'textbox';
|
controlType = 'textbox';
|
||||||
type: string;
|
|
||||||
|
|
||||||
constructor(options: {} = {}) {
|
|
||||||
super(options);
|
|
||||||
this.type = options['type'] || '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ export class QuestionService {
|
|||||||
// TODO: get from a remote source of question metadata
|
// TODO: get from a remote source of question metadata
|
||||||
getQuestions() {
|
getQuestions() {
|
||||||
|
|
||||||
let questions: QuestionBase<string>[] = [
|
const questions: QuestionBase<string>[] = [
|
||||||
|
|
||||||
new DropdownQuestion({
|
new DropdownQuestion({
|
||||||
key: 'brave',
|
key: 'brave',
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, by, element, ElementFinder, ExpectedConditions as EC } from 'protractor';
|
import { browser, by, element, ElementFinder, ExpectedConditions as EC } from 'protractor';
|
||||||
|
|
||||||
/* tslint:disable:quotemark */
|
/* tslint:disable:quotemark */
|
||||||
@ -16,7 +14,7 @@ describe('Elements', () => {
|
|||||||
const waitForText = (elem: ElementFinder) => {
|
const waitForText = (elem: ElementFinder) => {
|
||||||
// Waiting for the element to have some text, makes the tests less flaky.
|
// Waiting for the element to have some text, makes the tests less flaky.
|
||||||
browser.wait(async () => /\S/.test(await elem.getText()), 5000);
|
browser.wait(async () => /\S/.test(await elem.getText()), 5000);
|
||||||
}
|
};
|
||||||
|
|
||||||
beforeEach(() => browser.get(''));
|
beforeEach(() => browser.get(''));
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
// tslint:disable: variable-name
|
||||||
|
// #docregion
|
||||||
|
import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core';
|
||||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -7,9 +9,6 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
|||||||
<span>Popup: {{message}}</span>
|
<span>Popup: {{message}}</span>
|
||||||
<button (click)="closed.next()">✖</button>
|
<button (click)="closed.next()">✖</button>
|
||||||
`,
|
`,
|
||||||
host: {
|
|
||||||
'[@state]': 'state',
|
|
||||||
},
|
|
||||||
animations: [
|
animations: [
|
||||||
trigger('state', [
|
trigger('state', [
|
||||||
state('opened', style({transform: 'translateY(0%)'})),
|
state('opened', style({transform: 'translateY(0%)'})),
|
||||||
@ -39,15 +38,16 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
|||||||
`]
|
`]
|
||||||
})
|
})
|
||||||
export class PopupComponent {
|
export class PopupComponent {
|
||||||
|
@HostBinding('@state')
|
||||||
state: 'opened' | 'closed' = 'closed';
|
state: 'opened' | 'closed' = 'closed';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
|
get message(): string { return this._message; }
|
||||||
set message(message: string) {
|
set message(message: string) {
|
||||||
this._message = message;
|
this._message = message;
|
||||||
this.state = 'opened';
|
this.state = 'opened';
|
||||||
}
|
}
|
||||||
get message(): string { return this._message; }
|
private _message: string;
|
||||||
_message: string;
|
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
closed = new EventEmitter();
|
closed = new EventEmitter();
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
import { browser, element, by, protractor } from 'protractor';
|
describe('Event binding example', () => {
|
||||||
|
|
||||||
describe('Event binding example', function () {
|
beforeEach(() => {
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
let saveButton = element.all(by.css('button')).get(0);
|
const saveButton = element.all(by.css('button')).get(0);
|
||||||
let onSaveButton = element.all(by.css('button')).get(1);
|
const onSaveButton = element.all(by.css('button')).get(1);
|
||||||
let myClick = element.all(by.css('button')).get(2);
|
const myClick = element.all(by.css('button')).get(2);
|
||||||
let deleteButton = element.all(by.css('button')).get(3);
|
const deleteButton = element.all(by.css('button')).get(3);
|
||||||
let saveNoProp = element.all(by.css('button')).get(4);
|
const saveNoProp = element.all(by.css('button')).get(4);
|
||||||
let saveProp = element.all(by.css('button')).get(5);
|
const saveProp = element.all(by.css('button')).get(5);
|
||||||
|
|
||||||
|
|
||||||
it('should display Event Binding with Angular', function () {
|
it('should display Event Binding with Angular', () => {
|
||||||
expect(element(by.css('h1')).getText()).toEqual('Event Binding');
|
expect(element(by.css('h1')).getText()).toEqual('Event Binding');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display 6 buttons', function() {
|
it('should display 6 buttons', () => {
|
||||||
expect(saveButton.getText()).toBe('Save');
|
expect(saveButton.getText()).toBe('Save');
|
||||||
expect(onSaveButton.getText()).toBe('on-click Save');
|
expect(onSaveButton.getText()).toBe('on-click Save');
|
||||||
expect(myClick.getText()).toBe('click with myClick');
|
expect(myClick.getText()).toBe('click with myClick');
|
||||||
@ -29,24 +27,23 @@ describe('Event binding example', function () {
|
|||||||
expect(saveProp.getText()).toBe('Save with propagation');
|
expect(saveProp.getText()).toBe('Save with propagation');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support user input', function () {
|
it('should support user input', () => {
|
||||||
let input = element(by.css('input'));
|
const input = element(by.css('input'));
|
||||||
let bindingResult = element.all(by.css('h4')).get(1);
|
const bindingResult = element.all(by.css('h4')).get(1);
|
||||||
expect(bindingResult.getText()).toEqual('Result: teapot');
|
expect(bindingResult.getText()).toEqual('Result: teapot');
|
||||||
input.sendKeys('abc');
|
input.sendKeys('abc');
|
||||||
expect(bindingResult.getText()).toEqual('Result: teapotabc');
|
expect(bindingResult.getText()).toEqual('Result: teapotabc');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide the item img', async () => {
|
it('should hide the item img', async () => {
|
||||||
let deleteButton = element.all(by.css('button')).get(3);
|
|
||||||
await deleteButton.click();
|
await deleteButton.click();
|
||||||
browser.switchTo().alert().accept();
|
browser.switchTo().alert().accept();
|
||||||
expect(element.all(by.css('img')).get(0).getCssValue('display')).toEqual('none');
|
expect(element.all(by.css('img')).get(0).getCssValue('display')).toEqual('none');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show two alerts', async () => {
|
it('should show two alerts', async () => {
|
||||||
let parentDiv = element.all(by.css('.parent-div'));
|
const parentDiv = element.all(by.css('.parent-div'));
|
||||||
let childDiv = element.all(by.css('div > div')).get(1);
|
const childDiv = element.all(by.css('div > div')).get(1);
|
||||||
await parentDiv.click();
|
await parentDiv.click();
|
||||||
browser.switchTo().alert().accept();
|
browser.switchTo().alert().accept();
|
||||||
expect(childDiv.getText()).toEqual('Click me too! (child)');
|
expect(childDiv.getText()).toEqual('Click me too! (child)');
|
||||||
|
@ -12,7 +12,7 @@ export class AppComponent {
|
|||||||
clickMessage = '';
|
clickMessage = '';
|
||||||
|
|
||||||
onSave(event?: KeyboardEvent) {
|
onSave(event?: KeyboardEvent) {
|
||||||
const evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : '';
|
const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
|
||||||
alert('Saved.' + evtMsg);
|
alert('Saved.' + evtMsg);
|
||||||
if (event) { event.stopPropagation(); }
|
if (event) { event.stopPropagation(); }
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@ export class AppComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClickMe(event?: KeyboardEvent) {
|
onClickMe(event?: KeyboardEvent) {
|
||||||
const evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
|
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
|
||||||
alert('Click me.' + evtMsg);
|
alert('Click me.' + evtMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* tslint:disable use-output-property-decorator directive-class-suffix */
|
// tslint:disable: directive-selector
|
||||||
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
|
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
|
||||||
|
|
||||||
@Directive({selector: '[myClick]'})
|
@Directive({selector: '[myClick]'})
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
'use strict'; // necessary for node!
|
|
||||||
|
|
||||||
import { browser, element, by, protractor, ElementFinder, ElementArrayFinder } from 'protractor';
|
import { browser, element, by, protractor, ElementFinder, ElementArrayFinder } from 'protractor';
|
||||||
|
|
||||||
// THESE TESTS ARE INCOMPLETE
|
// THESE TESTS ARE INCOMPLETE
|
||||||
describe('Form Validation Tests', function () {
|
describe('Form Validation Tests', () => {
|
||||||
|
|
||||||
beforeAll(function () {
|
beforeAll(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -52,11 +50,11 @@ let page: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function getPage(sectionTag: string) {
|
function getPage(sectionTag: string) {
|
||||||
let section = element(by.css(sectionTag));
|
const section = element(by.css(sectionTag));
|
||||||
let buttons = section.all(by.css('button'));
|
const buttons = section.all(by.css('button'));
|
||||||
|
|
||||||
page = {
|
page = {
|
||||||
section: section,
|
section,
|
||||||
form: section.element(by.css('form')),
|
form: section.element(by.css('form')),
|
||||||
title: section.element(by.css('h1')),
|
title: section.element(by.css('h1')),
|
||||||
nameInput: section.element(by.css('#name')),
|
nameInput: section.element(by.css('#name')),
|
||||||
@ -73,31 +71,31 @@ function getPage(sectionTag: string) {
|
|||||||
|
|
||||||
function tests(title: string) {
|
function tests(title: string) {
|
||||||
|
|
||||||
it('should display correct title', function () {
|
it('should display correct title', () => {
|
||||||
expect(page.title.getText()).toContain(title);
|
expect(page.title.getText()).toContain(title);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not display submitted message before submit', function () {
|
it('should not display submitted message before submit', () => {
|
||||||
expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(false);
|
expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have form buttons', function () {
|
it('should have form buttons', () => {
|
||||||
expect(page.heroFormButtons.count()).toEqual(2);
|
expect(page.heroFormButtons.count()).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have error at start', function () {
|
it('should have error at start', () => {
|
||||||
expectFormIsInvalid();
|
expectFormIsInvalid();
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('showForm', function () {
|
// it('showForm', () => {
|
||||||
// page.form.getInnerHtml().then(html => console.log(html));
|
// page.form.getInnerHtml().then(html => console.log(html));
|
||||||
// });
|
// });
|
||||||
|
|
||||||
it('should have disabled submit button', function () {
|
it('should have disabled submit button', () => {
|
||||||
expect(page.heroFormButtons.get(0).isEnabled()).toBe(false);
|
expect(page.heroFormButtons.get(0).isEnabled()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resetting name to valid name should clear errors', function () {
|
it('resetting name to valid name should clear errors', () => {
|
||||||
const ele = page.nameInput;
|
const ele = page.nameInput;
|
||||||
expect(ele.isPresent()).toBe(true, 'nameInput should exist');
|
expect(ele.isPresent()).toBe(true, 'nameInput should exist');
|
||||||
ele.clear();
|
ele.clear();
|
||||||
@ -105,7 +103,7 @@ function tests(title: string) {
|
|||||||
expectFormIsValid();
|
expectFormIsValid();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should produce "required" error after clearing name', function () {
|
it('should produce "required" error after clearing name', () => {
|
||||||
page.nameInput.clear();
|
page.nameInput.clear();
|
||||||
// page.alterEgoInput.click(); // to blur ... didn't work
|
// page.alterEgoInput.click(); // to blur ... didn't work
|
||||||
page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh!
|
page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh!
|
||||||
@ -113,37 +111,37 @@ function tests(title: string) {
|
|||||||
expect(page.errorMessages.get(0).getText()).toContain('required');
|
expect(page.errorMessages.get(0).getText()).toContain('required');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should produce "at least 4 characters" error when name="x"', function () {
|
it('should produce "at least 4 characters" error when name="x"', () => {
|
||||||
page.nameInput.clear();
|
page.nameInput.clear();
|
||||||
page.nameInput.sendKeys('x'); // too short
|
page.nameInput.sendKeys('x'); // too short
|
||||||
expectFormIsInvalid();
|
expectFormIsInvalid();
|
||||||
expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters');
|
expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resetting name to valid name again should clear errors', function () {
|
it('resetting name to valid name again should clear errors', () => {
|
||||||
page.nameInput.sendKeys(testName);
|
page.nameInput.sendKeys(testName);
|
||||||
expectFormIsValid();
|
expectFormIsValid();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have enabled submit button', function () {
|
it('should have enabled submit button', () => {
|
||||||
const submitBtn = page.heroFormButtons.get(0);
|
const submitBtn = page.heroFormButtons.get(0);
|
||||||
expect(submitBtn.isEnabled()).toBe(true);
|
expect(submitBtn.isEnabled()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide form after submit', function () {
|
it('should hide form after submit', () => {
|
||||||
page.heroFormButtons.get(0).click();
|
page.heroFormButtons.get(0).click();
|
||||||
expect(page.heroFormButtons.get(0).isDisplayed()).toBe(false);
|
expect(page.heroFormButtons.get(0).isDisplayed()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('submitted form should be displayed', function () {
|
it('submitted form should be displayed', () => {
|
||||||
expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(true);
|
expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('submitted form should have new hero name', function () {
|
it('submitted form should have new hero name', () => {
|
||||||
expect(page.heroSubmitted.getText()).toContain(testName);
|
expect(page.heroSubmitted.getText()).toContain(testName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clicking edit button should reveal form again', function () {
|
it('clicking edit button should reveal form again', () => {
|
||||||
const newFormBtn = page.heroSubmitted.element(by.css('button'));
|
const newFormBtn = page.heroSubmitted.element(by.css('button'));
|
||||||
newFormBtn.click();
|
newFormBtn.click();
|
||||||
expect(page.heroSubmitted.isElementPresent(by.css('p')))
|
expect(page.heroSubmitted.isElementPresent(by.css('p')))
|
||||||
@ -162,7 +160,7 @@ function expectFormIsInvalid() {
|
|||||||
|
|
||||||
function triggerAlterEgoValidation() {
|
function triggerAlterEgoValidation() {
|
||||||
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
|
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
|
||||||
element(by.css('app-root')).click()
|
element(by.css('app-root')).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForAlterEgoValidation() {
|
function waitForAlterEgoValidation() {
|
||||||
@ -173,7 +171,7 @@ function waitForAlterEgoValidation() {
|
|||||||
function bobTests() {
|
function bobTests() {
|
||||||
const emsg = 'Name cannot be Bob.';
|
const emsg = 'Name cannot be Bob.';
|
||||||
|
|
||||||
it('should produce "no bob" error after setting name to "Bobby"', function () {
|
it('should produce "no bob" error after setting name to "Bobby"', () => {
|
||||||
// Re-populate select element
|
// Re-populate select element
|
||||||
page.powerSelect.click();
|
page.powerSelect.click();
|
||||||
page.powerOption.click();
|
page.powerOption.click();
|
||||||
@ -184,7 +182,7 @@ function bobTests() {
|
|||||||
expect(page.errorMessages.get(0).getText()).toBe(emsg);
|
expect(page.errorMessages.get(0).getText()).toBe(emsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be ok again with valid name', function () {
|
it('should be ok again with valid name', () => {
|
||||||
page.nameInput.clear();
|
page.nameInput.clear();
|
||||||
page.nameInput.sendKeys(testName);
|
page.nameInput.sendKeys(testName);
|
||||||
expectFormIsValid();
|
expectFormIsValid();
|
||||||
@ -194,7 +192,7 @@ function bobTests() {
|
|||||||
function asyncValidationTests() {
|
function asyncValidationTests() {
|
||||||
const emsg = 'Alter ego is already taken.';
|
const emsg = 'Alter ego is already taken.';
|
||||||
|
|
||||||
it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () {
|
it(`should produce "${emsg}" error after setting alterEgo to Eric`, () => {
|
||||||
page.alterEgoInput.clear();
|
page.alterEgoInput.clear();
|
||||||
page.alterEgoInput.sendKeys('Eric');
|
page.alterEgoInput.sendKeys('Eric');
|
||||||
|
|
||||||
@ -205,7 +203,7 @@ function asyncValidationTests() {
|
|||||||
expect(page.alterEgoErrors.getText()).toBe(emsg);
|
expect(page.alterEgoErrors.getText()).toBe(emsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be ok again with different values', function () {
|
it('should be ok again with different values', () => {
|
||||||
page.alterEgoInput.clear();
|
page.alterEgoInput.clear();
|
||||||
page.alterEgoInput.sendKeys('John');
|
page.alterEgoInput.sendKeys('John');
|
||||||
|
|
||||||
@ -220,7 +218,7 @@ function asyncValidationTests() {
|
|||||||
function crossValidationTests() {
|
function crossValidationTests() {
|
||||||
const emsg = 'Name cannot match alter ego.';
|
const emsg = 'Name cannot match alter ego.';
|
||||||
|
|
||||||
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () {
|
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, () => {
|
||||||
page.nameInput.clear();
|
page.nameInput.clear();
|
||||||
page.nameInput.sendKeys('Batman');
|
page.nameInput.sendKeys('Batman');
|
||||||
|
|
||||||
@ -234,7 +232,7 @@ function crossValidationTests() {
|
|||||||
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be ok again with different values', function () {
|
it('should be ok again with different values', () => {
|
||||||
page.nameInput.clear();
|
page.nameInput.clear();
|
||||||
page.nameInput.sendKeys('Batman');
|
page.nameInput.sendKeys('Batman');
|
||||||
|
|
||||||
|
@ -22,13 +22,13 @@ export class HeroFormReactiveComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// #docregion custom-validator
|
// #docregion custom-validator
|
||||||
this.heroForm = new FormGroup({
|
this.heroForm = new FormGroup({
|
||||||
'name': new FormControl(this.hero.name, [
|
name: new FormControl(this.hero.name, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.minLength(4),
|
Validators.minLength(4),
|
||||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||||
]),
|
]),
|
||||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
alterEgo: new FormControl(this.hero.alterEgo),
|
||||||
'power': new FormControl(this.hero.power, Validators.required)
|
power: new FormControl(this.hero.power, Validators.required)
|
||||||
});
|
});
|
||||||
// #enddocregion custom-validator
|
// #enddocregion custom-validator
|
||||||
|
|
||||||
|
@ -22,16 +22,16 @@ export class HeroFormReactiveComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// #docregion async-validation
|
// #docregion async-validation
|
||||||
this.heroForm = new FormGroup({
|
this.heroForm = new FormGroup({
|
||||||
'name': new FormControl(this.hero.name, [
|
name: new FormControl(this.hero.name, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.minLength(4),
|
Validators.minLength(4),
|
||||||
forbiddenNameValidator(/bob/i)
|
forbiddenNameValidator(/bob/i)
|
||||||
]),
|
]),
|
||||||
'alterEgo': new FormControl(this.hero.alterEgo, {
|
alterEgo: new FormControl(this.hero.alterEgo, {
|
||||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||||
updateOn: 'blur'
|
updateOn: 'blur'
|
||||||
}),
|
}),
|
||||||
'power': new FormControl(this.hero.power, Validators.required)
|
power: new FormControl(this.hero.power, Validators.required)
|
||||||
});
|
});
|
||||||
// #enddocregion async-validation
|
// #enddocregion async-validation
|
||||||
}
|
}
|
||||||
|
@ -21,16 +21,16 @@ export class HeroFormReactiveComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.heroForm = new FormGroup({
|
this.heroForm = new FormGroup({
|
||||||
'name': new FormControl(this.hero.name, [
|
name: new FormControl(this.hero.name, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.minLength(4),
|
Validators.minLength(4),
|
||||||
forbiddenNameValidator(/bob/i)
|
forbiddenNameValidator(/bob/i)
|
||||||
]),
|
]),
|
||||||
'alterEgo': new FormControl(this.hero.alterEgo, {
|
alterEgo: new FormControl(this.hero.alterEgo, {
|
||||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||||
updateOn: 'blur'
|
updateOn: 'blur'
|
||||||
}),
|
}),
|
||||||
'power': new FormControl(this.hero.power, Validators.required)
|
power: new FormControl(this.hero.power, Validators.required)
|
||||||
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } fr
|
|||||||
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
||||||
return (control: AbstractControl): {[key: string]: any} | null => {
|
return (control: AbstractControl): {[key: string]: any} | null => {
|
||||||
const forbidden = nameRe.test(control.value);
|
const forbidden = nameRe.test(control.value);
|
||||||
return forbidden ? {'forbiddenName': {value: control.value}} : null;
|
return forbidden ? {forbiddenName: {value: control.value}} : null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// #enddocregion custom-validator
|
// #enddocregion custom-validator
|
||||||
|
@ -8,7 +8,7 @@ export const identityRevealedValidator: ValidatorFn = (control: FormGroup): Vali
|
|||||||
const name = control.get('name');
|
const name = control.get('name');
|
||||||
const alterEgo = control.get('alterEgo');
|
const alterEgo = control.get('alterEgo');
|
||||||
|
|
||||||
return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
|
return name && alterEgo && name.value === alterEgo.value ? { identityRevealed: true } : null;
|
||||||
};
|
};
|
||||||
// #enddocregion cross-validation-validator
|
// #enddocregion cross-validation-validator
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ export const identityRevealedValidator: ValidatorFn = (control: FormGroup): Vali
|
|||||||
})
|
})
|
||||||
export class IdentityRevealedValidatorDirective implements Validator {
|
export class IdentityRevealedValidatorDirective implements Validator {
|
||||||
validate(control: AbstractControl): ValidationErrors {
|
validate(control: AbstractControl): ValidationErrors {
|
||||||
return identityRevealedValidator(control)
|
return identityRevealedValidator(control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion cross-validation-directive
|
// #enddocregion cross-validation-directive
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Forms Overview Tests', function () {
|
describe('Forms Overview Tests', () => {
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import { TestBed, async } from '@angular/core/testing';
|
import { TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { TemplateModule } from './template/template.module';
|
|
||||||
import { ReactiveModule } from './reactive/reactive.module';
|
import { ReactiveModule } from './reactive/reactive.module';
|
||||||
|
import { TemplateModule } from './template/template.module';
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
beforeEach(async(() => {
|
TestBed
|
||||||
TestBed.configureTestingModule({
|
.configureTestingModule({
|
||||||
imports: [ReactiveModule, TemplateModule],
|
imports: [ReactiveModule, TemplateModule],
|
||||||
declarations: [
|
declarations: [AppComponent],
|
||||||
AppComponent
|
})
|
||||||
],
|
.compileComponents();
|
||||||
}).compileComponents();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should create the app', async(() => {
|
it('should create the app', waitForAsync(() => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
const app = fixture.componentInstance;
|
const app = fixture.componentInstance;
|
||||||
|
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should render title', async(() => {
|
it('should render title', waitForAsync(() => {
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { FavoriteColorComponent } from './favorite-color.component';
|
|
||||||
import { createNewEvent } from '../../shared/utils';
|
import { createNewEvent } from '../../shared/utils';
|
||||||
|
import { FavoriteColorComponent } from './favorite-color.component';
|
||||||
|
|
||||||
describe('Favorite Color Component', () => {
|
describe('Favorite Color Component', () => {
|
||||||
let component: FavoriteColorComponent;
|
let component: FavoriteColorComponent;
|
||||||
let fixture: ComponentFixture<FavoriteColorComponent>;
|
let fixture: ComponentFixture<FavoriteColorComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed
|
||||||
imports: [ ReactiveFormsModule ],
|
.configureTestingModule(
|
||||||
declarations: [ FavoriteColorComponent ]
|
{imports: [ReactiveFormsModule], declarations: [FavoriteColorComponent]})
|
||||||
})
|
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export function createNewEvent(eventName: string, bubbles = false, cancelable = false) {
|
export function createNewEvent(eventName: string, bubbles = false, cancelable = false) {
|
||||||
let evt = document.createEvent('CustomEvent');
|
const evt = document.createEvent('CustomEvent');
|
||||||
evt.initCustomEvent(eventName, bubbles, cancelable, null);
|
evt.initCustomEvent(eventName, bubbles, cancelable, null);
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { FavoriteColorComponent } from './favorite-color.component';
|
|
||||||
import { createNewEvent } from '../../shared/utils';
|
import { createNewEvent } from '../../shared/utils';
|
||||||
|
import { FavoriteColorComponent } from './favorite-color.component';
|
||||||
|
|
||||||
describe('FavoriteColorComponent', () => {
|
describe('FavoriteColorComponent', () => {
|
||||||
let component: FavoriteColorComponent;
|
let component: FavoriteColorComponent;
|
||||||
let fixture: ComponentFixture<FavoriteColorComponent>;
|
let fixture: ComponentFixture<FavoriteColorComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({imports: [FormsModule], declarations: [FavoriteColorComponent]})
|
||||||
imports: [ FormsModule ],
|
|
||||||
declarations: [ FavoriteColorComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -1,60 +1,60 @@
|
|||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Forms Tests', function () {
|
describe('Forms Tests', () => {
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display correct title', function () {
|
it('should display correct title', () => {
|
||||||
expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form');
|
expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not display message before submit', function () {
|
it('should not display message before submit', () => {
|
||||||
let ele = element(by.css('h2'));
|
const ele = element(by.css('h2'));
|
||||||
expect(ele.isDisplayed()).toBe(false);
|
expect(ele.isDisplayed()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide form after submit', function () {
|
it('should hide form after submit', () => {
|
||||||
let ele = element.all(by.css('h1')).get(0);
|
const ele = element.all(by.css('h1')).get(0);
|
||||||
expect(ele.isDisplayed()).toBe(true);
|
expect(ele.isDisplayed()).toBe(true);
|
||||||
let b = element.all(by.css('button[type=submit]')).get(0);
|
const b = element.all(by.css('button[type=submit]')).get(0);
|
||||||
b.click().then(function() {
|
b.click().then(() => {
|
||||||
expect(ele.isDisplayed()).toBe(false);
|
expect(ele.isDisplayed()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display message after submit', function () {
|
it('should display message after submit', () => {
|
||||||
let b = element.all(by.css('button[type=submit]')).get(0);
|
const b = element.all(by.css('button[type=submit]')).get(0);
|
||||||
b.click().then(function() {
|
b.click().then(() => {
|
||||||
expect(element(by.css('h2')).getText()).toContain('You submitted the following');
|
expect(element(by.css('h2')).getText()).toContain('You submitted the following');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide form after submit', function () {
|
it('should hide form after submit', () => {
|
||||||
let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
|
const alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
|
||||||
expect(alterEgoEle.isDisplayed()).toBe(true);
|
expect(alterEgoEle.isDisplayed()).toBe(true);
|
||||||
let submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
|
const submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
|
||||||
submitButtonEle.click().then(function() {
|
submitButtonEle.click().then(() => {
|
||||||
expect(alterEgoEle.isDisplayed()).toBe(false);
|
expect(alterEgoEle.isDisplayed()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reflect submitted data after submit', function () {
|
it('should reflect submitted data after submit', () => {
|
||||||
let test = 'testing 1 2 3';
|
const test = 'testing 1 2 3';
|
||||||
let newValue: string;
|
let newValue: string;
|
||||||
let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
|
const alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
|
||||||
alterEgoEle.getAttribute('value').then(function(value: string) {
|
alterEgoEle.getAttribute('value').then((value: string) => {
|
||||||
alterEgoEle.sendKeys(test);
|
alterEgoEle.sendKeys(test);
|
||||||
newValue = value + test;
|
newValue = value + test;
|
||||||
expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
|
expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
|
||||||
let b = element.all(by.css('button[type=submit]')).get(0);
|
const b = element.all(by.css('button[type=submit]')).get(0);
|
||||||
return b.click();
|
return b.click();
|
||||||
}).then(function() {
|
}).then(() => {
|
||||||
let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
|
const alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
|
||||||
expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
|
expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
|
||||||
let divEle = element(by.cssContainingText('div', newValue));
|
const divEle = element(by.cssContainingText('div', newValue));
|
||||||
expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
|
expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -35,7 +35,7 @@ export class HeroFormComponent {
|
|||||||
|
|
||||||
skyDog(): Hero {
|
skyDog(): Hero {
|
||||||
// #docregion SkyDog
|
// #docregion SkyDog
|
||||||
let myHero = new Hero(42, 'SkyDog',
|
const myHero = new Hero(42, 'SkyDog',
|
||||||
'Fetch any object at any distance',
|
'Fetch any object at any distance',
|
||||||
'Leslie Rollover');
|
'Leslie Rollover');
|
||||||
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
|
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
|
||||||
@ -48,9 +48,9 @@ export class HeroFormComponent {
|
|||||||
// Reveal in html:
|
// Reveal in html:
|
||||||
// Name via form.controls = {{showFormControls(heroForm)}}
|
// Name via form.controls = {{showFormControls(heroForm)}}
|
||||||
showFormControls(form: any) {
|
showFormControls(form: any) {
|
||||||
return form && form.controls['name'] &&
|
return form && form.controls.name &&
|
||||||
// #docregion form-controls
|
// #docregion form-controls
|
||||||
form.controls['name'].value; // Dr. IQ
|
form.controls.name.value; // Dr. IQ
|
||||||
// #enddocregion form-controls
|
// #enddocregion form-controls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
describe('Getting Started V0', () => {
|
describe('Getting Started V0', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => browser.get('/'));
|
||||||
return browser.get('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display "My Store" in the top bar', async () => {
|
it('should display "My Store" in the top bar', async () => {
|
||||||
const title = await element(by.css('app-root app-top-bar h1')).getText();
|
const title = await element(by.css('app-root app-top-bar h1')).getText();
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, element, by, ExpectedConditions as EC, logging, ElementFinder, ElementArrayFinder } from 'protractor';
|
import { browser, element, by, ExpectedConditions as EC, logging, ElementFinder, ElementArrayFinder } from 'protractor';
|
||||||
|
|
||||||
describe('Getting Started', () => {
|
describe('Getting Started', () => {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, by, element } from 'protractor';
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
describe('Hierarchical dependency injection', () => {
|
describe('Hierarchical dependency injection', () => {
|
||||||
@ -9,7 +7,7 @@ describe('Hierarchical dependency injection', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Heroes Scenario', () => {
|
describe('Heroes Scenario', () => {
|
||||||
let page = {
|
const page = {
|
||||||
heroName: '',
|
heroName: '',
|
||||||
income: '',
|
income: '',
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// tslint:disable: no-output-native
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { HeroTaxReturn } from './hero';
|
import { HeroTaxReturn } from './hero';
|
||||||
@ -30,9 +31,9 @@ export class HeroTaxReturnComponent {
|
|||||||
onCanceled() {
|
onCanceled() {
|
||||||
this.flashMessage('Canceled');
|
this.flashMessage('Canceled');
|
||||||
this.heroTaxReturnService.restoreTaxReturn();
|
this.heroTaxReturnService.restoreTaxReturn();
|
||||||
};
|
}
|
||||||
|
|
||||||
onClose() { this.close.emit(); };
|
onClose() { this.close.emit(); }
|
||||||
|
|
||||||
onSaved() {
|
onSaved() {
|
||||||
this.flashMessage('Saved');
|
this.flashMessage('Saved');
|
||||||
|
@ -24,11 +24,11 @@ const page = {
|
|||||||
uploadMessage: element(by.css('app-uploader p'))
|
uploadMessage: element(by.css('app-uploader p'))
|
||||||
};
|
};
|
||||||
|
|
||||||
let checkLogForMessage = (message: string) => {
|
const checkLogForMessage = (message: string) => {
|
||||||
expect(page.logList.getText()).toContain(message);
|
expect(page.logList.getText()).toContain(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Http Tests', function() {
|
describe('Http Tests', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
browser.get('');
|
browser.get('');
|
||||||
});
|
});
|
||||||
|
@ -40,8 +40,8 @@ export class ConfigComponent {
|
|||||||
this.configService.getConfig_1()
|
this.configService.getConfig_1()
|
||||||
// #docregion v1, v1_callback
|
// #docregion v1, v1_callback
|
||||||
.subscribe((data: Config) => this.config = {
|
.subscribe((data: Config) => this.config = {
|
||||||
heroesUrl: data['heroesUrl'],
|
heroesUrl: data.heroesUrl,
|
||||||
textfile: data['textfile']
|
textfile: data.textfile
|
||||||
});
|
});
|
||||||
// #enddocregion v1_callback
|
// #enddocregion v1_callback
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import { HttpErrorHandler, HandleError } from '../http-error-handler.service';
|
|||||||
const httpOptions = {
|
const httpOptions = {
|
||||||
headers: new HttpHeaders({
|
headers: new HttpHeaders({
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': 'my-auth-token'
|
Authorization: 'my-auth-token'
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
// #enddocregion http-options
|
// #enddocregion http-options
|
||||||
|
@ -15,8 +15,10 @@ export class HttpErrorHandler {
|
|||||||
constructor(private messageService: MessageService) { }
|
constructor(private messageService: MessageService) { }
|
||||||
|
|
||||||
/** Create curried handleError function that already knows the service name */
|
/** Create curried handleError function that already knows the service name */
|
||||||
createHandleError = (serviceName = '') => <T>
|
createHandleError = (serviceName = '') => {
|
||||||
(operation = 'operation', result = {} as T) => this.handleError(serviceName, operation, result);
|
return <T>(operation = 'operation', result = {} as T) =>
|
||||||
|
this.handleError(serviceName, operation, result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a function that handles Http operation failures.
|
* Returns a function that handles Http operation failures.
|
||||||
|
@ -12,7 +12,7 @@ export interface RequestCacheEntry {
|
|||||||
// #docregion request-cache
|
// #docregion request-cache
|
||||||
export abstract class RequestCache {
|
export abstract class RequestCache {
|
||||||
abstract get(req: HttpRequest<any>): HttpResponse<any> | undefined;
|
abstract get(req: HttpRequest<any>): HttpResponse<any> | undefined;
|
||||||
abstract put(req: HttpRequest<any>, response: HttpResponse<any>): void
|
abstract put(req: HttpRequest<any>, response: HttpResponse<any>): void;
|
||||||
}
|
}
|
||||||
// #enddocregion request-cache
|
// #enddocregion request-cache
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ export class RequestCacheWithMap implements RequestCache {
|
|||||||
const url = req.urlWithParams;
|
const url = req.urlWithParams;
|
||||||
this.messenger.add(`Caching response from "${url}".`);
|
this.messenger.add(`Caching response from "${url}".`);
|
||||||
|
|
||||||
const entry = { url, response, lastRead: Date.now() };
|
const newEntry = { url, response, lastRead: Date.now() };
|
||||||
this.cache.set(url, entry);
|
this.cache.set(url, newEntry);
|
||||||
|
|
||||||
// remove expired cache entries
|
// remove expired cache entries
|
||||||
const expired = Date.now() - maxAge;
|
const expired = Date.now() - maxAge;
|
||||||
|
@ -23,12 +23,12 @@ bootstrap();
|
|||||||
|
|
||||||
//
|
//
|
||||||
function bootstrap() {
|
function bootstrap() {
|
||||||
if (window['jasmineRef']) {
|
if ((window as any).jasmineRef) {
|
||||||
location.reload();
|
location.reload();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
window.onload(undefined);
|
window.onload(undefined);
|
||||||
window['jasmineRef'] = jasmine.getEnv();
|
(window as any).jasmineRef = jasmine.getEnv();
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, initialize the Angular testing environment.
|
// First, initialize the Angular testing environment.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import jasmineRequire from 'jasmine-core/lib/jasmine-core/jasmine.js';
|
import jasmineRequire from 'jasmine-core/lib/jasmine-core/jasmine.js';
|
||||||
|
|
||||||
window['jasmineRequire'] = jasmineRequire;
|
(window as any).jasmineRequire = jasmineRequire;
|
||||||
|
@ -73,7 +73,7 @@ describe('HttpClient testing', () => {
|
|||||||
|
|
||||||
// Make an HTTP GET request with specific header
|
// Make an HTTP GET request with specific header
|
||||||
httpClient.get<Data>(testUrl, {
|
httpClient.get<Data>(testUrl, {
|
||||||
headers: new HttpHeaders({'Authorization': 'my-auth-token'})
|
headers: new HttpHeaders({Authorization: 'my-auth-token'})
|
||||||
})
|
})
|
||||||
.subscribe(data =>
|
.subscribe(data =>
|
||||||
expect(data).toEqual(testData)
|
expect(data).toEqual(testData)
|
||||||
@ -83,14 +83,14 @@ describe('HttpClient testing', () => {
|
|||||||
// #docregion predicate
|
// #docregion predicate
|
||||||
// Expect one request with an authorization header
|
// Expect one request with an authorization header
|
||||||
const req = httpTestingController.expectOne(
|
const req = httpTestingController.expectOne(
|
||||||
req => req.headers.has('Authorization')
|
request => request.headers.has('Authorization')
|
||||||
);
|
);
|
||||||
// #enddocregion predicate
|
// #enddocregion predicate
|
||||||
req.flush(testData);
|
req.flush(testData);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can test multiple requests', () => {
|
it('can test multiple requests', () => {
|
||||||
let testData: Data[] = [
|
const testData: Data[] = [
|
||||||
{ name: 'bob' }, { name: 'carol' },
|
{ name: 'bob' }, { name: 'carol' },
|
||||||
{ name: 'ted' }, { name: 'alice' }
|
{ name: 'ted' }, { name: 'alice' }
|
||||||
];
|
];
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
<!-- #enddocregion translated-plural -->
|
<!-- #enddocregion translated-plural -->
|
||||||
<!-- #docregion translated-select -->
|
<!-- #docregion translated-select -->
|
||||||
<!-- #docregion translate-select-1 -->
|
<!-- #docregion translate-select-1 -->
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="f99f34ac9bd4606345071bd813858dec29f3b7d1" datatype="html">
|
<trans-unit id="f99f34ac9bd4606345071bd813858dec29f3b7d1" datatype="html">
|
||||||
<source>The author is <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></source>
|
<source>The author is <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></source>
|
||||||
<target>L'auteur est <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></target>
|
<target>L'auteur est <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></target>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user