diff --git a/.bazelignore b/.bazelignore index 64c85b64d2..c55c9439ca 100644 --- a/.bazelignore +++ b/.bazelignore @@ -1,10 +1,97 @@ +# Bazel does not yet support wildcards or other .gitignore semantics for +# .bazelignore. Two issues for this feature request are outstanding: +# https://github.com/bazelbuild/bazel/issues/7093 +# https://github.com/bazelbuild/bazel/issues/8106 .git node_modules dist aio/content aio/node_modules aio/tools/examples/shared/node_modules -integration/bazel -integration/bazel-schematics/demo -integration/platform-server/node_modules packages/bazel/node_modules +integration/bazel/bazel-bazel +integration/bazel/bazel-bin +integration/bazel/bazel-out +integration/bazel/bazel-testlogs +integration/bazel-schematics/demo +# All integration test node_modules folders +integration/bazel/node_modules +integration/bazel-schematics/node_modules +integration/cli-hello-world/node_modules +integration/cli-hello-world-ivy-compat/node_modules +integration/cli-hello-world-ivy-i18n/node_modules +integration/cli-hello-world-ivy-minimal/node_modules +integration/cli-hello-world-lazy/node_modules +integration/cli-hello-world-lazy-rollup/node_modules +integration/dynamic-compiler/node_modules +integration/hello_world__closure/node_modules +integration/hello_world__systemjs_umd/node_modules +integration/i18n/node_modules +integration/injectable-def/node_modules +integration/ivy-i18n/node_modules +integration/language_service_plugin/node_modules +integration/ng_elements/node_modules +integration/ng_elements_schematics/node_modules +integration/ng_update/node_modules +integration/ng_update_migrations/node_modules +integration/ngcc/node_modules +integration/platform-server/node_modules +integration/service-worker-schema/node_modules +integration/side-effects/node_modules +integration/terser/node_modules +integration/typings_test_ts36/node_modules +integration/typings_test_ts37/node_modules +# All integration test .yarn_local_cache folders +integration/bazel/.yarn_local_cache +integration/bazel-schematics/.yarn_local_cache +integration/cli-hello-world/.yarn_local_cache +integration/cli-hello-world-ivy-compat/.yarn_local_cache +integration/cli-hello-world-ivy-i18n/.yarn_local_cache +integration/cli-hello-world-ivy-minimal/.yarn_local_cache +integration/cli-hello-world-lazy/.yarn_local_cache +integration/cli-hello-world-lazy-rollup/.yarn_local_cache +integration/dynamic-compiler/.yarn_local_cache +integration/hello_world__closure/.yarn_local_cache +integration/hello_world__systemjs_umd/.yarn_local_cache +integration/i18n/.yarn_local_cache +integration/injectable-def/.yarn_local_cache +integration/ivy-i18n/.yarn_local_cache +integration/language_service_plugin/.yarn_local_cache +integration/ng_elements/.yarn_local_cache +integration/ng_elements_schematics/.yarn_local_cache +integration/ng_update/.yarn_local_cache +integration/ng_update_migrations/.yarn_local_cache +integration/ngcc/.yarn_local_cache +integration/platform-server/.yarn_local_cache +integration/service-worker-schema/.yarn_local_cache +integration/side-effects/.yarn_local_cache +integration/terser/.yarn_local_cache +integration/typings_test_ts36/.yarn_local_cache +integration/typings_test_ts37/.yarn_local_cache +# All integration test NPM_PACKAGE_MANIFEST.json folders +integration/bazel/NPM_PACKAGE_MANIFEST.json +integration/bazel-schematics/NPM_PACKAGE_MANIFEST.json +integration/cli-hello-world/NPM_PACKAGE_MANIFEST.json +integration/cli-hello-world-ivy-compat/NPM_PACKAGE_MANIFEST.json +integration/cli-hello-world-ivy-i18n/NPM_PACKAGE_MANIFEST.json +integration/cli-hello-world-ivy-minimal/NPM_PACKAGE_MANIFEST.json +integration/cli-hello-world-lazy/NPM_PACKAGE_MANIFEST.json +integration/cli-hello-world-lazy-rollup/NPM_PACKAGE_MANIFEST.json +integration/dynamic-compiler/NPM_PACKAGE_MANIFEST.json +integration/hello_world__closure/NPM_PACKAGE_MANIFEST.json +integration/hello_world__systemjs_umd/NPM_PACKAGE_MANIFEST.json +integration/i18n/NPM_PACKAGE_MANIFEST.json +integration/injectable-def/NPM_PACKAGE_MANIFEST.json +integration/ivy-i18n/NPM_PACKAGE_MANIFEST.json +integration/language_service_plugin/NPM_PACKAGE_MANIFEST.json +integration/ng_elements/NPM_PACKAGE_MANIFEST.json +integration/ng_elements_schematics/NPM_PACKAGE_MANIFEST.json +integration/ng_update/NPM_PACKAGE_MANIFEST.json +integration/ng_update_migrations/NPM_PACKAGE_MANIFEST.json +integration/ngcc/NPM_PACKAGE_MANIFEST.json +integration/platform-server/NPM_PACKAGE_MANIFEST.json +integration/service-worker-schema/NPM_PACKAGE_MANIFEST.json +integration/side-effects/NPM_PACKAGE_MANIFEST.json +integration/terser/NPM_PACKAGE_MANIFEST.json +integration/typings_test_ts36/NPM_PACKAGE_MANIFEST.json +integration/typings_test_ts37/NPM_PACKAGE_MANIFEST.json diff --git a/.bazelrc b/.bazelrc index c1321d4bf9..37f7b117fb 100644 --- a/.bazelrc +++ b/.bazelrc @@ -33,6 +33,11 @@ build --incompatible_strict_action_env run --incompatible_strict_action_env test --incompatible_strict_action_env +# Do not build runfile trees by default. If an execution strategy relies on runfile +# symlink teee, the tree is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627 +# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df +build --nobuild_runfile_links + ############################### # Release support # # Turn on these settings with # @@ -42,7 +47,7 @@ test --incompatible_strict_action_env # Releases should always be stamped with version control info # This command assumes node on the path and is a workaround for # https://github.com/bazelbuild/bazel/issues/4802 -build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js" +build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp" build:release --stamp ############################### @@ -62,6 +67,16 @@ test --test_output=errors # Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc +################################## +# Settings for integration tests # +################################## + +# Trick bazel into treating BUILD files under integration/bazel as being regular files +# This lets us glob() up all the files inside this integration test to make them inputs to tests +# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it) +build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e +query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e + ################################ # Temporary Settings for Ivy # ################################ @@ -100,7 +115,6 @@ build:remote --javabase=@rbe_ubuntu1604_angular//java:jdk build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8 build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8 build:remote --crosstool_top=@rbe_ubuntu1604_angular//cc:toolchain -build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 build:remote --extra_toolchains=@rbe_ubuntu1604_angular//config:cc-toolchain build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular build:remote --host_platform=//tools:rbe_ubuntu1604-angular diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000000..be57528b92 --- /dev/null +++ b/.bazelversion @@ -0,0 +1,3 @@ +2.1.1 +# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117] +# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl diff --git a/.circleci/bazel.linux.rc b/.circleci/bazel.linux.rc index 4889f934b2..afb8fd47f1 100644 --- a/.circleci/bazel.linux.rc +++ b/.circleci/bazel.linux.rc @@ -14,7 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache # Bazel doesn't calculate the memory ceiling correctly when running under Docker. # Limit Bazel to consuming resources that fit in CircleCI "xlarge" class # https://circleci.com/docs/2.0/configuration-reference/#resource_class -build --local_resources=14336,8.0,1.0 +build --local_cpu_resources=8 +build --local_ram_resources=14336 # All build executed remotely should be done using our RBE configuration. build:remote --google_default_credentials diff --git a/.circleci/bazel.windows.rc b/.circleci/bazel.windows.rc index 9efa954554..ce3e53392c 100644 --- a/.circleci/bazel.windows.rc +++ b/.circleci/bazel.windows.rc @@ -10,6 +10,10 @@ try-import %workspace%/.circleci/bazel.common.rc # speeding up the analysis time significantly with Bazel managed node dependencies on the CI. build --repository_cache=C:/Users/circleci/bazel_repository_cache +# Manually set the local resources used in windows CI runs +build --local_ram_resources=13500 +build --local_cpu_resources=4 + # All windows jobs run on master and should use http caching build --remote_http_cache=https://storage.googleapis.com/angular-team-cache build --remote_accept_cached=true diff --git a/.circleci/config.yml b/.circleci/config.yml index 8b67136c25..da85f1baed 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,15 +22,18 @@ version: 2.1 # **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match. # **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks. # See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI. -var_3: &cache_key v3-angular-node-12-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} -var_4: &cache_key_fallback v3-angular-node-12- -var_3_win: &cache_key_win v5-angular-win-node-12-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} -var_4_win: &cache_key_win_fallback v5-angular-win-node-12- +var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} +# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache +# folder will contain all previously used versions and ultimately cause the cache restoring to +# be slower due to its growing size. +var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }} +var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} +var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }} # Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the # cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable. -var_5: &components_repo_unit_tests_cache_key v5-angular-components-97a7e2babbccd3dc58e7b3364004e45ed5bd9968 -var_6: &components_repo_unit_tests_cache_key_fallback v5-angular-components- +var_5: &components_repo_unit_tests_cache_key v7-angular-components-448523bffffecd2b53a3d2854c3051b6b7a3934f +var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components- # Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and # `build-ivy-npm-packages`. @@ -65,8 +68,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: @@ -78,20 +81,6 @@ executors: resource_class: << parameters.resource_class >> working_directory: ~/ng - browsers-executor: - parameters: - resource_class: - type: string - default: medium - docker: - # The browser docker image comes with Chrome and Firefox preinstalled. This is just - # needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be - # fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a - # docker image with browsers pre-installed. - - image: circleci/node:12.14.1-browsers@sha256:792797ab9be3179be7c9fc38a0931a3349288e699467c8d646d7c54e148ae46c - resource_class: << parameters.resource_class >> - working_directory: ~/ng - windows-executor: working_directory: ~/ng resource_class: windows.medium @@ -120,11 +109,45 @@ commands: - attach_workspace: at: *workspace_location + # 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: Install shared Chrome libs + command: | + 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 + + # Install java runtime which is required by some integration tests such as + # //integration:hello_world__closure_test, //integration:i18n_test and + # //integration:ng_elements_test to run the closure compiler + install_java: + description: Install java + steps: + - run: + name: Install java + command: | + sudo apt-get update + # Install java runtime + sudo apt-get install default-jre + # Initializes the CI environment by setting up common environment variables. init_environment: description: Initializing environment (setting up variables) steps: - - run: ./.circleci/env.sh + - 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. @@ -175,10 +198,11 @@ commands: - *cache_key_win_fallback # Reinstall to get windows binaries. - run: yarn install --frozen-lockfile --non-interactive - # Install @bazel/bazel globally and use that for the first run. + # Install @bazel/bazelisk globally and use that for the first run. # Workaround for https://github.com/bazelbuild/rules_nodejs/issues/894 - - run: yarn global add @bazel/bazel@$env:BAZEL_VERSION - - run: bazel info + # NB: the issue was for @bazel/bazel but the same problem applies to @bazel/bazelisk + - run: yarn global add @bazel/bazelisk@$env:BAZELISK_VERSION + - run: bazelisk info notify_webhook_on_fail: description: Notify a webhook about failure @@ -202,6 +226,7 @@ jobs: executor: default-executor steps: - checkout + - init_environment - run: name: Rebase PR on target branch # After checkout, rebase on top of target branch. @@ -211,7 +236,7 @@ jobs: git config user.name "angular-ci" git config user.email "angular-ci" # Rebase PR on top of target branch. - node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER} + node tools/rebase-pr.js else echo "This build is not over a PR, nothing to do." fi @@ -220,7 +245,6 @@ jobs: keys: - *cache_key - *cache_key_fallback - - init_environment - run: name: Running Yarn install command: yarn install --frozen-lockfile --non-interactive @@ -248,21 +272,25 @@ jobs: - custom_attach_workspace - init_environment - - run: 'yarn bazel:format -mode=check || - (echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)' - # Run the skylark linter to check our Bazel rules - - run: 'yarn bazel:lint || - (echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)' - - - run: yarn gulp lint + - run: yarn -s tslint + - run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check + - run: yarn -s ts-circular-deps:check + - run: yarn -s ng-dev pullapprove verify + - run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE test: executor: name: default-executor - resource_class: xlarge + # Now that large integration tests are running locally in parallel (they can't run on RBE yet + # as they require network access for yarn install), this test is running out of memory + # consistently with the xlarge machine. + # TODO: switch back to xlarge once integration tests are running on remote-exec + resource_class: 2xlarge+ steps: - custom_attach_workspace - init_environment + - install_chrome_libs + - install_java - run: command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only no_output_timeout: 20m @@ -275,6 +303,7 @@ jobs: steps: - custom_attach_workspace - init_environment + - install_chrome_libs # We need to explicitly specify the --symlink_prefix option because otherwise we would # not be able to easily find the output bin directory when uploading artifacts for size # measurements. @@ -301,11 +330,7 @@ jobs: path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br destination: core/todo/bundle.br - # This job is currently a PoC for running tests on SauceLabs via bazel. It runs a subset of the - # tests in `legacy-unit-tests-saucelabs` (see - # [BUILD.bazel](https://github.com/angular/angular/blob/ef44f51d5/BUILD.bazel#L66-L92)). - # - # NOTE: This is currently limited to master builds only. See the `default_workflow` configuration. + # NOTE: This is currently limited to master builds only. See the `monitoring` configuration. saucelabs_view_engine: executor: name: default-executor @@ -318,16 +343,18 @@ jobs: - init_environment - init_saucelabs_environment - run: - name: Run Bazel tests on Saucelabs + name: Run Bazel tests on Saucelabs with ViewEngine # See /tools/saucelabs/README.md for more info command: | yarn bazel run //tools/saucelabs:sauce_service_setup - yarn bazel test //:saucelabs_unit_tests_poc_suite --config=saucelabs + TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "ivy-only", ...) except attr("tags", "fixme-saucelabs-ve", ...)') + yarn bazel test --config=saucelabs ${TESTS} yarn bazel run //tools/saucelabs:sauce_service_stop - no_output_timeout: 20m + no_output_timeout: 40m - notify_webhook_on_fail: webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL + # NOTE: This is currently limited to master builds only. See the `monitoring` configuration. saucelabs_ivy: executor: name: default-executor @@ -340,24 +367,23 @@ jobs: - init_environment - init_saucelabs_environment - run: - name: Run Bazel tests on Saucelabs + name: Run Bazel tests on Saucelabs with Ivy # See /tools/saucelabs/README.md for more info command: | yarn bazel run //tools/saucelabs:sauce_service_setup - yarn bazel test //:saucelabs_unit_tests --config=saucelabs --config=ivy + TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "no-ivy-aot", ...) except attr("tags", "fixme-saucelabs-ivy", ...)') + yarn bazel test --config=saucelabs --config=ivy ${TESTS} yarn bazel run //tools/saucelabs:sauce_service_stop - no_output_timeout: 20m + no_output_timeout: 40m + - notify_webhook_on_fail: + webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL 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 - # Compile dependencies to ivy - # Running `ngcc` here (instead of implicitly via `ng build`) allows us to take advantage of - # the parallel, async mode speed-up (~20-25s on CI). - - run: yarn --cwd aio ngcc --properties es2015 + - install_chrome_libs # Build aio - run: yarn --cwd aio build --progress=false # Lint the code @@ -376,11 +402,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 @@ -390,11 +416,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<>-ci # Run unit tests @@ -420,32 +446,23 @@ jobs: test_docs_examples: parameters: - ivy: + viewengine: 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: - condition: << parameters.ivy >> - steps: - # Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO - # package installer picks up the locally built packages from that location. - # *Note*: We could also adjust the packages installer, but given we won't have - # two different folders of Angular distributions in the future, we should keep - # the packages installer unchanged. - - run: mv dist/packages-dist-ivy-aot dist/packages-dist # Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled. # Since the parallelism is set to "5", there will be five parallel CircleCI containers. # with either "0", "1", etc as node index. This can be passed to the "--shard" argument. - - run: yarn --cwd aio example-e2e --setup --local <<# parameters.ivy >>--ivy<> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2 + - run: yarn --cwd aio example-e2e --setup --local <<# parameters.viewengine >>--viewengine<> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2 # This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`. aio_preview: @@ -465,11 +482,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 @@ -491,7 +508,7 @@ jobs: steps: - custom_attach_workspace - init_environment - - run: node scripts/build-packages-dist.js + - run: node scripts/build/build-packages-dist.js # Save the npm packages from //packages/... for other workflow jobs to read - persist_to_workspace: @@ -507,6 +524,7 @@ jobs: - "node_modules" - "aio/node_modules" - "~/bazel_repository_cache" + - "~/.cache/bazelisk" # Build the ivy npm packages. build-ivy-npm-packages: @@ -516,35 +534,14 @@ jobs: steps: - custom_attach_workspace - init_environment - - run: node scripts/build-ivy-npm-packages.js + - run: node scripts/build/build-ivy-npm-packages.js # Save the npm packages from //packages/... for other workflow jobs to read - persist_to_workspace: root: *workspace_location paths: - ng/dist/packages-dist-ivy-aot - - # We run the integration tests outside of Bazel for now. - # They are a separate workflow job so that they can be easily re-run. - # When the tests are ported to bazel test targets, they should move to the "test" - # job above, as part of the bazel test command. That has flaky_test_attempts so the - # need to re-run manually should be alleviated. - # 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) - 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 - # on a 4G worker so we use a larger machine here too. - resource_class: xlarge - parallelism: 4 - steps: - - custom_attach_workspace - - init_environment - # Runs the integration tests in parallel across multiple CircleCI container instances. The - # amount of container nodes for this job is controlled by the "parallelism" option. - - run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL} + - ng/dist/zone.js-dist-ivy-aot # This job creates compressed tarballs (`.tgz` files) for all Angular packages and stores them as # build artifacts. This makes it easy to try out changes from a PR build for testing purposes. @@ -608,12 +605,11 @@ 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 @@ -632,12 +628,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 @@ -724,7 +719,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: @@ -742,6 +737,7 @@ jobs: cp dist/bin/packages/zone.js/npm_package/dist/zone-mix.js ./packages/zone.js/test/extra/ && cp dist/bin/packages/zone.js/npm_package/dist/zone-patch-electron.js ./packages/zone.js/test/extra/ && yarn --cwd packages/zone.js electrontest + - run: yarn --cwd packages/zone.js jesttest # Windows jobs # Docs: https://circleci.com/docs/2.0/hello-world-windows/ @@ -797,17 +793,6 @@ workflows: - legacy-unit-tests-saucelabs: requires: - setup - - saucelabs_ivy: - requires: - - test_ivy_aot - - saucelabs_view_engine: - # This job is currently a PoC and a subset of `legacy-unit-tests-saucelabs`. Running on - # master only to avoid wasting resources. - # TODO: Run this job on all branches (including PRs) as soon as it is not a PoC and - # we can remove the legacy saucelabs job. - <<: *only_on_master - requires: - - setup - test_aio: requires: - setup @@ -829,10 +814,10 @@ workflows: requires: - build-npm-packages - test_docs_examples: - name: test_docs_examples_ivy - ivy: true + name: test_docs_examples_viewengine + viewengine: true requires: - - build-ivy-npm-packages + - build-npm-packages - aio_preview: # Only run on PR builds. (There can be no previews for non-PR builds.) <<: *only_on_pull_requests @@ -841,9 +826,6 @@ workflows: - test_aio_preview: requires: - aio_preview - - integration_test: - requires: - - build-npm-packages - publish_packages_as_artifacts: requires: - build-npm-packages @@ -856,22 +838,19 @@ workflows: # Only publish if tests and integration tests pass - test - test_ivy_aot - - integration_test - - saucelabs_ivy # Only publish if `aio`/`docs` tests using the locally built Angular packages pass - test_aio_local - test_aio_local_viewengine - test_docs_examples - - test_docs_examples_ivy + - test_docs_examples_viewengine # Get the artifacts to publish from the build-packages-dist job # since the publishing script expects the legacy outputs layout. - build-npm-packages - build-ivy-npm-packages - legacy-unit-tests-saucelabs - # 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 @@ -890,7 +869,7 @@ workflows: requires: - test_ivy_aot - aio_monitoring: + monitoring: jobs: - setup - aio_monitoring_stable: @@ -899,8 +878,26 @@ workflows: - aio_monitoring_next: requires: - setup + - saucelabs_ivy: + # Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it + # each karma_web_test target is provisioning and tearing down browsers which is adding + # a lot of overhead. Running once daily on master only to avoid wasting resources and + # slowing down CI for PRs. + # TODO: Run this job on all branches (including PRs) once karma_web_test targets can + # share provisioned browsers and we can remove the legacy saucelabs job. + requires: + - setup + - saucelabs_view_engine: + # Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it + # each karma_web_test target is provisioning and tearing down browsers which is adding + # a lot of overhead. Running once daily on master only to avoid wasting resources and + # slowing down CI for PRs. + # TODO: Run this job on all branches (including PRs) once karma_web_test targets can + # share provisioned browsers and we can remove the legacy saucelabs job. + requires: + - setup triggers: - schedule: <<: *only_on_master - # Runs AIO monitoring jobs at 10:00AM every day. + # Runs monitoring jobs at 10:00AM every day. cron: "0 10 * * *" diff --git a/.circleci/env.sh b/.circleci/env.sh index 5fd67478a0..20cc3e3b2f 100755 --- a/.circleci/env.sh +++ b/.circleci/env.sh @@ -3,6 +3,7 @@ # Variables readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; +readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache"; # Load helpers and make them available everywhere (through `$BASH_ENV`). source $envHelpersPath; @@ -14,27 +15,23 @@ echo "source $envHelpersPath;" >> $BASH_ENV; #################################################################################################### # See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. #################################################################################################### +setPublicVar CI "$CI" setPublicVar PROJECT_ROOT "$projectDir"; 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_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}"; +setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}"; +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"; +setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME"; +setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME"; #################################################################################################### @@ -77,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo" setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git" setPublicVar COMPONENTS_REPO_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 "448523bffffecd2b53a3d2854c3051b6b7a3934f" #################################################################################################### @@ -91,6 +88,22 @@ openssl aes-256-cbc -d -in "${projectDir}/.circleci/gcp_token" \ #################################################################################################### cp "${projectDir}/.circleci/bazel.linux.rc" "$HOME/.bazelrc"; +#################################################################################################### +# Create shell script in /tmp for Bazel actions to access CI envs without +# busting the cache. Used by payload-size.sh script in integration tests. +#################################################################################################### +readonly bazelVarEnv="/tmp/bazel-ci-env.sh" +echo "# Setup by /.circle/env.sh" > $bazelVarEnv +echo "export PROJECT_ROOT=\"${PROJECT_ROOT}\";" >> $bazelVarEnv +echo "export CI_BRANCH=\"${CI_BRANCH}\";" >> $bazelVarEnv +echo "export CI_BUILD_URL=\"${CI_BUILD_URL}\";" >> $bazelVarEnv +echo "export CI_COMMIT=\"${CI_COMMIT}\";" >> $bazelVarEnv +echo "export CI_COMMIT_RANGE=\"${CI_COMMIT_RANGE}\";" >> $bazelVarEnv +echo "export CI_PULL_REQUEST=\"${CI_PULL_REQUEST}\";" >> $bazelVarEnv +echo "export CI_REPO_NAME=\"${CI_REPO_NAME}\";" >> $bazelVarEnv +echo "export CI_REPO_OWNER=\"${CI_REPO_OWNER}\";" >> $bazelVarEnv +echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOKEN}\";" >> $bazelVarEnv + #################################################################################################### #################################################################################################### ## Source `$BASH_ENV` to make the variables available immediately. ## diff --git a/.circleci/get-commit-range.js b/.circleci/get-commit-range.js deleted file mode 100644 index c5f569c6f6..0000000000 --- a/.circleci/get-commit-range.js +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env node - -/** - * **Usage:** - * ``` - * node get-commit-range [ []] - * ``` - * - * 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(); - }); -} diff --git a/.circleci/windows-env.ps1 b/.circleci/windows-env.ps1 index a5b065d584..ece28bb8bf 100644 --- a/.circleci/windows-env.ps1 +++ b/.circleci/windows-env.ps1 @@ -14,12 +14,12 @@ Add-Content $profile '$Env:path = "${Env:ProgramFiles}\nodejs\;C:\Users\circleci # Environment variables for Bazel Add-Content $profile '$Env:BAZEL_SH = "C:\tools\msys64\usr\bin\bash.exe"' -# Get the bazel version devdep and store it in a global var for use in the circleci job. -$bazelVersion = & ${Env:ProgramFiles}\nodejs\node.exe -e "console.log(require('./package.json').devDependencies['@bazel/bazel'])" -# This is a tricky situation: we want $bazelVersion to be evaluated but not $Env:BAZEL_VERSION. +# Get the bazelisk version devdep and store it in a global var for use in the circleci job. +$bazeliskVersion = & ${Env:ProgramFiles}\nodejs\node.exe -e "console.log(require('./package.json').devDependencies['@bazel/bazelisk'])" +# This is a tricky situation: we want $bazeliskVersion to be evaluated but not $Env:BAZELISK_VERSION. # Formatting works https://stackoverflow.com/questions/32127583/expand-variable-inside-single-quotes -$bazelVersionGlobalVar = '$Env:BAZEL_VERSION = "{0}"' -f $bazelVersion -Add-Content $profile $bazelVersionGlobalVar +$bazeliskVersionGlobalVar = '$Env:BAZELISK_VERSION = "{0}"' -f $bazeliskVersion +Add-Content $profile $bazeliskVersionGlobalVar # Remove the CircleCI checkout SSH override, because it breaks cloning repositories through Bazel. # See https://circleci.com/gh/angular/angular/401454 for an example. diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml index a8350d626d..4b88f003a7 100644 --- a/.github/workflows/lock-closed.yml +++ b/.github/workflows/lock-closed.yml @@ -7,6 +7,7 @@ on: jobs: lock_closed: + if: github.repository == 'angular/angular' runs-on: ubuntu-latest steps: - uses: angular/dev-infra/github-actions/lock-closed@66462f6 diff --git a/.ng-dev-config.ts b/.ng-dev-config.ts new file mode 100644 index 0000000000..ae4c7c0897 --- /dev/null +++ b/.ng-dev-config.ts @@ -0,0 +1,79 @@ +// The configuration for `ng-dev commit-message` commands. +const commitMessage = { + 'maxLength': 120, + 'minBodyLength': 100, + 'types': [ + 'build', + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + 'release', + 'style', + 'test', + ], + 'scopes': [ + 'animations', + 'bazel', + 'benchpress', + 'changelog', + 'common', + 'compiler', + 'compiler-cli', + 'core', + 'dev-infra', + 'docs-infra', + 'elements', + 'forms', + 'http', + 'language-service', + 'localize', + 'ngcc', + 'packaging', + 'platform-browser', + 'platform-browser-dynamic', + 'platform-server', + 'platform-webworker', + 'platform-webworker-dynamic', + 'router', + 'service-worker', + 'upgrade', + 've', + 'zone.js', + ] +}; + +// The configuration for `ng-dev format` commands. +const format = { + 'clang-format': { + 'matchers': [ + 'dev-infra/**/*.{js,ts}', + 'packages/**/*.{js,ts}', + '!packages/zone.js', + '!packages/common/locales/**/*.{js,ts}', + '!packages/common/src/i18n/available_locales.ts', + '!packages/common/src/i18n/currencies.ts', + '!packages/common/src/i18n/locale_en.ts', + 'modules/benchmarks/**/*.{js,ts}', + 'modules/playground/**/*.{js,ts}', + 'tools/**/*.{js,ts}', + '!tools/gulp-tasks/cldr/extract.js', + '!tools/public_api_guard/**/*.d.ts', + '!tools/ts-api-guardian/test/fixtures/**', + '*.{js,ts}', + '!**/node_modules/**', + '!**/dist/**', + '!**/built/**', + '!shims_for_IE.js', + ] + }, + 'buildifier': true +}; + +// Export function to build ng-dev configuration object. +module.exports = { + commitMessage, + format, +}; diff --git a/.pullapprove.yml b/.pullapprove.yml index e81307c268..d78318ccd5 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -40,6 +40,7 @@ # AndrewKushnir - Andrew Kushnir # andrewseguin - Andrew Seguin # atscott - Andrew Scott +# ayazhafiz - Ayaz Hafiz # clydin - Charles Lyding # crisbeto - Kristiyan Kostadinov # dennispbrown - Denny Brown @@ -96,6 +97,12 @@ version: 3 +# Meta field that goes unused by PullApprove to allow for defining aliases to be +# used throughout the config. +meta: + 1: &can-be-global-approved "\"global-approvers\" not in groups.approved" + 2: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved" + # turn on 'draft' support # https://docs.pullapprove.com/config/github-api-version/ # https://developer.github.com/v3/previews/#draft-pull-requests @@ -108,14 +115,55 @@ 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: + # ========================================================= + # Global Approvers + # + # All reviews performed for global approvals require using + # the `Reviewed-for:` specifier to set the approval + # specificity as documented at: + # https://docs.pullapprove.com/reviewed-for/ + # ========================================================= + global-approvers: + type: optional + reviewers: + teams: + - framework-global-approvers + reviews: + request: 0 + required: 1 + reviewed_for: required + + # ========================================================= + # Global Approvers For Docs + # + # All reviews performed for global docs approvals require + # using the `Reviewed-for:` specifier to set the approval + # specificity as documented at: + # https://docs.pullapprove.com/reviewed-for/ + # ========================================================= + global-docs-approvers: + type: optional + reviewers: + teams: + - framework-global-approvers-for-docs-only-changes + reviews: + request: 0 + required: 1 + reviewed_for: required + # ========================================================= # Framework: Animations # ========================================================= fw-animations: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/animations/**', @@ -131,9 +179,6 @@ groups: reviewers: users: - matsko - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -141,8 +186,10 @@ groups: # ========================================================= fw-compiler: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > - contains_any_globs(files, [ + contains_any_globs(files.exclude('packages/compiler-cli/ngcc/**'), [ 'packages/compiler/**', 'packages/examples/compiler/**', 'packages/compiler-cli/**', @@ -157,9 +204,6 @@ groups: - AndrewKushnir - JoostK - kara - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -167,19 +211,31 @@ groups: # ========================================================= fw-ngcc: conditions: - - > - contains_any_globs(files, [ - 'packages/compiler-cli/ngcc/**' - ]) + - *can-be-global-approved + - *can-be-global-docs-approved + - files.include('packages/compiler-cli/ngcc/**') reviewers: users: - alxhub - gkalpak - JoostK - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes + + + # ========================================================= + # Framework: Migrations + # ========================================================= + fw-migrations: + conditions: + - *can-be-global-approved + - *can-be-global-docs-approved + - files.include("packages/core/schematics/**") + reviewers: + users: + - alxhub + - crisbeto + - devversion + - kara # ========================================================= @@ -187,8 +243,10 @@ groups: # ========================================================= fw-core: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > - contains_any_globs(files, [ + contains_any_globs(files.exclude("packages/core/schematics/**"), [ 'packages/core/**', 'packages/examples/core/**', 'packages/common/**', @@ -254,7 +312,6 @@ groups: 'aio/content/examples/ngmodules/**', 'aio/content/guide/ngmodule-api.md', 'aio/content/guide/ngmodule-faq.md', - 'aio/content/examples/ngmodule-faq/**', 'aio/content/guide/ngmodule-vs-jsmodule.md', 'aio/content/guide/module-types.md', 'aio/content/guide/template-syntax.md', @@ -297,9 +354,6 @@ groups: - kara - mhevery - pkozlowski-opensource - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -307,10 +361,11 @@ groups: # ========================================================= fw-http: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/common/http/**', - 'packages/http/**', 'packages/examples/http/**', 'aio/content/guide/http.md', 'aio/content/examples/http/**', @@ -320,9 +375,6 @@ groups: users: - alxhub - IgorMinar - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -330,6 +382,8 @@ groups: # ========================================================= fw-elements: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/elements/**', @@ -341,9 +395,6 @@ groups: users: - andrewseguin - gkalpak - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -351,6 +402,8 @@ groups: # ========================================================= fw-forms: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/forms/**', @@ -374,9 +427,6 @@ groups: reviewers: users: - AndrewKushnir - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -384,6 +434,8 @@ groups: # ========================================================= fw-i18n: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/core/src/i18n/**', @@ -408,9 +460,6 @@ groups: - AndrewKushnir - mhevery - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -418,6 +467,8 @@ groups: # ========================================================= fw-platform-server: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/platform-server/**', @@ -428,9 +479,6 @@ groups: users: - alxhub - kyliau - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -438,27 +486,30 @@ groups: # ========================================================= fw-router: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/router/**', 'packages/examples/router/**', 'aio/content/guide/router.md', + 'aio/content/guide/router-tutorial.md', + 'aio/content/examples/router-tutorial/**', 'aio/content/examples/router/**', 'aio/content/images/guide/router/**' ]) reviewers: users: - atscott - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= # Framework: Service Worker # ========================================================= - fw-server-worker: + fw-service-worker: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/service-worker/**', @@ -477,9 +528,6 @@ groups: - alxhub - gkalpak - IgorMinar - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -487,6 +535,8 @@ groups: # ========================================================= fw-upgrade: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/upgrade/**', @@ -508,9 +558,6 @@ groups: users: - gkalpak - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -518,6 +565,8 @@ groups: # ========================================================= fw-testing: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ '**/testing/**', @@ -527,12 +576,10 @@ groups: ]) reviewers: users: + - AndrewKushnir - IgorMinar - kara - pkozlowski-opensource - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -540,9 +587,9 @@ groups: # ========================================================= fw-benchmarks: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ - 'modules/benchmarks_external/**', 'modules/benchmarks/**' ]) reviewers: @@ -550,8 +597,6 @@ groups: - IgorMinar - kara - pkozlowski-opensource - teams: - - ~framework-global-approvers # ========================================================= @@ -559,6 +604,7 @@ groups: # ========================================================= fw-playground: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'modules/playground/**' @@ -567,8 +613,6 @@ groups: users: - IgorMinar - kara - teams: - - ~framework-global-approvers # ========================================================= @@ -576,6 +620,8 @@ groups: # ========================================================= fw-security: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/core/src/sanitization/**', @@ -590,15 +636,14 @@ groups: users: - IgorMinar - mhevery - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= # Bazel # ========================================================= bazel: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/bazel/**', @@ -609,9 +654,6 @@ groups: - IgorMinar - josephperrott - kyliau - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -619,6 +661,8 @@ groups: # ========================================================= language-service: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/language-service/**', @@ -627,10 +671,8 @@ groups: ]) reviewers: users: + - ayazhafiz - kyliau - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -638,17 +680,17 @@ groups: # ========================================================= zone-js: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ - 'packages/zone.js/**' + 'packages/zone.js/**', + 'aio/content/guide/zone.md' ]) reviewers: users: - JiaLiPassion - mhevery - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -656,6 +698,8 @@ groups: # ========================================================= benchpress: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'packages/benchpress/**' @@ -663,9 +707,6 @@ groups: reviewers: users: - alxhub - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -673,6 +714,7 @@ groups: # ========================================================= integration-tests: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ 'integration/**' @@ -683,8 +725,6 @@ groups: - josephperrott - kara - mhevery - teams: - - ~framework-global-approvers # ========================================================= @@ -692,6 +732,8 @@ groups: # ========================================================= docs-getting-started-and-tutorial: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/setup-local.md', @@ -715,9 +757,6 @@ groups: - aikidave - IgorMinar - StephenFluin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -725,6 +764,8 @@ groups: # ========================================================= docs-marketing: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/marketing/**', @@ -736,18 +777,17 @@ groups: ]) reviewers: users: + - aikidave - IgorMinar - StephenFluin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes - # ========================================================= # Docs: Observables # ========================================================= docs-observables: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/observables.md', @@ -764,9 +804,6 @@ groups: reviewers: users: - alxhub - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -774,6 +811,8 @@ groups: # ========================================================= docs-packaging-and-releasing: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'docs/PUBLIC_API.md', @@ -798,9 +837,6 @@ groups: users: - IgorMinar - kara - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -808,6 +844,8 @@ groups: # ========================================================= docs-cli: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/cli/**', @@ -821,7 +859,7 @@ groups: 'aio/content/images/guide/deployment/**', 'aio/content/guide/file-structure.md', 'aio/content/guide/ivy.md', - 'aio/content/guide/web-worker.md' + 'aio/content/guide/web-worker.md', 'aio/content/guide/workspace-config.md', ]) reviewers: @@ -829,9 +867,6 @@ groups: - clydin - IgorMinar - mgechev - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -839,6 +874,8 @@ groups: # ========================================================= docs-libraries: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/creating-libraries.md', @@ -850,9 +887,6 @@ groups: - alan-agius4 - IgorMinar - mgechev - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -860,6 +894,8 @@ groups: # ========================================================= docs-schematics: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/content/guide/schematics.md', @@ -873,9 +909,6 @@ groups: - alan-agius4 - IgorMinar - mgechev - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -883,6 +916,8 @@ groups: # ========================================================= docs-infra: conditions: + - *can-be-global-approved + - *can-be-global-docs-approved - > contains_any_globs(files, [ 'aio/*', @@ -896,17 +931,13 @@ groups: 'aio/content/guide/docs-style-guide.md', 'aio/content/examples/docs-style-guide/**', 'aio/content/images/guide/docs-style-guide/**', - 'aio/content/guide/visual-studio-2015.md', - 'aio/content/examples/visual-studio-2015/**' + 'aio/content/guide/visual-studio-2015.md' ]) reviewers: users: - gkalpak - IgorMinar - petebacondarwin - teams: - - ~framework-global-approvers - - ~framework-global-approvers-for-docs-only-changes # ========================================================= @@ -914,6 +945,7 @@ groups: # ========================================================= dev-infra: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ '*', @@ -922,6 +954,7 @@ groups: '.github/**', '.vscode/**', '.yarn/**', + 'dev-infra/**', 'docs/BAZEL.md', 'docs/CARETAKER.md', 'docs/COMMITTER.md', @@ -933,6 +966,7 @@ groups: 'docs/SAVED_REPLIES.md', 'docs/TOOLS.md', 'docs/TRIAGE_AND_LABELS.md', + 'goldens/*', 'modules/e2e_util/e2e_util.ts', 'modules/e2e_util/perf_util.ts', 'modules/*', @@ -946,13 +980,13 @@ groups: 'tools/browsers/**', 'tools/build/**', 'tools/circular_dependency_test/**', + 'tools/contributing-stats/**', + 'tools/components/**', 'tools/gulp-tasks/**', 'tools/ng_rollup_bundle/**', 'tools/ngcontainer/**', 'tools/npm/**', - 'tools/public_api_guard/BUILD.bazel', - 'tools/public_api_guard/public_api_guard.bzl', - 'tools/pullapprove/**', + 'tools/npm_integration_test/**', 'tools/rxjs/**', 'tools/saucelabs/**', 'tools/size-tracking/**', @@ -961,7 +995,7 @@ groups: 'tools/testing/**', 'tools/ts-api-guardian/**', 'tools/tslint/**', - 'tools/validate-commit-message/**', + 'tools/utils/**', 'tools/yarn/**', 'tools/*', '**/*.bzl', @@ -974,8 +1008,6 @@ groups: - gkalpak - IgorMinar - josephperrott - teams: - - ~framework-global-approvers # ========================================================= @@ -983,9 +1015,10 @@ groups: # ========================================================= public-api: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ - 'tools/public_api_guard/**', + 'goldens/public-api/**', 'docs/NAMING.md', 'aio/content/guide/glossary.md', 'aio/content/guide/styleguide.md', @@ -995,8 +1028,6 @@ groups: reviewers: users: - IgorMinar - teams: - - ~framework-global-approvers # ================================================ @@ -1004,17 +1035,32 @@ groups: # ================================================ size-tracking: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ - 'aio/scripts/_payload-limits.json', - 'integration/_payload-limits.json' + 'goldens/size-tracking/**' ]) reviewers: users: - IgorMinar - kara - teams: - - ~framework-global-approvers + + + # ================================================ + # Circular dependencies + # ================================================ + circular-dependencies: + conditions: + - *can-be-global-approved + - > + contains_any_globs(files, [ + 'goldens/circular-deps/packages.json' + ]) + reviewers: + users: + - IgorMinar + - josephperrott + - kara #################################################################################### @@ -1026,6 +1072,7 @@ groups: # ========================================================= code-ownership: conditions: + - *can-be-global-approved - > contains_any_globs(files, [ '.pullapprove.yml' @@ -1033,8 +1080,6 @@ groups: reviewers: users: - IgorMinar - teams: - - ~framework-global-approvers # ==================================================== @@ -1042,6 +1087,7 @@ groups: # ==================================================== fallback: conditions: + - *can-be-global-approved # Groups which are found to have matching conditions are `active` # according to PullApprove. If no groups are matched and considered # active, we still want to have a review occur. @@ -1049,5 +1095,3 @@ groups: reviewers: users: - IgorMinar - teams: - - ~framework-global-approvers diff --git a/.vscode/recommended-launch.json b/.vscode/recommended-launch.json index bafab6c732..cd29cdd0f6 100644 --- a/.vscode/recommended-launch.json +++ b/.vscode/recommended-launch.json @@ -34,7 +34,7 @@ "name": "IVY:packages/core/test/acceptance", "type": "node", "request": "launch", - "program": "${workspaceFolder}/node_modules/.bin/bazel", + "program": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", @@ -51,7 +51,7 @@ "name": "IVY:packages/core/test/render3", "type": "node", "request": "launch", - "program": "${workspaceFolder}/node_modules/.bin/bazel", + "program": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", @@ -68,7 +68,7 @@ "name": "IVY:packages/core/test", "type": "node", "request": "launch", - "program": "${workspaceFolder}/node_modules/.bin/bazel", + "program": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", diff --git a/.vscode/recommended-tasks.json b/.vscode/recommended-tasks.json index b129145b3b..0bd05ff37c 100644 --- a/.vscode/recommended-tasks.json +++ b/.vscode/recommended-tasks.json @@ -6,7 +6,7 @@ { "label": "IVY:packages/core/test/...", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", @@ -23,7 +23,7 @@ { "label": "VE:packages/core/test/...", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "packages/core/test", @@ -39,7 +39,7 @@ { "label": "IVY:packages/core/test/acceptance", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", @@ -54,7 +54,7 @@ { "label": "VE:packages/core/test/acceptance", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "packages/core/test/acceptance", @@ -68,7 +68,7 @@ { "label": "IVY:packages/core/test", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", @@ -83,7 +83,7 @@ { "label": "VE:packages/core/test", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "packages/core/test", @@ -97,7 +97,7 @@ { "label": "IVY:packages/core/test/render3", "type": "shell", - "command": "${workspaceFolder}/node_modules/.bin/bazel", + "command": "${workspaceFolder}/node_modules/.bin/bazelisk", "args": [ "test", "--config=ivy", diff --git a/.yarn/releases/yarn-1.21.1.js b/.yarn/releases/yarn-1.22.4.js similarity index 99% rename from .yarn/releases/yarn-1.21.1.js rename to .yarn/releases/yarn-1.22.4.js index 9420daa5b2..09eb37a2cb 100755 --- a/.yarn/releases/yarn-1.21.1.js +++ b/.yarn/releases/yarn-1.22.4.js @@ -34805,15 +34805,27 @@ function hasMergeConflicts(str) { function parse(str, fileLoc) { const parser = new Parser(str, fileLoc); parser.next(); - try { - return parser.parse(); - } catch (error1) { + + if (!fileLoc.endsWith(`.yml`)) { try { - return safeLoad(str, { - schema: FAILSAFE_SCHEMA - }); - } catch (error2) { - throw error1; + return parser.parse(); + } catch (error1) { + try { + return safeLoad(str, { + schema: FAILSAFE_SCHEMA + }); + } catch (error2) { + throw error1; + } + } + } else { + const result = safeLoad(str, { + schema: FAILSAFE_SCHEMA + }); + if (typeof result === 'object') { + return result; + } else { + return {}; } } } @@ -46666,7 +46678,7 @@ function mkdirfix (name, opts, cb) { /* 194 */ /***/ (function(module, exports) { -module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.21.1","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} +module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} /***/ }), /* 195 */ @@ -69876,12 +69888,12 @@ function getRcConfigForFolder(cwd) { } function loadRcFile(fileText, filePath) { - var _parse = (0, (_lockfile || _load_lockfile()).parse)(fileText, 'yarnrc'); + var _parse = (0, (_lockfile || _load_lockfile()).parse)(fileText, filePath); let values = _parse.object; - if (filePath.match(/\.yml$/)) { + if (filePath.match(/\.yml$/) && typeof values.yarnPath === 'string') { values = { 'yarn-path': values.yarnPath }; } @@ -74848,7 +74860,20 @@ let run = exports.run = (() => { } else { let suggestion; - for (const commandName in scripts) { + for (var _iterator9 = scripts.keys(), _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref16; + + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref16 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref16 = _i9.value; + } + + const commandName = _ref16; + const steps = leven(commandName, action); if (steps < 2) { suggestion = commandName; @@ -74933,19 +74958,19 @@ let run = exports.run = (() => { const printedCommands = new Map(); - for (var _iterator9 = pkgCommands, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref16; + for (var _iterator10 = pkgCommands, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref17; - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref16 = _iterator9[_i9++]; + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref17 = _iterator10[_i10++]; } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref16 = _i9.value; + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref17 = _i10.value; } - const pkgCommand = _ref16; + const pkgCommand = _ref17; const action = scripts.get(pkgCommand); invariant(action, 'Action must exists'); @@ -76076,6 +76101,11 @@ class TarballFetcher extends (_baseFetcher || _load_baseFetcher()).default { chown: false, // don't chown. just leave as it is map: header => { header.mtime = now; + if (header.linkname) { + const basePath = path.posix.dirname(path.join('/', header.name)); + const jailPath = path.posix.join(basePath, header.linkname); + header.linkname = path.posix.relative('/', jailPath); + } return header; }, fs: patchedFs @@ -78409,6 +78439,11 @@ class RequestManager { rejectNext(err); }; + const rejectWithoutUrl = function rejectWithoutUrl(err) { + err.message = err.message; + rejectNext(err); + }; + const queueForRetry = reason => { const attempts = params.retryAttempts || 0; if (attempts >= this.maxRetryAttempts - 1) { @@ -78464,6 +78499,11 @@ class RequestManager { } } + if (res.statusCode === 401 && res.caseless && res.caseless.get('server') === 'GitHub.com') { + const message = `${res.body.message}. If using GITHUB_TOKEN in your env, check that it is valid.`; + rejectWithoutUrl(new Error(this.reporter.lang('unauthorizedResponse', res.caseless.get('server'), message))); + } + if (res.statusCode === 401 && res.headers['www-authenticate']) { const authMethods = res.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase()); @@ -96966,12 +97006,14 @@ function _load_asyncToGenerator() { let run = exports.run = (() => { var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (config, reporter, flags, args) { - if (flags.install) { + const installVersion = flags[`2`] ? `berry` : flags.install; + + if (installVersion) { const lockfilePath = path.resolve(config.cwd, 'yarn.lock'); if (!(yield (_fs || _load_fs()).exists(lockfilePath))) { yield (_fs || _load_fs()).writeFile(lockfilePath, ''); } - yield (_child || _load_child()).spawn((_constants || _load_constants()).NODE_BIN_PATH, [process.argv[1], 'policies', 'set-version', flags.install], { + yield (_child || _load_child()).spawn((_constants || _load_constants()).NODE_BIN_PATH, [process.argv[1], 'policies', 'set-version', installVersion], { stdio: 'inherit', cwd: config.cwd }); @@ -97266,6 +97308,7 @@ function setFlags(commander) { commander.option('-y, --yes', 'use default options'); commander.option('-p, --private', 'use default options and private true'); commander.option('-i, --install ', 'install a specific Yarn release'); + commander.option('-2', 'generates the project using Yarn 2'); } function hasWrapper(commander, args) { @@ -98254,6 +98297,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def let bundleUrl; let bundleVersion; + let isV2 = false; if (range === 'nightly' || range === 'nightlies') { bundleUrl = 'https://nightly.yarnpkg.com/latest.js'; @@ -98261,10 +98305,18 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def } else if (range === 'berry' || range === 'v2' || range === '2') { bundleUrl = 'https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js'; bundleVersion = 'berry'; + isV2 = true; } else { - const releases = yield fetchReleases(config, { - includePrereleases: allowRc - }); + let releases = []; + + try { + releases = yield fetchReleases(config, { + includePrereleases: allowRc + }); + } catch (e) { + reporter.error(e.message); + return; + } const release = releases.find(function (release) { // $FlowFixMe @@ -98285,7 +98337,6 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def reporter.log(`Downloading ${chalk.green(bundleUrl)}...`); const bundle = yield fetchBundle(config, bundleUrl); - const rc = (0, (_rc || _load_rc()).getRcConfigForFolder)(config.lockfileFolder); const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`); reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`); @@ -98293,10 +98344,22 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def yield (_fs || _load_fs()).writeFile(yarnPath, bundle); yield (_fs || _load_fs()).chmod(yarnPath, 0o755); - const rcPath = `${config.lockfileFolder}/.yarnrc`; - reporter.log(`Updating ${chalk.magenta(rcPath)}...`); - rc['yarn-path'] = path.relative(config.lockfileFolder, yarnPath); - yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `${(0, (_lockfile || _load_lockfile()).stringify)(rc)}\n`); + const targetPath = path.relative(config.lockfileFolder, yarnPath).replace(/\\/g, '/'); + + if (isV2) { + const rcPath = `${config.lockfileFolder}/.yarnrc.yml`; + reporter.log(`Updating ${chalk.magenta(rcPath)}...`); + + yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `yarnPath: ${JSON.stringify(targetPath)}\n`); + } else { + const rcPath = `${config.lockfileFolder}/.yarnrc`; + reporter.log(`Updating ${chalk.magenta(rcPath)}...`); + + const rc = (0, (_rc || _load_rc()).getRcConfigForFolder)(config.lockfileFolder); + rc['yarn-path'] = targetPath; + + yield (_fs || _load_fs()).writeFilePreservingEol(rcPath, `${(0, (_lockfile || _load_lockfile()).stringify)(rc)}\n`); + } reporter.log(`Done!`); })(); @@ -99619,11 +99682,11 @@ let run = exports.run = (() => { throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceRootNotFound', config.cwd)); } - if (flags.originalArgs < 1) { + if (args.length < 1) { throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingWorkspace')); } - if (flags.originalArgs < 2) { + if (args.length < 2) { throw new (_errors || _load_errors()).MessageError(reporter.lang('workspaceMissingCommand')); } @@ -99632,7 +99695,7 @@ let run = exports.run = (() => { const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest); - var _ref2 = flags.originalArgs || []; + var _ref2 = args || []; const workspaceName = _ref2[0], rest = _ref2.slice(1); @@ -99818,28 +99881,23 @@ let runScript = exports.runScript = (() => { const workspaces = yield config.resolveWorkspaces(workspaceRootFolder, manifest); try { - var _ref6 = flags.originalArgs || []; - - const _ = _ref6[0], - rest = _ref6.slice(1); - for (var _iterator4 = Object.keys(workspaces), _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref7; + var _ref6; if (_isArray4) { if (_i4 >= _iterator4.length) break; - _ref7 = _iterator4[_i4++]; + _ref6 = _iterator4[_i4++]; } else { _i4 = _iterator4.next(); if (_i4.done) break; - _ref7 = _i4.value; + _ref6 = _i4.value; } - const workspaceName = _ref7; + const workspaceName = _ref6; const loc = workspaces[workspaceName].loc; reporter.log(`${os.EOL}> ${workspaceName}`); - yield (_child || _load_child()).spawn((_constants2 || _load_constants2()).NODE_BIN_PATH, [(_constants2 || _load_constants2()).YARN_BIN_PATH, ...rest], { + yield (_child || _load_child()).spawn((_constants2 || _load_constants2()).NODE_BIN_PATH, [(_constants2 || _load_constants2()).YARN_BIN_PATH, 'run', ...args], { stdio: 'inherit', cwd: loc }); @@ -100059,7 +100117,11 @@ let main = exports.main = (() => { commandName = 'install'; isKnownCommand = true; } - + if (commandName === 'set' && args[0] === 'version') { + commandName = 'policies'; + args.splice(0, 1, 'set-version'); + isKnownCommand = true; + } if (!isKnownCommand) { // if command is not recognized, then set default to `run` args.unshift(commandName); @@ -100070,15 +100132,20 @@ let main = exports.main = (() => { let warnAboutRunDashDash = false; // we are using "yarn - - - - - - - - - - - - - - - diff --git a/aio/content/examples/testing/src/tests.sb.ts b/aio/content/examples/testing/src/tests.sb.ts index 2cb3b440f6..8c3e9ae454 100644 --- a/aio/content/examples/testing/src/tests.sb.ts +++ b/aio/content/examples/testing/src/tests.sb.ts @@ -18,6 +18,7 @@ import './app/hero/hero-detail.component.spec.ts'; import './app/hero/hero-list.component.spec.ts'; import './app/model/hero.service.spec.ts'; import './app/model/testing/http-client.spec.ts'; +import './app/shared/canvas.component.spec.ts'; import './app/shared/highlight.directive.spec.ts'; import './app/shared/title-case.pipe.spec.ts'; import './app/twain/twain.component.spec.ts'; diff --git a/aio/content/examples/toh-pt0/src/app/app.component.spec.ts b/aio/content/examples/toh-pt0/src/app/app.component.spec.ts deleted file mode 100644 index 9510495a2d..0000000000 --- a/aio/content/examples/toh-pt0/src/app/app.component.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { TestBed, async } from '@angular/core/testing'; - -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ - AppComponent - ], - }).compileComponents(); - })); - - it('should create the app', async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app).toBeTruthy(); - })); - - it(`should have as title 'app'`, async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app.title).toEqual('app'); - })); - - it('should render title in a h1 tag', async(() => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); - })); -}); diff --git a/aio/content/examples/toh-pt0/src/index.html b/aio/content/examples/toh-pt0/src/index.html index 1c4106381f..0e589b1238 100644 --- a/aio/content/examples/toh-pt0/src/index.html +++ b/aio/content/examples/toh-pt0/src/index.html @@ -4,7 +4,6 @@ Tour of Heroes - diff --git a/aio/content/examples/toh-pt0/src/main.ts b/aio/content/examples/toh-pt0/src/main.ts index a9ca1caf8c..28bfa9e1fe 100644 --- a/aio/content/examples/toh-pt0/src/main.ts +++ b/aio/content/examples/toh-pt0/src/main.ts @@ -8,4 +8,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); + diff --git a/aio/content/examples/toh-pt1/src/app/heroes/heroes.component.spec.ts b/aio/content/examples/toh-pt1/src/app/heroes/heroes.component.spec.ts deleted file mode 100644 index 9c3b1c4d9f..0000000000 --- a/aio/content/examples/toh-pt1/src/app/heroes/heroes.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HeroesComponent } from './heroes.component'; - -describe('HeroesComponent', () => { - let component: HeroesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt1/src/index.html b/aio/content/examples/toh-pt1/src/index.html index 1c4106381f..0e589b1238 100644 --- a/aio/content/examples/toh-pt1/src/index.html +++ b/aio/content/examples/toh-pt1/src/index.html @@ -4,7 +4,6 @@ Tour of Heroes - diff --git a/aio/content/examples/toh-pt1/src/main.ts b/aio/content/examples/toh-pt1/src/main.ts index 6fd45e7d1e..682ef69512 100644 --- a/aio/content/examples/toh-pt1/src/main.ts +++ b/aio/content/examples/toh-pt1/src/main.ts @@ -9,5 +9,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); // #enddocregion diff --git a/aio/content/examples/toh-pt2/src/app/heroes/heroes.component.spec.ts b/aio/content/examples/toh-pt2/src/app/heroes/heroes.component.spec.ts deleted file mode 100644 index 9c3b1c4d9f..0000000000 --- a/aio/content/examples/toh-pt2/src/app/heroes/heroes.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HeroesComponent } from './heroes.component'; - -describe('HeroesComponent', () => { - let component: HeroesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt2/src/index.html b/aio/content/examples/toh-pt2/src/index.html index 1c4106381f..0e589b1238 100644 --- a/aio/content/examples/toh-pt2/src/index.html +++ b/aio/content/examples/toh-pt2/src/index.html @@ -4,7 +4,6 @@ Tour of Heroes - diff --git a/aio/content/examples/toh-pt2/src/main.ts b/aio/content/examples/toh-pt2/src/main.ts index 6fd45e7d1e..682ef69512 100644 --- a/aio/content/examples/toh-pt2/src/main.ts +++ b/aio/content/examples/toh-pt2/src/main.ts @@ -9,5 +9,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); // #enddocregion diff --git a/aio/content/examples/toh-pt3/src/app/heroes/heroes.component.spec.ts b/aio/content/examples/toh-pt3/src/app/heroes/heroes.component.spec.ts deleted file mode 100644 index 9c3b1c4d9f..0000000000 --- a/aio/content/examples/toh-pt3/src/app/heroes/heroes.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HeroesComponent } from './heroes.component'; - -describe('HeroesComponent', () => { - let component: HeroesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt3/src/index.html b/aio/content/examples/toh-pt3/src/index.html index 1c4106381f..0e589b1238 100644 --- a/aio/content/examples/toh-pt3/src/index.html +++ b/aio/content/examples/toh-pt3/src/index.html @@ -4,7 +4,6 @@ Tour of Heroes - diff --git a/aio/content/examples/toh-pt3/src/main.ts b/aio/content/examples/toh-pt3/src/main.ts index 6fd45e7d1e..682ef69512 100644 --- a/aio/content/examples/toh-pt3/src/main.ts +++ b/aio/content/examples/toh-pt3/src/main.ts @@ -9,5 +9,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); // #enddocregion diff --git a/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.1.ts b/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.1.ts index 909955f097..cf775d0952 100644 --- a/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.1.ts +++ b/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.1.ts @@ -5,9 +5,9 @@ import { Observable } from 'rxjs'; class DummyHeroesComponent { heroes: Observable; - + // #docregion ctor constructor(private heroService: HeroService) {} - + // #enddocregion ctor // #docregion getHeroes getHeroes(): void { // #docregion get-heroes diff --git a/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.spec.ts b/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.spec.ts deleted file mode 100644 index 9c3b1c4d9f..0000000000 --- a/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HeroesComponent } from './heroes.component'; - -describe('HeroesComponent', () => { - let component: HeroesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.ts b/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.ts index 4a3667871e..a631a85103 100644 --- a/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.ts +++ b/aio/content/examples/toh-pt4/src/app/heroes/heroes.component.ts @@ -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', diff --git a/aio/content/examples/toh-pt4/src/app/message.service.spec.ts b/aio/content/examples/toh-pt4/src/app/message.service.spec.ts deleted file mode 100644 index 63ecfd8ff6..0000000000 --- a/aio/content/examples/toh-pt4/src/app/message.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { MessageService } from './message.service'; - -describe('MessageService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [MessageService] - }); - }); - - it('should be created', inject([MessageService], (service: MessageService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/aio/content/examples/toh-pt4/src/app/messages/messages.component.spec.ts b/aio/content/examples/toh-pt4/src/app/messages/messages.component.spec.ts deleted file mode 100644 index 3c2b2b1537..0000000000 --- a/aio/content/examples/toh-pt4/src/app/messages/messages.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MessagesComponent } from './messages.component'; - -describe('MessagesComponent', () => { - let component: MessagesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ MessagesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(MessagesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt4/src/index.html b/aio/content/examples/toh-pt4/src/index.html index 1c4106381f..0e589b1238 100644 --- a/aio/content/examples/toh-pt4/src/index.html +++ b/aio/content/examples/toh-pt4/src/index.html @@ -4,7 +4,6 @@ Tour of Heroes - diff --git a/aio/content/examples/toh-pt4/src/main.ts b/aio/content/examples/toh-pt4/src/main.ts index a9ca1caf8c..c7b673cf44 100644 --- a/aio/content/examples/toh-pt4/src/main.ts +++ b/aio/content/examples/toh-pt4/src/main.ts @@ -8,4 +8,5 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/aio/content/examples/toh-pt5/src/app/dashboard/dashboard.component.spec.ts b/aio/content/examples/toh-pt5/src/app/dashboard/dashboard.component.spec.ts deleted file mode 100644 index fea6bfb4db..0000000000 --- a/aio/content/examples/toh-pt5/src/app/dashboard/dashboard.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DashboardComponent } from './dashboard.component'; - -describe('DashboardComponent', () => { - let component: DashboardComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ DashboardComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DashboardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt5/src/app/hero-detail/hero-detail.component.css b/aio/content/examples/toh-pt5/src/app/hero-detail/hero-detail.component.css index 3981760d49..835a13de5b 100644 --- a/aio/content/examples/toh-pt5/src/app/hero-detail/hero-detail.component.css +++ b/aio/content/examples/toh-pt5/src/app/hero-detail/hero-detail.component.css @@ -19,7 +19,6 @@ button { padding: 5px 10px; border-radius: 4px; cursor: pointer; - cursor: hand; } button:hover { background-color: #cfd8dc; diff --git a/aio/content/examples/toh-pt5/src/app/heroes/heroes.component.spec.ts b/aio/content/examples/toh-pt5/src/app/heroes/heroes.component.spec.ts deleted file mode 100644 index 9c3b1c4d9f..0000000000 --- a/aio/content/examples/toh-pt5/src/app/heroes/heroes.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HeroesComponent } from './heroes.component'; - -describe('HeroesComponent', () => { - let component: HeroesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt5/src/app/message.service.spec.ts b/aio/content/examples/toh-pt5/src/app/message.service.spec.ts deleted file mode 100644 index 63ecfd8ff6..0000000000 --- a/aio/content/examples/toh-pt5/src/app/message.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { MessageService } from './message.service'; - -describe('MessageService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [MessageService] - }); - }); - - it('should be created', inject([MessageService], (service: MessageService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/aio/content/examples/toh-pt5/src/app/messages/messages.component.spec.ts b/aio/content/examples/toh-pt5/src/app/messages/messages.component.spec.ts deleted file mode 100644 index 3c2b2b1537..0000000000 --- a/aio/content/examples/toh-pt5/src/app/messages/messages.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MessagesComponent } from './messages.component'; - -describe('MessagesComponent', () => { - let component: MessagesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ MessagesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(MessagesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt5/src/index.html b/aio/content/examples/toh-pt5/src/index.html index f5fed0967e..41147fe767 100644 --- a/aio/content/examples/toh-pt5/src/index.html +++ b/aio/content/examples/toh-pt5/src/index.html @@ -6,7 +6,6 @@ - diff --git a/aio/content/examples/toh-pt5/src/main.ts b/aio/content/examples/toh-pt5/src/main.ts index a9ca1caf8c..c7b673cf44 100644 --- a/aio/content/examples/toh-pt5/src/main.ts +++ b/aio/content/examples/toh-pt5/src/main.ts @@ -8,4 +8,5 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/aio/content/examples/toh-pt6/e2e/src/app.e2e-spec.ts b/aio/content/examples/toh-pt6/e2e/src/app.e2e-spec.ts index 5095aefeaa..cbd0df76d4 100644 --- a/aio/content/examples/toh-pt6/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/toh-pt6/e2e/src/app.e2e-spec.ts @@ -44,10 +44,7 @@ class Hero { } } -// TODO(i): temorarily disable these tests because angular-in-memory-web-api is not compatible with rxjs v6 yet -// and we don't have the backwards compatibility package yet. -// Reenable after rxjs v6 compatibility package is out or angular-in-memory-web-api is compatible with rxjs v6 -xdescribe('Tutorial part 6', () => { +describe('Tutorial part 6', () => { beforeAll(() => browser.get('')); diff --git a/aio/content/examples/toh-pt6/example-config.json b/aio/content/examples/toh-pt6/example-config.json index e69de29bb2..5f2a94dfa8 100644 --- a/aio/content/examples/toh-pt6/example-config.json +++ b/aio/content/examples/toh-pt6/example-config.json @@ -0,0 +1,6 @@ +{ + "tests": [ + {"cmd": "yarn", "args": ["test", "--browsers=ChromeHeadless", "--no-watch"]}, + {"cmd": "yarn", "args": ["e2e", "--prod", "--protractor-config=e2e/protractor-puppeteer.conf.js", "--no-webdriver-update", "--port={PORT}"]} + ] +} diff --git a/aio/content/examples/toh-pt6/src/app/app.module.ts b/aio/content/examples/toh-pt6/src/app/app.module.ts index f2ba5ba879..db08f4a4df 100644 --- a/aio/content/examples/toh-pt6/src/app/app.module.ts +++ b/aio/content/examples/toh-pt6/src/app/app.module.ts @@ -1,5 +1,5 @@ // #docplaster -// #docregion, v1 +// #docregion , v1 import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; @@ -59,6 +59,5 @@ import { MessagesComponent } from './messages/messages.component'; // #docregion import-httpclientmodule }) // #enddocregion import-httpclientmodule - export class AppModule { } // #enddocregion , v1 diff --git a/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.css b/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.css index 062544af48..835a13de5b 100644 --- a/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.css +++ b/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.css @@ -18,7 +18,7 @@ button { border: none; padding: 5px 10px; border-radius: 4px; - cursor: pointer; cursor: hand; + cursor: pointer; } button:hover { background-color: #cfd8dc; diff --git a/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.ts b/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.ts index d1259eebf0..18c7d8f93f 100644 --- a/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.ts +++ b/aio/content/examples/toh-pt6/src/app/hero-detail/hero-detail.component.ts @@ -33,10 +33,10 @@ export class HeroDetailComponent implements OnInit { this.location.back(); } - // #docregion save - save(): void { + // #docregion save + save(): void { this.heroService.updateHero(this.hero) .subscribe(() => this.goBack()); } -// #enddocregion save + // #enddocregion save } diff --git a/aio/content/examples/toh-pt6/src/app/hero-search/hero-search.component.spec.ts b/aio/content/examples/toh-pt6/src/app/hero-search/hero-search.component.spec.ts deleted file mode 100644 index 8414f1c223..0000000000 --- a/aio/content/examples/toh-pt6/src/app/hero-search/hero-search.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; - -import { HeroSearchComponent } from './hero-search.component'; - - -describe('HeroSearchComponent', () => { - let component: HeroSearchComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroSearchComponent ], - imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroSearchComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt6/src/app/hero.service.ts b/aio/content/examples/toh-pt6/src/app/hero.service.ts index 740b822313..5bc7168441 100644 --- a/aio/content/examples/toh-pt6/src/app/hero.service.ts +++ b/aio/content/examples/toh-pt6/src/app/hero.service.ts @@ -36,7 +36,7 @@ export class HeroService { // #docregion getHeroes, getHeroes-1 /** GET heroes from the server */ // #docregion getHeroes-2 - getHeroes (): Observable { + getHeroes(): Observable { return this.http.get(this.heroesUrl) // #enddocregion getHeroes-1 .pipe( @@ -98,7 +98,7 @@ export class HeroService { // #docregion addHero /** POST: add a new hero to the server */ - addHero (hero: Hero): Observable { + addHero(hero: Hero): Observable { return this.http.post(this.heroesUrl, hero, this.httpOptions).pipe( tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)), catchError(this.handleError('addHero')) @@ -108,7 +108,7 @@ export class HeroService { // #docregion deleteHero /** DELETE: delete the hero from the server */ - deleteHero (hero: Hero | number): Observable { + deleteHero(hero: Hero | number): Observable { const id = typeof hero === 'number' ? hero : hero.id; const url = `${this.heroesUrl}/${id}`; @@ -121,7 +121,7 @@ export class HeroService { // #docregion updateHero /** PUT: update the hero on the server */ - updateHero (hero: Hero): Observable { + updateHero(hero: Hero): Observable { return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe( tap(_ => this.log(`updated hero id=${hero.id}`)), catchError(this.handleError('updateHero')) @@ -136,7 +136,7 @@ export class HeroService { * @param operation - name of the operation that failed * @param result - optional value to return as the observable result */ - private handleError (operation = 'operation', result?: T) { + private handleError(operation = 'operation', result?: T) { return (error: any): Observable => { // TODO: send the error to remote logging infrastructure diff --git a/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.css b/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.css index 0b13f0fe4c..9046623926 100644 --- a/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.css +++ b/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.css @@ -30,7 +30,7 @@ } .heroes a:hover { - color:#607D8B; + color: #607D8B; } .heroes .badge { @@ -38,7 +38,7 @@ font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; - background-color:#405061; + background-color: #405061; line-height: 1em; position: relative; left: -1px; diff --git a/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.spec.ts b/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.spec.ts deleted file mode 100644 index 6991d69010..0000000000 --- a/aio/content/examples/toh-pt6/src/app/heroes/heroes.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { HeroesComponent } from './heroes.component'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; - -describe('HeroesComponent', () => { - let component: HeroesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HeroesComponent ], - imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule], - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HeroesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt6/src/app/in-memory-data.service.ts b/aio/content/examples/toh-pt6/src/app/in-memory-data.service.ts index cb95d5eae5..db97393d75 100644 --- a/aio/content/examples/toh-pt6/src/app/in-memory-data.service.ts +++ b/aio/content/examples/toh-pt6/src/app/in-memory-data.service.ts @@ -1,7 +1,7 @@ // #docregion , init +import { Injectable } from '@angular/core'; import { InMemoryDbService } from 'angular-in-memory-web-api'; import { Hero } from './hero'; -import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', diff --git a/aio/content/examples/toh-pt6/src/app/message.service.spec.ts b/aio/content/examples/toh-pt6/src/app/message.service.spec.ts deleted file mode 100644 index 63ecfd8ff6..0000000000 --- a/aio/content/examples/toh-pt6/src/app/message.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { MessageService } from './message.service'; - -describe('MessageService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [MessageService] - }); - }); - - it('should be created', inject([MessageService], (service: MessageService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/aio/content/examples/toh-pt6/src/app/messages/messages.component.spec.ts b/aio/content/examples/toh-pt6/src/app/messages/messages.component.spec.ts deleted file mode 100644 index 3c2b2b1537..0000000000 --- a/aio/content/examples/toh-pt6/src/app/messages/messages.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MessagesComponent } from './messages.component'; - -describe('MessagesComponent', () => { - let component: MessagesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ MessagesComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(MessagesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/toh-pt6/src/index.html b/aio/content/examples/toh-pt6/src/index.html index 1c4106381f..0e589b1238 100644 --- a/aio/content/examples/toh-pt6/src/index.html +++ b/aio/content/examples/toh-pt6/src/index.html @@ -4,7 +4,6 @@ Tour of Heroes - diff --git a/aio/content/examples/toh-pt6/src/main.ts b/aio/content/examples/toh-pt6/src/main.ts index 0740658908..5f3e6c96be 100644 --- a/aio/content/examples/toh-pt6/src/main.ts +++ b/aio/content/examples/toh-pt6/src/main.ts @@ -9,4 +9,5 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/aio/content/examples/two-way-binding/src/app/app.component.spec.ts b/aio/content/examples/two-way-binding/src/app/app.component.spec.ts deleted file mode 100644 index bcbdf36b3e..0000000000 --- a/aio/content/examples/two-way-binding/src/app/app.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { TestBed, async } from '@angular/core/testing'; -import { AppComponent } from './app.component'; -describe('AppComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ - AppComponent - ], - }).compileComponents(); - })); - it('should create the app', async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app).toBeTruthy(); - })); - it(`should have as title 'app'`, async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.debugElement.componentInstance; - expect(app.title).toEqual('app'); - })); - it('should render title in a h1 tag', async(() => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); - })); -}); diff --git a/aio/content/examples/two-way-binding/src/app/sizer/sizer.component.spec.ts b/aio/content/examples/two-way-binding/src/app/sizer/sizer.component.spec.ts deleted file mode 100644 index 3c3a3ada05..0000000000 --- a/aio/content/examples/two-way-binding/src/app/sizer/sizer.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SizerComponent } from './sizer.component'; - -describe('SizerComponent', () => { - let component: SizerComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SizerComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SizerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/two-way-binding/src/index.html b/aio/content/examples/two-way-binding/src/index.html index 74a6a34c64..17601cf77f 100644 --- a/aio/content/examples/two-way-binding/src/index.html +++ b/aio/content/examples/two-way-binding/src/index.html @@ -4,7 +4,6 @@ Two-way Binding - diff --git a/aio/content/examples/universal/e2e/src/app.e2e-spec.ts b/aio/content/examples/universal/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..92cae94e31 --- /dev/null +++ b/aio/content/examples/universal/e2e/src/app.e2e-spec.ts @@ -0,0 +1,300 @@ +import { browser, by, element, ElementArrayFinder, ElementFinder, logging } from 'protractor'; + +class Hero { + id: number; + name: string; + + // Factory methods + + // Hero from string formatted as ' '. + static fromString(s: string): Hero { + return { + id: +s.substr(0, s.indexOf(' ')), + name: s.substr(s.indexOf(' ') + 1), + }; + } + + // Hero from hero list
  • element. + static async fromLi(li: ElementFinder): Promise { + const stringsFromA = await li.all(by.css('a')).getText(); + const strings = stringsFromA[0].split(' '); + return { id: +strings[0], name: strings[1] }; + } + + // Hero id and name from the given detail element. + static async fromDetail(detail: ElementFinder): Promise { + // Get hero id from the first
    + const id = await detail.all(by.css('div')).first().getText(); + // Get name from the h2 + const name = await detail.element(by.css('h2')).getText(); + return { + id: +id.substr(id.indexOf(' ') + 1), + name: name.substr(0, name.lastIndexOf(' ')) + }; + } +} + +describe('Universal', () => { + const expectedH1 = 'Tour of Heroes'; + const expectedTitle = `${expectedH1}`; + const targetHero = { id: 15, name: 'Magneta' }; + const targetHeroDashboardIndex = 3; + const nameSuffix = 'X'; + const newHeroName = targetHero.name + nameSuffix; + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + const severeLogs = logs.filter(entry => entry.level === logging.Level.SEVERE); + expect(severeLogs).toEqual([]); + }); + + describe('Initial page', () => { + beforeAll(() => browser.get('')); + + it(`has title '${expectedTitle}'`, () => { + expect(browser.getTitle()).toEqual(expectedTitle); + }); + + it(`has h1 '${expectedH1}'`, () => { + expectHeading(1, expectedH1); + }); + + const expectedViewNames = ['Dashboard', 'Heroes']; + it(`has views ${expectedViewNames}`, () => { + const viewNames = getPageElts().navElts.map((el: ElementFinder) => el.getText()); + expect(viewNames).toEqual(expectedViewNames); + }); + + it('has dashboard as the active view', () => { + const page = getPageElts(); + expect(page.appDashboard.isPresent()).toBeTruthy(); + }); + }); + + describe('Dashboard tests', () => { + beforeAll(() => browser.get('')); + + it('has top heroes', () => { + const page = getPageElts(); + expect(page.topHeroes.count()).toEqual(4); + }); + + it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero); + + it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView); + + it(`cancels and shows ${targetHero.name} in Dashboard`, () => { + element(by.buttonText('go back')).click(); + browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6 + + const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex); + expect(targetHeroElt.getText()).toEqual(targetHero.name); + }); + + it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero); + + it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView); + + it(`saves and shows ${newHeroName} in Dashboard`, () => { + element(by.buttonText('save')).click(); + browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6 + + const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex); + expect(targetHeroElt.getText()).toEqual(newHeroName); + }); + }); + + describe('Heroes tests', () => { + beforeAll(() => browser.get('')); + + it('can switch to Heroes view', () => { + getPageElts().appHeroesHref.click(); + const page = getPageElts(); + expect(page.appHeroes.isPresent()).toBeTruthy(); + expect(page.allHeroes.count()).toEqual(10, 'number of heroes'); + }); + + it('can route to hero details', async () => { + getHeroLiEltById(targetHero.id).click(); + + const page = getPageElts(); + expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail'); + const hero = await Hero.fromDetail(page.heroDetail); + expect(hero.id).toEqual(targetHero.id); + expect(hero.name).toEqual(targetHero.name.toUpperCase()); + }); + + it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView); + + it(`shows ${newHeroName} in Heroes list`, () => { + element(by.buttonText('save')).click(); + browser.waitForAngular(); + const expectedText = `${targetHero.id} ${newHeroName}`; + expect(getHeroAEltById(targetHero.id).getText()).toEqual(expectedText); + }); + + it(`deletes ${newHeroName} from Heroes list`, async () => { + const heroesBefore = await toHeroArray(getPageElts().allHeroes); + const li = getHeroLiEltById(targetHero.id); + li.element(by.buttonText('x')).click(); + + const page = getPageElts(); + expect(page.appHeroes.isPresent()).toBeTruthy(); + expect(page.allHeroes.count()).toEqual(9, 'number of heroes'); + const heroesAfter = await toHeroArray(page.allHeroes); + // console.log(await Hero.fromLi(page.allHeroes[0])); + const expectedHeroes = heroesBefore.filter(h => h.name !== newHeroName); + expect(heroesAfter).toEqual(expectedHeroes); + // expect(page.selectedHeroSubview.isPresent()).toBeFalsy(); + }); + + it(`adds back ${targetHero.name}`, async () => { + const updatedHeroName = 'Alice'; + const heroesBefore = await toHeroArray(getPageElts().allHeroes); + const numHeroes = heroesBefore.length; + + element(by.css('input')).sendKeys(updatedHeroName); + element(by.buttonText('add')).click(); + + const page = getPageElts(); + const heroesAfter = await toHeroArray(page.allHeroes); + expect(heroesAfter.length).toEqual(numHeroes + 1, 'number of heroes'); + + expect(heroesAfter.slice(0, numHeroes)).toEqual(heroesBefore, 'Old heroes are still there'); + + const maxId = heroesBefore[heroesBefore.length - 1].id; + expect(heroesAfter[numHeroes]).toEqual({id: maxId + 1, name: updatedHeroName}); + }); + + it('displays correctly styled buttons', async () => { + element.all(by.buttonText('x')).then(buttons => { + for (const button of buttons) { + // Inherited styles from styles.css + expect(button.getCssValue('font-family')).toBe('Arial'); + expect(button.getCssValue('border')).toContain('none'); + expect(button.getCssValue('padding')).toBe('5px 10px'); + expect(button.getCssValue('border-radius')).toBe('4px'); + // Styles defined in heroes.component.css + expect(button.getCssValue('left')).toBe('194px'); + expect(button.getCssValue('top')).toBe('-32px'); + } + }); + + const addButton = element(by.buttonText('add')); + // Inherited styles from styles.css + expect(addButton.getCssValue('font-family')).toBe('Arial'); + expect(addButton.getCssValue('border')).toContain('none'); + expect(addButton.getCssValue('padding')).toBe('5px 10px'); + expect(addButton.getCssValue('border-radius')).toBe('4px'); + }); + }); + + describe('Progressive hero search', () => { + beforeAll(() => browser.get('')); + + it(`searches for 'Ma'`, async () => { + getPageElts().searchBox.sendKeys('Ma'); + browser.sleep(1000); + + expect(getPageElts().searchResults.count()).toBe(4); + }); + + it(`continues search with 'g'`, async () => { + getPageElts().searchBox.sendKeys('g'); + browser.sleep(1000); + expect(getPageElts().searchResults.count()).toBe(2); + }); + + it(`continues search with 'e' and gets ${targetHero.name}`, async () => { + getPageElts().searchBox.sendKeys('n'); + browser.sleep(1000); + const page = getPageElts(); + expect(page.searchResults.count()).toBe(1); + const hero = page.searchResults.get(0); + expect(hero.getText()).toEqual(targetHero.name); + }); + + it(`navigates to ${targetHero.name} details view`, async () => { + const hero = getPageElts().searchResults.get(0); + expect(hero.getText()).toEqual(targetHero.name); + hero.click(); + + const page = getPageElts(); + expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail'); + const hero2 = await Hero.fromDetail(page.heroDetail); + expect(hero2.id).toEqual(targetHero.id); + expect(hero2.name).toEqual(targetHero.name.toUpperCase()); + }); + }); + + // Helpers + function addToHeroName(text: string): Promise { + return element(by.css('input')).sendKeys(text) as Promise; + } + + async function dashboardSelectTargetHero(): Promise { + const targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex); + expect(targetHeroElt.getText()).toEqual(targetHero.name); + targetHeroElt.click(); + browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6 + + const page = getPageElts(); + expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail'); + const hero = await Hero.fromDetail(page.heroDetail); + expect(hero.id).toEqual(targetHero.id); + expect(hero.name).toEqual(targetHero.name.toUpperCase()); + } + + function expectHeading(hLevel: number, expectedText: string): void { + const hTag = `h${hLevel}`; + const hText = element(by.css(hTag)).getText(); + expect(hText).toEqual(expectedText, hTag); + } + + function getHeroAEltById(id: number): ElementFinder { + const spanForId = element(by.cssContainingText('li span.badge', id.toString())); + return spanForId.element(by.xpath('..')); + } + + function getHeroLiEltById(id: number): ElementFinder { + const spanForId = element(by.cssContainingText('li span.badge', id.toString())); + return spanForId.element(by.xpath('../..')); + } + + function getPageElts() { + const navElts = element.all(by.css('app-root nav a')); + + return { + navElts, + + appDashboardHref: navElts.get(0), + appDashboard: element(by.css('app-root app-dashboard')), + topHeroes: element.all(by.css('app-root app-dashboard > div h4')), + + appHeroesHref: navElts.get(1), + appHeroes: element(by.css('app-root app-heroes')), + allHeroes: element.all(by.css('app-root app-heroes li')), + selectedHeroSubview: element(by.css('app-root app-heroes > div:last-child')), + + heroDetail: element(by.css('app-root app-hero-detail > div')), + + searchBox: element(by.css('#search-box')), + searchResults: element.all(by.css('.search-result li')) + }; + } + + async function toHeroArray(allHeroes: ElementArrayFinder): Promise { + return await allHeroes.map(Hero.fromLi); + } + + async function updateHeroNameInDetailView(): Promise { + // Assumes that the current view is the hero details view. + addToHeroName(nameSuffix); + + const page = getPageElts(); + const hero = await Hero.fromDetail(page.heroDetail); + expect(hero.id).toEqual(targetHero.id); + expect(hero.name).toEqual(newHeroName.toUpperCase()); + } +}); diff --git a/aio/content/examples/universal/example-config.json b/aio/content/examples/universal/example-config.json index 2c13a4178b..5a915d9184 100644 --- a/aio/content/examples/universal/example-config.json +++ b/aio/content/examples/universal/example-config.json @@ -1,3 +1,7 @@ { - "projectType": "universal" + "projectType": "universal", + "e2e": [ + {"cmd": "yarn", "args": ["e2e", "--prod", "--protractor-config=e2e/protractor-puppeteer.conf.js", "--no-webdriver-update", "--port={PORT}"]}, + {"cmd": "yarn", "args": ["run", "build:ssr"]} + ] } diff --git a/aio/content/examples/universal/server.ts b/aio/content/examples/universal/server.ts index 4b552b4b41..7f4aace2fa 100644 --- a/aio/content/examples/universal/server.ts +++ b/aio/content/examples/universal/server.ts @@ -6,24 +6,28 @@ import { join } from 'path'; import { AppServerModule } from './src/main.server'; import { APP_BASE_HREF } from '@angular/common'; +import { existsSync } from 'fs'; // The Express app is exported so that it can be used by serverless Functions. export function app() { const server = express(); - const distFolder = join(process.cwd(), 'dist/express-engine-ivy/browser'); + const distFolder = join(process.cwd(), 'dist/browser'); + const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'; // #docregion ngExpressEngine + // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) server.engine('html', ngExpressEngine({ bootstrap: AppServerModule, })); // #enddocregion ngExpressEngine + server.set('view engine', 'html'); server.set('views', distFolder); // #docregion data-request // TODO: implement data requests securely - server.get('/api/*', (req, res) => { - res.status(404).send('data requests are not supported'); + server.get('/api/**', (req, res) => { + res.status(404).send('data requests are not yet supported'); }); // #enddocregion data-request @@ -37,7 +41,7 @@ export function app() { // #docregion navigation-request // All regular routes use the Universal engine server.get('*', (req, res) => { - res.render('index', { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] }); + res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] }); }); // #enddocregion navigation-request @@ -59,7 +63,8 @@ function run() { // The below code is to ensure that the server is run only when not requiring the bundle. declare const __non_webpack_require__: NodeRequire; const mainModule = __non_webpack_require__.main; -if (mainModule && mainModule.filename === __filename) { +const moduleFilename = mainModule && mainModule.filename || ''; +if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { run(); } diff --git a/aio/content/examples/universal/src/app/app.component.css b/aio/content/examples/universal/src/app/app.component.css index bf741e4575..c1d8721226 100644 --- a/aio/content/examples/universal/src/app/app.component.css +++ b/aio/content/examples/universal/src/app/app.component.css @@ -1,7 +1,6 @@ /* AppComponent's private CSS styles */ h1 { font-size: 1.2em; - color: #999; margin-bottom: 0; } h2 { @@ -18,7 +17,7 @@ nav a { border-radius: 4px; } nav a:visited, a:link { - color: #607D8B; + color: #334953; } nav a:hover { color: #039be5; diff --git a/aio/content/examples/universal/src/app/app.module.ts b/aio/content/examples/universal/src/app/app.module.ts index 1dab81e17b..ed6e1a32db 100644 --- a/aio/content/examples/universal/src/app/app.module.ts +++ b/aio/content/examples/universal/src/app/app.module.ts @@ -14,8 +14,6 @@ import { DashboardComponent } from './dashboard/dashboard.component'; import { HeroDetailComponent } from './hero-detail/hero-detail.component'; import { HeroesComponent } from './heroes/heroes.component'; import { HeroSearchComponent } from './hero-search/hero-search.component'; -import { HeroService } from './hero.service'; -import { MessageService } from './message.service'; import { MessagesComponent } from './messages/messages.component'; // #docregion platform-detection @@ -32,6 +30,10 @@ import { isPlatformBrowser } from '@angular/common'; FormsModule, AppRoutingModule, HttpClientModule, + + // The HttpClientInMemoryWebApiModule module intercepts HTTP requests + // and returns simulated server responses. + // Remove it when a real server is ready to receive requests. HttpClientInMemoryWebApiModule.forRoot( InMemoryDataService, { dataEncapsulation: false } ) @@ -44,7 +46,6 @@ import { isPlatformBrowser } from '@angular/common'; MessagesComponent, HeroSearchComponent ], - providers: [ HeroService, MessageService ], bootstrap: [ AppComponent ] }) export class AppModule { diff --git a/aio/content/examples/universal/src/app/app.server.module.ts b/aio/content/examples/universal/src/app/app.server.module.ts index b32046a592..a2cdc20c9b 100644 --- a/aio/content/examples/universal/src/app/app.server.module.ts +++ b/aio/content/examples/universal/src/app/app.server.module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; -import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; @@ -9,11 +8,10 @@ import { AppComponent } from './app.component'; imports: [ AppModule, ServerModule, - ModuleMapLoaderModule ], providers: [ - // Add universal-only providers here + // Add server-only providers here. ], - bootstrap: [ AppComponent ], + bootstrap: [AppComponent], }) export class AppServerModule {} diff --git a/aio/content/examples/universal/src/app/dashboard/dashboard.component.css b/aio/content/examples/universal/src/app/dashboard/dashboard.component.css index 6222c23bb7..938b84b2b0 100644 --- a/aio/content/examples/universal/src/app/dashboard/dashboard.component.css +++ b/aio/content/examples/universal/src/app/dashboard/dashboard.component.css @@ -34,7 +34,7 @@ h4 { color: #eee; max-height: 120px; min-width: 120px; - background-color: #607D8B; + background-color: #3f525c; border-radius: 2px; } .module:hover { diff --git a/aio/content/examples/universal/src/app/dashboard/dashboard.component.html b/aio/content/examples/universal/src/app/dashboard/dashboard.component.html index 36e86053a6..b70c0c7d6e 100644 --- a/aio/content/examples/universal/src/app/dashboard/dashboard.component.html +++ b/aio/content/examples/universal/src/app/dashboard/dashboard.component.html @@ -8,4 +8,4 @@
    - + diff --git a/aio/content/examples/universal/src/app/dashboard/dashboard.component.spec.ts b/aio/content/examples/universal/src/app/dashboard/dashboard.component.spec.ts deleted file mode 100644 index fea6bfb4db..0000000000 --- a/aio/content/examples/universal/src/app/dashboard/dashboard.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DashboardComponent } from './dashboard.component'; - -describe('DashboardComponent', () => { - let component: DashboardComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ DashboardComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(DashboardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/aio/content/examples/universal/src/app/dashboard/dashboard.component.ts b/aio/content/examples/universal/src/app/dashboard/dashboard.component.ts index f152c10c7e..c559ccdd4d 100644 --- a/aio/content/examples/universal/src/app/dashboard/dashboard.component.ts +++ b/aio/content/examples/universal/src/app/dashboard/dashboard.component.ts @@ -18,6 +18,6 @@ export class DashboardComponent implements OnInit { getHeroes(): void { this.heroService.getHeroes() - .subscribe(heroes => this.heroes = heroes.slice(1, 5)); + .subscribe(heroes => this.heroes = heroes.slice(1, 5)); } } diff --git a/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.css b/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.css index 3981760d49..835a13de5b 100644 --- a/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.css +++ b/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.css @@ -19,7 +19,6 @@ button { padding: 5px 10px; border-radius: 4px; cursor: pointer; - cursor: hand; } button:hover { background-color: #cfd8dc; diff --git a/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.html b/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.html index 6fa498ee4f..7ac41967f9 100644 --- a/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.html +++ b/aio/content/examples/universal/src/app/hero-detail/hero-detail.component.html @@ -1,5 +1,5 @@
    -

    {{ hero.name | uppercase }} Details

    +

    {{hero.name | uppercase}} Details

    id: {{hero.id}}