Compare commits
99 Commits
Author | SHA1 | Date | |
---|---|---|---|
5edeee69dd | |||
ce85cbf2d3 | |||
c305b5ca31 | |||
b970028057 | |||
e67c69a782 | |||
03a8b16ec9 | |||
fd4ce84584 | |||
0671e540c2 | |||
45c7b23cc8 | |||
9b31f77c19 | |||
927d691d56 | |||
4fb5e21426 | |||
6518dae45e | |||
83d68b3521 | |||
cf28373629 | |||
598b3ff5d7 | |||
011937555f | |||
f33d8fe11d | |||
c9d80b2fc8 | |||
81c40cb5e8 | |||
822036362b | |||
eee8c7f718 | |||
1797390c8b | |||
4a4b6be731 | |||
4b1dcaf0f5 | |||
4f8d30361a | |||
098ba19560 | |||
5149d98acc | |||
db693f482f | |||
84c9c6ecc2 | |||
a52d103341 | |||
ec77bc4fc5 | |||
1129f10f26 | |||
3006a560fd | |||
8e9be6ce0d | |||
99e4daec94 | |||
90c249bc78 | |||
83941d68df | |||
a30fd2993b | |||
a84093a971 | |||
37e1c04e6a | |||
985762b5bc | |||
be0f994657 | |||
6aa259246e | |||
ad7850e4b8 | |||
c3b5ce4bb2 | |||
554c2cbd5c | |||
a245e9d0a3 | |||
e19eebcba1 | |||
fe42930ddd | |||
c99165f789 | |||
cd2ffea668 | |||
94d002b64e | |||
3cc24a9ac4 | |||
d13cab77fc | |||
91a2fd5c33 | |||
9251519a0e | |||
baf77e4adf | |||
d9fcfd3b24 | |||
bfcf6d09e1 | |||
9f03a85694 | |||
6d39a4a031 | |||
480a4c3061 | |||
18ed9dcd83 | |||
411d4ad79a | |||
b285630767 | |||
32d103816e | |||
5b5dc811c1 | |||
dea1b962c7 | |||
dce230eb00 | |||
672abb5871 | |||
b582bc26d7 | |||
c3c11403c0 | |||
24ffe3745b | |||
dfa8356dc4 | |||
b6a3a739bf | |||
da85e733d7 | |||
f3f4195ec9 | |||
55cc2040d1 | |||
8e3c0d6639 | |||
14fc1812f3 | |||
7c1fd91b8f | |||
931338e9d8 | |||
829f506732 | |||
727f92f75d | |||
4e6d237e54 | |||
0599d6f7bc | |||
b81631e737 | |||
acf8d49829 | |||
60b887090c | |||
bcb539f6de | |||
20237d2f1d | |||
f4b9d664f2 | |||
dd01329408 | |||
becab76c4a | |||
bdda57c23c | |||
1da1c3ca4d | |||
933bf53803 | |||
41f7db792f |
@ -29,7 +29,7 @@ var_4_win: &cache_key_win_fallback v5-angular-win-node-12-
|
||||
|
||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||
var_5: &components_repo_unit_tests_cache_key v5-angular-components-97a7e2babbccd3dc58e7b3364004e45ed5bd9968
|
||||
var_5: &components_repo_unit_tests_cache_key v5-angular-components-2ec7254f88c4865e0de251f74c27e64c9d00d40a
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v5-angular-components-
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||
@ -65,8 +65,8 @@ var_10: &only_on_master
|
||||
# (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 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
||||
# version that is compatible with the Chrome version in the image.
|
||||
# `--versions.chrome` arg in `integration/bazel-schematics/test.sh` specifies a
|
||||
# ChromeDriver version that is compatible with the Chrome version in the image.
|
||||
executors:
|
||||
default-executor:
|
||||
parameters:
|
||||
@ -120,25 +120,32 @@ commands:
|
||||
- attach_workspace:
|
||||
at: *workspace_location
|
||||
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
overwrite_yarn:
|
||||
description: Overwrite yarn with our own version
|
||||
# Install shared libs used by Chrome that is either provisioned by
|
||||
# rules_webtesting or by puppeteer.
|
||||
install_chrome_libs:
|
||||
description: Install shared Chrome libs
|
||||
steps:
|
||||
- run:
|
||||
name: Overwrite yarn
|
||||
name: Install shared Chrome libs
|
||||
command: |
|
||||
localYarnPath=`node ./.circleci/get-vendored-yarn-path.js`
|
||||
sudo chmod a+x $localYarnPath
|
||||
sudo ln -fs $localYarnPath /usr/local/bin/yarn
|
||||
- run: node --version
|
||||
- run: yarn --version
|
||||
sudo apt-get update
|
||||
# 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)
|
||||
# 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
|
||||
# circleci/node:x.x.x-browsers image.
|
||||
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
||||
|
||||
# Initializes the CI environment by setting up common environment variables.
|
||||
init_environment:
|
||||
description: Initializing environment (setting up variables, overwriting Yarn)
|
||||
description: Initializing environment (setting up variables)
|
||||
steps:
|
||||
- run: ./.circleci/env.sh
|
||||
- overwrite_yarn
|
||||
- run:
|
||||
name: Set up environment
|
||||
environment:
|
||||
CIRCLE_GIT_BASE_REVISION: << pipeline.git.base_revision >>
|
||||
CIRCLE_GIT_REVISION: << pipeline.git.revision >>
|
||||
command: ./.circleci/env.sh
|
||||
- run:
|
||||
# Configure git as the CircleCI `checkout` command does.
|
||||
# This is needed because we only checkout on the setup job.
|
||||
@ -150,6 +157,27 @@ commands:
|
||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||
git config --global gc.auto 0 || true
|
||||
|
||||
init_saucelabs_environment:
|
||||
description: Sets up a domain that resolves to the local host.
|
||||
steps:
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: |
|
||||
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
||||
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
||||
# `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not
|
||||
# resolve to anything on SauceLabs VMs ensures that such requests are always resolved
|
||||
# through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM).
|
||||
# More context can be found in: https://github.com/angular/angular/pull/35171.
|
||||
setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local"
|
||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||
- run:
|
||||
# Sets up a local domain in the machine's host file that resolves to the local
|
||||
# host. This domain is helpful in Saucelabs tests where devices are not able to
|
||||
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
||||
name: Setting up alias domain for local host.
|
||||
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
||||
|
||||
# Normally this would be an individual job instead of a command.
|
||||
# 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.
|
||||
@ -160,10 +188,6 @@ commands:
|
||||
- custom_attach_workspace
|
||||
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
|
||||
- run: ./.circleci/windows-env.ps1
|
||||
- run:
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
name: Overwrite yarn with our own version
|
||||
command: ./.circleci/windows-yarn-setup.ps1
|
||||
- run: node --version
|
||||
- run: yarn --version
|
||||
- restore_cache:
|
||||
@ -252,6 +276,7 @@ jobs:
|
||||
(echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'
|
||||
|
||||
- run: yarn gulp lint
|
||||
- run: node tools/pullapprove/verify.js
|
||||
|
||||
test:
|
||||
executor:
|
||||
@ -313,9 +338,7 @@ jobs:
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||
- init_saucelabs_environment
|
||||
- run:
|
||||
name: Run Bazel tests on Saucelabs
|
||||
# See /tools/saucelabs/README.md for more info
|
||||
@ -337,9 +360,7 @@ jobs:
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||
- init_saucelabs_environment
|
||||
- run:
|
||||
name: Run Bazel tests on Saucelabs
|
||||
# See /tools/saucelabs/README.md for more info
|
||||
@ -350,11 +371,11 @@ jobs:
|
||||
no_output_timeout: 20m
|
||||
|
||||
test_aio:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
executor: browsers-executor
|
||||
executor: default-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- 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).
|
||||
@ -377,11 +398,11 @@ jobs:
|
||||
- run: yarn --cwd aio redirects-test
|
||||
|
||||
deploy_aio:
|
||||
# Needed because before deploying the deploy-production script runs the PWA score tests.
|
||||
executor: browsers-executor
|
||||
executor: default-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run: yarn --cwd aio deploy-production
|
||||
@ -391,11 +412,11 @@ jobs:
|
||||
viewengine:
|
||||
type: boolean
|
||||
default: false
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
executor: browsers-executor
|
||||
executor: default-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Build aio (with local Angular packages)
|
||||
- run: yarn --cwd aio build-local<<# parameters.viewengine >>-with-viewengine<</ parameters.viewengine >>-ci
|
||||
# Run unit tests
|
||||
@ -425,13 +446,13 @@ jobs:
|
||||
type: boolean
|
||||
default: false
|
||||
executor:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
name: browsers-executor
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
parallelism: 5
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- when:
|
||||
@ -466,11 +487,11 @@ jobs:
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
test_aio_preview:
|
||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||
executor: browsers-executor
|
||||
executor: default-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run:
|
||||
name: Wait for preview and run tests
|
||||
@ -533,7 +554,8 @@ jobs:
|
||||
# See comments inside the integration/run_tests.sh script.
|
||||
integration_test:
|
||||
executor:
|
||||
# Needed because the integration tests expect Chrome to be installed (e.g cli-hello-world)
|
||||
# Needed because the integration/bazel-schematics test expects Chrome to be installed
|
||||
# TODO(gregmagolan): remove the dependency on local chrome from that test
|
||||
name: browsers-executor
|
||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||
# of memory. Together with the system under test, this can exhaust the RAM
|
||||
@ -609,22 +631,19 @@ jobs:
|
||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
||||
|
||||
aio_monitoring_stable:
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
executor: browsers-executor
|
||||
executor: default-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run:
|
||||
name: Check out `aio/` and yarn from the stable branch
|
||||
command: |
|
||||
git fetch origin $CI_STABLE_BRANCH
|
||||
git checkout --force origin/$CI_STABLE_BRANCH -- aio/ .yarn/ .yarnrc
|
||||
# Overwrite yarn again to use the version from the checked out branch.
|
||||
- overwrite_yarn
|
||||
# Ignore yarn's engines check, because we checked out `aio/package.json` from the stable
|
||||
# branch and there could be a node version skew, which is acceptable in this monitoring job.
|
||||
# Ignore yarn's engines check, because we checked out `aio/package.json` from the stable
|
||||
# branch and there could be a node version skew, which is acceptable in this monitoring job.
|
||||
- run: yarn config set ignore-engines true
|
||||
- run:
|
||||
name: Run tests against https://angular.io/
|
||||
@ -635,12 +654,11 @@ jobs:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
aio_monitoring_next:
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
executor: browsers-executor
|
||||
executor: default-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- run:
|
||||
name: Run tests against https://next.angular.io/
|
||||
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||
@ -659,11 +677,7 @@ jobs:
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: |
|
||||
setPublicVar KARMA_JS_BROWSERS $(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
|
||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||
- init_saucelabs_environment
|
||||
- run:
|
||||
name: Starting Saucelabs tunnel service
|
||||
command: ./tools/saucelabs/sauce-service.sh run
|
||||
@ -675,7 +689,11 @@ jobs:
|
||||
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
|
||||
name: Waiting for Saucelabs tunnel to connect
|
||||
command: ./tools/saucelabs/sauce-service.sh ready-wait
|
||||
- run: yarn karma start ./karma-js.conf.js --single-run --browsers=${KARMA_JS_BROWSERS}
|
||||
- run:
|
||||
name: Running tests on Saucelabs.
|
||||
command: |
|
||||
browsers=$(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
|
||||
yarn karma start ./karma-js.conf.js --single-run --browsers=${browsers}
|
||||
- run:
|
||||
name: Stop Saucelabs tunnel service
|
||||
command: ./tools/saucelabs/sauce-service.sh stop
|
||||
@ -727,7 +745,7 @@ jobs:
|
||||
command: node scripts/ci/update-deps-to-dist-packages.js ${COMPONENTS_REPO_TMP_DIR}/package.json dist/packages-dist/
|
||||
- run:
|
||||
name: "Running `angular/components` unit tests"
|
||||
command: ./scripts/ci/run_angular_components_unit_tests.sh | exit 0
|
||||
command: ./scripts/ci/run_angular_components_unit_tests.sh
|
||||
|
||||
test_zonejs:
|
||||
executor:
|
||||
@ -871,10 +889,9 @@ workflows:
|
||||
- build-npm-packages
|
||||
- build-ivy-npm-packages
|
||||
- legacy-unit-tests-saucelabs
|
||||
# FIXME - uncomment this job once https://github.com/angular/components/pull/18355 lands
|
||||
# - components-repo-unit-tests:
|
||||
# requires:
|
||||
# - build-npm-packages
|
||||
- components-repo-unit-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_zonejs:
|
||||
requires:
|
||||
- setup
|
||||
|
@ -19,19 +19,10 @@ setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
||||
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
||||
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
|
||||
# ChromeDriver version compatible with the Chrome version included in the docker image used in
|
||||
# `.circleci/config.yml`. See http://chromedriver.chromium.org/downloads for a list of versions.
|
||||
# This variable is intended to be passed as an arg to the `webdriver-manager update` command (e.g.
|
||||
# `"postinstall": "webdriver-manager update $CI_CHROMEDRIVER_VERSION_ARG"`).
|
||||
setPublicVar CI_CHROMEDRIVER_VERSION_ARG "--versions.chrome 79.0.3945.130";
|
||||
setPublicVar CI_COMMIT "$CIRCLE_SHA1";
|
||||
# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun
|
||||
# workflows of such builds).
|
||||
# NOTE: With [CircleCI Pipelines](https://circleci.com/docs/2.0/build-processing) enabled,
|
||||
# `CIRCLE_COMPARE_URL` is no longer available and the commit range cannot be reliably
|
||||
# detected. Fall back to only considering the last commit (which is accurate in the majority
|
||||
# of cases for push builds).
|
||||
setPublicVar CI_COMMIT_RANGE "`[[ ${CIRCLE_PR_NUMBER:-false} != false ]] && echo "" || echo "$CIRCLE_SHA1~1...$CIRCLE_SHA1"`";
|
||||
setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION";
|
||||
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
||||
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
||||
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
||||
@ -65,6 +56,7 @@ setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CI
|
||||
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
|
||||
setPublicVar SAUCE_READY_FILE_TIMEOUT 120
|
||||
|
||||
|
||||
####################################################################################################
|
||||
# Define environment variables for the `angular/components` repo unit tests job.
|
||||
####################################################################################################
|
||||
@ -76,7 +68,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "97a7e2babbccd3dc58e7b3364004e45ed5bd9968"
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "2ec7254f88c4865e0de251f74c27e64c9d00d40a"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
@ -1,166 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* **Usage:**
|
||||
* ```
|
||||
* node get-commit-range <build-number> [<compare-url> [<circle-token>]]
|
||||
* ```
|
||||
*
|
||||
* Returns the commit range, either extracting it from `compare-url` (if defined), which is of the
|
||||
* format of the `CIRCLE_COMPARE_URL` environment variable, or by retrieving the equivalent of
|
||||
* `CIRCLE_COMPARE_URL` for jobs that are part of a rerun workflow and extracting it from there.
|
||||
*
|
||||
* > !!! WARNING !!!
|
||||
* > !!
|
||||
* > !! When [CircleCI Pipelines](https://circleci.com/docs/2.0/build-processing) is enabled, the
|
||||
* > !! `CIRCLE_COMPARE_URL` environment variable is not available at all and this script does not
|
||||
* > !! work.
|
||||
* > !!!!!!!!!!!!!!!
|
||||
*
|
||||
* **Context:**
|
||||
* CircleCI sets the `CIRCLE_COMPARE_URL` environment variable (from which we can extract the commit
|
||||
* range) on push builds (a.k.a. non-PR, non-scheduled builds). Yet, when a workflow is rerun
|
||||
* (either from the beginning or from failed jobs) - e.g. when a job flakes - CircleCI does not set
|
||||
* the `CIRCLE_COMPARE_URL`.
|
||||
*
|
||||
* **Implementation details:**
|
||||
* This script relies on the fact that all rerun workflows share the same CircleCI workspace and the
|
||||
* (undocumented) fact that the workspace ID happens to be the same as the workflow ID that first
|
||||
* created it.
|
||||
*
|
||||
* For example, for a job on push build workflows, the CircleCI API will return data that look like:
|
||||
* ```js
|
||||
* {
|
||||
* compare: 'THE_COMPARE_URL_WE_ARE_LOOKING_FOR',
|
||||
* //...
|
||||
* previous: {
|
||||
* // ...
|
||||
* build_num: 12345,
|
||||
* },
|
||||
* //...
|
||||
* workflows: {
|
||||
* //...
|
||||
* workflow_id: 'SOME_ID_A',
|
||||
* workspace_id: 'SOME_ID_A', // Same as `workflow_id`.
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* If the workflow is rerun, the data for jobs on the new workflow will look like:
|
||||
* ```js
|
||||
* {
|
||||
* compare: null, // ¯\_(ツ)_/¯
|
||||
* //...
|
||||
* previous: {
|
||||
* // ...
|
||||
* build_num: 23456,
|
||||
* },
|
||||
* //...
|
||||
* workflows: {
|
||||
* //...
|
||||
* workflow_id: 'SOME_ID_B',
|
||||
* workspace_id: 'SOME_ID_A', // Different from current `workflow_id`.
|
||||
* // Same as original `workflow_id`. \o/
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This script uses the `previous.build_num` (which points to the previous build number on the same
|
||||
* branch) to traverse the jobs backwards, until it finds a job from the original workflow. Such a
|
||||
* job (if found) should also contain the compare URL.
|
||||
*
|
||||
* **NOTE 1:**
|
||||
* This is only useful on workflows which are created by rerunning a workflow for which
|
||||
* `CIRCLE_COMPARE_URL` was defined.
|
||||
*
|
||||
* **NOTE 2:**
|
||||
* The `circleToken` will be used for CircleCI API requests if provided, but it is not needed for
|
||||
* accessing the read-only endpoints that we need (as long as the current project is FOSS and the
|
||||
* corresponding setting is turned on in "Advanced Settings" in the project dashboard).
|
||||
*
|
||||
* ---
|
||||
* Inspired by https://circleci.com/orbs/registry/orb/iynere/compare-url
|
||||
* (source code: https://github.com/iynere/compare-url-orb).
|
||||
*
|
||||
* We are not using the `compare-url` orb for the following reasons:
|
||||
* 1. (By looking at the code) it would only work if the rerun workflow is the latest workflow on
|
||||
* the branch (which is not guaranteed to be true).
|
||||
* 2. It is less efficient (e.g. makes unnecessary CircleCI API requests for builds on different
|
||||
* branches, installs extra dependencies, persists files to the workspace (as a means of passing
|
||||
* the result to the calling job), etc.).
|
||||
* 3. It is slightly more complicated to setup and consume than our own script.
|
||||
* 4. Its implementation is more complicated than needed for our usecase (e.g. handles different git
|
||||
* providers, handles newly created branches, etc.).
|
||||
*/
|
||||
|
||||
// Imports
|
||||
const {get: httpsGet} = require('https');
|
||||
|
||||
// Constants
|
||||
const API_URL_BASE = 'https://circleci.com/api/v1.1/project/github/angular/angular';
|
||||
const COMPARE_URL_RE = /^.*\/([0-9a-f]+\.\.\.[0-9a-f]+)$/i;
|
||||
|
||||
// Run
|
||||
_main(process.argv.slice(2));
|
||||
|
||||
// Helpers
|
||||
async function _main([buildNumber, compareUrl = '', circleToken = '']) {
|
||||
try {
|
||||
if (!buildNumber || isNaN(buildNumber)) {
|
||||
throw new Error(
|
||||
'Missing or invalid arguments.\n' +
|
||||
'Expected: buildNumber (number), compareUrl? (string), circleToken? (string)');
|
||||
}
|
||||
|
||||
if (!compareUrl) {
|
||||
compareUrl = await getCompareUrl(buildNumber, circleToken);
|
||||
}
|
||||
|
||||
const commitRangeMatch = COMPARE_URL_RE.exec(compareUrl)
|
||||
const commitRange = commitRangeMatch ? commitRangeMatch[1] : '';
|
||||
|
||||
console.log(commitRange);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildInfo(buildNumber, circleToken) {
|
||||
console.error(`BUILD ${buildNumber}`);
|
||||
const url = `${API_URL_BASE}/${buildNumber}?circle-token=${circleToken}`;
|
||||
return getJson(url);
|
||||
}
|
||||
|
||||
async function getCompareUrl(buildNumber, circleToken) {
|
||||
let info = await getBuildInfo(buildNumber, circleToken);
|
||||
const targetWorkflowId = info.workflows.workspace_id;
|
||||
|
||||
while (info.workflows.workflow_id !== targetWorkflowId) {
|
||||
info = await getBuildInfo(info.previous.build_num, circleToken);
|
||||
}
|
||||
|
||||
return info.compare || '';
|
||||
}
|
||||
|
||||
function getJson(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const opts = {headers: {Accept: 'application/json'}};
|
||||
const onResponse = res => {
|
||||
const statusCode = res.statusCode || -1;
|
||||
const isSuccess = (200 <= statusCode) && (statusCode < 400);
|
||||
let responseText = '';
|
||||
|
||||
res.
|
||||
on('error', reject).
|
||||
on('data', d => responseText += d).
|
||||
on('end', () => isSuccess ?
|
||||
resolve(JSON.parse(responseText)) :
|
||||
reject(`Error getting '${url}' (status ${statusCode}):\n${responseText}`));
|
||||
};
|
||||
|
||||
httpsGet(url, opts, onResponse).
|
||||
on('error', reject).
|
||||
end();
|
||||
});
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* **Usage:**
|
||||
* ```
|
||||
* node get-vendored-yarn-path
|
||||
* ```
|
||||
*
|
||||
* Returns the path to the vendored `yarn.js` script, so that it can be used for setting up yarn
|
||||
* aliases/symlinks and use the local, vendored yarn script instead of a globally installed one.
|
||||
*
|
||||
* **Context:**
|
||||
* We keep a version of yarn in the repo, at `third_party/github.com/yarnpkg/`. All CI jobs should
|
||||
* use that version for consistency (and easier updates). The path to the actual `yarn.js` script,
|
||||
* however, changes depending on the version (e.g. `third_party/github.com/yarnpkg/v1.21.1/...`).
|
||||
*
|
||||
* This script infers the correct path, so that we don't have to update the path in several places,
|
||||
* when we update the version of yarn in `third_party/github.com/yarnpkg/`.
|
||||
*/
|
||||
|
||||
const {readdirSync} = require('fs');
|
||||
const {normalize} = require('path');
|
||||
|
||||
const yarnDownloadDir = `${__dirname}/../third_party/github.com/yarnpkg/yarn/releases/download`;
|
||||
const yarnVersionSubdirs = readdirSync(yarnDownloadDir);
|
||||
|
||||
// Based on our current process, there should be exactly one sub-directory inside
|
||||
// `vendoredYarnDownloadDir` at all times. Throw, if that is not the case.
|
||||
if (yarnVersionSubdirs.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected exactly 1 yarn version in '${yarnDownloadDir}', but found ` +
|
||||
`${yarnVersionSubdirs.length}: ${yarnVersionSubdirs.join(', ')}`);
|
||||
}
|
||||
|
||||
console.log(normalize(`${yarnDownloadDir}/${yarnVersionSubdirs[0]}/bin/yarn.js`));
|
@ -1,14 +0,0 @@
|
||||
# Use our local, vendored yarn in the global `yarn` command.
|
||||
$globalYarnDir = "$HOME\AppData\Roaming\yarn"
|
||||
$localYarnPath = & ${Env:ProgramFiles}\nodejs\node.exe ".\.circleci\get-vendored-yarn-path.js"
|
||||
|
||||
# Create a directory to put the yarn PowerShell script.
|
||||
New-Item -Path "$globalYarnDir" -ItemType "directory" >$null
|
||||
|
||||
# Create the yarn PowerShell script (using the inferred path to the local yarn script).
|
||||
Get-Content -Path ".\.circleci\windows-yarn.ps1.template" |
|
||||
%{$_ -replace "{{ LOCAL_YARN_PATH_PLACEHOLDER }}", "$localYarnPath"} |
|
||||
Add-Content -Path "$globalYarnDir\yarn.ps1"
|
||||
|
||||
# Add the directory containing the yarn PowerShell script to `PATH`.
|
||||
Add-Content -Path $profile -Value ('$Env:path = "{0};" + $Env:path' -f $globalYarnDir)
|
@ -1,15 +0,0 @@
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "{{ LOCAL_YARN_PATH_PLACEHOLDER }}" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "{{ LOCAL_YARN_PATH_PLACEHOLDER }}" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -108,6 +108,9 @@ pullapprove_conditions:
|
||||
- condition: "'PR state: WIP' not in labels"
|
||||
unmet_status: pending
|
||||
explanation: "Waiting to send reviews as PR is WIP"
|
||||
- condition: "not draft"
|
||||
unmet_status: pending
|
||||
explanation: "Waiting to send reviews as PR is in draft"
|
||||
|
||||
|
||||
groups:
|
||||
@ -961,6 +964,7 @@ groups:
|
||||
'tools/testing/**',
|
||||
'tools/ts-api-guardian/**',
|
||||
'tools/tslint/**',
|
||||
'tools/utils/**',
|
||||
'tools/validate-commit-message/**',
|
||||
'tools/yarn/**',
|
||||
'tools/*',
|
||||
@ -978,26 +982,6 @@ groups:
|
||||
- ~framework-global-approvers
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Material CI
|
||||
# =========================================================
|
||||
material-ci:
|
||||
conditions:
|
||||
- >
|
||||
contains_any_globs(files, [
|
||||
'tools/components-repo-ci/**'
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- alxhub
|
||||
- AndrewKushnir
|
||||
- kara
|
||||
- mhevery
|
||||
- pkozlowski-opensource
|
||||
teams:
|
||||
- ~framework-global-approvers
|
||||
|
||||
|
||||
# =========================================================
|
||||
# Public API
|
||||
# =========================================================
|
||||
|
1945
CHANGELOG.md
1945
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
12
WORKSPACE
12
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
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "6bcef105e75cac3c5f8212e0d0431b6ec1aaa1963e093b0091474ab98ecf29d2",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.2.2/rules_nodejs-1.2.2.tar.gz"],
|
||||
sha256 = "b6670f9f43faa66e3009488bbd909bc7bc46a5a9661a33f6bc578068d1837f37",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.3.0/rules_nodejs-1.3.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the bazel version and download npm dependencies
|
||||
@ -18,6 +18,7 @@ load("@build_bazel_rules_nodejs//:index.bzl", "check_bazel_version", "check_rule
|
||||
# Bazel version must be at least the following version because:
|
||||
# - 0.26.0 managed_directories feature added which is required for nodejs rules 0.30.0
|
||||
# - 0.27.0 has a fix for managed_directories after `rm -rf node_modules`
|
||||
# - 2.1.0 feature added to honor .bazelignore in external repositories
|
||||
check_bazel_version(
|
||||
message = """
|
||||
You no longer need to install Bazel on your machine.
|
||||
@ -26,10 +27,10 @@ Try running `yarn bazel` instead.
|
||||
(If you did run that, check that you've got a fresh `yarn install`)
|
||||
|
||||
""",
|
||||
minimum_bazel_version = "2.0.0",
|
||||
minimum_bazel_version = "2.1.0",
|
||||
)
|
||||
|
||||
check_rules_nodejs_version(minimum_version_string = "1.2.2")
|
||||
check_rules_nodejs_version(minimum_version_string = "1.3.0")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
@ -40,9 +41,6 @@ node_repositories(
|
||||
},
|
||||
node_version = "12.14.1",
|
||||
package_json = ["//:package.json"],
|
||||
# Label needs to explicitly specify the current workspace name because otherwise Bazel does
|
||||
# not provide all needed data (like "workspace_root") to the repository context.
|
||||
vendored_yarn = "@angular//:third_party/github.com/yarnpkg/yarn/releases/download/v1.21.1",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
|
@ -5,7 +5,8 @@
|
||||
"packageManager": "yarn",
|
||||
"warnings": {
|
||||
"typescriptMismatch": false
|
||||
}
|
||||
},
|
||||
"analytics": false
|
||||
},
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
@ -192,4 +193,4 @@
|
||||
}
|
||||
},
|
||||
"defaultProject": "site"
|
||||
}
|
||||
}
|
@ -25,23 +25,12 @@ describe('Attribute binding example', function () {
|
||||
});
|
||||
|
||||
it('should display a blue div with a red border', function () {
|
||||
expect(element.all(by.css('div')).get(4).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||
expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)');
|
||||
});
|
||||
|
||||
it('should display a div with replaced classes', function () {
|
||||
expect(element.all(by.css('div')).get(5).getAttribute('class')).toEqual('new-class');
|
||||
});
|
||||
|
||||
it('should display four buttons', function() {
|
||||
let redButton = element.all(by.css('button')).get(1);
|
||||
let saveButton = element.all(by.css('button')).get(2);
|
||||
let bigButton = element.all(by.css('button')).get(3);
|
||||
let smallButton = element.all(by.css('button')).get(4);
|
||||
|
||||
expect(redButton.getCssValue('color')).toEqual('rgba(255, 0, 0, 1)');
|
||||
expect(saveButton.getCssValue('background-color')).toEqual('rgba(0, 255, 255, 1)');
|
||||
expect(bigButton.getText()).toBe('Big');
|
||||
expect(smallButton.getText()).toBe('Small');
|
||||
it('should display a div with many classes', function () {
|
||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special');
|
||||
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -27,39 +27,41 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Class binding</h2>
|
||||
<h2>Styling precedence</h2>
|
||||
|
||||
<!-- #docregion is-special -->
|
||||
<h3>toggle the "special" class on/off with a property:</h3>
|
||||
<div [class.special]="isSpecial">The class binding is special.</div>
|
||||
<!-- #docregion basic-specificity -->
|
||||
<h3>Basic specificity</h3>
|
||||
|
||||
<h3>binding to class.special overrides the class attribute:</h3>
|
||||
<div class="special" [class.special]="!isSpecial">This one is not so special.</div>
|
||||
<!-- The `class.special` binding will override any value for the `special` class in `classExpr`. -->
|
||||
<div [class.special]="isSpecial" [class]="classExpr">Some text.</div>
|
||||
|
||||
<h3>Using the bind- syntax:</h3>
|
||||
<div bind-class.special="isSpecial">This class binding is special too.</div>
|
||||
<!-- #enddocregion is-special -->
|
||||
<!-- The `style.color` binding will override any value for the `color` property in `styleExpr`. -->
|
||||
<div [style.color]="color" [style]="styleExpr">Some text.</div>
|
||||
<!-- #enddocregion basic-specificity -->
|
||||
|
||||
<!-- #docregion add-class -->
|
||||
<h3>Add a class:</h3>
|
||||
<div class="item clearance special" [class.item-clearance]="itemClearance">Add another class</div>
|
||||
<!-- #enddocregion add-class -->
|
||||
<!-- #docregion source-specificity -->
|
||||
<h3>Source specificity</h3>
|
||||
|
||||
<!-- #docregion class-override -->
|
||||
<h3>Overwrite all existing classes with a new class:</h3>
|
||||
<div class="item clearance special" [attr.class]="resetClasses">Reset all classes at once</div>
|
||||
<!-- #enddocregion class-override -->
|
||||
<!-- The `class.special` template binding will override any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
||||
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>
|
||||
|
||||
<hr />
|
||||
<!-- The `style.color` template binding will override any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
||||
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>
|
||||
<!-- #enddocregion source-specificity -->
|
||||
|
||||
<h2>Style binding</h2>
|
||||
<!-- #docregion dynamic-priority -->
|
||||
<h3>Dynamic vs static</h3>
|
||||
|
||||
<!-- If `classExpr` has a value for the `special` class, this value will override the `class="special"` below -->
|
||||
<div class="special" [class]="classExpr">Some text.</div>
|
||||
|
||||
<!-- If `styleExpr` has a value for the `color` property, this value will override the `style="color: blue"` below -->
|
||||
<div style="color: blue" [style]="styleExpr">Some text.</div>
|
||||
|
||||
<!-- #enddocregion dynamic-priority -->
|
||||
|
||||
<!-- #docregion style-delegation -->
|
||||
<comp-with-host-binding dirWithHostBinding></comp-with-host-binding>
|
||||
<!-- #enddocregion style-delegation -->
|
||||
|
||||
<!-- #docregion style-binding-->
|
||||
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
|
||||
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
|
||||
<!-- #enddocregion style-binding -->
|
||||
|
||||
<!-- #docregion style-binding-condition-->
|
||||
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
|
||||
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
|
||||
<!-- #enddocregion style-binding-condition-->
|
||||
|
@ -8,8 +8,8 @@ import { Component } from '@angular/core';
|
||||
export class AppComponent {
|
||||
actionName = 'Go for it';
|
||||
isSpecial = true;
|
||||
itemClearance = true;
|
||||
resetClasses = 'new-class';
|
||||
canSave = true;
|
||||
|
||||
classExpr = 'special clearance';
|
||||
styleExpr = 'color: red';
|
||||
color = 'blue';
|
||||
}
|
||||
|
@ -3,11 +3,13 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { CompWithHostBindingComponent } from './comp-with-host-binding.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
AppComponent,
|
||||
CompWithHostBindingComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'comp-with-host-binding',
|
||||
template: 'I am a component!',
|
||||
host: {
|
||||
'[class.special]': 'isSpecial',
|
||||
'[style.color]': 'color',
|
||||
'[style.width]': 'width'
|
||||
}
|
||||
})
|
||||
export class CompWithHostBindingComponent {
|
||||
isSpecial = false;
|
||||
color = 'green';
|
||||
width = '200px';
|
||||
}
|
@ -4,9 +4,10 @@
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export class HeroContactComponent {
|
||||
|
||||
@Host() // limit search for logger; hides the application-wide logger
|
||||
@Optional() // ok if the logger doesn't exist
|
||||
private loggerService: LoggerService
|
||||
private loggerService?: LoggerService
|
||||
// #enddocregion ctor-params
|
||||
) {
|
||||
if (loggerService) {
|
||||
|
@ -52,7 +52,7 @@ const templateC = `
|
||||
export class CarolComponent {
|
||||
name = 'Carol';
|
||||
// #docregion carol-ctor
|
||||
constructor( @Optional() public parent: Parent ) { }
|
||||
constructor( @Optional() public parent?: Parent ) { }
|
||||
// #enddocregion carol-ctor
|
||||
}
|
||||
// #enddocregion carol-class
|
||||
@ -64,7 +64,7 @@ export class CarolComponent {
|
||||
})
|
||||
export class ChrisComponent {
|
||||
name = 'Chris';
|
||||
constructor( @Optional() public parent: Parent ) { }
|
||||
constructor( @Optional() public parent?: Parent ) { }
|
||||
}
|
||||
|
||||
////// Craig ///////////
|
||||
@ -81,7 +81,7 @@ export class ChrisComponent {
|
||||
</div>`
|
||||
})
|
||||
export class CraigComponent {
|
||||
constructor( @Optional() public alex: Base ) { }
|
||||
constructor( @Optional() public alex?: Base ) { }
|
||||
}
|
||||
// #enddocregion craig
|
||||
|
||||
@ -105,7 +105,7 @@ const templateB = `
|
||||
export class BarryComponent implements Parent {
|
||||
name = 'Barry';
|
||||
// #docregion barry-ctor
|
||||
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
|
||||
constructor( @SkipSelf() @Optional() public parent?: Parent ) { }
|
||||
// #enddocregion barry-ctor
|
||||
}
|
||||
// #enddocregion barry
|
||||
@ -117,7 +117,7 @@ export class BarryComponent implements Parent {
|
||||
})
|
||||
export class BobComponent implements Parent {
|
||||
name = 'Bob';
|
||||
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
|
||||
constructor( @SkipSelf() @Optional() public parent?: Parent ) { }
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -129,7 +129,7 @@ export class BobComponent implements Parent {
|
||||
})
|
||||
export class BethComponent implements Parent {
|
||||
name = 'Beth';
|
||||
constructor( @SkipSelf() @Optional() public parent: Parent ) { }
|
||||
constructor( @SkipSelf() @Optional() public parent?: Parent ) { }
|
||||
}
|
||||
|
||||
///////// A - Grandparent //////
|
||||
@ -200,7 +200,7 @@ export class AliceComponent implements Parent
|
||||
</div>`
|
||||
})
|
||||
export class CathyComponent {
|
||||
constructor( @Optional() public alex: AlexComponent ) { }
|
||||
constructor( @Optional() public alex?: AlexComponent ) { }
|
||||
}
|
||||
// #enddocregion cathy
|
||||
|
||||
|
@ -4,9 +4,10 @@
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ let some_message = 'Hello from the injected logger';
|
||||
export class Provider10Component implements OnInit {
|
||||
log: string;
|
||||
// #docregion provider-10-ctor
|
||||
constructor(@Optional() private logger: Logger) {
|
||||
constructor(@Optional() private logger?: Logger) {
|
||||
if (this.logger) {
|
||||
this.logger.log(some_message);
|
||||
}
|
||||
|
@ -39,10 +39,10 @@ export class CartComponent implements OnInit {
|
||||
// #enddocregion props-services
|
||||
onSubmit(customerData) {
|
||||
// Process checkout data here
|
||||
console.warn('Your order has been submitted', customerData);
|
||||
|
||||
this.items = this.cartService.clearCart();
|
||||
this.checkoutForm.reset();
|
||||
|
||||
console.warn('Your order has been submitted', customerData);
|
||||
}
|
||||
// #docregion props-services, inject-form-builder, checkout-form, checkout-form-group
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ export class ProductDetailsComponent implements OnInit {
|
||||
// #enddocregion props-methods, get-product
|
||||
// #docregion add-to-cart
|
||||
addToCart(product) {
|
||||
window.alert('Your product has been added to the cart!');
|
||||
this.cartService.addToCart(product);
|
||||
window.alert('Your product has been added to the cart!');
|
||||
}
|
||||
// #docregion props-methods, get-product, inject-cart-service
|
||||
}
|
||||
|
@ -5,9 +5,10 @@
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import { UserServiceConfig } from './user.service';
|
||||
})
|
||||
export class GreetingModule {
|
||||
// #docregion ctor
|
||||
constructor (@Optional() @SkipSelf() parentModule: GreetingModule) {
|
||||
constructor (@Optional() @SkipSelf() parentModule?: GreetingModule) {
|
||||
if (parentModule) {
|
||||
throw new Error(
|
||||
'GreetingModule is already loaded. Import it in the AppModule only');
|
||||
|
@ -15,7 +15,7 @@ export class UserService {
|
||||
private _userName = 'Sherlock Holmes';
|
||||
|
||||
// #docregion ctor
|
||||
constructor(@Optional() config: UserServiceConfig) {
|
||||
constructor(@Optional() config?: UserServiceConfig) {
|
||||
if (config) { this._userName = config.userName; }
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
@ -24,7 +24,7 @@ export class ChildComponent {
|
||||
// inspector is in the content.
|
||||
|
||||
|
||||
// constructor( public flower: FlowerService, @Optional() @Host() public animal: AnimalService) { }
|
||||
// constructor( public flower: FlowerService, @Optional() @Host() public animal?: AnimalService) { }
|
||||
|
||||
// Comment out the above constructor and alternately
|
||||
// uncomment the two following constructors to see the
|
||||
@ -32,11 +32,11 @@ export class ChildComponent {
|
||||
|
||||
// constructor(
|
||||
// @Host() public animal : AnimalService,
|
||||
// @Host() @Optional() public flower : FlowerService) { }
|
||||
// @Host() @Optional() public flower ?: FlowerService) { }
|
||||
|
||||
// constructor(
|
||||
// @SkipSelf() @Host() public animal : AnimalService,
|
||||
// @SkipSelf() @Host() @Optional() public flower : FlowerService) { }
|
||||
// @SkipSelf() @Host() @Optional() public flower ?: FlowerService) { }
|
||||
|
||||
// #docregion provide-animal-service
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { FlowerService } from '../flower.service';
|
||||
})
|
||||
export class HostComponent {
|
||||
// use @Host() in the constructor when injecting the service
|
||||
constructor(@Host() @Optional() public flower: FlowerService) { }
|
||||
constructor(@Host() @Optional() public flower?: FlowerService) { }
|
||||
|
||||
}
|
||||
// #enddocregion host-component
|
||||
|
@ -9,7 +9,7 @@ import { OptionalService } from '../optional.service';
|
||||
|
||||
// #docregion optional-component
|
||||
export class OptionalComponent {
|
||||
constructor(@Optional() public optional: OptionalService) {}
|
||||
constructor(@Optional() public optional?: OptionalService) {}
|
||||
}
|
||||
// #enddocregion optional-component
|
||||
|
||||
|
@ -8,7 +8,7 @@ import { LeafService } from '../leaf.service';
|
||||
styleUrls: ['./self-no-data.component.css']
|
||||
})
|
||||
export class SelfNoDataComponent {
|
||||
constructor(@Self() @Optional() public leaf: LeafService) { }
|
||||
constructor(@Self() @Optional() public leaf?: LeafService) { }
|
||||
}
|
||||
|
||||
// #enddocregion self-no-data-component
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"projectType": "service-worker",
|
||||
"e2e": [
|
||||
{"cmd": "yarn", "args": ["e2e", "--no-webdriver-update", "--port={PORT}"]},
|
||||
{"cmd": "yarn", "args": ["e2e", "--protractor-config=e2e/protractor-puppeteer.conf.js", "--no-webdriver-update", "--port={PORT}"]},
|
||||
{"cmd": "yarn", "args": ["build", "--prod"]},
|
||||
{"cmd": "node", "args": ["--eval", "assert(fs.existsSync('./dist/ngsw.json'), 'ngsw.json is missing')"]},
|
||||
{"cmd": "node", "args": ["--eval", "assert(fs.existsSync('./dist/ngsw-worker.js'), 'ngsw-worker.js is missing')"]},
|
||||
|
@ -15,7 +15,7 @@ import { throwIfAlreadyLoaded } from './module-import-guard';
|
||||
providers: [LoggerService]
|
||||
})
|
||||
export class CoreModule {
|
||||
constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
|
||||
constructor( @Optional() @SkipSelf() parentModule?: CoreModule) {
|
||||
throwIfAlreadyLoaded(parentModule, 'CoreModule');
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ export class TestViewProvidersComponent {
|
||||
export class ExternalTemplateComponent implements OnInit {
|
||||
serviceValue: string;
|
||||
|
||||
constructor(@Optional() private service: ValueService) { }
|
||||
constructor(@Optional() private service?: ValueService) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.service) { this.serviceValue = this.service.getValue(); }
|
||||
|
@ -5,9 +5,9 @@ import { Observable } from 'rxjs';
|
||||
class DummyHeroesComponent {
|
||||
|
||||
heroes: Observable<Hero[]>;
|
||||
|
||||
// #docregion ctor
|
||||
constructor(private heroService: HeroService) {}
|
||||
|
||||
// #enddocregion ctor
|
||||
// #docregion getHeroes
|
||||
getHeroes(): void {
|
||||
// #docregion get-heroes
|
||||
|
@ -5,8 +5,8 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { Hero } from '../hero';
|
||||
// #docregion hero-service-import
|
||||
import { HeroService } from '../hero.service';
|
||||
import { MessageService } from '../message.service';
|
||||
// #enddocregion hero-service-import
|
||||
import { MessageService } from '../message.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-heroes',
|
||||
|
@ -21,7 +21,7 @@ export class HeroService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private messageService: MessageService,
|
||||
@Optional() @Inject(APP_BASE_HREF) origin: string) {
|
||||
@Optional() @Inject(APP_BASE_HREF) origin?: string) {
|
||||
this.heroesUrl = `${origin}${this.heroesUrl}`;
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
@ -6,7 +6,7 @@ The Angular [ahead-of-time (AOT) compiler](guide/glossary#aot) converts your Ang
|
||||
|
||||
This guide explains how to specify metadata and apply available compiler options to compile your applications efficiently using the AOT compiler.
|
||||
|
||||
<div class="alert is-helpful"
|
||||
<div class="alert is-helpful">
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo">Watch compiler author Tobias Bosch explain the Angular compiler</a> at AngularConnect 2016.
|
||||
|
||||
@ -570,7 +570,7 @@ In the template type-checking phase, the Angular template compiler uses the Type
|
||||
Enable this phase explicitly by adding the compiler option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's `tsconfig.json`
|
||||
(see [Angular Compiler Options](guide/angular-compiler-options)).
|
||||
|
||||
<div class="alert is-helpful>
|
||||
<div class="alert is-helpful">
|
||||
|
||||
In [Angular Ivy](guide/ivy), the template type checker has been completely rewritten to be more capable as well as stricter, meaning it can catch a variety of new errors that the previous type checker would not detect.
|
||||
|
||||
|
@ -119,6 +119,7 @@ npm publish
|
||||
|
||||
If you've never published a package in npm before, you must create a user account. Read more in [Publishing npm Packages](https://docs.npmjs.com/getting-started/publishing-npm-packages).
|
||||
|
||||
|
||||
## Linked libraries
|
||||
|
||||
While working on a published library, you can use [npm link](https://docs.npmjs.com/cli/link) to avoid reinstalling the library on every build.
|
||||
@ -156,6 +157,7 @@ List all the peer dependencies that your library uses in the workspace TypeScrip
|
||||
|
||||
This mapping ensures that your library always loads the local copies of the modules it needs.
|
||||
|
||||
|
||||
## Using your own library in apps
|
||||
|
||||
You don't have to publish your library to the npm package manager in order to use it in your own apps, but you do have to build it first.
|
||||
@ -213,3 +215,14 @@ For this reason, an app that depends on a library should only use TypeScript pat
|
||||
TypeScript path mappings should *not* point to the library source `.ts` files.
|
||||
|
||||
</div>
|
||||
|
||||
{@a lib-assets}
|
||||
|
||||
### Managing library assets with ng-packagr
|
||||
|
||||
Starting with version 9.x of the [ng-packagr](https://github.com/ng-packagr/ng-packagr/blob/master/README.md) tool, you can configure the tool to automatically copy assets into your library package as part of the build process.
|
||||
You can use this feature when your library needs to publish optional theming files, Sass mixins, or documentation (like a changelog).
|
||||
|
||||
* Learn how to [copy assets into your library as part of the build](https://github.com/ng-packagr/ng-packagr/blob/master/docs/copy-assets.md).
|
||||
|
||||
* Learn more about how to use the tool to [embed assets in CSS](https://github.com/ng-packagr/ng-packagr/blob/master/docs/embed-assets-css.md).
|
||||
|
@ -593,7 +593,7 @@ Compare to [NgModule](#ngmodule).
|
||||
|
||||
## ngcc
|
||||
|
||||
Angular compatability compiler.
|
||||
Angular compatibility compiler.
|
||||
If you build your app using [Ivy](#ivy), but it depends on libraries have not been compiled with Ivy, the CLI uses `ngcc` to automatically update the dependent libraries to use Ivy.
|
||||
|
||||
|
||||
|
@ -320,7 +320,7 @@ Use `@SkipSelf()` with `@Optional()` to prevent an error if the value is `null`.
|
||||
|
||||
``` ts
|
||||
class Person {
|
||||
constructor(@Optional() @SkipSelf() parent: Person) {}
|
||||
constructor(@Optional() @SkipSelf() parent?: Person) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -17,6 +17,23 @@ If you're still seeing the errors, they are not specific to Ivy. In this case, y
|
||||
|
||||
If the errors are gone, switch back to Ivy by removing the changes to the `tsconfig.json` and review the list of expected changes below.
|
||||
|
||||
{@a payload-size-debugging}
|
||||
### Payload size debugging
|
||||
|
||||
If you notice that the size of your application's main bundle has increased with Ivy, you may want to check the following:
|
||||
|
||||
1. Verify that the components and `NgModules` that you want to be lazy loaded are only imported in lazy modules.
|
||||
Anything that you import outside lazy modules can end up in the main bundle.
|
||||
See more details in the original issue [here](https://github.com/angular/angular-cli/issues/16146#issuecomment-557559287).
|
||||
|
||||
1. Check that imported libraries have been marked side-effect-free.
|
||||
If your app imports from shared libraries that are meant to be free from side effects, add "sideEffects": false to their `package.json`.
|
||||
This will ensure that the libraries will be properly tree-shaken if they are imported but not directly referenced.
|
||||
See more details in the original issue [here](https://github.com/angular/angular-cli/issues/16799#issuecomment-580912090).
|
||||
|
||||
1. Projects not using Angular CLI will see a significant size regression unless they update their minifier settings and set compile-time constants `ngDevMode`, `ngI18nClosureMode` and `ngJitMode` to `false` (for Terser, please set these to `false` via [`global_defs` config option](https://terser.org/docs/api-reference.html#conditional-compilation)).
|
||||
Please note that these constants are not meant to be used by 3rd party library or application code as they are not part of our public api surface and might change in the future.
|
||||
|
||||
|
||||
{@a common-changes}
|
||||
### Changes you may see
|
||||
@ -64,4 +81,10 @@ If the errors are gone, switch back to Ivy by removing the changes to the `tscon
|
||||
|
||||
* `DebugElement.classes` returns `undefined` for classes that were added and then subsequently removed (previously, classes added and later removed would have a value of `false`).
|
||||
|
||||
* If selecting the native `<option>` element in a `<select>` where the `<option>`s are created via `*ngFor`, use the `[selected]` property of an `<option>` instead of binding to the `[value]` property of the `<select>` element (previously, you could bind to either.) [details](guide/ivy-compatibility-examples#select-value-binding)
|
||||
* If selecting the native `<option>` element in a `<select>` where the `<option>`s are created via `*ngFor`, use the `[selected]` property of an `<option>` instead of binding to the `[value]` property of the `<select>` element (previously, you could bind to either.) [details](guide/ivy-compatibility-examples#select-value-binding)
|
||||
|
||||
* Embedded views (such as ones created by `*ngFor`) are now inserted in front of anchor DOM comment node (e.g. `<!--ng-for-of-->`) rather than behind it as was the case previously.
|
||||
In most cases this does not have any impact on rendered DOM.
|
||||
In some cases (such as animations delaying the removal of an embedded view) any new embedded views will be inserted after the embedded view being animated away.
|
||||
This difference only last while the animation is active, and might alter the visual appearance of the animation.
|
||||
Once the animation is finished the resulting rendered DOM is identical to that rendered with View Engine.
|
||||
|
@ -54,17 +54,13 @@ See [Keeping Up-to-Date](guide/updating "Updating your projects") for more infor
|
||||
{@a previews}
|
||||
### Preview releases
|
||||
|
||||
We let you preview what's coming by providing Beta releases and Release Candidates (`rc`) for each major and minor release:
|
||||
We let you preview what's coming by providing "Next" and Release Candidates (`rc`) pre-releases for each major and minor release:
|
||||
|
||||
<!--
|
||||
* **Next:** The release that is under active development. The next release is indicated by a release tag appended with the `next` identifier, such as `8.1.0-next.0`. For the next version of the documentation, see [next.angular.io](https://next.angular.io).
|
||||
-->
|
||||
* **Next:** The release that is under active development and testing. The next release is indicated by a release tag appended with the `-next` identifier, such as `8.1.0-next.0`.
|
||||
|
||||
* **Beta:** A release that is under active development and testing. A Beta release is indicated by a release tag appended with the `beta` identifier, such as `8.0.0-beta.0`.
|
||||
* **Release candidate:** A release that is feature complete and in final testing. A release candidate is indicated by a release tag appended with the `-rc` identifier, such as version `8.1.0-rc.0`.
|
||||
|
||||
* **Release candidate:** A release that is feature complete and in final testing. A release candidate is indicated by a release tag appended with the `rc` identifier, such as version `8.1.0-rc`.
|
||||
|
||||
The next version of the documentation is available at [next.angular.io](https://next.angular.io). This includes any documentation for Beta or Release Candidate features and APIs.
|
||||
The latest `next` or `rc` pre-release version of the documentation is available at [next.angular.io](https://next.angular.io).
|
||||
|
||||
|
||||
{@a frequency}
|
||||
@ -72,36 +68,21 @@ The next version of the documentation is available at [next.angular.io](https://
|
||||
|
||||
We work toward a regular schedule of releases, so that you can plan and coordinate your updates with the continuing evolution of Angular.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Disclaimer: Dates are offered as general guidance and will be adjusted by us when necessary to ensure delivery of a high-quality platform.
|
||||
|
||||
</div>
|
||||
|
||||
In general, you can expect the following release cycle:
|
||||
|
||||
* A major release every 6 months
|
||||
|
||||
* 1-3 minor releases for each major release
|
||||
|
||||
* A patch release almost every week
|
||||
|
||||
This cadence of releases gives you access to new features as soon as they are ready, while maintaining the stability and reliability of the platform for production users.
|
||||
|
||||
|
||||
|
||||
{@a schedule}
|
||||
## Release schedule
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Disclaimer: The dates are offered as general guidance and may be adjusted by us when necessary to ensure delivery of a high-quality platform.
|
||||
|
||||
</div>
|
||||
|
||||
The following table contains our current target release dates for the next two major versions of Angular:
|
||||
|
||||
Date | Stable Release | Compatibility
|
||||
---------------------- | -------------- | -------------
|
||||
October/November 2019 | 9.0.0 | ^8.0.0
|
||||
May 2020 | 10.0.0 | ^9.0.0
|
||||
|
||||
Compatibility note: The primary goal of the backward compatibility promise is to ensure that changes in the core framework and tooling don't break the existing ecosystem of components and applications and don't put undue upgrade/migration burden on Angular application and component authors.
|
||||
* A patch release and pre-release (`next` or `rc`) build almost every week
|
||||
|
||||
This cadence of releases gives eager developers access to new features as soon as they are fully developed and pass through our code review and integration testing processes, while maintaining the stability and reliability of the platform for production users that prefer to receive features after they have been validated by Google and other developers that use the pre-release builds.
|
||||
|
||||
|
||||
|
||||
@ -142,7 +123,7 @@ To help ensure that you have sufficient time and a clear path to update, this is
|
||||
* **Announcement:** We announce deprecated APIs and features in the [change log](https://github.com/angular/angular/blob/master/CHANGELOG.md "Angular change log"). Deprecated APIs appear in the [documentation](api?status=deprecated) with ~~strikethrough.~~ When we announce a deprecation, we also announce a recommended update path. For convenience, [Deprecations](guide/deprecations) contains a summary of deprecated APIs and features.
|
||||
|
||||
|
||||
* **Deprecation period:** When an API or a feature is deprecated, it will still be present in the [next two major releases](#schedule). After that, deprecated APIs and features will be candidates for removal. A deprecation can be announced in any release, but the removal of a deprecated API or feature will happen only in major release. Until a deprecated API or feature is removed, it will be maintained according to the LTS support policy, meaning that only critical and security issues will be fixed.
|
||||
* **Deprecation period:** When an API or a feature is deprecated, it will still be present in the next two major releases. After that, deprecated APIs and features will be candidates for removal. A deprecation can be announced in any release, but the removal of a deprecated API or feature will happen only in major release. Until a deprecated API or feature is removed, it will be maintained according to the LTS support policy, meaning that only critical and security issues will be fixed.
|
||||
|
||||
|
||||
* **npm dependencies:** We only make npm dependency updates that require changes to your apps in a major release.
|
||||
|
@ -107,7 +107,7 @@ Use the `query()` method to find and animate elements within the current host co
|
||||
|
||||
Let's assume that we are routing from the *Home => About*.
|
||||
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="query" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts (Continuation from above)" region="query" language="typescript"></code-example>
|
||||
|
||||
The animation code does the following after styling the views:
|
||||
|
||||
|
@ -297,7 +297,7 @@ describes additional `NgFor` directive properties and context properties.
|
||||
|
||||
These microsyntax mechanisms are also available to you when you write your own structural directives.
|
||||
For example, microsyntax in Angular allows you to write `<div *ngFor="let item of items">{{item}}</div>`
|
||||
instead of `<ng-template ngFor [ngForOf]="items"><div>{{item}}</div></ng-template>`.
|
||||
instead of `<ng-template ngFor let-item [ngForOf]="items"><div>{{item}}</div></ng-template>`.
|
||||
The following sections provide detailed information on constraints, grammar,
|
||||
and translation of microsyntax.
|
||||
|
||||
|
@ -731,11 +731,11 @@ As you can see here, the `parentItem` in `AppComponent` is a string, which the `
|
||||
The previous simple example showed passing in a string. To pass in an object,
|
||||
the syntax and thinking are the same.
|
||||
|
||||
In this scenario, `ListItemComponent` is nested within `AppComponent` and the `item` property expects an object.
|
||||
In this scenario, `ListItemComponent` is nested within `AppComponent` and the `items` property expects an array of objects.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The `item` property is declared in the `ListItemComponent` with a type of `Item` and decorated with `@Input()`:
|
||||
The `items` property is declared in the `ListItemComponent` with a type of `Item` and decorated with `@Input()`:
|
||||
|
||||
<code-example path="property-binding/src/app/list-item/list-item.component.ts" region="item-input" header="src/app/list-item.component.ts"></code-example>
|
||||
|
||||
@ -748,7 +748,7 @@ specify a different item in `app.component.ts` so that the new item will render:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts"></code-example>
|
||||
|
||||
You just have to make sure, in this case, that you're supplying an object because that's the type of `item` and is what the nested component, `ListItemComponent`, expects.
|
||||
You just have to make sure, in this case, that you're supplying an array of objects because that's the type of `items` and is what the nested component, `ListItemComponent`, expects.
|
||||
|
||||
In this example, `AppComponent` specifies a different `item` object
|
||||
(`currentItem`) and passes it to the nested `ListItemComponent`. `ListItemComponent` was able to use `currentItem` because it matches what an `Item` object is according to `item.ts`. The `item.ts` file is where
|
||||
@ -892,58 +892,97 @@ Instead, you'd use property binding and write it like this:
|
||||
|
||||
### Class binding
|
||||
|
||||
Add and remove CSS class names from an element's `class` attribute with
|
||||
a **class binding**.
|
||||
|
||||
Here's how to set the attribute without binding in plain HTML:
|
||||
Here's how to set the `class` attribute without a binding in plain HTML:
|
||||
|
||||
```html
|
||||
<!-- standard class attribute setting -->
|
||||
<div class="item clearance special">Item clearance special</div>
|
||||
<div class="foo bar">Some text</div>
|
||||
```
|
||||
|
||||
Class binding syntax resembles property binding, but instead of an element property between brackets, start with the prefix `class`,
|
||||
optionally followed by a dot (`.`) and the name of a CSS class: `[class.class-name]`.
|
||||
You can also add and remove CSS class names from an element's `class` attribute with a **class binding**.
|
||||
|
||||
You can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding.
|
||||
To create a single class binding, start with the prefix `class` followed by a dot (`.`) and the name of the CSS class (for example, `[class.foo]="hasFoo"`).
|
||||
Angular adds the class when the bound expression is truthy, and it removes the class when the expression is falsy (with the exception of `undefined`, see [styling delegation](#styling-delegation)).
|
||||
|
||||
To create a binding to multiple classes, use a generic `[class]` binding without the dot (for example, `[class]="classExpr"`).
|
||||
The expression can be a space-delimited string of class names, or you can format it as an object with class names as the keys and truthy/falsy expressions as the values.
|
||||
With object format, Angular will add a class only if its associated value is truthy.
|
||||
|
||||
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
||||
Updating the property without changing object identity will have no effect.
|
||||
|
||||
If there are multiple bindings to the same class name, conflicts are resolved using [styling precedence](#styling-precedence).
|
||||
|
||||
<style>
|
||||
td, th {vertical-align: top}
|
||||
</style>
|
||||
|
||||
<table width="100%">
|
||||
<col width="15%">
|
||||
</col>
|
||||
<col width="20%">
|
||||
</col>
|
||||
<col width="35%">
|
||||
</col>
|
||||
<col width="30%">
|
||||
</col>
|
||||
<tr>
|
||||
<th>
|
||||
Binding Type
|
||||
</th>
|
||||
<th>
|
||||
Syntax
|
||||
</th>
|
||||
<th>
|
||||
Input Type
|
||||
</th>
|
||||
<th>
|
||||
Example Input Values
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Single class binding</td>
|
||||
<td><code>[class.foo]="hasFoo"</code></td>
|
||||
<td><code>boolean | undefined | null</code></td>
|
||||
<td><code>true</code>, <code>false</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=3>Multi-class binding</td>
|
||||
<td rowspan=3><code>[class]="classExpr"</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td><code>"my-class-1 my-class-2 my-class-3"</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>{[key: string]: boolean | undefined | null}</code></td>
|
||||
<td><code>{foo: true, bar: false}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Array</code><<code>string</code>></td>
|
||||
<td><code>['foo', 'bar']</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="class-override" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can also add a class to an element without overwriting the classes already on the element:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="add-class" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Finally, you can bind to a specific class name.
|
||||
Angular adds the class when the template expression evaluates to truthy.
|
||||
It removes the class when the expression is falsy.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="is-special" header="src/app/app.component.html"></code-example>
|
||||
|
||||
While this technique is suitable for toggling a single class name,
|
||||
consider the [`NgClass`](guide/template-syntax#ngClass) directive when
|
||||
managing multiple class names at the same time.
|
||||
The [NgClass](#ngclass) directive can be used as an alternative to direct `[class]` bindings.
|
||||
However, using the above class binding syntax without `NgClass` is preferred because due to improvements in class binding in Angular, `NgClass` no longer provides significant value, and might eventually be removed in the future.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
### Style binding
|
||||
|
||||
You can set inline styles with a **style binding**.
|
||||
Here's how to set the `style` attribute without a binding in plain HTML:
|
||||
|
||||
Style binding syntax resembles property binding.
|
||||
Instead of an element property between brackets, start with the prefix `style`,
|
||||
followed by a dot (`.`) and the name of a CSS style property: `[style.style-property]`.
|
||||
```html
|
||||
<!-- standard style attribute setting -->
|
||||
<div style="color: blue">Some text</div>
|
||||
```
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding" header="src/app/app.component.html"></code-example>
|
||||
You can also set styles dynamically with a **style binding**.
|
||||
|
||||
Some style binding styles have a unit extension.
|
||||
The following example conditionally sets the font size in “em” and “%” units.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html"></code-example>
|
||||
|
||||
This technique is suitable for setting a single style, but consider
|
||||
the [`NgStyle`](guide/template-syntax#ngStyle) directive when setting several inline styles at the same time.
|
||||
To create a single style binding, start with the prefix `style` followed by a dot (`.`) and the name of the CSS style property (for example, `[style.width]="width"`).
|
||||
The property will be set to the value of the bound expression, which is normally a string.
|
||||
Optionally, you can add a unit extension like `em` or `%`, which requires a number type.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -953,8 +992,140 @@ Note that a _style property_ name can be written in either
|
||||
|
||||
</div>
|
||||
|
||||
If there are multiple styles you'd like to toggle, you can bind to the `[style]` property directly without the dot (for example, `[style]="styleExpr"`).
|
||||
The expression attached to the `[style]` binding is most often a string list of styles like `"width: 100px; height: 100px;"`.
|
||||
|
||||
You can also format the expression as an object with style names as the keys and style values as the values, like `{width: '100px', height: '100px'}`.
|
||||
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
||||
Updating the property without changing object identity will have no effect.
|
||||
|
||||
If there are multiple bindings to the same style property, conflicts are resolved using [styling precedence rules](#styling-precedence).
|
||||
|
||||
<style>
|
||||
td, th {vertical-align: top}
|
||||
</style>
|
||||
|
||||
<table width="100%">
|
||||
<col width="15%">
|
||||
</col>
|
||||
<col width="20%">
|
||||
</col>
|
||||
<col width="35%">
|
||||
</col>
|
||||
<col width="30%">
|
||||
</col>
|
||||
<tr>
|
||||
<th>
|
||||
Binding Type
|
||||
</th>
|
||||
<th>
|
||||
Syntax
|
||||
</th>
|
||||
<th>
|
||||
Input Type
|
||||
</th>
|
||||
<th>
|
||||
Example Input Values
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Single style binding</td>
|
||||
<td><code>[style.width]="width"</code></td>
|
||||
<td><code>string | undefined | null</code></td>
|
||||
<td><code>"100px"</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>Single style binding with units</td>
|
||||
<td><code>[style.width.px]="width"</code></td>
|
||||
<td><code>number | undefined | null</code></td>
|
||||
<td><code>100</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=3>Multi-style binding</td>
|
||||
<td rowspan=3><code>[style]="styleExpr"</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td><code>"width: 100px; height: 100px"</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>{[key: string]: string | undefined | null}</code></td>
|
||||
<td><code>{width: '100px', height: '100px'}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Array</code><<code>string</code>></td>
|
||||
<td><code>['width', '100px']</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The [NgStyle](#ngstyle) directive can be used as an alternative to direct `[style]` bindings.
|
||||
However, using the above style binding syntax without `NgStyle` is preferred because due to improvements in style binding in Angular, `NgStyle` no longer provides significant value, and might eventually be removed in the future.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a styling-precedence}
|
||||
### Styling Precedence
|
||||
|
||||
A single HTML element can have its CSS class list and style values bound to a multiple sources (for example, host bindings from multiple directives).
|
||||
|
||||
When there are multiple bindings to the same class name or style property, Angular uses a set of precedence rules to resolve conflicts and determine which classes or styles are ultimately applied to the element.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<h4>Styling precedence (highest to lowest)</h4>
|
||||
|
||||
1. Template bindings
|
||||
1. Property binding (for example, `<div [class.foo]="hasFoo">` or `<div [style.color]="color">`)
|
||||
1. Map binding (for example, `<div [class]="classExpr">` or `<div [style]="styleExpr">`)
|
||||
1. Static value (for example, `<div class="foo">` or `<div style="color: blue">`)
|
||||
1. Directive host bindings
|
||||
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
||||
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
||||
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
||||
1. Component host bindings
|
||||
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
||||
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
||||
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
||||
|
||||
</div>
|
||||
|
||||
The more specific a class or style binding is, the higher its precedence.
|
||||
|
||||
A binding to a specific class (for example, `[class.foo]`) will take precedence over a generic `[class]` binding, and a binding to a specific style (for example, `[style.bar]`) will take precedence over a generic `[style]` binding.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Specificity rules also apply when it comes to bindings that originate from different sources.
|
||||
It's possible for an element to have bindings in the template where it's declared, from host bindings on matched directives, and from host bindings on matched components.
|
||||
|
||||
Template bindings are the most specific because they apply to the element directly and exclusively, so they have the highest precedence.
|
||||
|
||||
Directive host bindings are considered less specific because directives can be used in multiple locations, so they have a lower precedence than template bindings.
|
||||
|
||||
Directives often augment component behavior, so host bindings from components have the lowest precedence.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="source-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
In addition, bindings take precedence over static attributes.
|
||||
|
||||
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding will take precedence because it is dynamic.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a styling-delegation}
|
||||
### Delegating to styles with lower precedence
|
||||
|
||||
It is possible for higher precedence styles to "delegate" to lower precedence styles using `undefined` values.
|
||||
Whereas setting a style property to `null` ensures the style is removed, setting it to `undefined` will cause Angular to fall back to the next-highest precedence binding to that style.
|
||||
|
||||
For example, consider the following template:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
||||
In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `width` property will fall back to the value of the `comp-with-host-binding` host binding.
|
||||
However, if `dirWithHostBinding` sets its binding to `null`, the `width` property will be removed entirely.
|
||||
|
||||
|
||||
{@a event-binding}
|
||||
|
||||
## Event binding `(event)`
|
||||
|
@ -620,8 +620,8 @@ It also generates an initial test file for the component, `banner-external.compo
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Because `compileComponents` is asynchronous, it uses
|
||||
the [`async`](api/core/testing/async) utility
|
||||
Because `compileComponents` is asynchronous, it uses
|
||||
the [`async`](api/core/testing/async) utility
|
||||
function imported from `@angular/core/testing`.
|
||||
|
||||
Please refer to the [async](#async) section for more details.
|
||||
@ -1261,19 +1261,19 @@ XHR calls within a test are rare, but if you need to call XHR, see [`async()`](#
|
||||
|
||||
#### The _tick()_ function
|
||||
|
||||
You do have to call `tick()` to advance the (virtual) clock.
|
||||
You do have to call [tick()](api/core/testing/tick) to advance the (virtual) clock.
|
||||
|
||||
Calling `tick()` simulates the passage of time until all pending asynchronous activities finish.
|
||||
Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish.
|
||||
In this case, it waits for the error handler's `setTimeout()`.
|
||||
|
||||
The `tick()` function accepts milliseconds as a parameter (defaults to 0 if not provided). The parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback.
|
||||
The [tick()](api/core/testing/tick) function accepts milliseconds as a parameter (defaults to 0 if not provided). The parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback.
|
||||
|
||||
<code-example
|
||||
path="testing/src/app/demo/async-helper.spec.ts"
|
||||
region="fake-async-test-tick">
|
||||
</code-example>
|
||||
|
||||
The `tick()` function is one of the Angular testing utilities that you import with `TestBed`.
|
||||
The [tick()](api/core/testing/tick) function is one of the Angular testing utilities that you import with `TestBed`.
|
||||
It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body.
|
||||
|
||||
#### Comparing dates inside fakeAsync()
|
||||
@ -1422,7 +1422,7 @@ in the real world.
|
||||
Notice that the quote element displays the placeholder value (`'...'`) after `ngOnInit()`.
|
||||
The first quote hasn't arrived yet.
|
||||
|
||||
To flush the first quote from the observable, you call `tick()`.
|
||||
To flush the first quote from the observable, you call [tick()](api/core/testing/tick).
|
||||
Then call `detectChanges()` to tell Angular to update the screen.
|
||||
|
||||
Then you can assert that the quote element displays the expected text.
|
||||
@ -1468,7 +1468,7 @@ When using an `intervalTimer()` such as `setInterval()` in `async()`, remember t
|
||||
#### _whenStable_
|
||||
|
||||
The test must wait for the `getQuote()` observable to emit the next quote.
|
||||
Instead of calling `tick()`, it calls `fixture.whenStable()`.
|
||||
Instead of calling [tick()](api/core/testing/tick), it calls `fixture.whenStable()`.
|
||||
|
||||
The `fixture.whenStable()` returns a promise that resolves when the JavaScript engine's
|
||||
task queue becomes empty.
|
||||
@ -1577,7 +1577,7 @@ you tell the `TestScheduler` to _flush_ its queue of prepared tasks like this.
|
||||
path="testing/src/app/twain/twain.component.marbles.spec.ts"
|
||||
region="test-scheduler-flush"></code-example>
|
||||
|
||||
This step serves a purpose analogous to `tick()` and `whenStable()` in the
|
||||
This step serves a purpose analogous to [tick()](api/core/testing/tick) and `whenStable()` in the
|
||||
earlier `fakeAsync()` and `async()` examples.
|
||||
The balance of the test is the same as those examples.
|
||||
|
||||
@ -1589,7 +1589,7 @@ Here's the marble testing version of the `getQuote()` error test.
|
||||
path="testing/src/app/twain/twain.component.marbles.spec.ts"
|
||||
region="error-test"></code-example>
|
||||
|
||||
It's still an async test, calling `fakeAsync()` and `tick()`, because the component itself
|
||||
It's still an async test, calling `fakeAsync()` and [tick()](api/core/testing/tick), because the component itself
|
||||
calls `setTimeout()` when processing errors.
|
||||
|
||||
Look at the marble observable definition.
|
||||
@ -3582,13 +3582,13 @@ The Angular `By` class has three static methods for common predicates:
|
||||
|
||||
<hr>
|
||||
|
||||
{@a faq}
|
||||
{@a useful-tips}
|
||||
|
||||
## Frequently Asked Questions
|
||||
## Useful tips
|
||||
|
||||
{@a q-spec-file-location}
|
||||
|
||||
#### Why put spec file next to the file it tests?
|
||||
#### Place your spec file next to the file it tests
|
||||
|
||||
It's a good idea to put unit test spec files in the same folder
|
||||
as the application source code files that they test:
|
||||
@ -3599,11 +3599,9 @@ as the application source code files that they test:
|
||||
- When you move the source (inevitable), you remember to move the test.
|
||||
- When you rename the source file (inevitable), you remember to rename the test file.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a q-specs-in-test-folder}
|
||||
|
||||
#### When would I put specs in a test folder?
|
||||
#### Place your spec files in a test folder
|
||||
|
||||
Application integration specs can test the interactions of multiple parts
|
||||
spread across folders and modules.
|
||||
@ -3615,15 +3613,17 @@ It's often better to create an appropriate folder for them in the `tests` direct
|
||||
Of course specs that test the test helpers belong in the `test` folder,
|
||||
next to their corresponding helper files.
|
||||
|
||||
{@a q-e2e}
|
||||
{@a q-kiss}
|
||||
|
||||
#### Why not rely on E2E tests of DOM integration?
|
||||
#### Keep it simple
|
||||
|
||||
The component DOM tests described in this guide often require extensive setup and
|
||||
advanced techniques whereas the [unit tests](#component-class-testing)
|
||||
are comparatively simple.
|
||||
[Component class testing](#component-class-testing) should be kept very clean and simple.
|
||||
It should test only a single unit. On a first glance, you should be able to understand
|
||||
what the test is testing. If it's doing more, then it doesn't belong here.
|
||||
|
||||
#### Why not defer DOM integration tests to end-to-end (E2E) testing?
|
||||
{@a q-end-to-end}
|
||||
|
||||
#### Use E2E (end-to-end) to test more than a single unit
|
||||
|
||||
E2E tests are great for high-level validation of the entire system.
|
||||
But they can't give you the comprehensive test coverage that you'd expect from unit tests.
|
||||
|
@ -214,7 +214,7 @@ import {REQUEST} from '@nguniversal/express-engine/tokens';
|
||||
@Injectable()
|
||||
export class UniversalInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(@Optional() @Inject(REQUEST) protected request: Request) {}
|
||||
constructor(@Optional() @Inject(REQUEST) protected request?: Request) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
||||
let serverReq: HttpRequest<any> = req;
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Updating to Angular version 9
|
||||
|
||||
This guide contains everything you need to know about updating to the next Angular version.
|
||||
This guide contains information related to updating to version 9 of Angular.
|
||||
|
||||
## Updating CLI Apps
|
||||
|
||||
|
@ -68,5 +68,3 @@ For simple updates, the CLI command [`ng update`](cli/update) is all you need. W
|
||||
* Update command reference: [Angular CLI `ng update` command reference](cli/update)
|
||||
|
||||
* Versioning, release, support, and deprecation practices: [Angular versioning and releases](guide/releases "Angular versioning and releases")
|
||||
|
||||
* Release schedule: [Angular versioning and releases](guide/releases#schedule "Angular versioning and releases")
|
||||
|
@ -1162,11 +1162,19 @@ Begin by installing TypeScript to the project.
|
||||
</code-example>
|
||||
|
||||
Install type definitions for the existing libraries that
|
||||
you're using but that don't come with prepackaged types: AngularJS and the
|
||||
you're using but that don't come with prepackaged types: AngularJS, AngularJS Material, and the
|
||||
Jasmine unit test framework.
|
||||
|
||||
For the PhoneCat app, we can install the necessary type definitions by running the following command:
|
||||
|
||||
<code-example format="">
|
||||
npm install @types/jasmine @types/angular @types/angular-animate @types/angular-cookies @types/angular-mocks @types/angular-resource @types/angular-route @types/angular-sanitize --save-dev
|
||||
npm install @types/jasmine @types/angular @types/angular-animate @types/angular-aria @types/angular-cookies @types/angular-mocks @types/angular-resource @types/angular-route @types/angular-sanitize --save-dev
|
||||
</code-example>
|
||||
|
||||
If you are using AngularJS Material, you can install the type definitions via:
|
||||
|
||||
<code-example format="">
|
||||
npm install @types/angular-material --save-dev
|
||||
</code-example>
|
||||
|
||||
You should also configure the TypeScript compiler with a `tsconfig.json` in the project directory
|
||||
|
@ -135,17 +135,17 @@
|
||||
{
|
||||
"url": "tutorial",
|
||||
"title": "Introduction",
|
||||
"tooltip": "Introduction to the Tour of Heroes tutorial"
|
||||
"tooltip": "Introduction to the Tour of Heroes app and tutorial"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt0",
|
||||
"title": "The Application Shell",
|
||||
"title": "Create a Project",
|
||||
"tooltip": "Creating the application shell"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt1",
|
||||
"title": "1. The Hero Editor",
|
||||
"tooltip": "Part 1: Build a simple hero editor"
|
||||
"tooltip": "Part 1: Build a simple editor"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt2",
|
||||
@ -872,6 +872,10 @@
|
||||
}
|
||||
],
|
||||
"docVersions": [
|
||||
{
|
||||
"title": "v8",
|
||||
"url": "https://v8.angular.io/"
|
||||
},
|
||||
{
|
||||
"title": "v7",
|
||||
"url": "https://v7.angular.io/"
|
||||
|
@ -33,8 +33,6 @@ Later, the [Forms](start/forms "Getting Started: Forms") part of
|
||||
this tutorial guides you through accessing this cart service
|
||||
from the page where the user checks out.
|
||||
|
||||
Later, the [Forms](start/forms "Getting Started: Forms") part of this tutorial guides you through accessing this cart service from the page where the user checks out.
|
||||
|
||||
</div>
|
||||
|
||||
{@a generate-cart-service}
|
||||
@ -236,7 +234,7 @@ This section shows you how to use the HTTP client to retrieve shipping prices fr
|
||||
|
||||
### Predefined shipping data
|
||||
|
||||
The app StackBlitz generates for this guide comes with predefined shipping data in `assets/shipping.json`.
|
||||
The application that StackBlitz generates for this guide comes with predefined shipping data in `assets/shipping.json`.
|
||||
Use this data to add shipping prices for items in the cart.
|
||||
|
||||
<code-example header="src/assets/shipping.json" path="getting-started/src/assets/shipping.json">
|
||||
@ -316,7 +314,7 @@ Now that your app can retrieve shipping data, create a shipping component and t
|
||||
|
||||
There's no link to the new shipping component yet, but you can see its template in the preview pane by entering the URL its route specifies. The URL has the pattern: `https://getting-started.stackblitz.io/shipping` where the `getting-started.stackblitz.io` part may be different for your StackBlitz project.
|
||||
|
||||
1. Modify the shipping component so it uses the cart service to retrieve shipping data via HTTP from the `shipping.json` file.
|
||||
1. Modify the shipping component so that it uses the cart service to retrieve shipping data via HTTP from the `shipping.json` file.
|
||||
|
||||
1. Import the cart service.
|
||||
|
||||
|
@ -66,13 +66,13 @@ One of the easiest ways to get your site live is to host it using Firebase.
|
||||
|
||||
1. Sign up for a firebase account on [Firebase](https://firebase.google.com/ "Firebase web site").
|
||||
1. Create a new project, giving it any name you like.
|
||||
1. Install the `firebase-tools` CLI that will handle your deployment using `npm install -g firebase-tools`.
|
||||
1. Add the `@angular/fire` schematics that will handle your deployment using `ng add @angular/fire`.
|
||||
1. Connect your CLI to your Firebase account and initialize the connection to your project using `firebase login` and `firebase init`.
|
||||
1. Follow the prompts to select the `Firebase` project you are creating for hosting.
|
||||
- Select the `Hosting` option on the first prompt.
|
||||
- Select the project you previously created on Firebase.
|
||||
- Select `dist/my-project-name` as the public directory.
|
||||
1. Deploy your application with `firebase deploy`, because the command `firebase init` has created a `firebase.json` file that tells Firebase how to serve your app.
|
||||
- Select the `Hosting` option on the first prompt.
|
||||
- Select the project you previously created on Firebase.
|
||||
- Select `dist/my-project-name` as the public directory.
|
||||
1. Deploy your application with `ng deploy`.
|
||||
1. Once deployed, visit https://your-firebase-project-name.firebaseapp.com to see it live!
|
||||
|
||||
### Hosting an Angular app anywhere else
|
||||
|
@ -40,7 +40,7 @@ of the constructor.
|
||||
|
||||
1. For the checkout process, users need to submit their name and address. When they submit their order, the form should reset and the cart should clear.
|
||||
|
||||
1. In `cart.component.ts`, define an `onSubmit()` method to process the form. Use the `CartService` `clearCart()` method to empty the cart items and reset the form after it is submission. In a real-world app, this method would also submit the data to an external server. The entire cart component class is as follows:
|
||||
1. In `cart.component.ts`, define an `onSubmit()` method to process the form. Use the `CartService` `clearCart()` method to empty the cart items and reset the form after its submission. In a real-world app, this method would also submit the data to an external server. The entire cart component class is as follows:
|
||||
|
||||
<code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.ts">
|
||||
</code-example>
|
||||
|
@ -92,7 +92,7 @@ To help you get going, the following steps use predefined product data from the
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
`*ngFor` is a "structural directive". Structural directives shape or reshape the DOM's structure, typically by adding, removing, and manipulating the elements to which they are attached. Any directive with an asterisk, `*`, is a structural directive.
|
||||
`*ngFor` is a "structural directive". Structural directives shape or reshape the DOM's structure, typically by adding, removing, and manipulating the elements to which they are attached. Directives with an asterisk, `*`, are structural directives.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Create services
|
||||
# Add services
|
||||
|
||||
The Tour of Heroes `HeroesComponent` is currently getting and displaying fake data.
|
||||
|
||||
@ -122,7 +122,7 @@ Replace the definition of the `heroes` property with a simple declaration.
|
||||
|
||||
Add a private `heroService` parameter of type `HeroService` to the constructor.
|
||||
|
||||
<code-example path="toh-pt4/src/app/heroes/heroes.component.ts" header="src/app/heroes/heroes.component.ts" region="ctor">
|
||||
<code-example path="toh-pt4/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts" region="ctor">
|
||||
</code-example>
|
||||
|
||||
The parameter simultaneously defines a private `heroService` property and identifies it as a `HeroService` injection site.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Add in-app navigation (routing)
|
||||
# Add in-app navigation with routing
|
||||
|
||||
There are new requirements for the Tour of Heroes app:
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
@ -30,7 +31,14 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
browserNoActivityTimeout: 60000,
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
|
@ -72,7 +72,7 @@
|
||||
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
|
||||
"generate-zips": "node ./tools/example-zipper/generateZips",
|
||||
"build-404-page": "node scripts/build-404-page",
|
||||
"update-webdriver": "webdriver-manager update --standalone false --gecko false $CI_CHROMEDRIVER_VERSION_ARG",
|
||||
"update-webdriver": "node ../scripts/webdriver-manager-update.js",
|
||||
"~~audit-web-app": "node scripts/audit-web-app",
|
||||
"~~check-env": "node scripts/check-environment",
|
||||
"~~clean-generated": "node --eval \"require('shelljs').rm('-rf', 'src/generated')\"",
|
||||
@ -87,28 +87,28 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "9.0.0-rc.11",
|
||||
"@angular/cdk": "9.0.0-rc.8",
|
||||
"@angular/common": "9.0.0-rc.11",
|
||||
"@angular/compiler": "9.0.0-rc.11",
|
||||
"@angular/core": "9.0.0-rc.11",
|
||||
"@angular/elements": "9.0.0-rc.11",
|
||||
"@angular/forms": "9.0.0-rc.11",
|
||||
"@angular/material": "9.0.0-rc.8",
|
||||
"@angular/platform-browser": "9.0.0-rc.11",
|
||||
"@angular/platform-browser-dynamic": "9.0.0-rc.11",
|
||||
"@angular/router": "9.0.0-rc.11",
|
||||
"@angular/service-worker": "9.0.0-rc.11",
|
||||
"@webcomponents/custom-elements": "^1.2.0",
|
||||
"@angular/animations": "9.0.0",
|
||||
"@angular/cdk": "^9.0.0",
|
||||
"@angular/common": "9.0.0",
|
||||
"@angular/compiler": "9.0.0",
|
||||
"@angular/core": "9.0.0",
|
||||
"@angular/elements": "9.0.0",
|
||||
"@angular/forms": "9.0.0",
|
||||
"@angular/material": "^9.0.0",
|
||||
"@angular/platform-browser": "9.0.0",
|
||||
"@angular/platform-browser-dynamic": "9.0.0",
|
||||
"@angular/router": "9.0.0",
|
||||
"@angular/service-worker": "9.0.0",
|
||||
"@webcomponents/custom-elements": "1.2.1",
|
||||
"rxjs": "^6.5.3",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.900.0-rc.14",
|
||||
"@angular/cli": "9.0.0-rc.14",
|
||||
"@angular/compiler-cli": "9.0.0-rc.11",
|
||||
"@angular/language-service": "9.0.0-rc.11",
|
||||
"@angular-devkit/build-angular": "0.900.1",
|
||||
"@angular/cli": "9.0.1",
|
||||
"@angular/compiler-cli": "9.0.0",
|
||||
"@angular/language-service": "9.0.0",
|
||||
"@types/jasmine": "^3.4.2",
|
||||
"@types/jasminewd2": "^2.0.8",
|
||||
"@types/lunr": "^2.3.2",
|
||||
@ -118,13 +118,12 @@
|
||||
"archiver": "^1.3.0",
|
||||
"canonical-path": "1.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"chrome-launcher": "^0.10.7",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "^5.1.2",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.11",
|
||||
"dgeni-packages": "^0.27.5",
|
||||
"dgeni-packages": "^0.28.3",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
@ -155,6 +154,7 @@
|
||||
"lunr": "^2.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"protractor": "^5.4.2",
|
||||
"puppeteer": "2.1.1",
|
||||
"rehype": "^6.0.0",
|
||||
"rehype-slug": "^2.0.0",
|
||||
"remark": "^9.0.0",
|
||||
|
@ -3,7 +3,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2987,
|
||||
"main-es2015": 455897,
|
||||
"main-es2015": 449223,
|
||||
"polyfills-es2015": 52195
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2987,
|
||||
"main-es2015": 448928,
|
||||
"main-es2015": 450017,
|
||||
"polyfills-es2015": 52195
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3097,
|
||||
"main-es2015": 426513,
|
||||
"main-es2015": 426533,
|
||||
"polyfills-es2015": 52195
|
||||
}
|
||||
}
|
||||
|
@ -27,14 +27,13 @@
|
||||
*/
|
||||
|
||||
// Imports
|
||||
const chromeLauncher = require('chrome-launcher');
|
||||
const lighthouse = require('lighthouse');
|
||||
const printer = require('lighthouse/lighthouse-cli/printer');
|
||||
const logger = require('lighthouse-logger');
|
||||
const puppeteer = require('puppeteer');
|
||||
|
||||
// Constants
|
||||
const AUDIT_CATEGORIES = ['accessibility', 'best-practices', 'performance', 'pwa', 'seo'];
|
||||
const CHROME_LAUNCH_OPTS = {chromeFlags: ['--headless']};
|
||||
const LIGHTHOUSE_FLAGS = {logLevel: process.env.CI ? 'error' : 'info'}; // Be less verbose on CI.
|
||||
const SKIPPED_HTTPS_AUDITS = ['redirects-http', 'uses-http2'];
|
||||
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer';
|
||||
@ -84,13 +83,13 @@ function formatScore(score) {
|
||||
}
|
||||
|
||||
async function launchChromeAndRunLighthouse(url, flags, config) {
|
||||
const chrome = await chromeLauncher.launch(CHROME_LAUNCH_OPTS);
|
||||
flags.port = chrome.port;
|
||||
const browser = await puppeteer.launch();
|
||||
flags.port = (new URL(browser.wsEndpoint())).port;
|
||||
|
||||
try {
|
||||
return await lighthouse(url, flags, config);
|
||||
} finally {
|
||||
await chrome.kill();
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,35 +1,32 @@
|
||||
import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core';
|
||||
import { inject, ComponentFixture, TestBed, fakeAsync, flushMicrotasks, tick } from '@angular/core/testing';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, fakeAsync, flushMicrotasks, inject, TestBed, tick } from '@angular/core/testing';
|
||||
import { MatProgressBar } from '@angular/material/progress-bar';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { Subject, of, timer } from 'rxjs';
|
||||
import { first, mapTo } from 'rxjs/operators';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppModule } from './app.module';
|
||||
import { CurrentNodes } from 'app/navigation/navigation.model';
|
||||
import { By, Title } from '@angular/platform-browser';
|
||||
import { ElementsLoader } from 'app/custom-elements/elements-loader';
|
||||
import { DocumentService } from 'app/documents/document.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { CurrentNodes } from 'app/navigation/navigation.model';
|
||||
import { NavigationNode, NavigationService } from 'app/navigation/navigation.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { ElementsLoader } from 'app/custom-elements/elements-loader';
|
||||
import { GaService } from 'app/shared/ga.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SelectComponent } from 'app/shared/select/select.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
import { of, Subject, timer } from 'rxjs';
|
||||
import { first, mapTo } from 'rxjs/operators';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
import { MockLogger } from 'testing/logger.service';
|
||||
import { MockSearchService } from 'testing/search.service';
|
||||
import { NavigationNode, NavigationService } from 'app/navigation/navigation.service';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { SelectComponent } from 'app/shared/select/select.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
const sideBySideBreakPoint = 992;
|
||||
const hideToCBreakPoint = 800;
|
||||
@ -405,10 +402,12 @@ describe('AppComponent', () => {
|
||||
// Older docs versions have an href
|
||||
it('should navigate when change to a version with a url', async () => {
|
||||
await setupSelectorForTesting();
|
||||
locationService.urlSubject.next('new-page?id=1#section-1');
|
||||
const versionWithUrlIndex = component.docVersions.findIndex(v => !!v.url);
|
||||
const versionWithUrl = component.docVersions[versionWithUrlIndex];
|
||||
const versionWithUrlAndPage = `${versionWithUrl.url}new-page?id=1#section-1`;
|
||||
selectElement.triggerEventHandler('change', { option: versionWithUrl, index: versionWithUrlIndex});
|
||||
expect(locationService.go).toHaveBeenCalledWith(versionWithUrl.url);
|
||||
expect(locationService.go).toHaveBeenCalledWith(versionWithUrlAndPage);
|
||||
});
|
||||
|
||||
it('should not navigate when change to a version without a url', async () => {
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { Component, ElementRef, HostBinding, HostListener, OnInit,
|
||||
QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { Component, ElementRef, HostBinding, HostListener, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
|
||||
import { CurrentNodes, NavigationService, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import { DocumentService, DocumentContents } from 'app/documents/document.service';
|
||||
import { DocumentContents, DocumentService } from 'app/documents/document.service';
|
||||
import { NotificationComponent } from 'app/layout/notification/notification.component';
|
||||
import { CurrentNodes, NavigationNode, NavigationService, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { NotificationComponent } from 'app/layout/notification/notification.component';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
|
||||
@ -77,6 +74,8 @@ export class AppComponent implements OnInit {
|
||||
|
||||
versionInfo: VersionInfo;
|
||||
|
||||
private currentUrl: string;
|
||||
|
||||
get isOpened() { return this.isSideBySide && this.isSideNavDoc; }
|
||||
get mode() { return this.isSideBySide ? 'side' : 'over'; }
|
||||
|
||||
@ -148,27 +147,27 @@ export class AppComponent implements OnInit {
|
||||
this.navigationService.versionInfo,
|
||||
this.navigationService.navigationViews.pipe(map(views => views['docVersions'])),
|
||||
]).subscribe(([versionInfo, versions]) => {
|
||||
// TODO(pbd): consider whether we can lookup the stable and next versions from the internet
|
||||
const computedVersions: NavigationNode[] = [
|
||||
{ title: 'next', url: 'https://next.angular.io' },
|
||||
{ title: 'stable', url: 'https://angular.io' },
|
||||
];
|
||||
if (this.deployment.mode === 'archive') {
|
||||
computedVersions.push({ title: `v${versionInfo.major}` });
|
||||
}
|
||||
this.docVersions = [...computedVersions, ...versions];
|
||||
// TODO(pbd): consider whether we can lookup the stable and next versions from the internet
|
||||
const computedVersions: NavigationNode[] = [
|
||||
{ title: 'next', url: 'https://next.angular.io' },
|
||||
{ title: 'stable', url: 'https://angular.io' },
|
||||
];
|
||||
if (this.deployment.mode === 'archive') {
|
||||
computedVersions.push({ title: `v${versionInfo.major}` });
|
||||
}
|
||||
this.docVersions = [...computedVersions, ...versions];
|
||||
|
||||
// Find the current version - eithers title matches the current deployment mode
|
||||
// or its title matches the major version of the current version info
|
||||
this.currentDocVersion = this.docVersions.find(version =>
|
||||
version.title === this.deployment.mode || version.title === `v${versionInfo.major}`)!;
|
||||
this.currentDocVersion.title += ` (v${versionInfo.raw})`;
|
||||
});
|
||||
// Find the current version - eithers title matches the current deployment mode
|
||||
// or its title matches the major version of the current version info
|
||||
this.currentDocVersion = this.docVersions.find(version =>
|
||||
version.title === this.deployment.mode || version.title === `v${versionInfo.major}`)!;
|
||||
this.currentDocVersion.title += ` (v${versionInfo.raw})`;
|
||||
});
|
||||
|
||||
this.navigationService.navigationViews.subscribe(views => {
|
||||
this.footerNodes = views['Footer'] || [];
|
||||
this.footerNodes = views['Footer'] || [];
|
||||
this.sideNavNodes = views['SideNav'] || [];
|
||||
this.topMenuNodes = views['TopBar'] || [];
|
||||
this.topMenuNodes = views['TopBar'] || [];
|
||||
this.topMenuNarrowNodes = views['TopBarNarrow'] || this.topMenuNodes;
|
||||
});
|
||||
|
||||
@ -188,6 +187,8 @@ export class AppComponent implements OnInit {
|
||||
this.navigationService.currentNodes, // ...needed to determine `sidenav` state
|
||||
]).pipe(first())
|
||||
.subscribe(() => this.updateShell());
|
||||
|
||||
this.locationService.currentUrl.subscribe(url => this.currentUrl = url);
|
||||
}
|
||||
|
||||
onDocReady() {
|
||||
@ -231,7 +232,7 @@ export class AppComponent implements OnInit {
|
||||
onDocVersionChange(versionIndex: number) {
|
||||
const version = this.docVersions[versionIndex];
|
||||
if (version.url) {
|
||||
this.locationService.go(version.url);
|
||||
this.locationService.go(`${version.url}${this.currentUrl}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +264,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
// Deal with anchor clicks; climb DOM tree until anchor found (or null)
|
||||
let target: HTMLElement|null = eventTarget;
|
||||
let target: HTMLElement | null = eventTarget;
|
||||
while (target && !(target instanceof HTMLAnchorElement)) {
|
||||
target = target.parentElement;
|
||||
}
|
||||
@ -406,7 +407,7 @@ export class AppComponent implements OnInit {
|
||||
if (key === '/' || keyCode === 191) {
|
||||
this.focusSearchBox();
|
||||
}
|
||||
if (key === 'Escape' || keyCode === 27 ) {
|
||||
if (key === 'Escape' || keyCode === 27) {
|
||||
// escape key
|
||||
if (this.showSearchResults) {
|
||||
this.hideSearchResults();
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<div *ngFor="let resource of subCategory.resources">
|
||||
<div class="c-resource" *ngIf="resource.rev">
|
||||
<a class="l-flex--column resource-row-link" target="_blank" [href]="resource.url">
|
||||
<a class="l-flex--column resource-row-link" rel="noopener" target="_blank" [href]="resource.url">
|
||||
<div>
|
||||
<h4>{{resource.title}}</h4>
|
||||
<p class="resource-description">{{resource.desc || 'No Description'}}</p>
|
||||
|
@ -20,9 +20,9 @@
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/images/favicons/favicon-144x144.png">
|
||||
|
||||
<!-- NOTE: These need to be kept in sync with `ngsw-config.json`. -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono&display=swap">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons&display=block">
|
||||
<!-- -->
|
||||
|
||||
<link rel="manifest" href="pwa-manifest.json">
|
||||
|
@ -113,3 +113,9 @@ table {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.events-container {
|
||||
tr > td, tr > th {
|
||||
width: 33%;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ exports.config = {
|
||||
suite: 'full',
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
directConnect: true,
|
||||
framework: 'jasmine',
|
||||
|
@ -14,6 +14,11 @@ exports.config = {
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
|
@ -112,6 +112,7 @@ class ExampleZipper {
|
||||
'!**/npm-debug.log',
|
||||
'!**/example-config.json',
|
||||
'!**/wallaby.js',
|
||||
'!e2e/protractor-puppeteer.conf.js',
|
||||
// AOT related files
|
||||
'!**/aot/**/*.*',
|
||||
'!**/*-aot.*'
|
||||
|
@ -15,8 +15,9 @@ const BOILERPLATE_PATHS = {
|
||||
'src/environments/environment.prod.ts', 'src/environments/environment.ts',
|
||||
'src/assets/.gitkeep', 'browserslist', 'src/favicon.ico', 'karma.conf.js',
|
||||
'src/polyfills.ts', 'src/test.ts', 'tsconfig.app.json', 'tsconfig.spec.json',
|
||||
'tslint.json', 'e2e/src/app.po.ts', 'e2e/protractor.conf.js', 'e2e/tsconfig.json',
|
||||
'.editorconfig', 'angular.json', 'package.json', 'tsconfig.json', 'tslint.json'
|
||||
'tslint.json', 'e2e/src/app.po.ts', 'e2e/protractor-puppeteer.conf.js',
|
||||
'e2e/protractor.conf.js', 'e2e/tsconfig.json', '.editorconfig', 'angular.json', 'package.json',
|
||||
'tsconfig.json', 'tslint.json'
|
||||
],
|
||||
systemjs: [
|
||||
'src/systemjs-angular-loader.js', 'src/systemjs.config.js', 'src/tsconfig.json',
|
||||
|
@ -10,7 +10,7 @@ describe('example-boilerplate tool', () => {
|
||||
const sharedDir = path.resolve(__dirname, 'shared');
|
||||
const sharedNodeModulesDir = path.resolve(sharedDir, 'node_modules');
|
||||
const BPFiles = {
|
||||
cli: 19,
|
||||
cli: 20,
|
||||
i18n: 2,
|
||||
universal: 2,
|
||||
systemjs: 7,
|
||||
|
@ -262,7 +262,13 @@ function runE2eTestsCLI(appDir, outputFile, bufferOutput, port) {
|
||||
const config = loadExampleConfig(appDir);
|
||||
const commands = config.e2e || [{
|
||||
cmd: 'yarn',
|
||||
args: ['e2e', '--prod', '--no-webdriver-update', `--port=${port || DEFAULT_CLI_EXAMPLE_PORT}`]
|
||||
args: [
|
||||
'e2e',
|
||||
'--prod',
|
||||
'--protractor-config=e2e/protractor-puppeteer.conf.js',
|
||||
'--no-webdriver-update',
|
||||
'--port={PORT}',
|
||||
],
|
||||
}];
|
||||
let bufferedOutput = `\n\n============== AIO example output for: ${appDir}\n\n`;
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
// @ts-check
|
||||
// A protractor config to use to run the tests using the Chrome version provided by `puppeteer`.
|
||||
// This is useful to ensure deterministic runs on CI and locally. This file is ignored when creating
|
||||
// StackBlitz examples and ZIP archives for each example.
|
||||
|
||||
const {config} = require('./protractor.conf.js');
|
||||
|
||||
exports.config = {
|
||||
...config,
|
||||
capabilities: {
|
||||
...config.capabilities,
|
||||
chromeOptions: {
|
||||
...config.capabilities.chromeOptions,
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
};
|
@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"http-server": "http-server",
|
||||
"protractor": "protractor",
|
||||
"webdriver:update": "webdriver-manager update --standalone false --gecko false $CI_CHROMEDRIVER_VERSION_ARG",
|
||||
"webdriver:update": "node ../../../../scripts/webdriver-manager-update.js",
|
||||
"preinstall": "node ../../../../tools/yarn/check-yarn.js",
|
||||
"postinstall": "yarn webdriver:update"
|
||||
},
|
||||
@ -74,6 +74,7 @@
|
||||
"lite-server": "^2.2.2",
|
||||
"lodash": "^4.16.2",
|
||||
"protractor": "^5.4.2",
|
||||
"puppeteer": "2.1.1",
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup": "^1.1.0",
|
||||
"rollup-plugin-commonjs": "^9.2.1",
|
||||
|
@ -20,7 +20,12 @@ exports.config = {
|
||||
|
||||
// Capabilities to be passed to the webdriver instance.
|
||||
capabilities: {
|
||||
'browserName': 'chrome',
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
|
||||
// Framework to use. Jasmine is recommended.
|
||||
|
@ -450,6 +450,11 @@
|
||||
dependencies:
|
||||
"@types/sizzle" "*"
|
||||
|
||||
"@types/mime-types@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73"
|
||||
integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=
|
||||
|
||||
"@types/mime@*":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
|
||||
@ -729,6 +734,11 @@ agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0:
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
agent-base@5:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
|
||||
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
|
||||
|
||||
agentkeepalive@^3.4.1:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67"
|
||||
@ -1942,6 +1952,16 @@ concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
||||
concat-stream@1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
concat-stream@^1.5.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
|
||||
@ -2239,6 +2259,13 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^3.0.0, debug@^3.2.5, debug@^3.2.6:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
@ -2246,13 +2273,6 @@ debug@^3.0.0, debug@^3.2.5, debug@^3.2.6:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debuglog@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||
@ -2943,6 +2963,16 @@ extglob@^2.0.2, extglob@^2.0.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
extract-zip@^1.6.6:
|
||||
version "1.6.7"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
|
||||
integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
|
||||
dependencies:
|
||||
concat-stream "1.6.2"
|
||||
debug "2.6.9"
|
||||
mkdirp "0.5.1"
|
||||
yauzl "2.4.1"
|
||||
|
||||
extsprintf@1.3.0, extsprintf@^1.2.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
|
||||
@ -2972,6 +3002,13 @@ faye-websocket@~0.11.1:
|
||||
dependencies:
|
||||
websocket-driver ">=0.5.1"
|
||||
|
||||
fd-slicer@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
|
||||
integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
figgy-pudding@^3.1.0, figgy-pudding@^3.4.1, figgy-pudding@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||
@ -3735,6 +3772,14 @@ https-proxy-agent@^2.2.1:
|
||||
agent-base "^4.1.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
https-proxy-agent@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b"
|
||||
integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==
|
||||
dependencies:
|
||||
agent-base "5"
|
||||
debug "4"
|
||||
|
||||
humanize-ms@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
|
||||
@ -4887,6 +4932,11 @@ miller-rabin@^4.0.0:
|
||||
bn.js "^4.0.0"
|
||||
brorand "^1.0.1"
|
||||
|
||||
mime-db@1.43.0:
|
||||
version "1.43.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
|
||||
integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
|
||||
|
||||
"mime-db@>= 1.40.0 < 2":
|
||||
version "1.40.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
|
||||
@ -4906,6 +4956,13 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16,
|
||||
dependencies:
|
||||
mime-db "~1.30.0"
|
||||
|
||||
mime-types@^2.1.25:
|
||||
version "2.1.26"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
|
||||
integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
|
||||
dependencies:
|
||||
mime-db "1.43.0"
|
||||
|
||||
mime-types@~2.1.18, mime-types@~2.1.19:
|
||||
version "2.1.20"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
|
||||
@ -4933,6 +4990,11 @@ mime@^1.6.0:
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mime@^2.0.3:
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
|
||||
integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
|
||||
|
||||
mime@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369"
|
||||
@ -5039,7 +5101,7 @@ mkdirp@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
|
||||
|
||||
mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
dependencies:
|
||||
@ -5804,6 +5866,11 @@ pbkdf2@^3.0.3:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
|
||||
|
||||
performance-now@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
|
||||
@ -5939,6 +6006,11 @@ process@^0.11.0:
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
|
||||
progress@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
promise-inflight@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
|
||||
@ -6000,6 +6072,11 @@ proxy-addr@~2.0.4:
|
||||
forwarded "~0.1.2"
|
||||
ipaddr.js "1.9.0"
|
||||
|
||||
proxy-from-env@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
|
||||
integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
|
||||
|
||||
prr@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
|
||||
@ -6056,6 +6133,22 @@ punycode@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d"
|
||||
|
||||
puppeteer@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.1.1.tgz#ccde47c2a688f131883b50f2d697bd25189da27e"
|
||||
integrity sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg==
|
||||
dependencies:
|
||||
"@types/mime-types" "^2.1.0"
|
||||
debug "^4.1.0"
|
||||
extract-zip "^1.6.6"
|
||||
https-proxy-agent "^4.0.0"
|
||||
mime "^2.0.3"
|
||||
mime-types "^2.1.25"
|
||||
progress "^2.0.1"
|
||||
proxy-from-env "^1.0.0"
|
||||
rimraf "^2.6.1"
|
||||
ws "^6.1.0"
|
||||
|
||||
q@1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
|
||||
@ -8303,6 +8396,13 @@ ws@1.1.1:
|
||||
options ">=0.0.5"
|
||||
ultron "1.0.x"
|
||||
|
||||
ws@^6.1.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
|
||||
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@~3.3.1:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
|
||||
@ -8481,6 +8581,13 @@ yargs@~3.10.0:
|
||||
decamelize "^1.0.0"
|
||||
window-size "0.1.0"
|
||||
|
||||
yauzl@2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
|
||||
integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
|
||||
dependencies:
|
||||
fd-slicer "~1.0.1"
|
||||
|
||||
yeast@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
|
||||
|
4182
aio/yarn.lock
4182
aio/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -27,21 +27,14 @@ var CIconfiguration = {
|
||||
'IE10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.4': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android6': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// Temporary disabled due to SauceLab timeout.
|
||||
// https://angular-team.atlassian.net/browse/FW-1821
|
||||
// 'Android7': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android7': {unitTest: {target: null, required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari10': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': {unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Android7': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android8': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android9': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android10': {unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Safari12': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari13': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS10': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS11': {unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': {unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
@ -101,6 +94,27 @@ var customLaunchers = {
|
||||
version: '7.1',
|
||||
device: 'Android GoogleAPI Emulator'
|
||||
},
|
||||
'SL_ANDROID8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'Chrome',
|
||||
platform: 'Android',
|
||||
version: '8.0',
|
||||
device: 'Android GoogleAPI Emulator'
|
||||
},
|
||||
'SL_ANDROID9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'Chrome',
|
||||
platform: 'Android',
|
||||
version: '9.0',
|
||||
device: 'Android GoogleAPI Emulator'
|
||||
},
|
||||
'SL_ANDROID10': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'Chrome',
|
||||
platform: 'Android',
|
||||
version: '10.0',
|
||||
device: 'Android GoogleAPI Emulator'
|
||||
},
|
||||
|
||||
'BS_CHROME': {base: 'BrowserStack', browser: 'chrome', os: 'OS X', os_version: 'Yosemite'},
|
||||
'BS_FIREFOX': {base: 'BrowserStack', browser: 'firefox', os: 'Windows', os_version: '10'},
|
||||
|
@ -41,7 +41,7 @@ cef93a51b (pr/24623_top) ci: scripts to review PRs locally
|
||||
637805a0c (pr/24623_base) docs: update `lowercase` pipe example in "AngularJS to Angular" guide (#24588)
|
||||
```
|
||||
|
||||
Knowing `pr/24623_top` and `pr/24623_base` makes it convenient to refer to different SHAs in PR when rebasing or reseting.
|
||||
Knowing `pr/24623_top` and `pr/24623_base` makes it convenient to refer to different SHAs in PR when rebasing or resetting.
|
||||
|
||||
### 2. Review PR
|
||||
|
||||
|
@ -64,3 +64,37 @@ $ ./integration/run_tests.sh
|
||||
|
||||
The test runner will first re-build any stale npm packages, then `cd` into each subdirectory to
|
||||
execute the test.
|
||||
|
||||
## Browser tests
|
||||
|
||||
For integration tests we use the puppeteer provisioned version of Chrome. For both Karma and Protractor tests we set a number of browser testing flags. To avoid duplication, they will be listed and explained here and the code will reference this file for more information.
|
||||
|
||||
### No Sandbox: --no-sandbox
|
||||
|
||||
The sandbox needs to be disabled with the `--no-sandbox` flag for both Karma and Protractor tests, because it causes Chrome to crash on some environments.
|
||||
|
||||
See: http://chromedriver.chromium.org/help/chrome-doesn-t-start
|
||||
See: https://github.com/puppeteer/puppeteer/blob/v1.0.0/docs/troubleshooting.md#chrome-headless-fails-due-to-sandbox-issues
|
||||
|
||||
### Headless: --headless
|
||||
|
||||
So that browsers are not popping up and tearing down when running these tests we run Chrome in headless mode. The `--headless` flag puts Chrome in headless mode and a number of other flags are recommended in this mode as well:
|
||||
|
||||
* `--headless`
|
||||
* `--disable-gpu`
|
||||
* `--disable-dev-shm-usage`
|
||||
* `--hide-scrollbars`
|
||||
* `--mute-audio`
|
||||
|
||||
These come from the flags that puppeteer passes to chrome when it launches it in headless mode: https://github.com/puppeteer/puppeteer/blob/18f2ecdffdfc70e891750b570bfe8bea5b5ca8c2/lib/Launcher.js#L91
|
||||
|
||||
And from the flags that the Karma `ChromeHeadless` browser passes to Chrome: https://github.com/karma-runner/karma-chrome-launcher/blob/5f70a76de87ecbb57f3f3cb556aa6a2a1a4f643f/index.js#L171
|
||||
|
||||
#### Disable shared memory space: --disable-dev-shm-usage
|
||||
|
||||
The `--disable-dev-shm-usage` flag disables the usage of `/dev/shm` because it causes Chrome to crash on some environments.
|
||||
|
||||
On CircleCI, the puppeteer provisioned Chrome crashes with `CI we get Root cause: org.openqa.selenium.WebDriverException: unknown error: DevToolsActivePort file doesn't exist which resolves` without this flag.
|
||||
|
||||
See: https://github.com/puppeteer/puppeteer/blob/v1.0.0/docs/troubleshooting.md#tips
|
||||
See: https://stackoverflow.com/questions/50642308/webdriverexception-unknown-error-devtoolsactiveport-file-doesnt-exist-while-t
|
||||
|
@ -3,7 +3,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 1485,
|
||||
"main-es2015": 141000,
|
||||
"main-es2015": 141569,
|
||||
"polyfills-es2015": 36657
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 1485,
|
||||
"main-es2015": 16312,
|
||||
"main-es2015": 16514,
|
||||
"polyfills-es2015": 36657
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 1485,
|
||||
"main-es2015": 147082,
|
||||
"main-es2015": 147647,
|
||||
"polyfills-es2015": 36657
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 1485,
|
||||
"main-es2015": 136250,
|
||||
"main-es2015": 136777,
|
||||
"polyfills-es2015": 37334
|
||||
}
|
||||
}
|
||||
@ -39,7 +39,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2289,
|
||||
"main-es2015": 246583,
|
||||
"main-es2015": 247198,
|
||||
"polyfills-es2015": 36657,
|
||||
"5-es2015": 751
|
||||
}
|
||||
@ -49,7 +49,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2289,
|
||||
"main-es2015": 225584,
|
||||
"main-es2015": 226144,
|
||||
"polyfills-es2015": 36657,
|
||||
"5-es2015": 779
|
||||
}
|
||||
@ -64,4 +64,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -53,7 +53,9 @@ function testNonBazel() {
|
||||
yarn --cwd node_modules/@angular/cli version --new-version 0.0.0 --no-git-tag-version
|
||||
# re-add build-angular
|
||||
yarn add --dev file:../../../node_modules/@angular-devkit/build-angular
|
||||
yarn webdriver-manager update --gecko=false --standalone=false ${CI_CHROMEDRIVER_VERSION_ARG:---versions.chrome 2.45}
|
||||
# TODO: Find a way to use the Chrome version provided by `puppeteer` as the rest of the
|
||||
# integration projects. See https://github.com/angular/angular/pull/35049 for details.
|
||||
yarn webdriver-manager update --gecko=false --standalone=false --versions.chrome=79.0.3945.130;
|
||||
ng build --progress=false
|
||||
ng test --progress=false --watch=false
|
||||
ng e2e --configuration=production --webdriver-update=false
|
||||
|
@ -2,18 +2,18 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular-devkit/architect@0.900.0-rc.3":
|
||||
version "0.900.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.0-rc.3.tgz#e01d0296153624d6ef073650484d0d8063bb2a42"
|
||||
integrity sha512-QVD/iL3CxG6nNFveYN7NhsNLir/SmgVGrsPt7FtKJq2tWxmBEsU3d3zYGYNtFNv/WshHZ7K6UFg+VsRpqnQTtQ==
|
||||
"@angular-devkit/architect@0.900.0-rc.11":
|
||||
version "0.900.0-rc.11"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.0-rc.11.tgz#e9f3e5e372d467a220027cf53231b88e8e857fbc"
|
||||
integrity sha512-rRbq4ipppnY4FvVo89Cv+yC7rlt1/VFE/jaB77Ra2tI6zVlFWCTjnMzuc9TYz/3jK1ssThzgEA2sebPDmjH47w==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "9.0.0-rc.3"
|
||||
"@angular-devkit/core" "9.0.0-rc.11"
|
||||
rxjs "6.5.3"
|
||||
|
||||
"@angular-devkit/core@9.0.0-rc.3":
|
||||
version "9.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.0-rc.3.tgz#42703073d0f83eee9b319d59d5f61ef407f0ff13"
|
||||
integrity sha512-x9x4yj0HMzg8rAIgxEPrefjMXB0GOlgzzbHxgcB+qxIP/feASzhnB28OyXSCa4Sgv3er3Jd5wzV/P3QYHKLeoQ==
|
||||
"@angular-devkit/core@9.0.0-rc.11":
|
||||
version "9.0.0-rc.11"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.0-rc.11.tgz#9e69545eb21284a573ad78e4c33003f2ea25afd5"
|
||||
integrity sha512-ki7Sln+mQdCctJNBalzy70tiFn2hOCY2Yyte8B0xKWVHnofZySvG+ANzoLgodnKFOBH18AQy35FhgzZM++N9tQ==
|
||||
dependencies:
|
||||
ajv "6.10.2"
|
||||
fast-json-stable-stringify "2.0.0"
|
||||
@ -21,12 +21,12 @@
|
||||
rxjs "6.5.3"
|
||||
source-map "0.7.3"
|
||||
|
||||
"@angular-devkit/schematics@9.0.0-rc.3":
|
||||
version "9.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.0.0-rc.3.tgz#3c3177098251a003a9e1c0ab4f65e055fea7d941"
|
||||
integrity sha512-fxwhzE8i1zNs+f4wjpFJfwLE9FIJGYvmDCdXjh5eBA4rPBx8tIrGYXaCZgOrVVkrpO+1bNgrqCaC/lFm4jY8oA==
|
||||
"@angular-devkit/schematics@9.0.0-rc.11":
|
||||
version "9.0.0-rc.11"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.0.0-rc.11.tgz#e0d4d271d8d783ebf05eced576262f20e6c3562c"
|
||||
integrity sha512-aJqOLzsoAkVj3AVTf1ehH2hA9wHHz1+7TTtfqI+Yx+S3jFyvGmnKrNBCKtMuIV5JdEHiXmhhuGbNBHwRFWpOow==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "9.0.0-rc.3"
|
||||
"@angular-devkit/core" "9.0.0-rc.11"
|
||||
ora "4.0.2"
|
||||
rxjs "6.5.3"
|
||||
|
||||
@ -35,16 +35,16 @@
|
||||
dependencies:
|
||||
"@microsoft/api-extractor" "^7.3.9"
|
||||
shelljs "0.8.2"
|
||||
tsickle "^0.37.1"
|
||||
tsickle "^0.38.0"
|
||||
|
||||
"@angular/cli@file:../../node_modules/@angular/cli":
|
||||
version "9.0.0-rc.3"
|
||||
version "9.0.0-rc.11"
|
||||
dependencies:
|
||||
"@angular-devkit/architect" "0.900.0-rc.3"
|
||||
"@angular-devkit/core" "9.0.0-rc.3"
|
||||
"@angular-devkit/schematics" "9.0.0-rc.3"
|
||||
"@schematics/angular" "9.0.0-rc.3"
|
||||
"@schematics/update" "0.900.0-rc.3"
|
||||
"@angular-devkit/architect" "0.900.0-rc.11"
|
||||
"@angular-devkit/core" "9.0.0-rc.11"
|
||||
"@angular-devkit/schematics" "9.0.0-rc.11"
|
||||
"@schematics/angular" "9.0.0-rc.11"
|
||||
"@schematics/update" "0.900.0-rc.11"
|
||||
"@yarnpkg/lockfile" "1.1.0"
|
||||
ansi-colors "4.1.1"
|
||||
debug "^4.1.1"
|
||||
@ -61,29 +61,29 @@
|
||||
universal-analytics "^0.4.20"
|
||||
uuid "^3.3.2"
|
||||
|
||||
"@bazel/bazel-darwin_x64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/bazel-darwin_x64/-/bazel-darwin_x64-1.1.0.tgz#9402ecadfaf0383bc366ef5b37b933e0d0e804fc"
|
||||
integrity sha512-/dnpkjqnl2Qrcy+qFerOe+lV9QZ2HoUHtTplQgRxa+OH8AtQ7mcopdJ9/3Y10GqgT2Kp+AR6G99R59/Si+BOMg==
|
||||
"@bazel/bazel-darwin_x64@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/bazel-darwin_x64/-/bazel-darwin_x64-2.0.0.tgz#bd678069216dd470c6816a22c405f21e7f048038"
|
||||
integrity sha512-I/pP+B+2xfY0g+OEpEcVnk8rizuC761pAzBOQjP3b+gz3AzeRgm05CpcSY7tfPIppMSYoy3uTZJ1XlwgUg7IQQ==
|
||||
|
||||
"@bazel/bazel-linux_x64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/bazel-linux_x64/-/bazel-linux_x64-1.1.0.tgz#98d75240e3e9ff5ba14fa48d6241d5d741e89926"
|
||||
integrity sha512-yDR1URphRQTkXYjl4U2NLmbGR8ar8imhytK3txZZqlPf5pfWI/7wa7gSg0H4VbRRLIGAy/nD2eXZpgSj1eUiqA==
|
||||
"@bazel/bazel-linux_x64@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/bazel-linux_x64/-/bazel-linux_x64-2.0.0.tgz#2c76e3301e9178a90ec3ad00649e89b953eda0b7"
|
||||
integrity sha512-iOr45G+511IbP7e+ISriG97WpfCAVXekTrTgL5mGg3NDBFCVNs350VquHAvmlXAoP5+IEug2pCOlkdEl4bLl8g==
|
||||
|
||||
"@bazel/bazel-win32_x64@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/bazel-win32_x64/-/bazel-win32_x64-1.1.0.tgz#e9c80a8c6495834ee7fc6184c425284d1151ac38"
|
||||
integrity sha512-mj3ujcifKO+hjAjHvLoutYxzs90YWuc/fYJuVaEQrk4YFrRW5g70OpjN74zzBHRstObOjSZ3cOj+HDB19SIFKw==
|
||||
"@bazel/bazel-win32_x64@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/bazel-win32_x64/-/bazel-win32_x64-2.0.0.tgz#f12ac0738d2eac0fd255f099776194807cedfe50"
|
||||
integrity sha512-5qs2qoa/paG/YYEM0yvrwuJIShoPVK2FX+Oz9jEWAQJsmU4drHA9Aq+gbBOirEFLmvYhleZ9XORCwu/5uAo8vA==
|
||||
|
||||
"@bazel/bazel@file:../../node_modules/@bazel/bazel":
|
||||
version "1.1.0"
|
||||
version "2.0.0"
|
||||
dependencies:
|
||||
"@bazel/hide-bazel-files" latest
|
||||
optionalDependencies:
|
||||
"@bazel/bazel-darwin_x64" "1.1.0"
|
||||
"@bazel/bazel-linux_x64" "1.1.0"
|
||||
"@bazel/bazel-win32_x64" "1.1.0"
|
||||
"@bazel/bazel-darwin_x64" "2.0.0"
|
||||
"@bazel/bazel-linux_x64" "2.0.0"
|
||||
"@bazel/bazel-win32_x64" "2.0.0"
|
||||
|
||||
"@bazel/hide-bazel-files@latest":
|
||||
version "0.37.1"
|
||||
@ -143,21 +143,21 @@
|
||||
resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.14.tgz#0e0810a0a174e50e22dfe8edb30599840712f22d"
|
||||
integrity sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q==
|
||||
|
||||
"@schematics/angular@9.0.0-rc.3":
|
||||
version "9.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.0.0-rc.3.tgz#804fc0a360109298ec694d77bdaadcd3652cf094"
|
||||
integrity sha512-HIOlaReZFGQpjoLxGqDpNbw0UDg306KrPTDZ8eI0lSEAz6KKY3CsMWSGZ57cKRfw5PJYIybbpWeIvGsXq6i7uQ==
|
||||
"@schematics/angular@9.0.0-rc.11":
|
||||
version "9.0.0-rc.11"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.0.0-rc.11.tgz#d544c0d4e7b3dd59ed56be5183e038ebe06a165e"
|
||||
integrity sha512-9InC+F71KiPXE0jl7Ow4iPFJ2AZZDbfTM6yWZoYLk3hzTCohAZZciBl00Tfyu2uerGshx8akbJMLySjXtf+q0g==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "9.0.0-rc.3"
|
||||
"@angular-devkit/schematics" "9.0.0-rc.3"
|
||||
"@angular-devkit/core" "9.0.0-rc.11"
|
||||
"@angular-devkit/schematics" "9.0.0-rc.11"
|
||||
|
||||
"@schematics/update@0.900.0-rc.3":
|
||||
version "0.900.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.900.0-rc.3.tgz#b33b1d006fd76b6fc7a19ab3c84251d6ffc7fb9b"
|
||||
integrity sha512-+CFVwY3hLE54ZOjf1Big+49XW92Aiu7+wgU/kXnKhAGx7HtM4jflfMMtMJq2IjmBfr3+ObjwETSBQkWv4RtGYw==
|
||||
"@schematics/update@0.900.0-rc.11":
|
||||
version "0.900.0-rc.11"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.900.0-rc.11.tgz#d22df30f13a6f38970b759db61ad84d3f9b03a78"
|
||||
integrity sha512-nV0oCPzzd0vi2Exo1910rWXwz/RnMc4zF9FxSOCZzsIv+AkwIehhL815OKyjUSCzU9+IM0/o1LKkPPrSWK7QEA==
|
||||
dependencies:
|
||||
"@angular-devkit/core" "9.0.0-rc.3"
|
||||
"@angular-devkit/schematics" "9.0.0-rc.3"
|
||||
"@angular-devkit/core" "9.0.0-rc.11"
|
||||
"@angular-devkit/schematics" "9.0.0-rc.11"
|
||||
"@yarnpkg/lockfile" "1.1.0"
|
||||
ini "1.3.5"
|
||||
npm-package-arg "^7.0.0"
|
||||
@ -1912,10 +1912,10 @@ tough-cookie@~2.4.3:
|
||||
psl "^1.1.24"
|
||||
punycode "^1.4.1"
|
||||
|
||||
tsickle@^0.37.1:
|
||||
version "0.37.1"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.37.1.tgz#2f8a87c1b15766e866457bd06fb6c0e0d84eed09"
|
||||
integrity sha512-0GwgOJEnsmRsrONXCvcbAWY0CvdqF3UugPVoupUpA8Ul0qCPTuqqq0ou/hLqtKZOyyulzCP6MYRjb9/J1g9bJg==
|
||||
tsickle@^0.38.0:
|
||||
version "0.38.0"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.38.0.tgz#89f5952c9bb3ba0b36dc384975e23cf90e584822"
|
||||
integrity sha512-k7kI6afBuLd2jIrj9JR8lKhEkp99sFVRKQbHeaHQkdvDaH5AvzwqA/qX+aNj28OfuAsWryOKAZoXm24l7JelEw==
|
||||
|
||||
tslib@^1.9.0:
|
||||
version "1.9.3"
|
||||
|
@ -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
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "6bcef105e75cac3c5f8212e0d0431b6ec1aaa1963e093b0091474ab98ecf29d2",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.2.2/rules_nodejs-1.2.2.tar.gz"],
|
||||
sha256 = "b6670f9f43faa66e3009488bbd909bc7bc46a5a9661a33f6bc578068d1837f37",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.3.0/rules_nodejs-1.3.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Fetch sass rules for compiling sass files
|
||||
|
@ -23,11 +23,11 @@
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@bazel/bazel": "file:../../node_modules/@bazel/bazel",
|
||||
"@bazel/karma": "1.2.2",
|
||||
"@bazel/protractor": "1.2.2",
|
||||
"@bazel/rollup": "1.2.2",
|
||||
"@bazel/terser": "1.2.2",
|
||||
"@bazel/typescript": "1.2.2",
|
||||
"@bazel/karma": "1.3.0",
|
||||
"@bazel/protractor": "1.3.0",
|
||||
"@bazel/rollup": "1.3.0",
|
||||
"@bazel/terser": "1.3.0",
|
||||
"@bazel/typescript": "1.3.0",
|
||||
"@types/jasmine": "2.8.8",
|
||||
"http-server": "0.12.0",
|
||||
"karma": "4.4.1",
|
||||
|
@ -31,6 +31,7 @@
|
||||
chokidar "^3.0.0"
|
||||
convert-source-map "^1.5.1"
|
||||
dependency-graph "^0.7.2"
|
||||
fs-extra "4.0.2"
|
||||
magic-string "^0.25.0"
|
||||
minimist "^1.2.0"
|
||||
reflect-metadata "^0.1.2"
|
||||
@ -92,32 +93,32 @@
|
||||
resolved "https://registry.yarnpkg.com/@bazel/hide-bazel-files/-/hide-bazel-files-0.38.3.tgz#e98231d3d360d51860d9c1a7c3345b40dab4cf81"
|
||||
integrity sha512-o+dNkfDm3qxWQ8h/04cWuTcjR7qnjZi3pQGv4aklVb16oPWx2jF8BzbkwvWuIkdbOl9VnqYP0vaHzwQVJRRcIA==
|
||||
|
||||
"@bazel/karma@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.2.2.tgz#545c0f86f921229879511a9a0b2edba1329a1fb8"
|
||||
integrity sha512-jUTv6DKoLkU3VUIFHCTnRU94qbOBPRZYbg7//2AnzHAS77SQyIAEUG5xU/W26+kle+sj7nBMavlMOxLjsrpMFA==
|
||||
"@bazel/karma@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/karma/-/karma-1.3.0.tgz#8d4c336f04357ccc9633b9b6cdf8c79b15793ef1"
|
||||
integrity sha512-TyGoLBGTt9Mlp/FhwAytYaolUfrwMYuhNNzOYQ0lNzF73oEXOD1G9vIYLn2NvxQLPAaa2guBmNWSf0EkLhMuiw==
|
||||
dependencies:
|
||||
tmp "0.1.0"
|
||||
|
||||
"@bazel/protractor@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.2.2.tgz#33bd58dfbfa33f1ec4b037ef5d4edc5de130aa2b"
|
||||
integrity sha512-PgE/VAwgvtlFPKdCR1o2ofZyc4khLeEu+nztzuohZ1MqD5Yn5tx6QGhOAhLDLNCim1DRqRXct7LerAy1VNm5iA==
|
||||
"@bazel/protractor@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/protractor/-/protractor-1.3.0.tgz#050edc3480a6b5cf2b4c96daeced5cb871839298"
|
||||
integrity sha512-EwTlHbMwHIyPy7FhDiKLum2nlmmy27p/yQzWzN8dRnCIjmq4ezTfgNMnyZ5Z9HiQ2HM4kyzNx1f65eKNXXZK7w==
|
||||
|
||||
"@bazel/rollup@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.2.2.tgz#f5522ac308c41c6e11cbbdf3f0300a1db885410d"
|
||||
integrity sha512-TGdL06eO8ARHEGVYdwA4MXoVM/V8EeFhi2DAMK+VrN609B7/CvtukdlT9KnuAONo5W7mttPzyuCgsvkaADrNOA==
|
||||
"@bazel/rollup@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/rollup/-/rollup-1.3.0.tgz#266b5621bee430efdeb453ade97aa76700c78fd2"
|
||||
integrity sha512-8DnFc4ZUT8kJmZ1bZ86wkqVJYYGBt22p30NhpKUEKUQxROvSr+9lKoAEW+vdJ16rtu8UZDOvHlxr1MqkgnD8Jg==
|
||||
|
||||
"@bazel/terser@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.2.2.tgz#a245e345028fd0e15b76b67da1a60c0d9635c76f"
|
||||
integrity sha512-IsHxbSjbNesSjIQDRpm1tDPwiHadTQIIIOUgtQQN1hDlzmDufTNvqGpB+34MeSLNzn2KVHObqYbYs6LVpW1a8A==
|
||||
"@bazel/terser@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/terser/-/terser-1.3.0.tgz#cb9baf996d87dc5632828943293bfa8e0d779e6f"
|
||||
integrity sha512-esCmSwLH2PLi5Lnq/88hFpAYyYpvsfmhGe7TQI7C27hLTX2jK0eKEm5DU15oJIEjMM1r7zA015eomsNsUScKEQ==
|
||||
|
||||
"@bazel/typescript@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.2.2.tgz#9acb8e07d1bd2b5d9c7afd01b434687b277f9dcb"
|
||||
integrity sha512-qEkkkLOsKvcTvyToiMLVTU67iEHTOzVUIJGDo3+7g8vRL4OqG9EWk0ooNzVLWZ129Caz9TyVyleh1RhuReJWOw==
|
||||
"@bazel/typescript@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-1.3.0.tgz#6df7409ad8b2fcd80dfe859904fe91aacfb1a2cf"
|
||||
integrity sha512-F1Cjnjby+b3cO+rVuZY/9tzepf8wgoXZP9PtFmVWTQ+NtBkKEYx6IQ3AXZJl33mYVoN/Zb5qBTrGW/QfHUvakw==
|
||||
dependencies:
|
||||
protobufjs "6.8.8"
|
||||
semver "5.6.0"
|
||||
@ -1043,6 +1044,15 @@ form-data@~2.3.2:
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs-extra@4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b"
|
||||
integrity sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^7.0.1, fs-extra@~7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
|
@ -1,7 +1,6 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
@ -13,7 +12,12 @@ exports.config = {
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
},
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
|
@ -1,6 +1,11 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
// Env var CHROME_BIN is later picked up by karma-chrome-launcher that is triggered by
|
||||
// `browsers: ['ChromeHeadlessNoSandbox']` below.
|
||||
// See https://github.com/karma-runner/karma-chrome-launcher#usage for more info.
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
@ -25,7 +30,14 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
|
@ -7,8 +7,7 @@
|
||||
"e2e": "ng e2e",
|
||||
"lint": "ng lint",
|
||||
"ng": "ng",
|
||||
"postinstall": "yarn update-webdriver && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"update-webdriver": "webdriver-manager update --gecko=false --standalone=false $CI_CHROMEDRIVER_VERSION_ARG",
|
||||
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"start": "ng serve",
|
||||
"pretest": "ng version",
|
||||
"test": "ng test && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production"
|
||||
@ -44,8 +43,13 @@
|
||||
"karma-jasmine": "2.0.1",
|
||||
"karma-jasmine-html-reporter": "1.4.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"puppeteer": "file:../../node_modules/puppeteer",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "5.18.0",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
},
|
||||
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
|
||||
"resolutions": {
|
||||
"**/webdriver-manager": "file:../../node_modules/webdriver-manager"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
@ -15,8 +14,9 @@ exports.config = {
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN,
|
||||
args: ['--no-sandbox']
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
|
@ -1,6 +1,11 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
// Env var CHROME_BIN is later picked up by karma-chrome-launcher that is triggered by
|
||||
// `browsers: ['ChromeHeadlessNoSandbox']` below.
|
||||
// See https://github.com/karma-runner/karma-chrome-launcher#usage for more info.
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
@ -25,7 +30,14 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
|
@ -7,8 +7,7 @@
|
||||
"e2e": "ng e2e",
|
||||
"lint": "ng lint",
|
||||
"ng": "ng",
|
||||
"postinstall": "yarn update-webdriver && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"update-webdriver": "webdriver-manager update --gecko=false --standalone=false $CI_CHROMEDRIVER_VERSION_ARG",
|
||||
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"start": "ng serve",
|
||||
"pretest": "ng version",
|
||||
"test": "ng e2e --prod && ng xi18n && yarn translate && ng e2e --configuration fr && ng e2e --configuration de",
|
||||
@ -49,9 +48,14 @@
|
||||
"karma-jasmine-html-reporter": "1.4.2",
|
||||
"npm-run-all": "4.1.5",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"puppeteer": "file:../../node_modules/puppeteer",
|
||||
"serve": "11.2.0",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "5.18.0",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
},
|
||||
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
|
||||
"resolutions": {
|
||||
"**/webdriver-manager": "file:../../node_modules/webdriver-manager"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
@ -15,8 +14,9 @@ exports.config = {
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN,
|
||||
args: ['--no-sandbox']
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
|
@ -1,6 +1,11 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
// Env var CHROME_BIN is later picked up by karma-chrome-launcher that is triggered by
|
||||
// `browsers: ['ChromeHeadlessNoSandbox']` below.
|
||||
// See https://github.com/karma-runner/karma-chrome-launcher#usage for more info.
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
@ -25,7 +30,14 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
|
@ -7,8 +7,7 @@
|
||||
"e2e": "ng e2e",
|
||||
"lint": "ng lint",
|
||||
"ng": "ng",
|
||||
"postinstall": "yarn update-webdriver && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"update-webdriver": "webdriver-manager update --gecko=false --standalone=false $CI_CHROMEDRIVER_VERSION_ARG",
|
||||
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"start": "ng serve",
|
||||
"pretest": "ng version",
|
||||
"test": "ng test && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production"
|
||||
@ -44,8 +43,13 @@
|
||||
"karma-jasmine": "2.0.1",
|
||||
"karma-jasmine-html-reporter": "1.4.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"puppeteer": "file:../../node_modules/puppeteer",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "5.18.0",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
},
|
||||
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
|
||||
"resolutions": {
|
||||
"**/webdriver-manager": "file:../../node_modules/webdriver-manager"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
@ -13,7 +12,12 @@ exports.config = {
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
binary: require('puppeteer').executablePath(),
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
},
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
|
@ -1,6 +1,11 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
// Env var CHROME_BIN is later picked up by karma-chrome-launcher that is triggered by
|
||||
// `browsers: ['ChromeHeadlessNoSandbox']` below.
|
||||
// See https://github.com/karma-runner/karma-chrome-launcher#usage for more info.
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
@ -25,7 +30,14 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio']
|
||||
}
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
|
@ -5,8 +5,7 @@
|
||||
"build": "ng build --prod",
|
||||
"e2e": "ng e2e --prod",
|
||||
"test": "yarn e2e && yarn build && node check-output-for-ngdevmode.js",
|
||||
"postinstall": "yarn update-webdriver && ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points",
|
||||
"update-webdriver": "webdriver-manager update --gecko=false --standalone=false $CI_CHROMEDRIVER_VERSION_ARG"
|
||||
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@ -39,8 +38,13 @@
|
||||
"karma-jasmine": "2.0.1",
|
||||
"karma-jasmine-html-reporter": "1.4.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"puppeteer": "file:../../node_modules/puppeteer",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "5.18.0",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
},
|
||||
"//resolutions-comment": "Ensure a single version of webdriver-manager which comes from root node_modules that has already run webdriver-manager update",
|
||||
"resolutions": {
|
||||
"**/webdriver-manager": "file:../../node_modules/webdriver-manager"
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user