Compare commits
187 Commits
Author | SHA1 | Date | |
---|---|---|---|
793a001d7c | |||
5c3774cfe6 | |||
12266b2042 | |||
e385abc83c | |||
933cbfb828 | |||
c5e725111d | |||
93993864b1 | |||
26f49151e7 | |||
4b3f9ac739 | |||
80604d3a76 | |||
e615a10371 | |||
c8f2ca2349 | |||
a67afcc932 | |||
fd7698253e | |||
32de025dce | |||
a4e1768a74 | |||
b336871303 | |||
92fa6399f7 | |||
3992341d34 | |||
8c559ef104 | |||
0bac2b062c | |||
58d028178f | |||
e06512b22f | |||
603b0944d5 | |||
918e628f9b | |||
468cf69c55 | |||
c8f3fa9f3e | |||
78136cc3a7 | |||
66724fd159 | |||
8e7f9033a3 | |||
c8f9092364 | |||
bc995835b9 | |||
1bfa908ab3 | |||
2479f7d7ef | |||
333b8679ad | |||
5da621d3dd | |||
cbed582a1a | |||
d5aa6b5bd6 | |||
33eee43263 | |||
72053b0f27 | |||
d20ef47b16 | |||
bdc05aef64 | |||
798d959bee | |||
3570aaa363 | |||
421b6a97d6 | |||
b28a5f6eef | |||
52ab9397a0 | |||
b99e539b0e | |||
fd2ef74a31 | |||
cbc25bbf31 | |||
7385b2cf25 | |||
0daa48800e | |||
ffa4e11db1 | |||
41a41e741d | |||
c5384aee58 | |||
b02c950aac | |||
2eaa304aba | |||
9faf2e4a96 | |||
0e7a89a391 | |||
af4269471d | |||
915a6c17b9 | |||
93b32d3023 | |||
bd82b34f79 | |||
c626e90ab0 | |||
c6bbb0d8e5 | |||
f48a065db0 | |||
aeb6d0d0f7 | |||
655c9e386b | |||
2ad20b953f | |||
7059eaf2d2 | |||
939fda410f | |||
35ab6acab3 | |||
f4ee067cb8 | |||
068e28381d | |||
dff52ecb11 | |||
56af303dc3 | |||
136596ddb4 | |||
e515c913d9 | |||
31eaf78035 | |||
b0d680daa0 | |||
93cbef26c7 | |||
e2b221b8fa | |||
7a33c456d3 | |||
61db817eed | |||
4894220acf | |||
74b7a8eaf5 | |||
bfa55162de | |||
776f991c1a | |||
d9d1a1e682 | |||
867ff14a4a | |||
879457ca05 | |||
efbe4ad28a | |||
64f72c0600 | |||
81c05bfa01 | |||
56abcf088d | |||
6818432cd0 | |||
79bfb037df | |||
0fce20d79d | |||
9f486c31d2 | |||
d36066dd6c | |||
a631b99c69 | |||
78211c42ea | |||
5ff5a11509 | |||
e80047e897 | |||
eee5eeae76 | |||
c3ce1903b6 | |||
21da0346c7 | |||
af2d74fde9 | |||
8de62a9811 | |||
26e28453d6 | |||
8ea30b662f | |||
cb0a2a055d | |||
14ae3c0a21 | |||
90cae34c05 | |||
7bb35889fa | |||
03a3c753bb | |||
74b1464dff | |||
ff523c9ec9 | |||
392ef93c2b | |||
29dcb5bf68 | |||
de594ca221 | |||
3545a49a79 | |||
f4681b6e40 | |||
fb16557381 | |||
61c9da5542 | |||
dbb1eb0ffe | |||
002b81c0b0 | |||
beb3f19dc0 | |||
e8f3c47b03 | |||
c58e6ba13a | |||
61e5ab4703 | |||
10385c27da | |||
0ab244f39e | |||
eafa5260db | |||
15cffa210c | |||
df8e45a193 | |||
bb8744db94 | |||
43bad87ae1 | |||
c3b297a318 | |||
6ea232eb3c | |||
0baab0f3db | |||
e53b686375 | |||
5c28af0e74 | |||
60adc35d30 | |||
024cc3b84a | |||
ac17142001 | |||
8d7066223f | |||
0cb3c04128 | |||
2649794e65 | |||
bf426c5f0d | |||
aa6b9f0382 | |||
822b420a11 | |||
294c6297d7 | |||
58fc65d198 | |||
b6bd8d7572 | |||
b08168bb90 | |||
407fa42679 | |||
aef432384a | |||
fb70083339 | |||
c9c2408176 | |||
e066bddfe9 | |||
447a600477 | |||
70f9bfff43 | |||
57c02b044c | |||
6defe962c8 | |||
267bcb3e9c | |||
b0b66881b4 | |||
9ff8d78bcd | |||
563b707497 | |||
5357e643b3 | |||
f71d132f7c | |||
ba3edda230 | |||
0767d37c07 | |||
8ba24578bc | |||
133a97ad67 | |||
4e67a3ab3f | |||
377f0010fc | |||
6e09129e4c | |||
d80e51a6b1 | |||
feb66b00da | |||
cb19eac105 | |||
6e0564ade6 | |||
05eeb7d279 | |||
2ce5fa3cce | |||
e140cdcb34 | |||
14b2db1d43 | |||
2afc7e982e |
7
.bazelrc
7
.bazelrc
@ -33,11 +33,6 @@ build --incompatible_strict_action_env
|
|||||||
run --incompatible_strict_action_env
|
run --incompatible_strict_action_env
|
||||||
test --incompatible_strict_action_env
|
test --incompatible_strict_action_env
|
||||||
|
|
||||||
# Do not build runfile trees by default. If an execution strategy relies on runfile
|
|
||||||
# symlink teee, the tree is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
|
|
||||||
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df
|
|
||||||
build --nobuild_runfile_links
|
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Release support #
|
# Release support #
|
||||||
# Turn on these settings with #
|
# Turn on these settings with #
|
||||||
@ -47,7 +42,7 @@ build --nobuild_runfile_links
|
|||||||
# Releases should always be stamped with version control info
|
# Releases should always be stamped with version control info
|
||||||
# This command assumes node on the path and is a workaround for
|
# This command assumes node on the path and is a workaround for
|
||||||
# https://github.com/bazelbuild/bazel/issues/4802
|
# https://github.com/bazelbuild/bazel/issues/4802
|
||||||
build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp"
|
build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js"
|
||||||
build:release --stamp
|
build:release --stamp
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
3.2.0
|
2.1.1
|
||||||
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
||||||
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
||||||
|
@ -12,7 +12,7 @@ We use this as a symmetric AES encryption key to encrypt tokens like
|
|||||||
a GitHub token that enables publishing snapshots.
|
a GitHub token that enables publishing snapshots.
|
||||||
|
|
||||||
To create the github_token file, we take this approach:
|
To create the github_token file, we take this approach:
|
||||||
- Find the angular-builds:token in the internal pw database
|
- Find the angular-builds:token in http://valentine
|
||||||
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
|
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
|
||||||
- echo "https://[token]:@github.com" > credentials
|
- echo "https://[token]:@github.com" > credentials
|
||||||
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
||||||
|
@ -19,12 +19,4 @@ build --local_ram_resources=14336
|
|||||||
|
|
||||||
# All build executed remotely should be done using our RBE configuration.
|
# All build executed remotely should be done using our RBE configuration.
|
||||||
build:remote --google_default_credentials
|
build:remote --google_default_credentials
|
||||||
|
|
||||||
# Upload to GCP's Build Status viewer to allow for us to have better viewing of execution/build
|
|
||||||
# logs. This is only done on CI as the BES (GCP's Build Status viewer) API requires credentials
|
|
||||||
# from service accounts, rather than end user accounts.
|
|
||||||
build:remote --bes_backend=buildeventservice.googleapis.com
|
|
||||||
build:remote --bes_timeout=30s
|
|
||||||
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
|
||||||
|
|
||||||
build --config=remote
|
build --config=remote
|
||||||
|
@ -22,18 +22,18 @@ version: 2.1
|
|||||||
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||||
var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
var_3: &cache_key v6-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
||||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
||||||
# be slower due to its growing size.
|
# be slower due to its growing size.
|
||||||
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
var_4: &cache_key_fallback v6-angular-node-12-{{ checksum ".bazelversion" }}
|
||||||
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
var_3_win: &cache_key_win v6-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
var_4_win: &cache_key_win_fallback v6-angular-win-node-12-{{ checksum ".bazelversion" }}
|
||||||
|
|
||||||
# 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-189d98e8b01b33974328255f085de04251d61567
|
var_5: &components_repo_unit_tests_cache_key v6-angular-components-598db096e668aa7e9debd56eedfd127b7a55e371
|
||||||
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
|
var_6: &components_repo_unit_tests_cache_key_fallback v6-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`.
|
||||||
@ -67,6 +67,9 @@ var_10: &only_on_master
|
|||||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||||
|
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||||
|
# `--versions.chrome` arg in `integration/bazel-schematics/test.sh` specifies a
|
||||||
|
# ChromeDriver version that is compatible with the Chrome version in the image.
|
||||||
executors:
|
executors:
|
||||||
default-executor:
|
default-executor:
|
||||||
parameters:
|
parameters:
|
||||||
@ -117,7 +120,7 @@ commands:
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
|
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
|
||||||
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
|
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
|
||||||
# which are dependencies of chrome & needed for karma & protractor headless chrome tests.
|
# which are dependendies of chrome & needed for karma & protractor headless chrome tests.
|
||||||
# This is a very small install which takes around 7s in comparing to using the full
|
# This is a very small install which takes around 7s in comparing to using the full
|
||||||
# circleci/node:x.x.x-browsers image.
|
# circleci/node:x.x.x-browsers image.
|
||||||
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
||||||
@ -160,7 +163,7 @@ commands:
|
|||||||
description: Sets up a domain that resolves to the local host.
|
description: Sets up a domain that resolves to the local host.
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: Preparing environment for running tests on Sauce Labs.
|
name: Preparing environment for running tests on Saucelabs.
|
||||||
command: |
|
command: |
|
||||||
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
||||||
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
||||||
@ -172,13 +175,13 @@ commands:
|
|||||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||||
- run:
|
- run:
|
||||||
# Sets up a local domain in the machine's host file that resolves to the local
|
# Sets up a local domain in the machine's host file that resolves to the local
|
||||||
# host. This domain is helpful in Sauce Labs tests where devices are not able to
|
# host. This domain is helpful in Saucelabs tests where devices are not able to
|
||||||
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
||||||
name: Setting up alias domain for local host.
|
name: Setting up alias domain for local host.
|
||||||
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
||||||
|
|
||||||
# Normally this would be an individual job instead of a command.
|
# Normally this would be an individual job instead of a command.
|
||||||
# But startup and setup time for each individual windows job are high enough to discourage
|
# But startup and setup time for each invidual windows job are high enough to discourage
|
||||||
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
||||||
setup_win:
|
setup_win:
|
||||||
description: Setup windows node environment
|
description: Setup windows node environment
|
||||||
@ -233,7 +236,7 @@ jobs:
|
|||||||
git config user.name "angular-ci"
|
git config user.name "angular-ci"
|
||||||
git config user.email "angular-ci"
|
git config user.email "angular-ci"
|
||||||
# Rebase PR on top of target branch.
|
# Rebase PR on top of target branch.
|
||||||
node tools/rebase-pr.js
|
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||||
else
|
else
|
||||||
echo "This build is not over a PR, nothing to do."
|
echo "This build is not over a PR, nothing to do."
|
||||||
fi
|
fi
|
||||||
@ -269,8 +272,13 @@ jobs:
|
|||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
|
|
||||||
- run: yarn -s tslint
|
- run: 'yarn bazel:format -mode=check ||
|
||||||
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
|
||||||
|
# Run the skylark linter to check our Bazel rules
|
||||||
|
- run: 'yarn bazel:lint ||
|
||||||
|
(echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'
|
||||||
|
|
||||||
|
- run: yarn -s lint --branch $CI_GIT_BASE_REVISION
|
||||||
- run: yarn -s ts-circular-deps:check
|
- run: yarn -s ts-circular-deps:check
|
||||||
- run: yarn -s ng-dev pullapprove verify
|
- run: yarn -s ng-dev pullapprove verify
|
||||||
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
||||||
@ -381,6 +389,10 @@ jobs:
|
|||||||
- custom_attach_workspace
|
- custom_attach_workspace
|
||||||
- init_environment
|
- init_environment
|
||||||
- install_chrome_libs
|
- install_chrome_libs
|
||||||
|
# Compile dependencies to ivy
|
||||||
|
# Running `ngcc` here (instead of implicitly via `ng build`) allows us to take advantage of
|
||||||
|
# the parallel, async mode speed-up (~20-25s on CI).
|
||||||
|
- run: yarn --cwd aio ngcc --properties es2015
|
||||||
# Build aio
|
# Build aio
|
||||||
- run: yarn --cwd aio build --progress=false
|
- run: yarn --cwd aio build --progress=false
|
||||||
# Lint the code
|
# Lint the code
|
||||||
@ -596,8 +608,8 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Decrypt github credentials
|
name: Decrypt github credentials
|
||||||
# We need ensure that the same default digest is used for encoding and decoding with
|
# We need ensure that the same default digest is used for encoding and decoding with
|
||||||
# OpenSSL. OpenSSL versions might have different default digests which can cause
|
# openssl. Openssl versions might have different default digests which can cause
|
||||||
# decryption failures based on the installed OpenSSL version. https://stackoverflow.com/a/39641378/4317734
|
# decryption failures based on the installed openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||||
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
||||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
- run: ./scripts/ci/publish-build-artifacts.sh
|
||||||
|
|
||||||
|
@ -5,6 +5,13 @@ readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
|
|||||||
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
||||||
readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
|
readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
|
||||||
|
|
||||||
|
if [ -f $bashEnvCachePath ]; then
|
||||||
|
# Since a bash env cache is present, load this into the $BASH_ENV
|
||||||
|
cat "$bashEnvCachePath" >> $BASH_ENV;
|
||||||
|
echo "BASH environment loaded from cached value at $bashEnvCachePath";
|
||||||
|
else
|
||||||
|
# Since no bash env cache is present, build out $BASH_ENV values.
|
||||||
|
|
||||||
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
||||||
source $envHelpersPath;
|
source $envHelpersPath;
|
||||||
echo "source $envHelpersPath;" >> $BASH_ENV;
|
echo "source $envHelpersPath;" >> $BASH_ENV;
|
||||||
@ -15,7 +22,6 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
setPublicVar CI "$CI"
|
|
||||||
setPublicVar PROJECT_ROOT "$projectDir";
|
setPublicVar PROJECT_ROOT "$projectDir";
|
||||||
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
||||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
||||||
@ -30,8 +36,9 @@ setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION";
|
|||||||
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
||||||
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
||||||
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
||||||
setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME";
|
|
||||||
setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME";
|
# Store a PR's refs and shas so they don't need to be requested multiple times.
|
||||||
|
setPublicVar GITHUB_REFS_AND_SHAS $(node tools/utils/get-refs-and-shas-for-target.js ${CIRCLE_PR_NUMBER:-false} | awk '{ gsub(/"/,"\\\"") } 1');
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
@ -74,7 +81,11 @@ 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 "189d98e8b01b33974328255f085de04251d61567"
|
setPublicVar COMPONENTS_REPO_COMMIT "598db096e668aa7e9debd56eedfd127b7a55e371"
|
||||||
|
|
||||||
|
# Save the created BASH_ENV into the bash env cache file.
|
||||||
|
cat "$BASH_ENV" >> $bashEnvCachePath;
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
@ -60,12 +60,11 @@ if (require.resolve === module) {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
function _main(args) {
|
function _main(args) {
|
||||||
triggerWebhook(...args)
|
triggerWebhook(...args).
|
||||||
.then(
|
then(({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
||||||
({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
|
||||||
console.log(`Status: ${statusCode}\n${responseText}`) :
|
console.log(`Status: ${statusCode}\n${responseText}`) :
|
||||||
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`)))
|
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
|
||||||
.catch(err => {
|
catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
@ -78,12 +77,15 @@ function postJson(url, data) {
|
|||||||
const statusCode = res.statusCode || -1;
|
const statusCode = res.statusCode || -1;
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
|
|
||||||
res.on('error', reject)
|
res.
|
||||||
.on('data', d => responseText += d)
|
on('error', reject).
|
||||||
.on('end', () => resolve({statusCode, responseText}));
|
on('data', d => responseText += d).
|
||||||
|
on('end', () => resolve({statusCode, responseText}));
|
||||||
};
|
};
|
||||||
|
|
||||||
request(url, opts, onResponse).on('error', reject).end(JSON.stringify(data));
|
request(url, opts, onResponse).
|
||||||
|
on('error', reject).
|
||||||
|
end(JSON.stringify(data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
.dev-infra.json
Normal file
47
.dev-infra.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"commitMessage": {
|
||||||
|
"maxLength": 120,
|
||||||
|
"minBodyLength": 0,
|
||||||
|
"types": [
|
||||||
|
"build",
|
||||||
|
"ci",
|
||||||
|
"docs",
|
||||||
|
"feat",
|
||||||
|
"fix",
|
||||||
|
"perf",
|
||||||
|
"refactor",
|
||||||
|
"release",
|
||||||
|
"style",
|
||||||
|
"test"
|
||||||
|
],
|
||||||
|
"scopes": [
|
||||||
|
"animations",
|
||||||
|
"bazel",
|
||||||
|
"benchpress",
|
||||||
|
"changelog",
|
||||||
|
"common",
|
||||||
|
"compiler",
|
||||||
|
"compiler-cli",
|
||||||
|
"core",
|
||||||
|
"dev-infra",
|
||||||
|
"docs-infra",
|
||||||
|
"elements",
|
||||||
|
"forms",
|
||||||
|
"http",
|
||||||
|
"language-service",
|
||||||
|
"localize",
|
||||||
|
"ngcc",
|
||||||
|
"packaging",
|
||||||
|
"platform-browser",
|
||||||
|
"platform-browser-dynamic",
|
||||||
|
"platform-server",
|
||||||
|
"platform-webworker",
|
||||||
|
"platform-webworker-dynamic",
|
||||||
|
"router",
|
||||||
|
"service-worker",
|
||||||
|
"upgrade",
|
||||||
|
"ve",
|
||||||
|
"zone.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
2
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@ -32,7 +32,7 @@ Existing issues often contain information about workarounds, resolution, or prog
|
|||||||
|
|
||||||
## 🔬 Minimal Reproduction
|
## 🔬 Minimal Reproduction
|
||||||
<!--
|
<!--
|
||||||
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-ivy
|
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-issue-repro2
|
||||||
-->
|
-->
|
||||||
<!-- ✍️--> https://stackblitz.com/...
|
<!-- ✍️--> https://stackblitz.com/...
|
||||||
|
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
|
||||||
|
|
||||||
// The configuration for `ng-dev commit-message` commands.
|
|
||||||
const commitMessage = {
|
|
||||||
'maxLength': 120,
|
|
||||||
'minBodyLength': 100,
|
|
||||||
'minBodyLengthTypeExcludes': ['docs'],
|
|
||||||
'types': [
|
|
||||||
'build',
|
|
||||||
'ci',
|
|
||||||
'docs',
|
|
||||||
'feat',
|
|
||||||
'fix',
|
|
||||||
'perf',
|
|
||||||
'refactor',
|
|
||||||
'release',
|
|
||||||
'style',
|
|
||||||
'test',
|
|
||||||
],
|
|
||||||
'scopes': [
|
|
||||||
'animations',
|
|
||||||
'bazel',
|
|
||||||
'benchpress',
|
|
||||||
'changelog',
|
|
||||||
'common',
|
|
||||||
'compiler',
|
|
||||||
'compiler-cli',
|
|
||||||
'core',
|
|
||||||
'dev-infra',
|
|
||||||
'docs-infra',
|
|
||||||
'elements',
|
|
||||||
'forms',
|
|
||||||
'http',
|
|
||||||
'language-service',
|
|
||||||
'localize',
|
|
||||||
'migrations',
|
|
||||||
'ngcc',
|
|
||||||
'packaging',
|
|
||||||
'platform-browser',
|
|
||||||
'platform-browser-dynamic',
|
|
||||||
'platform-server',
|
|
||||||
'platform-webworker',
|
|
||||||
'platform-webworker-dynamic',
|
|
||||||
'router',
|
|
||||||
'service-worker',
|
|
||||||
'upgrade',
|
|
||||||
've',
|
|
||||||
'zone.js',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// The configuration for `ng-dev format` commands.
|
|
||||||
const format = {
|
|
||||||
'clang-format': {
|
|
||||||
'matchers': [
|
|
||||||
'**/*.{js,ts}',
|
|
||||||
// TODO: burn down format failures and remove aio and integration exceptions.
|
|
||||||
'!aio/**',
|
|
||||||
'!integration/**',
|
|
||||||
// Both third_party and .yarn are directories containing copied code which should
|
|
||||||
// not be modified.
|
|
||||||
'!third_party/**',
|
|
||||||
'!.yarn/**',
|
|
||||||
// Do not format d.ts files as they are generated
|
|
||||||
'!**/*.d.ts',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'buildifier': true
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Github metadata information for `ng-dev` commands. */
|
|
||||||
const github = {
|
|
||||||
owner: 'angular',
|
|
||||||
name: 'angular',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configuration for the `ng-dev pr merge` command. The command can be used
|
|
||||||
// for merging upstream pull requests into branches based on a PR target label.
|
|
||||||
const merge = () => {
|
|
||||||
// TODO: resume dynamically determining patch branch
|
|
||||||
const patch = '10.0.x';
|
|
||||||
const config: MergeConfig = {
|
|
||||||
githubApiMerge: false,
|
|
||||||
claSignedLabel: 'cla: yes',
|
|
||||||
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
|
||||||
commitMessageFixupLabel: 'commit message fixup',
|
|
||||||
labels: [
|
|
||||||
{
|
|
||||||
pattern: 'PR target: master-only',
|
|
||||||
branches: ['master'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'PR target: patch-only',
|
|
||||||
branches: [patch],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: 'PR target: master & patch',
|
|
||||||
branches: ['master', patch],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
requiredBaseCommits: {
|
|
||||||
// PRs that target either `master` or the patch branch, need to be rebased
|
|
||||||
// on top of the latest commit message validation fix.
|
|
||||||
// These SHAs are the commits that update the required license text in the header.
|
|
||||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
|
||||||
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export function to build ng-dev configuration object.
|
|
||||||
module.exports = {
|
|
||||||
commitMessage,
|
|
||||||
format,
|
|
||||||
github,
|
|
||||||
merge,
|
|
||||||
};
|
|
297
.pullapprove.yml
297
.pullapprove.yml
@ -34,8 +34,41 @@
|
|||||||
####################################################################################
|
####################################################################################
|
||||||
# GitHub usernames
|
# GitHub usernames
|
||||||
####################################################################################
|
####################################################################################
|
||||||
# See reviewer list under `required-minimum-review` group. Team member names and
|
# aikidave - Dave Shevitz
|
||||||
# usernames are managed there.
|
# alan-agius4 - Alan Agius
|
||||||
|
# alxhub - Alex Rickabaugh
|
||||||
|
# AndrewKushnir - Andrew Kushnir
|
||||||
|
# andrewseguin - Andrew Seguin
|
||||||
|
# atscott - Andrew Scott
|
||||||
|
# ayazhafiz - Ayaz Hafiz
|
||||||
|
# clydin - Charles Lyding
|
||||||
|
# crisbeto - Kristiyan Kostadinov
|
||||||
|
# dennispbrown - Denny Brown
|
||||||
|
# devversion - Paul Gschwendtner
|
||||||
|
# dgp1130 - Doug Parker
|
||||||
|
# filipesilva - Filipe Silva
|
||||||
|
# gkalpak - Georgios Kalpakas
|
||||||
|
# gregmagolan - Greg Magolan
|
||||||
|
# IgorMinar - Igor Minar
|
||||||
|
# jbogarthyde - Judy Bogart
|
||||||
|
# jelbourn - Jeremy Elbourn
|
||||||
|
# JiaLiPassion - Jia Li
|
||||||
|
# JoostK - Joost Koehoorn
|
||||||
|
# josephperrott - Joey Perrott
|
||||||
|
# juleskremer - Jules Kremer
|
||||||
|
# kapunahelewong - Kapunahele Wong
|
||||||
|
# kara - Kara Erickson
|
||||||
|
# kyliau - Keen Yee Liau
|
||||||
|
# manughub - Manu Murthy
|
||||||
|
# matsko - Matias Niemela
|
||||||
|
# mgechev - Minko Gechev
|
||||||
|
# mhevery - Miško Hevery
|
||||||
|
# michaelprentice - Michael Prentice
|
||||||
|
# mmalerba - Miles Malerba
|
||||||
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
|
# pkozlowski-opensource - Pawel Kozlowski
|
||||||
|
# robwormald - Rob Wormald
|
||||||
|
# StephenFluin - Stephen Fluin
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
@ -47,8 +80,8 @@
|
|||||||
# Used for approving minor changes, large-scale refactorings, and in emergency situations.
|
# Used for approving minor changes, large-scale refactorings, and in emergency situations.
|
||||||
#
|
#
|
||||||
# IgorMinar
|
# IgorMinar
|
||||||
# jelbourn
|
|
||||||
# josephperrott
|
# josephperrott
|
||||||
|
# kara
|
||||||
# mhevery
|
# mhevery
|
||||||
#
|
#
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -67,16 +100,8 @@ 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:
|
||||||
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
1: &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"
|
2: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
||||||
defaults: &defaults
|
|
||||||
reviews:
|
|
||||||
# Authors provide their approval implicitly, this approval allows for a reviewer
|
|
||||||
# from a group not to need a review specifically for an area of the repository
|
|
||||||
# they own. This is coupled with the `required-minimum-review` group which requires
|
|
||||||
# that all PRs are reviewed by at least one team member who is not the author of
|
|
||||||
# the PR.
|
|
||||||
author_value: 1
|
|
||||||
|
|
||||||
# turn on 'draft' support
|
# turn on 'draft' support
|
||||||
# https://docs.pullapprove.com/config/github-api-version/
|
# https://docs.pullapprove.com/config/github-api-version/
|
||||||
@ -96,55 +121,6 @@ pullapprove_conditions:
|
|||||||
|
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
# =========================================================
|
|
||||||
# Require review on all PRs
|
|
||||||
#
|
|
||||||
# All PRs require at least one review. This rule will not
|
|
||||||
# request any reviewers, however will require that at least
|
|
||||||
# one review is provided before the group is satisfied.
|
|
||||||
# =========================================================
|
|
||||||
required-minimum-review:
|
|
||||||
reviews:
|
|
||||||
request: 0 # Do not request any reviews from the reviewer group
|
|
||||||
required: 1 # Require that all PRs have approval from at least one of the users in the group
|
|
||||||
author_value: 0 # The author of the PR cannot provide an approval for themself
|
|
||||||
reviewers:
|
|
||||||
users:
|
|
||||||
- aikidave # Dave Shevitz
|
|
||||||
- alan-agius4 # Alan Agius
|
|
||||||
- alxhub # Alex Rickabaugh
|
|
||||||
- AndrewKushnir # Andrew Kushnir
|
|
||||||
- andrewseguin # Andrew Seguin
|
|
||||||
- atscott # Andrew Scott
|
|
||||||
- ayazhafiz # Ayaz Hafiz
|
|
||||||
- clydin # Charles Lyding
|
|
||||||
- crisbeto # Kristiyan Kostadinov
|
|
||||||
- dennispbrown # Denny Brown
|
|
||||||
- devversion # Paul Gschwendtner
|
|
||||||
- dgp1130 # Doug Parker
|
|
||||||
- filipesilva # Filipe Silva
|
|
||||||
- gkalpak # Georgios Kalpakas
|
|
||||||
- gregmagolan # Greg Magolan
|
|
||||||
- IgorMinar # Igor Minar
|
|
||||||
- jbogarthyde # Judy Bogart
|
|
||||||
- jelbourn # Jeremy Elbourn
|
|
||||||
- JiaLiPassion # Jia Li
|
|
||||||
- JoostK # Joost Koehoorn
|
|
||||||
- josephperrott # Joey Perrott
|
|
||||||
- juleskremer # Jules Kremer
|
|
||||||
- kapunahelewong # Kapunahele Wong
|
|
||||||
- kara # Kara Erickson
|
|
||||||
- kyliau # Keen Yee Liau
|
|
||||||
- manughub # Manu Murthy
|
|
||||||
- matsko # Matias Niemela
|
|
||||||
- mgechev # Minko Gechev
|
|
||||||
- mhevery # Miško Hevery
|
|
||||||
- michaelprentice # Michael Prentice
|
|
||||||
- mmalerba # Miles Malerba
|
|
||||||
- petebacondarwin # Pete Bacon Darwin
|
|
||||||
- pkozlowski-opensource # Pawel Kozlowski
|
|
||||||
- StephenFluin # Stephen Fluin
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Global Approvers
|
# Global Approvers
|
||||||
#
|
#
|
||||||
@ -185,7 +161,6 @@ groups:
|
|||||||
# Framework: Animations
|
# Framework: Animations
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-animations:
|
fw-animations:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -210,12 +185,11 @@ groups:
|
|||||||
# Framework: Compiler
|
# Framework: Compiler
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-compiler:
|
fw-compiler:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files.exclude('packages/compiler-cli/ngcc/**'), [
|
contains_any_globs(files, [
|
||||||
'packages/compiler/**',
|
'packages/compiler/**',
|
||||||
'packages/examples/compiler/**',
|
'packages/examples/compiler/**',
|
||||||
'packages/compiler-cli/**',
|
'packages/compiler-cli/**',
|
||||||
@ -224,22 +198,29 @@ groups:
|
|||||||
'aio/content/guide/aot-metadata-errors.md',
|
'aio/content/guide/aot-metadata-errors.md',
|
||||||
'aio/content/guide/template-typecheck.md '
|
'aio/content/guide/template-typecheck.md '
|
||||||
])
|
])
|
||||||
|
- >
|
||||||
|
not contains_any_globs(files, [
|
||||||
|
'packages/compiler-cli/ngcc/**'
|
||||||
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- JoostK
|
- JoostK
|
||||||
|
- kara
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Compiler / ngcc
|
# Framework: Compiler / ngcc
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-ngcc:
|
fw-ngcc:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- files.include('packages/compiler-cli/ngcc/**')
|
- >
|
||||||
|
contains_any_globs(files, [
|
||||||
|
'packages/compiler-cli/ngcc/**'
|
||||||
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
@ -248,32 +229,15 @@ groups:
|
|||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Framework: Migrations
|
|
||||||
# =========================================================
|
|
||||||
fw-migrations:
|
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
|
||||||
- *can-be-global-approved
|
|
||||||
- *can-be-global-docs-approved
|
|
||||||
- files.include("packages/core/schematics/**")
|
|
||||||
reviewers:
|
|
||||||
users:
|
|
||||||
- alxhub
|
|
||||||
- crisbeto
|
|
||||||
- devversion
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Core
|
# Framework: Core
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-core:
|
fw-core:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files.exclude("packages/core/schematics/**"), [
|
contains_any_globs(files, [
|
||||||
'packages/core/**',
|
'packages/core/**',
|
||||||
'packages/examples/core/**',
|
'packages/examples/core/**',
|
||||||
'packages/common/**',
|
'packages/common/**',
|
||||||
@ -378,8 +342,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- atscott
|
- kara
|
||||||
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
|
||||||
- mhevery
|
- mhevery
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
@ -388,13 +351,13 @@ groups:
|
|||||||
# Framework: Http
|
# Framework: Http
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-http:
|
fw-http:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'packages/common/http/**',
|
'packages/common/http/**',
|
||||||
|
'packages/http/**',
|
||||||
'packages/examples/http/**',
|
'packages/examples/http/**',
|
||||||
'aio/content/guide/http.md',
|
'aio/content/guide/http.md',
|
||||||
'aio/content/examples/http/**',
|
'aio/content/examples/http/**',
|
||||||
@ -410,7 +373,6 @@ groups:
|
|||||||
# Framework: Elements
|
# Framework: Elements
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-elements:
|
fw-elements:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -431,7 +393,6 @@ groups:
|
|||||||
# Framework: Forms
|
# Framework: Forms
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-forms:
|
fw-forms:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -464,7 +425,6 @@ groups:
|
|||||||
# Framework: i18n
|
# Framework: i18n
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-i18n:
|
fw-i18n:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -498,7 +458,6 @@ groups:
|
|||||||
# Framework: Platform Server
|
# Framework: Platform Server
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-platform-server:
|
fw-platform-server:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -518,7 +477,6 @@ groups:
|
|||||||
# Framework: Router
|
# Framework: Router
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-router:
|
fw-router:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -527,8 +485,6 @@ groups:
|
|||||||
'packages/router/**',
|
'packages/router/**',
|
||||||
'packages/examples/router/**',
|
'packages/examples/router/**',
|
||||||
'aio/content/guide/router.md',
|
'aio/content/guide/router.md',
|
||||||
'aio/content/guide/router-tutorial.md',
|
|
||||||
'aio/content/examples/router-tutorial/**',
|
|
||||||
'aio/content/examples/router/**',
|
'aio/content/examples/router/**',
|
||||||
'aio/content/images/guide/router/**'
|
'aio/content/images/guide/router/**'
|
||||||
])
|
])
|
||||||
@ -540,8 +496,7 @@ groups:
|
|||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Service Worker
|
# Framework: Service Worker
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-service-worker:
|
fw-server-worker:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -569,7 +524,6 @@ groups:
|
|||||||
# Framework: Upgrade
|
# Framework: Upgrade
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-upgrade:
|
fw-upgrade:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -600,12 +554,11 @@ groups:
|
|||||||
# Framework: Testing
|
# Framework: Testing
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-testing:
|
fw-testing:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files.exclude('packages/compiler-cli/**'), [
|
contains_any_globs(files, [
|
||||||
'**/testing/**',
|
'**/testing/**',
|
||||||
'aio/content/guide/testing.md',
|
'aio/content/guide/testing.md',
|
||||||
'aio/content/examples/testing/**',
|
'aio/content/examples/testing/**',
|
||||||
@ -613,8 +566,8 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- AndrewKushnir
|
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
|
- kara
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
@ -622,7 +575,6 @@ groups:
|
|||||||
# Framework: Benchmarks
|
# Framework: Benchmarks
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-benchmarks:
|
fw-benchmarks:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -632,6 +584,7 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
|
- kara
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
@ -639,7 +592,6 @@ groups:
|
|||||||
# Framework: Playground
|
# Framework: Playground
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-playground:
|
fw-playground:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -649,15 +601,13 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
- kara
|
||||||
- pkozlowski-opensource
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Security
|
# Framework: Security
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-security:
|
fw-security:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -675,25 +625,18 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- mhevery
|
- mhevery
|
||||||
- jelbourn
|
|
||||||
- pkozlowski-opensource
|
|
||||||
reviews:
|
|
||||||
request: -1 # request reviews from everyone
|
|
||||||
required: 2 # require at least 2 approvals
|
|
||||||
reviewed_for: required
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Bazel
|
# Bazel
|
||||||
# =========================================================
|
# =========================================================
|
||||||
bazel:
|
bazel:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'packages/bazel/**',
|
'packages/bazel/**',
|
||||||
|
'aio/content/guide/bazel.md'
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
@ -706,7 +649,6 @@ groups:
|
|||||||
# Language Service
|
# Language Service
|
||||||
# =========================================================
|
# =========================================================
|
||||||
language-service:
|
language-service:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -726,7 +668,6 @@ groups:
|
|||||||
# zone.js
|
# zone.js
|
||||||
# =========================================================
|
# =========================================================
|
||||||
zone-js:
|
zone-js:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -745,7 +686,6 @@ groups:
|
|||||||
# Benchpress
|
# Benchpress
|
||||||
# =========================================================
|
# =========================================================
|
||||||
benchpress:
|
benchpress:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -756,14 +696,12 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
- josephperrott
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Integration Tests
|
# Integration Tests
|
||||||
# =========================================================
|
# =========================================================
|
||||||
integration-tests:
|
integration-tests:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -774,6 +712,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- josephperrott
|
- josephperrott
|
||||||
|
- kara
|
||||||
- mhevery
|
- mhevery
|
||||||
|
|
||||||
|
|
||||||
@ -781,7 +720,6 @@ groups:
|
|||||||
# Docs: Gettings Started & Tutorial
|
# Docs: Gettings Started & Tutorial
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-getting-started-and-tutorial:
|
docs-getting-started-and-tutorial:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -814,7 +752,6 @@ groups:
|
|||||||
# Docs: Marketing
|
# Docs: Marketing
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-marketing:
|
docs-marketing:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -829,15 +766,14 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- aikidave
|
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- StephenFluin
|
- StephenFluin
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Docs: Observables
|
# Docs: Observables
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-observables:
|
docs-observables:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -863,7 +799,6 @@ groups:
|
|||||||
# Docs: Packaging, Tooling, Releasing
|
# Docs: Packaging, Tooling, Releasing
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-packaging-and-releasing:
|
docs-packaging-and-releasing:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -883,47 +818,20 @@ groups:
|
|||||||
'aio/content/guide/migration-localize.md',
|
'aio/content/guide/migration-localize.md',
|
||||||
'aio/content/guide/migration-module-with-providers.md',
|
'aio/content/guide/migration-module-with-providers.md',
|
||||||
'aio/content/guide/static-query-migration.md',
|
'aio/content/guide/static-query-migration.md',
|
||||||
'aio/content/guide/updating-to-version-10.md',
|
'aio/content/guide/updating-to-version-9.md',
|
||||||
'aio/content/guide/ivy-compatibility.md',
|
'aio/content/guide/ivy-compatibility.md',
|
||||||
'aio/content/guide/ivy-compatibility-examples.md'
|
'aio/content/guide/ivy-compatibility-examples.md'
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
- kara
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Tooling: Compiler API shared with Angular CLI
|
|
||||||
#
|
|
||||||
# Changing this API might break Angular CLI, so we require
|
|
||||||
# the CLI team to approve changes here.
|
|
||||||
# =========================================================
|
|
||||||
tooling-cli-shared-api:
|
|
||||||
conditions:
|
|
||||||
- *can-be-global-approved
|
|
||||||
- *can-be-global-docs-approved
|
|
||||||
- >
|
|
||||||
contains_any_globs(files, [
|
|
||||||
'packages/compiler-cli/src/tooling.ts'
|
|
||||||
])
|
|
||||||
reviewers:
|
|
||||||
users:
|
|
||||||
- alan-agius4
|
|
||||||
- clydin
|
|
||||||
- kyliau
|
|
||||||
- IgorMinar
|
|
||||||
reviews:
|
|
||||||
request: -1 # request reviews from everyone
|
|
||||||
required: 2 # require at least 2 approvals
|
|
||||||
reviewed_for: required
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Docs: CLI
|
# Docs: CLI
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-cli:
|
docs-cli:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -940,12 +848,8 @@ groups:
|
|||||||
'aio/content/images/guide/deployment/**',
|
'aio/content/images/guide/deployment/**',
|
||||||
'aio/content/guide/file-structure.md',
|
'aio/content/guide/file-structure.md',
|
||||||
'aio/content/guide/ivy.md',
|
'aio/content/guide/ivy.md',
|
||||||
'aio/content/guide/strict-mode.md',
|
'aio/content/guide/web-worker.md'
|
||||||
'aio/content/guide/web-worker.md',
|
|
||||||
'aio/content/guide/workspace-config.md',
|
'aio/content/guide/workspace-config.md',
|
||||||
'aio/content/guide/migration-solution-style-tsconfig.md',
|
|
||||||
'aio/content/guide/migration-update-module-and-target-compiler-options.md',
|
|
||||||
'aio/content/guide/migration-update-libraries-tslib.md',
|
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
@ -958,7 +862,6 @@ groups:
|
|||||||
# Docs: CLI Libraries
|
# Docs: CLI Libraries
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-libraries:
|
docs-libraries:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -979,7 +882,6 @@ groups:
|
|||||||
# Docs: Schematics
|
# Docs: Schematics
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-schematics:
|
docs-schematics:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -1002,7 +904,6 @@ groups:
|
|||||||
# Docs-infra
|
# Docs-infra
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-infra:
|
docs-infra:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -1032,16 +933,14 @@ groups:
|
|||||||
# Dev-infra
|
# Dev-infra
|
||||||
# =========================================================
|
# =========================================================
|
||||||
dev-infra:
|
dev-infra:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files.exclude("CHANGELOG.md").exclude("packages/compiler-cli/**/BUILD.bazel"), [
|
contains_any_globs(files, [
|
||||||
'*',
|
'*',
|
||||||
'.circleci/**',
|
'.circleci/**',
|
||||||
'.devcontainer/**',
|
'.devcontainer/**',
|
||||||
'.github/**',
|
'.github/**',
|
||||||
'.ng-dev/**',
|
|
||||||
'.vscode/**',
|
'.vscode/**',
|
||||||
'.yarn/**',
|
'.yarn/**',
|
||||||
'dev-infra/**',
|
'dev-infra/**',
|
||||||
@ -1057,6 +956,8 @@ groups:
|
|||||||
'docs/TOOLS.md',
|
'docs/TOOLS.md',
|
||||||
'docs/TRIAGE_AND_LABELS.md',
|
'docs/TRIAGE_AND_LABELS.md',
|
||||||
'goldens/*',
|
'goldens/*',
|
||||||
|
'modules/e2e_util/e2e_util.ts',
|
||||||
|
'modules/e2e_util/perf_util.ts',
|
||||||
'modules/*',
|
'modules/*',
|
||||||
'packages/*',
|
'packages/*',
|
||||||
'packages/examples/test-utils/**',
|
'packages/examples/test-utils/**',
|
||||||
@ -1064,10 +965,15 @@ groups:
|
|||||||
'packages/examples/*',
|
'packages/examples/*',
|
||||||
'scripts/**',
|
'scripts/**',
|
||||||
'third_party/**',
|
'third_party/**',
|
||||||
|
'tools/brotli-cli/**',
|
||||||
|
'tools/browsers/**',
|
||||||
'tools/build/**',
|
'tools/build/**',
|
||||||
'tools/circular_dependency_test/**',
|
'tools/circular_dependency_test/**',
|
||||||
'tools/contributing-stats/**',
|
'tools/contributing-stats/**',
|
||||||
|
'tools/components/**',
|
||||||
'tools/gulp-tasks/**',
|
'tools/gulp-tasks/**',
|
||||||
|
'tools/ng_rollup_bundle/**',
|
||||||
|
'tools/ngcontainer/**',
|
||||||
'tools/npm/**',
|
'tools/npm/**',
|
||||||
'tools/npm_integration_test/**',
|
'tools/npm_integration_test/**',
|
||||||
'tools/rxjs/**',
|
'tools/rxjs/**',
|
||||||
@ -1097,13 +1003,11 @@ groups:
|
|||||||
# Public API
|
# Public API
|
||||||
# =========================================================
|
# =========================================================
|
||||||
public-api:
|
public-api:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'goldens/public-api/**',
|
'goldens/public-api/**',
|
||||||
'CHANGELOG.md',
|
|
||||||
'docs/NAMING.md',
|
'docs/NAMING.md',
|
||||||
'aio/content/guide/glossary.md',
|
'aio/content/guide/glossary.md',
|
||||||
'aio/content/guide/styleguide.md',
|
'aio/content/guide/styleguide.md',
|
||||||
@ -1112,56 +1016,41 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- alxhub
|
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
|
||||||
- pkozlowski-opensource
|
|
||||||
reviews:
|
|
||||||
request: -1 # request reviews from everyone
|
|
||||||
required: 3 # require at least 3 approvals
|
|
||||||
reviewed_for: required
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
# ================================================
|
||||||
# Size tracking
|
# Size tracking
|
||||||
# ================================================
|
# ================================================
|
||||||
size-tracking:
|
size-tracking:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'goldens/size-tracking/**'
|
'aio/scripts/_payload-limits.json',
|
||||||
|
'integration/_payload-limits.json'
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- alxhub
|
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
- kara
|
||||||
- pkozlowski-opensource
|
|
||||||
reviews:
|
|
||||||
request: -1 # request reviews from everyone
|
|
||||||
required: 2 # require at least 2 approvals
|
|
||||||
reviewed_for: required
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
# ================================================
|
||||||
# Circular dependencies
|
# Circular dependencies
|
||||||
# ================================================
|
# ================================================
|
||||||
circular-dependencies:
|
circular-dependencies:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'goldens/circular-deps/packages.json'
|
'goldens/packages-circular-deps.json'
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
|
||||||
- josephperrott
|
- josephperrott
|
||||||
- pkozlowski-opensource
|
- kara
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
@ -1172,7 +1061,6 @@ groups:
|
|||||||
# Code Ownership
|
# Code Ownership
|
||||||
# =========================================================
|
# =========================================================
|
||||||
code-ownership:
|
code-ownership:
|
||||||
<<: *defaults
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -1188,33 +1076,12 @@ groups:
|
|||||||
# Catch all for if no groups match the code change
|
# Catch all for if no groups match the code change
|
||||||
# ====================================================
|
# ====================================================
|
||||||
fallback:
|
fallback:
|
||||||
<<: *defaults
|
|
||||||
# A group is considered to be `active` for a PR if at least one of group's
|
|
||||||
# conditions matches the PR.
|
|
||||||
#
|
|
||||||
# The PullApprove CI check should fail if a PR has no `active` groups, as
|
|
||||||
# this indicates the PR is modifying a file that has no owner.
|
|
||||||
#
|
|
||||||
# This is enforced through the pullapprove verification check done
|
|
||||||
# as part of the CircleCI lint job. Failures in this lint job should be
|
|
||||||
# fixed as part of the PR. This can be done by updating the
|
|
||||||
# `.pullapprove.yml` file cover the unmatched path.
|
|
||||||
# The pullapprove verification script is part of the ng-dev tool and can be
|
|
||||||
# run locally with the command: `yarn -s ng-dev pullapprove verify`
|
|
||||||
#
|
|
||||||
# For cases in which the verification check fails to ensure coverage, this
|
|
||||||
# group will be active. The expectation is that this should be remedied
|
|
||||||
# before merging the PR as described above. In an emergency situation
|
|
||||||
# `global-approvers` can still approve PRs that match this `fallback` rule,
|
|
||||||
# but that should be an exception and not an expectation.
|
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
# The following groups have no conditions and will be `active` on all PRs
|
# Groups which are found to have matching conditions are `active`
|
||||||
# - `global-approvers`
|
# according to PullApprove. If no groups are matched and considered
|
||||||
# - `global-docs-approvers`
|
# active, we still want to have a review occur.
|
||||||
#
|
- len(groups.active) == 0
|
||||||
# Since this means the minimum number of active groups a PR can have is 2, this
|
reviewers:
|
||||||
# `fallback` group should be matched anytime the number of active groups is at or
|
users:
|
||||||
# below this minimum. This work as a protection to ensure that pullapprove does
|
- IgorMinar
|
||||||
# not incidently mark a PR as passing without meeting the review criteria.
|
|
||||||
- len(groups.active) <= 2
|
|
||||||
|
@ -2,6 +2,7 @@ package(default_visibility = ["//visibility:public"])
|
|||||||
|
|
||||||
exports_files([
|
exports_files([
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
|
"protractor-perf.conf.js",
|
||||||
"karma-js.conf.js",
|
"karma-js.conf.js",
|
||||||
"browser-providers.conf.js",
|
"browser-providers.conf.js",
|
||||||
"scripts/ci/track-payload-size.sh",
|
"scripts/ci/track-payload-size.sh",
|
||||||
@ -24,7 +25,7 @@ filegroup(
|
|||||||
"//packages/zone.js/dist:zone-testing.js",
|
"//packages/zone.js/dist:zone-testing.js",
|
||||||
"//packages/zone.js/dist:task-tracking.js",
|
"//packages/zone.js/dist:task-tracking.js",
|
||||||
"//:test-events.js",
|
"//:test-events.js",
|
||||||
"//:third_party/shims_for_IE.js",
|
"//:shims_for_IE.js",
|
||||||
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||||
"@npm//:node_modules/systemjs/dist/system.src.js",
|
"@npm//:node_modules/systemjs/dist/system.src.js",
|
||||||
"@npm//:node_modules/reflect-metadata/Reflect.js",
|
"@npm//:node_modules/reflect-metadata/Reflect.js",
|
||||||
|
564
CHANGELOG.md
564
CHANGELOG.md
@ -1,486 +1,3 @@
|
|||||||
<a name="10.0.3"></a>
|
|
||||||
## 10.0.3 (2020-07-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **core:** handle spaces after `select` and `plural` ICU keywords ([#37866](https://github.com/angular/angular/issues/37866)) ([790bb94](https://github.com/angular/angular/commit/790bb94))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.0.2"></a>
|
|
||||||
## [10.0.2](https://github.com/angular/angular/compare/10.0.1...10.0.2) (2020-06-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#37783](https://github.com/angular/angular/issues/37783)) ([12a71bc](https://github.com/angular/angular/commit/12a71bc))
|
|
||||||
* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#37783](https://github.com/angular/angular/issues/37783)) ([b0b7248](https://github.com/angular/angular/commit/b0b7248)), closes [#25214](https://github.com/angular/angular/issues/25214)
|
|
||||||
* **core:** don't consider inherited NG_ELEMENT_ID during DI ([#37574](https://github.com/angular/angular/issues/37574)) ([64b0ae9](https://github.com/angular/angular/commit/64b0ae9)), closes [#36235](https://github.com/angular/angular/issues/36235)
|
|
||||||
* **core:** error when invoking callbacks registered via ViewRef.onDestroy ([#37543](https://github.com/angular/angular/issues/37543)) ([75b119e](https://github.com/angular/angular/commit/75b119e)), closes [#36213](https://github.com/angular/angular/issues/36213)
|
|
||||||
* **core:** error when invoking callbacks registered via ViewRef.onDestroy ([#37543](https://github.com/angular/angular/issues/37543)) ([#37783](https://github.com/angular/angular/issues/37783)) ([df2cd37](https://github.com/angular/angular/commit/df2cd37)), closes [#36213](https://github.com/angular/angular/issues/36213)
|
|
||||||
* **core:** fake_async_fallback should have the same logic with fake-async ([#37680](https://github.com/angular/angular/issues/37680)) ([7a91b23](https://github.com/angular/angular/commit/7a91b23))
|
|
||||||
* **elements:** fire custom element output events during component initialization ([#37570](https://github.com/angular/angular/issues/37570)) ([89e16ed](https://github.com/angular/angular/commit/89e16ed)), closes [/github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts#L167-L170](https://github.com//github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts/issues/L167-L170) [/github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts#L164](https://github.com//github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts/issues/L164) [/github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/component-factory-strategy.ts#L158](https://github.com//github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/component-factory-strategy.ts/issues/L158) [#36141](https://github.com/angular/angular/issues/36141)
|
|
||||||
* **language-service:** incorrect autocomplete results on unknown symbol ([#37518](https://github.com/angular/angular/issues/37518)) ([7c0b25f](https://github.com/angular/angular/commit/7c0b25f))
|
|
||||||
* **ngcc:** ensure lockfile is removed when analyzeFn fails ([#37739](https://github.com/angular/angular/issues/37739)) ([1a1f99a](https://github.com/angular/angular/commit/1a1f99a))
|
|
||||||
* **ngcc:** prevent including JavaScript sources outside of the package ([#37596](https://github.com/angular/angular/issues/37596)) ([4b90b6a](https://github.com/angular/angular/commit/4b90b6a)), closes [#37508](https://github.com/angular/angular/issues/37508)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **compiler-cli:** fix memory leak in retained incremental state ([#37835](https://github.com/angular/angular/issues/37835)) ([57a518a](https://github.com/angular/angular/commit/57a518a))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.0.1"></a>
|
|
||||||
## [10.0.1](https://github.com/angular/angular/compare/10.0.0...10.0.1) (2020-06-26)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **core:** cleanup DOM elements when root view is removed ([#37600](https://github.com/angular/angular/issues/37600)) ([64f2ffa](https://github.com/angular/angular/commit/64f2ffa)), closes [#36449](https://github.com/angular/angular/issues/36449)
|
|
||||||
* **forms:** change error message ([#37643](https://github.com/angular/angular/issues/37643)) ([c5bc2e7](https://github.com/angular/angular/commit/c5bc2e7))
|
|
||||||
* **forms:** correct usage of `selectedOptions` ([#37620](https://github.com/angular/angular/issues/37620)) ([dfb58c4](https://github.com/angular/angular/commit/dfb58c4)), closes [#37433](https://github.com/angular/angular/issues/37433)
|
|
||||||
* **http:** avoid abort a request when fetch operation is completed ([#37367](https://github.com/angular/angular/issues/37367)) ([a5d5f67](https://github.com/angular/angular/commit/a5d5f67))
|
|
||||||
* **language-service:** reinstate getExternalFiles() ([#37750](https://github.com/angular/angular/issues/37750)) ([ad6680f](https://github.com/angular/angular/commit/ad6680f))
|
|
||||||
* **migrations:** do not incorrectly add todo for @Injectable or @Pipe ([#37732](https://github.com/angular/angular/issues/37732)) ([13020b9](https://github.com/angular/angular/commit/13020b9)), closes [#37726](https://github.com/angular/angular/issues/37726)
|
|
||||||
* **router:** `RouterLinkActive` should run CD when setting `isActive` ([#21411](https://github.com/angular/angular/issues/21411)) ([a8ea817](https://github.com/angular/angular/commit/a8ea817)), closes [#15943](https://github.com/angular/angular/issues/15943) [#19934](https://github.com/angular/angular/issues/19934)
|
|
||||||
* **router:** add null support for RouterLink directive ([#32616](https://github.com/angular/angular/issues/32616)) ([69948ce](https://github.com/angular/angular/commit/69948ce))
|
|
||||||
* **router:** fix error when calling ParamMap.get function ([#31599](https://github.com/angular/angular/issues/31599)) ([3190ccf](https://github.com/angular/angular/commit/3190ccf))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **compiler-cli:** fix regressions in incremental program reuse ([#37690](https://github.com/angular/angular/issues/37690)) ([96b96fb](https://github.com/angular/angular/commit/96b96fb))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.0.0"></a>
|
|
||||||
# [10.0.0](https://github.com/angular/angular/compare/10.0.0-rc.6...10.0.0) (2020-06-24)
|
|
||||||
|
|
||||||
### Release Highlights & Update instructions
|
|
||||||
To learn about the release highlights and our CLI-powered automated update workflow for your projects please check out the [v10 release announcement](https://blog.angular.io/version-10-of-angular-now-available-78960babd41).
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **bazel:** expose explicit mapping from closure to devmode files ([#36262](https://github.com/angular/angular/issues/36262)) ([ba796bb](https://github.com/angular/angular/commit/ba796bb))
|
|
||||||
* **bazel:** simplify ng_package by dropping esm5 and fesm5 ([#36944](https://github.com/angular/angular/issues/36944)) ([9dbb30f](https://github.com/angular/angular/commit/9dbb30f))
|
|
||||||
* **compiler-cli:** report error if undecorated class with Angular features is discovered ([#36921](https://github.com/angular/angular/issues/36921)) ([4c92cf4](https://github.com/angular/angular/commit/4c92cf4))
|
|
||||||
* **compiler:** Propagate value span of ExpressionBinding to ParsedProperty ([#36133](https://github.com/angular/angular/issues/36133)) ([d714b95](https://github.com/angular/angular/commit/d714b95))
|
|
||||||
* **compiler:** add dependency info and ng-content selectors to metadata ([#35695](https://github.com/angular/angular/issues/35695)) ([32ce8b1](https://github.com/angular/angular/commit/32ce8b1))
|
|
||||||
* **compiler:** add name spans for property reads and method calls ([#36826](https://github.com/angular/angular/issues/36826)) ([eb34aa5](https://github.com/angular/angular/commit/eb34aa5))
|
|
||||||
* **core** make generic mandatory for ModuleWithProviders ([#36892](https://github.com/angular/angular/issues/36892)) ([20cc3ab](https://github.com/angular/angular/commit/20cc3ab))
|
|
||||||
* **core** update to tslib 2.0 and move to direct dependencies ([#37198](https://github.com/angular/angular/pull/37198)), closes [#37188](https://github.com/angular/angular/issues/37188)
|
|
||||||
* **core:** undecorated-classes migration should handle derived abstract classes ([#35339](https://github.com/angular/angular/issues/35339)) ([c24ad56](https://github.com/angular/angular/commit/c24ad56))
|
|
||||||
* **core:** undecorated-classes-with-decorated-fields migration should handle classes with lifecycle hooks ([#36921](https://github.com/angular/angular/issues/36921)) ([c6ecdc9](https://github.com/angular/angular/commit/c6ecdc9))
|
|
||||||
* **language-service:** Remove HTML entities autocompletion ([#37515](https://github.com/angular/angular/issues/37515)) ([67bd88b](https://github.com/angular/angular/commit/67bd88b))
|
|
||||||
* **language-service:** TS references from template items ([#37437](https://github.com/angular/angular/issues/37437)) ([bf2cb6f](https://github.com/angular/angular/commit/bf2cb6f))
|
|
||||||
* **language-service:** [ivy] Parse Angular compiler options ([#36922](https://github.com/angular/angular/issues/36922)) ([dbd0f8e](https://github.com/angular/angular/commit/dbd0f8e))
|
|
||||||
* **language-service:** [ivy] wrap ngtsc to handle typecheck files ([#36930](https://github.com/angular/angular/issues/36930)) ([1142c37](https://github.com/angular/angular/commit/1142c37))
|
|
||||||
* **localize:** support merging multiple translation files ([#36792](https://github.com/angular/angular/issues/36792)) ([72f534f](https://github.com/angular/angular/commit/72f534f))
|
|
||||||
* **ngcc:** allow async locking timeouts to be configured ([#36838](https://github.com/angular/angular/issues/36838)) ([38f805c](https://github.com/angular/angular/commit/38f805c))
|
|
||||||
* **ngcc:** implement a program-based entry-point finder ([#37075](https://github.com/angular/angular/issues/37075)) ([f3ccd29](https://github.com/angular/angular/commit/f3ccd29))
|
|
||||||
* **ngcc:** support for new APF where `module` points to esm2015 output ([#36944](https://github.com/angular/angular/issues/36944)) ([c98a4d6](https://github.com/angular/angular/commit/c98a4d6))
|
|
||||||
* **ngcc:** support marking an in-progress task as unprocessed ([#36626](https://github.com/angular/angular/issues/36626)) ([4665c35](https://github.com/angular/angular/commit/4665c35))
|
|
||||||
* **ngcc:** support reverting a file written by `FileWriter` ([#36626](https://github.com/angular/angular/issues/36626)) ([772ccf0](https://github.com/angular/angular/commit/772ccf0))
|
|
||||||
* **platform-server:** use absolute URLs from Location for HTTP ([#37071](https://github.com/angular/angular/issues/37071)) ([9edea0b](https://github.com/angular/angular/commit/9edea0b))
|
|
||||||
* **router:** allow CanLoad guard to return UrlTree ([#36610](https://github.com/angular/angular/issues/36610)) ([00e6cb1](https://github.com/angular/angular/commit/00e6cb1)), closes [#26521](https://github.com/angular/angular/issues/26521) [#28306](https://github.com/angular/angular/issues/28306)
|
|
||||||
* **service-worker:** include `CacheQueryOptions` options in ngsw-config ([#34663](https://github.com/angular/angular/issues/34663)) ([dc9f4b9](https://github.com/angular/angular/commit/dc9f4b9)), closes [#28443](https://github.com/angular/angular/issues/28443)
|
|
||||||
* **service-worker:** support timeout in `registerWhenStable` SW registration strategy ([#35870](https://github.com/angular/angular/issues/35870)) ([00efacf](https://github.com/angular/angular/commit/00efacf)), closes [#34464](https://github.com/angular/angular/issues/34464)
|
|
||||||
* **service-worker:** use `ignoreVary: true` when retrieving responses from cache ([#34663](https://github.com/angular/angular/issues/34663)) ([ee35e22](https://github.com/angular/angular/commit/ee35e22)), closes [#36638](https://github.com/angular/angular/issues/36638)
|
|
||||||
* remove TypeScript 3.6 and 3.7 support ([#36329](https://github.com/angular/angular/issues/36329)) ([fbd281c](https://github.com/angular/angular/commit/fbd281c))
|
|
||||||
* remove support for TypeScript 3.8 ([#37129](https://github.com/angular/angular/issues/37129)) ([6466fb2](https://github.com/angular/angular/commit/6466fb2))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **bazel:** ng_package rule should update "package.json" of ts_library targets ([#36944](https://github.com/angular/angular/issues/36944)) ([d5293d2](https://github.com/angular/angular/commit/d5293d2))
|
|
||||||
* **common:** `locales/global/*.js` are not ES5 compliant ([#36342](https://github.com/angular/angular/issues/36342)) ([078b0be](https://github.com/angular/angular/commit/078b0be)), closes [angular/angular-cli#16394](https://github.com/angular/angular-cli/issues/16394)
|
|
||||||
* **common:** format day-periods that cross midnight ([#36611](https://github.com/angular/angular/issues/36611)) ([c6e5fc4](https://github.com/angular/angular/commit/c6e5fc4)), closes [#36566](https://github.com/angular/angular/issues/36566)
|
|
||||||
* **common:** let `KeyValuePipe` accept type unions with `null` ([#36093](https://github.com/angular/angular/issues/36093)) ([d783519](https://github.com/angular/angular/commit/d783519)), closes [#35743](https://github.com/angular/angular/issues/35743)
|
|
||||||
* **common:** prevent duplicate URL change notifications ([#37404](https://github.com/angular/angular/issues/37404)) ([fff424a](https://github.com/angular/angular/commit/fff424a))
|
|
||||||
* **common:** prevent duplicate URL change notifications ([#37459](https://github.com/angular/angular/issues/37459)) ([0864726](https://github.com/angular/angular/commit/0864726))
|
|
||||||
* **compiler-cli:** `isCaseSensitive()` returns correct value ([#36859](https://github.com/angular/angular/issues/36859)) ([fc4741f](https://github.com/angular/angular/commit/fc4741f))
|
|
||||||
* **compiler-cli:** compute the correct target output for `$localize` messages ([#36989](https://github.com/angular/angular/issues/36989)) ([4e1b5e4](https://github.com/angular/angular/commit/4e1b5e4))
|
|
||||||
* **compiler-cli:** don't try to tag non-ts files as shims ([#36987](https://github.com/angular/angular/issues/36987)) ([42d1091](https://github.com/angular/angular/commit/42d1091))
|
|
||||||
* **compiler-cli:** downlevel angular decorators to static properties ([#37382](https://github.com/angular/angular/issues/37382)) ([323651b](https://github.com/angular/angular/commit/323651b)), closes [#30586](https://github.com/angular/angular/issues/30586) [#30106](https://github.com/angular/angular/issues/30106) [#30586](https://github.com/angular/angular/issues/30586) [#30141](https://github.com/angular/angular/issues/30141)
|
|
||||||
* **compiler-cli:** ensure LogicalFileSystem handles case-sensitivity ([#36859](https://github.com/angular/angular/issues/36859)) ([53a8459](https://github.com/angular/angular/commit/53a8459))
|
|
||||||
* **compiler-cli:** ensure LogicalFileSystem maintains case in paths ([#37008](https://github.com/angular/angular/issues/37008)) ([3dfc770](https://github.com/angular/angular/commit/3dfc770)), closes [#36992](https://github.com/angular/angular/issues/36992) [#36993](https://github.com/angular/angular/issues/36993) [#37000](https://github.com/angular/angular/issues/37000)
|
|
||||||
* **compiler-cli:** ensure `MockFileSystem` handles case-sensitivity ([#36859](https://github.com/angular/angular/issues/36859)) ([26eacd4](https://github.com/angular/angular/commit/26eacd4))
|
|
||||||
* **compiler-cli:** ensure `getRootDirs()` handles case-sensitivity ([#36859](https://github.com/angular/angular/issues/36859)) ([3f3e9b7](https://github.com/angular/angular/commit/3f3e9b7))
|
|
||||||
* **compiler-cli:** fix bug tracking indirect NgModule dependencies ([#36211](https://github.com/angular/angular/issues/36211)) ([bab90a7](https://github.com/angular/angular/commit/bab90a7))
|
|
||||||
* **compiler-cli:** fix case-sensitivity issues in NgtscCompilerHost ([#36859](https://github.com/angular/angular/issues/36859)) ([0ec0ff3](https://github.com/angular/angular/commit/0ec0ff3))
|
|
||||||
* **compiler-cli:** normalize mock Windows file paths correctly ([#36859](https://github.com/angular/angular/issues/36859)) ([b682bd1](https://github.com/angular/angular/commit/b682bd1))
|
|
||||||
* **compiler-cli:** pass real source spans where they are empty ([#31805](https://github.com/angular/angular/issues/31805)) ([e893c5a](https://github.com/angular/angular/commit/e893c5a))
|
|
||||||
* **compiler-cli:** use CompilerHost to ensure canonical file paths ([#36859](https://github.com/angular/angular/issues/36859)) ([a10c126](https://github.com/angular/angular/commit/a10c126))
|
|
||||||
* **compiler-cli:** use ModuleWithProviders type if static eval fails ([#37126](https://github.com/angular/angular/issues/37126)) ([305b5a3](https://github.com/angular/angular/commit/305b5a3))
|
|
||||||
* **compiler:** avoid generating i18n attributes in plain form ([#36422](https://github.com/angular/angular/issues/36422)) ([88b0985](https://github.com/angular/angular/commit/88b0985))
|
|
||||||
* **compiler:** avoid undefined expressions in holey array ([#36343](https://github.com/angular/angular/issues/36343)) ([5516802](https://github.com/angular/angular/commit/5516802))
|
|
||||||
* **compiler:** handle type references to namespaced symbols correctly ([#36106](https://github.com/angular/angular/issues/36106)) ([4aa4e6f](https://github.com/angular/angular/commit/4aa4e6f)), closes [#36006](https://github.com/angular/angular/issues/36006)
|
|
||||||
* **compiler:** normalize line endings in ICU expansions ([#36741](https://github.com/angular/angular/issues/36741)) ([70dd27f](https://github.com/angular/angular/commit/70dd27f)), closes [#36725](https://github.com/angular/angular/issues/36725)
|
|
||||||
* **compiler:** record correct end of expression ([#34690](https://github.com/angular/angular/issues/34690)) ([df890d7](https://github.com/angular/angular/commit/df890d7)), closes [#33477](https://github.com/angular/angular/issues/33477)
|
|
||||||
* **compiler:** remove outdated and invalid warning for unresolved DI parameters ([#36985](https://github.com/angular/angular/issues/36985)) ([d0280a0](https://github.com/angular/angular/commit/d0280a0))
|
|
||||||
* **compiler:** resolve enum values in binary operations ([#36461](https://github.com/angular/angular/issues/36461)) ([64022f5](https://github.com/angular/angular/commit/64022f5)), closes [#35584](https://github.com/angular/angular/issues/35584)
|
|
||||||
* **compiler:** switch to 'referencedFiles' for shim generation ([#36211](https://github.com/angular/angular/issues/36211)) ([4213e8d](https://github.com/angular/angular/commit/4213e8d))
|
|
||||||
* **compiler:** unable to resolve destructuring variable declarations ([#37497](https://github.com/angular/angular/issues/37497)) ([df10597](https://github.com/angular/angular/commit/df10597)), closes [#36917](https://github.com/angular/angular/issues/36917)
|
|
||||||
* **core:** Host classes should not be fed back into `@Input` ([#35889](https://github.com/angular/angular/issues/35889)) ([cda2530](https://github.com/angular/angular/commit/cda2530)), closes [#35383](https://github.com/angular/angular/issues/35383)
|
|
||||||
* **core:** Refresh transplanted views at insertion point only ([#35968](https://github.com/angular/angular/issues/35968)) ([1786586](https://github.com/angular/angular/commit/1786586)), closes [#35400](https://github.com/angular/angular/issues/35400) [#21324](https://github.com/angular/angular/issues/21324)
|
|
||||||
* **core:** attempt to recover from user errors during creation ([#36381](https://github.com/angular/angular/issues/36381)) ([3d82aa7](https://github.com/angular/angular/commit/3d82aa7)), closes [#31221](https://github.com/angular/angular/issues/31221)
|
|
||||||
* **core:** avoid migration error when non-existent symbol is imported ([#36367](https://github.com/angular/angular/issues/36367)) ([d43c306](https://github.com/angular/angular/commit/d43c306)), closes [#36346](https://github.com/angular/angular/issues/36346)
|
|
||||||
* **core:** correct "development mode" console message ([#36571](https://github.com/angular/angular/issues/36571)) ([8d8e419](https://github.com/angular/angular/commit/8d8e419)), closes [#36570](https://github.com/angular/angular/issues/36570)
|
|
||||||
* **core:** disable tsickle pass when producing APF packages ([#37221](https://github.com/angular/angular/issues/37221)) ([a1001f2](https://github.com/angular/angular/commit/a1001f2))
|
|
||||||
* **core:** do not use unbound attributes as inputs to structural directives ([#36441](https://github.com/angular/angular/issues/36441)) ([acf6075](https://github.com/angular/angular/commit/acf6075))
|
|
||||||
* **core:** handle empty translations correctly ([#36499](https://github.com/angular/angular/issues/36499)) ([b1f1d3f](https://github.com/angular/angular/commit/b1f1d3f)), closes [#36476](https://github.com/angular/angular/issues/36476)
|
|
||||||
* **core:** handle pluralize functions that expect a number ([#36901](https://github.com/angular/angular/issues/36901)) ([2ff4b35](https://github.com/angular/angular/commit/2ff4b35)), closes [#36888](https://github.com/angular/angular/issues/36888)
|
|
||||||
* **core:** handle synthetic props in Directive host bindings correctly ([#35568](https://github.com/angular/angular/issues/35568)) ([f27deea](https://github.com/angular/angular/commit/f27deea)), closes [#35501](https://github.com/angular/angular/issues/35501)
|
|
||||||
* **core:** infinite loop if injectable using inheritance has a custom decorator ([#37022](https://github.com/angular/angular/issues/37022)) ([bc54936](https://github.com/angular/angular/commit/bc54936)), closes [#35733](https://github.com/angular/angular/issues/35733)
|
|
||||||
* **core:** inheritance delegate ctor regex updated to work on minified code ([#36962](https://github.com/angular/angular/issues/36962)) ([ea971f7](https://github.com/angular/angular/commit/ea971f7))
|
|
||||||
* **core:** log error instead of warning for unknown properties and elements ([#36399](https://github.com/angular/angular/issues/36399)) ([9d9d46f](https://github.com/angular/angular/commit/9d9d46f)), closes [#35699](https://github.com/angular/angular/issues/35699)
|
|
||||||
* **core:** missing-injectable migration should not migrate `@NgModule` classes ([#36369](https://github.com/angular/angular/issues/36369)) ([28995db](https://github.com/angular/angular/commit/28995db)), closes [#35700](https://github.com/angular/angular/issues/35700)
|
|
||||||
* **core:** ngOnDestroy on multi providers called with incorrect context ([#35840](https://github.com/angular/angular/issues/35840)) ([95fc3d4](https://github.com/angular/angular/commit/95fc3d4)), closes [#35231](https://github.com/angular/angular/issues/35231)
|
|
||||||
* **core:** pipes injecting viewProviders when used on a component host node ([#36512](https://github.com/angular/angular/issues/36512)) ([81d23b3](https://github.com/angular/angular/commit/81d23b3)), closes [#36146](https://github.com/angular/angular/issues/36146)
|
|
||||||
* **core:** prevent unknown property check for AOT-compiled components ([#36072](https://github.com/angular/angular/issues/36072)) ([4a9f0be](https://github.com/angular/angular/commit/4a9f0be)), closes [#35945](https://github.com/angular/angular/issues/35945)
|
|
||||||
* **core:** properly get root nodes from embedded views with <ng-content> ([#36051](https://github.com/angular/angular/issues/36051)) ([e30e132](https://github.com/angular/angular/commit/e30e132)), closes [#35967](https://github.com/angular/angular/issues/35967)
|
|
||||||
* **core:** properly identify modules affected by overrides in TestBed ([#36649](https://github.com/angular/angular/issues/36649)) ([942b986](https://github.com/angular/angular/commit/942b986)), closes [#36619](https://github.com/angular/angular/issues/36619)
|
|
||||||
* **core:** reenable decorator downleveling for Angular npm packages ([#37317](https://github.com/angular/angular/issues/37317)) ([d16a7f3](https://github.com/angular/angular/commit/d16a7f3)), closes [#37221](https://github.com/angular/angular/issues/37221) [#37221](https://github.com/angular/angular/issues/37221)
|
|
||||||
* **core:** run `APP_INITIALIZER`s before accessing `LOCALE_ID` token in Ivy TestBed ([#36237](https://github.com/angular/angular/issues/36237)) ([1649743](https://github.com/angular/angular/commit/1649743)), closes [#36230](https://github.com/angular/angular/issues/36230)
|
|
||||||
* **core:** should fake a top event task when coalescing events to prevent draining microTaskQueue too early. ([#36841](https://github.com/angular/angular/issues/36841)) ([9b8eb42](https://github.com/angular/angular/commit/9b8eb42)), closes [#36839](https://github.com/angular/angular/issues/36839)
|
|
||||||
* **core:** undecorated-classes-with-decorated-fields migration does not decorate derived classes ([#35339](https://github.com/angular/angular/issues/35339)) ([32eafef](https://github.com/angular/angular/commit/32eafef)), closes [#34376](https://github.com/angular/angular/issues/34376)
|
|
||||||
* **core:** undecorated-classes-with-decorated-fields migration should avoid error if base class has no value declaration ([#36543](https://github.com/angular/angular/issues/36543)) ([ca67748](https://github.com/angular/angular/commit/ca67748)), closes [#36522](https://github.com/angular/angular/issues/36522)
|
|
||||||
* **core:** workaround Terser inlining bug ([#36200](https://github.com/angular/angular/issues/36200)) ([0ce8ad3](https://github.com/angular/angular/commit/0ce8ad3))
|
|
||||||
* **elements:** capture input properties set before upgrading the element ([#36114](https://github.com/angular/angular/issues/36114)) ([2fc5ae5](https://github.com/angular/angular/commit/2fc5ae5)), closes [#30848](https://github.com/angular/angular/issues/30848) [#31416](https://github.com/angular/angular/issues/31416)
|
|
||||||
* **elements:** correctly handle getting/setting properties before connecting the element ([#36114](https://github.com/angular/angular/issues/36114)) ([327980b](https://github.com/angular/angular/commit/327980b)), closes [#30848](https://github.com/angular/angular/issues/30848)
|
|
||||||
* **elements:** correctly handle setting inputs to `undefined` ([#36140](https://github.com/angular/angular/issues/36140)) ([9ba46d9](https://github.com/angular/angular/commit/9ba46d9))
|
|
||||||
* **elements:** correctly set `SimpleChange#firstChange` for pre-existing inputs ([#36140](https://github.com/angular/angular/issues/36140)) ([b14ac96](https://github.com/angular/angular/commit/b14ac96)), closes [#36130](https://github.com/angular/angular/issues/36130)
|
|
||||||
* **elements:** do not break when the constructor of an Angular Element is not called ([#36114](https://github.com/angular/angular/issues/36114)) ([89b44d1](https://github.com/angular/angular/commit/89b44d1))
|
|
||||||
* **elements:** fire custom element output events during component initialization ([#36161](https://github.com/angular/angular/issues/36161)) ([e9bff5f](https://github.com/angular/angular/commit/e9bff5f)), closes [#36141](https://github.com/angular/angular/issues/36141)
|
|
||||||
* **forms:** handle numeric values properly in the validator ([#36157](https://github.com/angular/angular/issues/36157)) ([88a235d](https://github.com/angular/angular/commit/88a235d)), closes [#35591](https://github.com/angular/angular/issues/35591)
|
|
||||||
* **forms:** number input fires valueChanges twice ([#36087](https://github.com/angular/angular/issues/36087)) ([97d6d90](https://github.com/angular/angular/commit/97d6d90)), closes [#12540](https://github.com/angular/angular/issues/12540)
|
|
||||||
* **language-service:** Improve signature selection by finding exact match ([#37494](https://github.com/angular/angular/issues/37494)) ([e97a2d4](https://github.com/angular/angular/commit/e97a2d4))
|
|
||||||
* **language-service:** Recover from error in analyzing Ng Modules ([#37108](https://github.com/angular/angular/issues/37108)) ([2c1f35e](https://github.com/angular/angular/commit/2c1f35e))
|
|
||||||
* **language-service:** disable update the `[@angular](https://github.com/angular)/core` module ([#36783](https://github.com/angular/angular/issues/36783)) ([dd049ca](https://github.com/angular/angular/commit/dd049ca))
|
|
||||||
* **language-service:** infer type of elements of array-like objects ([#36312](https://github.com/angular/angular/issues/36312)) ([fe2b692](https://github.com/angular/angular/commit/fe2b692)), closes [#36191](https://github.com/angular/angular/issues/36191)
|
|
||||||
* **language-service:** properly evaluate types in comparable expressions ([#36529](https://github.com/angular/angular/issues/36529)) ([8be0972](https://github.com/angular/angular/commit/8be0972))
|
|
||||||
* **language-service:** use empty statement as parent of type node ([#36989](https://github.com/angular/angular/issues/36989)) ([a32cbed](https://github.com/angular/angular/commit/a32cbed))
|
|
||||||
* **language-service:** use the `HtmlAst` to get the span of HTML tag ([#36371](https://github.com/angular/angular/issues/36371)) ([81195a2](https://github.com/angular/angular/commit/81195a2))
|
|
||||||
* **language-service:** wrong completions in conditional operator ([#37505](https://github.com/angular/angular/issues/37505)) ([32020f9](https://github.com/angular/angular/commit/32020f9))
|
|
||||||
* **localize:** allow ICU expansion case to start with any character except `}` ([#36123](https://github.com/angular/angular/issues/36123)) ([fced8ee](https://github.com/angular/angular/commit/fced8ee)), closes [#31586](https://github.com/angular/angular/issues/31586)
|
|
||||||
* **localize:** ensure `getLocation()` works ([#36853](https://github.com/angular/angular/issues/36853)) ([70b25a3](https://github.com/angular/angular/commit/70b25a3))
|
|
||||||
* **localize:** include legacy ids when describing messages ([#36761](https://github.com/angular/angular/issues/36761)) ([47f9867](https://github.com/angular/angular/commit/47f9867))
|
|
||||||
* **ngcc:** `viaModule` should be `null` for local imports ([#36989](https://github.com/angular/angular/issues/36989)) ([d268d2a](https://github.com/angular/angular/commit/d268d2a))
|
|
||||||
* **ngcc:** add process title ([#36448](https://github.com/angular/angular/issues/36448)) ([76a8cd5](https://github.com/angular/angular/commit/76a8cd5)), closes [36414#issuecomment-609644282](https://github.com/angular/angular/issues/36414#issuecomment-609644282)
|
|
||||||
* **ngcc:** allow ngcc configuration to match pre-release versions of packages ([#36370](https://github.com/angular/angular/issues/36370)) ([326240e](https://github.com/angular/angular/commit/326240e))
|
|
||||||
* **ngcc:** capture dynamic import expressions as well as declarations ([#37075](https://github.com/angular/angular/issues/37075)) ([5c0bdae](https://github.com/angular/angular/commit/5c0bdae))
|
|
||||||
* **ngcc:** correctly detect external files from nested `node_modules/` ([#36559](https://github.com/angular/angular/issues/36559)) ([6ab43d7](https://github.com/angular/angular/commit/6ab43d7)), closes [#36526](https://github.com/angular/angular/issues/36526)
|
|
||||||
* **ngcc:** correctly detect imported TypeScript helpers ([#36284](https://github.com/angular/angular/issues/36284)) ([ca25c95](https://github.com/angular/angular/commit/ca25c95)), closes [#36089](https://github.com/angular/angular/issues/36089)
|
|
||||||
* **ngcc:** correctly get config for packages in nested `node_modules/` ([#37040](https://github.com/angular/angular/issues/37040)) ([9ade1c3](https://github.com/angular/angular/commit/9ade1c3))
|
|
||||||
* **ngcc:** correctly get config for sub-entry-points when primary entry-point is ignored ([#37040](https://github.com/angular/angular/issues/37040)) ([bf57776](https://github.com/angular/angular/commit/bf57776))
|
|
||||||
* **ngcc:** correctly identify relative Windows-style import paths ([#36372](https://github.com/angular/angular/issues/36372)) ([aecf9de](https://github.com/angular/angular/commit/aecf9de))
|
|
||||||
* **ngcc:** correctly identify the package path of secondary entry-points ([#36249](https://github.com/angular/angular/issues/36249)) ([995cd15](https://github.com/angular/angular/commit/995cd15)), closes [#35747](https://github.com/angular/angular/issues/35747)
|
|
||||||
* **ngcc:** correctly retrieve a package's version from its `package.json` ([#37040](https://github.com/angular/angular/issues/37040)) ([11c0402](https://github.com/angular/angular/commit/11c0402))
|
|
||||||
* **ngcc:** detect non-emitted, non-imported TypeScript helpers ([#36418](https://github.com/angular/angular/issues/36418)) ([5fa7b8b](https://github.com/angular/angular/commit/5fa7b8b))
|
|
||||||
* **ngcc:** display output from the unlocker process on Windows ([#36569](https://github.com/angular/angular/issues/36569)) ([e041ac6](https://github.com/angular/angular/commit/e041ac6))
|
|
||||||
* **ngcc:** display unlocker process output in sync mode ([#36637](https://github.com/angular/angular/issues/36637)) ([cabf997](https://github.com/angular/angular/commit/cabf997)), closes [/github.com/nodejs/node/issues/3596#issuecomment-250890218](https://github.com/nodejs/node/issues/3596#issuecomment-250890218)
|
|
||||||
* **ngcc:** do not inline source-maps for non-inline typings source-maps ([#37363](https://github.com/angular/angular/issues/37363)) ([b4e26b5](https://github.com/angular/angular/commit/b4e26b5)), closes [#37324](https://github.com/angular/angular/issues/37324)
|
|
||||||
* **ngcc:** do not run in parallel mode if there are less than 3 CPU cores ([#36626](https://github.com/angular/angular/issues/36626)) ([4c63241](https://github.com/angular/angular/commit/4c63241))
|
|
||||||
* **ngcc:** do not scan import expressions in d.ts files ([#37503](https://github.com/angular/angular/issues/37503)) ([8248307](https://github.com/angular/angular/commit/8248307))
|
|
||||||
* **ngcc:** do not spawn more processes than intended in parallel mode ([#36280](https://github.com/angular/angular/issues/36280)) ([5cee709](https://github.com/angular/angular/commit/5cee709)), closes [#35719](https://github.com/angular/angular/issues/35719) [#36278](https://github.com/angular/angular/issues/36278)
|
|
||||||
* **ngcc:** do not spawn unlocker processes on cluster workers ([#36569](https://github.com/angular/angular/issues/36569)) ([66effde](https://github.com/angular/angular/commit/66effde)), closes [#35861](https://github.com/angular/angular/issues/35861)
|
|
||||||
* **ngcc:** do not use cached file-system ([#36687](https://github.com/angular/angular/issues/36687)) ([0c2ed4c](https://github.com/angular/angular/commit/0c2ed4c)), closes [/github.com/angular/angular-cli/issues/16860#issuecomment-614694269](https://github.com/angular/angular-cli/issues/16860#issuecomment-614694269)
|
|
||||||
* **ngcc:** do not warn if `paths` mapping does not exist ([#36525](https://github.com/angular/angular/issues/36525)) ([717df13](https://github.com/angular/angular/commit/717df13)), closes [#36518](https://github.com/angular/angular/issues/36518)
|
|
||||||
* **ngcc:** do not write entry-point manifest outside node_modules ([#36299](https://github.com/angular/angular/issues/36299)) ([c6dd900](https://github.com/angular/angular/commit/c6dd900)), closes [#36296](https://github.com/angular/angular/issues/36296)
|
|
||||||
* **ngcc:** don't crash on cyclic source-map references ([#36452](https://github.com/angular/angular/issues/36452)) ([ee70a18](https://github.com/angular/angular/commit/ee70a18)), closes [#35727](https://github.com/angular/angular/issues/35727) [#35757](https://github.com/angular/angular/issues/35757)
|
|
||||||
* **ngcc:** ensure reflection hosts can handle TS 3.9 IIFE wrapped classes ([#36989](https://github.com/angular/angular/issues/36989)) ([d7440c4](https://github.com/angular/angular/commit/d7440c4))
|
|
||||||
* **ngcc:** ensure rendering formatters work with IIFE wrapped classes ([#36989](https://github.com/angular/angular/issues/36989)) ([c8ee390](https://github.com/angular/angular/commit/c8ee390))
|
|
||||||
* **ngcc:** ensure that more dependencies are found by `EsmDependencyHost` ([#37075](https://github.com/angular/angular/issues/37075)) ([c6872c0](https://github.com/angular/angular/commit/c6872c0))
|
|
||||||
* **ngcc:** find decorated constructor params on IIFE wrapped classes ([#37436](https://github.com/angular/angular/issues/37436)) ([2cb3b66](https://github.com/angular/angular/commit/2cb3b66)), closes [#37330](https://github.com/angular/angular/issues/37330)
|
|
||||||
* **ngcc:** force ngcc to exit on error ([#36622](https://github.com/angular/angular/issues/36622)) ([663b768](https://github.com/angular/angular/commit/663b768)), closes [#36616](https://github.com/angular/angular/issues/36616)
|
|
||||||
* **ngcc:** give up re-spawning crashed worker process after 3 attempts ([#36626](https://github.com/angular/angular/issues/36626)) ([793cb32](https://github.com/angular/angular/commit/793cb32))
|
|
||||||
* **ngcc:** handle `ENOMEM` errors in worker processes ([#36626](https://github.com/angular/angular/issues/36626)) ([4779c4b](https://github.com/angular/angular/commit/4779c4b))
|
|
||||||
* **ngcc:** handle bad path mappings when finding entry-points ([#36331](https://github.com/angular/angular/issues/36331)) ([cc4b813](https://github.com/angular/angular/commit/cc4b813)), closes [#36313](https://github.com/angular/angular/issues/36313) [#36283](https://github.com/angular/angular/issues/36283)
|
|
||||||
* **ngcc:** handle entry-points within container folders ([#36305](https://github.com/angular/angular/issues/36305)) ([38ad1d9](https://github.com/angular/angular/commit/38ad1d9)), closes [#35756](https://github.com/angular/angular/issues/35756) [#36216](https://github.com/angular/angular/issues/36216)
|
|
||||||
* **ngcc:** identifier ModuleWithProviders functions in IIFE wrapped classes ([#37206](https://github.com/angular/angular/issues/37206)) ([97e1399](https://github.com/angular/angular/commit/97e1399)), closes [#37189](https://github.com/angular/angular/issues/37189)
|
|
||||||
* **ngcc:** provide a unique exit code for timeouts ([#36838](https://github.com/angular/angular/issues/36838)) ([d805526](https://github.com/angular/angular/commit/d805526))
|
|
||||||
* **ngcc:** recognize enum declarations emitted in JavaScript ([#36550](https://github.com/angular/angular/issues/36550)) ([89c5890](https://github.com/angular/angular/commit/89c5890)), closes [#35584](https://github.com/angular/angular/issues/35584)
|
|
||||||
* **ngcc:** sniff `main` property for ESM5 format ([#36396](https://github.com/angular/angular/issues/36396)) ([2463548](https://github.com/angular/angular/commit/2463548)), closes [#35788](https://github.com/angular/angular/issues/35788)
|
|
||||||
* **ngcc:** support ModuleWithProviders functions that delegate ([#36948](https://github.com/angular/angular/issues/36948)) ([fafa50d](https://github.com/angular/angular/commit/fafa50d)), closes [#36892](https://github.com/angular/angular/issues/36892)
|
|
||||||
* **ngcc:** support TS 3.9 wrapped ES2015 classes ([#36884](https://github.com/angular/angular/issues/36884)) ([db4c59d](https://github.com/angular/angular/commit/db4c59d))
|
|
||||||
* **ngcc:** support `defineProperty()` re-exports in CommonJS and UMD ([#36989](https://github.com/angular/angular/issues/36989)) ([91092f6](https://github.com/angular/angular/commit/91092f6))
|
|
||||||
* **ngcc:** support ignoring deep-imports via package config ([#36423](https://github.com/angular/angular/issues/36423)) ([f9fb833](https://github.com/angular/angular/commit/f9fb833)), closes [#35750](https://github.com/angular/angular/issues/35750)
|
|
||||||
* **ngcc:** support recovering when a worker process crashes ([#36626](https://github.com/angular/angular/issues/36626)) ([966598c](https://github.com/angular/angular/commit/966598c)), closes [#36278](https://github.com/angular/angular/issues/36278)
|
|
||||||
* **ngcc:** support simple `browser` property in entry-points ([#36396](https://github.com/angular/angular/issues/36396)) ([6b3aa60](https://github.com/angular/angular/commit/6b3aa60)), closes [#36062](https://github.com/angular/angular/issues/36062)
|
|
||||||
* **ngcc:** use annotateForClosureCompiler option ([#36652](https://github.com/angular/angular/issues/36652)) ([eca8d11](https://github.com/angular/angular/commit/eca8d11)), closes [#36618](https://github.com/angular/angular/issues/36618)
|
|
||||||
* **ngcc:** use path-mappings from tsconfig in dependency resolution ([#36180](https://github.com/angular/angular/issues/36180)) ([380de1e](https://github.com/angular/angular/commit/380de1e)), closes [#36119](https://github.com/angular/angular/issues/36119)
|
|
||||||
* **ngcc:** use preserve whitespaces from tsconfig if provided ([#36189](https://github.com/angular/angular/issues/36189)) ([b8e9a30](https://github.com/angular/angular/commit/b8e9a30)), closes [#35871](https://github.com/angular/angular/issues/35871)
|
|
||||||
* **platform-server:** correctly handle absolute relative URLs ([#37341](https://github.com/angular/angular/issues/37341)) ([420d1c3](https://github.com/angular/angular/commit/420d1c3)), closes [#37314](https://github.com/angular/angular/issues/37314)
|
|
||||||
* **platform-server:** update `xhr2` dependency ([#36366](https://github.com/angular/angular/issues/36366)) ([b59bc0e](https://github.com/angular/angular/commit/b59bc0e)), closes [#36358](https://github.com/angular/angular/issues/36358)
|
|
||||||
* **router:** Fix relative link generation from empty path components ([#37446](https://github.com/angular/angular/issues/37446)) ([585e3f6](https://github.com/angular/angular/commit/585e3f6)), closes [#26243](https://github.com/angular/angular/issues/26243) [#13011](https://github.com/angular/angular/issues/13011) [#35687](https://github.com/angular/angular/issues/35687)
|
|
||||||
* **router:** allow UrlMatcher to return null ([#36402](https://github.com/angular/angular/issues/36402)) ([568e9df](https://github.com/angular/angular/commit/568e9df)), closes [#29824](https://github.com/angular/angular/issues/29824)
|
|
||||||
* **router:** cancel navigation when at least one resolver completes with no "next" emission ([#24621](https://github.com/angular/angular/issues/24621)) ([d9c4840](https://github.com/angular/angular/commit/d9c4840)), closes [#24195](https://github.com/angular/angular/issues/24195)
|
|
||||||
* **router:** fix navigation ignoring logic to compare to the browser url ([#37408](https://github.com/angular/angular/issues/37408)) ([5db2e79](https://github.com/angular/angular/commit/5db2e79)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586)
|
|
||||||
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([80e6c07](https://github.com/angular/angular/commit/80e6c07)), closes [#34614](https://github.com/angular/angular/issues/34614)
|
|
||||||
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([e0415db](https://github.com/angular/angular/commit/e0415db)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
|
||||||
* **router:** update type for routerLink to include null and undefined ([#37018](https://github.com/angular/angular/issues/37018)) ([ef9f8df](https://github.com/angular/angular/commit/ef9f8df)), closes [#13380](https://github.com/angular/angular/issues/13380) [#36544](https://github.com/angular/angular/issues/36544)
|
|
||||||
* **service-worker:** Don't stay locked in EXISTING_CLIENTS_ONLY if corrupted data ([#37453](https://github.com/angular/angular/issues/37453)) ([6f93847](https://github.com/angular/angular/commit/6f93847)), closes [#31109](https://github.com/angular/angular/issues/31109) [#31865](https://github.com/angular/angular/issues/31865)
|
|
||||||
* **service-worker:** by default register the SW after 30s even the app never stabilizes ([#35870](https://github.com/angular/angular/issues/35870)) ([29e8a64](https://github.com/angular/angular/commit/29e8a64)), closes [#34464](https://github.com/angular/angular/issues/34464)
|
|
||||||
* **service-worker:** prevent SW registration strategies from affecting app stabilization ([#35870](https://github.com/angular/angular/issues/35870)) ([2d7c95f](https://github.com/angular/angular/commit/2d7c95f))
|
|
||||||
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([0cc53fb](https://github.com/angular/angular/commit/0cc53fb)), closes [#36492](https://github.com/angular/angular/issues/36492)
|
|
||||||
* add aikidave as reviewer for DOCS: Marketing ([#37014](https://github.com/angular/angular/issues/37014)) ([286fbf4](https://github.com/angular/angular/commit/286fbf4))
|
|
||||||
|
|
||||||
|
|
||||||
### Code Refactoring
|
|
||||||
|
|
||||||
* **common:** remove WrappedValue from AsyncPipe ([#36633](https://github.com/angular/angular/issues/36633)) ([49be32c](https://github.com/angular/angular/commit/49be32c)), closes [#29927](https://github.com/angular/angular/issues/29927)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **compiler-cli:** perform template type-checking incrementally ([#36211](https://github.com/angular/angular/issues/36211)) ([ecffc35](https://github.com/angular/angular/commit/ecffc35))
|
|
||||||
* **compiler-cli:** split Ivy template type-checking into multiple files ([#36211](https://github.com/angular/angular/issues/36211)) ([b861e9c](https://github.com/angular/angular/commit/b861e9c))
|
|
||||||
* **core:** avoid pulling in jit-specific code in aot bundles ([#37372](https://github.com/angular/angular/issues/37372)) ([#37514](https://github.com/angular/angular/issues/37514)) ([6114cd2](https://github.com/angular/angular/commit/6114cd2)), closes [#29083](https://github.com/angular/angular/issues/29083)
|
|
||||||
* **forms:** optimize internal method _anyControls in FormGroup ([#32534](https://github.com/angular/angular/issues/32534)) ([6c7467a](https://github.com/angular/angular/commit/6c7467a))
|
|
||||||
* **ngcc:** allow immediately reporting a stale lock file ([#37250](https://github.com/angular/angular/issues/37250)) ([930d204](https://github.com/angular/angular/commit/930d204))
|
|
||||||
* **ngcc:** cache parsed tsconfig between runs ([#37417](https://github.com/angular/angular/issues/37417)) ([f9daa13](https://github.com/angular/angular/commit/f9daa13)), closes [#36882](https://github.com/angular/angular/issues/36882)
|
|
||||||
* **ngcc:** only compute basePaths in TargetedEntryPointFinder when needed ([#36881](https://github.com/angular/angular/issues/36881)) ([ec6b9cc](https://github.com/angular/angular/commit/ec6b9cc)), closes [#36874](https://github.com/angular/angular/issues/36874)
|
|
||||||
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([3bedfda](https://github.com/angular/angular/commit/3bedfda))
|
|
||||||
* **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([a185efb](https://github.com/angular/angular/commit/a185efb)), closes [#issuecomment-608401834](https://github.com/angular/angular#issuecomment-608401834)
|
|
||||||
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([ec0ce60](https://github.com/angular/angular/commit/ec0ce60))
|
|
||||||
* **ngcc:** speed up the `getBasePaths()` computation ([#36881](https://github.com/angular/angular/issues/36881)) ([e037840](https://github.com/angular/angular/commit/e037840))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Dependency updates
|
|
||||||
@angular/compiler-cli now requires:
|
|
||||||
- TypeScript 3.9
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* TypeScript 3.6, 3.7 and 3.8 are no longer supported, please update to TypeScript 3.9.
|
|
||||||
* **core:** Angular npm packages no longer contain jsdoc comments
|
|
||||||
to support Closure Compiler's advanced optimizations
|
|
||||||
|
|
||||||
The support for Closure Compiler in Angular packages has been
|
|
||||||
experimental and broken for quite some time.
|
|
||||||
|
|
||||||
As of TS3.9, Closure is unusable with the JavaScript emit. Please follow
|
|
||||||
https://github.com/microsoft/TypeScript/issues/38374 for more
|
|
||||||
information and updates.
|
|
||||||
|
|
||||||
If you used Closure Compiler with Angular in the past, you will likely
|
|
||||||
be better off consuming Angular packages built from sources directly
|
|
||||||
rather than consuming the version we publish on npm,
|
|
||||||
which is primarily optimized for Webpack/Rollup + Terser build pipeline.
|
|
||||||
|
|
||||||
As a temporary workaround, you might consider using your current build
|
|
||||||
pipeline with Closure flag `--compilation_level=SIMPLE`. This flag
|
|
||||||
will ensure that your build pipeline produces buildable and
|
|
||||||
runnable artifacts, at the cost of increased payload size due to
|
|
||||||
advanced optimizations being disabled.
|
|
||||||
|
|
||||||
If you were affected by this change, please help us understand your
|
|
||||||
needs by leaving a comment on https://github.com/angular/angular/issues/37234.
|
|
||||||
|
|
||||||
* **core:** make generic mandatory for ModuleWithProviders
|
|
||||||
|
|
||||||
A generic type parameter has always been required for the `ModuleWithProviders` pattern to work with Ivy, but prior to this commit, View Engine allowed the generic type to be omitted (though support was officially deprecated).
|
|
||||||
If you're using `ModuleWithProviders` without a generic type in your application code, a v10 migration will update your code for you.
|
|
||||||
|
|
||||||
However, if you are using View Engine and also depending on a library that omits the generic type, you will now get a build time error similar to:
|
|
||||||
|
|
||||||
```
|
|
||||||
error TS2314: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s).
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case, ngcc won't help you (because it's Ivy-only) and the migration only covers application code.
|
|
||||||
You should contact the library author to fix their library to provide a type parameter when they use this class.
|
|
||||||
|
|
||||||
As a workaround, we suggest setting `skipLibChecks` to false in your tsconfig or updating your app to use Ivy.
|
|
||||||
|
|
||||||
* **forms:** Number inputs no longer listen to the `change` event.
|
|
||||||
|
|
||||||
Tests which trigger `change` events need to be updated to trigger `input` events instead.
|
|
||||||
|
|
||||||
The `change` event was in place to support IE9, as we found that `input` events were not fired with backspace or cut actions. If you need to maintain IE9 support, you will need to add a change event listener to number inputs and call the `onChange` method of `NumberValueAccessor` manually.
|
|
||||||
|
|
||||||
Lastly, old versions of WebDriver would synthetically trigger the `change` event on `WebElement.clear` and `WebElement.sendKeys`. If you are using an old version of WebDriver, you may need to update tests to ensure `input` events are triggered. For example, you could use `element.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);` in place of `element.clear()`.
|
|
||||||
* **forms:** The `minLength` and `maxLength` validators now verify that the form control's value has a
|
|
||||||
numeric `length` property, and only validate for length if that's the case.
|
|
||||||
|
|
||||||
Previously, falsey values without the length property (such as `0` or
|
|
||||||
`false` values) were triggering validation errors. If your code relies on
|
|
||||||
the old behavior, you can include other validators such as [min][1] or
|
|
||||||
[requiredTrue][2] to the list of validators for a particular field.
|
|
||||||
|
|
||||||
[1]: https://angular.io/api/forms/Validators#min
|
|
||||||
[2]: https://angular.io/api/forms/Validators#requiredTrue
|
|
||||||
* **bazel:** esm5 and fesm5 format is no longer distributed in
|
|
||||||
Angular's npm packages e.g. @angular/core
|
|
||||||
|
|
||||||
If you are not using Angular CLI to build your application or library,
|
|
||||||
and you need to be able to build es5 artifacts, then you will need to
|
|
||||||
downlevel the distributed Angular code to es5 on your own.
|
|
||||||
|
|
||||||
Angular CLI will automatically downlevel the code to es5 if differential
|
|
||||||
loading is enabled in the Angular project, so no action is required from
|
|
||||||
Angular CLI users.
|
|
||||||
|
|
||||||
* **core:** Warnings about unknown elements are now logged as errors. This won't break your app, but it may trip up tools that expect nothing to be logged via `console.error`.
|
|
||||||
* **router:** Any resolver which return EMPTY will cancel navigation.
|
|
||||||
If you want to allow the navigation to continue, you will need to update the resolvers to emit
|
|
||||||
some value, (i.e. defaultIfEmpty(...), of(...), etc).
|
|
||||||
* **service-worker:** Previously, [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary)
|
|
||||||
headers would be taken into account when retrieving resources from the
|
|
||||||
cache, completely preventing the retrieval of cached assets (due to
|
|
||||||
ServiceWorker implementation details) and leading to unpredictable
|
|
||||||
behavior due to inconsistent/buggy implementations in different
|
|
||||||
browsers.
|
|
||||||
|
|
||||||
Now, `Vary` headers are ignored when retrieving resources from the
|
|
||||||
ServiceWorker caches, which can result in resources being retrieved even
|
|
||||||
when their headers are different. If your application needs to
|
|
||||||
differentiate its responses based on request headers, please make sure
|
|
||||||
the Angular ServiceWorker is [configured](https://angular.io/guide/service-worker-config)
|
|
||||||
to avoid caching the affected resources.
|
|
||||||
* **common:** This change could result in ExpressionChangedAfterItHasBeenChecked errors that
|
|
||||||
were not detected before. The error could previously have gone undetected
|
|
||||||
because two WrappedValues are considered "equal" in all cases for the purposes
|
|
||||||
of the check, even if their respective unwrapped values are not.
|
|
||||||
|
|
||||||
Additionally, `[val]=(observable | async).someProperty` will no longer
|
|
||||||
trigger change detection if the value of `someProperty` is identical to
|
|
||||||
the value in the previous emit. If you need to force change detection,
|
|
||||||
either update the binding to use an object whose reference changes or
|
|
||||||
subscribe to the observable and call markForCheck as needed.
|
|
||||||
|
|
||||||
* **common:** format day-periods that cross midnight
|
|
||||||
|
|
||||||
When formatting a time with the `b` or `B` format codes, the rendered
|
|
||||||
string was not correctly handling day periods that spanned midnight.
|
|
||||||
Instead the logic was falling back to the default case of `AM`.
|
|
||||||
|
|
||||||
Now the logic has been updated so that it matches times that are within
|
|
||||||
a day period that spans midnight, so it will now render the correct
|
|
||||||
output, such as `at night` in the case of English.
|
|
||||||
|
|
||||||
Applications that are using either `formatDate()` or `DatePipe` and any
|
|
||||||
of the `b` or `B` format codes will be affected by this change.
|
|
||||||
|
|
||||||
* **router:** UrlMatcher's type now reflects that it could always return
|
|
||||||
null.
|
|
||||||
|
|
||||||
If you implemented your own Router or Recognizer class, please update it to
|
|
||||||
handle matcher returning null.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.9"></a>
|
|
||||||
## [9.1.9](https://github.com/angular/angular/compare/9.1.8...9.1.9) (2020-05-20)
|
|
||||||
|
|
||||||
This release contains a re-submit of the following 3 commits with fixes for TS 3.8.
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **elements:** capture input properties set before upgrading the element ([#36114](https://github.com/angular/angular/issues/36114)) ([#37226](https://github.com/angular/angular/issues/37226)) ([a33cb2d](https://github.com/angular/angular/commit/a33cb2d)), closes [#30848](https://github.com/angular/angular/issues/30848) [#31416](https://github.com/angular/angular/issues/31416)
|
|
||||||
* **elements:** correctly handle getting/setting properties before connecting the element ([#36114](https://github.com/angular/angular/issues/36114)) ([#37226](https://github.com/angular/angular/issues/37226)) ([6ac0042](https://github.com/angular/angular/commit/6ac0042)), closes [/github.com/angular/angular/pull/31416/files#r300326698](https://github.com/angular/angular/pull/31416/files/issues/r300326698)
|
|
||||||
* **elements:** do not break when the constructor of an Angular Element is not called ([#36114](https://github.com/angular/angular/issues/36114)) ([#37226](https://github.com/angular/angular/issues/37226)) ([1465372](https://github.com/angular/angular/commit/1465372))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.8"></a>
|
|
||||||
## [9.1.8](https://github.com/angular/angular/compare/9.1.6...9.1.8) (2020-05-20)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **core:** Host classes should not be fed back into `@Input` ([#35889](https://github.com/angular/angular/issues/35889)) ([f872b69](https://github.com/angular/angular/commit/f872b69)), closes [#35383](https://github.com/angular/angular/issues/35383)
|
|
||||||
* **core:** inheritance delegate ctor regex updated to work on minified code ([#36962](https://github.com/angular/angular/issues/36962)) ([e3d3395](https://github.com/angular/angular/commit/e3d3395))
|
|
||||||
* **elements:** capture input properties set before upgrading the element ([#36114](https://github.com/angular/angular/issues/36114)) ([1c8f179](https://github.com/angular/angular/commit/1c8f179)), closes [#30848](https://github.com/angular/angular/issues/30848) [#31416](https://github.com/angular/angular/issues/31416)
|
|
||||||
* **elements:** correctly handle getting/setting properties before connecting the element ([#36114](https://github.com/angular/angular/issues/36114)) ([363f14c](https://github.com/angular/angular/commit/363f14c)), closes [/github.com/angular/angular/pull/31416/files#r300326698](https://github.com/angular/angular/pull/31416/files/issues/r300326698)
|
|
||||||
* **elements:** do not break when the constructor of an Angular Element is not called ([#36114](https://github.com/angular/angular/issues/36114)) ([87b9f08](https://github.com/angular/angular/commit/87b9f08))
|
|
||||||
* **router:** update type for routerLink to include null and undefined ([#37018](https://github.com/angular/angular/issues/37018)) ([7de7871](https://github.com/angular/angular/commit/7de7871)), closes [#13380](https://github.com/angular/angular/issues/13380) [#36544](https://github.com/angular/angular/issues/36544)
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.7"></a>
|
|
||||||
## [9.1.7](https://github.com/angular/angular/compare/9.1.6...9.1.7) (2020-05-13)
|
|
||||||
|
|
||||||
This release contains various API docs improvements.
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.6"></a>
|
|
||||||
## [9.1.6](https://github.com/angular/angular/compare/9.1.5...9.1.6) (2020-05-08)
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler-cli**: Revert "fix(compiler-cli): fix case-sensitivity issues in NgtscCompilerHost (#36968)" (#37003)
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.5"></a>
|
|
||||||
## [9.1.5](https://github.com/angular/angular/compare/9.1.4...9.1.5) (2020-05-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler-cli:** `isCaseSensitive()` returns correct value ([#36968](https://github.com/angular/angular/issues/36968)) ([4becc1b](https://github.com/angular/angular/commit/4becc1b))
|
|
||||||
* **compiler-cli:** ensure `getRootDirs()` handles case-sensitivity ([#36968](https://github.com/angular/angular/issues/36968)) ([5bddeea](https://github.com/angular/angular/commit/5bddeea))
|
|
||||||
* **compiler-cli:** ensure `MockFileSystem` handles case-sensitivity ([#36968](https://github.com/angular/angular/issues/36968)) ([b6c042d](https://github.com/angular/angular/commit/b6c042d))
|
|
||||||
* **compiler-cli:** ensure LogicalFileSystem handles case-sensitivity ([#36968](https://github.com/angular/angular/issues/36968)) ([65337fb](https://github.com/angular/angular/commit/65337fb))
|
|
||||||
* **compiler-cli:** fix case-sensitivity issues in NgtscCompilerHost ([#36968](https://github.com/angular/angular/issues/36968)) ([4abd603](https://github.com/angular/angular/commit/4abd603))
|
|
||||||
* **compiler-cli:** normalize mock Windows file paths correctly ([#36968](https://github.com/angular/angular/issues/36968)) ([654868f](https://github.com/angular/angular/commit/654868f))
|
|
||||||
* **compiler-cli:** use CompilerHost to ensure canonical file paths ([#36968](https://github.com/angular/angular/issues/36968)) ([7e9d5f5](https://github.com/angular/angular/commit/7e9d5f5))
|
|
||||||
* **core:** handle pluralize functions that expect a number ([#36901](https://github.com/angular/angular/issues/36901)) ([e5317d5](https://github.com/angular/angular/commit/e5317d5)), closes [#36888](https://github.com/angular/angular/issues/36888)
|
|
||||||
* **core:** properly get root nodes from embedded views with <ng-content> ([#36051](https://github.com/angular/angular/issues/36051)) ([a576852](https://github.com/angular/angular/commit/a576852)), closes [#35967](https://github.com/angular/angular/issues/35967)
|
|
||||||
* **core:** Refresh transplanted views at insertion point only ([#35968](https://github.com/angular/angular/issues/35968)) ([c8c2272](https://github.com/angular/angular/commit/c8c2272)), closes [#35400](https://github.com/angular/angular/issues/35400) [#21324](https://github.com/angular/angular/issues/21324)
|
|
||||||
* **localize:** ensure `getLocation()` works ([#36920](https://github.com/angular/angular/issues/36920)) ([701016d](https://github.com/angular/angular/commit/701016d))
|
|
||||||
* **ngcc:** do not run in parallel mode if there are less than 3 CPU cores ([#36626](https://github.com/angular/angular/issues/36626)) ([3800455](https://github.com/angular/angular/commit/3800455))
|
|
||||||
* **ngcc:** give up re-spawing crashed worker process after 3 attempts ([#36626](https://github.com/angular/angular/issues/36626)) ([1863733](https://github.com/angular/angular/commit/1863733))
|
|
||||||
* **ngcc:** handle `ENOMEM` errors in worker processes ([#36626](https://github.com/angular/angular/issues/36626)) ([901b980](https://github.com/angular/angular/commit/901b980))
|
|
||||||
* **ngcc:** support ModuleWithProviders functions that delegate ([#36948](https://github.com/angular/angular/issues/36948)) ([9d13ee0](https://github.com/angular/angular/commit/9d13ee0)), closes [#36892](https://github.com/angular/angular/issues/36892)
|
|
||||||
* **ngcc:** support recovering when a worker process crashes ([#36626](https://github.com/angular/angular/issues/36626)) ([f30307a](https://github.com/angular/angular/commit/f30307a)), closes [#36278](https://github.com/angular/angular/issues/36278)
|
|
||||||
* **ngcc:** partially support TS 3.9 wrapped ES2015 classes ([#36884](https://github.com/angular/angular/issues/36884)) ([ebb4733](https://github.com/angular/angular/commit/ebb4733))
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **ngcc:** only compute basePaths in TargetedEntryPointFinder when needed ([#36881](https://github.com/angular/angular/issues/36881)) ([5ea51b2](https://github.com/angular/angular/commit/5ea51b2)), closes [#36874](https://github.com/angular/angular/issues/36874)
|
|
||||||
* **ngcc:** speed up the `getBasePaths()` computation ([#36881](https://github.com/angular/angular/issues/36881)) ([b6d0e21](https://github.com/angular/angular/commit/b6d0e21))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.4"></a>
|
|
||||||
## [9.1.4](https://github.com/angular/angular/compare/9.1.3...9.1.4) (2020-04-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **core:** attempt to recover from user errors during creation ([#36381](https://github.com/angular/angular/issues/36381)) ([d743331](https://github.com/angular/angular/commit/d743331)), closes [#31221](https://github.com/angular/angular/issues/31221)
|
|
||||||
* **core:** handle synthetic props in Directive host bindings correctly ([#35568](https://github.com/angular/angular/issues/35568)) ([0f389fa](https://github.com/angular/angular/commit/0f389fa)), closes [#35501](https://github.com/angular/angular/issues/35501)
|
|
||||||
* **language-service:** disable update the `[@angular](https://github.com/angular)/core` module ([#36783](https://github.com/angular/angular/issues/36783)) ([d3a77ea](https://github.com/angular/angular/commit/d3a77ea))
|
|
||||||
* **localize:** include legacy ids when describing messages ([#36761](https://github.com/angular/angular/issues/36761)) ([aa94cd5](https://github.com/angular/angular/commit/aa94cd5))
|
|
||||||
* **ngcc:** recognize enum declarations emitted in JavaScript ([#36550](https://github.com/angular/angular/issues/36550)) ([c440165](https://github.com/angular/angular/commit/c440165)), closes [#35584](https://github.com/angular/angular/issues/35584)
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.3"></a>
|
|
||||||
## [9.1.3](https://github.com/angular/angular/compare/9.1.2...9.1.3) (2020-04-22)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler:** avoid generating i18n attributes in plain form ([#36422](https://github.com/angular/angular/issues/36422)) ([08b8b51](https://github.com/angular/angular/commit/08b8b51))
|
|
||||||
* **core:** do not use unbound attributes as inputs to structural directives ([#36441](https://github.com/angular/angular/issues/36441)) ([c0ed57d](https://github.com/angular/angular/commit/c0ed57d))
|
|
||||||
* **core:** handle empty translations correctly ([#36499](https://github.com/angular/angular/issues/36499)) ([a5ea100](https://github.com/angular/angular/commit/a5ea100)), closes [#36476](https://github.com/angular/angular/issues/36476)
|
|
||||||
* **core:** missing-injectable migration should not migrate `@NgModule` classes ([#36369](https://github.com/angular/angular/issues/36369)) ([0bd50e2](https://github.com/angular/angular/commit/0bd50e2)), closes [#35700](https://github.com/angular/angular/issues/35700)
|
|
||||||
* **core:** pipes injecting viewProviders when used on a component host node ([#36512](https://github.com/angular/angular/issues/36512)) ([5ae8473](https://github.com/angular/angular/commit/5ae8473)), closes [#36146](https://github.com/angular/angular/issues/36146)
|
|
||||||
* **core:** prevent unknown property check for AOT-compiled components ([#36072](https://github.com/angular/angular/issues/36072)) ([fe1d9ba](https://github.com/angular/angular/commit/fe1d9ba)), closes [#35945](https://github.com/angular/angular/issues/35945)
|
|
||||||
* **core:** properly identify modules affected by overrides in TestBed ([#36649](https://github.com/angular/angular/issues/36649)) ([9724169](https://github.com/angular/angular/commit/9724169)), closes [#36619](https://github.com/angular/angular/issues/36619)
|
|
||||||
* **language-service:** properly evaluate types in comparable expressions ([#36529](https://github.com/angular/angular/issues/36529)) ([5bab498](https://github.com/angular/angular/commit/5bab498))
|
|
||||||
* **ngcc:** display unlocker process output in sync mode ([#36637](https://github.com/angular/angular/issues/36637)) ([da159bd](https://github.com/angular/angular/commit/da159bd)), closes [/github.com/nodejs/node/issues/3596#issuecomment-250890218](https://github.com/nodejs/node/issues/3596#issuecomment-250890218)
|
|
||||||
* **ngcc:** do not use cached file-system ([#36687](https://github.com/angular/angular/issues/36687)) ([18be33a](https://github.com/angular/angular/commit/18be33a)), closes [/github.com/angular/angular-cli/issues/16860#issuecomment-614694269](https://github.com/angular/angular-cli/issues/16860#issuecomment-614694269)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.2"></a>
|
<a name="9.1.2"></a>
|
||||||
## [9.1.2](https://github.com/angular/angular/compare/9.1.1...9.1.2) (2020-04-15)
|
## [9.1.2](https://github.com/angular/angular/compare/9.1.1...9.1.2) (2020-04-15)
|
||||||
|
|
||||||
@ -497,11 +14,15 @@ This release contains various API docs improvements.
|
|||||||
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([8e7f903](https://github.com/angular/angular/commit/8e7f903)), closes [#34614](https://github.com/angular/angular/issues/34614)
|
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([8e7f903](https://github.com/angular/angular/commit/8e7f903)), closes [#34614](https://github.com/angular/angular/issues/34614)
|
||||||
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([a67afcc](https://github.com/angular/angular/commit/a67afcc)), closes [#36492](https://github.com/angular/angular/issues/36492)
|
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([a67afcc](https://github.com/angular/angular/commit/a67afcc)), closes [#36492](https://github.com/angular/angular/issues/36492)
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([e06512b](https://github.com/angular/angular/commit/e06512b)) * **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([918e628](https://github.com/angular/angular/commit/918e628)), closes [#issuecomment-608401834](https://github.com/angular/angular#issuecomment-608401834)
|
|
||||||
|
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([e06512b](https://github.com/angular/angular/commit/e06512b))
|
||||||
|
* **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([918e628](https://github.com/angular/angular/commit/918e628)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
|
||||||
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([603b094](https://github.com/angular/angular/commit/603b094))
|
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([603b094](https://github.com/angular/angular/commit/603b094))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.1"></a>
|
<a name="9.1.1"></a>
|
||||||
## [9.1.1](https://github.com/angular/angular/compare/9.1.0...9.1.1) (2020-04-07)
|
## [9.1.1](https://github.com/angular/angular/compare/9.1.0...9.1.1) (2020-04-07)
|
||||||
|
|
||||||
@ -518,13 +39,13 @@ This release contains various API docs improvements.
|
|||||||
* **core:** undecorated-classes migration should handle derived abstract classes ([#35339](https://github.com/angular/angular/issues/35339)) ([a631b99](https://github.com/angular/angular/commit/a631b99))
|
* **core:** undecorated-classes migration should handle derived abstract classes ([#35339](https://github.com/angular/angular/issues/35339)) ([a631b99](https://github.com/angular/angular/commit/a631b99))
|
||||||
* **language-service:** infer type of elements of array-like objects ([#36312](https://github.com/angular/angular/issues/36312)) ([ff523c9](https://github.com/angular/angular/commit/ff523c9)), closes [#36191](https://github.com/angular/angular/issues/36191)
|
* **language-service:** infer type of elements of array-like objects ([#36312](https://github.com/angular/angular/issues/36312)) ([ff523c9](https://github.com/angular/angular/commit/ff523c9)), closes [#36191](https://github.com/angular/angular/issues/36191)
|
||||||
* **language-service:** use the `HtmlAst` to get the span of HTML tag ([#36371](https://github.com/angular/angular/issues/36371)) ([ffa4e11](https://github.com/angular/angular/commit/ffa4e11))
|
* **language-service:** use the `HtmlAst` to get the span of HTML tag ([#36371](https://github.com/angular/angular/issues/36371)) ([ffa4e11](https://github.com/angular/angular/commit/ffa4e11))
|
||||||
* **ngcc:** add process title ([#36448](https://github.com/angular/angular/issues/36448)) ([136596d](https://github.com/angular/angular/commit/136596d)), closes [36414#issuecomment-609644282](https://github.com/angular/angular/issues/36414#issuecomment-609644282)
|
* **ngcc:** add process title ([#36448](https://github.com/angular/angular/issues/36448)) ([136596d](https://github.com/angular/angular/commit/136596d)), closes [/github.com/angular/angular/issues/36414#issuecomment-609644282](https://github.com//github.com/angular/angular/issues/36414/issues/issuecomment-609644282)
|
||||||
* **ngcc:** allow ngcc configuration to match pre-release versions of packages ([#36370](https://github.com/angular/angular/issues/36370)) ([cb0a2a0](https://github.com/angular/angular/commit/cb0a2a0))
|
* **ngcc:** allow ngcc configuration to match pre-release versions of packages ([#36370](https://github.com/angular/angular/issues/36370)) ([cb0a2a0](https://github.com/angular/angular/commit/cb0a2a0))
|
||||||
* **ngcc:** correctly detect imported TypeScript helpers ([#36284](https://github.com/angular/angular/issues/36284)) ([879457c](https://github.com/angular/angular/commit/879457c)), closes [#36089](https://github.com/angular/angular/issues/36089)
|
* **ngcc:** correctly detect imported TypeScript helpers ([#36284](https://github.com/angular/angular/issues/36284)) ([879457c](https://github.com/angular/angular/commit/879457c)), closes [#36089](https://github.com/angular/angular/issues/36089)
|
||||||
* **ngcc:** correctly identify relative Windows-style import paths ([#36372](https://github.com/angular/angular/issues/36372)) ([0daa488](https://github.com/angular/angular/commit/0daa488))
|
* **ngcc:** correctly identify relative Windows-style import paths ([#36372](https://github.com/angular/angular/issues/36372)) ([0daa488](https://github.com/angular/angular/commit/0daa488))
|
||||||
* **ngcc:** correctly identify the package path of secondary entry-points ([#36249](https://github.com/angular/angular/issues/36249)) ([e53b686](https://github.com/angular/angular/commit/e53b686)), closes [#35747](https://github.com/angular/angular/issues/35747)
|
* **ngcc:** correctly identify the package path of secondary entry-points ([#36249](https://github.com/angular/angular/issues/36249)) ([e53b686](https://github.com/angular/angular/commit/e53b686)), closes [#35747](https://github.com/angular/angular/issues/35747)
|
||||||
* **ngcc:** detect non-emitted, non-imported TypeScript helpers ([#36418](https://github.com/angular/angular/issues/36418)) ([93b32d3](https://github.com/angular/angular/commit/93b32d3))
|
* **ngcc:** detect non-emitted, non-imported TypeScript helpers ([#36418](https://github.com/angular/angular/issues/36418)) ([93b32d3](https://github.com/angular/angular/commit/93b32d3))
|
||||||
* **ngcc:** do not spawn more processes than intended in parallel mode ([#36280](https://github.com/angular/angular/issues/36280)) ([6ea232e](https://github.com/angular/angular/commit/6ea232e)), closes [#35719](https://github.com/angular/angular/issues/35719) [#36278](https://github.com/angular/angular/issues/36278) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts#L429](https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts/issues/L429) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L108](https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L108) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L110](https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L110) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L199](https://github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L199)
|
* **ngcc:** do not spawn more processes than intended in parallel mode ([#36280](https://github.com/angular/angular/issues/36280)) ([6ea232e](https://github.com/angular/angular/commit/6ea232e)), closes [#35719](https://github.com/angular/angular/issues/35719) [#36278](https://github.com/angular/angular/issues/36278) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts#L429](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts/issues/L429) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L108](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L108) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L110](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L110) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L199](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L199)
|
||||||
* **ngcc:** do not write entry-point manifest outside node_modules ([#36299](https://github.com/angular/angular/issues/36299)) ([bb8744d](https://github.com/angular/angular/commit/bb8744d)), closes [#36296](https://github.com/angular/angular/issues/36296)
|
* **ngcc:** do not write entry-point manifest outside node_modules ([#36299](https://github.com/angular/angular/issues/36299)) ([bb8744d](https://github.com/angular/angular/commit/bb8744d)), closes [#36296](https://github.com/angular/angular/issues/36296)
|
||||||
* **ngcc:** don't crash on cyclic source-map references ([#36452](https://github.com/angular/angular/issues/36452)) ([56af303](https://github.com/angular/angular/commit/56af303)), closes [#35727](https://github.com/angular/angular/issues/35727) [#35757](https://github.com/angular/angular/issues/35757)
|
* **ngcc:** don't crash on cyclic source-map references ([#36452](https://github.com/angular/angular/issues/36452)) ([56af303](https://github.com/angular/angular/commit/56af303)), closes [#35727](https://github.com/angular/angular/issues/35727) [#35757](https://github.com/angular/angular/issues/35757)
|
||||||
* **ngcc:** handle bad path mappings when finding entry-points ([#36331](https://github.com/angular/angular/issues/36331)) ([7bb3588](https://github.com/angular/angular/commit/7bb3588)), closes [#36313](https://github.com/angular/angular/issues/36313) [#36283](https://github.com/angular/angular/issues/36283)
|
* **ngcc:** handle bad path mappings when finding entry-points ([#36331](https://github.com/angular/angular/issues/36331)) ([7bb3588](https://github.com/angular/angular/commit/7bb3588)), closes [#36313](https://github.com/angular/angular/issues/36313) [#36283](https://github.com/angular/angular/issues/36283)
|
||||||
@ -536,6 +57,7 @@ This release contains various API docs improvements.
|
|||||||
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([0e7a89a](https://github.com/angular/angular/commit/0e7a89a)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([0e7a89a](https://github.com/angular/angular/commit/0e7a89a)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.0"></a>
|
<a name="9.1.0"></a>
|
||||||
# [9.1.0](https://github.com/angular/angular/compare/9.0.0...9.1.0) (2020-03-25)
|
# [9.1.0](https://github.com/angular/angular/compare/9.0.0...9.1.0) (2020-03-25)
|
||||||
|
|
||||||
@ -694,7 +216,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ngcc:** use path-mappings from tsconfig in dependency resolution ([#36180](https://github.com/angular/angular/issues/36180)) ([6defe96](https://github.com/angular/angular/commit/6defe96)), closes [#36119](https://github.com/angular/angular/issues/36119)
|
* **ngcc:** use path-mappings from tsconfig in dependency resolution ([#36180](https://github.com/angular/angular/issues/36180)) ([6defe96](https://github.com/angular/angular/commit/6defe96)), closes [#36119](https://github.com/angular/angular/issues/36119)
|
||||||
* **ngcc:** use preserve whitespaces from tsconfig if provided ([#36189](https://github.com/angular/angular/issues/36189)) ([aef4323](https://github.com/angular/angular/commit/aef4323)), closes [#35871](https://github.com/angular/angular/issues/35871)
|
* **ngcc:** use preserve whitespaces from tsconfig if provided ([#36189](https://github.com/angular/angular/issues/36189)) ([aef4323](https://github.com/angular/angular/commit/aef4323)), closes [#35871](https://github.com/angular/angular/issues/35871)
|
||||||
* **platform-browser:** add missing peerDependency on `[@angular](https://github.com/angular)/animations` ([#35949](https://github.com/angular/angular/issues/35949)) ([64d6f13](https://github.com/angular/angular/commit/64d6f13)), closes [#35888](https://github.com/angular/angular/issues/35888)
|
* **platform-browser:** add missing peerDependency on `[@angular](https://github.com/angular)/animations` ([#35949](https://github.com/angular/angular/issues/35949)) ([64d6f13](https://github.com/angular/angular/commit/64d6f13)), closes [#35888](https://github.com/angular/angular/issues/35888)
|
||||||
* **router:** removed unused ApplicationRef dependency ([#35642](https://github.com/angular/angular/issues/35642)) ([c839c05](https://github.com/angular/angular/commit/c839c05)), closes [/github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97#diff-c0baae5e1df628e1a217e8dc38557](https://github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97/issues/diff-c0baae5e1df628e1a217e8dc38557)
|
* **router:** removed unused ApplicationRef dependency ([#35642](https://github.com/angular/angular/issues/35642)) ([c839c05](https://github.com/angular/angular/commit/c839c05)), closes [/github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97#diff-c0baae5e1df628e1a217e8dc38557](https://github.com//github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97/issues/diff-c0baae5e1df628e1a217e8dc38557)
|
||||||
* **router:** state data missing in routerLink ([#33203](https://github.com/angular/angular/issues/33203)) ([de67978](https://github.com/angular/angular/commit/de67978))
|
* **router:** state data missing in routerLink ([#33203](https://github.com/angular/angular/issues/33203)) ([de67978](https://github.com/angular/angular/commit/de67978))
|
||||||
* **service-worker:** treat 503 as offline ([#35595](https://github.com/angular/angular/issues/35595)) ([96cdf03](https://github.com/angular/angular/commit/96cdf03)), closes [#35571](https://github.com/angular/angular/issues/35571)
|
* **service-worker:** treat 503 as offline ([#35595](https://github.com/angular/angular/issues/35595)) ([96cdf03](https://github.com/angular/angular/commit/96cdf03)), closes [#35571](https://github.com/angular/angular/issues/35571)
|
||||||
* fix flaky test cases of passive events ([#35679](https://github.com/angular/angular/issues/35679)) ([8ef29b6](https://github.com/angular/angular/commit/8ef29b6))
|
* fix flaky test cases of passive events ([#35679](https://github.com/angular/angular/issues/35679)) ([8ef29b6](https://github.com/angular/angular/commit/8ef29b6))
|
||||||
@ -829,7 +351,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ngcc:** correctly detect emitted TS helpers in ES5 ([#35191](https://github.com/angular/angular/issues/35191)) ([af4fe3a](https://github.com/angular/angular/commit/af4fe3a))
|
* **ngcc:** correctly detect emitted TS helpers in ES5 ([#35191](https://github.com/angular/angular/issues/35191)) ([af4fe3a](https://github.com/angular/angular/commit/af4fe3a))
|
||||||
* **ngcc:** correctly detect outer aliased class identifiers in ES5 ([#35527](https://github.com/angular/angular/issues/35527)) ([39bd9a7](https://github.com/angular/angular/commit/39bd9a7)), closes [#35399](https://github.com/angular/angular/issues/35399)
|
* **ngcc:** correctly detect outer aliased class identifiers in ES5 ([#35527](https://github.com/angular/angular/issues/35527)) ([39bd9a7](https://github.com/angular/angular/commit/39bd9a7)), closes [#35399](https://github.com/angular/angular/issues/35399)
|
||||||
* **ngcc:** handle imports in dts files when processing CommonJS ([#35191](https://github.com/angular/angular/issues/35191)) ([12e3db8](https://github.com/angular/angular/commit/12e3db8)), closes [#34356](https://github.com/angular/angular/issues/34356)
|
* **ngcc:** handle imports in dts files when processing CommonJS ([#35191](https://github.com/angular/angular/issues/35191)) ([12e3db8](https://github.com/angular/angular/commit/12e3db8)), closes [#34356](https://github.com/angular/angular/issues/34356)
|
||||||
* **router:** removed unused ApplicationRef dependency ([#35642](https://github.com/angular/angular/issues/35642)) ([2f140f5](https://github.com/angular/angular/commit/2f140f5)), closes [/github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97#diff-c0baae5e1df628e1a217e8dc38557](https://github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97/issues/diff-c0baae5e1df628e1a217e8dc38557)
|
* **router:** removed unused ApplicationRef dependency ([#35642](https://github.com/angular/angular/issues/35642)) ([2f140f5](https://github.com/angular/angular/commit/2f140f5)), closes [/github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97#diff-c0baae5e1df628e1a217e8dc38557](https://github.com//github.com/angular/angular/commit/5a849829c42330d7e88e83e916e6e36380c97a97/issues/diff-c0baae5e1df628e1a217e8dc38557)
|
||||||
* **service-worker:** treat 503 as offline ([#35595](https://github.com/angular/angular/issues/35595)) ([64a415b](https://github.com/angular/angular/commit/64a415b)), closes [#35571](https://github.com/angular/angular/issues/35571)
|
* **service-worker:** treat 503 as offline ([#35595](https://github.com/angular/angular/issues/35595)) ([64a415b](https://github.com/angular/angular/commit/64a415b)), closes [#35571](https://github.com/angular/angular/issues/35571)
|
||||||
|
|
||||||
|
|
||||||
@ -906,7 +428,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** support emitting a reference to interface declarations ([#34849](https://github.com/angular/angular/issues/34849)) ([ad9ec52](https://github.com/angular/angular/commit/ad9ec52)), closes [#34021](https://github.com/angular/angular/issues/34021) [#34837](https://github.com/angular/angular/issues/34837)
|
* **ivy:** support emitting a reference to interface declarations ([#34849](https://github.com/angular/angular/issues/34849)) ([ad9ec52](https://github.com/angular/angular/commit/ad9ec52)), closes [#34021](https://github.com/angular/angular/issues/34021) [#34837](https://github.com/angular/angular/issues/34837)
|
||||||
* **ivy:** template type-check errors from TS should not use NG error codes ([#35146](https://github.com/angular/angular/issues/35146)) ([cf3071f](https://github.com/angular/angular/commit/cf3071f))
|
* **ivy:** template type-check errors from TS should not use NG error codes ([#35146](https://github.com/angular/angular/issues/35146)) ([cf3071f](https://github.com/angular/angular/commit/cf3071f))
|
||||||
* **ivy:** host-styling throws assert exception inside *ngFor ([#35133](https://github.com/angular/angular/issues/35133)) ([31e9873](https://github.com/angular/angular/commit/31e9873)), closes [#35118](https://github.com/angular/angular/issues/35118)
|
* **ivy:** host-styling throws assert exception inside *ngFor ([#35133](https://github.com/angular/angular/issues/35133)) ([31e9873](https://github.com/angular/angular/commit/31e9873)), closes [#35118](https://github.com/angular/angular/issues/35118)
|
||||||
* **ngcc:** correctly invalidate cache when moving/removing files/directories ([#35106](https://github.com/angular/angular/issues/35106)) ([22357d4](https://github.com/angular/angular/commit/22357d4)), closes [/github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L54](https://github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts/issues/L54) [/github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L61](https://github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts/issues/L61) [#35095](https://github.com/angular/angular/issues/35095)
|
* **ngcc:** correctly invalidate cache when moving/removing files/directories ([#35106](https://github.com/angular/angular/issues/35106)) ([22357d4](https://github.com/angular/angular/commit/22357d4)), closes [/github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L54](https://github.com//github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts/issues/L54) [/github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L61](https://github.com//github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts/issues/L61) [#35095](https://github.com/angular/angular/issues/35095)
|
||||||
* **ngcc:** do not lock if the target is not compiled by Angular ([#35057](https://github.com/angular/angular/issues/35057)) ([c30c518](https://github.com/angular/angular/commit/c30c518)), closes [#35000](https://github.com/angular/angular/issues/35000)
|
* **ngcc:** do not lock if the target is not compiled by Angular ([#35057](https://github.com/angular/angular/issues/35057)) ([c30c518](https://github.com/angular/angular/commit/c30c518)), closes [#35000](https://github.com/angular/angular/issues/35000)
|
||||||
* **compiler-cli:** add `sass` as a valid css preprocessor extension ([#35052](https://github.com/angular/angular/issues/35052)) ([7f96fbb](https://github.com/angular/angular/commit/7f96fbb))
|
* **compiler-cli:** add `sass` as a valid css preprocessor extension ([#35052](https://github.com/angular/angular/issues/35052)) ([7f96fbb](https://github.com/angular/angular/commit/7f96fbb))
|
||||||
* should also allow subclass Promise without Symbol.species ([#34533](https://github.com/angular/angular/issues/34533)) ([0de632a](https://github.com/angular/angular/commit/0de632a))
|
* should also allow subclass Promise without Symbol.species ([#34533](https://github.com/angular/angular/issues/34533)) ([0de632a](https://github.com/angular/angular/commit/0de632a))
|
||||||
@ -916,10 +438,10 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** ensure `DebugNode`/`DebugElement` are tree-shakeable in Ivy ([#35003](https://github.com/angular/angular/issues/35003)) ([23c0147](https://github.com/angular/angular/commit/23c0147)), closes [#30130](https://github.com/angular/angular/issues/30130)
|
* **ivy:** ensure `DebugNode`/`DebugElement` are tree-shakeable in Ivy ([#35003](https://github.com/angular/angular/issues/35003)) ([23c0147](https://github.com/angular/angular/commit/23c0147)), closes [#30130](https://github.com/angular/angular/issues/30130)
|
||||||
* **ivy:** ensure multi providers in ModuleWithProviders are not duplicated ([#34914](https://github.com/angular/angular/issues/34914)) ([4975f89](https://github.com/angular/angular/commit/4975f89))
|
* **ivy:** ensure multi providers in ModuleWithProviders are not duplicated ([#34914](https://github.com/angular/angular/issues/34914)) ([4975f89](https://github.com/angular/angular/commit/4975f89))
|
||||||
* **ivy:** update ViewContainerRef to get the correct parentInjector ([#35013](https://github.com/angular/angular/issues/35013)) ([eaa4a5a](https://github.com/angular/angular/commit/eaa4a5a))
|
* **ivy:** update ViewContainerRef to get the correct parentInjector ([#35013](https://github.com/angular/angular/issues/35013)) ([eaa4a5a](https://github.com/angular/angular/commit/eaa4a5a))
|
||||||
* **language-service:** prune duplicate returned definitions ([#34995](https://github.com/angular/angular/issues/34995)) ([71f5417](https://github.com/angular/angular/commit/71f5417)), closes [/github.com/angular/angular/pull/34847#discussion_r371006680](https://github.com/angular/angular/pull/34847/issues/discussion_r371006680)
|
* **language-service:** prune duplicate returned definitions ([#34995](https://github.com/angular/angular/issues/34995)) ([71f5417](https://github.com/angular/angular/commit/71f5417)), closes [/github.com/angular/angular/pull/34847#discussion_r371006680](https://github.com//github.com/angular/angular/pull/34847/issues/discussion_r371006680)
|
||||||
* **language-service:** remove repeated symbol definitions for structural directive ([#34847](https://github.com/angular/angular/issues/34847)) ([35916d3](https://github.com/angular/angular/commit/35916d3))
|
* **language-service:** remove repeated symbol definitions for structural directive ([#34847](https://github.com/angular/angular/issues/34847)) ([35916d3](https://github.com/angular/angular/commit/35916d3))
|
||||||
* **language-service:** warn, not error, on missing context members ([#35036](https://github.com/angular/angular/issues/35036)) ([0e76821](https://github.com/angular/angular/commit/0e76821))
|
* **language-service:** warn, not error, on missing context members ([#35036](https://github.com/angular/angular/issues/35036)) ([0e76821](https://github.com/angular/angular/commit/0e76821))
|
||||||
* **localize:** re-enable filename in code-frame error messages ([#34994](https://github.com/angular/angular/issues/34994)) ([c7c7b20](https://github.com/angular/angular/commit/c7c7b20)), closes [/github.com/angular/angular/pull/34974/files#r371034476](https://github.com/angular/angular/pull/34974/files/issues/r371034476)
|
* **localize:** re-enable filename in code-frame error messages ([#34994](https://github.com/angular/angular/issues/34994)) ([c7c7b20](https://github.com/angular/angular/commit/c7c7b20)), closes [/github.com/angular/angular/pull/34974/files#r371034476](https://github.com//github.com/angular/angular/pull/34974/files/issues/r371034476)
|
||||||
* **ngcc:** improve lockfile error message ([#35001](https://github.com/angular/angular/issues/35001)) ([1d31c81](https://github.com/angular/angular/commit/1d31c81)), closes [#35000](https://github.com/angular/angular/issues/35000)
|
* **ngcc:** improve lockfile error message ([#35001](https://github.com/angular/angular/issues/35001)) ([1d31c81](https://github.com/angular/angular/commit/1d31c81)), closes [#35000](https://github.com/angular/angular/issues/35000)
|
||||||
* **ivy:** correctly emit component when it's removed from its module ([#34912](https://github.com/angular/angular/issues/34912)) ([adc663e](https://github.com/angular/angular/commit/adc663e)), closes [#34813](https://github.com/angular/angular/issues/34813)
|
* **ivy:** correctly emit component when it's removed from its module ([#34912](https://github.com/angular/angular/issues/34912)) ([adc663e](https://github.com/angular/angular/commit/adc663e)), closes [#34813](https://github.com/angular/angular/issues/34813)
|
||||||
* **ivy:** DebugNode.classes not working on SVG elements ([#34872](https://github.com/angular/angular/issues/34872)) ([7e8aac1](https://github.com/angular/angular/commit/7e8aac1)), closes [#34868](https://github.com/angular/angular/issues/34868)
|
* **ivy:** DebugNode.classes not working on SVG elements ([#34872](https://github.com/angular/angular/issues/34872)) ([7e8aac1](https://github.com/angular/angular/commit/7e8aac1)), closes [#34868](https://github.com/angular/angular/issues/34868)
|
||||||
@ -942,7 +464,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **compiler-cli:** require node 10 as runtime engine ([#34722](https://github.com/angular/angular/issues/34722)) ([7b77b3d](https://github.com/angular/angular/commit/7b77b3d))
|
* **compiler-cli:** require node 10 as runtime engine ([#34722](https://github.com/angular/angular/issues/34722)) ([7b77b3d](https://github.com/angular/angular/commit/7b77b3d))
|
||||||
* **language-service:** specific suggestions for template context diags ([#34751](https://github.com/angular/angular/issues/34751)) ([cc7fca4](https://github.com/angular/angular/commit/cc7fca4))
|
* **language-service:** specific suggestions for template context diags ([#34751](https://github.com/angular/angular/issues/34751)) ([cc7fca4](https://github.com/angular/angular/commit/cc7fca4))
|
||||||
* **language-service:** support multiple symbol definitions ([#34782](https://github.com/angular/angular/issues/34782)) ([2f2396c](https://github.com/angular/angular/commit/2f2396c))
|
* **language-service:** support multiple symbol definitions ([#34782](https://github.com/angular/angular/issues/34782)) ([2f2396c](https://github.com/angular/angular/commit/2f2396c))
|
||||||
* **ngcc:** lock ngcc when processing ([#34722](https://github.com/angular/angular/issues/34722)) ([6dd51f1](https://github.com/angular/angular/commit/6dd51f1)), closes [32431#issuecomment-571825781](https://github.com/angular/angular/issues/32431#issuecomment-571825781)
|
* **ngcc:** lock ngcc when processing ([#34722](https://github.com/angular/angular/issues/34722)) ([6dd51f1](https://github.com/angular/angular/commit/6dd51f1)), closes [/github.com/angular/angular/issues/32431#issuecomment-571825781](https://github.com//github.com/angular/angular/issues/32431/issues/issuecomment-571825781)
|
||||||
* work around 'noImplicityAny' incompatibility due to ts3.7 update ([#34798](https://github.com/angular/angular/issues/34798)) ([251d548](https://github.com/angular/angular/commit/251d548))
|
* work around 'noImplicityAny' incompatibility due to ts3.7 update ([#34798](https://github.com/angular/angular/issues/34798)) ([251d548](https://github.com/angular/angular/commit/251d548))
|
||||||
* **animations:** not waiting for child animations to finish when removing parent in Ivy ([#34702](https://github.com/angular/angular/issues/34702)) ([92c17fe](https://github.com/angular/angular/commit/92c17fe)), closes [#33597](https://github.com/angular/angular/issues/33597)
|
* **animations:** not waiting for child animations to finish when removing parent in Ivy ([#34702](https://github.com/angular/angular/issues/34702)) ([92c17fe](https://github.com/angular/angular/commit/92c17fe)), closes [#33597](https://github.com/angular/angular/issues/33597)
|
||||||
* **common:** ensure diffing in ngStyle/ngClass correctly emits value changes ([#34307](https://github.com/angular/angular/issues/34307)) ([93a035f](https://github.com/angular/angular/commit/93a035f)), closes [#34336](https://github.com/angular/angular/issues/34336) [#34444](https://github.com/angular/angular/issues/34444)
|
* **common:** ensure diffing in ngStyle/ngClass correctly emits value changes ([#34307](https://github.com/angular/angular/issues/34307)) ([93a035f](https://github.com/angular/angular/commit/93a035f)), closes [#34336](https://github.com/angular/angular/issues/34336) [#34444](https://github.com/angular/angular/issues/34444)
|
||||||
@ -957,19 +479,19 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** ngClass not applying classes with trailing/leading spaces ([#34539](https://github.com/angular/angular/issues/34539)) ([98ed7c6](https://github.com/angular/angular/commit/98ed7c6)), closes [#34476](https://github.com/angular/angular/issues/34476)
|
* **ivy:** ngClass not applying classes with trailing/leading spaces ([#34539](https://github.com/angular/angular/issues/34539)) ([98ed7c6](https://github.com/angular/angular/commit/98ed7c6)), closes [#34476](https://github.com/angular/angular/issues/34476)
|
||||||
* **ivy:** Prevent errors when querying DebugElement roots that were outside angular context ([#34687](https://github.com/angular/angular/issues/34687)) ([f1cdb8f](https://github.com/angular/angular/commit/f1cdb8f))
|
* **ivy:** Prevent errors when querying DebugElement roots that were outside angular context ([#34687](https://github.com/angular/angular/issues/34687)) ([f1cdb8f](https://github.com/angular/angular/commit/f1cdb8f))
|
||||||
* **ivy:** properly bootstrap components with attribute selectors ([#34450](https://github.com/angular/angular/issues/34450)) ([2c0b9ea](https://github.com/angular/angular/commit/2c0b9ea)), closes [#34349](https://github.com/angular/angular/issues/34349)
|
* **ivy:** properly bootstrap components with attribute selectors ([#34450](https://github.com/angular/angular/issues/34450)) ([2c0b9ea](https://github.com/angular/angular/commit/2c0b9ea)), closes [#34349](https://github.com/angular/angular/issues/34349)
|
||||||
* **ivy:** warn instead of throwing for unknown elements ([#34524](https://github.com/angular/angular/issues/34524)) ([d9ae70e](https://github.com/angular/angular/commit/d9ae70e)), closes [/github.com/angular/angular/pull/33419#discussion_r339296216](https://github.com/angular/angular/pull/33419/issues/discussion_r339296216)
|
* **ivy:** warn instead of throwing for unknown elements ([#34524](https://github.com/angular/angular/issues/34524)) ([d9ae70e](https://github.com/angular/angular/commit/d9ae70e)), closes [/github.com/angular/angular/pull/33419#discussion_r339296216](https://github.com//github.com/angular/angular/pull/33419/issues/discussion_r339296216)
|
||||||
* **language-service:** apply suggested change. ([#34564](https://github.com/angular/angular/issues/34564)) ([b3af220](https://github.com/angular/angular/commit/b3af220))
|
* **language-service:** apply suggested change. ([#34564](https://github.com/angular/angular/issues/34564)) ([b3af220](https://github.com/angular/angular/commit/b3af220))
|
||||||
* **language-service:** break the hover/definitions for two-way binding ([#34564](https://github.com/angular/angular/issues/34564)) ([eb5c20c](https://github.com/angular/angular/commit/eb5c20c))
|
* **language-service:** break the hover/definitions for two-way binding ([#34564](https://github.com/angular/angular/issues/34564)) ([eb5c20c](https://github.com/angular/angular/commit/eb5c20c))
|
||||||
* **language-service:** CRLF offset in inline template ([#34737](https://github.com/angular/angular/issues/34737)) ([80315b5](https://github.com/angular/angular/commit/80315b5))
|
* **language-service:** CRLF offset in inline template ([#34737](https://github.com/angular/angular/issues/34737)) ([80315b5](https://github.com/angular/angular/commit/80315b5))
|
||||||
* **language-service:** do not use an i18n parser for templates ([#34531](https://github.com/angular/angular/issues/34531)) ([cb7dcb3](https://github.com/angular/angular/commit/cb7dcb3))
|
* **language-service:** do not use an i18n parser for templates ([#34531](https://github.com/angular/angular/issues/34531)) ([cb7dcb3](https://github.com/angular/angular/commit/cb7dcb3))
|
||||||
* **language-service:** only visit directives ([#34564](https://github.com/angular/angular/issues/34564)) ([76d7aa7](https://github.com/angular/angular/commit/76d7aa7))
|
* **language-service:** only visit directives ([#34564](https://github.com/angular/angular/issues/34564)) ([76d7aa7](https://github.com/angular/angular/commit/76d7aa7))
|
||||||
* **ngcc:** avoid error due to circular dependency in `EsmDependencyHost` ([#34512](https://github.com/angular/angular/issues/34512)) ([7c3172a](https://github.com/angular/angular/commit/7c3172a)), closes [/github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/utils.ts#L10](https://github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/utils.ts/issues/L10) [/github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts#L10](https://github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts/issues/L10) [/github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts#L9](https://github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts/issues/L9)
|
* **ngcc:** avoid error due to circular dependency in `EsmDependencyHost` ([#34512](https://github.com/angular/angular/issues/34512)) ([7c3172a](https://github.com/angular/angular/commit/7c3172a)), closes [/github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/utils.ts#L10](https://github.com//github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/utils.ts/issues/L10) [/github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts#L10](https://github.com//github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/esm_dependency_host.ts/issues/L10) [/github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts#L9](https://github.com//github.com/angular/angular/blob/18d89c9c8/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts/issues/L9)
|
||||||
* **ngcc:** correctly detect dependencies in CommonJS ([#34528](https://github.com/angular/angular/issues/34528)) ([ff02ddf](https://github.com/angular/angular/commit/ff02ddf))
|
* **ngcc:** correctly detect dependencies in CommonJS ([#34528](https://github.com/angular/angular/issues/34528)) ([ff02ddf](https://github.com/angular/angular/commit/ff02ddf))
|
||||||
* **ngcc:** correctly handle inline exports in UMD ([#34512](https://github.com/angular/angular/issues/34512)) ([7bbfccf](https://github.com/angular/angular/commit/7bbfccf))
|
* **ngcc:** correctly handle inline exports in UMD ([#34512](https://github.com/angular/angular/issues/34512)) ([7bbfccf](https://github.com/angular/angular/commit/7bbfccf))
|
||||||
* **ngcc:** don't crash if symbol has no declarations ([#34658](https://github.com/angular/angular/issues/34658)) ([4def99e](https://github.com/angular/angular/commit/4def99e)), closes [/github.com/angular/angular/blob/8d0de89e/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts#L309](https://github.com/angular/angular/blob/8d0de89e/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts/issues/L309) [#34560](https://github.com/angular/angular/issues/34560)
|
* **ngcc:** don't crash if symbol has no declarations ([#34658](https://github.com/angular/angular/issues/34658)) ([4def99e](https://github.com/angular/angular/commit/4def99e)), closes [/github.com/angular/angular/blob/8d0de89e/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts#L309](https://github.com//github.com/angular/angular/blob/8d0de89e/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts/issues/L309) [#34560](https://github.com/angular/angular/issues/34560)
|
||||||
* **ngcc:** handle UMD factories that do not use all params ([#34660](https://github.com/angular/angular/issues/34660)) ([83868be](https://github.com/angular/angular/commit/83868be)), closes [#34653](https://github.com/angular/angular/issues/34653)
|
* **ngcc:** handle UMD factories that do not use all params ([#34660](https://github.com/angular/angular/issues/34660)) ([83868be](https://github.com/angular/angular/commit/83868be)), closes [#34653](https://github.com/angular/angular/issues/34653)
|
||||||
* **ngcc:** insert definitions after statement ([#34677](https://github.com/angular/angular/issues/34677)) ([f295240](https://github.com/angular/angular/commit/f295240)), closes [#34648](https://github.com/angular/angular/issues/34648)
|
* **ngcc:** insert definitions after statement ([#34677](https://github.com/angular/angular/issues/34677)) ([f295240](https://github.com/angular/angular/commit/f295240)), closes [#34648](https://github.com/angular/angular/issues/34648)
|
||||||
* **ngcc:** recognize re-exports with `require()` calls in UMD ([#34512](https://github.com/angular/angular/issues/34512)) ([79be354](https://github.com/angular/angular/commit/79be354)), closes [/github.com/angular/angular/pull/34254/files#r359515373](https://github.com/angular/angular/pull/34254/files/issues/r359515373)
|
* **ngcc:** recognize re-exports with `require()` calls in UMD ([#34512](https://github.com/angular/angular/issues/34512)) ([79be354](https://github.com/angular/angular/commit/79be354)), closes [/github.com/angular/angular/pull/34254/files#r359515373](https://github.com//github.com/angular/angular/pull/34254/files/issues/r359515373)
|
||||||
* **ngcc:** recognize re-exports with imported TS helpers in CommonJS and UMD ([#34527](https://github.com/angular/angular/issues/34527)) ([a88dc17](https://github.com/angular/angular/commit/a88dc17))
|
* **ngcc:** recognize re-exports with imported TS helpers in CommonJS and UMD ([#34527](https://github.com/angular/angular/issues/34527)) ([a88dc17](https://github.com/angular/angular/commit/a88dc17))
|
||||||
* **common:** remove trailing whitespace for CurrencyPipe ([#34642](https://github.com/angular/angular/issues/34642)) ([c42b90b](https://github.com/angular/angular/commit/c42b90b)), closes [#34641](https://github.com/angular/angular/issues/34641)
|
* **common:** remove trailing whitespace for CurrencyPipe ([#34642](https://github.com/angular/angular/issues/34642)) ([c42b90b](https://github.com/angular/angular/commit/c42b90b)), closes [#34641](https://github.com/angular/angular/issues/34641)
|
||||||
* **ivy:** append `advance` instructions before `i18nExp` ([#34436](https://github.com/angular/angular/issues/34436)) ([ba4c31c](https://github.com/angular/angular/commit/ba4c31c))
|
* **ivy:** append `advance` instructions before `i18nExp` ([#34436](https://github.com/angular/angular/issues/34436)) ([ba4c31c](https://github.com/angular/angular/commit/ba4c31c))
|
||||||
@ -1016,7 +538,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** add flag to skip non-exported classes ([#33921](https://github.com/angular/angular/issues/33921)) ([#34340](https://github.com/angular/angular/issues/34340)) ([7ed984b](https://github.com/angular/angular/commit/7ed984b)), closes [#33724](https://github.com/angular/angular/issues/33724)
|
* **ivy:** add flag to skip non-exported classes ([#33921](https://github.com/angular/angular/issues/33921)) ([#34340](https://github.com/angular/angular/issues/34340)) ([7ed984b](https://github.com/angular/angular/commit/7ed984b)), closes [#33724](https://github.com/angular/angular/issues/33724)
|
||||||
* **ivy:** align TestBed.overrideProvider with what happens with providers in TestBed providers array ([#33769](https://github.com/angular/angular/issues/33769)) ([10a33ef](https://github.com/angular/angular/commit/10a33ef))
|
* **ivy:** align TestBed.overrideProvider with what happens with providers in TestBed providers array ([#33769](https://github.com/angular/angular/issues/33769)) ([10a33ef](https://github.com/angular/angular/commit/10a33ef))
|
||||||
* **ivy:** do not invoke change detection for destroyed views ([#34241](https://github.com/angular/angular/issues/34241)) ([24bbcaf](https://github.com/angular/angular/commit/24bbcaf))
|
* **ivy:** do not invoke change detection for destroyed views ([#34241](https://github.com/angular/angular/issues/34241)) ([24bbcaf](https://github.com/angular/angular/commit/24bbcaf))
|
||||||
* **ivy:** handle SafeStyles in [style.prop] correctly ([#34286](https://github.com/angular/angular/issues/34286)) ([b0d5784](https://github.com/angular/angular/commit/b0d5784)), closes [/github.com/angular/angular/blob/master/packages/core/src/render3/styling/bindings.ts#L620](https://github.com/angular/angular/blob/master/packages/core/src/render3/styling/bindings.ts/issues/L620)
|
* **ivy:** handle SafeStyles in [style.prop] correctly ([#34286](https://github.com/angular/angular/issues/34286)) ([b0d5784](https://github.com/angular/angular/commit/b0d5784)), closes [/github.com/angular/angular/blob/master/packages/core/src/render3/styling/bindings.ts#L620](https://github.com//github.com/angular/angular/blob/master/packages/core/src/render3/styling/bindings.ts/issues/L620)
|
||||||
* **ivy:** inherit static coercion members from base classes ([#34296](https://github.com/angular/angular/issues/34296)) ([edfaab6](https://github.com/angular/angular/commit/edfaab6)), closes [#33830](https://github.com/angular/angular/issues/33830)
|
* **ivy:** inherit static coercion members from base classes ([#34296](https://github.com/angular/angular/issues/34296)) ([edfaab6](https://github.com/angular/angular/commit/edfaab6)), closes [#33830](https://github.com/angular/angular/issues/33830)
|
||||||
* **ivy:** properly parenthesize ternary expressions when emitted ([#34221](https://github.com/angular/angular/issues/34221)) ([af36bc6](https://github.com/angular/angular/commit/af36bc6)), closes [#34087](https://github.com/angular/angular/issues/34087)
|
* **ivy:** properly parenthesize ternary expressions when emitted ([#34221](https://github.com/angular/angular/issues/34221)) ([af36bc6](https://github.com/angular/angular/commit/af36bc6)), closes [#34087](https://github.com/angular/angular/issues/34087)
|
||||||
* **ivy:** throw a better error when DI can't inject a ctor param ([#33739](https://github.com/angular/angular/issues/33739)) ([#34340](https://github.com/angular/angular/issues/34340)) ([676aca1](https://github.com/angular/angular/commit/676aca1)), closes [#33637](https://github.com/angular/angular/issues/33637)
|
* **ivy:** throw a better error when DI can't inject a ctor param ([#33739](https://github.com/angular/angular/issues/33739)) ([#34340](https://github.com/angular/angular/issues/34340)) ([676aca1](https://github.com/angular/angular/commit/676aca1)), closes [#33637](https://github.com/angular/angular/issues/33637)
|
||||||
@ -1029,7 +551,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ngcc:** log Angular error codes correctly ([#34014](https://github.com/angular/angular/issues/34014)) ([3cd43c1](https://github.com/angular/angular/commit/3cd43c1))
|
* **ngcc:** log Angular error codes correctly ([#34014](https://github.com/angular/angular/issues/34014)) ([3cd43c1](https://github.com/angular/angular/commit/3cd43c1))
|
||||||
* **ngcc:** report diagnostics from migrations ([#34014](https://github.com/angular/angular/issues/34014)) ([599dcd0](https://github.com/angular/angular/commit/599dcd0))
|
* **ngcc:** report diagnostics from migrations ([#34014](https://github.com/angular/angular/issues/34014)) ([599dcd0](https://github.com/angular/angular/commit/599dcd0))
|
||||||
* **bazel:** don't rely on [@angular](https://github.com/angular)/core being as a depedency to install [@angular](https://github.com/angular)/bazel ([#34181](https://github.com/angular/angular/issues/34181)) ([716fc84](https://github.com/angular/angular/commit/716fc84)), closes [#34164](https://github.com/angular/angular/issues/34164)
|
* **bazel:** don't rely on [@angular](https://github.com/angular)/core being as a depedency to install [@angular](https://github.com/angular)/bazel ([#34181](https://github.com/angular/angular/issues/34181)) ([716fc84](https://github.com/angular/angular/commit/716fc84)), closes [#34164](https://github.com/angular/angular/issues/34164)
|
||||||
* **bazel:** reenable template type checking in ng_module ([#34144](https://github.com/angular/angular/issues/34144)) ([572e731](https://github.com/angular/angular/commit/572e731)), closes [/github.com/angular/angular/blob/168abc6d6f52713383411b14980e104c99bfeef5/packages/compiler-cli/src/ngtsc/program.ts#L430-L434](https://github.com/angular/angular/blob/168abc6d6f52713383411b14980e104c99bfeef5/packages/compiler-cli/src/ngtsc/program.ts/issues/L430-L434)
|
* **bazel:** reenable template type checking in ng_module ([#34144](https://github.com/angular/angular/issues/34144)) ([572e731](https://github.com/angular/angular/commit/572e731)), closes [/github.com/angular/angular/blob/168abc6d6f52713383411b14980e104c99bfeef5/packages/compiler-cli/src/ngtsc/program.ts#L430-L434](https://github.com//github.com/angular/angular/blob/168abc6d6f52713383411b14980e104c99bfeef5/packages/compiler-cli/src/ngtsc/program.ts/issues/L430-L434)
|
||||||
* **common:** reflect input type in NgForOf context ([#33997](https://github.com/angular/angular/issues/33997)) ([b640d38](https://github.com/angular/angular/commit/b640d38))
|
* **common:** reflect input type in NgForOf context ([#33997](https://github.com/angular/angular/issues/33997)) ([b640d38](https://github.com/angular/angular/commit/b640d38))
|
||||||
* **common:** reflect input type in NgIf context ([#33997](https://github.com/angular/angular/issues/33997)) ([7504543](https://github.com/angular/angular/commit/7504543)), closes [#31556](https://github.com/angular/angular/issues/31556)
|
* **common:** reflect input type in NgIf context ([#33997](https://github.com/angular/angular/issues/33997)) ([7504543](https://github.com/angular/angular/commit/7504543)), closes [#31556](https://github.com/angular/angular/issues/31556)
|
||||||
* **compiler:** i18n - trim whitespace from i18n custom ids ([#34154](https://github.com/angular/angular/issues/34154)) ([64317c6](https://github.com/angular/angular/commit/64317c6)), closes [#34147](https://github.com/angular/angular/issues/34147)
|
* **compiler:** i18n - trim whitespace from i18n custom ids ([#34154](https://github.com/angular/angular/issues/34154)) ([64317c6](https://github.com/angular/angular/commit/64317c6)), closes [#34147](https://github.com/angular/angular/issues/34147)
|
||||||
@ -1065,7 +587,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** support inserting a `viewRef` that is already present ([#34052](https://github.com/angular/angular/issues/34052)) ([978b500](https://github.com/angular/angular/commit/978b500)), closes [#33924](https://github.com/angular/angular/issues/33924)
|
* **ivy:** support inserting a `viewRef` that is already present ([#34052](https://github.com/angular/angular/issues/34052)) ([978b500](https://github.com/angular/angular/commit/978b500)), closes [#33924](https://github.com/angular/angular/issues/33924)
|
||||||
* **ivy:** take styles extracted from template into account in JIT mode ([#34017](https://github.com/angular/angular/issues/34017)) ([b659aa3](https://github.com/angular/angular/commit/b659aa3))
|
* **ivy:** take styles extracted from template into account in JIT mode ([#34017](https://github.com/angular/angular/issues/34017)) ([b659aa3](https://github.com/angular/angular/commit/b659aa3))
|
||||||
* **ivy:** track changes across failed builds ([#33971](https://github.com/angular/angular/issues/33971)) ([1ffbde1](https://github.com/angular/angular/commit/1ffbde1)), closes [#32214](https://github.com/angular/angular/issues/32214)
|
* **ivy:** track changes across failed builds ([#33971](https://github.com/angular/angular/issues/33971)) ([1ffbde1](https://github.com/angular/angular/commit/1ffbde1)), closes [#32214](https://github.com/angular/angular/issues/32214)
|
||||||
* **ivy:** wrap functions from "providers" in parentheses in Closure mode ([#33609](https://github.com/angular/angular/issues/33609)) ([fc6ad19](https://github.com/angular/angular/commit/fc6ad19)), closes [/github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts#L1021](https://github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts/issues/L1021)
|
* **ivy:** wrap functions from "providers" in parentheses in Closure mode ([#33609](https://github.com/angular/angular/issues/33609)) ([fc6ad19](https://github.com/angular/angular/commit/fc6ad19)), closes [/github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts#L1021](https://github.com//github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts/issues/L1021)
|
||||||
* **language-service:** determine index types accessed using dot notation ([#33884](https://github.com/angular/angular/issues/33884)) ([e8ec296](https://github.com/angular/angular/commit/e8ec296)), closes [#29811](https://github.com/angular/angular/issues/29811) [#29811](https://github.com/angular/angular/issues/29811)
|
* **language-service:** determine index types accessed using dot notation ([#33884](https://github.com/angular/angular/issues/33884)) ([e8ec296](https://github.com/angular/angular/commit/e8ec296)), closes [#29811](https://github.com/angular/angular/issues/29811) [#29811](https://github.com/angular/angular/issues/29811)
|
||||||
* **language-service:** fix error of array-index out of bounds exception ([#33928](https://github.com/angular/angular/issues/33928)) ([b05ce85](https://github.com/angular/angular/commit/b05ce85))
|
* **language-service:** fix error of array-index out of bounds exception ([#33928](https://github.com/angular/angular/issues/33928)) ([b05ce85](https://github.com/angular/angular/commit/b05ce85))
|
||||||
* **language-service:** function.bind() should not be an error ([#34041](https://github.com/angular/angular/issues/34041)) ([#34046](https://github.com/angular/angular/issues/34046)) ([d22f3d6](https://github.com/angular/angular/commit/d22f3d6))
|
* **language-service:** function.bind() should not be an error ([#34041](https://github.com/angular/angular/issues/34041)) ([#34046](https://github.com/angular/angular/issues/34046)) ([d22f3d6](https://github.com/angular/angular/commit/d22f3d6))
|
||||||
@ -1073,7 +595,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ngcc:** do not output duplicate ɵprov properties ([#34085](https://github.com/angular/angular/issues/34085)) ([5a8d25d](https://github.com/angular/angular/commit/5a8d25d))
|
* **ngcc:** do not output duplicate ɵprov properties ([#34085](https://github.com/angular/angular/issues/34085)) ([5a8d25d](https://github.com/angular/angular/commit/5a8d25d))
|
||||||
* **ngcc:** render localized strings when in ES5 format ([#33857](https://github.com/angular/angular/issues/33857)) ([c6695fa](https://github.com/angular/angular/commit/c6695fa))
|
* **ngcc:** render localized strings when in ES5 format ([#33857](https://github.com/angular/angular/issues/33857)) ([c6695fa](https://github.com/angular/angular/commit/c6695fa))
|
||||||
* **ngcc:** render UMD global imports correctly ([#34012](https://github.com/angular/angular/issues/34012)) ([83989b8](https://github.com/angular/angular/commit/83989b8))
|
* **ngcc:** render UMD global imports correctly ([#34012](https://github.com/angular/angular/issues/34012)) ([83989b8](https://github.com/angular/angular/commit/83989b8))
|
||||||
* **ngcc:** report errors from `analyze` and `resolve` processing ([#33964](https://github.com/angular/angular/issues/33964)) ([ca5d772](https://github.com/angular/angular/commit/ca5d772)), closes [33685#issuecomment-557091719](https://github.com/angular/angular/issues/33685#issuecomment-557091719)
|
* **ngcc:** report errors from `analyze` and `resolve` processing ([#33964](https://github.com/angular/angular/issues/33964)) ([ca5d772](https://github.com/angular/angular/commit/ca5d772)), closes [/github.com/angular/angular/issues/33685#issuecomment-557091719](https://github.com//github.com/angular/angular/issues/33685/issues/issuecomment-557091719)
|
||||||
* **router:** make routerLinkActive work with query params which contain arrays ([#22666](https://github.com/angular/angular/issues/22666)) ([f1bf5b2](https://github.com/angular/angular/commit/f1bf5b2)), closes [#22223](https://github.com/angular/angular/issues/22223)
|
* **router:** make routerLinkActive work with query params which contain arrays ([#22666](https://github.com/angular/angular/issues/22666)) ([f1bf5b2](https://github.com/angular/angular/commit/f1bf5b2)), closes [#22223](https://github.com/angular/angular/issues/22223)
|
||||||
* **service-worker:** allow creating post api requests after cache failure ([#33930](https://github.com/angular/angular/issues/33930)) ([63c9123](https://github.com/angular/angular/commit/63c9123)), closes [#33793](https://github.com/angular/angular/issues/33793)
|
* **service-worker:** allow creating post api requests after cache failure ([#33930](https://github.com/angular/angular/issues/33930)) ([63c9123](https://github.com/angular/angular/commit/63c9123)), closes [#33793](https://github.com/angular/angular/issues/33793)
|
||||||
* **service-worker:** throw when using the unsupported `versionedFiles` option in config ([#33903](https://github.com/angular/angular/issues/33903)) ([250e6fd](https://github.com/angular/angular/commit/250e6fd))
|
* **service-worker:** throw when using the unsupported `versionedFiles` option in config ([#33903](https://github.com/angular/angular/issues/33903)) ([250e6fd](https://github.com/angular/angular/commit/250e6fd))
|
||||||
@ -1152,7 +674,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **language-service:** Should not crash if expr ends unexpectedly ([#33524](https://github.com/angular/angular/issues/33524)) ([9ebac71](https://github.com/angular/angular/commit/9ebac71))
|
* **language-service:** Should not crash if expr ends unexpectedly ([#33524](https://github.com/angular/angular/issues/33524)) ([9ebac71](https://github.com/angular/angular/commit/9ebac71))
|
||||||
* **ngcc:** handle new `__spreadArrays` tslib helper ([#33617](https://github.com/angular/angular/issues/33617)) ([d749dd3](https://github.com/angular/angular/commit/d749dd3)), closes [#33614](https://github.com/angular/angular/issues/33614)
|
* **ngcc:** handle new `__spreadArrays` tslib helper ([#33617](https://github.com/angular/angular/issues/33617)) ([d749dd3](https://github.com/angular/angular/commit/d749dd3)), closes [#33614](https://github.com/angular/angular/issues/33614)
|
||||||
* **ngcc:** override `getInternalNameOfClass()` and `getAdjacentNameOfClass()` for ES5 ([#33533](https://github.com/angular/angular/issues/33533)) ([93a23b9](https://github.com/angular/angular/commit/93a23b9))
|
* **ngcc:** override `getInternalNameOfClass()` and `getAdjacentNameOfClass()` for ES5 ([#33533](https://github.com/angular/angular/issues/33533)) ([93a23b9](https://github.com/angular/angular/commit/93a23b9))
|
||||||
* **ngcc:** render adjacent statements after static properties ([#33630](https://github.com/angular/angular/issues/33630)) ([fe12d0d](https://github.com/angular/angular/commit/fe12d0d)), closes [/github.com/angular/angular/pull/33337#issuecomment-545487737](https://github.com/angular/angular/pull/33337#issuecomment-545487737)
|
* **ngcc:** render adjacent statements after static properties ([#33630](https://github.com/angular/angular/issues/33630)) ([fe12d0d](https://github.com/angular/angular/commit/fe12d0d)), closes [/github.com/angular/angular/pull/33337#issuecomment-545487737](https://github.com//github.com/angular/angular/pull/33337/issues/issuecomment-545487737)
|
||||||
* **ngcc:** render new definitions using the inner name of the class ([#33533](https://github.com/angular/angular/issues/33533)) ([85298e3](https://github.com/angular/angular/commit/85298e3))
|
* **ngcc:** render new definitions using the inner name of the class ([#33533](https://github.com/angular/angular/issues/33533)) ([85298e3](https://github.com/angular/angular/commit/85298e3))
|
||||||
* **service-worker:** ensure initialization before handling messages ([#32525](https://github.com/angular/angular/issues/32525)) ([72eba77](https://github.com/angular/angular/commit/72eba77)), closes [#25611](https://github.com/angular/angular/issues/25611)
|
* **service-worker:** ensure initialization before handling messages ([#32525](https://github.com/angular/angular/issues/32525)) ([72eba77](https://github.com/angular/angular/commit/72eba77)), closes [#25611](https://github.com/angular/angular/issues/25611)
|
||||||
* **compiler:** i18n - ignore `alt-trans` tags in XLIFF 1.2 ([#33450](https://github.com/angular/angular/issues/33450)) ([936700a](https://github.com/angular/angular/commit/936700a)), closes [#33161](https://github.com/angular/angular/issues/33161)
|
* **compiler:** i18n - ignore `alt-trans` tags in XLIFF 1.2 ([#33450](https://github.com/angular/angular/issues/33450)) ([936700a](https://github.com/angular/angular/commit/936700a)), closes [#33161](https://github.com/angular/angular/issues/33161)
|
||||||
@ -1182,7 +704,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **language-service:** Add directive selectors & banana-in-a-box to completions ([#33311](https://github.com/angular/angular/issues/33311)) ([49eec5d](https://github.com/angular/angular/commit/49eec5d))
|
* **language-service:** Add directive selectors & banana-in-a-box to completions ([#33311](https://github.com/angular/angular/issues/33311)) ([49eec5d](https://github.com/angular/angular/commit/49eec5d))
|
||||||
* **language-service:** Add global symbol for $any() ([#33245](https://github.com/angular/angular/issues/33245)) ([3f257e9](https://github.com/angular/angular/commit/3f257e9))
|
* **language-service:** Add global symbol for $any() ([#33245](https://github.com/angular/angular/issues/33245)) ([3f257e9](https://github.com/angular/angular/commit/3f257e9))
|
||||||
* **language-service:** Preserve CRLF in templates for language-service ([#33241](https://github.com/angular/angular/issues/33241)) ([65a0d2b](https://github.com/angular/angular/commit/65a0d2b))
|
* **language-service:** Preserve CRLF in templates for language-service ([#33241](https://github.com/angular/angular/issues/33241)) ([65a0d2b](https://github.com/angular/angular/commit/65a0d2b))
|
||||||
* **ngcc:** do not fail when multiple workers try to create the same directory ([#33237](https://github.com/angular/angular/issues/33237)) ([8017229](https://github.com/angular/angular/commit/8017229)), closes [/github.com/angular/angular/pull/33049#issuecomment-540485703](https://github.com/angular/angular/pull/33049#issuecomment-540485703)
|
* **ngcc:** do not fail when multiple workers try to create the same directory ([#33237](https://github.com/angular/angular/issues/33237)) ([8017229](https://github.com/angular/angular/commit/8017229)), closes [/github.com/angular/angular/pull/33049#issuecomment-540485703](https://github.com//github.com/angular/angular/pull/33049/issues/issuecomment-540485703)
|
||||||
* **bazel:** Remove angular devkit and restore ngc postinstall ([#32946](https://github.com/angular/angular/issues/32946)) ([f036684](https://github.com/angular/angular/commit/f036684))
|
* **bazel:** Remove angular devkit and restore ngc postinstall ([#32946](https://github.com/angular/angular/issues/32946)) ([f036684](https://github.com/angular/angular/commit/f036684))
|
||||||
* **common:** remove deprecated support for intl API ([#29250](https://github.com/angular/angular/issues/29250)) ([9e7668f](https://github.com/angular/angular/commit/9e7668f)), closes [#18284](https://github.com/angular/angular/issues/18284)
|
* **common:** remove deprecated support for intl API ([#29250](https://github.com/angular/angular/issues/29250)) ([9e7668f](https://github.com/angular/angular/commit/9e7668f)), closes [#18284](https://github.com/angular/angular/issues/18284)
|
||||||
* **compiler:** absolute source span for template attribute expressions ([#33189](https://github.com/angular/angular/issues/33189)) ([fd4fed1](https://github.com/angular/angular/commit/fd4fed1))
|
* **compiler:** absolute source span for template attribute expressions ([#33189](https://github.com/angular/angular/issues/33189)) ([fd4fed1](https://github.com/angular/angular/commit/fd4fed1))
|
||||||
@ -1196,7 +718,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** use ReflectionHost to check exports when writing an import ([#33192](https://github.com/angular/angular/issues/33192)) ([de44570](https://github.com/angular/angular/commit/de44570))
|
* **ivy:** use ReflectionHost to check exports when writing an import ([#33192](https://github.com/angular/angular/issues/33192)) ([de44570](https://github.com/angular/angular/commit/de44570))
|
||||||
* **language-service:** Increase project/script version in MockHost.reset() ([#33200](https://github.com/angular/angular/issues/33200)) ([43241a5](https://github.com/angular/angular/commit/43241a5))
|
* **language-service:** Increase project/script version in MockHost.reset() ([#33200](https://github.com/angular/angular/issues/33200)) ([43241a5](https://github.com/angular/angular/commit/43241a5))
|
||||||
* **language-service:** reset MockHost after every spec instead of creating new LS ([#33200](https://github.com/angular/angular/issues/33200)) ([11bf767](https://github.com/angular/angular/commit/11bf767))
|
* **language-service:** reset MockHost after every spec instead of creating new LS ([#33200](https://github.com/angular/angular/issues/33200)) ([11bf767](https://github.com/angular/angular/commit/11bf767))
|
||||||
* **ngcc:** avoid warning when reflecting on index signature member ([#33198](https://github.com/angular/angular/issues/33198)) ([78214e7](https://github.com/angular/angular/commit/78214e7)), closes [/github.com/angular/angular/blob/4659cc26e/packages/common/http/src/jsonp.ts#L39](https://github.com/angular/angular/blob/4659cc26e/packages/common/http/src/jsonp.ts/issues/L39)
|
* **ngcc:** avoid warning when reflecting on index signature member ([#33198](https://github.com/angular/angular/issues/33198)) ([78214e7](https://github.com/angular/angular/commit/78214e7)), closes [/github.com/angular/angular/blob/4659cc26e/packages/common/http/src/jsonp.ts#L39](https://github.com//github.com/angular/angular/blob/4659cc26e/packages/common/http/src/jsonp.ts/issues/L39)
|
||||||
* **ngcc:** better detection of end of decorator expression ([#33192](https://github.com/angular/angular/issues/33192)) ([5071083](https://github.com/angular/angular/commit/5071083))
|
* **ngcc:** better detection of end of decorator expression ([#33192](https://github.com/angular/angular/issues/33192)) ([5071083](https://github.com/angular/angular/commit/5071083))
|
||||||
* **ngcc:** Esm5ReflectionHost.getDeclarationOfIdentifier should handle aliased inner declarations ([#33252](https://github.com/angular/angular/issues/33252)) ([bfd07b3](https://github.com/angular/angular/commit/bfd07b3))
|
* **ngcc:** Esm5ReflectionHost.getDeclarationOfIdentifier should handle aliased inner declarations ([#33252](https://github.com/angular/angular/issues/33252)) ([bfd07b3](https://github.com/angular/angular/commit/bfd07b3))
|
||||||
* **ngcc:** report the correct viaModule when reflecting over commonjs ([#33192](https://github.com/angular/angular/issues/33192)) ([afcff73](https://github.com/angular/angular/commit/afcff73))
|
* **ngcc:** report the correct viaModule when reflecting over commonjs ([#33192](https://github.com/angular/angular/issues/33192)) ([afcff73](https://github.com/angular/angular/commit/afcff73))
|
||||||
@ -1215,7 +737,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* google3 sync which requires type hints ([#33108](https://github.com/angular/angular/issues/33108)) ([0c69ec2](https://github.com/angular/angular/commit/0c69ec2))
|
* google3 sync which requires type hints ([#33108](https://github.com/angular/angular/issues/33108)) ([0c69ec2](https://github.com/angular/angular/commit/0c69ec2))
|
||||||
* **ngcc:** rename the executable from `ivy-ngcc` to `ngcc` ([#33140](https://github.com/angular/angular/issues/33140)) ([1a34fbc](https://github.com/angular/angular/commit/1a34fbc))
|
* **ngcc:** rename the executable from `ivy-ngcc` to `ngcc` ([#33140](https://github.com/angular/angular/issues/33140)) ([1a34fbc](https://github.com/angular/angular/commit/1a34fbc))
|
||||||
* **service-worker:** continue serving api requests on cache failure ([#32996](https://github.com/angular/angular/issues/32996)) ([52483bf](https://github.com/angular/angular/commit/52483bf)), closes [#21412](https://github.com/angular/angular/issues/21412)
|
* **service-worker:** continue serving api requests on cache failure ([#32996](https://github.com/angular/angular/issues/32996)) ([52483bf](https://github.com/angular/angular/commit/52483bf)), closes [#21412](https://github.com/angular/angular/issues/21412)
|
||||||
* **common:** expand type for "ngForOf" input to work with strict null checks ([#31371](https://github.com/angular/angular/issues/31371)) ([c1bb886](https://github.com/angular/angular/commit/c1bb886)), closes [#16373](https://github.com/angular/components/pull/16373)
|
* **common:** expand type for "ngForOf" input to work with strict null checks ([#31371](https://github.com/angular/angular/issues/31371)) ([c1bb886](https://github.com/angular/angular/commit/c1bb886)), closes [#16373](https://github.com//github.com/angular/components/pull/16373)
|
||||||
* **core:** ngNoopZone should have the same signature with ngZone ([#32068](https://github.com/angular/angular/issues/32068)) ([3a53e2c](https://github.com/angular/angular/commit/3a53e2c)), closes [#32063](https://github.com/angular/angular/issues/32063)
|
* **core:** ngNoopZone should have the same signature with ngZone ([#32068](https://github.com/angular/angular/issues/32068)) ([3a53e2c](https://github.com/angular/angular/commit/3a53e2c)), closes [#32063](https://github.com/angular/angular/issues/32063)
|
||||||
* **core:** set migration schematic versions to valid semver versions ([#32991](https://github.com/angular/angular/issues/32991)) ([0119f46](https://github.com/angular/angular/commit/0119f46))
|
* **core:** set migration schematic versions to valid semver versions ([#32991](https://github.com/angular/angular/issues/32991)) ([0119f46](https://github.com/angular/angular/commit/0119f46))
|
||||||
* **core:** update migration descriptions with links to AIO documentation ([#32991](https://github.com/angular/angular/issues/32991)) ([f8eca84](https://github.com/angular/angular/commit/f8eca84))
|
* **core:** update migration descriptions with links to AIO documentation ([#32991](https://github.com/angular/angular/issues/32991)) ([f8eca84](https://github.com/angular/angular/commit/f8eca84))
|
||||||
@ -1239,7 +761,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **language-service:** Turn on strict mode for test project ([#32783](https://github.com/angular/angular/issues/32783)) ([28358b6](https://github.com/angular/angular/commit/28358b6))
|
* **language-service:** Turn on strict mode for test project ([#32783](https://github.com/angular/angular/issues/32783)) ([28358b6](https://github.com/angular/angular/commit/28358b6))
|
||||||
* **ngcc:** ensure private exports are added for `ModuleWithProviders` ([#32902](https://github.com/angular/angular/issues/32902)) ([002a97d](https://github.com/angular/angular/commit/002a97d))
|
* **ngcc:** ensure private exports are added for `ModuleWithProviders` ([#32902](https://github.com/angular/angular/issues/32902)) ([002a97d](https://github.com/angular/angular/commit/002a97d))
|
||||||
* **ngcc:** handle presence of both `ctorParameters` and `__decorate` ([#32901](https://github.com/angular/angular/issues/32901)) ([747f0cf](https://github.com/angular/angular/commit/747f0cf))
|
* **ngcc:** handle presence of both `ctorParameters` and `__decorate` ([#32901](https://github.com/angular/angular/issues/32901)) ([747f0cf](https://github.com/angular/angular/commit/747f0cf))
|
||||||
* **ngcc:** make the build-marker error more clear ([#32712](https://github.com/angular/angular/issues/32712)) ([0ea4875](https://github.com/angular/angular/commit/0ea4875)), closes [31354#issuecomment-532080537](https://github.com/angular/angular/issues/31354#issuecomment-532080537)
|
* **ngcc:** make the build-marker error more clear ([#32712](https://github.com/angular/angular/issues/32712)) ([0ea4875](https://github.com/angular/angular/commit/0ea4875)), closes [/github.com/angular/angular/issues/31354#issuecomment-532080537](https://github.com//github.com/angular/angular/issues/31354/issues/issuecomment-532080537)
|
||||||
* **upgrade:** fix AngularJsUrlCodec to support Safari ([#32959](https://github.com/angular/angular/issues/32959)) ([39e8ceb](https://github.com/angular/angular/commit/39e8ceb))
|
* **upgrade:** fix AngularJsUrlCodec to support Safari ([#32959](https://github.com/angular/angular/issues/32959)) ([39e8ceb](https://github.com/angular/angular/commit/39e8ceb))
|
||||||
* **ivy:** ensure `window.ng.getDebugNode` returns debug info for component elements ([#32780](https://github.com/angular/angular/issues/32780)) ([5651fa3](https://github.com/angular/angular/commit/5651fa3))
|
* **ivy:** ensure `window.ng.getDebugNode` returns debug info for component elements ([#32780](https://github.com/angular/angular/issues/32780)) ([5651fa3](https://github.com/angular/angular/commit/5651fa3))
|
||||||
* **ivy:** ensure multiple map-based bindings do not skip intermediate values ([#32774](https://github.com/angular/angular/issues/32774)) ([86fd571](https://github.com/angular/angular/commit/86fd571))
|
* **ivy:** ensure multiple map-based bindings do not skip intermediate values ([#32774](https://github.com/angular/angular/issues/32774)) ([86fd571](https://github.com/angular/angular/commit/86fd571))
|
||||||
@ -1348,7 +870,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** graceful evaluation of unknown or invalid expressions ([#33453](https://github.com/angular/angular/issues/33453)) ([ce30888](https://github.com/angular/angular/commit/ce30888))
|
* **ivy:** graceful evaluation of unknown or invalid expressions ([#33453](https://github.com/angular/angular/issues/33453)) ([ce30888](https://github.com/angular/angular/commit/ce30888))
|
||||||
* **ivy:** implement unknown element detection in jit mode ([#33419](https://github.com/angular/angular/issues/33419)) ([c83f501](https://github.com/angular/angular/commit/c83f501))
|
* **ivy:** implement unknown element detection in jit mode ([#33419](https://github.com/angular/angular/issues/33419)) ([c83f501](https://github.com/angular/angular/commit/c83f501))
|
||||||
* add a flag in bootstrap to enable coalesce event change detection to improve performance ([#30533](https://github.com/angular/angular/issues/30533)) ([44623a1](https://github.com/angular/angular/commit/44623a1))
|
* add a flag in bootstrap to enable coalesce event change detection to improve performance ([#30533](https://github.com/angular/angular/issues/30533)) ([44623a1](https://github.com/angular/angular/commit/44623a1))
|
||||||
* **bazel:** update [@bazel](https://github.com/bazel)/schematics to Bazel 1.0.0 ([#33476](https://github.com/angular/angular/issues/33476)) ([540d104](https://github.com/angular/angular/commit/540d104)), closes [/github.com/angular/angular/pull/33367#issuecomment-547643246](https://github.com/angular/angular/pull/33367#issuecomment-547643246)
|
* **bazel:** update [@bazel](https://github.com/bazel)/schematics to Bazel 1.0.0 ([#33476](https://github.com/angular/angular/issues/33476)) ([540d104](https://github.com/angular/angular/commit/540d104)), closes [/github.com/angular/angular/pull/33367#issuecomment-547643246](https://github.com//github.com/angular/angular/pull/33367/issues/issuecomment-547643246)
|
||||||
* **bazel:** update bazel-schematics to use Ivy and new rollup_bundle ([#33435](https://github.com/angular/angular/issues/33435)) ([bf913cc](https://github.com/angular/angular/commit/bf913cc))
|
* **bazel:** update bazel-schematics to use Ivy and new rollup_bundle ([#33435](https://github.com/angular/angular/issues/33435)) ([bf913cc](https://github.com/angular/angular/commit/bf913cc))
|
||||||
* **ivy:** i18n - support inlining of XTB formatted translation files ([#33444](https://github.com/angular/angular/issues/33444)) ([2c623fd](https://github.com/angular/angular/commit/2c623fd))
|
* **ivy:** i18n - support inlining of XTB formatted translation files ([#33444](https://github.com/angular/angular/issues/33444)) ([2c623fd](https://github.com/angular/angular/commit/2c623fd))
|
||||||
* **language-service:** add support for text replacement ([#33091](https://github.com/angular/angular/issues/33091)) ([da4eb91](https://github.com/angular/angular/commit/da4eb91))
|
* **language-service:** add support for text replacement ([#33091](https://github.com/angular/angular/issues/33091)) ([da4eb91](https://github.com/angular/angular/commit/da4eb91))
|
||||||
@ -1390,7 +912,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **ivy:** i18n - implement compile-time inlining ([#32881](https://github.com/angular/angular/issues/32881)) ([2cdb3a0](https://github.com/angular/angular/commit/2cdb3a0))
|
* **ivy:** i18n - implement compile-time inlining ([#32881](https://github.com/angular/angular/issues/32881)) ([2cdb3a0](https://github.com/angular/angular/commit/2cdb3a0))
|
||||||
* **ivy:** i18n - render legacy message ids in `$localize` if requested ([#32937](https://github.com/angular/angular/issues/32937)) ([bcbf3e4](https://github.com/angular/angular/commit/bcbf3e4))
|
* **ivy:** i18n - render legacy message ids in `$localize` if requested ([#32937](https://github.com/angular/angular/issues/32937)) ([bcbf3e4](https://github.com/angular/angular/commit/bcbf3e4))
|
||||||
* **language-service:** module definitions on directive hover ([#32763](https://github.com/angular/angular/issues/32763)) ([0d186dd](https://github.com/angular/angular/commit/0d186dd)), closes [#32565](https://github.com/angular/angular/issues/32565)
|
* **language-service:** module definitions on directive hover ([#32763](https://github.com/angular/angular/issues/32763)) ([0d186dd](https://github.com/angular/angular/commit/0d186dd)), closes [#32565](https://github.com/angular/angular/issues/32565)
|
||||||
* **ngcc:** expose `--create-ivy-entry-points` option on ivy-ngcc ([#33049](https://github.com/angular/angular/issues/33049)) ([b2b917d](https://github.com/angular/angular/commit/b2b917d)), closes [/github.com/angular/angular/pull/32999#issuecomment-539937368](https://github.com/angular/angular/pull/32999#issuecomment-539937368)
|
* **ngcc:** expose `--create-ivy-entry-points` option on ivy-ngcc ([#33049](https://github.com/angular/angular/issues/33049)) ([b2b917d](https://github.com/angular/angular/commit/b2b917d)), closes [/github.com/angular/angular/pull/32999#issuecomment-539937368](https://github.com//github.com/angular/angular/pull/32999/issues/issuecomment-539937368)
|
||||||
* update rxjs peerDependencies minimum requirment to 6.5.3 ([#32812](https://github.com/angular/angular/issues/32812)) ([66658c4](https://github.com/angular/angular/commit/66658c4))
|
* update rxjs peerDependencies minimum requirment to 6.5.3 ([#32812](https://github.com/angular/angular/issues/32812)) ([66658c4](https://github.com/angular/angular/commit/66658c4))
|
||||||
* **ivy:** support ng-add in localize package ([#32791](https://github.com/angular/angular/issues/32791)) ([e41cbfb](https://github.com/angular/angular/commit/e41cbfb))
|
* **ivy:** support ng-add in localize package ([#32791](https://github.com/angular/angular/issues/32791)) ([e41cbfb](https://github.com/angular/angular/commit/e41cbfb))
|
||||||
* **language-service:** allow retreiving synchronized analyzed NgModules ([#32779](https://github.com/angular/angular/issues/32779)) ([98feee7](https://github.com/angular/angular/commit/98feee7))
|
* **language-service:** allow retreiving synchronized analyzed NgModules ([#32779](https://github.com/angular/angular/issues/32779)) ([98feee7](https://github.com/angular/angular/commit/98feee7))
|
||||||
@ -1799,7 +1321,7 @@ This release contains various API docs improvements.
|
|||||||
|
|
||||||
* use the correct WTF array to iterate over ([#31208](https://github.com/angular/angular/issues/31208)) ([4aed480](https://github.com/angular/angular/commit/4aed480))
|
* use the correct WTF array to iterate over ([#31208](https://github.com/angular/angular/issues/31208)) ([4aed480](https://github.com/angular/angular/commit/4aed480))
|
||||||
* **compiler-cli:** Return original sourceFile instead of redirected sourceFile from getSourceFile ([#26036](https://github.com/angular/angular/issues/26036)) ([13dbb98](https://github.com/angular/angular/commit/13dbb98)), closes [#22524](https://github.com/angular/angular/issues/22524)
|
* **compiler-cli:** Return original sourceFile instead of redirected sourceFile from getSourceFile ([#26036](https://github.com/angular/angular/issues/26036)) ([13dbb98](https://github.com/angular/angular/commit/13dbb98)), closes [#22524](https://github.com/angular/angular/issues/22524)
|
||||||
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
|
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com//github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
|
||||||
|
|
||||||
|
|
||||||
<a name="8.1.1"></a>
|
<a name="8.1.1"></a>
|
||||||
@ -1808,7 +1330,7 @@ This release contains various API docs improvements.
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
|
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com//github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1904,7 +1426,7 @@ This release contains various API docs improvements.
|
|||||||
* **bazel:** do not modify tsconfig.json ([#30984](https://github.com/angular/angular/issues/30984)) ([49307f0](https://github.com/angular/angular/commit/49307f0))
|
* **bazel:** do not modify tsconfig.json ([#30984](https://github.com/angular/angular/issues/30984)) ([49307f0](https://github.com/angular/angular/commit/49307f0))
|
||||||
* **bazel:** Load global stylesheet in dev and prod ([#30879](https://github.com/angular/angular/issues/30879)) ([5a7bcd1](https://github.com/angular/angular/commit/5a7bcd1))
|
* **bazel:** Load global stylesheet in dev and prod ([#30879](https://github.com/angular/angular/issues/30879)) ([5a7bcd1](https://github.com/angular/angular/commit/5a7bcd1))
|
||||||
* **common:** expose the `HttpUploadProgressEvent` interface as public API ([#30852](https://github.com/angular/angular/issues/30852)) ([4e8614b](https://github.com/angular/angular/commit/4e8614b)), closes [#30814](https://github.com/angular/angular/issues/30814)
|
* **common:** expose the `HttpUploadProgressEvent` interface as public API ([#30852](https://github.com/angular/angular/issues/30852)) ([4e8614b](https://github.com/angular/angular/commit/4e8614b)), closes [#30814](https://github.com/angular/angular/issues/30814)
|
||||||
* **core:** TypeScript related migrations should cater for BOM ([#30719](https://github.com/angular/angular/issues/30719)) ([26e3615](https://github.com/angular/angular/commit/26e3615)), closes [/github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts#L72](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts/issues/L72) [#30713](https://github.com/angular/angular/issues/30713)
|
* **core:** TypeScript related migrations should cater for BOM ([#30719](https://github.com/angular/angular/issues/30719)) ([26e3615](https://github.com/angular/angular/commit/26e3615)), closes [/github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts#L72](https://github.com//github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts/issues/L72) [#30713](https://github.com/angular/angular/issues/30713)
|
||||||
* **service-worker:** avoid uncaught rejection warning when registration fails ([#30876](https://github.com/angular/angular/issues/30876)) ([08c38a1](https://github.com/angular/angular/commit/08c38a1))
|
* **service-worker:** avoid uncaught rejection warning when registration fails ([#30876](https://github.com/angular/angular/issues/30876)) ([08c38a1](https://github.com/angular/angular/commit/08c38a1))
|
||||||
|
|
||||||
|
|
||||||
@ -1915,7 +1437,7 @@ This release contains various API docs improvements.
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **core:** TypeScript related migrations should cater for BOM ([#30719](https://github.com/angular/angular/issues/30719)) ([80394ce](https://github.com/angular/angular/commit/80394ce)), closes [/github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts#L72](https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts/issues/L72) [#30713](https://github.com/angular/angular/issues/30713)
|
* **core:** TypeScript related migrations should cater for BOM ([#30719](https://github.com/angular/angular/issues/30719)) ([80394ce](https://github.com/angular/angular/commit/80394ce)), closes [/github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts#L72](https://github.com//github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts/issues/L72) [#30713](https://github.com/angular/angular/issues/30713)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2068,7 +1590,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **common:** add upgrade sub-package to `ng_package` rule for @angular/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
* **common:** add upgrade sub-package to `ng_package` rule for @angular/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||||
* **common:** adjust `MockPlatformLocation` to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
* **common:** adjust `MockPlatformLocation` to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||||
* **compiler:** Fix compiler crash due to isSkipSelf of null ([#30075](https://github.com/angular/angular/issues/30075)) ([28fd5ab](https://github.com/angular/angular/commit/28fd5ab))
|
* **compiler:** Fix compiler crash due to isSkipSelf of null ([#30075](https://github.com/angular/angular/issues/30075)) ([28fd5ab](https://github.com/angular/angular/commit/28fd5ab))
|
||||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([0ddf2e7](https://github.com/angular/angular/commit/0ddf2e7)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([0ddf2e7](https://github.com/angular/angular/commit/0ddf2e7)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||||
* **bazel:** do not typecheck core schematic files ([#29876](https://github.com/angular/angular/issues/29876)) ([2ba799d](https://github.com/angular/angular/commit/2ba799d))
|
* **bazel:** do not typecheck core schematic files ([#29876](https://github.com/angular/angular/issues/29876)) ([2ba799d](https://github.com/angular/angular/commit/2ba799d))
|
||||||
* **bazel:** restore `ng build --prod` ([#30005](https://github.com/angular/angular/issues/30005)) ([96a8289](https://github.com/angular/angular/commit/96a8289))
|
* **bazel:** restore `ng build --prod` ([#30005](https://github.com/angular/angular/issues/30005)) ([96a8289](https://github.com/angular/angular/commit/96a8289))
|
||||||
* **common:** prevent repeated application of `HttpParams` mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([8e8e89a](https://github.com/angular/angular/commit/8e8e89a)), closes [#20430](https://github.com/angular/angular/issues/20430)
|
* **common:** prevent repeated application of `HttpParams` mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([8e8e89a](https://github.com/angular/angular/commit/8e8e89a)), closes [#20430](https://github.com/angular/angular/issues/20430)
|
||||||
@ -2122,7 +1644,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **platform-server:** update minimum domino version to latest released ([#28893](https://github.com/angular/angular/issues/28893)) ([79e2ca0](https://github.com/angular/angular/commit/79e2ca0))
|
* **platform-server:** update minimum domino version to latest released ([#28893](https://github.com/angular/angular/issues/28893)) ([79e2ca0](https://github.com/angular/angular/commit/79e2ca0))
|
||||||
* **router:** removed obsolete TODO comment ([#29085](https://github.com/angular/angular/issues/29085)) ([72ecc45](https://github.com/angular/angular/commit/72ecc45))
|
* **router:** removed obsolete TODO comment ([#29085](https://github.com/angular/angular/issues/29085)) ([72ecc45](https://github.com/angular/angular/commit/72ecc45))
|
||||||
* **service-worker:** detect new version even if files are identical to an old one ([#26006](https://github.com/angular/angular/issues/26006)) ([586234b](https://github.com/angular/angular/commit/586234b)), closes [#24338](https://github.com/angular/angular/issues/24338)
|
* **service-worker:** detect new version even if files are identical to an old one ([#26006](https://github.com/angular/angular/issues/26006)) ([586234b](https://github.com/angular/angular/commit/586234b)), closes [#24338](https://github.com/angular/angular/issues/24338)
|
||||||
* **service-worker:** ignore passive mixed content requests ([#25994](https://github.com/angular/angular/issues/25994)) ([48214e2](https://github.com/angular/angular/commit/48214e2)), closes [23012#issuecomment-376430187](https://github.com/angular/angular/issues/23012#issuecomment-376430187) [#23012](https://github.com/angular/angular/issues/23012)
|
* **service-worker:** ignore passive mixed content requests ([#25994](https://github.com/angular/angular/issues/25994)) ([48214e2](https://github.com/angular/angular/commit/48214e2)), closes [/github.com/angular/angular/issues/23012#issuecomment-376430187](https://github.com//github.com/angular/angular/issues/23012/issues/issuecomment-376430187) [#23012](https://github.com/angular/angular/issues/23012)
|
||||||
* **bazel:** Pin browsers for schematics ([#28913](https://github.com/angular/angular/issues/28913)) ([1145bdb](https://github.com/angular/angular/commit/1145bdb))
|
* **bazel:** Pin browsers for schematics ([#28913](https://github.com/angular/angular/issues/28913)) ([1145bdb](https://github.com/angular/angular/commit/1145bdb))
|
||||||
* **bazel:** rxjs_umd_modules should always be present ([#28881](https://github.com/angular/angular/issues/28881)) ([9ae14db](https://github.com/angular/angular/commit/9ae14db))
|
* **bazel:** rxjs_umd_modules should always be present ([#28881](https://github.com/angular/angular/issues/28881)) ([9ae14db](https://github.com/angular/angular/commit/9ae14db))
|
||||||
* **compiler:** use correct variable in invalid function ([#28656](https://github.com/angular/angular/issues/28656)) ([f75acbd](https://github.com/angular/angular/commit/f75acbd))
|
* **compiler:** use correct variable in invalid function ([#28656](https://github.com/angular/angular/issues/28656)) ([f75acbd](https://github.com/angular/angular/commit/f75acbd))
|
||||||
@ -2130,7 +1652,7 @@ To learn about the release highlights and our CLI-powered automated update workf
|
|||||||
* **language-service:** Fix completions for input/output with alias ([#28904](https://github.com/angular/angular/issues/28904)) ([ad4a9bf](https://github.com/angular/angular/commit/ad4a9bf)), closes [#27959](https://github.com/angular/angular/issues/27959)
|
* **language-service:** Fix completions for input/output with alias ([#28904](https://github.com/angular/angular/issues/28904)) ([ad4a9bf](https://github.com/angular/angular/commit/ad4a9bf)), closes [#27959](https://github.com/angular/angular/issues/27959)
|
||||||
* **bazel:** Add postinstall step to generate summaries ([#28850](https://github.com/angular/angular/issues/28850)) ([96b597c](https://github.com/angular/angular/commit/96b597c))
|
* **bazel:** Add postinstall step to generate summaries ([#28850](https://github.com/angular/angular/issues/28850)) ([96b597c](https://github.com/angular/angular/commit/96b597c))
|
||||||
* **bazel:** Schematics should upgrade rxjs to 6.4.0 ([#28841](https://github.com/angular/angular/issues/28841)) ([2d80419](https://github.com/angular/angular/commit/2d80419))
|
* **bazel:** Schematics should upgrade rxjs to 6.4.0 ([#28841](https://github.com/angular/angular/issues/28841)) ([2d80419](https://github.com/angular/angular/commit/2d80419))
|
||||||
* **compiler-cli:** incorrect bundled metadata for static class member call expressions ([#28762](https://github.com/angular/angular/issues/28762)) ([4131715](https://github.com/angular/angular/commit/4131715)), closes [/github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts#L121](https://github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts/issues/L121) [#28741](https://github.com/angular/angular/issues/28741)
|
* **compiler-cli:** incorrect bundled metadata for static class member call expressions ([#28762](https://github.com/angular/angular/issues/28762)) ([4131715](https://github.com/angular/angular/commit/4131715)), closes [/github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts#L121](https://github.com//github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts/issues/L121) [#28741](https://github.com/angular/angular/issues/28741)
|
||||||
* **bazel:** Install angular repo before yarn_install ([#28670](https://github.com/angular/angular/issues/28670)) ([49fb8c3](https://github.com/angular/angular/commit/49fb8c3))
|
* **bazel:** Install angular repo before yarn_install ([#28670](https://github.com/angular/angular/issues/28670)) ([49fb8c3](https://github.com/angular/angular/commit/49fb8c3))
|
||||||
* **bazel:** Turn on strict action env ([#28675](https://github.com/angular/angular/issues/28675)) ([2ea030c](https://github.com/angular/angular/commit/2ea030c))
|
* **bazel:** Turn on strict action env ([#28675](https://github.com/angular/angular/issues/28675)) ([2ea030c](https://github.com/angular/angular/commit/2ea030c))
|
||||||
* **compiler:** ensure that event handlers have the correct source spans ([#28055](https://github.com/angular/angular/issues/28055)) ([cffd862](https://github.com/angular/angular/commit/cffd862))
|
* **compiler:** ensure that event handlers have the correct source spans ([#28055](https://github.com/angular/angular/issues/28055)) ([cffd862](https://github.com/angular/angular/commit/cffd862))
|
||||||
@ -2283,7 +1805,7 @@ For details on how to migrate, please refer to [the deprecations guide](https://
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([#30107](https://github.com/angular/angular/issues/30107)) ([1084c19](https://github.com/angular/angular/commit/1084c19)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([#30107](https://github.com/angular/angular/issues/30107)) ([1084c19](https://github.com/angular/angular/commit/1084c19)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||||
|
|
||||||
|
|
||||||
<a name="7.2.13"></a>
|
<a name="7.2.13"></a>
|
||||||
@ -2337,7 +1859,7 @@ This release contains various API docs improvements.
|
|||||||
* **animations:** ensure `position` and `display` styles are handled outside of keyframes/web-animations ([#28911](https://github.com/angular/angular/issues/28911)) ([86981b3](https://github.com/angular/angular/commit/86981b3)), closes [#24923](https://github.com/angular/angular/issues/24923) [#25635](https://github.com/angular/angular/issues/25635)
|
* **animations:** ensure `position` and `display` styles are handled outside of keyframes/web-animations ([#28911](https://github.com/angular/angular/issues/28911)) ([86981b3](https://github.com/angular/angular/commit/86981b3)), closes [#24923](https://github.com/angular/angular/issues/24923) [#25635](https://github.com/angular/angular/issues/25635)
|
||||||
* **router:** removed obsolete TODO comment ([#29085](https://github.com/angular/angular/issues/29085)) ([2a25ac2](https://github.com/angular/angular/commit/2a25ac2))
|
* **router:** removed obsolete TODO comment ([#29085](https://github.com/angular/angular/issues/29085)) ([2a25ac2](https://github.com/angular/angular/commit/2a25ac2))
|
||||||
* **service-worker:** detect new version even if files are identical to an old one ([#26006](https://github.com/angular/angular/issues/26006)) ([5669333](https://github.com/angular/angular/commit/5669333)), closes [#24338](https://github.com/angular/angular/issues/24338)
|
* **service-worker:** detect new version even if files are identical to an old one ([#26006](https://github.com/angular/angular/issues/26006)) ([5669333](https://github.com/angular/angular/commit/5669333)), closes [#24338](https://github.com/angular/angular/issues/24338)
|
||||||
* **service-worker:** ignore passive mixed content requests ([#25994](https://github.com/angular/angular/issues/25994)) ([b598e88](https://github.com/angular/angular/commit/b598e88)), closes [23012#issuecomment-376430187](https://github.com/angular/angular/issues/23012#issuecomment-376430187) [#23012](https://github.com/angular/angular/issues/23012)
|
* **service-worker:** ignore passive mixed content requests ([#25994](https://github.com/angular/angular/issues/25994)) ([b598e88](https://github.com/angular/angular/commit/b598e88)), closes [/github.com/angular/angular/issues/23012#issuecomment-376430187](https://github.com//github.com/angular/angular/issues/23012/issues/issuecomment-376430187) [#23012](https://github.com/angular/angular/issues/23012)
|
||||||
|
|
||||||
|
|
||||||
<a name="7.2.7"></a>
|
<a name="7.2.7"></a>
|
||||||
@ -2357,7 +1879,7 @@ This release contains various API docs improvements.
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **compiler-cli:** incorrect bundled metadata for static class member call expressions ([#28762](https://github.com/angular/angular/issues/28762)) ([ab69c31](https://github.com/angular/angular/commit/ab69c31)), closes [/github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts#L121](https://github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts/issues/L121) [#28741](https://github.com/angular/angular/issues/28741)
|
* **compiler-cli:** incorrect bundled metadata for static class member call expressions ([#28762](https://github.com/angular/angular/issues/28762)) ([ab69c31](https://github.com/angular/angular/commit/ab69c31)), closes [/github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts#L121](https://github.com//github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts/issues/L121) [#28741](https://github.com/angular/angular/issues/28741)
|
||||||
|
|
||||||
|
|
||||||
<a name="7.2.5"></a>
|
<a name="7.2.5"></a>
|
||||||
@ -2500,7 +2022,7 @@ This release contains various API docs improvements.
|
|||||||
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([522919a](https://github.com/angular/angular/commit/522919a))
|
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([522919a](https://github.com/angular/angular/commit/522919a))
|
||||||
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([1cc08b4](https://github.com/angular/angular/commit/1cc08b4))
|
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([1cc08b4](https://github.com/angular/angular/commit/1cc08b4))
|
||||||
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([8313ffc](https://github.com/angular/angular/commit/8313ffc))
|
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([8313ffc](https://github.com/angular/angular/commit/8313ffc))
|
||||||
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([44dfa60](https://github.com/angular/angular/commit/44dfa60)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([44dfa60](https://github.com/angular/angular/commit/44dfa60)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com//github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
||||||
* **bazel:** Set module_name and enable ng test ([#27715](https://github.com/angular/angular/issues/27715)) ([85866de](https://github.com/angular/angular/commit/85866de))
|
* **bazel:** Set module_name and enable ng test ([#27715](https://github.com/angular/angular/issues/27715)) ([85866de](https://github.com/angular/angular/commit/85866de))
|
||||||
* **bazel:** fix TS errors in the `schematics/bazel-workspace` files ([#27600](https://github.com/angular/angular/issues/27600)) ([3290fc3](https://github.com/angular/angular/commit/3290fc3))
|
* **bazel:** fix TS errors in the `schematics/bazel-workspace` files ([#27600](https://github.com/angular/angular/issues/27600)) ([3290fc3](https://github.com/angular/angular/commit/3290fc3))
|
||||||
* **bazel:** Read latest versions from latest-versions.ts & use semver check ([#27526](https://github.com/angular/angular/issues/27526)) ([30a3b49](https://github.com/angular/angular/commit/30a3b49))
|
* **bazel:** Read latest versions from latest-versions.ts & use semver check ([#27526](https://github.com/angular/angular/issues/27526)) ([30a3b49](https://github.com/angular/angular/commit/30a3b49))
|
||||||
@ -2540,7 +2062,7 @@ This release contains various API docs improvements.
|
|||||||
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([0d8528b](https://github.com/angular/angular/commit/0d8528b))
|
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([0d8528b](https://github.com/angular/angular/commit/0d8528b))
|
||||||
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([3ed1e84](https://github.com/angular/angular/commit/3ed1e84))
|
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([3ed1e84](https://github.com/angular/angular/commit/3ed1e84))
|
||||||
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([89ace1a](https://github.com/angular/angular/commit/89ace1a))
|
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([89ace1a](https://github.com/angular/angular/commit/89ace1a))
|
||||||
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([ef056c5](https://github.com/angular/angular/commit/ef056c5)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([ef056c5](https://github.com/angular/angular/commit/ef056c5)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com//github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
||||||
* **bazel:** Read latest versions from latest-versions.ts & use semver check ([#27591](https://github.com/angular/angular/issues/27591)) ([93078e3](https://github.com/angular/angular/commit/93078e3))
|
* **bazel:** Read latest versions from latest-versions.ts & use semver check ([#27591](https://github.com/angular/angular/issues/27591)) ([93078e3](https://github.com/angular/angular/commit/93078e3))
|
||||||
* **bazel:** Set module_name and enable ng test ([#27715](https://github.com/angular/angular/issues/27715)) ([183f278](https://github.com/angular/angular/commit/183f278))
|
* **bazel:** Set module_name and enable ng test ([#27715](https://github.com/angular/angular/issues/27715)) ([183f278](https://github.com/angular/angular/commit/183f278))
|
||||||
* **common:** KeyValuePipe should return empty array for empty objects ([#27258](https://github.com/angular/angular/issues/27258)) ([fa3af8b](https://github.com/angular/angular/commit/fa3af8b))
|
* **common:** KeyValuePipe should return empty array for empty objects ([#27258](https://github.com/angular/angular/issues/27258)) ([fa3af8b](https://github.com/angular/angular/commit/fa3af8b))
|
||||||
@ -2569,7 +2091,7 @@ This release contains various API docs improvements.
|
|||||||
* **bazel:** ng_package not generating UMD bundles on windows ([#27200](https://github.com/angular/angular/issues/27200)) ([e476c38](https://github.com/angular/angular/commit/e476c38))
|
* **bazel:** ng_package not generating UMD bundles on windows ([#27200](https://github.com/angular/angular/issues/27200)) ([e476c38](https://github.com/angular/angular/commit/e476c38))
|
||||||
* **bazel:** ng_package should correctly map to source maps in secondary entry-points ([#27313](https://github.com/angular/angular/issues/27313)) ([fc2c23e](https://github.com/angular/angular/commit/fc2c23e)), closes [#25510](https://github.com/angular/angular/issues/25510)
|
* **bazel:** ng_package should correctly map to source maps in secondary entry-points ([#27313](https://github.com/angular/angular/issues/27313)) ([fc2c23e](https://github.com/angular/angular/commit/fc2c23e)), closes [#25510](https://github.com/angular/angular/issues/25510)
|
||||||
* **compiler-cli:** flatModuleIndex files not generated on windows with multiple input files ([#27200](https://github.com/angular/angular/issues/27200)) ([8087b6b](https://github.com/angular/angular/commit/8087b6b))
|
* **compiler-cli:** flatModuleIndex files not generated on windows with multiple input files ([#27200](https://github.com/angular/angular/issues/27200)) ([8087b6b](https://github.com/angular/angular/commit/8087b6b))
|
||||||
* **compiler-cli:** ngtsc shim files not being generated on case-insensitive platforms ([#27466](https://github.com/angular/angular/issues/27466)) ([84f2928](https://github.com/angular/angular/commit/84f2928)), closes [/github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts#L681-L682](https://github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts/issues/L681-L682)
|
* **compiler-cli:** ngtsc shim files not being generated on case-insensitive platforms ([#27466](https://github.com/angular/angular/issues/27466)) ([84f2928](https://github.com/angular/angular/commit/84f2928)), closes [/github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts#L681-L682](https://github.com//github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts/issues/L681-L682)
|
||||||
* **platform-server:** add @angular/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
* **platform-server:** add @angular/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||||
|
|
||||||
|
|
||||||
@ -2793,7 +2315,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
|||||||
* **compiler:** update compiler to flatten nested template fns ([#24943](https://github.com/angular/angular/issues/24943)) ([fe14f18](https://github.com/angular/angular/commit/fe14f18))
|
* **compiler:** update compiler to flatten nested template fns ([#24943](https://github.com/angular/angular/issues/24943)) ([fe14f18](https://github.com/angular/angular/commit/fe14f18))
|
||||||
* **compiler:** update compiler to generate new slot allocations ([#25607](https://github.com/angular/angular/issues/25607)) ([27e2039](https://github.com/angular/angular/commit/27e2039))
|
* **compiler:** update compiler to generate new slot allocations ([#25607](https://github.com/angular/angular/issues/25607)) ([27e2039](https://github.com/angular/angular/commit/27e2039))
|
||||||
* **core:** In Testability.whenStable update callback, pass more complete ([#25010](https://github.com/angular/angular/issues/25010)) ([16c03c0](https://github.com/angular/angular/commit/16c03c0))
|
* **core:** In Testability.whenStable update callback, pass more complete ([#25010](https://github.com/angular/angular/issues/25010)) ([16c03c0](https://github.com/angular/angular/commit/16c03c0))
|
||||||
* **core:** add missing `peerDependency ` to `@angular/compiler` ([#26033](https://github.com/angular/angular/issues/26033)) ([549de1e](https://github.com/angular/angular/commit/549de1e)), closes [/github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e#diff-58563046c4439699f2e6a89187099a54](https://github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e/issues/diff-58563046c4439699f2e6a89187099a54)
|
* **core:** add missing `peerDependency ` to `@angular/compiler` ([#26033](https://github.com/angular/angular/issues/26033)) ([549de1e](https://github.com/angular/angular/commit/549de1e)), closes [/github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e#diff-58563046c4439699f2e6a89187099a54](https://github.com//github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e/issues/diff-58563046c4439699f2e6a89187099a54)
|
||||||
* **core:** allow null value for renderer setElement(…) ([#17065](https://github.com/angular/angular/issues/17065)) ([ff15043](https://github.com/angular/angular/commit/ff15043)), closes [#13686](https://github.com/angular/angular/issues/13686)
|
* **core:** allow null value for renderer setElement(…) ([#17065](https://github.com/angular/angular/issues/17065)) ([ff15043](https://github.com/angular/angular/commit/ff15043)), closes [#13686](https://github.com/angular/angular/issues/13686)
|
||||||
* **core:** do not clear element content when using shadow dom ([#24861](https://github.com/angular/angular/issues/24861)) ([6e828bb](https://github.com/angular/angular/commit/6e828bb))
|
* **core:** do not clear element content when using shadow dom ([#24861](https://github.com/angular/angular/issues/24861)) ([6e828bb](https://github.com/angular/angular/commit/6e828bb))
|
||||||
* **core:** size regression with closure compiler ([#25531](https://github.com/angular/angular/issues/25531)) ([1f59f2f](https://github.com/angular/angular/commit/1f59f2f))
|
* **core:** size regression with closure compiler ([#25531](https://github.com/angular/angular/issues/25531)) ([1f59f2f](https://github.com/angular/angular/commit/1f59f2f))
|
||||||
|
@ -42,9 +42,7 @@ Please consider what kind of change it is:
|
|||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||||
and help you to craft the change so that it is successfully accepted into the project. **Note**:
|
and help you to craft the change so that it is successfully accepted into the project.
|
||||||
Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major
|
|
||||||
feature.
|
|
||||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||||
|
|
||||||
## <a name="submit"></a> Submission Guidelines
|
## <a name="submit"></a> Submission Guidelines
|
||||||
@ -57,7 +55,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
|
|||||||
|
|
||||||
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
||||||
|
|
||||||
We will be insisting on a minimal reproduction scenario in order to save maintainers' time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
We will be insisting on a minimal reproduction scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
||||||
|
|
||||||
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
|
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
|
||||||
|
|
||||||
@ -85,7 +83,8 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||||
and ensure that all tests pass.
|
and ensure that all tests pass.
|
||||||
1. Commit your changes using a descriptive commit message that follows our
|
1. Commit your changes using a descriptive commit message that follows our
|
||||||
[commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
[commit message conventions](#commit). Adherence to these conventions
|
||||||
|
is necessary because release notes are automatically generated from these messages.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git commit -a
|
git commit -a
|
||||||
@ -180,13 +179,13 @@ Samples: (even more [samples](https://github.com/angular/angular/commits/master)
|
|||||||
docs(changelog): update changelog to beta.5
|
docs(changelog): update changelog to beta.5
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
fix(release): need to depend on the latest rxjs and zone.js
|
fix(release): need to depend on latest rxjs and zone.js
|
||||||
|
|
||||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Revert
|
### Revert
|
||||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||||
|
|
||||||
### Type
|
### Type
|
||||||
Must be one of the following:
|
Must be one of the following:
|
||||||
@ -237,7 +236,6 @@ There are currently a few exceptions to the "use package name" rule:
|
|||||||
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
||||||
repo
|
repo
|
||||||
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
||||||
* **migrations**: used for changes to the `ng update` migrations.
|
|
||||||
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
||||||
* **ve**: used for changes specific to ViewEngine (legacy compiler/renderer).
|
* **ve**: used for changes specific to ViewEngine (legacy compiler/renderer).
|
||||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
||||||
@ -281,7 +279,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
|||||||
* https://help.github.com/articles/about-commit-email-addresses/
|
* https://help.github.com/articles/about-commit-email-addresses/
|
||||||
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||||
|
|
||||||
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA before you commit changes. If not, your PR will fail the CLA check.
|
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
15
WORKSPACE
15
WORKSPACE
@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|||||||
# Fetch rules_nodejs so we can install our npm dependencies
|
# Fetch rules_nodejs so we can install our npm dependencies
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
|
sha256 = "f9e7b9f42ae202cc2d2ce6d698ccb49a9f7f7ea572a78fd451696d03ef2ee116",
|
||||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
|
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.6.0/rules_nodejs-1.6.0.tar.gz"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check the rules_nodejs version and download npm dependencies
|
# Check the rules_nodejs version and download npm dependencies
|
||||||
@ -17,7 +17,7 @@ http_archive(
|
|||||||
# assert on that.
|
# assert on that.
|
||||||
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||||
|
|
||||||
check_rules_nodejs_version(minimum_version_string = "1.7.0")
|
check_rules_nodejs_version(minimum_version_string = "1.6.0")
|
||||||
|
|
||||||
# Setup the Node.js toolchain
|
# Setup the Node.js toolchain
|
||||||
node_repositories(
|
node_repositories(
|
||||||
@ -64,7 +64,7 @@ load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories"
|
|||||||
|
|
||||||
web_test_repositories()
|
web_test_repositories()
|
||||||
|
|
||||||
load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
|
load("//tools/browsers:browser_repositories.bzl", "browser_repositories")
|
||||||
|
|
||||||
browser_repositories()
|
browser_repositories()
|
||||||
|
|
||||||
@ -91,18 +91,17 @@ rbe_autoconfig(
|
|||||||
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
||||||
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
||||||
# need to pull the image and run it in order determine the toolchain configuration. See:
|
# need to pull the image and run it in order determine the toolchain configuration. See:
|
||||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl
|
# https://github.com/bazelbuild/bazel-toolchains/blob/1.1.2/configs/ubuntu16_04_clang/versions.bzl
|
||||||
base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332",
|
base_container_digest = "sha256:1ab40405810effefa0b2f45824d6d608634ccddbf06366760c341ef6fbead011",
|
||||||
# Note that if you change the `digest`, you might also need to update the
|
# Note that if you change the `digest`, you might also need to update the
|
||||||
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
||||||
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
||||||
# the same Clang and JDK installed. Clang is needed because of the dependency on
|
# the same Clang and JDK installed. Clang is needed because of the dependency on
|
||||||
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
||||||
digest = "sha256:f743114235a43355bf8324e2ba0fa6a597236fe06f7bc99aaa9ac703631c306b",
|
digest = "sha256:0b8fa87db4b8e5366717a7164342a029d1348d2feea7ecc4b18c780bc2507059",
|
||||||
env = clang_env(),
|
env = clang_env(),
|
||||||
registry = "marketplace.gcr.io",
|
registry = "marketplace.gcr.io",
|
||||||
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
||||||
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
|
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
|
||||||
repository = "google/rbe-ubuntu16-04-webtest",
|
repository = "google/rbe-ubuntu16-04-webtest",
|
||||||
use_checked_in_confs = "Force",
|
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Image metadata and config
|
# Image metadata and config
|
||||||
FROM debian:buster
|
FROM debian:stretch
|
||||||
|
|
||||||
LABEL name="angular.io PR preview" \
|
LABEL name="angular.io PR preview" \
|
||||||
description="This image implements the PR preview functionality for angular.io." \
|
description="This image implements the PR preview functionality for angular.io." \
|
||||||
@ -37,9 +37,9 @@ ARG TEST_AIO_NGINX_PORT_HTTPS=4433
|
|||||||
ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)'
|
ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)'
|
||||||
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
|
||||||
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||||
ARG TEST_AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL
|
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
|
||||||
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||||
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME
|
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
|
||||||
ARG AIO_ARTIFACT_MAX_SIZE=26214400
|
ARG AIO_ARTIFACT_MAX_SIZE=26214400
|
||||||
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
|
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
|
||||||
ARG AIO_PREVIEW_SERVER_PORT=3000
|
ARG AIO_PREVIEW_SERVER_PORT=3000
|
||||||
@ -72,29 +72,24 @@ RUN mkdir /var/log/aio
|
|||||||
|
|
||||||
|
|
||||||
# Add extra package sources
|
# Add extra package sources
|
||||||
RUN apt-get update -y && apt-get install -y curl=7.64.0-4+deb10u1
|
RUN apt-get update -y && apt-get install -y curl
|
||||||
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_12.x | bash -
|
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_10.x | bash -
|
||||||
RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
||||||
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
|
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
|
||||||
|
RUN echo "deb http://ftp.debian.org/debian stretch-backports main" | tee /etc/apt/sources.list.d/backports.list
|
||||||
|
|
||||||
|
|
||||||
# Install packages
|
# Install packages
|
||||||
# NOTE: Some packages (such as `nginx`, `nodejs`, `openssl`) make older versions unavailable on the
|
|
||||||
# repositories, so we cannot pin to specific versions for these packages :(
|
|
||||||
# See for example:
|
|
||||||
# - https://github.com/nodesource/distributions/issues/33
|
|
||||||
# - https://askubuntu.com/questions/715104/how-can-i-downgrade-openssl-via-apt-get
|
|
||||||
RUN apt-get update -y && apt-get install -y \
|
RUN apt-get update -y && apt-get install -y \
|
||||||
cron=3.0pl1-134+deb10u1 \
|
cron=3.0pl1-128+deb9u1 \
|
||||||
dnsmasq=2.80-1 \
|
dnsmasq=2.76-5+deb9u2 \
|
||||||
nano=3.2-3 \
|
nano=2.7.4-1 \
|
||||||
nginx \
|
nginx=1.10.3-1+deb9u2 \
|
||||||
nodejs \
|
nodejs=10.15.3-1nodesource1 \
|
||||||
openssl \
|
openssl=1.1.0j-1~deb9u1 \
|
||||||
rsyslog=8.1901.0-1 \
|
rsyslog=8.24.0-1 \
|
||||||
vim=2:8.1.0875-5 \
|
yarn=1.15.2-1
|
||||||
yarn=1.22.4-1
|
RUN yarn global add pm2@3.5.0
|
||||||
RUN yarn global add pm2@4.4.0
|
|
||||||
|
|
||||||
|
|
||||||
# Set up log rotation
|
# Set up log rotation
|
||||||
@ -167,7 +162,8 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
|
|||||||
|
|
||||||
# Set up the Node.js scripts
|
# Set up the Node.js scripts
|
||||||
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
||||||
RUN yarn --cwd="$AIO_SCRIPTS_JS_DIR/" install --production --frozen-lockfile
|
WORKDIR $AIO_SCRIPTS_JS_DIR/
|
||||||
|
RUN yarn install --production --frozen-lockfile
|
||||||
|
|
||||||
|
|
||||||
# Set up health check
|
# Set up health check
|
||||||
|
@ -35,7 +35,6 @@ export class BuildCleaner {
|
|||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('ERROR:', error);
|
this.logger.error('ERROR:', error);
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
|
import {promisify} from 'util';
|
||||||
import {PreviewServerError} from './preview-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,7 +13,7 @@ export async function respondWithError(res: express.Response, err: any): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.status(err.status);
|
res.status(err.status);
|
||||||
return new Promise(resolve => res.end(err.message, resolve));
|
await promisify(res.end.bind(res))(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +93,7 @@ class Helper {
|
|||||||
return fs.readFileSync(absFilePath, 'utf8');
|
return fs.readFileSync(absFilePath, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
public runCmd(cmd: string, opts: cp.ExecOptions = {}): Promise<CmdResult> {
|
public runCmd(cmd: string, opts: cp.ExecFileOptions = {}): Promise<CmdResult> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
|
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
|
||||||
this.createCleanUpFn(() => proc.kill());
|
this.createCleanUpFn(() => proc.kill());
|
||||||
@ -101,7 +101,7 @@ class Helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
|
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
|
||||||
Object.entries(this.portPerScheme).forEach(([scheme, port]) => suiteFactory(scheme, port));
|
Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
|
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
|
||||||
|
@ -15,7 +15,7 @@ describe(`nginx`, () => {
|
|||||||
afterEach(() => h.cleanUp());
|
afterEach(() => h.cleanUp());
|
||||||
|
|
||||||
|
|
||||||
it('should redirect HTTP to HTTPS', async () => {
|
it('should redirect HTTP to HTTPS', done => {
|
||||||
const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`;
|
const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`;
|
||||||
const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`;
|
const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`;
|
||||||
const urlMap = {
|
const urlMap = {
|
||||||
@ -24,15 +24,16 @@ describe(`nginx`, () => {
|
|||||||
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
|
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyRedirection = async (fromUrl: string, toUrl: string) => {
|
const verifyRedirection = (httpUrl: string) => h.runCmd(`curl -i ${httpUrl}`).then(result => {
|
||||||
const result = await h.runCmd(`curl -i ${fromUrl}`);
|
|
||||||
h.verifyResponse(307)(result);
|
h.verifyResponse(307)(result);
|
||||||
|
|
||||||
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
|
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
|
||||||
expect(headers).toContain(`Location: ${toUrl}`);
|
expect(headers).toContain(`Location: ${urlMap[httpUrl]}`);
|
||||||
};
|
});
|
||||||
|
|
||||||
await Promise.all(Object.entries(urlMap).map(urls => verifyRedirection(...urls)));
|
Promise.
|
||||||
|
all(Object.keys(urlMap).map(verifyRedirection)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -61,15 +62,15 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return /index.html', async () => {
|
it('should return /index.html', done => {
|
||||||
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
||||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -89,11 +90,12 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return /foo/bar.js', async () => {
|
it('should return /foo/bar.js', done => {
|
||||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
||||||
|
|
||||||
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
|
||||||
then(h.verifyResponse(200, bodyRegex));
|
then(h.verifyResponse(200, bodyRegex)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -109,46 +111,47 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 403 for directories', async () => {
|
it('should respond with 403 for directories', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/`).then(h.verifyResponse(403)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/`).then(h.verifyResponse(403)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo`).then(h.verifyResponse(403)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo`).then(h.verifyResponse(403)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths to files', async () => {
|
it('should respond with 404 for unknown paths to files', done => {
|
||||||
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
|
||||||
then(h.verifyResponse(404));
|
then(h.verifyResponse(404)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', async () => {
|
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', done => {
|
||||||
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
|
||||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${origin}/foo/baz`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/foo/baz`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${origin}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)),
|
h.runCmd(`curl -iL ${origin}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown PRs/SHAs', async () => {
|
it('should respond with 404 for unknown PRs/SHAs', done => {
|
||||||
const otherPr = 54321;
|
const otherPr = 54321;
|
||||||
const otherShortSha = computeShortSha('8'.repeat(40));
|
const otherShortSha = computeShortSha('8'.repeat(40));
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}9-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}9-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${otherPr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${otherPr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}9.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}9.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${otherShortSha}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${otherShortSha}.${host}`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the subdomain format is wrong', async () => {
|
it('should respond with 404 if the subdomain format is wrong', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://xpr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://xpr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://prx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://prx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
@ -157,25 +160,26 @@ describe(`nginx`, () => {
|
|||||||
h.runCmd(`curl -iL ${scheme}://${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}_${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}_${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject PRs with leading zeros', async () => {
|
it('should reject PRs with leading zeros', done => {
|
||||||
await h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
|
h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
|
||||||
then(h.verifyResponse(404));
|
then(h.verifyResponse(404)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should accept SHAs with leading zeros (but not trim the zeros)', async () => {
|
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||||
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||||
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-0${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-0${shortSha9}.${host}`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)),
|
||||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
|
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -227,23 +231,23 @@ describe(`nginx`, () => {
|
|||||||
|
|
||||||
describe(`${host}/health-check`, () => {
|
describe(`${host}/health-check`, () => {
|
||||||
|
|
||||||
it('should respond with 200', async () => {
|
it('should respond with 200', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check`).then(h.verifyResponse(200)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check/`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check/`).then(h.verifyResponse(200)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the path does not match exactly', async () => {
|
it('should respond with 404 if the path does not match exactly', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-check-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-check-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/foo/health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/foo/health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/foo-health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/foo-health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${scheme}://${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${scheme}://${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -287,28 +291,29 @@ describe(`nginx`, () => {
|
|||||||
|
|
||||||
describe(`${host}/circle-build`, () => {
|
describe(`${host}/circle-build`, () => {
|
||||||
|
|
||||||
it('should disallow non-POST requests', async () => {
|
it('should disallow non-POST requests', done => {
|
||||||
const url = `${scheme}://${host}/circle-build`;
|
const url = `${scheme}://${host}/circle-build`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the preview server', async () => {
|
it('should pass requests through to the preview server', done => {
|
||||||
await h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
|
||||||
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/));
|
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
it('should respond with 404 for unknown paths', done => {
|
||||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`${cmdPrefix}/foo/circle-build/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo/circle-build/`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foo-circle-build/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo-circle-build/`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/fooncircle-build/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/fooncircle-build/`).then(h.verifyResponse(404)),
|
||||||
@ -317,7 +322,7 @@ describe(`nginx`, () => {
|
|||||||
h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -327,33 +332,38 @@ describe(`nginx`, () => {
|
|||||||
const url = `${scheme}://${host}/pr-updated`;
|
const url = `${scheme}://${host}/pr-updated`;
|
||||||
|
|
||||||
|
|
||||||
it('should disallow non-POST requests', async () => {
|
it('should disallow non-POST requests', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
|
||||||
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the preview server', async () => {
|
it('should pass requests through to the preview server', done => {
|
||||||
await h.runCmd(`curl -iLX POST --header "Content-Type: application/json" ${url}`).
|
const cmdPrefix = `curl -iLX POST --header "Content-Type: application/json"`;
|
||||||
then(h.verifyResponse(400, /Missing or empty 'number' field/));
|
|
||||||
|
const cmd1 = `${cmdPrefix} ${url}`;
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
h.runCmd(cmd1).then(h.verifyResponse(400, /Missing or empty 'number' field/)),
|
||||||
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
it('should respond with 404 for unknown paths', done => {
|
||||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -364,7 +374,7 @@ describe(`nginx`, () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => {
|
['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => {
|
||||||
const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath);
|
const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath);
|
||||||
h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
|
return h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -105,8 +105,8 @@ describe('preview-server', () => {
|
|||||||
|
|
||||||
|
|
||||||
describe(`${host}/circle-build`, () => {
|
describe(`${host}/circle-build`, () => {
|
||||||
const curl = makeCurl(`${host}/circle-build`);
|
|
||||||
|
|
||||||
|
const curl = makeCurl(`${host}/circle-build`);
|
||||||
|
|
||||||
it('should disallow non-POST requests', async () => {
|
it('should disallow non-POST requests', async () => {
|
||||||
const bodyRegex = /^Unknown resource/;
|
const bodyRegex = /^Unknown resource/;
|
||||||
@ -189,7 +189,8 @@ describe('preview-server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should respond with 201 if a new public build is created', async () => {
|
it('should respond with 201 if a new public build is created', async () => {
|
||||||
await curl(payload(BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER)).then(h.verifyResponse(201));
|
await curl(payload(BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER))
|
||||||
|
.then(h.verifyResponse(201));
|
||||||
expect({ prNum: PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER }).toExistAsABuild();
|
expect({ prNum: PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER }).toExistAsABuild();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -198,7 +199,7 @@ describe('preview-server', () => {
|
|||||||
expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild();
|
expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild();
|
||||||
});
|
});
|
||||||
|
|
||||||
[true, false].forEach(isPublic => {
|
[true].forEach(isPublic => {
|
||||||
const build = isPublic ? BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : BuildNums.TRUST_CHECK_UNTRUSTED;
|
const build = isPublic ? BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : BuildNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const prNum = isPublic ? PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : PrNums.TRUST_CHECK_UNTRUSTED;
|
const prNum = isPublic ? PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : PrNums.TRUST_CHECK_UNTRUSTED;
|
||||||
const label = isPublic ? 'public' : 'non-public';
|
const label = isPublic ? 'public' : 'non-public';
|
||||||
@ -363,23 +364,23 @@ describe('preview-server', () => {
|
|||||||
|
|
||||||
describe(`${host}/health-check`, () => {
|
describe(`${host}/health-check`, () => {
|
||||||
|
|
||||||
it('should respond with 200', async () => {
|
it('should respond with 200', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${host}/health-check`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${host}/health-check`).then(h.verifyResponse(200)),
|
||||||
h.runCmd(`curl -iL ${host}/health-check/`).then(h.verifyResponse(200)),
|
h.runCmd(`curl -iL ${host}/health-check/`).then(h.verifyResponse(200)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the path does not match exactly', async () => {
|
it('should respond with 404 if the path does not match exactly', done => {
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${host}/health-check/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/health-check/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/health-check-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/health-check-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/health-checknfoo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/foo/health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/foo/health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/foo-health-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/foo-health-check`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`curl -iL ${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
h.runCmd(`curl -iL ${host}/foonhealth-check`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -425,18 +426,18 @@ describe('preview-server', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
it('should respond with 404 for unknown paths', done => {
|
||||||
const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER });
|
const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER });
|
||||||
const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`;
|
const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
|
||||||
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -550,10 +551,10 @@ describe('preview-server', () => {
|
|||||||
|
|
||||||
describe(`${host}/*`, () => {
|
describe(`${host}/*`, () => {
|
||||||
|
|
||||||
it('should respond with 404 for requests to unknown URLs', async () => {
|
it('should respond with 404 for requests to unknown URLs', done => {
|
||||||
const bodyRegex = /^Unknown resource/;
|
const bodyRegex = /^Unknown resource/;
|
||||||
|
|
||||||
await Promise.all([
|
Promise.all([
|
||||||
h.runCmd(`curl -iL ${host}/index.html`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iL ${host}/index.html`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${host}/`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iL ${host}/`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iL ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iL ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
@ -561,7 +562,7 @@ describe('preview-server', () => {
|
|||||||
h.runCmd(`curl -iLX POST ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iLX POST ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iLX PATCH ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iLX PATCH ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
h.runCmd(`curl -iLX DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
h.runCmd(`curl -iLX DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)),
|
||||||
]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -14,41 +14,42 @@
|
|||||||
"predev": "yarn build || true",
|
"predev": "yarn build || true",
|
||||||
"dev": "run-p ~~build-watch ~~test-watch",
|
"dev": "run-p ~~build-watch ~~test-watch",
|
||||||
"lint": "tslint --project tsconfig.json",
|
"lint": "tslint --project tsconfig.json",
|
||||||
"pretest": "run-s build lint",
|
"pretest": "yarn build",
|
||||||
"test": "yarn ~~test-only",
|
"test": "yarn ~~test-only",
|
||||||
"pretest-watch": "yarn pretest",
|
"pretest-watch": "yarn pretest",
|
||||||
"test-watch": "yarn ~~test-watch",
|
"test-watch": "yarn ~~test-watch",
|
||||||
"~~build": "tsc",
|
"~~build": "tsc",
|
||||||
"~~build-watch": "yarn ~~build --watch",
|
"~~build-watch": "yarn ~~build --watch",
|
||||||
|
"pre~~test-only": "yarn lint",
|
||||||
"~~test-only": "node dist/test",
|
"~~test-only": "node dist/test",
|
||||||
"~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist"
|
"~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.18.3",
|
||||||
"delete-empty": "^3.0.0",
|
"delete-empty": "^2.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.16.3",
|
||||||
"jasmine": "^3.5.0",
|
"jasmine": "^3.2.0",
|
||||||
"nock": "^12.0.3",
|
"nock": "^9.6.1",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.2.0",
|
||||||
"shelljs": "^0.8.4",
|
"shelljs": "^0.8.2",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.9",
|
||||||
"tar-stream": "^2.1.2",
|
"tar-stream": "^1.6.1",
|
||||||
"tslib": "^1.11.1"
|
"tslib": "^1.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/body-parser": "^1.19.0",
|
"@types/body-parser": "^1.17.0",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.16.0",
|
||||||
"@types/jasmine": "^3.5.10",
|
"@types/jasmine": "^2.8.8",
|
||||||
"@types/nock": "^11.1.0",
|
"@types/nock": "^9.3.0",
|
||||||
"@types/node": "^13.13.2",
|
"@types/node": "^10.9.2",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.1.2",
|
||||||
"@types/shelljs": "^0.8.7",
|
"@types/shelljs": "^0.8.0",
|
||||||
"@types/supertest": "^2.0.8",
|
"@types/supertest": "^2.0.5",
|
||||||
"nodemon": "^2.0.3",
|
"nodemon": "^1.18.3",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^3.1.0",
|
||||||
"tslint": "^6.1.1",
|
"tslint": "^5.11.0",
|
||||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ const EXISTING_DOWNLOADS = [
|
|||||||
'20-1234567-build.zip',
|
'20-1234567-build.zip',
|
||||||
];
|
];
|
||||||
const OPEN_PRS = [10, 40];
|
const OPEN_PRS = [10, 40];
|
||||||
|
const ANY_DATE = jasmine.any(String);
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildCleaner', () => {
|
describe('BuildCleaner', () => {
|
||||||
@ -76,18 +77,22 @@ describe('BuildCleaner', () => {
|
|||||||
let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
|
let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers').and.resolveTo(EXISTING_BUILDS);
|
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers')
|
||||||
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers').and.resolveTo(OPEN_PRS);
|
.and.callFake(() => Promise.resolve(EXISTING_BUILDS));
|
||||||
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads').and.resolveTo(EXISTING_DOWNLOADS);
|
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers')
|
||||||
|
.and.callFake(() => Promise.resolve(OPEN_PRS));
|
||||||
|
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads')
|
||||||
|
.and.callFake(() => Promise.resolve(EXISTING_DOWNLOADS));
|
||||||
|
|
||||||
cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
|
cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
|
||||||
cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
|
cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', async () => {
|
||||||
const promise = cleaner.cleanUp();
|
const promise = cleaner.cleanUp();
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
|
|
||||||
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
|
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
|
||||||
await promise;
|
await promise;
|
||||||
@ -125,32 +130,52 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
|
it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
|
||||||
cleanerGetOpenPrNumbersSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerGetOpenPrNumbersSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
|
it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
|
||||||
cleanerGetExistingBuildNumbersSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerGetExistingBuildNumbersSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'getExistingDownloads()\' rejects', async () => {
|
it('should reject if \'getExistingDownloads()\' rejects', async () => {
|
||||||
cleanerGetExistingDownloadsSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerGetExistingDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
|
it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
|
||||||
cleanerRemoveUnnecessaryBuildsSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerRemoveUnnecessaryBuildsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
||||||
cleanerRemoveUnnecessaryDownloadsSpy.and.rejectWith('Test');
|
try {
|
||||||
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test');
|
cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
await cleaner.cleanUp();
|
||||||
|
} catch (err) {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -162,15 +187,13 @@ describe('BuildCleaner', () => {
|
|||||||
let promise: Promise<number[]>;
|
let promise: Promise<number[]>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake(
|
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
|
||||||
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
|
|
||||||
);
|
|
||||||
promise = cleaner.getExistingBuildNumbers();
|
promise = cleaner.getExistingBuildNumbers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -180,27 +203,43 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if an error occurs while getting the files', async () => {
|
it('should reject if an error occurs while getting the files', done => {
|
||||||
|
promise.catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb('Test');
|
readdirCb('Test');
|
||||||
await expectAsync(promise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned files (as numbers)', async () => {
|
it('should resolve with the returned files (as numbers)', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual([12, 34, 56]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, ['12', '34', '56']);
|
readdirCb(null, ['12', '34', '56']);
|
||||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', async () => {
|
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual([12, 34, 56]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]);
|
readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]);
|
||||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should ignore files with non-numeric (or zero) names', async () => {
|
it('should ignore files with non-numeric (or zero) names', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual([12, 34, 56]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
|
readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
|
||||||
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -220,7 +259,7 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -229,15 +268,31 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if an error occurs while fetching PRs', async () => {
|
it('should reject if an error occurs while fetching PRs', done => {
|
||||||
|
promise.catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
prDeferred.reject('Test');
|
prDeferred.reject('Test');
|
||||||
await expectAsync(promise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the numbers of the fetched PRs', async () => {
|
it('should resolve with the numbers of the fetched PRs', done => {
|
||||||
|
promise.then(prNumbers => {
|
||||||
|
expect(prNumbers).toEqual([1, 2, 3]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
|
prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
|
||||||
await expectAsync(promise).toBeResolvedTo([1, 2, 3]);
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should log the number of open PRs', () => {
|
||||||
|
promise.then(prNumbers => {
|
||||||
|
expect(loggerLogSpy).toHaveBeenCalledWith(
|
||||||
|
ANY_DATE, 'BuildCleaner: ', `Open pull requests: ${prNumbers}`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -249,15 +304,13 @@ describe('BuildCleaner', () => {
|
|||||||
let promise: Promise<string[]>;
|
let promise: Promise<string[]>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake(
|
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
|
||||||
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
|
|
||||||
);
|
|
||||||
promise = cleaner.getExistingDownloads();
|
promise = cleaner.getExistingDownloads();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -267,21 +320,33 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if an error occurs while getting the files', async () => {
|
it('should reject if an error occurs while getting the files', done => {
|
||||||
|
promise.catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb('Test');
|
readdirCb('Test');
|
||||||
await expectAsync(promise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned file names', async () => {
|
it('should resolve with the returned file names', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual(EXISTING_DOWNLOADS);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, EXISTING_DOWNLOADS);
|
readdirCb(null, EXISTING_DOWNLOADS);
|
||||||
await expectAsync(promise).toBeResolvedTo(EXISTING_DOWNLOADS);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should ignore files that do not match the artifactPath', async () => {
|
it('should ignore files that do not match the artifactPath', done => {
|
||||||
|
promise.then(result => {
|
||||||
|
expect(result).toEqual(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']);
|
readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']);
|
||||||
await expectAsync(promise).toBeResolvedTo(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -299,7 +364,7 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should test if the directory exists (and return if it does not)', () => {
|
it('should test if the directory exists (and return if is does not)', () => {
|
||||||
shellTestSpy.and.returnValue(false);
|
shellTestSpy.and.returnValue(false);
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
@ -316,19 +381,22 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should make the directory and its content writable before removing', () => {
|
it('should make the directory and its content writable before removing', () => {
|
||||||
|
shellRmSpy.and.callFake(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar'));
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalledBefore(shellRmSpy);
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar');
|
|
||||||
expect(shellRmSpy).toHaveBeenCalled();
|
expect(shellRmSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should catch errors and log them', () => {
|
it('should catch errors and log them', () => {
|
||||||
shellRmSpy.and.throwError('Test');
|
shellRmSpy.and.callFake(() => {
|
||||||
|
// tslint:disable-next-line: no-string-throw
|
||||||
|
throw 'Test';
|
||||||
|
});
|
||||||
|
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', new Error('Test'));
|
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -381,7 +449,7 @@ describe('BuildCleaner', () => {
|
|||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
|
||||||
cleanerRemoveDirSpy.calls.reset();
|
cleanerRemoveDirSpy.calls.reset();
|
||||||
|
|
||||||
cleaner.removeUnnecessaryBuilds([1, 2, 3, 4], []);
|
(cleaner as any).removeUnnecessaryBuilds([1, 2, 3, 4], []);
|
||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
|
||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
|
||||||
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
|
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));
|
||||||
|
@ -45,15 +45,25 @@ describe('CircleCIApi', () => {
|
|||||||
const errorMessage = 'Invalid request';
|
const errorMessage = 'Invalid request';
|
||||||
const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`);
|
const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`);
|
||||||
|
|
||||||
|
try {
|
||||||
request.replyWithError(errorMessage);
|
request.replyWithError(errorMessage);
|
||||||
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError(
|
await api.getBuildInfo(buildNum);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI build info request failed ` +
|
`CircleCI build info request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
request.reply(404, errorMessage);
|
request.reply(404, errorMessage);
|
||||||
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError(
|
await api.getBuildInfo(buildNum);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI build info request failed ` +
|
`CircleCI build info request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,7 +78,8 @@ describe('CircleCIApi', () => {
|
|||||||
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
||||||
.reply(200, [artifact0, artifact1, artifact2]);
|
.reply(200, [artifact0, artifact1, artifact2]);
|
||||||
|
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeResolvedTo('https://url/1');
|
const artifactUrl = await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||||
|
expect(artifactUrl).toEqual('https://url/1');
|
||||||
request.done();
|
request.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,15 +90,25 @@ describe('CircleCIApi', () => {
|
|||||||
const errorMessage = 'Invalid request';
|
const errorMessage = 'Invalid request';
|
||||||
const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`);
|
const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`);
|
||||||
|
|
||||||
|
try {
|
||||||
request.replyWithError(errorMessage);
|
request.replyWithError(errorMessage);
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError(
|
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI artifact URL request failed ` +
|
`CircleCI artifact URL request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
request.reply(404, errorMessage);
|
request.reply(404, errorMessage);
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError(
|
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI artifact URL request failed ` +
|
`CircleCI artifact URL request failed ` +
|
||||||
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if the response does not contain the specified artifact', async () => {
|
it('should throw an error if the response does not contain the specified artifact', async () => {
|
||||||
@ -100,9 +121,14 @@ describe('CircleCIApi', () => {
|
|||||||
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
|
||||||
.reply(200, [artifact0, artifact1, artifact2]);
|
.reply(200, [artifact0, artifact1, artifact2]);
|
||||||
|
|
||||||
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/3')).toBeRejectedWithError(
|
try {
|
||||||
|
await api.getBuildArtifactUrl(buildNum, 'some/path/3');
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.message).toEqual(
|
||||||
`CircleCI artifact URL request failed ` +
|
`CircleCI artifact URL request failed ` +
|
||||||
`(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`);
|
`(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -118,7 +118,7 @@ describe('GithubApi', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect((api as any).getPaginated()).toBeInstanceOf(Promise);
|
expect((api as any).getPaginated()).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -131,30 +131,45 @@ describe('GithubApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if the request fails', async () => {
|
it('should reject if the request fails', done => {
|
||||||
const responsePromise = (api as any).getPaginated('/foo/bar');
|
(api as any).getPaginated('/foo/bar').catch((err: any) => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
deferreds[0].reject('Test');
|
deferreds[0].reject('Test');
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith('Test');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned items', async () => {
|
it('should resolve with the returned items', done => {
|
||||||
const items = [{id: 1}, {id: 2}];
|
const items = [{id: 1}, {id: 2}];
|
||||||
const responsePromise = (api as any).getPaginated('/foo/bar');
|
|
||||||
deferreds[0].resolve(items);
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeResolvedTo(items);
|
(api as any).getPaginated('/foo/bar').then((data: any) => {
|
||||||
|
expect(data).toEqual(items);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
deferreds[0].resolve(items);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should iteratively call \'get()\' to fetch all items', async () => {
|
it('should iteratively call \'get()\' to fetch all items', done => {
|
||||||
// Create an array or 250 objects.
|
// Create an array or 250 objects.
|
||||||
const allItems = new Array(250).fill(null).map((_, i) => ({id: i}));
|
const allItems = '.'.repeat(250).split('').map((_, i) => ({id: i}));
|
||||||
const apiGetSpy = api.get as jasmine.Spy;
|
const apiGetSpy = api.get as jasmine.Spy;
|
||||||
|
|
||||||
|
(api as any).getPaginated('/foo/bar', {baz: 'qux'}).then((data: any) => {
|
||||||
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
|
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
|
||||||
|
|
||||||
const responsePromise = (api as any).getPaginated('/foo/bar', {baz: 'qux'});
|
expect(apiGetSpy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
|
||||||
|
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
|
||||||
|
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
|
||||||
|
|
||||||
|
expect(data).toEqual(allItems);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
deferreds[0].resolve(allItems.slice(0, 100));
|
deferreds[0].resolve(allItems.slice(0, 100));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -163,13 +178,6 @@ describe('GithubApi', () => {
|
|||||||
deferreds[2].resolve(allItems.slice(200));
|
deferreds[2].resolve(allItems.slice(200));
|
||||||
}, 0);
|
}, 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeResolvedTo(allItems);
|
|
||||||
|
|
||||||
expect(apiGetSpy).toHaveBeenCalledTimes(3);
|
|
||||||
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
|
|
||||||
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
|
|
||||||
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -209,8 +217,8 @@ describe('GithubApi', () => {
|
|||||||
|
|
||||||
describe('request()', () => {
|
describe('request()', () => {
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
nock('https://api.github.com').get('/').reply(200);
|
nock('https://api.github.com').get('').reply(200);
|
||||||
expect((api as any).request()).toBeInstanceOf(Promise);
|
expect((api as any).request()).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -239,8 +247,9 @@ describe('GithubApi', () => {
|
|||||||
nock('https://api.github.com')
|
nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.replyWithError('Test');
|
.replyWithError('Test');
|
||||||
|
let message = 'Failed to reject error';
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError('Test');
|
await (api as any).request('method', '/path').catch((err: any) => message = err.message);
|
||||||
|
expect(message).toEqual('Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -254,69 +263,81 @@ describe('GithubApi', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if response statusCode is <200', async () => {
|
it('should reject if response statusCode is <200', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(199);
|
.reply(199);
|
||||||
const responsePromise = (api as any).request('method', '/path');
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('failed'));
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('status: 199'));
|
|
||||||
|
|
||||||
|
(api as any).request('method', '/path')
|
||||||
|
.catch((err: string) => {
|
||||||
|
expect(err).toContain('failed');
|
||||||
|
expect(err).toContain('status: 199');
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if response statusCode is >=400', async () => {
|
it('should reject if response statusCode is >=400', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(400);
|
.reply(400);
|
||||||
const responsePromise = (api as any).request('method', '/path');
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('failed'));
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('status: 400'));
|
|
||||||
|
|
||||||
|
(api as any).request('method', '/path')
|
||||||
|
.catch((err: string) => {
|
||||||
|
expect(err).toContain('failed');
|
||||||
|
expect(err).toContain('status: 400');
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should include the response text in the rejection message', async () => {
|
it('should include the response text in the rejection message', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(500, 'Test');
|
.reply(500, 'Test');
|
||||||
const responsePromise = (api as any).request('method', '/path');
|
|
||||||
|
|
||||||
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('Test'));
|
|
||||||
|
|
||||||
|
(api as any).request('method', '/path')
|
||||||
|
.catch((err: string) => {
|
||||||
|
expect(err).toContain('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve if returned statusCode is >=200 and <400', async () => {
|
it('should resolve if returned statusCode is >=200 and <400', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(200);
|
.reply(200);
|
||||||
|
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeResolved();
|
(api as any).request('method', '/path').then(done);
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should parse the response body into an object using \'JSON.parse\'', async () => {
|
it('should parse the response body into an object using \'JSON.parse\'', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(300, '{"foo": "bar"}');
|
.reply(300, '{"foo": "bar"}');
|
||||||
|
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeResolvedTo({foo: 'bar'});
|
(api as any).request('method', '/path').then((data: any) => {
|
||||||
|
expect(data).toEqual({foo: 'bar'});
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject if the response text is malformed JSON', async () => {
|
it('should reject if the response text is malformed JSON', done => {
|
||||||
const requestHandler = nock('https://api.github.com')
|
const requestHandler = nock('https://api.github.com')
|
||||||
.intercept('/path', 'method')
|
.intercept('/path', 'method')
|
||||||
.reply(300, '}');
|
.reply(300, '}');
|
||||||
|
|
||||||
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError(SyntaxError);
|
(api as any).request('method', '/path').catch((err: any) => {
|
||||||
|
expect(err).toEqual(jasmine.any(SyntaxError));
|
||||||
|
done();
|
||||||
|
});
|
||||||
requestHandler.done();
|
requestHandler.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Imports
|
// Imports
|
||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubPullRequests, PullRequest} from '../../lib/common/github-pull-requests';
|
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('GithubPullRequests', () => {
|
describe('GithubPullRequests', () => {
|
||||||
@ -47,21 +47,27 @@ describe('GithubPullRequests', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should make a POST request to Github with the correct pathname, params and data', () => {
|
it('should make a POST request to Github with the correct pathname, params and data', () => {
|
||||||
githubApi.post.and.resolveTo();
|
githubApi.post.and.callFake(() => Promise.resolve());
|
||||||
prs.addComment(42, 'body');
|
prs.addComment(42, 'body');
|
||||||
expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'});
|
expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if the request fails', async () => {
|
it('should reject if the request fails', done => {
|
||||||
githubApi.post.and.rejectWith('Test');
|
githubApi.post.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync(prs.addComment(42, 'body')).toBeRejectedWith('Test');
|
prs.addComment(42, 'body').catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data from the Github POST', async () => {
|
it('should resolve with the data from the Github POST', done => {
|
||||||
githubApi.post.and.resolveTo('Test');
|
githubApi.post.and.callFake(() => Promise.resolve('Test'));
|
||||||
await expectAsync(prs.addComment(42, 'body')).toBeResolvedTo('Test');
|
prs.addComment(42, 'body').then(data => {
|
||||||
|
expect(data).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -81,11 +87,13 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data returned from GitHub', async () => {
|
it('should resolve with the data returned from GitHub', done => {
|
||||||
const expected: any = {number: 42};
|
const expected: any = {number: 42};
|
||||||
githubApi.get.and.resolveTo(expected);
|
githubApi.get.and.callFake(() => Promise.resolve(expected));
|
||||||
|
prs.fetch(42).then(data => {
|
||||||
await expectAsync(prs.fetch(42)).toBeResolvedTo(expected);
|
expect(data).toEqual(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -117,14 +125,9 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should forward the value returned by \'getPaginated()\'', async () => {
|
it('should forward the value returned by \'getPaginated()\'', () => {
|
||||||
const mockPrs: PullRequest[] = [
|
githubApi.getPaginated.and.returnValue('Test');
|
||||||
{number: 1, user: {login: 'foo'}, labels: []},
|
expect(prs.fetchAll() as any).toBe('Test');
|
||||||
{number: 2, user: {login: 'bar'}, labels: []},
|
|
||||||
];
|
|
||||||
|
|
||||||
githubApi.getPaginated.and.resolveTo(mockPrs);
|
|
||||||
expect(await prs.fetchAll()).toBe(mockPrs);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -144,11 +147,13 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data returned from GitHub', async () => {
|
it('should resolve with the data returned from GitHub', done => {
|
||||||
const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}];
|
const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}];
|
||||||
githubApi.getPaginated.and.resolveTo(expected);
|
githubApi.getPaginated.and.callFake(() => Promise.resolve(expected));
|
||||||
|
prs.fetchFiles(42).then(data => {
|
||||||
await expectAsync(prs.fetchFiles(42)).toBeResolvedTo(expected);
|
expect(data).toEqual(expected);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubTeams, Team} from '../../lib/common/github-teams';
|
import {GithubTeams} from '../../lib/common/github-teams';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('GithubTeams', () => {
|
describe('GithubTeams', () => {
|
||||||
@ -16,7 +16,6 @@ describe('GithubTeams', () => {
|
|||||||
expect(() => new GithubTeams(githubApi, '')).
|
expect(() => new GithubTeams(githubApi, '')).
|
||||||
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
toThrowError('Missing or empty required parameter \'githubOrg\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -34,14 +33,9 @@ describe('GithubTeams', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should forward the value returned by \'getPaginated()\'', async () => {
|
it('should forward the value returned by \'getPaginated()\'', () => {
|
||||||
const mockTeams: Team[] = [
|
githubApi.getPaginated.and.returnValue('Test');
|
||||||
{id: 1, slug: 'foo'},
|
expect(teams.fetchAll() as any).toBe('Test');
|
||||||
{id: 2, slug: 'bar'},
|
|
||||||
];
|
|
||||||
|
|
||||||
githubApi.getPaginated.and.resolveTo(mockTeams);
|
|
||||||
expect(await teams.fetchAll()).toBe(mockTeams);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -56,77 +50,100 @@ describe('GithubTeams', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
githubApi.get.and.resolveTo();
|
githubApi.get.and.callFake(() => Promise.resolve());
|
||||||
const promise = teams.isMemberById('user', [1]);
|
const promise = teams.isMemberById('user', [1]);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if called with an empty array', async () => {
|
it('should resolve with false if called with an empty array', done => {
|
||||||
await expectAsync(teams.isMemberById('user', [])).toBeResolvedTo(false);
|
teams.isMemberById('user', []).then(isMember => {
|
||||||
|
expect(isMember).toBe(false);
|
||||||
expect(githubApi.get).not.toHaveBeenCalled();
|
expect(githubApi.get).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'get()\' with the correct pathname', async () => {
|
it('should call \'get()\' with the correct pathname', done => {
|
||||||
githubApi.get.and.resolveTo();
|
githubApi.get.and.callFake(() => Promise.resolve());
|
||||||
await teams.isMemberById('user', [1]);
|
teams.isMemberById('user', [1]).then(() => {
|
||||||
|
|
||||||
expect(githubApi.get).toHaveBeenCalledWith('/teams/1/memberships/user');
|
expect(githubApi.get).toHaveBeenCalledWith('/teams/1/memberships/user');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if \'get()\' rejects', async () => {
|
it('should resolve with false if \'get()\' rejects', done => {
|
||||||
githubApi.get.and.rejectWith(null);
|
githubApi.get.and.callFake(() => Promise.reject(null));
|
||||||
|
teams.isMemberById('user', [1]).then(isMember => {
|
||||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(false);
|
expect(isMember).toBe(false);
|
||||||
expect(githubApi.get).toHaveBeenCalled();
|
expect(githubApi.get).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if the membership is not active', async () => {
|
it('should resolve with false if the membership is not active', done => {
|
||||||
githubApi.get.and.resolveTo({state: 'pending'});
|
githubApi.get.and.callFake(() => Promise.resolve({state: 'pending'}));
|
||||||
|
teams.isMemberById('user', [1]).then(isMember => {
|
||||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(false);
|
expect(isMember).toBe(false);
|
||||||
expect(githubApi.get).toHaveBeenCalled();
|
expect(githubApi.get).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with true if the membership is active', async () => {
|
it('should resolve with true if the membership is active', done => {
|
||||||
githubApi.get.and.resolveTo({state: 'active'});
|
githubApi.get.and.callFake(() => Promise.resolve({state: 'active'}));
|
||||||
await expectAsync(teams.isMemberById('user', [1])).toBeResolvedTo(true);
|
teams.isMemberById('user', [1]).then(isMember => {
|
||||||
|
expect(isMember).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should sequentially call \'get()\' until an active membership is found', async () => {
|
it('should sequentially call \'get()\' until an active membership is found', done => {
|
||||||
githubApi.get.
|
const trainedResponses: {[pathname: string]: Promise<{state: string}>} = {
|
||||||
withArgs('/teams/1/memberships/user').and.resolveTo({state: 'pending'}).
|
'/teams/1/memberships/user': Promise.resolve({state: 'pending'}),
|
||||||
withArgs('/teams/2/memberships/user').and.rejectWith(null).
|
'/teams/2/memberships/user': Promise.reject(null),
|
||||||
withArgs('/teams/3/memberships/user').and.resolveTo({state: 'active'});
|
'/teams/3/memberships/user': Promise.resolve({state: 'active'}),
|
||||||
|
};
|
||||||
|
githubApi.get.and.callFake((pathname: string) => trainedResponses[pathname]);
|
||||||
|
|
||||||
await expectAsync(teams.isMemberById('user', [1, 2, 3, 4])).toBeResolvedTo(true);
|
teams.isMemberById('user', [1, 2, 3, 4]).then(isMember => {
|
||||||
|
expect(isMember).toBe(true);
|
||||||
|
|
||||||
expect(githubApi.get).toHaveBeenCalledTimes(3);
|
expect(githubApi.get).toHaveBeenCalledTimes(3);
|
||||||
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if no active membership is found', async () => {
|
it('should resolve with false if no active membership is found', done => {
|
||||||
githubApi.get.
|
const trainedResponses: {[pathname: string]: Promise<{state: string}>} = {
|
||||||
withArgs('/teams/1/memberships/user').and.resolveTo({state: 'pending'}).
|
'/teams/1/memberships/user': Promise.resolve({state: 'pending'}),
|
||||||
withArgs('/teams/2/memberships/user').and.rejectWith(null).
|
'/teams/2/memberships/user': Promise.reject(null),
|
||||||
withArgs('/teams/3/memberships/user').and.resolveTo({state: 'not active'}).
|
'/teams/3/memberships/user': Promise.resolve({state: 'not active'}),
|
||||||
withArgs('/teams/4/memberships/user').and.rejectWith(null);
|
'/teams/4/memberships/user': Promise.reject(null),
|
||||||
|
};
|
||||||
|
githubApi.get.and.callFake((pathname: string) => trainedResponses[pathname]);
|
||||||
|
|
||||||
await expectAsync(teams.isMemberById('user', [1, 2, 3, 4])).toBeResolvedTo(false);
|
teams.isMemberById('user', [1, 2, 3, 4]).then(isMember => {
|
||||||
|
expect(isMember).toBe(false);
|
||||||
|
|
||||||
expect(githubApi.get).toHaveBeenCalledTimes(4);
|
expect(githubApi.get).toHaveBeenCalledTimes(4);
|
||||||
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
expect(githubApi.get.calls.argsFor(0)[0]).toBe('/teams/1/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
expect(githubApi.get.calls.argsFor(1)[0]).toBe('/teams/2/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
expect(githubApi.get.calls.argsFor(2)[0]).toBe('/teams/3/memberships/user');
|
||||||
expect(githubApi.get.calls.argsFor(3)[0]).toBe('/teams/4/memberships/user');
|
expect(githubApi.get.calls.argsFor(3)[0]).toBe('/teams/4/memberships/user');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -140,13 +157,14 @@ describe('GithubTeams', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
teams = new GithubTeams(githubApi, 'foo');
|
teams = new GithubTeams(githubApi, 'foo');
|
||||||
|
|
||||||
teamsFetchAllSpy = spyOn(teams, 'fetchAll').and.resolveTo([{id: 1, slug: 'team1'}, {id: 2, slug: 'team2'}]);
|
const mockResponse = Promise.resolve([{id: 1, slug: 'team1'}, {id: 2, slug: 'team2'}]);
|
||||||
|
teamsFetchAllSpy = spyOn(teams, 'fetchAll').and.returnValue(mockResponse);
|
||||||
teamsIsMemberByIdSpy = spyOn(teams, 'isMemberById');
|
teamsIsMemberByIdSpy = spyOn(teams, 'isMemberById');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect(teams.isMemberBySlug('user', ['team-slug'])).toBeInstanceOf(Promise);
|
expect(teams.isMemberBySlug('user', ['team-slug'])).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -156,46 +174,55 @@ describe('GithubTeams', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if \'fetchAll()\' rejects', async () => {
|
it('should resolve with false if \'fetchAll()\' rejects', done => {
|
||||||
teamsFetchAllSpy.and.rejectWith(null);
|
teamsFetchAllSpy.and.callFake(() => Promise.reject(null));
|
||||||
await expectAsync(teams.isMemberBySlug('user', ['team-slug'])).toBeResolvedTo(false);
|
teams.isMemberBySlug('user', ['team-slug']).then(isMember => {
|
||||||
|
expect(isMember).toBe(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'isMemberById()\' with the correct params if no team is found', async () => {
|
it('should call \'isMemberById()\' with the correct params if no team is found', done => {
|
||||||
await teams.isMemberBySlug('user', ['no-match']);
|
teams.isMemberBySlug('user', ['no-match']).then(() => {
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', []);
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', []);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'isMemberById()\' with the correct params if teams are found', async () => {
|
it('should call \'isMemberById()\' with the correct params if teams are found', done => {
|
||||||
await teams.isMemberBySlug('userA', ['team1']);
|
const spy = teamsIsMemberByIdSpy;
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userA', [1]);
|
|
||||||
|
|
||||||
await teams.isMemberBySlug('userB', ['team2']);
|
Promise.all([
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userB', [2]);
|
teams.isMemberBySlug('user', ['team1']).then(() => expect(spy).toHaveBeenCalledWith('user', [1])),
|
||||||
|
teams.isMemberBySlug('user', ['team2']).then(() => expect(spy).toHaveBeenCalledWith('user', [2])),
|
||||||
await teams.isMemberBySlug('userC', ['team1', 'team2']);
|
teams.isMemberBySlug('user', ['team1', 'team2']).then(() => expect(spy).toHaveBeenCalledWith('user', [1, 2])),
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userC', [1, 2]);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with false if \'isMemberById()\' rejects', async () => {
|
it('should resolve with false if \'isMemberById()\' rejects', done => {
|
||||||
teamsIsMemberByIdSpy.and.rejectWith(null);
|
teamsIsMemberByIdSpy.and.callFake(() => Promise.reject(null));
|
||||||
|
teams.isMemberBySlug('user', ['team1']).then(isMember => {
|
||||||
await expectAsync(teams.isMemberBySlug('user', ['team1'])).toBeResolvedTo(false);
|
expect(isMember).toBe(false);
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalled();
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the value \'isMemberById()\' resolves with', async () => {
|
it('should resolve with the value \'isMemberById()\' resolves with', async () => {
|
||||||
teamsIsMemberByIdSpy.and.resolveTo(true);
|
|
||||||
await expectAsync(teams.isMemberBySlug('userA', ['team1'])).toBeResolvedTo(true);
|
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userA', [1]);
|
|
||||||
|
|
||||||
teamsIsMemberByIdSpy.and.resolveTo(false);
|
teamsIsMemberByIdSpy.and.callFake(() => Promise.resolve(true));
|
||||||
await expectAsync(teams.isMemberBySlug('userB', ['team1'])).toBeResolvedTo(false);
|
const isMember1 = await teams.isMemberBySlug('user', ['team1']);
|
||||||
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('userB', [1]);
|
expect(isMember1).toBe(true);
|
||||||
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', [1]);
|
||||||
|
|
||||||
|
teamsIsMemberByIdSpy.and.callFake(() => Promise.resolve(false));
|
||||||
|
const isMember2 = await teams.isMemberBySlug('user', ['team1']);
|
||||||
|
expect(isMember2).toBe(false);
|
||||||
|
expect(teamsIsMemberByIdSpy).toHaveBeenCalledWith('user', [1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -9,8 +9,7 @@ import {Logger} from '../../lib/common/utils';
|
|||||||
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
import {customAsyncMatchers} from './jasmine-custom-async-matchers';
|
import {expectToBePreviewServerError} from './helpers';
|
||||||
|
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildCreator', () => {
|
describe('BuildCreator', () => {
|
||||||
@ -25,7 +24,6 @@ describe('BuildCreator', () => {
|
|||||||
const publicShaDir = path.join(publicPrDir, shortSha);
|
const publicShaDir = path.join(publicPrDir, shortSha);
|
||||||
let bc: BuildCreator;
|
let bc: BuildCreator;
|
||||||
|
|
||||||
beforeEach(() => jasmine.addAsyncMatchers(customAsyncMatchers));
|
|
||||||
beforeEach(() => bc = new BuildCreator(buildsDir));
|
beforeEach(() => bc = new BuildCreator(buildsDir));
|
||||||
|
|
||||||
|
|
||||||
@ -37,8 +35,8 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should extend EventEmitter', () => {
|
it('should extend EventEmitter', () => {
|
||||||
expect(bc).toBeInstanceOf(BuildCreator);
|
expect(bc).toEqual(jasmine.any(BuildCreator));
|
||||||
expect(bc).toBeInstanceOf(EventEmitter);
|
expect(bc).toEqual(jasmine.any(EventEmitter));
|
||||||
|
|
||||||
expect(Object.getPrototypeOf(bc)).toBe(BuildCreator.prototype);
|
expect(Object.getPrototypeOf(bc)).toBe(BuildCreator.prototype);
|
||||||
});
|
});
|
||||||
@ -69,43 +67,47 @@ describe('BuildCreator', () => {
|
|||||||
const shaDir = isPublic ? publicShaDir : hiddenShaDir;
|
const shaDir = isPublic ? publicShaDir : hiddenShaDir;
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = bc.create(pr, sha, archive, isPublic);
|
const promise = bc.create(pr, sha, archive, isPublic);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `extractArchive()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
// `extractArchive()`.
|
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should update the PR\'s visibility first if necessary', async () => {
|
it('should update the PR\'s visibility first if necessary', done => {
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bcUpdatePrVisibilitySpy.and.callFake(() => expect(shellMkdirSpy).not.toHaveBeenCalled());
|
||||||
|
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledBefore(shellMkdirSpy);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
|
then(() => {
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(pr, isPublic);
|
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(pr, isPublic);
|
||||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should create the build directory (and any parent directories)', async () => {
|
it('should create the build directory (and any parent directories)', done => {
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(shellMkdirSpy).toHaveBeenCalledWith('-p', shaDir);
|
then(() => expect(shellMkdirSpy).toHaveBeenCalledWith('-p', shaDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should extract the archive contents into the build directory', async () => {
|
it('should extract the archive contents into the build directory', done => {
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(bcExtractArchiveSpy).toHaveBeenCalledWith(archive, shaDir);
|
then(() => expect(bcExtractArchiveSpy).toHaveBeenCalledWith(archive, shaDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should emit a CreatedBuildEvent on success', async () => {
|
it('should emit a CreatedBuildEvent on success', done => {
|
||||||
let emitted = false;
|
let emitted = false;
|
||||||
|
|
||||||
bcEmitSpy.and.callFake((type: string, evt: CreatedBuildEvent) => {
|
bcEmitSpy.and.callFake((type: string, evt: CreatedBuildEvent) => {
|
||||||
expect(type).toBe(CreatedBuildEvent.type);
|
expect(type).toBe(CreatedBuildEvent.type);
|
||||||
expect(evt).toBeInstanceOf(CreatedBuildEvent);
|
expect(evt).toEqual(jasmine.any(CreatedBuildEvent));
|
||||||
expect(evt.pr).toBe(+pr);
|
expect(evt.pr).toBe(+pr);
|
||||||
expect(evt.sha).toBe(shortSha);
|
expect(evt.sha).toBe(shortSha);
|
||||||
expect(evt.isPublic).toBe(isPublic);
|
expect(evt.isPublic).toBe(isPublic);
|
||||||
@ -113,108 +115,130 @@ describe('BuildCreator', () => {
|
|||||||
emitted = true;
|
emitted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await bc.create(pr, sha, archive, isPublic);
|
bc.create(pr, sha, archive, isPublic).
|
||||||
expect(emitted).toBe(true);
|
then(() => expect(emitted).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('on error', () => {
|
describe('on error', () => {
|
||||||
|
let existsValues: {[dir: string]: boolean};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
bcExistsSpy.and.returnValue(false);
|
existsValues = {
|
||||||
|
[prDir]: false,
|
||||||
|
[shaDir]: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
bcExistsSpy.and.callFake((dir: string) => existsValues[dir]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if changing the PR\'s visibility fails', async () => {
|
it('should abort and skip further operations if changing the PR\'s visibility fails', done => {
|
||||||
const mockError = new PreviewServerError(543, 'Test');
|
const mockError = new PreviewServerError(543, 'Test');
|
||||||
bcUpdatePrVisibilitySpy.and.rejectWith(mockError);
|
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject(mockError));
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWith(mockError);
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
|
expect(err).toBe(mockError);
|
||||||
|
|
||||||
expect(bcExistsSpy).not.toHaveBeenCalled();
|
expect(bcExistsSpy).not.toHaveBeenCalled();
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if the build does already exist', async () => {
|
it('should abort and skip further operations if the build does already exist', done => {
|
||||||
bcExistsSpy.withArgs(shaDir).and.returnValue(true);
|
existsValues[shaDir] = true;
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
409, `Request to overwrite existing ${isPublic ? '' : 'non-'}public directory: ${shaDir}`);
|
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
|
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should detect existing build directory after visibility change', async () => {
|
it('should detect existing build directory after visibility change', done => {
|
||||||
bcUpdatePrVisibilitySpy.and.callFake(() => bcExistsSpy.and.returnValue(true));
|
bcUpdatePrVisibilitySpy.and.callFake(() => existsValues[prDir] = existsValues[shaDir] = true);
|
||||||
|
|
||||||
expect(bcExistsSpy(prDir)).toBe(false);
|
expect(bcExistsSpy(prDir)).toBe(false);
|
||||||
expect(bcExistsSpy(shaDir)).toBe(false);
|
expect(bcExistsSpy(shaDir)).toBe(false);
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
409, `Request to overwrite existing ${isPublic ? '' : 'non-'}public directory: ${shaDir}`);
|
const publicOrNot = isPublic ? 'public' : 'non-public';
|
||||||
|
expectToBePreviewServerError(err, 409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
|
||||||
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
expect(shellMkdirSpy).not.toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to create the directories', async () => {
|
it('should abort and skip further operations if it fails to create the directories', done => {
|
||||||
shellMkdirSpy.and.throwError('');
|
shellMkdirSpy.and.throwError('');
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
|
||||||
|
|
||||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to extract the archive', async () => {
|
it('should abort and skip further operations if it fails to extract the archive', done => {
|
||||||
bcExtractArchiveSpy.and.throwError('');
|
bcExtractArchiveSpy.and.throwError('');
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
|
||||||
|
|
||||||
expect(shellMkdirSpy).toHaveBeenCalled();
|
expect(shellMkdirSpy).toHaveBeenCalled();
|
||||||
expect(bcExtractArchiveSpy).toHaveBeenCalled();
|
expect(bcExtractArchiveSpy).toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete the PR directory (for new PR)', async () => {
|
it('should delete the PR directory (for new PR)', done => {
|
||||||
bcExtractArchiveSpy.and.throwError('');
|
bcExtractArchiveSpy.and.throwError('');
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith('-rf', prDir);
|
expect(shellRmSpy).toHaveBeenCalledWith('-rf', prDir);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete the SHA directory (for existing PR)', async () => {
|
it('should delete the SHA directory (for existing PR)', done => {
|
||||||
bcExistsSpy.withArgs(prDir).and.returnValue(true);
|
existsValues[prDir] = true;
|
||||||
bcExtractArchiveSpy.and.throwError('');
|
bcExtractArchiveSpy.and.throwError('');
|
||||||
|
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejected();
|
bc.create(pr, sha, archive, isPublic).catch(() => {
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith('-rf', shaDir);
|
expect(shellRmSpy).toHaveBeenCalledWith('-rf', shaDir);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject with an PreviewServerError', async () => {
|
it('should reject with an PreviewServerError', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
// tslint:disable-next-line: no-string-throw
|
||||||
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
|
||||||
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(
|
expectToBePreviewServerError(err, 500, `Error while creating preview at: ${shaDir}\nTest`);
|
||||||
500, `Error while creating preview at: ${shaDir}\nTest`);
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass PreviewServerError instances unmodified', async () => {
|
it('should pass PreviewServerError instances unmodified', done => {
|
||||||
shellMkdirSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
shellMkdirSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||||
await expectAsync(bc.create(pr, sha, archive, isPublic)).toBeRejectedWithPreviewServerError(543, 'Test');
|
bc.create(pr, sha, archive, isPublic).catch(err => {
|
||||||
|
expectToBePreviewServerError(err, 543, 'Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -241,12 +265,12 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = bc.updatePrVisibility(pr, true);
|
const promise = bc.updatePrVisibility(pr, true);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `extractArchive()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual `extractArchive()`.
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -255,53 +279,58 @@ describe('BuildCreator', () => {
|
|||||||
const newPrDir = makePublic ? publicPrDir : hiddenPrDir;
|
const newPrDir = makePublic ? publicPrDir : hiddenPrDir;
|
||||||
|
|
||||||
|
|
||||||
it('should rename the directory', async () => {
|
it('should rename the directory', done => {
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir);
|
then(() => expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('when the visibility is updated', () => {
|
describe('when the visibility is updated', () => {
|
||||||
|
|
||||||
it('should resolve to true', async () => {
|
it('should resolve to true', done => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(true);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
|
then(result => expect(result).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should rename the directory', async () => {
|
it('should rename the directory', done => {
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir);
|
then(() => expect(shellMvSpy).toHaveBeenCalledWith(oldPrDir, newPrDir)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should emit a ChangedPrVisibilityEvent on success', async () => {
|
it('should emit a ChangedPrVisibilityEvent on success', done => {
|
||||||
let emitted = false;
|
let emitted = false;
|
||||||
|
|
||||||
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
||||||
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
||||||
expect(evt).toBeInstanceOf(ChangedPrVisibilityEvent);
|
expect(evt).toEqual(jasmine.any(ChangedPrVisibilityEvent));
|
||||||
expect(evt.pr).toBe(+pr);
|
expect(evt.pr).toBe(+pr);
|
||||||
expect(evt.shas).toBeInstanceOf(Array);
|
expect(evt.shas).toEqual(jasmine.any(Array));
|
||||||
expect(evt.isPublic).toBe(makePublic);
|
expect(evt.isPublic).toBe(makePublic);
|
||||||
|
|
||||||
emitted = true;
|
emitted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(emitted).toBe(true);
|
then(() => expect(emitted).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should include all shas in the emitted event', async () => {
|
it('should include all shas in the emitted event', done => {
|
||||||
const shas = ['foo', 'bar', 'baz'];
|
const shas = ['foo', 'bar', 'baz'];
|
||||||
let emitted = false;
|
let emitted = false;
|
||||||
|
|
||||||
bcListShasByDate.and.resolveTo(shas);
|
bcListShasByDate.and.callFake(() => Promise.resolve(shas));
|
||||||
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
bcEmitSpy.and.callFake((type: string, evt: ChangedPrVisibilityEvent) => {
|
||||||
expect(bcListShasByDate).toHaveBeenCalledWith(newPrDir);
|
expect(bcListShasByDate).toHaveBeenCalledWith(newPrDir);
|
||||||
|
|
||||||
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
expect(type).toBe(ChangedPrVisibilityEvent.type);
|
||||||
expect(evt).toBeInstanceOf(ChangedPrVisibilityEvent);
|
expect(evt).toEqual(jasmine.any(ChangedPrVisibilityEvent));
|
||||||
expect(evt.pr).toBe(+pr);
|
expect(evt.pr).toBe(+pr);
|
||||||
expect(evt.shas).toBe(shas);
|
expect(evt.shas).toBe(shas);
|
||||||
expect(evt.isPublic).toBe(makePublic);
|
expect(evt.isPublic).toBe(makePublic);
|
||||||
@ -309,82 +338,94 @@ describe('BuildCreator', () => {
|
|||||||
emitted = true;
|
emitted = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
await bc.updatePrVisibility(pr, makePublic);
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
expect(emitted).toBe(true);
|
then(() => expect(emitted).toBe(true)).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should do nothing if the visibility is already up-to-date', async () => {
|
it('should do nothing if the visibility is already up-to-date', done => {
|
||||||
bcExistsSpy.and.callFake((dir: string) => dir === newPrDir);
|
bcExistsSpy.and.callFake((dir: string) => dir === newPrDir);
|
||||||
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(false);
|
then(result => {
|
||||||
|
expect(result).toBe(false);
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should do nothing if the PR directory does not exist', async () => {
|
it('should do nothing if the PR directory does not exist', done => {
|
||||||
bcExistsSpy.and.returnValue(false);
|
bcExistsSpy.and.returnValue(false);
|
||||||
|
bc.updatePrVisibility(pr, makePublic).
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeResolvedTo(false);
|
then(result => {
|
||||||
|
expect(result).toBe(false);
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('on error', () => {
|
describe('on error', () => {
|
||||||
|
|
||||||
it('should abort and skip further operations if both directories exist', async () => {
|
it('should abort and skip further operations if both directories exist', done => {
|
||||||
bcExistsSpy.and.returnValue(true);
|
bcExistsSpy.and.returnValue(true);
|
||||||
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(
|
expectToBePreviewServerError(err, 409,
|
||||||
409, `Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
`Request to move '${oldPrDir}' to existing directory '${newPrDir}'.`);
|
||||||
|
|
||||||
expect(shellMvSpy).not.toHaveBeenCalled();
|
expect(shellMvSpy).not.toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to rename the directory', async () => {
|
it('should abort and skip further operations if it fails to rename the directory', done => {
|
||||||
shellMvSpy.and.throwError('');
|
shellMvSpy.and.throwError('');
|
||||||
|
bc.updatePrVisibility(pr, makePublic).catch(() => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejected();
|
|
||||||
|
|
||||||
expect(shellMvSpy).toHaveBeenCalled();
|
expect(shellMvSpy).toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).not.toHaveBeenCalled();
|
expect(bcListShasByDate).not.toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to list the SHAs', async () => {
|
it('should abort and skip further operations if it fails to list the SHAs', done => {
|
||||||
bcListShasByDate.and.throwError('');
|
bcListShasByDate.and.throwError('');
|
||||||
|
bc.updatePrVisibility(pr, makePublic).catch(() => {
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejected();
|
|
||||||
|
|
||||||
expect(shellMvSpy).toHaveBeenCalled();
|
expect(shellMvSpy).toHaveBeenCalled();
|
||||||
expect(bcListShasByDate).toHaveBeenCalled();
|
expect(bcListShasByDate).toHaveBeenCalled();
|
||||||
expect(bcEmitSpy).not.toHaveBeenCalled();
|
expect(bcEmitSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject with an PreviewServerError', async () => {
|
it('should reject with an PreviewServerError', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
// tslint:disable-next-line: no-string-throw
|
||||||
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
shellMvSpy.and.callFake(() => { throw 'Test'; });
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
expectToBePreviewServerError(err, 500,
|
||||||
|
`Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass PreviewServerError instances unmodified', async () => {
|
it('should pass PreviewServerError instances unmodified', done => {
|
||||||
shellMvSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
shellMvSpy.and.callFake(() => { throw new PreviewServerError(543, 'Test'); });
|
||||||
await expectAsync(bc.updatePrVisibility(pr, makePublic)).toBeRejectedWithPreviewServerError(543, 'Test');
|
bc.updatePrVisibility(pr, makePublic).catch(err => {
|
||||||
|
expectToBePreviewServerError(err, 543, 'Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -402,14 +443,12 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fsAccessCbs = [];
|
fsAccessCbs = [];
|
||||||
fsAccessSpy = spyOn(fs, 'access').and.callFake(
|
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb));
|
||||||
((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb)) as unknown as typeof fs.access,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect((bc as any).exists('foo')).toBeInstanceOf(Promise);
|
expect((bc as any).exists('foo')).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -419,29 +458,25 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with \'true\' if \'fs.access()\' succeeds', async () => {
|
it('should resolve with \'true\' if \'fs.access()\' succeeds', done => {
|
||||||
const existsPromises = [
|
Promise.
|
||||||
(bc as any).exists('foo'),
|
all([(bc as any).exists('foo'), (bc as any).exists('bar')]).
|
||||||
(bc as any).exists('bar'),
|
then(results => expect(results).toEqual([true, true])).
|
||||||
];
|
then(done);
|
||||||
|
|
||||||
fsAccessCbs[0]();
|
fsAccessCbs[0]();
|
||||||
fsAccessCbs[1](null);
|
fsAccessCbs[1](null);
|
||||||
|
|
||||||
await expectAsync(Promise.all(existsPromises)).toBeResolvedTo([true, true]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with \'false\' if \'fs.access()\' errors', async () => {
|
it('should resolve with \'false\' if \'fs.access()\' errors', done => {
|
||||||
const existsPromises = [
|
Promise.
|
||||||
(bc as any).exists('foo'),
|
all([(bc as any).exists('foo'), (bc as any).exists('bar')]).
|
||||||
(bc as any).exists('bar'),
|
then(results => expect(results).toEqual([false, false])).
|
||||||
];
|
then(done);
|
||||||
|
|
||||||
fsAccessCbs[0]('Error');
|
fsAccessCbs[0]('Error');
|
||||||
fsAccessCbs[1](new Error());
|
fsAccessCbs[1](new Error());
|
||||||
|
|
||||||
await expectAsync(Promise.all(existsPromises)).toBeResolvedTo([false, false]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -460,15 +495,12 @@ describe('BuildCreator', () => {
|
|||||||
consoleWarnSpy = spyOn(Logger.prototype, 'warn');
|
consoleWarnSpy = spyOn(Logger.prototype, 'warn');
|
||||||
shellChmodSpy = spyOn(shell, 'chmod');
|
shellChmodSpy = spyOn(shell, 'chmod');
|
||||||
shellRmSpy = spyOn(shell, 'rm');
|
shellRmSpy = spyOn(shell, 'rm');
|
||||||
cpExecSpy = spyOn(cp, 'exec').and.callFake(
|
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
|
||||||
((_: string, cb: (...args: any[]) => void) =>
|
|
||||||
cpExecCbs.push(cb)) as unknown as typeof cp.exec,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', () => {
|
it('should return a promise', () => {
|
||||||
expect((bc as any).extractArchive('foo', 'bar')).toBeInstanceOf(Promise);
|
expect((bc as any).extractArchive('foo', 'bar')).toEqual(jasmine.any(Promise));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -480,68 +512,78 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should log (as a warning) any stderr output if extracting succeeded', async () => {
|
it('should log (as a warning) any stderr output if extracting succeeded', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
(bc as any).extractArchive('foo', 'bar').
|
||||||
|
then(() => expect(consoleWarnSpy).toHaveBeenCalledWith('This is stderr')).
|
||||||
|
then(done);
|
||||||
|
|
||||||
cpExecCbs[0](null, 'This is stdout', 'This is stderr');
|
cpExecCbs[0](null, 'This is stdout', 'This is stderr');
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeResolved();
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledWith('This is stderr');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should make the build directory non-writable', async () => {
|
it('should make the build directory non-writable', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
(bc as any).extractArchive('foo', 'bar').
|
||||||
cpExecCbs[0]();
|
then(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a-w', 'bar')).
|
||||||
|
then(done);
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeResolved();
|
cpExecCbs[0]();
|
||||||
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a-w', 'bar');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should delete the build artifact file on success', async () => {
|
it('should delete the build artifact file on success', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('input/file', 'output/dir');
|
(bc as any).extractArchive('input/file', 'output/dir').
|
||||||
cpExecCbs[0]();
|
then(() => expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file')).
|
||||||
|
then(done);
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeResolved();
|
cpExecCbs[0]();
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith('-f', 'input/file');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('on error', () => {
|
describe('on error', () => {
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to extract the archive', async () => {
|
it('should abort and skip further operations if it fails to extract the archive', done => {
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
cpExecCbs[0]('Test');
|
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
|
||||||
expect(shellChmodSpy).not.toHaveBeenCalled();
|
expect(shellChmodSpy).not.toHaveBeenCalled();
|
||||||
expect(shellRmSpy).not.toHaveBeenCalled();
|
expect(shellRmSpy).not.toHaveBeenCalled();
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
cpExecCbs[0]('Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and skip further operations if it fails to make non-writable', async () => {
|
it('should abort and skip further operations if it fails to make non-writable', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
shellChmodSpy.and.callFake(() => { throw 'Test'; });
|
|
||||||
|
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
|
||||||
cpExecCbs[0]();
|
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalled();
|
expect(shellChmodSpy).toHaveBeenCalled();
|
||||||
expect(shellRmSpy).not.toHaveBeenCalled();
|
expect(shellRmSpy).not.toHaveBeenCalled();
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
shellChmodSpy.and.callFake(() => {
|
||||||
|
// tslint:disable-next-line: no-string-throw
|
||||||
|
throw 'Test';
|
||||||
|
});
|
||||||
|
|
||||||
|
cpExecCbs[0]();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should abort and reject if it fails to remove the build artifact file', async () => {
|
it('should abort and reject if it fails to remove the build artifact file', done => {
|
||||||
// tslint:disable-next-line: no-string-throw
|
(bc as any).extractArchive('foo', 'bar').catch((err: any) => {
|
||||||
shellRmSpy.and.callFake(() => { throw 'Test'; });
|
|
||||||
|
|
||||||
const extractPromise = (bc as any).extractArchive('foo', 'bar');
|
|
||||||
cpExecCbs[0]();
|
|
||||||
|
|
||||||
await expectAsync(extractPromise).toBeRejectedWith('Test');
|
|
||||||
expect(shellChmodSpy).toHaveBeenCalled();
|
expect(shellChmodSpy).toHaveBeenCalled();
|
||||||
expect(shellRmSpy).toHaveBeenCalled();
|
expect(shellRmSpy).toHaveBeenCalled();
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
shellRmSpy.and.callFake(() => {
|
||||||
|
// tslint:disable-next-line: no-string-throw
|
||||||
|
throw 'Test';
|
||||||
|
});
|
||||||
|
|
||||||
|
cpExecCbs[0]();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -558,54 +600,62 @@ describe('BuildCreator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
shellLsSpy = spyOn(shell, 'ls').and.returnValue([] as unknown as shell.ShellArray);
|
shellLsSpy = spyOn(shell, 'ls').and.returnValue([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = (bc as any).listShasByDate('input/dir');
|
const promise = (bc as any).listShasByDate('input/dir');
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `ls()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual `ls()`.
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should `ls()` files with their metadata', async () => {
|
it('should `ls()` files with their metadata', done => {
|
||||||
await (bc as any).listShasByDate('input/dir');
|
(bc as any).listShasByDate('input/dir').
|
||||||
expect(shellLsSpy).toHaveBeenCalledWith('-l', 'input/dir');
|
then(() => expect(shellLsSpy).toHaveBeenCalledWith('-l', 'input/dir')).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if listing files fails', async () => {
|
it('should reject if listing files fails', done => {
|
||||||
shellLsSpy.and.rejectWith('Test');
|
shellLsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeRejectedWith('Test');
|
(bc as any).listShasByDate('input/dir').catch((err: string) => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return the filenames', async () => {
|
it('should return the filenames', done => {
|
||||||
shellLsSpy.and.resolveTo([
|
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||||
lsResult('foo', 100),
|
lsResult('foo', 100),
|
||||||
lsResult('bar', 200),
|
lsResult('bar', 200),
|
||||||
lsResult('baz', 300),
|
lsResult('baz', 300),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['foo', 'bar', 'baz']);
|
(bc as any).listShasByDate('input/dir').
|
||||||
|
then((shas: string[]) => expect(shas).toEqual(['foo', 'bar', 'baz'])).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should sort by date', async () => {
|
it('should sort by date', done => {
|
||||||
shellLsSpy.and.resolveTo([
|
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||||
lsResult('foo', 300),
|
lsResult('foo', 300),
|
||||||
lsResult('bar', 100),
|
lsResult('bar', 100),
|
||||||
lsResult('baz', 200),
|
lsResult('baz', 200),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['bar', 'baz', 'foo']);
|
(bc as any).listShasByDate('input/dir').
|
||||||
|
then((shas: string[]) => expect(shas).toEqual(['bar', 'baz', 'foo'])).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not break with ShellJS\' custom `sort()` method', async () => {
|
it('should not break with ShellJS\' custom `sort()` method', done => {
|
||||||
const mockArray = [
|
const mockArray = [
|
||||||
lsResult('foo', 300),
|
lsResult('foo', 300),
|
||||||
lsResult('bar', 100),
|
lsResult('bar', 100),
|
||||||
@ -613,21 +663,26 @@ describe('BuildCreator', () => {
|
|||||||
];
|
];
|
||||||
mockArray.sort = jasmine.createSpy('sort');
|
mockArray.sort = jasmine.createSpy('sort');
|
||||||
|
|
||||||
shellLsSpy.and.resolveTo(mockArray);
|
shellLsSpy.and.callFake(() => Promise.resolve(mockArray));
|
||||||
|
(bc as any).listShasByDate('input/dir').
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['bar', 'baz', 'foo']);
|
then((shas: string[]) => {
|
||||||
|
expect(shas).toEqual(['bar', 'baz', 'foo']);
|
||||||
expect(mockArray.sort).not.toHaveBeenCalled();
|
expect(mockArray.sort).not.toHaveBeenCalled();
|
||||||
|
}).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should only include directories', async () => {
|
it('should only include directories', done => {
|
||||||
shellLsSpy.and.resolveTo([
|
shellLsSpy.and.callFake(() => Promise.resolve([
|
||||||
lsResult('foo', 100),
|
lsResult('foo', 100),
|
||||||
lsResult('bar', 200, false),
|
lsResult('bar', 200, false),
|
||||||
lsResult('baz', 300),
|
lsResult('baz', 300),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
await expectAsync((bc as any).listShasByDate('input/dir')).toBeResolvedTo(['foo', 'baz']);
|
(bc as any).listShasByDate('input/dir').
|
||||||
|
then((shas: string[]) => expect(shas).toEqual(['foo', 'baz'])).
|
||||||
|
then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -32,18 +32,18 @@ describe('BuildRetriever', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
||||||
spyOn(api, 'getBuildInfo').and.resolveTo(BUILD_INFO);
|
spyOn(api, 'getBuildInfo').and.callFake(() => Promise.resolve(BUILD_INFO));
|
||||||
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl').and.resolveTo(BASE_URL + ARTIFACT_PATH);
|
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl')
|
||||||
|
.and.callFake(() => Promise.resolve(BASE_URL + ARTIFACT_PATH));
|
||||||
|
|
||||||
WRITEFILE_RESULT = undefined;
|
WRITEFILE_RESULT = undefined;
|
||||||
writeFileSpy = spyOn(fs, 'writeFile').and.callFake(
|
writeFileSpy = spyOn(fs, 'writeFile').and.callFake(
|
||||||
((_path: string, _buffer: Buffer, callback: fs.NoParamCallback) =>
|
(_path: string, _buffer: Buffer, callback: (err?: any) => {}) => callback(WRITEFILE_RESULT),
|
||||||
callback(WRITEFILE_RESULT)) as typeof fs.writeFile,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
EXISTS_RESULT = false;
|
EXISTS_RESULT = false;
|
||||||
existsSpy = spyOn(fs, 'exists').and.callFake(
|
existsSpy = spyOn(fs, 'exists').and.callFake(
|
||||||
((_path, callback) => callback(EXISTS_RESULT)) as typeof fs.exists,
|
(_path: string, callback: (exists: boolean) => {}) => callback(EXISTS_RESULT),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -56,7 +56,6 @@ describe('BuildRetriever', () => {
|
|||||||
expect(() => new BuildRetriever(api, -1, DOWNLOAD_DIR))
|
expect(() => new BuildRetriever(api, -1, DOWNLOAD_DIR))
|
||||||
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
.toThrowError(`Invalid parameter "downloadSizeLimit" should be a number greater than 0.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the "downloadDir" is missing', () => {
|
it('should fail if the "downloadDir" is missing', () => {
|
||||||
expect(() => new BuildRetriever(api, MAX_DOWNLOAD_SIZE, ''))
|
expect(() => new BuildRetriever(api, MAX_DOWNLOAD_SIZE, ''))
|
||||||
.toThrowError(`Missing or empty required parameter 'downloadDir'!`);
|
.toThrowError(`Missing or empty required parameter 'downloadDir'!`);
|
||||||
@ -73,10 +72,14 @@ describe('BuildRetriever', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should error if it is not possible to extract the PR number from the branch', async () => {
|
it('should error if it is not possible to extract the PR number from the branch', async () => {
|
||||||
BUILD_INFO.branch = 'master';
|
|
||||||
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
const retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
||||||
|
try {
|
||||||
await expectAsync(retriever.getGithubInfo(12345)).toBeRejectedWithError('No PR found in branch field: master');
|
BUILD_INFO.branch = 'master';
|
||||||
|
await retriever.getGithubInfo(12345);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('No PR found in branch field: master');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,10 +110,12 @@ describe('BuildRetriever', () => {
|
|||||||
it('should fail if the artifact is too large', async () => {
|
it('should fail if the artifact is too large', async () => {
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||||
retriever = new BuildRetriever(api, 10, DOWNLOAD_DIR);
|
retriever = new BuildRetriever(api, 10, DOWNLOAD_DIR);
|
||||||
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
toBeRejectedWith(jasmine.objectContaining({status: 413}));
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.status).toEqual(413);
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -139,39 +144,49 @@ describe('BuildRetriever', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the CircleCI API fails', async () => {
|
it('should fail if the CircleCI API fails', async () => {
|
||||||
getBuildArtifactUrlSpy.and.rejectWith('getBuildArtifactUrl failed');
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
getBuildArtifactUrlSpy.and.callFake(() => Promise.reject('getBuildArtifactUrl failed'));
|
||||||
toBeRejectedWithError('CircleCI artifact download failed (getBuildArtifactUrl failed)');
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed (getBuildArtifactUrl failed)');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the URL fetch errors', async () => {
|
it('should fail if the URL fetch errors', async () => {
|
||||||
// create a new handler that errors
|
// create a new handler that errors
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).replyWithError('Artifact Request Failed');
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).replyWithError('Artifact Request Failed');
|
||||||
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).toBeRejectedWithError(
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
'CircleCI artifact download failed ' +
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed ' +
|
||||||
'(request to http://test.com/some/path/build.zip failed, reason: Artifact Request Failed)');
|
'(request to http://test.com/some/path/build.zip failed, reason: Artifact Request Failed)');
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the URL fetch 404s', async () => {
|
it('should fail if the URL fetch 404s', async () => {
|
||||||
// create a new handler that errors
|
// create a new handler that errors
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(404, 'No such artifact');
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(404, 'No such artifact');
|
||||||
|
try {
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
toBeRejectedWithError('CircleCI artifact download failed (Error 404 - Not Found)');
|
throw new Error('Exception Expected');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed (Error 404 - Not Found)');
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if file write fails', async () => {
|
it('should fail if file write fails', async () => {
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||||
|
try {
|
||||||
WRITEFILE_RESULT = 'Test Error';
|
WRITEFILE_RESULT = 'Test Error';
|
||||||
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
await expectAsync(retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH)).
|
throw new Error('Exception Expected');
|
||||||
toBeRejectedWithError('CircleCI artifact download failed (Test Error)');
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual('CircleCI artifact download failed (Test Error)');
|
||||||
|
}
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -51,10 +51,7 @@ describe('BuildVerifier', () => {
|
|||||||
describe('getSignificantFilesChanged', () => {
|
describe('getSignificantFilesChanged', () => {
|
||||||
it('should return false if none of the fetched files match the given pattern', async () => {
|
it('should return false if none of the fetched files match the given pattern', async () => {
|
||||||
const fetchFilesSpy = spyOn(prs, 'fetchFiles');
|
const fetchFilesSpy = spyOn(prs, 'fetchFiles');
|
||||||
fetchFilesSpy.and.resolveTo([
|
fetchFilesSpy.and.callFake(() => Promise.resolve([{filename: 'a/b/c'}, {filename: 'd/e/f'}]));
|
||||||
{filename: 'a/b/c', sha: 'a1'},
|
|
||||||
{filename: 'd/e/f', sha: 'b2'},
|
|
||||||
]);
|
|
||||||
expect(await bv.getSignificantFilesChanged(777, /^x/)).toEqual(false);
|
expect(await bv.getSignificantFilesChanged(777, /^x/)).toEqual(false);
|
||||||
expect(fetchFilesSpy).toHaveBeenCalledWith(777);
|
expect(fetchFilesSpy).toHaveBeenCalledWith(777);
|
||||||
|
|
||||||
@ -81,30 +78,37 @@ describe('BuildVerifier', () => {
|
|||||||
user: {login: 'username'},
|
user: {login: 'username'},
|
||||||
};
|
};
|
||||||
|
|
||||||
prsFetchSpy = spyOn(GithubPullRequests.prototype, 'fetch').and.resolveTo(mockPrInfo);
|
prsFetchSpy = spyOn(GithubPullRequests.prototype, 'fetch').
|
||||||
teamsIsMemberBySlugSpy = spyOn(GithubTeams.prototype, 'isMemberBySlug').and.resolveTo(true);
|
and.callFake(() => Promise.resolve(mockPrInfo));
|
||||||
|
|
||||||
|
teamsIsMemberBySlugSpy = spyOn(GithubTeams.prototype, 'isMemberBySlug').
|
||||||
|
and.callFake(() => Promise.resolve(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', done => {
|
||||||
const promise = bv.getPrIsTrusted(pr);
|
const promise = bv.getPrIsTrusted(pr);
|
||||||
expect(promise).toBeInstanceOf(Promise);
|
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||||
|
// to avoid running the actual `GithubTeams#isMemberBySlug()`.
|
||||||
|
|
||||||
// Do not complete the test (and release the spies) synchronously to avoid running the actual
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
// `GithubTeams#isMemberBySlug()`.
|
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fetch the corresponding PR', async () => {
|
it('should fetch the corresponding PR', done => {
|
||||||
await bv.getPrIsTrusted(pr);
|
bv.getPrIsTrusted(pr).then(() => {
|
||||||
expect(prsFetchSpy).toHaveBeenCalledWith(pr);
|
expect(prsFetchSpy).toHaveBeenCalledWith(pr);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fail if fetching the PR errors', async () => {
|
it('should fail if fetching the PR errors', done => {
|
||||||
prsFetchSpy.and.rejectWith('Test');
|
prsFetchSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeRejectedWith('Test');
|
bv.getPrIsTrusted(pr).catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -113,14 +117,19 @@ describe('BuildVerifier', () => {
|
|||||||
beforeEach(() => mockPrInfo.labels.push({name: 'trusted: pr-label'}));
|
beforeEach(() => mockPrInfo.labels.push({name: 'trusted: pr-label'}));
|
||||||
|
|
||||||
|
|
||||||
it('should resolve to true', async () => {
|
it('should resolve to true', done => {
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(true);
|
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||||
|
expect(isTrusted).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not try to verify the author\'s membership status', async () => {
|
it('should not try to verify the author\'s membership status', done => {
|
||||||
await expectAsync(bv.getPrIsTrusted(pr));
|
bv.getPrIsTrusted(pr).then(() => {
|
||||||
expect(teamsIsMemberBySlugSpy).not.toHaveBeenCalled();
|
expect(teamsIsMemberBySlugSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -128,27 +137,40 @@ describe('BuildVerifier', () => {
|
|||||||
|
|
||||||
describe('when the PR does not have the "trusted PR" label', () => {
|
describe('when the PR does not have the "trusted PR" label', () => {
|
||||||
|
|
||||||
it('should verify the PR author\'s membership in the specified teams', async () => {
|
it('should verify the PR author\'s membership in the specified teams', done => {
|
||||||
await bv.getPrIsTrusted(pr);
|
bv.getPrIsTrusted(pr).then(() => {
|
||||||
expect(teamsIsMemberBySlugSpy).toHaveBeenCalledWith('username', ['team1', 'team2']);
|
expect(teamsIsMemberBySlugSpy).toHaveBeenCalledWith('username', ['team1', 'team2']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should fail if verifying membership errors', async () => {
|
it('should fail if verifying membership errors', done => {
|
||||||
teamsIsMemberBySlugSpy.and.rejectWith('Test');
|
teamsIsMemberBySlugSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeRejectedWith('Test');
|
bv.getPrIsTrusted(pr).catch(err => {
|
||||||
|
expect(err).toBe('Test');
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve to true if the PR\'s author is a member', async () => {
|
it('should resolve to true if the PR\'s author is a member', done => {
|
||||||
teamsIsMemberBySlugSpy.and.resolveTo(true);
|
teamsIsMemberBySlugSpy.and.callFake(() => Promise.resolve(true));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(true);
|
|
||||||
|
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||||
|
expect(isTrusted).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve to false if the PR\'s author is not a member', async () => {
|
it('should resolve to false if the PR\'s author is not a member', done => {
|
||||||
teamsIsMemberBySlugSpy.and.resolveTo(false);
|
teamsIsMemberBySlugSpy.and.callFake(() => Promise.resolve(false));
|
||||||
await expectAsync(bv.getPrIsTrusted(pr)).toBeResolvedTo(false);
|
|
||||||
|
bv.getPrIsTrusted(pr).then(isTrusted => {
|
||||||
|
expect(isTrusted).toBe(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
|
|
||||||
|
export const expectToBePreviewServerError = (actual: PreviewServerError, status?: number, message?: string) => {
|
||||||
|
expect(actual).toEqual(jasmine.any(PreviewServerError));
|
||||||
|
if (status != null) {
|
||||||
|
expect(actual.status).toBe(status);
|
||||||
|
}
|
||||||
|
if (message != null) {
|
||||||
|
expect(actual.message).toBe(message);
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +0,0 @@
|
|||||||
declare module jasmine {
|
|
||||||
interface AsyncMatchers {
|
|
||||||
toBeRejectedWithPreviewServerError(status: number, message?: string | RegExp): Promise<void>;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
|
||||||
|
|
||||||
|
|
||||||
// Matchers
|
|
||||||
const toBeRejectedWithPreviewServerError: jasmine.CustomAsyncMatcherFactory = () => {
|
|
||||||
return {
|
|
||||||
async compare(actualPromise: Promise<never>, expectedStatus: number, expectedMessage?: string | RegExp) {
|
|
||||||
if (!(actualPromise instanceof Promise)) {
|
|
||||||
throw new Error(`Expected '${toBeRejectedWithPreviewServerError.name}()' to be called on a promise.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await actualPromise;
|
|
||||||
|
|
||||||
return {
|
|
||||||
pass: false,
|
|
||||||
message: `Expected a promise to be rejected with a '${PreviewServerError.name}', but it was resolved.`,
|
|
||||||
};
|
|
||||||
} catch (actualError) {
|
|
||||||
const actualPrintValue = stringify(actualError);
|
|
||||||
const expectedPrintValue =
|
|
||||||
stringify(new PreviewServerError(expectedStatus, expectedMessage && `${expectedMessage}`));
|
|
||||||
|
|
||||||
const pass = errorMatches(actualError, expectedStatus, expectedMessage);
|
|
||||||
const message =
|
|
||||||
`Expected a promise ${pass ? 'not ' : ''}to be rejected with ${expectedPrintValue}, but is was` +
|
|
||||||
`${pass ? '' : ` rejected with ${actualPrintValue}`}.`;
|
|
||||||
|
|
||||||
return {pass, message};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
function errorMatches(actualErr: unknown, expectedStatus: number, expectedMsg?: string | RegExp): boolean {
|
|
||||||
if (!(actualErr instanceof PreviewServerError)) return false;
|
|
||||||
if (actualErr.status !== expectedStatus) return false;
|
|
||||||
return messageMatches(actualErr.message, expectedMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function messageMatches(actualMsg: string, expectedMsg?: string | RegExp): boolean {
|
|
||||||
if (typeof expectedMsg === 'undefined') return true;
|
|
||||||
if (typeof expectedMsg === 'string') return actualMsg === expectedMsg;
|
|
||||||
return expectedMsg.test(actualMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringify(value: unknown): string {
|
|
||||||
if (value instanceof PreviewServerError) {
|
|
||||||
return `${PreviewServerError.name}(${value.status}${value.message ? `, ${value.message}` : ''})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jasmine.pp(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Exports
|
|
||||||
export const customAsyncMatchers: jasmine.CustomAsyncMatcherFactories = {
|
|
||||||
toBeRejectedWithPreviewServerError,
|
|
||||||
};
|
|
@ -9,8 +9,8 @@ describe('PreviewServerError', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should extend Error', () => {
|
it('should extend Error', () => {
|
||||||
expect(err).toBeInstanceOf(PreviewServerError);
|
expect(err).toEqual(jasmine.any(PreviewServerError));
|
||||||
expect(err).toBeInstanceOf(Error);
|
expect(err).toEqual(jasmine.any(Error));
|
||||||
|
|
||||||
expect(Object.getPrototypeOf(err)).toBe(PreviewServerError.prototype);
|
expect(Object.getPrototypeOf(err)).toBe(PreviewServerError.prototype);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Imports
|
// Imports
|
||||||
|
import * as express from 'express';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as supertest from 'supertest';
|
import * as supertest from 'supertest';
|
||||||
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
||||||
@ -133,7 +134,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
const buildCreator = jasmine.any(BuildCreator);
|
const buildCreator = jasmine.any(BuildCreator);
|
||||||
expect(usfCreateMiddlewareSpy).toHaveBeenCalledWith(buildRetriever, buildVerifier, buildCreator, defaultConfig);
|
expect(usfCreateMiddlewareSpy).toHaveBeenCalledWith(buildRetriever, buildVerifier, buildCreator, defaultConfig);
|
||||||
|
|
||||||
const middleware = usfCreateMiddlewareSpy.calls.mostRecent().returnValue;
|
const middleware: express.Express = usfCreateMiddlewareSpy.calls.mostRecent().returnValue;
|
||||||
expect(httpCreateServerSpy).toHaveBeenCalledWith(middleware);
|
expect(httpCreateServerSpy).toHaveBeenCalledWith(middleware);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
expect(prsAddCommentSpy).toHaveBeenCalledTimes(2);
|
expect(prsAddCommentSpy).toHaveBeenCalledTimes(2);
|
||||||
expect(prs).toBe(allCalls[1].object);
|
expect(prs).toBe(allCalls[1].object);
|
||||||
expect(prs).toBeInstanceOf(GithubPullRequests);
|
expect(prs).toEqual(jasmine.any(GithubPullRequests));
|
||||||
expect(prs.repoSlug).toBe('organisation/repo');
|
expect(prs.repoSlug).toBe('organisation/repo');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -301,8 +302,9 @@ describe('PreviewServerFactory', () => {
|
|||||||
let bvGetSignificantFilesChangedSpy: jasmine.Spy;
|
let bvGetSignificantFilesChangedSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.resolveTo(true);
|
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.returnValue(Promise.resolve(true));
|
||||||
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').and.resolveTo(true);
|
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').
|
||||||
|
and.returnValue(Promise.resolve(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -329,7 +331,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR did not touch any significant files', async () => {
|
it('should respond appropriately if the PR did not touch any significant files', async () => {
|
||||||
bvGetSignificantFilesChangedSpy.and.resolveTo(false);
|
bvGetSignificantFilesChangedSpy.and.returnValue(Promise.resolve(false));
|
||||||
|
|
||||||
const expectedResponse = {canHavePublicPreview: false, reason: 'No significant files touched.'};
|
const expectedResponse = {canHavePublicPreview: false, reason: 'No significant files touched.'};
|
||||||
const expectedLog = `PR:${pr} - Cannot have a public preview, because it did not touch any significant files.`;
|
const expectedLog = `PR:${pr} - Cannot have a public preview, because it did not touch any significant files.`;
|
||||||
@ -343,7 +345,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR is not automatically verifiable as "trusted"', async () => {
|
it('should respond appropriately if the PR is not automatically verifiable as "trusted"', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.resolveTo(false);
|
bvGetPrIsTrustedSpy.and.returnValue(Promise.resolve(false));
|
||||||
|
|
||||||
const expectedResponse = {canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'};
|
const expectedResponse = {canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'};
|
||||||
const expectedLog =
|
const expectedLog =
|
||||||
@ -370,7 +372,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with error if `getSignificantFilesChanged()` fails', async () => {
|
it('should respond with error if `getSignificantFilesChanged()` fails', async () => {
|
||||||
bvGetSignificantFilesChangedSpy.and.rejectWith('getSignificantFilesChanged error');
|
bvGetSignificantFilesChangedSpy.and.callFake(() => Promise.reject('getSignificantFilesChanged error'));
|
||||||
|
|
||||||
await agent.get(url).expect(500, 'getSignificantFilesChanged error');
|
await agent.get(url).expect(500, 'getSignificantFilesChanged error');
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', 'getSignificantFilesChanged error');
|
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', 'getSignificantFilesChanged error');
|
||||||
@ -378,10 +380,11 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with error if `getPrIsTrusted()` fails', async () => {
|
it('should respond with error if `getPrIsTrusted()` fails', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.throwError('getPrIsTrusted error');
|
const error = new Error('getPrIsTrusted error');
|
||||||
|
bvGetPrIsTrustedSpy.and.callFake(() => { throw error; });
|
||||||
|
|
||||||
await agent.get(url).expect(500, 'getPrIsTrusted error');
|
await agent.get(url).expect(500, 'getPrIsTrusted error');
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', new Error('getPrIsTrusted error'));
|
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -494,7 +497,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
// Note it is important to put the `reject` into `and.callFake`;
|
// Note it is important to put the `reject` into `and.callFake`;
|
||||||
// If you just `and.returnValue` the rejected promise
|
// If you just `and.returnValue` the rejected promise
|
||||||
// then you get an "unhandled rejection" message in the console.
|
// then you get an "unhandled rejection" message in the console.
|
||||||
getGithubInfoSpy.and.rejectWith('Test Error');
|
getGithubInfoSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||||
@ -515,7 +518,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if the artifact fetch request fails', async () => {
|
it('should fail if the artifact fetch request fails', async () => {
|
||||||
downloadBuildArtifactSpy.and.rejectWith('Test Error');
|
downloadBuildArtifactSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||||
@ -524,7 +527,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if verifying the PR fails', async () => {
|
it('should fail if verifying the PR fails', async () => {
|
||||||
getPrIsTrustedSpy.and.rejectWith('Test Error');
|
getPrIsTrustedSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||||
@ -533,7 +536,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if creating the preview build fails', async () => {
|
it('should fail if creating the preview build fails', async () => {
|
||||||
createBuildSpy.and.rejectWith('Test Error');
|
createBuildSpy.and.callFake(() => Promise.reject('Test Error'));
|
||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(500, 'Test Error');
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).toHaveBeenCalled();
|
||||||
@ -602,7 +605,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should propagate errors from BuildVerifier', async () => {
|
it('should propagate errors from BuildVerifier', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.rejectWith('Test');
|
bvGetPrIsTrustedSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
|
||||||
await createRequest(+pr).expect(500, 'Test');
|
await createRequest(+pr).expect(500, 'Test');
|
||||||
|
|
||||||
@ -612,9 +615,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.callFake((pr2: number) => Promise.resolve(pr2 === 42));
|
||||||
withArgs(24).and.resolveTo(false).
|
|
||||||
withArgs(42).and.resolveTo(true);
|
|
||||||
|
|
||||||
await createRequest(24);
|
await createRequest(24);
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
||||||
@ -625,7 +626,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should propagate errors from BuildCreator', async () => {
|
it('should propagate errors from BuildCreator', async () => {
|
||||||
bcUpdatePrVisibilitySpy.and.rejectWith('Test');
|
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await createRequest(+pr).expect(500, 'Test');
|
await createRequest(+pr).expect(500, 'Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -633,9 +634,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
describe('on success', () => {
|
describe('on success', () => {
|
||||||
|
|
||||||
it('should respond with 200 (action: undefined)', async () => {
|
it('should respond with 200 (action: undefined)', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
withArgs(2).and.resolveTo(false).
|
|
||||||
withArgs(4).and.resolveTo(true);
|
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await Promise.all(reqs);
|
||||||
@ -643,9 +642,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (action: labeled)', async () => {
|
it('should respond with 200 (action: labeled)', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
withArgs(2).and.resolveTo(false).
|
|
||||||
withArgs(4).and.resolveTo(true);
|
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num, 'labeled').expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num, 'labeled').expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await Promise.all(reqs);
|
||||||
@ -653,9 +650,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (action: unlabeled)', async () => {
|
it('should respond with 200 (action: unlabeled)', async () => {
|
||||||
bvGetPrIsTrustedSpy.
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
withArgs(2).and.resolveTo(false).
|
|
||||||
withArgs(4).and.resolveTo(true);
|
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num, 'unlabeled').expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num, 'unlabeled').expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await Promise.all(reqs);
|
||||||
|
@ -39,7 +39,7 @@ describe('preview-server/utils', () => {
|
|||||||
throwRequestError(505, 'ERROR MESSAGE', request);
|
throwRequestError(505, 'ERROR MESSAGE', request);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
caught = true;
|
caught = true;
|
||||||
expect(error).toBeInstanceOf(PreviewServerError);
|
expect(error).toEqual(jasmine.any(PreviewServerError));
|
||||||
expect(error.status).toEqual(505);
|
expect(error.status).toEqual(505);
|
||||||
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,32 +8,10 @@ exitCode=0
|
|||||||
|
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
function checkCert {
|
|
||||||
local certPath=$1
|
|
||||||
|
|
||||||
if [[ ! -f "$certPath" ]]; then
|
|
||||||
echo "Certificate '$certPath' does not exist. Skipping expiration check..."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
openssl x509 -checkend 0 -in "$certPath" -noout > /dev/null
|
|
||||||
reportStatus "Certificate '$certPath'"
|
|
||||||
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
echo " [WARN]"
|
|
||||||
echo " If you did not provide the certificate explicitly, try running the"
|
|
||||||
echo " 'docker build' command again with the '--no-cache' option to generate"
|
|
||||||
echo " a new self-signed certificate."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportStatus {
|
function reportStatus {
|
||||||
local lastExitCode=$?
|
local lastExitCode=$?
|
||||||
|
|
||||||
echo "$1: $([[ $lastExitCode -eq 0 ]] && echo OK || echo NOT OK)"
|
echo "$1: $([[ $lastExitCode -eq 0 ]] && echo OK || echo NOT OK)"
|
||||||
[[ $lastExitCode -eq 0 ]] || exitCode=1
|
[[ $lastExitCode -eq 0 ]] || exitCode=1
|
||||||
|
|
||||||
return $lastExitCode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -50,16 +28,6 @@ for s in ${services[@]}; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
# Check SSL/TLS certificates expiration
|
|
||||||
certs=(
|
|
||||||
"$AIO_LOCALCERTS_DIR/$AIO_DOMAIN_NAME.crt"
|
|
||||||
"$TEST_AIO_LOCALCERTS_DIR/$TEST_AIO_DOMAIN_NAME.crt"
|
|
||||||
)
|
|
||||||
for c in ${certs[@]}; do
|
|
||||||
checkCert $c
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# Check servers
|
# Check servers
|
||||||
origins=(
|
origins=(
|
||||||
http://$AIO_PREVIEW_SERVER_HOSTNAME:$AIO_PREVIEW_SERVER_PORT
|
http://$AIO_PREVIEW_SERVER_HOSTNAME:$AIO_PREVIEW_SERVER_PORT
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
## Create `aio-builds` persistent disk (if not already exists)
|
## Create `aio-builds` persistent disk (if not already exists)
|
||||||
- Follow instructions [here](https://cloud.google.com/compute/docs/disks/add-persistent-disk#create_disk).
|
- Follow instructions [here](https://cloud.google.com/compute/docs/disks/add-persistent-disk#create_disk).
|
||||||
- `sudo mkfs.ext4 -m 0 -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-aio-builds`
|
- `sudo mkfs.ext4 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-aio-builds`
|
||||||
|
|
||||||
|
|
||||||
## Mount disk
|
## Mount disk
|
||||||
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
## Mount disk on boot
|
## Mount disk on boot
|
||||||
- Run:
|
- Run:
|
||||||
```sh
|
```
|
||||||
echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
||||||
/mnt/disks/aio-builds ext4 defaults,discard,nofail 0 2 | sudo tee -a /etc/fstab
|
/mnt/disks/aio-builds ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
|
||||||
```
|
```
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
# VM setup - Create docker image
|
# VM setup - Create docker image
|
||||||
|
|
||||||
|
|
||||||
## Install git, Node.js and yarn
|
## Install node and yarn
|
||||||
- `sudo apt-get update`
|
- Install [nvm](https://github.com/creationix/nvm#installation).
|
||||||
- `sudo apt-get install -y git`
|
- Install node.js: `nvm install 8`
|
||||||
- Install the latest stable version of [Node.js](https://nodejs.org/en/download).
|
- Install yarn: `npm -g install yarn`
|
||||||
- Install the latest stable version of [yarn](https://classic.yarnpkg.com/en/docs/install).
|
|
||||||
|
|
||||||
|
|
||||||
## Checkout repository
|
## Checkout repository
|
||||||
@ -17,11 +16,7 @@
|
|||||||
- You can overwrite the default environment variables inside the image, by passing new values using
|
- You can overwrite the default environment variables inside the image, by passing new values using
|
||||||
`--build-arg`.
|
`--build-arg`.
|
||||||
|
|
||||||
**Note 1:** The script has to execute docker commands with `sudo`.
|
**Note:** The script has to execute docker commands with `sudo`.
|
||||||
|
|
||||||
**Note 2:**
|
|
||||||
The script has to execute `yarn` commands, so make sure `yarn` is on the `PATH` when invoking the
|
|
||||||
script.
|
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
@ -31,7 +26,7 @@ The following commands would create a docker image from GitHub repo `foo/bar` to
|
|||||||
|
|
||||||
- `git clone https://github.com/foo/bar.git foobar`
|
- `git clone https://github.com/foo/bar.git foobar`
|
||||||
- Run:
|
- Run:
|
||||||
```sh
|
```
|
||||||
./foobar/aio-builds-setup/scripts/create-image.sh foobar-builds \
|
./foobar/aio-builds-setup/scripts/create-image.sh foobar-builds \
|
||||||
--build-arg AIO_REPO_SLUG=foo/bar \
|
--build-arg AIO_REPO_SLUG=foo/bar \
|
||||||
--build-arg AIO_DOMAIN_NAME=foobar-builds.io \
|
--build-arg AIO_DOMAIN_NAME=foobar-builds.io \
|
||||||
|
@ -3,17 +3,24 @@
|
|||||||
|
|
||||||
## Install docker
|
## Install docker
|
||||||
|
|
||||||
Official installation instructions: https://docs.docker.com/engine/install
|
_Debian (jessie):_
|
||||||
Example:
|
- `sudo apt-get update`
|
||||||
|
- `sudo apt-get install -y apt-transport-https ca-certificates curl git software-properties-common`
|
||||||
|
- `curl -fsSL https://apt.dockerproject.org/gpg | sudo apt-key add -`
|
||||||
|
- `apt-key fingerprint 58118E89F3A912897C070ADBF76221572C52609D`
|
||||||
|
- `sudo add-apt-repository "deb https://apt.dockerproject.org/repo/ debian-$(lsb_release -cs) main"`
|
||||||
|
- `sudo apt-get update`
|
||||||
|
- `sudo apt-get -y install docker-engine`
|
||||||
|
|
||||||
_Debian (buster):_
|
_Ubuntu (16.04):_
|
||||||
- `sudo apt-get update`
|
- `sudo apt-get update`
|
||||||
- `sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common`
|
- `sudo apt-get install -y curl git linux-image-extra-$(uname -r) linux-image-extra-virtual`
|
||||||
- `curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -`
|
- `sudo apt-get install -y apt-transport-https ca-certificates`
|
||||||
- `sudo apt-key fingerprint 0EBFCD88`
|
- `curl -fsSL https://yum.dockerproject.org/gpg | sudo apt-key add -`
|
||||||
- `sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"`
|
- `apt-key fingerprint 58118E89F3A912897C070ADBF76221572C52609D`
|
||||||
|
- `sudo add-apt-repository "deb https://apt.dockerproject.org/repo/ ubuntu-$(lsb_release -cs) main"`
|
||||||
- `sudo apt-get update`
|
- `sudo apt-get update`
|
||||||
- `sudo apt-get -y install docker-ce docker-ce-cli containerd.io`
|
- `sudo apt-get -y install docker-engine`
|
||||||
|
|
||||||
|
|
||||||
## Start the docker
|
## Start the docker
|
||||||
|
@ -8,16 +8,16 @@ VM host to update the preview server based on changes in the source code.
|
|||||||
|
|
||||||
The script will pull the latest changes from the origin's master branch and examine if there have
|
The script will pull the latest changes from the origin's master branch and examine if there have
|
||||||
been any changes in files inside the preview server source code directory (see below). If there are,
|
been any changes in files inside the preview server source code directory (see below). If there are,
|
||||||
it will create a new image and verify that it works as expected. Finally, it will stop and remove
|
it will create a new image and verify that is works as expected. Finally, it will stop and remove
|
||||||
the old docker container and image, create a new container based on the new image and start it.
|
the old docker container and image, create a new container based on the new image and start it.
|
||||||
|
|
||||||
The script assumes that the preview server source code is in the repository's
|
The script assumes that the preview server source code is in the repository's
|
||||||
`aio/aio-builds-setup/` directory and expects the following inputs:
|
`aio/aio-builds-setup/` directory and expects the following inputs:
|
||||||
|
|
||||||
- **$1**: `HOST_REPO_DIR`
|
- **$1**: `HOST_REPO_DIR`
|
||||||
- **$2**: `HOST_SECRETS_DIR`
|
- **$2**: `HOST_LOCALCERTS_DIR`
|
||||||
- **$3**: `HOST_BUILDS_DIR`
|
- **$3**: `HOST_SECRETS_DIR`
|
||||||
- **$4**: `HOST_LOCALCERTS_DIR`
|
- **$4**: `HOST_BUILDS_DIR`
|
||||||
- **$5**: `HOST_LOGS_DIR`
|
- **$5**: `HOST_LOGS_DIR`
|
||||||
|
|
||||||
See [here](vm-setup--create-host-dirs-and-files.md) for more info on what each input directory is
|
See [here](vm-setup--create-host-dirs-and-files.md) for more info on what each input directory is
|
||||||
@ -25,38 +25,28 @@ used for.
|
|||||||
|
|
||||||
**Note 1:** The script has to execute docker commands with `sudo`.
|
**Note 1:** The script has to execute docker commands with `sudo`.
|
||||||
|
|
||||||
**Note 2:**
|
**Note 2:** Make sure the user that executes the script has access to update the repository
|
||||||
The script has to execute `yarn` commands, so make sure `yarn` is on the `PATH` when invoking the
|
|
||||||
script.
|
|
||||||
|
|
||||||
**Note 3:** Make sure the user that executes the script has access to update the repository.
|
|
||||||
|
|
||||||
|
|
||||||
## Run the script manually
|
## Run the script manually
|
||||||
You may choose to manually run the script, when necessary. Example:
|
You may choose to manually run the script, when necessary. Example:
|
||||||
|
|
||||||
```sh
|
```
|
||||||
update-preview-server.sh \
|
update-preview-server.sh \
|
||||||
/path/to/repo \
|
/path/to/repo \
|
||||||
|
/path/to/localcerts \
|
||||||
/path/to/secrets \
|
/path/to/secrets \
|
||||||
/path/to/builds \
|
/path/to/builds \
|
||||||
/path/to/localcerts \
|
|
||||||
/path/to/logs
|
/path/to/logs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Run the script automatically
|
## Run the script automatically
|
||||||
You may choose to automatically trigger the script, e.g. using a cronjob. For example, the following
|
You may choose to automatically trigger the script, e.g. using a cronjob. For example, the following
|
||||||
cronjob entry would run the script every 30 minutes, update the preview server (if necessary) and
|
cronjob entry would run the script every hour and update the preview server (assuming the user has
|
||||||
log its output to `update-preview-server.log` (assuming the user has the necessary permissions):
|
the necessary permissions):
|
||||||
|
|
||||||
```
|
```
|
||||||
# Periodically check for changes and update the preview server (if necessary)
|
# Periodically check for changes and update the preview server (if necessary)
|
||||||
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/secrets /path/to/builds /path/to/localcerts /path/to/logs >> /path/to/update-preview-server.log 2>&1
|
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/localcerts /path/to/secrets /path/to/builds /path/to/logs
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:**
|
|
||||||
Keep in mind that cron jobs run in non-interactive, non-login shells. This means that the execution
|
|
||||||
context might be different compared to when running the same commands from an interactive, login
|
|
||||||
shell. For example, `.bashrc` files are normally _not_ sourced automatically in cron jobs. See
|
|
||||||
[here](http://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html) for more info.
|
|
||||||
|
@ -7,14 +7,13 @@ echo -e "\n\n[`date`] - Updating the preview server..."
|
|||||||
|
|
||||||
# Input
|
# Input
|
||||||
readonly HOST_REPO_DIR=$1
|
readonly HOST_REPO_DIR=$1
|
||||||
readonly HOST_SECRETS_DIR=$2
|
readonly HOST_LOCALCERTS_DIR=$2
|
||||||
readonly HOST_BUILDS_DIR=$3
|
readonly HOST_SECRETS_DIR=$3
|
||||||
readonly HOST_LOCALCERTS_DIR=$4
|
readonly HOST_BUILDS_DIR=$4
|
||||||
readonly HOST_LOGS_DIR=$5
|
readonly HOST_LOGS_DIR=$5
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
readonly PROVISIONAL_TAG=provisional
|
readonly PROVISIONAL_IMAGE_NAME=aio-builds:provisional
|
||||||
readonly PROVISIONAL_IMAGE_NAME=aio-builds:$PROVISIONAL_TAG
|
|
||||||
readonly LATEST_IMAGE_NAME=aio-builds:latest
|
readonly LATEST_IMAGE_NAME=aio-builds:latest
|
||||||
readonly CONTAINER_NAME=aio
|
readonly CONTAINER_NAME=aio
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ readonly CONTAINER_NAME=aio
|
|||||||
# Do not update the server unless files inside `aio-builds-setup/` have changed
|
# Do not update the server unless files inside `aio-builds-setup/` have changed
|
||||||
# or the last attempt failed (identified by the provisional image still being around).
|
# or the last attempt failed (identified by the provisional image still being around).
|
||||||
readonly relevantChangedFilesCount=$(git diff --name-only $lastDeployedCommit...HEAD | grep -P "^aio/aio-builds-setup/" | wc -l)
|
readonly relevantChangedFilesCount=$(git diff --name-only $lastDeployedCommit...HEAD | grep -P "^aio/aio-builds-setup/" | wc -l)
|
||||||
readonly lastAttemptFailed=$(sudo docker image ls | grep "$PROVISIONAL_TAG" >> /dev/fd/3 && echo "true" || echo "false")
|
readonly lastAttemptFailed=$(sudo docker rmi "$PROVISIONAL_IMAGE_NAME" >> /dev/fd/3 && echo "true" || echo "false")
|
||||||
if [[ $relevantChangedFilesCount -eq 0 ]] && [[ "$lastAttemptFailed" != "true" ]]; then
|
if [[ $relevantChangedFilesCount -eq 0 ]] && [[ "$lastAttemptFailed" != "true" ]]; then
|
||||||
echo "Skipping update because no relevant files have been touched."
|
echo "Skipping update because no relevant files have been touched."
|
||||||
exit 0
|
exit 0
|
||||||
@ -61,9 +60,9 @@ readonly CONTAINER_NAME=aio
|
|||||||
--publish 80:80 \
|
--publish 80:80 \
|
||||||
--publish 443:443 \
|
--publish 443:443 \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
|
--volume $HOST_LOCALCERTS_DIR:/etc/ssl/localcerts:ro \
|
||||||
--volume $HOST_SECRETS_DIR:/aio-secrets:ro \
|
--volume $HOST_SECRETS_DIR:/aio-secrets:ro \
|
||||||
--volume $HOST_BUILDS_DIR:/var/www/aio-builds \
|
--volume $HOST_BUILDS_DIR:/var/www/aio-builds \
|
||||||
--volume $HOST_LOCALCERTS_DIR:/etc/ssl/localcerts:ro \
|
|
||||||
--volume $HOST_LOGS_DIR:/var/log/aio \
|
--volume $HOST_LOGS_DIR:/var/log/aio \
|
||||||
"$LATEST_IMAGE_NAME"
|
"$LATEST_IMAGE_NAME"
|
||||||
|
|
||||||
|
@ -109,3 +109,9 @@ Options that specify files can be given as absolute paths, or as paths relative
|
|||||||
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
||||||
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
||||||
Schematic options are supplied to the command in the same format as immediate command options.
|
Schematic options are supplied to the command in the same format as immediate command options.
|
||||||
|
|
||||||
|
|
||||||
|
### Building with Bazel
|
||||||
|
|
||||||
|
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
|
||||||
|
|
||||||
|
3
aio/content/examples/.gitignore
vendored
3
aio/content/examples/.gitignore
vendored
@ -82,6 +82,9 @@ upgrade-phonecat-2-hybrid/aot/**/*
|
|||||||
# styleguide
|
# styleguide
|
||||||
!styleguide/src/systemjs.custom.js
|
!styleguide/src/systemjs.custom.js
|
||||||
|
|
||||||
|
# universal
|
||||||
|
!universal/webpack.server.config.js
|
||||||
|
|
||||||
# stackblitz
|
# stackblitz
|
||||||
*stackblitz.no-link.html
|
*stackblitz.no-link.html
|
||||||
|
|
||||||
|
@ -200,4 +200,13 @@
|
|||||||
(ngModelChange)="model.name = $event">
|
(ngModelChange)="model.name = $event">
|
||||||
TODO: remove this: {{model.name}}
|
TODO: remove this: {{model.name}}
|
||||||
<!-- #enddocregion ngModel-3-->
|
<!-- #enddocregion ngModel-3-->
|
||||||
|
<hr>
|
||||||
|
<!-- #docregion ngModelName-2 -->
|
||||||
|
<input type="text" class="form-control" id="name"
|
||||||
|
required
|
||||||
|
[(ngModel)]="model.name" name="name"
|
||||||
|
#spy>
|
||||||
|
<br>TODO: remove this: {{spy.className}}
|
||||||
|
<!-- #enddocregion ngModelName-2 -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,13 +13,13 @@ import { searchUrl } from '../package-search/package-search.service';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If request is cacheable (e.g., package search) and
|
* If request is cachable (e.g., package search) and
|
||||||
* response is in cache return the cached response as observable.
|
* response is in cache return the cached response as observable.
|
||||||
* If has 'x-refresh' header that is true,
|
* If has 'x-refresh' header that is true,
|
||||||
* then also re-run the package search, using response from next(),
|
* then also re-run the package search, using response from next(),
|
||||||
* returning an observable that emits the cached response first.
|
* returning an observable that emits the cached response first.
|
||||||
*
|
*
|
||||||
* If not in cache or not cacheable,
|
* If not in cache or not cachable,
|
||||||
* pass request through to next()
|
* pass request through to next()
|
||||||
*/
|
*/
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
@ -28,8 +28,8 @@ export class CachingInterceptor implements HttpInterceptor {
|
|||||||
constructor(private cache: RequestCache) {}
|
constructor(private cache: RequestCache) {}
|
||||||
|
|
||||||
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
||||||
// continue if not cacheable.
|
// continue if not cachable.
|
||||||
if (!isCacheable(req)) { return next.handle(req); }
|
if (!isCachable(req)) { return next.handle(req); }
|
||||||
|
|
||||||
const cachedResponse = this.cache.get(req);
|
const cachedResponse = this.cache.get(req);
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
@ -51,11 +51,11 @@ export class CachingInterceptor implements HttpInterceptor {
|
|||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
|
|
||||||
|
|
||||||
/** Is this request cacheable? */
|
/** Is this request cachable? */
|
||||||
function isCacheable(req: HttpRequest<any>) {
|
function isCachable(req: HttpRequest<any>) {
|
||||||
// Only GET requests are cacheable
|
// Only GET requests are cachable
|
||||||
return req.method === 'GET' &&
|
return req.method === 'GET' &&
|
||||||
// Only npm package search is cacheable in this app
|
// Only npm package search is cachable in this app
|
||||||
-1 < req.url.indexOf(searchUrl);
|
-1 < req.url.indexOf(searchUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "i18n",
|
|
||||||
"files":[
|
|
||||||
"!**/*.d.ts",
|
|
||||||
"!**/*.js",
|
|
||||||
"!**/*.[0-9].*"
|
|
||||||
],
|
|
||||||
"file": "src/app/app.component.ts",
|
|
||||||
"tags": ["Angular", "i18n", "internationalization"]
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import {
|
|||||||
AfterContentInit,
|
AfterContentInit,
|
||||||
AfterViewChecked,
|
AfterViewChecked,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Directive,
|
|
||||||
DoCheck,
|
DoCheck,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
@ -16,8 +15,7 @@ import { LoggerService } from './logger.service';
|
|||||||
let nextId = 1;
|
let nextId = 1;
|
||||||
|
|
||||||
// #docregion ngOnInit
|
// #docregion ngOnInit
|
||||||
@Directive()
|
export class PeekABoo implements OnInit {
|
||||||
export class PeekABooDirective implements OnInit {
|
|
||||||
constructor(private logger: LoggerService) { }
|
constructor(private logger: LoggerService) { }
|
||||||
|
|
||||||
// implement OnInit's `ngOnInit` method
|
// implement OnInit's `ngOnInit` method
|
||||||
@ -36,7 +34,7 @@ export class PeekABooDirective implements OnInit {
|
|||||||
})
|
})
|
||||||
// Don't HAVE to mention the Lifecycle Hook interfaces
|
// Don't HAVE to mention the Lifecycle Hook interfaces
|
||||||
// unless we want typing and tool support.
|
// unless we want typing and tool support.
|
||||||
export class PeekABooComponent extends PeekABooDirective implements
|
export class PeekABooComponent extends PeekABoo implements
|
||||||
OnChanges, OnInit, DoCheck,
|
OnChanges, OnInit, DoCheck,
|
||||||
AfterContentInit, AfterContentChecked,
|
AfterContentInit, AfterContentChecked,
|
||||||
AfterViewInit, AfterViewChecked,
|
AfterViewInit, AfterViewChecked,
|
||||||
|
@ -23,7 +23,7 @@ export class GreetingModule {
|
|||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
// #docregion for-root
|
// #docregion for-root
|
||||||
static forRoot(config: UserServiceConfig): ModuleWithProviders<GreetingModule> {
|
static forRoot(config: UserServiceConfig): ModuleWithProviders {
|
||||||
return {
|
return {
|
||||||
ngModule: GreetingModule,
|
ngModule: GreetingModule,
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -6,5 +6,5 @@ import { Component } from '@angular/core';
|
|||||||
templateUrl: './app.component.html'
|
templateUrl: './app.component.html'
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
|
birthday = new Date(1988, 3, 15); // April 15, 1988
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,5 @@ import { Component } from '@angular/core';
|
|||||||
// #enddocregion hero-birthday-template
|
// #enddocregion hero-birthday-template
|
||||||
})
|
})
|
||||||
export class HeroBirthdayComponent {
|
export class HeroBirthdayComponent {
|
||||||
birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
|
birthday = new Date(1988, 3, 15); // April 15, 1988
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import { Component } from '@angular/core';
|
|||||||
})
|
})
|
||||||
// #docregion class
|
// #docregion class
|
||||||
export class HeroBirthday2Component {
|
export class HeroBirthday2Component {
|
||||||
birthday = new Date(1988, 3, 15); // April 15, 1988 -- since month parameter is zero-based
|
birthday = new Date(1988, 3, 15); // April 15, 1988
|
||||||
toggle = true; // start with true == shortDate
|
toggle = true; // start with true == shortDate
|
||||||
|
|
||||||
get format() { return this.toggle ? 'shortDate' : 'fullDate'; }
|
get format() { return this.toggle ? 'shortDate' : 'fullDate'; }
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
import { browser, element, by } from 'protractor';
|
|
||||||
|
|
||||||
describe('Router basic tutorial e2e tests', () => {
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
browser.get('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display Angular Router Sample', () => {
|
|
||||||
expect(element(by.css('h1')).getText()).toBe('Angular Router Sample');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display Crisis Center button', () => {
|
|
||||||
expect(element.all(by.css('a')).get(0).getText()).toBe('Crisis Center');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display Heroes button', () => {
|
|
||||||
expect(element.all(by.css('a')).get(1).getText()).toBe('Heroes');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display HEROES', () => {
|
|
||||||
expect(element(by.css('h3')).getText()).toBe('HEROES');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should change to display crisis list component', async () => {
|
|
||||||
const crisisButton = element.all(by.css('a')).get(0);
|
|
||||||
await crisisButton.click();
|
|
||||||
expect(element(by.css('h3')).getText()).toBe('CRISIS CENTER');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should change to display heroes component', async () => {
|
|
||||||
const heroesButton = element.all(by.css('a')).get(1);
|
|
||||||
await heroesButton.click();
|
|
||||||
expect(element(by.css('h3')).getText()).toBe('HEROES');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use wildcard route', async () => {
|
|
||||||
browser.get('/fake-page');
|
|
||||||
expect(browser.getCurrentUrl()).toContain('fake-page');
|
|
||||||
expect(element(by.css('h2')).getText()).toBe('Page Not Found');
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,34 +0,0 @@
|
|||||||
.button {
|
|
||||||
box-shadow: inset 0px 1px 0px 0px #ffffff;
|
|
||||||
background: linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%);
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #dcdcdc;
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #666666;
|
|
||||||
font-family: Arial;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 6px 24px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-shadow: 0px 1px 0px #ffffff;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.activebutton {
|
|
||||||
box-shadow: inset 0px 1px 0px 0px #dcecfb;
|
|
||||||
background: linear-gradient(to bottom, #bddbfa 5%, #80b5ea 100%);
|
|
||||||
background-color: #bddbfa;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid #84bbf3;
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #ffffff;
|
|
||||||
font-family: Arial;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 6px 24px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-shadow: 0px 1px 0px #528ecc;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
<!-- #docplaster -->
|
|
||||||
<!-- #docregion setup -->
|
|
||||||
<h1>Angular Router Sample</h1>
|
|
||||||
<!-- #enddocregion setup-->
|
|
||||||
<!-- #docregion routeractivelink -->
|
|
||||||
<nav>
|
|
||||||
<a class="button" routerLink="/crisis-list" routerLinkActive="activebutton">Crisis Center</a> |
|
|
||||||
<a class="button" routerLink="/heroes-list" routerLinkActive="activebutton">Heroes</a>
|
|
||||||
</nav>
|
|
||||||
<!-- #enddocregion routeractivelink-->
|
|
||||||
<!-- #docregion router-outlet -->
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
<!-- #enddocregion router-outlet -->
|
|
||||||
|
|
||||||
<div style="display: none;">
|
|
||||||
<!-- This HTML represents the initial state for the tutorial. It is not intended to appear in the app. -->
|
|
||||||
<!-- #docregion setup, components -->
|
|
||||||
<app-crisis-list></app-crisis-list>
|
|
||||||
<app-heroes-list></app-heroes-list>
|
|
||||||
<!-- #enddocregion setup, components -->
|
|
||||||
|
|
||||||
<!-- This HTML snippet is for when the user first adds the routerlink navigation. -->
|
|
||||||
<!-- #docregion nav -->
|
|
||||||
<nav>
|
|
||||||
<a class="button" routerLink="/crisis-list">Crisis Center</a> |
|
|
||||||
<a class="button" routerLink="/heroes-list">Heroes</a>
|
|
||||||
</nav>
|
|
||||||
<!-- #enddocregion nav-->
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-root',
|
|
||||||
templateUrl: './app.component.html',
|
|
||||||
styleUrls: ['./app.component.css']
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
title = 'angular-router-sample';
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
// #docplaster
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
// #docregion router-import
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
// #enddocregion router-import
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { CrisisListComponent } from './crisis-list/crisis-list.component';
|
|
||||||
import { HeroesListComponent } from './heroes-list/heroes-list.component';
|
|
||||||
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
AppComponent,
|
|
||||||
CrisisListComponent,
|
|
||||||
HeroesListComponent,
|
|
||||||
PageNotFoundComponent
|
|
||||||
],
|
|
||||||
// #docplaster
|
|
||||||
// #docregion import-basic, import-redirect, import-wildcard
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
RouterModule.forRoot([
|
|
||||||
{path: 'crisis-list', component: CrisisListComponent},
|
|
||||||
{path: 'heroes-list', component: HeroesListComponent},
|
|
||||||
// #enddocregion import-basic
|
|
||||||
{path: '', redirectTo: '/heroes-list', pathMatch: 'full'},
|
|
||||||
// #enddocregion import-redirect
|
|
||||||
{path: '**', component: PageNotFoundComponent}
|
|
||||||
// #enddocregion import-wildcard
|
|
||||||
// #docregion import-basic, import-redirect, import-wildcard
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
// #enddocregion import-basic, import-redirect, import-wildcard
|
|
||||||
providers: [],
|
|
||||||
bootstrap: [AppComponent]
|
|
||||||
})
|
|
||||||
export class AppModule { }
|
|
@ -1,2 +0,0 @@
|
|||||||
<h3>CRISIS CENTER</h3>
|
|
||||||
<p>Get your crisis here</p>
|
|
@ -1,10 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-crisis-list',
|
|
||||||
templateUrl: './crisis-list.component.html',
|
|
||||||
styleUrls: ['./crisis-list.component.css']
|
|
||||||
})
|
|
||||||
export class CrisisListComponent {
|
|
||||||
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
<h3>HEROES</h3>
|
|
||||||
<p>Get your heroes here</p>
|
|
@ -1,10 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-heroes-list',
|
|
||||||
templateUrl: './heroes-list.component.html',
|
|
||||||
styleUrls: ['./heroes-list.component.css']
|
|
||||||
})
|
|
||||||
export class HeroesListComponent {
|
|
||||||
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
<h2>Page Not Found</h2>
|
|
||||||
<p>We couldn't find that page! Not even with x-ray vision.</p>
|
|
@ -1,10 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-page-not-found',
|
|
||||||
templateUrl: './page-not-found.component.html',
|
|
||||||
styleUrls: ['./page-not-found.component.css']
|
|
||||||
})
|
|
||||||
export class PageNotFoundComponent {
|
|
||||||
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Angular Router Sample</title>
|
|
||||||
<base href="/">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<app-root></app-root>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,12 +0,0 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
|
||||||
import { environment } from './environments/environment';
|
|
||||||
|
|
||||||
if (environment.production) {
|
|
||||||
enableProdMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
|
||||||
.catch(err => console.error(err));
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "Router",
|
|
||||||
"files":[
|
|
||||||
"!**/*.d.ts",
|
|
||||||
"!**/*.js",
|
|
||||||
"!**/*.[0-9].*"
|
|
||||||
],
|
|
||||||
"tags": ["router-tutorial"]
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
|
|
||||||
|
|
||||||
const routes: Routes = []; // sets up routes constant where you define your routes
|
|
||||||
|
|
||||||
// configures NgModule imports and exports
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forRoot(routes)],
|
|
||||||
exports: [RouterModule]
|
|
||||||
})
|
|
||||||
export class AppRoutingModule { }
|
|
@ -1,26 +0,0 @@
|
|||||||
// #docplaster
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
|
|
||||||
|
|
||||||
// #docregion routes, routes-with-wildcard, redirect
|
|
||||||
const routes: Routes = [
|
|
||||||
{ path: 'first-component', component: FirstComponent },
|
|
||||||
{ path: 'second-component', component: SecondComponent },
|
|
||||||
// #enddocregion routes
|
|
||||||
{ path: '', redirectTo: '/first-component', pathMatch: 'full' }, // redirect to `first-component`
|
|
||||||
{ path: '**', component: FirstComponent },
|
|
||||||
// #enddocregion redirect
|
|
||||||
{ path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page
|
|
||||||
// #docregion routes
|
|
||||||
// #docregion redirect
|
|
||||||
];
|
|
||||||
// #enddocregion routes, routes-with-wildcard, redirect
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forRoot(routes)],
|
|
||||||
exports: [RouterModule]
|
|
||||||
})
|
|
||||||
export class AppRoutingModule { }
|
|
||||||
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
|||||||
// #docplaster
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { Routes, RouterModule } from '@angular/router'; // CLI imports router
|
|
||||||
|
|
||||||
// #docregion child-routes
|
|
||||||
const routes: Routes = [
|
|
||||||
{ path: 'first-component',
|
|
||||||
component: FirstComponent, // this is the component with the <router-outlet> in the template
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'child-a', // child route path
|
|
||||||
component: ChildAComponent // child route component that the router renders
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'child-b',
|
|
||||||
component: ChildBComponent // another child route component that the router renders
|
|
||||||
}
|
|
||||||
] },
|
|
||||||
// #enddocregion child-routes
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forRoot(routes)],
|
|
||||||
exports: [RouterModule]
|
|
||||||
})
|
|
||||||
export class AppRoutingModule { }
|
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-root',
|
|
||||||
templateUrl: 'app.component.html',
|
|
||||||
styleUrls: ['app.component.css']
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
// #docregion relative-to
|
|
||||||
goToItems() {
|
|
||||||
this.router.navigate(['items'], { relativeTo: this.route });
|
|
||||||
}
|
|
||||||
// #enddocregion relative-to
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<h1>Angular Router App</h1>
|
|
||||||
<!-- This nav gives you links to click, which tells the router which route to use (defined in the routes constant in AppRoutingModule) -->
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a routerLink="/first-component" routerLinkActive="active">First Component</a></li>
|
|
||||||
<li><a routerLink="/second-component" routerLinkActive="active">Second Component</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<!-- The routed views render in the <router-outlet>-->
|
|
||||||
<router-outlet></router-outlet>
|
|
@ -1,26 +0,0 @@
|
|||||||
<!-- #docregion child-routes-->
|
|
||||||
<h2>First Component</h2>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a routerLink="child-a">Child A</a></li>
|
|
||||||
<li><a routerLink="child-b">Child B</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
<!-- #enddocregion child-routes-->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- #docregion relative-route-->
|
|
||||||
|
|
||||||
<h2>First Component</h2>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a routerLink="../second-component">Relative Route to second component</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
|
|
||||||
<!-- #enddocregion relative-route-->
|
|
@ -1,17 +0,0 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { AppRoutingModule } from './app-routing.module'; // CLI imports AppRoutingModule
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
AppComponent
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
AppRoutingModule // CLI adds AppRoutingModule to the AppModule's imports array
|
|
||||||
],
|
|
||||||
providers: [],
|
|
||||||
bootstrap: [AppComponent]
|
|
||||||
})
|
|
||||||
export class AppModule { }
|
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
|
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
||||||
|
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
@ -12,20 +12,21 @@ export class AuthGuard implements CanActivate {
|
|||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
next: ActivatedRouteSnapshot,
|
next: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): true|UrlTree {
|
state: RouterStateSnapshot): boolean {
|
||||||
let url: string = state.url;
|
let url: string = state.url;
|
||||||
|
|
||||||
return this.checkLogin(url);
|
return this.checkLogin(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLogin(url: string): true|UrlTree {
|
checkLogin(url: string): boolean {
|
||||||
if (this.authService.isLoggedIn) { return true; }
|
if (this.authService.isLoggedIn) { return true; }
|
||||||
|
|
||||||
// Store the attempted URL for redirecting
|
// Store the attempted URL for redirecting
|
||||||
this.authService.redirectUrl = url;
|
this.authService.redirectUrl = url;
|
||||||
|
|
||||||
// Redirect to the login page
|
// Navigate to the login page with extras
|
||||||
return this.router.parseUrl('/login');
|
this.router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -4,8 +4,7 @@ import {
|
|||||||
CanActivate, Router,
|
CanActivate, Router,
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
CanActivateChild,
|
CanActivateChild
|
||||||
UrlTree
|
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): true|UrlTree {
|
state: RouterStateSnapshot): boolean {
|
||||||
let url: string = state.url;
|
let url: string = state.url;
|
||||||
|
|
||||||
return this.checkLogin(url);
|
return this.checkLogin(url);
|
||||||
@ -25,19 +24,20 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
|
|
||||||
canActivateChild(
|
canActivateChild(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): true|UrlTree {
|
state: RouterStateSnapshot): boolean {
|
||||||
return this.canActivate(route, state);
|
return this.canActivate(route, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion can-activate-child
|
// #enddocregion can-activate-child
|
||||||
checkLogin(url: string): true|UrlTree {
|
checkLogin(url: string): boolean {
|
||||||
if (this.authService.isLoggedIn) { return true; }
|
if (this.authService.isLoggedIn) { return true; }
|
||||||
|
|
||||||
// Store the attempted URL for redirecting
|
// Store the attempted URL for redirecting
|
||||||
this.authService.redirectUrl = url;
|
this.authService.redirectUrl = url;
|
||||||
|
|
||||||
// Redirect to the login page
|
// Navigate to the login page
|
||||||
return this.router.parseUrl('/login');
|
this.router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// #docregion can-activate-child
|
// #docregion can-activate-child
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,7 @@ import {
|
|||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
CanActivateChild,
|
CanActivateChild,
|
||||||
NavigationExtras,
|
NavigationExtras
|
||||||
UrlTree
|
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
@ -17,17 +16,17 @@ import { AuthService } from './auth.service';
|
|||||||
export class AuthGuard implements CanActivate, CanActivateChild {
|
export class AuthGuard implements CanActivate, CanActivateChild {
|
||||||
constructor(private authService: AuthService, private router: Router) {}
|
constructor(private authService: AuthService, private router: Router) {}
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||||
let url: string = state.url;
|
let url: string = state.url;
|
||||||
|
|
||||||
return this.checkLogin(url);
|
return this.checkLogin(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree {
|
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||||
return this.canActivate(route, state);
|
return this.canActivate(route, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLogin(url: string): true|UrlTree {
|
checkLogin(url: string): boolean {
|
||||||
if (this.authService.isLoggedIn) { return true; }
|
if (this.authService.isLoggedIn) { return true; }
|
||||||
|
|
||||||
// Store the attempted URL for redirecting
|
// Store the attempted URL for redirecting
|
||||||
@ -43,7 +42,8 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
fragment: 'anchor'
|
fragment: 'anchor'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Redirect to the login page with extras
|
// Navigate to the login page with extras
|
||||||
return this.router.createUrlTree(['/login'], navigationExtras);
|
this.router.navigate(['/login'], navigationExtras);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
// #docregion imports-route-info
|
|
||||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
// #enddocregion imports-route-info
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { HeroService } from '../hero.service';
|
import { HeroService } from '../hero.service';
|
||||||
@ -18,16 +16,11 @@ import { Hero } from '../hero';
|
|||||||
export class HeroDetailComponent implements OnInit {
|
export class HeroDetailComponent implements OnInit {
|
||||||
hero$: Observable<Hero>;
|
hero$: Observable<Hero>;
|
||||||
|
|
||||||
// #docregion activated-route
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
// #enddocregion activated-route
|
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private service: HeroService
|
private service: HeroService
|
||||||
// #docregion activated-route
|
|
||||||
) {}
|
) {}
|
||||||
// #enddocregion activated-route
|
|
||||||
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.hero$ = this.route.paramMap.pipe(
|
this.hero$ = this.route.paramMap.pipe(
|
||||||
|
@ -8,6 +8,4 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
|
|
||||||
|
@ -9,6 +9,5 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -9,6 +9,5 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -9,6 +9,5 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -33,7 +33,7 @@ export class HeroesComponent implements OnInit {
|
|||||||
|
|
||||||
onSelect(hero: Hero): void {
|
onSelect(hero: Hero): void {
|
||||||
this.selectedHero = hero;
|
this.selectedHero = hero;
|
||||||
this.messageService.add(`HeroesComponent: Selected hero id=${hero.id}`);
|
this.messageService.add(`HeroService: Selected hero id=${hero.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion getHeroes
|
// #docregion getHeroes
|
||||||
|
@ -8,5 +8,4 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
|
@ -19,6 +19,7 @@ button {
|
|||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
cursor: hand;
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #cfd8dc;
|
background-color: #cfd8dc;
|
||||||
|
@ -8,5 +8,4 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
|
@ -59,5 +59,6 @@ import { MessagesComponent } from './messages/messages.component';
|
|||||||
// #docregion import-httpclientmodule
|
// #docregion import-httpclientmodule
|
||||||
})
|
})
|
||||||
// #enddocregion import-httpclientmodule
|
// #enddocregion import-httpclientmodule
|
||||||
|
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
// #enddocregion , v1
|
// #enddocregion , v1
|
||||||
|
@ -18,7 +18,7 @@ button {
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer; cursor: hand;
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #cfd8dc;
|
background-color: #cfd8dc;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion , init
|
// #docregion , init
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
@ -9,5 +9,4 @@ if (environment.production) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
.catch(err => console.error(err));
|
|
||||||
|
@ -1,300 +0,0 @@
|
|||||||
import { browser, by, element, ElementArrayFinder, ElementFinder, logging } from 'protractor';
|
|
||||||
|
|
||||||
class Hero {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
// Factory methods
|
|
||||||
|
|
||||||
// Hero from string formatted as '<id> <name>'.
|
|
||||||
static fromString(s: string): Hero {
|
|
||||||
return {
|
|
||||||
id: +s.substr(0, s.indexOf(' ')),
|
|
||||||
name: s.substr(s.indexOf(' ') + 1),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hero from hero list <li> element.
|
|
||||||
static async fromLi(li: ElementFinder): Promise<Hero> {
|
|
||||||
const stringsFromA = await li.all(by.css('a')).getText();
|
|
||||||
const strings = stringsFromA[0].split(' ');
|
|
||||||
return { id: +strings[0], name: strings[1] };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hero id and name from the given detail element.
|
|
||||||
static async fromDetail(detail: ElementFinder): Promise<Hero> {
|
|
||||||
// Get hero id from the first <div>
|
|
||||||
const id = await detail.all(by.css('div')).first().getText();
|
|
||||||
// Get name from the h2
|
|
||||||
const name = await detail.element(by.css('h2')).getText();
|
|
||||||
return {
|
|
||||||
id: +id.substr(id.indexOf(' ') + 1),
|
|
||||||
name: name.substr(0, name.lastIndexOf(' '))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Universal', () => {
|
|
||||||
const expectedH1 = 'Tour of Heroes';
|
|
||||||
const expectedTitle = `${expectedH1}`;
|
|
||||||
const targetHero = { id: 15, name: 'Magneta' };
|
|
||||||
const targetHeroDashboardIndex = 3;
|
|
||||||
const nameSuffix = 'X';
|
|
||||||
const newHeroName = targetHero.name + nameSuffix;
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
// Assert that there are no errors emitted from the browser
|
|
||||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
|
||||||
const severeLogs = logs.filter(entry => entry.level === logging.Level.SEVERE);
|
|
||||||
expect(severeLogs).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Initial page', () => {
|
|
||||||
beforeAll(() => browser.get(''));
|
|
||||||
|
|
||||||
it(`has title '${expectedTitle}'`, () => {
|
|
||||||
expect(browser.getTitle()).toEqual(expectedTitle);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`has h1 '${expectedH1}'`, () => {
|
|
||||||
expectHeading(1, expectedH1);
|
|
||||||
});
|
|
||||||
|
|
||||||
const expectedViewNames = ['Dashboard', 'Heroes'];
|
|
||||||
it(`has views ${expectedViewNames}`, () => {
|
|
||||||
const viewNames = getPageElts().navElts.map((el: ElementFinder) => el.getText());
|
|
||||||
expect(viewNames).toEqual(expectedViewNames);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has dashboard as the active view', () => {
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.appDashboard.isPresent()).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Dashboard tests', () => {
|
|
||||||
beforeAll(() => browser.get(''));
|
|
||||||
|
|
||||||
it('has top heroes', () => {
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.topHeroes.count()).toEqual(4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
|
|
||||||
|
|
||||||
it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
|
|
||||||
|
|
||||||
it(`cancels and shows ${targetHero.name} in Dashboard`, () => {
|
|
||||||
element(by.buttonText('go back')).click();
|
|
||||||
browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6
|
|
||||||
|
|
||||||
const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
|
|
||||||
expect(targetHeroElt.getText()).toEqual(targetHero.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
|
|
||||||
|
|
||||||
it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
|
|
||||||
|
|
||||||
it(`saves and shows ${newHeroName} in Dashboard`, () => {
|
|
||||||
element(by.buttonText('save')).click();
|
|
||||||
browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6
|
|
||||||
|
|
||||||
const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
|
|
||||||
expect(targetHeroElt.getText()).toEqual(newHeroName);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Heroes tests', () => {
|
|
||||||
beforeAll(() => browser.get(''));
|
|
||||||
|
|
||||||
it('can switch to Heroes view', () => {
|
|
||||||
getPageElts().appHeroesHref.click();
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.appHeroes.isPresent()).toBeTruthy();
|
|
||||||
expect(page.allHeroes.count()).toEqual(10, 'number of heroes');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can route to hero details', async () => {
|
|
||||||
getHeroLiEltById(targetHero.id).click();
|
|
||||||
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
|
|
||||||
const hero = await Hero.fromDetail(page.heroDetail);
|
|
||||||
expect(hero.id).toEqual(targetHero.id);
|
|
||||||
expect(hero.name).toEqual(targetHero.name.toUpperCase());
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
|
|
||||||
|
|
||||||
it(`shows ${newHeroName} in Heroes list`, () => {
|
|
||||||
element(by.buttonText('save')).click();
|
|
||||||
browser.waitForAngular();
|
|
||||||
const expectedText = `${targetHero.id} ${newHeroName}`;
|
|
||||||
expect(getHeroAEltById(targetHero.id).getText()).toEqual(expectedText);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`deletes ${newHeroName} from Heroes list`, async () => {
|
|
||||||
const heroesBefore = await toHeroArray(getPageElts().allHeroes);
|
|
||||||
const li = getHeroLiEltById(targetHero.id);
|
|
||||||
li.element(by.buttonText('x')).click();
|
|
||||||
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.appHeroes.isPresent()).toBeTruthy();
|
|
||||||
expect(page.allHeroes.count()).toEqual(9, 'number of heroes');
|
|
||||||
const heroesAfter = await toHeroArray(page.allHeroes);
|
|
||||||
// console.log(await Hero.fromLi(page.allHeroes[0]));
|
|
||||||
const expectedHeroes = heroesBefore.filter(h => h.name !== newHeroName);
|
|
||||||
expect(heroesAfter).toEqual(expectedHeroes);
|
|
||||||
// expect(page.selectedHeroSubview.isPresent()).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`adds back ${targetHero.name}`, async () => {
|
|
||||||
const updatedHeroName = 'Alice';
|
|
||||||
const heroesBefore = await toHeroArray(getPageElts().allHeroes);
|
|
||||||
const numHeroes = heroesBefore.length;
|
|
||||||
|
|
||||||
element(by.css('input')).sendKeys(updatedHeroName);
|
|
||||||
element(by.buttonText('add')).click();
|
|
||||||
|
|
||||||
const page = getPageElts();
|
|
||||||
const heroesAfter = await toHeroArray(page.allHeroes);
|
|
||||||
expect(heroesAfter.length).toEqual(numHeroes + 1, 'number of heroes');
|
|
||||||
|
|
||||||
expect(heroesAfter.slice(0, numHeroes)).toEqual(heroesBefore, 'Old heroes are still there');
|
|
||||||
|
|
||||||
const maxId = heroesBefore[heroesBefore.length - 1].id;
|
|
||||||
expect(heroesAfter[numHeroes]).toEqual({id: maxId + 1, name: updatedHeroName});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays correctly styled buttons', async () => {
|
|
||||||
element.all(by.buttonText('x')).then(buttons => {
|
|
||||||
for (const button of buttons) {
|
|
||||||
// Inherited styles from styles.css
|
|
||||||
expect(button.getCssValue('font-family')).toBe('Arial');
|
|
||||||
expect(button.getCssValue('border')).toContain('none');
|
|
||||||
expect(button.getCssValue('padding')).toBe('5px 10px');
|
|
||||||
expect(button.getCssValue('border-radius')).toBe('4px');
|
|
||||||
// Styles defined in heroes.component.css
|
|
||||||
expect(button.getCssValue('left')).toBe('194px');
|
|
||||||
expect(button.getCssValue('top')).toBe('-32px');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const addButton = element(by.buttonText('add'));
|
|
||||||
// Inherited styles from styles.css
|
|
||||||
expect(addButton.getCssValue('font-family')).toBe('Arial');
|
|
||||||
expect(addButton.getCssValue('border')).toContain('none');
|
|
||||||
expect(addButton.getCssValue('padding')).toBe('5px 10px');
|
|
||||||
expect(addButton.getCssValue('border-radius')).toBe('4px');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Progressive hero search', () => {
|
|
||||||
beforeAll(() => browser.get(''));
|
|
||||||
|
|
||||||
it(`searches for 'Ma'`, async () => {
|
|
||||||
getPageElts().searchBox.sendKeys('Ma');
|
|
||||||
browser.sleep(1000);
|
|
||||||
|
|
||||||
expect(getPageElts().searchResults.count()).toBe(4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`continues search with 'g'`, async () => {
|
|
||||||
getPageElts().searchBox.sendKeys('g');
|
|
||||||
browser.sleep(1000);
|
|
||||||
expect(getPageElts().searchResults.count()).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`continues search with 'e' and gets ${targetHero.name}`, async () => {
|
|
||||||
getPageElts().searchBox.sendKeys('n');
|
|
||||||
browser.sleep(1000);
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.searchResults.count()).toBe(1);
|
|
||||||
const hero = page.searchResults.get(0);
|
|
||||||
expect(hero.getText()).toEqual(targetHero.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`navigates to ${targetHero.name} details view`, async () => {
|
|
||||||
const hero = getPageElts().searchResults.get(0);
|
|
||||||
expect(hero.getText()).toEqual(targetHero.name);
|
|
||||||
hero.click();
|
|
||||||
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
|
|
||||||
const hero2 = await Hero.fromDetail(page.heroDetail);
|
|
||||||
expect(hero2.id).toEqual(targetHero.id);
|
|
||||||
expect(hero2.name).toEqual(targetHero.name.toUpperCase());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
function addToHeroName(text: string): Promise<void> {
|
|
||||||
return element(by.css('input')).sendKeys(text) as Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function dashboardSelectTargetHero(): Promise<void> {
|
|
||||||
const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
|
|
||||||
expect(targetHeroElt.getText()).toEqual(targetHero.name);
|
|
||||||
targetHeroElt.click();
|
|
||||||
browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6
|
|
||||||
|
|
||||||
const page = getPageElts();
|
|
||||||
expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
|
|
||||||
const hero = await Hero.fromDetail(page.heroDetail);
|
|
||||||
expect(hero.id).toEqual(targetHero.id);
|
|
||||||
expect(hero.name).toEqual(targetHero.name.toUpperCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectHeading(hLevel: number, expectedText: string): void {
|
|
||||||
const hTag = `h${hLevel}`;
|
|
||||||
const hText = element(by.css(hTag)).getText();
|
|
||||||
expect(hText).toEqual(expectedText, hTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHeroAEltById(id: number): ElementFinder {
|
|
||||||
const spanForId = element(by.cssContainingText('li span.badge', id.toString()));
|
|
||||||
return spanForId.element(by.xpath('..'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHeroLiEltById(id: number): ElementFinder {
|
|
||||||
const spanForId = element(by.cssContainingText('li span.badge', id.toString()));
|
|
||||||
return spanForId.element(by.xpath('../..'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPageElts() {
|
|
||||||
const navElts = element.all(by.css('app-root nav a'));
|
|
||||||
|
|
||||||
return {
|
|
||||||
navElts,
|
|
||||||
|
|
||||||
appDashboardHref: navElts.get(0),
|
|
||||||
appDashboard: element(by.css('app-root app-dashboard')),
|
|
||||||
topHeroes: element.all(by.css('app-root app-dashboard > div h4')),
|
|
||||||
|
|
||||||
appHeroesHref: navElts.get(1),
|
|
||||||
appHeroes: element(by.css('app-root app-heroes')),
|
|
||||||
allHeroes: element.all(by.css('app-root app-heroes li')),
|
|
||||||
selectedHeroSubview: element(by.css('app-root app-heroes > div:last-child')),
|
|
||||||
|
|
||||||
heroDetail: element(by.css('app-root app-hero-detail > div')),
|
|
||||||
|
|
||||||
searchBox: element(by.css('#search-box')),
|
|
||||||
searchResults: element.all(by.css('.search-result li'))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toHeroArray(allHeroes: ElementArrayFinder): Promise<Hero[]> {
|
|
||||||
return await allHeroes.map(Hero.fromLi);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateHeroNameInDetailView(): Promise<void> {
|
|
||||||
// Assumes that the current view is the hero details view.
|
|
||||||
addToHeroName(nameSuffix);
|
|
||||||
|
|
||||||
const page = getPageElts();
|
|
||||||
const hero = await Hero.fromDetail(page.heroDetail);
|
|
||||||
expect(hero.id).toEqual(targetHero.id);
|
|
||||||
expect(hero.name).toEqual(newHeroName.toUpperCase());
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,7 +1,3 @@
|
|||||||
{
|
{
|
||||||
"projectType": "universal",
|
"projectType": "universal"
|
||||||
"e2e": [
|
|
||||||
{"cmd": "yarn", "args": ["e2e", "--prod", "--protractor-config=e2e/protractor-puppeteer.conf.js", "--no-webdriver-update", "--port={PORT}"]},
|
|
||||||
{"cmd": "yarn", "args": ["run", "build:ssr"]}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -6,28 +6,24 @@ import { join } from 'path';
|
|||||||
|
|
||||||
import { AppServerModule } from './src/main.server';
|
import { AppServerModule } from './src/main.server';
|
||||||
import { APP_BASE_HREF } from '@angular/common';
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
import { existsSync } from 'fs';
|
|
||||||
|
|
||||||
// The Express app is exported so that it can be used by serverless Functions.
|
// The Express app is exported so that it can be used by serverless Functions.
|
||||||
export function app() {
|
export function app() {
|
||||||
const server = express();
|
const server = express();
|
||||||
const distFolder = join(process.cwd(), 'dist/browser');
|
const distFolder = join(process.cwd(), 'dist/express-engine-ivy/browser');
|
||||||
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
|
|
||||||
|
|
||||||
// #docregion ngExpressEngine
|
// #docregion ngExpressEngine
|
||||||
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
|
|
||||||
server.engine('html', ngExpressEngine({
|
server.engine('html', ngExpressEngine({
|
||||||
bootstrap: AppServerModule,
|
bootstrap: AppServerModule,
|
||||||
}));
|
}));
|
||||||
// #enddocregion ngExpressEngine
|
// #enddocregion ngExpressEngine
|
||||||
|
|
||||||
server.set('view engine', 'html');
|
server.set('view engine', 'html');
|
||||||
server.set('views', distFolder);
|
server.set('views', distFolder);
|
||||||
|
|
||||||
// #docregion data-request
|
// #docregion data-request
|
||||||
// TODO: implement data requests securely
|
// TODO: implement data requests securely
|
||||||
server.get('/api/**', (req, res) => {
|
server.get('/api/*', (req, res) => {
|
||||||
res.status(404).send('data requests are not yet supported');
|
res.status(404).send('data requests are not supported');
|
||||||
});
|
});
|
||||||
// #enddocregion data-request
|
// #enddocregion data-request
|
||||||
|
|
||||||
@ -41,7 +37,7 @@ export function app() {
|
|||||||
// #docregion navigation-request
|
// #docregion navigation-request
|
||||||
// All regular routes use the Universal engine
|
// All regular routes use the Universal engine
|
||||||
server.get('*', (req, res) => {
|
server.get('*', (req, res) => {
|
||||||
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
|
res.render('index', { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
|
||||||
});
|
});
|
||||||
// #enddocregion navigation-request
|
// #enddocregion navigation-request
|
||||||
|
|
||||||
@ -63,8 +59,7 @@ function run() {
|
|||||||
// The below code is to ensure that the server is run only when not requiring the bundle.
|
// The below code is to ensure that the server is run only when not requiring the bundle.
|
||||||
declare const __non_webpack_require__: NodeRequire;
|
declare const __non_webpack_require__: NodeRequire;
|
||||||
const mainModule = __non_webpack_require__.main;
|
const mainModule = __non_webpack_require__.main;
|
||||||
const moduleFilename = mainModule && mainModule.filename || '';
|
if (mainModule && mainModule.filename === __filename) {
|
||||||
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
|
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* AppComponent's private CSS styles */
|
/* AppComponent's private CSS styles */
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
color: #999;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
@ -17,7 +18,7 @@ nav a {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
nav a:visited, a:link {
|
nav a:visited, a:link {
|
||||||
color: #334953;
|
color: #607D8B;
|
||||||
}
|
}
|
||||||
nav a:hover {
|
nav a:hover {
|
||||||
color: #039be5;
|
color: #039be5;
|
||||||
|
@ -14,6 +14,8 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
|||||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||||
import { HeroesComponent } from './heroes/heroes.component';
|
import { HeroesComponent } from './heroes/heroes.component';
|
||||||
import { HeroSearchComponent } from './hero-search/hero-search.component';
|
import { HeroSearchComponent } from './hero-search/hero-search.component';
|
||||||
|
import { HeroService } from './hero.service';
|
||||||
|
import { MessageService } from './message.service';
|
||||||
import { MessagesComponent } from './messages/messages.component';
|
import { MessagesComponent } from './messages/messages.component';
|
||||||
|
|
||||||
// #docregion platform-detection
|
// #docregion platform-detection
|
||||||
@ -30,10 +32,6 @@ import { isPlatformBrowser } from '@angular/common';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
|
||||||
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
|
|
||||||
// and returns simulated server responses.
|
|
||||||
// Remove it when a real server is ready to receive requests.
|
|
||||||
HttpClientInMemoryWebApiModule.forRoot(
|
HttpClientInMemoryWebApiModule.forRoot(
|
||||||
InMemoryDataService, { dataEncapsulation: false }
|
InMemoryDataService, { dataEncapsulation: false }
|
||||||
)
|
)
|
||||||
@ -46,6 +44,7 @@ import { isPlatformBrowser } from '@angular/common';
|
|||||||
MessagesComponent,
|
MessagesComponent,
|
||||||
HeroSearchComponent
|
HeroSearchComponent
|
||||||
],
|
],
|
||||||
|
providers: [ HeroService, MessageService ],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user